---
 features/sin_cucumber_support.feature |   22 +++++
 src/sin_task.erl                      |    3 +-
 src/sin_task_build.erl                |    3 -
 src/sin_task_cucumber.erl             |   81 ++++++++++++++++
 test/sin_cucumber_support.erl         |  166 +++++++++++++++++++++++++++++++++
 5 files changed, 271 insertions(+), 4 deletions(-)
 create mode 100644 features/sin_cucumber_support.feature
 create mode 100644 src/sin_task_cucumber.erl
 create mode 100644 test/sin_cucumber_support.erl

diff --git a/features/sin_cucumber_support.feature 
b/features/sin_cucumber_support.feature
new file mode 100644
index 0000000..46a24ae
--- /dev/null
+++ b/features/sin_cucumber_support.feature
@@ -0,0 +1,22 @@
+Feature: Support BDD with sinan
+  In order to make it easier to do behaviour driven development in sinan
+  As an Erlang Developer
+  I want to be able to put features in a features directory in the
+  project root and have those features be run by cumberl when I run a
+  "cucumber" command.
+
+  Scenario: Run cucumber on passing tests
+    Given a generated project
+    And a feature file in the features directory of that project
+    And an implementation of that feature that passes
+    When a cucumber step is run on this project
+    Then then sinan should run cucumberl on the features in the features 
directory
+    And report the build as passing
+
+  Scenario: Run cucumber on failing tests
+    Given a generated project
+    And a feature file in the features directory of that project
+    And an implementation of that feature that fails
+    When a cucumber step is run on this project
+
+    And report the build as failing
diff --git a/src/sin_task.erl b/src/sin_task.erl
index 8892975..5aeb806 100644
--- a/src/sin_task.erl
+++ b/src/sin_task.erl
@@ -63,7 +63,8 @@ get_tasks() ->
      sin_task_clean:description(),
      sin_task_build:description(),
      sin_task_xref:description(),
-     sin_task_erts:description()].
+     sin_task_erts:description(),
+     sin_task_cucumber:description()].
 
 %% @doc signal that an error has occured in the system
 -spec signal_error() -> ok.
diff --git a/src/sin_task_build.erl b/src/sin_task_build.erl
index 443197b..d3d9cb2 100644
--- a/src/sin_task_build.erl
+++ b/src/sin_task_build.erl
@@ -205,7 +205,6 @@ build_app(BuildRef, Cache, Env, AppName, Args) ->
 
     {EbinPaths, Includes} = setup_code_path(BuildRef2, Env, AppName),
 
-    ExistingPaths = code:get_path(),
     code:add_patha(Target),
     {NewCache, NewFileList} = process_source_files(BuildRef2,
                                                    Cache,
@@ -223,8 +222,6 @@ build_app(BuildRef, Cache, Env, AppName, Args) ->
     build_sources(BuildRef3, NewFileList,
                  Includes, Args, AppDir, Target),
 
-    % Do the source build
-    code:set_path(ExistingPaths),
     {NewCache, BuildRef3}.
 
 %% @doc go through each source file building with the correct build module.
diff --git a/src/sin_task_cucumber.erl b/src/sin_task_cucumber.erl
new file mode 100644
index 0000000..e7fe4b6
--- /dev/null
+++ b/src/sin_task_cucumber.erl
@@ -0,0 +1,81 @@
+%% -*- mode: Erlang; fill-column: 80; comment-column: 75; -*-
+%%%-------------------------------------------------------------------
+%%%---------------------------------------------------------------------------
+%%% @author Eric Merritt <[email protected]>
+%%% @doc
+%%%   Runs cucumber for any features that exist in the projecty
+%%% @end
+%%% @copyright (C) 2007-2011 Erlware
+%%%---------------------------------------------------------------------------
+-module(sin_task_cucumber).
+
+-behaviour(sin_task).
+
+-include("internal.hrl").
+-include_lib("cucumberl/include/cucumberl.hrl").
+
+%% API
+-export([description/0, do_task/1, format_exception/1]).
+
+-define(TASK, cucumber).
+-define(DEPS, [build]).
+
+%%====================================================================
+%% API
+%%====================================================================
+
+%% @doc provides a description of this task
+-spec description() ->  sin_task:task_description().
+description() ->
+    Desc = "Runs cucumberl for any features that exist in the system",
+    #task{name = ?TASK,
+          task_impl = ?MODULE,
+          bare = false,
+          deps = ?DEPS,
+          example = "test",
+          desc = Desc,
+          short_desc = "Runs all of the cucumber features in the project",
+          opts = []}.
+
+%% @doc run all the cucumber features in the system
+do_task(BuildRef) ->
+    ProjectRoot = sin_config:get_value(BuildRef, "project.dir"),
+    Features = find_files(filename:join([ProjectRoot, "features"]),
+                                        ".*\\.feature\$"),
+    Outcomes = [ run_feature(BuildRef, F) || F <- Features ],
+    case lists:all(fun(X) -> X =:= ok end, Outcomes) of
+        true    ->
+            ok;
+        false   ->
+            sin_error_store:signal_error(),
+            ok
+    end,
+    sin_config:store(BuildRef, "cucumber.features", Features).
+
+%% @doc Format an exception thrown by this module
+-spec format_exception(sin_exceptions:exception()) ->
+    string().
+format_exception(Exception) ->
+    sin_exceptions:format_exception(Exception).
+
+
+%%====================================================================
+%%% Internal functions
+%%====================================================================
+run_feature(BuildRef, FeatureFile) ->
+    try
+        case cucumberl:run(FeatureFile) of
+            {ok, _}  ->
+                ok;
+            {failed, #cucumberl_stats{failures=Failed}} ->
+                io:format("~p failed steps.~n", [length(Failed)]),
+                {failed, Failed}
+        end
+    catch
+        throw:{error, nofile} ->
+            ewl_talk:say("No behavior implementation for ~s", [FeatureFile]),
+            ?SIN_RAISE(BuildRef, {no_implementation, FeatureFile})
+    end.
+
+find_files(Dir, Regex) ->
+    filelib:fold_files(Dir, Regex, true, fun(F, Acc) -> [F | Acc] end, []).
diff --git a/test/sin_cucumber_support.erl b/test/sin_cucumber_support.erl
new file mode 100644
index 0000000..208f4c5
--- /dev/null
+++ b/test/sin_cucumber_support.erl
@@ -0,0 +1,166 @@
+%%%-------------------------------------------------------------------
+%%% @copyright (C) 2011, Erlware, LLC.
+%%% @doc
+%%%  Add the ability to test cucumber runs from sinan
+%%% @end
+%%% Created :  5 Sep 2011 by Eric Merritt <[email protected]>
+%%%-------------------------------------------------------------------
+-module(sin_cucumber_support).
+
+-export([given/3, 'when'/3, then/3]).
+
+-include_lib("eunit/include/eunit.hrl").
+
+%% Step definitions for the sample calculator Addition feature.
+
+given([a, generated, project], _, _) ->
+    {ok, BaseDir} = ewl_file:create_tmp_dir("/tmp"),
+    ProjectName = "foobachoo",
+    {ProjectDir, _} =
+        sin_test_project_gen:single_app_project(BaseDir, ProjectName),
+    {ok, {ProjectDir, ProjectName}};
+given([a, feature, file, in, the, features, directory, 'of',
+       that, project], {ProjectDir, ProjectName}, _) ->
+    ?assertMatch(ok,
+                 ewl_file:mkdir_p(filename:join([ProjectDir, "features"]))),
+    FeatureName = "test_feature",
+    FeaturePath = filename:join([ProjectDir, "features", FeatureName ++
+                                     ".feature"]),
+    file:write_file(FeaturePath, feature()),
+    {ok, {ProjectDir, ProjectName, FeatureName, FeaturePath}};
+given([an, implementation, 'of', that, feature, that, passes],
+      {ProjectDir, ProjectName, FeatureName, FeaturePath}, _) ->
+    write_impl(pass_feature_impl(), ProjectDir, FeatureName),
+    {ok, {ProjectDir, ProjectName, FeaturePath}};
+given([an, implementation, 'of', that, feature, that, fails],
+      {ProjectDir, ProjectName, FeatureName, FeaturePath}, _) ->
+    write_impl(fail_feature_impl(), ProjectDir, FeatureName),
+    {ok, {ProjectDir, ProjectName, FeaturePath}}.
+
+'when'([a, cucumber, step, is, run, on, this, project],
+       {ProjectDir, ProjectName, FeaturePath}, _) ->
+    Result = sinan:run_sinan(["-s", ProjectDir, "cucumber"]),
+    sin_test_project_gen:validate_single_app_project(ProjectDir, ProjectName),
+    {ok, {FeaturePath, Result}}.
+
+then([then, sinan, should, run, cucumberl, on,
+      the, features, in, the, features, directory],
+     State = {FeaturePath, {_, BuildResult}}, _) ->
+    ?assertMatch([FeaturePath],
+                 sin_config:get_value(BuildResult, "cucumber.features")),
+    {ok, State};
+then([report, the, build, as, failing],
+     State = {_, BuildResult}, _) ->
+    ?assertMatch({error, _}, BuildResult),
+    {ok, State};
+then([report, the, build, as, passing],
+     State = {_, BuildResult}, _) ->
+    ?assertMatch({ok, _}, BuildResult),
+    {ok, State}.
+
+%%====================================================================
+%% Internal functions
+%%====================================================================
+
+write_impl(Impl, ProjectDir, FeatureName) ->
+    ?assertMatch(ok,
+                 ewl_file:mkdir_p(filename:join([ProjectDir, "test"]))),
+    FeatureName = "test_feature",
+    FeatureImplPath = filename:join([ProjectDir, "test", FeatureName ++
+                                         ".erl"]),
+    file:write_file(FeatureImplPath, Impl),
+    FeatureImplPath.
+
+feature() ->
+    "Feature: Addition\n"
+        "  In order to avoid silly mistakes\n"
+        "  As a math idiot\n"
+        "  I want to be told the sum of two numbers\n"
+        "\n"
+        "  Scenario: Add two numbers\n"
+        "    Given I have entered 50 into the calculator\n"
+        "    And I have entered 70 into the calculator\n"
+        "    When I press add\n"
+        "    Then the result should be 120 on the screen\n".
+
+pass_feature_impl() ->
+    "-module(test_feature).\n"
+        "\n"
+        "-export([setup/0, teardown/1,\n"
+        "         given/3, 'when'/3, then/3]).\n"
+        "\n"
+        "-export([enter/2, press/2]).\n"
+        "\n"
+        "setup() ->\n"
+        "    [].\n"
+        "\n"
+        "%% Step definitions for the sample calculator Addition feature.\n"
+        "given([i, have, entered, N, into, the, calculator], State, _) ->\n"
+        "    {ok, enter(State, list_to_integer(atom_to_list(N)))}.\n"
+        "\n"
+        "'when'([i, press, Op], State, _) ->\n"
+        "    {ok, press(State, Op)}.\n"
+        "\n"
+        "then([the, result, should, be, Result, on, the, screen],\n"
+        "     State, _) ->\n"
+        "    list_to_integer(atom_to_list(Result)) =:=State.\n"
+        "\n"
+        "teardown(_State) ->\n"
+        "    ok.\n"
+        "\n"
+        "%% Implementing a simple model here...\n"
+        "\n"
+        "enter(State, N) ->\n"
+        "    [N|State].\n"
+        "\n"
+        "press(State, add) ->\n"
+        "    add(State);\n"
+        "press(State, multiply) ->\n"
+        "    multiply(State).\n"
+        "\n"
+        "add([X, Y]) ->\n"
+        "    X + Y.\n"
+        "\n"
+        "multiply([X, Y]) ->\n"
+        "    X * Y.\n".
+
+fail_feature_impl() ->
+    "-module(test_feature).\n"
+        "\n"
+        "-export([setup/0, teardown/1,\n"
+        "         given/3, 'when'/3, then/3]).\n"
+        "\n"
+        "-export([enter/2, press/2]).\n"
+        "\n"
+        "setup() ->\n"
+        "    [].\n"
+        "\n"
+        "%% Step definitions for the sample calculator Addition feature.\n"
+        "given([i, have, entered, N, into, the, calculator], State, _) ->\n"
+        "    {ok, enter(State, list_to_integer(atom_to_list(N)))}.\n"
+        "\n"
+        "'when'([i, press, Op], State, _) ->\n"
+        "    {ok, press(State, Op)}.\n"
+        "\n"
+        "then([the, result, should, be, Result, on, the, screen],\n"
+        "     State, _) ->\n"
+        "    list_to_integer(atom_to_list(Result)) =:=State.\n"
+        "\n"
+        "teardown(_State) ->\n"
+        "    ok.\n"
+        "\n"
+        "%% Implementing a simple model here...\n"
+        "\n"
+        "enter(State, N) ->\n"
+        "    [N|State].\n"
+        "\n"
+        "press(State, add) ->\n"
+        "    add(State);\n"
+        "press(State, multiply) ->\n"
+        "    multiply(State).\n"
+        "\n"
+        "add([X, Y]) ->\n"
+        "    X + Y + 22.\n"
+        "\n"
+        "multiply([X, Y]) ->\n"
+        "    X * Y.\n".
-- 
1.7.5.2

-- 
You received this message because you are subscribed to the Google Groups 
"erlware-dev" group.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to 
[email protected].
For more options, visit this group at 
http://groups.google.com/group/erlware-dev?hl=en.

Reply via email to