Initial version of the parser

This commit is contained in:
Emmanuel Briot 2014-02-06 13:18:29 +01:00
parent 921a40ee8e
commit b6e2af0284
20 changed files with 1223 additions and 45 deletions

31
docs/_static/adacore.css vendored Normal file
View File

@ -0,0 +1,31 @@
@import "sphinxdoc.css";
.menuselection {
background-color: #f2f2f2; /* same as .kbd */
border-bottom: 1px solid #ddd; /* same as .kbd */
color: #333; /* same as .kbd */
}
/* same as in release notes */
h1, h2, h3, h4 {color: #369}
h2 { margin-top: 60px; margin-bottom: 20px}
h3 { margin-bottom: 15px }
div.bodywrapper {
/* Limit width to make more readable */
max-width: 1000px;
}
div.sphinxsidebar {
width: auto;
max-width: 400px;
}
div.sphinxsidebar ul ul {
list-style: none;
}
.toctree-l1 a, .toctree-l2 a, .toctree-l3 a,
.toctree-l1 a tt, .toctree-l2 a tt, .toctree-l3 a tt
{
color: #369;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

View File

@ -66,12 +66,16 @@ There exist other similar software already.
the testsuite of CRM, GNAT Tracker and bugtool for a while. This tool is
very close to :program:`cucumber`, and uses a similar syntax for the test
description files.
It has several drawbacks, though: its implementation is 'experimental',
and lacks documentation. Its output is sometimes confusing, and we made
several iterations to make it usable, although it could be improved.
This tool is not distributed outside of Sogilis and AdaCore, so it has not
been extensively tested either.
It also lacks predefined regexps, step definitions, or even a full fledge
assertions library that would make it easier to write new steps.
* :program:`Fitnesse` uses tests written in a Wiki, and directly highlights
the web page to show passing and failing tests.
One of its nice aspects is that tests can be really mixed in with plain

View File

@ -11,7 +11,7 @@
# All configuration values have a default; values that are commented out
# serve to show the default.
import sys, os
import sys, os, time
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
@ -39,9 +39,12 @@ source_suffix = '.rst'
# The master toctree document.
master_doc = 'index'
def get_copyright():
return u'2013-%s, AdaCore' % time.strftime('%Y')
# General information about the project.
project = u'GNAT Test Driven Development'
copyright = u'2014, AdaCore (c)'
project = u'GNAT Behavior Driven Development'
copyright = get_copyright()
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
@ -91,7 +94,8 @@ pygments_style = 'sphinx'
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
html_theme = 'default'
html_theme = 'sphinxdoc'
html_style = 'adacore.css'
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
@ -110,7 +114,7 @@ html_theme = 'default'
# The name of an image file (relative to this directory) to place at the top
# of the sidebar.
#html_logo = None
html_logo = 'adacore_transparent.png'
# The name of an image file (within the static path) to use as favicon of the
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
@ -164,7 +168,7 @@ html_static_path = ['_static']
#html_file_suffix = None
# Output file base name for HTML help builder.
htmlhelp_basename = 'GNATTestDrivenDevelopmentdoc'
htmlhelp_basename = 'GNATbdd'
# -- Options for LaTeX output --------------------------------------------------
@ -183,7 +187,7 @@ latex_elements = {
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title, author, documentclass [howto/manual]).
latex_documents = [
('index', 'GNATTestDrivenDevelopment.tex', u'GNAT Test Driven Development Documentation',
('index', 'gnatbdd.tex', u'GNAT Behavior Driven Development',
u'AdaCore (c)', 'manual'),
]
@ -213,7 +217,7 @@ latex_documents = [
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
('index', 'gnattestdrivendevelopment', u'GNAT Test Driven Development Documentation',
('index', 'gnatbdd', u'GNAT Behavior Driven Development',
[u'AdaCore (c)'], 1)
]
@ -227,9 +231,9 @@ man_pages = [
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
('index', 'GNATTestDrivenDevelopment', u'GNAT Test Driven Development Documentation',
u'AdaCore (c)', 'GNATTestDrivenDevelopment', 'One line description of project.',
'Miscellaneous'),
('index', 'gnatbdd', u'GNAT Behavior Driven Development',
u'AdaCore (c)', 'GNATbdd', 'Behavior Driven Development.',
'Development'),
]
# Documents to append as an appendix to all manuals.
@ -245,10 +249,10 @@ texinfo_documents = [
# -- Options for Epub output ---------------------------------------------------
# Bibliographic Dublin Core info.
epub_title = u'GNAT Test Driven Development'
epub_title = u'GNAT Behavior Driven Development'
epub_author = u'AdaCore (c)'
epub_publisher = u'AdaCore (c)'
epub_copyright = u'2014, AdaCore (c)'
epub_copyright = copyright
# The language of the text. It defaults to the language option
# or en if the language is not set.

264
docs/features.rst Normal file
View File

@ -0,0 +1,264 @@
.. highlight:: gherkin
********
Features
********
An application's requirements are organized into features. Such a feature has a
name (for instance one feature we would use for GNATbdd itself would be "Find
features to run"), as well as a textual description. This description is only
meant for human readers, and is not used by the automatic tool.
The features are then further divided into one or more scenarios, which are
sometimes also called "tests". These will be discussed in the next section.
Finding features
================
Let's first examine how GNATbdd finds all the features for your application.
.. index:: switches; --ext
Typically, the features are written in text files with a :file:`.feature`
extension. Several editors will automatically provide syntax highlighting with
such an extension. However, you can choose your own extension by using the
:option:`--ext .EXT` switch when you run GNATbdd.
.. index:: switches; --features
By default, GNATbdd will find all the files with a matching extension in
a subdirectory names :file:`features`, within the directory where GNATbdd
is run. You can however specify one or more other directories to check,
by using the :option:`--features DIR` switch one or more times.
GNATbdd will always search recursively in those directories. This lets you
organize the files into subdirectories when you have several of them, and
thus perhaps make it easier to refer to them.
GNATbdd will always run the features file in alphabetical order on the full
path of the feature file). This provides a more consistent output when
GNATbdd is run multiple times.
Syntax of the features file
===========================
The syntax of the features file is based on the one from Cucumber, which is
a BDD standard tool in the Ruby world. The grammar and syntax in those files
is voluntarily information, since they are meant to be editable and readable
by people other than software developers.
As a result, this documentation is mostly based on examples. Let's start
with the first general layout for a file::
Feature: name of feature
high-level description
of what the feature does (plain English, not used by the tools)
Scenario: name of scenario
Given some precondition
And some other precondition
When I do some action
And I do some other action
Then I should see a specific result
And I see something else
But I do not see something else
There could be several **scenarios** within the **feature**. In fact, there
could also be several features in a given file, but this is not recommended in
general.
The scenario is split into several **steps**, each of which start on a separate
line. The steps are introduced by one of severaal keywords, all shown in bold
in the example above. You can use any of the keywords for any of the steps, but
in general they have the following semantic:
* **Given** puts the system in a known state, before the user starts
interacting with it. Avoid talking about user interaction in givens. These
are similar to preconditions in programming languages.
For instance, a given would setup a database, log in a user, and so on.
* **When** describes the actions performed by the user, like clicking on
elements, providing input, and so on.
* **Then** observes the outcome of the actions. These observations should be
related to the business benefit that was described in the feature. The
observation should be on some kind of output that is something that comes out
of the system (report, user interface, message,...) and preferrably not
something deeply buried in the system (use unit tests for those instead).
The example above indents each level of the description. This is not
strictly mandatory, but helps make the file more readable.
The keywords are case-sensitive, as is done in other BDD tools.
Comments
--------
A feature file can contain comments anywhere. These are lines whose first
non-blank character is '#'. The comment extends to the end of the line.
Tagging
-------
Features and scenarios can be tagged with one or more tags. These tags are
specific to your application and usage of GNATbdd. Primarily, they can be used
to run subsets of the whole set of scenarios. Here is an example::
@gui @editor @req-1-1
Feature: Opening an editor restores the previous location
@startup
Scenario: Restore open editors and their location on startup
Given a previous run that was editing foo.adb at line 5
When I start the application
Then I should see a window foo.adb at line 5
The tags of the feature automatically apply to its Scenarios
.. index:: switches; --tags
When you run GNATbdd, you can use the switch :option:`--tags` to control which
scenarios should be run. For instance, all scenarios related to '@gui', or all
scenarios not related to '@startup'. You can of course select subsets of
scenarios based on the file names, but tags provide a file-system-agnostic
selection mechanism.
Other usage of tags could be to identify *slow tests* (with @slow) so that
their timeout is increased.
A tag can also be used to link a scenario to a *high-level requirement* in your
application
Tags can also be used to identify *expected failures* (for instance @xfail), or
*work in progress* (for instance @wip).
Step configuration
------------------
Steps describe the actual actions to perform on the software, its input or its
output. In the examples above, we have seen various sentences used to describe
those actions. However, if we have to write a different sentence for every
little variation, this will end up being very difficult to maintain indeed.
So instead, the steps can be configured so that they apply to a wide variety of
scenario. For instance, going back the example on the editors above, there is
nothing specific in the test about the name *foo.adb* or the line *5*. We might
want to rerun a similar step on file *bar.adb* at line *10*. As we will see
when we discuss the definition of steps, this is of course doable.
But staying closer to the topic of the syntax, there are two other ways that
the steps can be configured, namely **multi-line strings** and **tables**.
* multi-line strings are convenient when the text to substitute contains
several lines. They can only be used as the last part of the step, as in
the following example::
Feature: Entering multiple lines of text in the editor
Scenario: Pressing the return key on the keyboaard
Given a blank editor
When I press the keys <a>, <enter>, <b>
Then the editor should contain
"""
a
b
"""
A multi-line string starts on a line of its own just after the step itself.
It starts with three double quotes (this is a notation that is familiar to
all Python developers), and ends on a similar line that contains double-quotes.
The double-quotes must appear on a line of their own.
We recommend indenting the quotes and their contains relatively to the step
itself to improve readability.
The lines between the quotes form the text that is used for the step itself.
Those lines are unindented by an amount equal to the indentation of the first
quotes line (so in the example above there will in fact be no whitespace
before 'a' and 'b' when we compare them to the actual output). If a line does
not start with enough white spaces, GNATbdd simply removes all leading white
spaces, but preserves the first non-white character.
* tables are another great way to provide input. They organize their data into
columns, which are interpreted by the step as it sees fit. Here an example::
Feature: Logging in on a website
Scenario: Logging with valid user account
Given the following users exist
| Name | Email | Phone |
| John | john@example.com | 1234 |
| Jack | jack@example.com | 5678 |
When I log in as "Jack"
Then I should see the home page
Background scenario
-------------------
The givens in the last scenario above (providing the name of multiple users for
a web site) would need to be duplicated if we wanted another scenario that tests
logging in with an invalid user. Obviously, duplication is just as bad in tests
as it is in the code itself.
Instead, you can defined a background for the feature. It defines steps to be
performed before running each of the step in the scenario. For instance, the
feature above would be better written as::
Feature: Logging in on a website
Background:
Given the following users exist
| Name | Email | Phone |
| John | john@example.com | 1234 |
| Jack | jack@example.com | 5678 |
Scenario: Logging with valid user account
When I log in as "Jack"
Then I should see the home page
Scenario: Logging with invalid user account
When I log in as "Henry"
Then I should see the login page
Scenario outlines
-----------------
We mentioned before that parts of the steps can be configured. For instance, we
could have a feature with the following two scenarios::
Feature: Testing addition in a calculator
Scenario: adding simple numbers
When I enter 5
And I add 12
Then I should get 17
Scenario: adding larger numbers
When I enter 105
And I add 1012
Then I should get 1117
The two scenarios are very similar, this is another case of duplication that
would best be avoided.
The feature file provides the notion of a **Scenario Outline**, which provides
text substitution to create multiple scenarios. Here is the example above
rewritten by taking advantage of this feature::
Feature: Testing addition in a calculator
Scenario Outline: adding simple numbers
When I enter <num1>
And I add <num2>
Then I should get <result>
Examples:
| num1 | num2 | result |
| 5 | 12 | 17 |
| 105 | 1012 | 1117 |
The **Examples** provide the values to substitute in the steps above. There
will be one scenario executed for each line in the examples.
For compatibility with other tools, the keyword **Examples:** can be replaced
with **Scenarios:**.

View File

@ -1,15 +1,16 @@
.. GNAT Test Driven Development documentation master file, created by
.. GNAT Behavior Driven Development documentation master file, created by
sphinx-quickstart on Mon Feb 3 10:18:55 2014.
You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive.
GNAT Test Driven Development
============================
GNAT Behavior Driven Development
================================
.. toctree::
:maxdepth: 2
bdd
features
output

View File

@ -7,11 +7,125 @@ GNATbdd provides a rich set of options for controlling the output.
Colors
======
.. index:: switches; --color
Color output is enabled by default when the terminal supports it. It is used to
highlight passing, failing and skipped tests.
highlight passing, failing and skipped tests. GNATbdd attempts to detect
automatically whether advanced features like colors or moving the cursor are
supported. However, you can also force a specific setting by using one of:
On Windows, you must install the package `ANSICON
<https://github.com/adoxa/ansicon/>`_ to get color output.
* :option:`--color=yes`
(See also https://pypi.python.org/pypi/colorama)
Or perhaps ansi.sys
Forces the use of colors in the output. This is in particular useful when the
output is redirected to a file which you intend to display in a console later
on. GNATbdd always uses ANSI escape sequences when redirecting to a file,
so on Windows you should install `ANSICON <https://github.com/adoxa/ansicon/>`_.
This is not necessary when you are directly displaying the output in the
console though.
* :option:`--color=no`
Disable the use of colors. GNATbdd will output strings such as "[OK]",
"[FAILED]" or "[SKIPPED]" instead.
* :option:`--color=auto` (the default)
Quiet and verbose output
========================
.. index:: switches; --output
Depending on your use of the tests (and for instance whether you are using
GNATbdd interactively or as part of an automatic testsuite), the amount of
output needs to be controlled.
Typically, a user developing the application needs to get as much information
as possible directly in the console, so that hopefully it is easy to undertand
why a test is failing. In fact, some users might want to see the passing tests
as well, just to make sure the testsuite is moving forward.
However, during an automatic test, all the output can be logged, and we are
only interested in getting a final count of passing and failing tests.
All these behavior can be controlled with the following command line
switches:
* :option:`--output=quiet`
GNATbdd only displays the final count for each categories of
tests (passed, failed and skipped).
The output therefore looks like::
5 scenarios (1 skipped, 2 failed, 2 passed)
0m1.002s
* :option:`--output=dots` (the default)
GNATbdd displays a "." for each test that passes, a "F" for each
test that fails (and will then display a full backtrace for those)
and a "S" for skipped tests (and will also display more information
for those).
Finally, it displays a summary for the final count for each category.
The output therefore looks like the following (although colors will be
used depending on the :option:`--color` switch)::
.F...
Feature: controlling the output
Scenario: quiet and verbose output # features/output.feature:3
Given 2 failing scenarios # features/output.feature:4
and 2 passing scenarios # features/output.feature:5
Then the output should look like "..." # [FAILED]
5 scenarios (1 failed, 4 passed)
0m1.002s
* :option:`--output=hide_passed`
In this mode, GNATbdd does not display anything when a scenario passes,
but displays the same backtrace as above for failed or skipped. One
advantage is that the output of the full traces is done after each
scenario has been run, not as a summary after all of them have run.
* :option:`--output=full`
In this mode, GNATbdd displays output of all scenarios, whether they
pass or fail. The output looks like the above, but instead of displaying
a ".", GNATbdd outputs the full lists of steps for that scenario.
Log files
=========
.. index:: switches; --log
In addition, GNATbdd can still generate log files which contain output
similar to the above, as well as any output done by your application
on stdout or stderr.
One log file is generated per scenario. Its name is that of the features
file and the line number at which the scenario starts. The switch
:option:`--log=DIR` can be used to control the directory in which the
log files are created. The default is to create them in a :file:`logs/`
subdirectory of the directory where GNATbdd is run.
.. note::
Should this be in the root project's object_dir, when GNATbdd is run
with a project in argument ?
Projects
========
.. index:: switches; -P
GNATbdd needs to compile the step definitions your application provides. The
features files themselves are parsed dynamically and do not need compile.
For the compilation to succeed, GNATbdd needs to find where the sources of the
steps are (see the :option:`--steps` switch), as well as all the sources on
which they depend. Such sources are found via a GNAT Project file, which can
be specified with the :option:`-P PROJECT.gpr` switch.

View File

@ -1,12 +1,22 @@
with "gnatcoll";
project GNATBDD is
for Main use ("gnattdd-main.adb", "driver.adb", "test_colors.adb");
for Main use ("main.adb", "driver.adb");
for Languages use ("Ada", "C");
for Source_Dirs use ("src");
for Object_Dir use "obj";
package Builder is
for Executable ("gnattdd-main.adb") use "gnattdd";
for Executable ("main.adb") use "gnattdd";
end Builder;
package Compiler is
for Switches ("Ada") use ("-g", "-gnata", "-gnatVa", "-gnatQ",
"-gnatyg0", "-gnatwaCJe", "-gnat2012",
"-gnateE");
end Compiler;
package Binder is
for Switches ("Ada") use ("-E");
end Binder;
end GNATBDD;

125
src/bdd-features.adb Normal file
View File

@ -0,0 +1,125 @@
-----------------------------------------------------------------------------
-- g N A T C O L L --
-- --
-- Copyright (C) 2014, AdaCore --
-- --
-- This library is free software; you can redistribute it and/or modify it --
-- under terms of the GNU General Public License as published by the Free --
-- Software Foundation; either version 3, or (at your option) any later --
-- version. This library is distributed in the hope that it will be useful, --
-- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- --
-- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. --
-- --
-- As a special exception under Section 7 of GPL version 3, you are granted --
-- additional permissions described in the GCC Runtime Library Exception, --
-- version 3.1, as published by the Free Software Foundation. --
-- --
-- You should have received a copy of the GNU General Public License and --
-- a copy of the GCC Runtime Library Exception along with this program; --
-- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see --
-- <http://www.gnu.org/licenses/>. --
-- --
------------------------------------------------------------------------------
with Ada.Strings.Fixed; use Ada.Strings, Ada.Strings.Fixed;
package body BDD.Features is
----------
-- Free --
----------
procedure Free (Self : in out Feature) is
begin
Self.File := No_File;
Free (Self.Name);
end Free;
--------------
-- Set_Name --
--------------
procedure Set_Name (Self : in out Feature; Name : String) is
begin
Free (Self.Name);
Self.Name := new String'(Trim (Name, Both));
end Set_Name;
----------
-- Name --
----------
function Name (Self : Feature) return String is
begin
if Self.Name = null then
return "";
end if;
return Self.Name.all;
end Name;
--------------
-- Set_File --
--------------
procedure Set_File
(Self : in out Feature; File : GNATCOLL.VFS.Virtual_File) is
begin
Self.File := File;
end Set_File;
----------
-- File --
----------
function File (Self : Feature) return GNATCOLL.VFS.Virtual_File is
begin
return Self.File;
end File;
---------
-- Add --
---------
procedure Add (Self : in out Feature; Scenar : Scenario'Class) is
begin
Self.Scenarios.Append (Scenar);
end Add;
----------
-- Free --
----------
procedure Free (Self : in out Scenario) is
begin
Self.Name := Null_Unbounded_String;
Self.Line := 1;
Self.Index := 1;
Self.Outline := False;
end Free;
--------------
-- Set_Name --
--------------
procedure Set_Name
(Self : in out Scenario;
Name : String;
Line : Positive;
Index : Positive)
is
begin
Self.Name := To_Unbounded_String (Trim (Name, Both));
Self.Line := Line;
Self.Index := Index;
end Set_Name;
--------------------
-- Set_Is_Outline --
--------------------
procedure Set_Is_Outline (Self : in out Scenario) is
begin
Self.Outline := True;
end Set_Is_Outline;
end BDD.Features;

97
src/bdd-features.ads Normal file
View File

@ -0,0 +1,97 @@
-----------------------------------------------------------------------------
-- g N A T C O L L --
-- --
-- Copyright (C) 2014, AdaCore --
-- --
-- This library is free software; you can redistribute it and/or modify it --
-- under terms of the GNU General Public License as published by the Free --
-- Software Foundation; either version 3, or (at your option) any later --
-- version. This library is distributed in the hope that it will be useful, --
-- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- --
-- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. --
-- --
-- As a special exception under Section 7 of GPL version 3, you are granted --
-- additional permissions described in the GCC Runtime Library Exception, --
-- version 3.1, as published by the Free Software Foundation. --
-- --
-- You should have received a copy of the GNU General Public License and --
-- a copy of the GCC Runtime Library Exception along with this program; --
-- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see --
-- <http://www.gnu.org/licenses/>. --
-- --
------------------------------------------------------------------------------
-- A feature and its scenarios.
with Ada.Containers.Indefinite_Doubly_Linked_Lists;
with Ada.Strings.Unbounded; use Ada.Strings.Unbounded;
with GNAT.Strings; use GNAT.Strings;
with GNATCOLL.VFS; use GNATCOLL.VFS;
package BDD.Features is
--------------
-- Scenario --
--------------
type Scenario is tagged private;
-- A scenario to be run within a feature
procedure Free (Self : in out Scenario);
-- Free the memory associated with Self
procedure Set_Name
(Self : in out Scenario;
Name : String;
Line : Positive;
Index : Positive);
-- Set the line of the feature file at which the scenario is defined, and
-- its index within its Feature.
procedure Set_Is_Outline (Self : in out Scenario);
-- The scenario is in fact an outline, which will run itself multiple
-- times and use values from a table to generate each test.
-------------
-- Feature --
-------------
type Feature is tagged limited private;
-- An object that represents a single feature and all its scenarios.
-- A specific features file might contain several features, but this object
-- only represents one of them.
procedure Free (Self : in out Feature);
-- Free the memory associated with Self
procedure Set_Name (Self : in out Feature; Name : String);
function Name (Self : Feature) return String;
-- The name of the feature
procedure Set_File
(Self : in out Feature; File : GNATCOLL.VFS.Virtual_File);
function File (Self : Feature) return GNATCOLL.VFS.Virtual_File;
-- The file in which the feature is defined
procedure Add (Self : in out Feature; Scenar : Scenario'Class);
-- Add a copy of the scenario to the feature
private
type Scenario is tagged record
Name : Ada.Strings.Unbounded.Unbounded_String;
Line : Positive := 1;
Index : Positive := 1;
Outline : Boolean := False;
end record;
-- Make sure this type can be put in a list and automatically reclaim
-- storage when the list is clearer.
package Scenario_Lists is new Ada.Containers.Indefinite_Doubly_Linked_Lists
(Scenario'Class);
type Feature is tagged limited record
File : GNATCOLL.VFS.Virtual_File;
Name : GNAT.Strings.String_Access;
Scenarios : Scenario_Lists.List;
end record;
end BDD.Features;

295
src/bdd-parser.adb Normal file
View File

@ -0,0 +1,295 @@
-----------------------------------------------------------------------------
-- g N A T C O L L --
-- --
-- Copyright (C) 2014, AdaCore --
-- --
-- This library is free software; you can redistribute it and/or modify it --
-- under terms of the GNU General Public License as published by the Free --
-- Software Foundation; either version 3, or (at your option) any later --
-- version. This library is distributed in the hope that it will be useful, --
-- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- --
-- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. --
-- --
-- As a special exception under Section 7 of GPL version 3, you are granted --
-- additional permissions described in the GCC Runtime Library Exception, --
-- version 3.1, as published by the Free Software Foundation. --
-- --
-- You should have received a copy of the GNU General Public License and --
-- a copy of the GCC Runtime Library Exception along with this program; --
-- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see --
-- <http://www.gnu.org/licenses/>. --
-- --
------------------------------------------------------------------------------
with GNATCOLL.Traces; use GNATCOLL.Traces;
with GNATCOLL.Utils; use GNATCOLL.Utils;
with GNAT.Strings; use GNAT.Strings;
package body BDD.Parser is
Me : constant Trace_Handle := Create ("BDD.PARSER");
Cst_Features : constant String := "Feature:";
Cst_Scenario : constant String := "Scenario:";
Cst_Scenario_Outline : constant String := "Scenario Outline:";
Cst_Given : constant String := "Given";
Cst_And : constant String := "And";
Cst_Then : constant String := "Then";
Cst_But : constant String := "But";
Cst_When : constant String := "When";
Cst_Background : constant String := "Background";
Cst_Examples : constant String := "Examples:";
Cst_Scenarios : constant String := "Scenarios:";
-- The keywords when parsing a file
-----------
-- Parse --
-----------
procedure Parse
(Self : Feature_Parser;
File : GNATCOLL.VFS.Virtual_File;
Callback : access procedure (F : BDD.Features.Feature))
is
pragma Unreferenced (Self);
type State_Type is (None, In_Feature, In_Scenario, In_String,
In_Outline, In_Examples);
State : State_Type := None;
F : Feature;
Scenar : Scenario;
Buffer : GNAT.Strings.String_Access := File.Read_File;
Index : Integer := Buffer'First;
Line : Natural := 0;
Index_In_Feature : Natural := 0;
String_Indent : Natural := 0; -- indentation of the mutli-line string
Line_S, Line_E : Integer; -- extents of the current line
First_Char : Integer; -- first non-blank character in line
String_Line_Start : Integer; -- first line of multi-line string
procedure Finish_Scenario;
procedure Finish_Feature;
-- Called when the end of a scenario or a feature are seen
---------------------
-- Finish_Scenario --
---------------------
procedure Finish_Scenario is
begin
case State is
when In_Scenario | In_Outline | In_Examples =>
F.Add (Scenar);
Free (Scenar);
State := In_Feature;
when others =>
null;
end case;
end Finish_Scenario;
--------------------
-- Finish_Feature --
--------------------
procedure Finish_Feature is
begin
Finish_Scenario;
if State /= None then
Callback (F);
Free (F);
end if;
State := None;
end Finish_Feature;
begin
Trace (Me, "Parsing " & File.Display_Full_Name);
while Index < Buffer'Last loop
Line_S := Index;
Line_E := Line_End (Buffer.all, Line_S);
Index := Line_E + 1;
if Buffer (Index) = ASCII.CR then
Index := Index + 1;
end if;
if Buffer (Index) = ASCII.LF then
Index := Index + 1;
end if;
Line := Line + 1;
if Active (Me) then
Trace (Me, "Line " & Image (Line, 3, Padding => ' ')
& " " & Buffer (Line_S .. Line_E));
end if;
First_Char := Line_S;
Skip_Blanks (Buffer (Line_S .. Line_E), First_Char);
if Starts_With (Buffer (First_Char .. Line_E), """""""") then
if State = In_String then
State := In_Scenario;
elsif State = In_Scenario then
State := In_String;
String_Indent := First_Char - Line_S;
String_Line_Start := Line;
else
raise Syntax_Error with "Multi-line strings only allowed in"
& " steps, at " & File.Display_Full_Name & ":"
& Image (Line, 1);
end if;
elsif State = In_String then
if First_Char < Line_S + String_Indent - 1 then
Trace (Me, "MANU String="
& Buffer (First_Char .. Line_E));
elsif Line_S + String_Indent - 1 <= Line_E then
Trace (Me, "MANU String="
& Buffer (Line_S + String_Indent - 1 .. Line_E));
else
Trace (Me, "MANU String=");
end if;
elsif First_Char > Line_E then
-- ignore blank lines
null;
elsif Buffer (First_Char) = '|' then
case State is
when In_Scenario | In_Outline =>
Trace (Me, "MANU Table=" & Buffer (First_Char .. Line_E));
when In_Examples =>
Trace (Me, "MANU Example=" & Buffer (First_Char .. Line_E));
when others =>
raise Syntax_Error with "Tables only allowed in"
& " steps, at " & File.Display_Full_Name & ":"
& Image (Line, 1);
end case;
elsif Buffer (First_Char) = '@' then
-- ??? parse tags
null;
elsif Buffer (First_Char) = '#' then
-- Ignore comment lines
null;
elsif Starts_With (Buffer (First_Char .. Line_E), Cst_Features) then
Finish_Feature;
F.Set_File (File);
F.Set_Name (Buffer (First_Char + Cst_Features'Length .. Line_E));
Index_In_Feature := 0;
State := In_Feature;
elsif Starts_With (Buffer (First_Char .. Line_E), Cst_Background) then
if State = None then
raise Syntax_Error with "Background must be defined within a"
& " Feature at " & File.Display_Full_Name & ":"
& Image (Line, 1);
end if;
Finish_Scenario;
-- ??? handle background
null;
State := In_Scenario;
elsif Starts_With (Buffer (First_Char .. Line_E), Cst_Scenario) then
if State = None then
raise Syntax_Error with "Scenario must be defined within a"
& " Feature at " & File.Display_Full_Name & ":"
& Image (Line, 1);
end if;
Finish_Scenario;
Index_In_Feature := Index_In_Feature + 1;
Scenar.Set_Name
(Name => Buffer (First_Char + Cst_Scenario'Length .. Line_E),
Line => Line,
Index => Index_In_Feature);
State := In_Scenario;
elsif Starts_With (Buffer (First_Char .. Line_E),
Cst_Scenario_Outline)
then
if State = None then
raise Syntax_Error with
"Scenario Outline must be defined within a Feature at "
& File.Display_Full_Name & ":" & Image (Line, 1);
end if;
Finish_Scenario;
Index_In_Feature := Index_In_Feature + 1;
Scenar.Set_Is_Outline;
Scenar.Set_Name
(Name => Buffer (First_Char + Cst_Scenario'Length .. Line_E),
Line => Line,
Index => Index_In_Feature);
State := In_Outline;
elsif Starts_With (Buffer (First_Char .. Line_E), Cst_Scenarios) then
if State /= In_Outline then
raise Syntax_Error with
"Scenarios must be defined within a Scenario Outline, at "
& File.Display_Full_Name & ":" & Image (Line, 1);
end if;
State := In_Examples;
elsif Starts_With (Buffer (First_Char .. Line_E), Cst_Examples) then
if State /= In_Outline then
raise Syntax_Error with
"Examples must be defined within a Scenario Outline, at "
& File.Display_Full_Name & ":" & Image (Line, 1);
end if;
State := In_Examples;
elsif Starts_With (Buffer (First_Char .. Line_E), Cst_Given)
or else Starts_With (Buffer (First_Char .. Line_E), Cst_And)
or else Starts_With (Buffer (First_Char .. Line_E), Cst_Then)
or else Starts_With (Buffer (First_Char .. Line_E), Cst_But)
or else Starts_With (Buffer (First_Char .. Line_E), Cst_When)
then
if State /= In_Scenario
and then State /= In_Outline
then
raise Syntax_Error with "Step must be defined with in a"
& " Scenario at " & File.Display_Full_Name & ":"
& Image (Line, 1);
end if;
-- ??? Should handle the step
null;
else
if State = None then
raise Syntax_Error with "Expected line starting with "
& Cst_Features & " at " & File.Display_Full_Name & ":"
& Image (Line, 1);
elsif State = In_Feature then
-- Ignored, these are the comments for the feature
null;
elsif State = In_Scenario
or else State = In_Outline
then
raise Syntax_Error with "Expected line starting with Given/And/"
& "But/When/Then at " & File.Display_Full_Name & ":"
& Image (Line, 1);
end if;
end if;
end loop;
if State = In_String then
raise Syntax_Error with "Multi-line string must end with """""" at "
& File.Display_Full_Name & ":" & Image (String_Line_Start, 1);
end if;
Finish_Feature;
Free (Buffer);
end Parse;
end BDD.Parser;

49
src/bdd-parser.ads Normal file
View File

@ -0,0 +1,49 @@
-----------------------------------------------------------------------------
-- g N A T C O L L --
-- --
-- Copyright (C) 2014, AdaCore --
-- --
-- This library is free software; you can redistribute it and/or modify it --
-- under terms of the GNU General Public License as published by the Free --
-- Software Foundation; either version 3, or (at your option) any later --
-- version. This library is distributed in the hope that it will be useful, --
-- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- --
-- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. --
-- --
-- As a special exception under Section 7 of GPL version 3, you are granted --
-- additional permissions described in the GCC Runtime Library Exception, --
-- version 3.1, as published by the Free Software Foundation. --
-- --
-- You should have received a copy of the GNU General Public License and --
-- a copy of the GCC Runtime Library Exception along with this program; --
-- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see --
-- <http://www.gnu.org/licenses/>. --
-- --
------------------------------------------------------------------------------
-- A parser for the features files
with BDD.Features; use BDD.Features;
with GNATCOLL.VFS; use GNATCOLL.VFS;
package BDD.Parser is
Syntax_Error : exception;
-- Raised when reading one of the features file raises a syntax error.
type Feature_Parser is tagged private;
procedure Parse
(Self : Feature_Parser;
File : GNATCOLL.VFS.Virtual_File;
Callback : access procedure (F : BDD.Features.Feature));
-- Parses a .feature file.
-- Calls Callback for each of the features found.
-- Raises Syntax_Error when the file does not contain valid syntax.
private
type Feature_Parser is tagged record
null;
end record;
end BDD.Parser;

100
src/bdd-runner.adb Normal file
View File

@ -0,0 +1,100 @@
-----------------------------------------------------------------------------
-- g N A T C O L L --
-- --
-- Copyright (C) 2005-2014, AdaCore --
-- --
-- This library is free software; you can redistribute it and/or modify it --
-- under terms of the GNU General Public License as published by the Free --
-- Software Foundation; either version 3, or (at your option) any later --
-- version. This library is distributed in the hope that it will be useful, --
-- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- --
-- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. --
-- --
-- As a special exception under Section 7 of GPL version 3, you are granted --
-- additional permissions described in the GCC Runtime Library Exception, --
-- version 3.1, as published by the Free Software Foundation. --
-- --
-- You should have received a copy of the GNU General Public License and --
-- a copy of the GCC Runtime Library Exception along with this program; --
-- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see --
-- <http://www.gnu.org/licenses/>. --
-- --
------------------------------------------------------------------------------
with BDD.Parser; use BDD.Parser;
package body BDD.Runner is
--------------
-- Discover --
--------------
procedure Discover
(Self : in out Feature_Runner;
Extension : Filesystem_String := ".feature";
Directory : GNATCOLL.VFS.Virtual_File := Create_From_Base ("features"))
is
Files : File_Array_Access;
begin
if Directory.Is_Directory then
Files := Directory.Read_Dir (Files_Only);
for F in Files'Range loop
if Files (F).File_Extension = Extension then
Self.Register (Files (F));
end if;
end loop;
Unchecked_Free (Files);
Files := Directory.Read_Dir (Dirs_Only);
for F in Files'Range loop
if Files (F).Base_Name /= "."
and then Files (F).Base_Name /= ".."
then
Self.Discover (Extension, Files (F));
end if;
end loop;
Unchecked_Free (Files);
end if;
end Discover;
--------------
-- Register --
--------------
procedure Register
(Self : in out Feature_Runner;
File : GNATCOLL.VFS.Virtual_File) is
begin
Append (Self.Files, File);
end Register;
--------------
-- Register --
--------------
procedure Register
(Self : in out Feature_Runner;
Files : GNATCOLL.VFS.File_Array) is
begin
Append (Self.Files, Files);
end Register;
--------------
-- For_Each --
--------------
procedure For_Each
(Self : in out Feature_Runner;
Callback : access procedure (F : Feature))
is
Parser : Feature_Parser;
begin
if Self.Files /= null then
Sort (Self.Files.all);
for F in Self.Files'Range loop
Parser.Parse (Self.Files (F), Callback);
end loop;
end if;
end For_Each;
end BDD.Runner;

62
src/bdd-runner.ads Normal file
View File

@ -0,0 +1,62 @@
-----------------------------------------------------------------------------
-- g N A T C O L L --
-- --
-- Copyright (C) 2014, AdaCore --
-- --
-- This library is free software; you can redistribute it and/or modify it --
-- under terms of the GNU General Public License as published by the Free --
-- Software Foundation; either version 3, or (at your option) any later --
-- version. This library is distributed in the hope that it will be useful, --
-- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- --
-- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. --
-- --
-- As a special exception under Section 7 of GPL version 3, you are granted --
-- additional permissions described in the GCC Runtime Library Exception, --
-- version 3.1, as published by the Free Software Foundation. --
-- --
-- You should have received a copy of the GNU General Public License and --
-- a copy of the GCC Runtime Library Exception along with this program; --
-- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see --
-- <http://www.gnu.org/licenses/>. --
-- --
------------------------------------------------------------------------------
-- Manipulating features files
with BDD.Features; use BDD.Features;
with GNATCOLL.VFS; use GNATCOLL.VFS;
package BDD.Runner is
type Feature_Runner is tagged private;
-- This type is responsible for running each of the features that are
-- registered.
-- You can either register features files explicitly, or by using the
-- Discover procedure below.
procedure Discover
(Self : in out Feature_Runner;
Extension : Filesystem_String := ".feature";
Directory : GNATCOLL.VFS.Virtual_File := Create_From_Base ("features"));
-- Analyze Directory recursively to find all features files.
-- Extension is the file extension for such files.
procedure Register
(Self : in out Feature_Runner;
File : GNATCOLL.VFS.Virtual_File);
procedure Register
(Self : in out Feature_Runner;
Files : GNATCOLL.VFS.File_Array);
-- Register one or more features file explicitly.
procedure For_Each
(Self : in out Feature_Runner;
Callback : access procedure (F : Feature));
-- Calls Callback for each of the registered features file.
private
type Feature_Runner is tagged record
Files : GNATCOLL.VFS.File_Array_Access;
end record;
end BDD.Runner;

26
src/bdd.ads Normal file
View File

@ -0,0 +1,26 @@
-----------------------------------------------------------------------------
-- g N A T C O L L --
-- --
-- Copyright (C) 2005-2014, AdaCore --
-- --
-- This library is free software; you can redistribute it and/or modify it --
-- under terms of the GNU General Public License as published by the Free --
-- Software Foundation; either version 3, or (at your option) any later --
-- version. This library is distributed in the hope that it will be useful, --
-- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- --
-- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. --
-- --
-- As a special exception under Section 7 of GPL version 3, you are granted --
-- additional permissions described in the GCC Runtime Library Exception, --
-- version 3.1, as published by the Free Software Foundation. --
-- --
-- You should have received a copy of the GNU General Public License and --
-- a copy of the GCC Runtime Library Exception along with this program; --
-- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see --
-- <http://www.gnu.org/licenses/>. --
-- --
------------------------------------------------------------------------------
package BDD is
end BDD;

View File

@ -2,10 +2,23 @@
-- An example test driver.
-- Such code would be automatically generated by gnattdd
with GNATTDD.Features; use GNATTDD.Features;
with Ada.Text_IO; use Ada.Text_IO;
with BDD.Runner; use BDD.Runner;
with BDD.Features; use BDD.Features;
with GNATCOLL.Traces; use GNATCOLL.Traces;
with GNATCOLL.VFS; use GNATCOLL.VFS;
procedure Driver is
begin
GNATTDD.Features.Discover_Features_Files (".");
procedure Run_Feature (F : Feature);
procedure Run_Feature (F : Feature) is
begin
Put_Line ("MANU Found feature: " & F.File.Display_Full_Name
& " => " & F.Name);
end Run_Feature;
Features : BDD.Runner.Feature_Runner;
begin
GNATCOLL.Traces.Parse_Config_File;
Features.Discover (".feature", Create_From_Base ("features"));
Features.For_Each (Run_Feature'Access);
end Driver;

View File

@ -1,15 +0,0 @@
package GNATTDD.Features is
type Feature_Runner is tagged private;
procedure Discover_Features
(Self : in out Feature_Runner;
Callback
private
type Feature_Runner is tagged record
null;
end record;
end GNATTDD.Features;

View File

@ -1,2 +0,0 @@
package GNATTDD is
end GNATTDD;

View File

@ -1,4 +1,4 @@
procedure GNATTDD.Main is
procedure Main is
begin
-- This subprogram generates the test driver by including all the
@ -6,4 +6,4 @@ begin
-- steps, regular epressions and mockups.
null;
end GNATTDD.Main;
end Main;