---
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.