Moved code for running tests around.
authorStanislaw Klekot <dozzie@jarowit.net>
Mon, 15 Jun 2015 20:01:51 +0000 (22:01 +0200)
committerStanislaw Klekot <dozzie@jarowit.net>
Mon, 15 Jun 2015 20:01:51 +0000 (22:01 +0200)
Now it gets much prettier call stacks on dying test cases.

bin/estap
src/estap_file.erl
src/estap_server.erl
src/estap_test.erl [new file with mode: 0644]

index 2cb9b74..65a32be 100755 (executable)
--- a/bin/estap
+++ b/bin/estap
@@ -10,72 +10,8 @@ main(["--help"]) ->
 main([File]) ->
   {ok, {_Module, Forms}} = estap_file:read_file(File, []),
   {ok, {Plan, Tests}} = estap_file:load_code(Forms),
-  TestRunId = case Plan of
-    no_plan -> estap_server:no_plan();
-    {plan, C} -> estap_server:plan(C)
-  end,
-  run_tests(Tests, TestRunId).
-
-%%----------------------------------------------------------------------------
-
-run_tests([] = _Tests, TestRunId) ->
-  estap_server:done(TestRunId),
-  ok;
-run_tests([{{Mod, Func}, Description, Status} | Rest] = _Tests, TestRunId) ->
-  % TODO: bail out support
-  estap_server:running(TestRunId, Description),
-  case Status of
-    run ->
-      case run_test(Mod, Func) of
-        {success, _Result} ->
-          estap_server:test_passed(TestRunId);
-        {failure, Result} ->
-          estap_server:test_failed(TestRunId, Result);
-        {dubious, Result} ->
-          estap_server:dubious_result(TestRunId, Result);
-        {died, Reason} ->
-          estap_server:test_died(TestRunId, Reason)
-      end;
-    {skip, Why} ->
-      estap_server:test_skipped(TestRunId, Why);
-    {todo, Why} ->
-      estap_server:test_todo(TestRunId, Why)
-  end,
-  run_tests(Rest, TestRunId).
-
-%%----------------------------------------------------------------------------
-
-run_test(Mod, Func) ->
-  Self = self(),
-  ResultRef = make_ref(),
-  {Pid, MonitorRef} = spawn_monitor(
-    fun() ->
-      TestResult = try Mod:Func() of
-        ok          -> {success, ok};
-        {ok, Value} -> {success, {ok, Value}};
-        true        -> {success, true};
-        error           -> {failure, error};
-        {error, Reason} -> {failure, {error, Reason}};
-        false           -> {failure, false};
-        Result          -> {dubious, Result}
-      catch
-        throw:ok          -> {success, ok};
-        throw:{ok, Value} -> {success, {ok, Value}};
-        throw:true        -> {success, true};
-        throw:error           -> {failure, error};
-        throw:{error, Reason} -> {failure, {error, Reason}};
-        throw:false           -> {failure, false}
-      end,
-      Self ! {result, ResultRef, TestResult}
-    end
-  ),
-  receive
-    {result, ResultRef, TestResult} ->
-      erlang:demonitor(MonitorRef, [flush]),
-      TestResult;
-    {'DOWN', MonitorRef, process, Pid, Reason} ->
-      {died, Reason}
-  end.
+  estap_test:run(Plan, Tests),
+  ok.
 
 %%----------------------------------------------------------------------------
 %% vim:ft=erlang
index 2c3c5b9..f9d9b3d 100644 (file)
   skip = false :: {true, Reason :: string()} | false
 }).
 
--type test() :: {Func :: {module(), atom()}, Description :: string(),
-                  Status :: run | {todo | skip, Why :: string()}}.
-
--type test_plan() :: {plan, pos_integer()} | no_plan.
-
 %%% }}}
 %%%---------------------------------------------------------------------------
 %%% public interface
@@ -166,7 +161,8 @@ parse_file(File, IncludePath) ->
 %%   Function returns list of tests to run, in order of their appearance.
 
 -spec load_code([erl_parse:abstract_form()]) ->
-  {ok, {test_plan(), [test()]}} | {error, sticky_directory | not_purged}.
+    {ok, {estap_test:test_plan(), [estap_test:test()]}}
+  | {error, sticky_directory | not_purged}.
 
 load_code(Forms) ->
   Exports = sets:from_list(exports(Forms)),
index e505c6a..72a6237 100644 (file)
@@ -22,6 +22,8 @@
 -export([handle_call/3, handle_cast/2, handle_info/2]).
 -export([code_change/3]).
 
+-export_type([test_run_id/0]).
+
 %%%---------------------------------------------------------------------------
 
 -type test_run_id() :: pid().
diff --git a/src/estap_test.erl b/src/estap_test.erl
new file mode 100644 (file)
index 0000000..35af1c9
--- /dev/null
@@ -0,0 +1,126 @@
+%%%---------------------------------------------------------------------------
+%%% @doc
+%%%   Functions to use when running test cases.
+%%% @end
+%%%---------------------------------------------------------------------------
+
+-module(estap_test).
+
+%% public interface
+-export([run/2]).
+
+%% private interface
+-export([call/4]).
+
+-export_type([test/0, test_plan/0]).
+
+%%%---------------------------------------------------------------------------
+%%% types {{{
+
+-type test() :: {Func :: {module(), atom()}, Description :: string(),
+                  Status :: run | {todo | skip, Why :: string()}}.
+
+-type test_plan() :: {plan, pos_integer()} | no_plan.
+
+%%% }}}
+%%%---------------------------------------------------------------------------
+%%% public interface
+%%%---------------------------------------------------------------------------
+
+%%----------------------------------------------------------
+
+%% @doc Run tests according to plan.
+
+-spec run(test_plan(), [test()]) ->
+  ok.
+
+run(Plan, Tests) ->
+  TestRun = case Plan of
+    no_plan   -> estap_server:no_plan();
+    {plan, C} -> estap_server:plan(C)
+  end,
+  run_tests(TestRun, Tests),
+  estap_server:done(TestRun).
+
+%% @doc Run tests, one by one, reporting their results to tracking process.
+%%
+%% @TODO Return something meaningful.
+
+-spec run_tests(estap_server:test_run_id(), [test()]) ->
+  ok.
+
+run_tests(_TestRun, [] = _Tests) ->
+  ok;
+run_tests(TestRun, [{TestFunSpec, Description, Status} | Rest] = _Tests) ->
+  estap_server:running(TestRun, Description),
+  case Status of
+    run ->
+      case test(TestFunSpec) of
+        {success, _Result} ->
+          estap_server:test_passed(TestRun);
+        {failure, Result} ->
+          estap_server:test_failed(TestRun, Result);
+        {dubious, Result} ->
+          estap_server:dubious_result(TestRun, Result);
+        {died, Reason} ->
+          estap_server:test_died(TestRun, Reason)
+      end;
+    {skip, Why} ->
+      estap_server:test_skipped(TestRun, Why);
+    {todo, Why} ->
+      estap_server:test_todo(TestRun, Why)
+  end,
+  run_tests(TestRun, Rest).
+
+%% @doc Run a single test function, according to its specification.
+
+-spec test({Module :: module(), Function :: atom()}) ->
+    {success, term()}
+  | {failure, term()}
+  | {dubious, term()}
+  | {died, term()}.
+
+test({Mod, Func} = _TestFunSpec) ->
+  Args = [],
+  ResultRef = make_ref(),
+  ResultTo = {self(), ResultRef},
+  {Pid, MonRef} = spawn_monitor(?MODULE, call, [ResultTo, Mod, Func, Args]),
+  receive
+    {result, ResultRef, TestResult} ->
+      erlang:demonitor(MonRef, [flush]),
+      TestResult;
+    {'DOWN', MonRef, process, Pid, Reason} ->
+      {died, Reason}
+  end.
+
+%% @private
+%% @doc Run the specified function, collect its result (possibly thrown) and
+%%   report it back to `ResultTo'.
+
+-spec call({pid(), term()}, module(), atom(), [term()]) ->
+  ok.
+
+call({Pid, Ref} = _ResultTo, Mod, Fun, Args) ->
+  TestResult = try apply(Mod, Fun, Args) of
+    ok          -> {success, ok};
+    {ok, Value} -> {success, {ok, Value}};
+    true        -> {success, true};
+    error           -> {failure, error};
+    {error, Reason} -> {failure, {error, Reason}};
+    false           -> {failure, false};
+    Result          -> {dubious, Result}
+  catch
+    throw:ok          -> {success, ok};
+    throw:{ok, Value} -> {success, {ok, Value}};
+    throw:true        -> {success, true};
+    throw:error           -> {failure, error};
+    throw:{error, Reason} -> {failure, {error, Reason}};
+    throw:false           -> {failure, false}
+  end,
+  Pid ! {result, Ref, TestResult},
+  ok.
+
+%%----------------------------------------------------------
+
+%%%---------------------------------------------------------------------------
+%%% vim:ft=erlang:foldmethod=marker