Added running TODO tests.
[erlang-estap.git] / src / estap_server.erl
1 %%%---------------------------------------------------------------------------
2 %%% @doc
3 %%%   Input/output and test plan tracking process.
4 %%% @end
5 %%%---------------------------------------------------------------------------
6
7 -module(estap_server).
8
9 -behaviour(gen_server).
10
11 %% public interface
12 -export([no_plan/0, plan/1, done/1]).
13 -export([running/2]).
14 -export([test_skipped/2, test_todo/3]).
15 -export([test_passed/1, test_failed/2, dubious_result/2, test_died/2]).
16
17 %% supervision tree API
18 -export([start/1, start_link/1]).
19
20 %% gen_server callbacks
21 -export([init/1, terminate/2]).
22 -export([handle_call/3, handle_cast/2, handle_info/2]).
23 -export([code_change/3]).
24
25 -export_type([test_run_id/0]).
26
27 %%%---------------------------------------------------------------------------
28
29 -type test_run_id() :: pid().
30
31 -type plan() :: no_plan | pos_integer().
32
33 -record(state, {
34   plan :: plan(),
35   test :: {TestNo :: pos_integer(), Description :: string()}
36 }).
37
38 %%%---------------------------------------------------------------------------
39 %%% public interface
40 %%%---------------------------------------------------------------------------
41
42 %% @doc "No plan" plan.
43
44 -spec no_plan() ->
45   test_run_id().
46
47 no_plan() ->
48   {ok, Pid} = start_link(no_plan),
49   Pid.
50
51 %% @doc Test plan.
52
53 -spec plan(pos_integer()) ->
54   test_run_id().
55
56 plan(TestCount) when is_integer(TestCount) ->
57   {ok, Pid} = start_link(TestCount),
58   Pid.
59
60 %% @doc Mark the end of tests in this run.
61
62 -spec done(test_run_id()) ->
63   ok.
64
65 done(TestRunId) ->
66   gen_server:call(TestRunId, done).
67
68 %% @doc Mark the beginning of new test.
69
70 -spec running(test_run_id(), string()) ->
71   ok.
72
73 running(TestRunId, Description) ->
74   gen_server:call(TestRunId, {next, Description}).
75
76 %% @doc Mark the end of a test with a success.
77
78 -spec test_passed(test_run_id()) ->
79   ok.
80
81 test_passed(TestRunId) ->
82   gen_server:call(TestRunId, {result, success}).
83
84 %% @doc Mark the end of a test with a failure.
85
86 -spec test_failed(test_run_id(), term()) ->
87   ok.
88
89 test_failed(TestRunId, ReturnValue) ->
90   gen_server:call(TestRunId, {result, {failure, ReturnValue}}).
91
92 %% @doc Mark the end of a test with a failure, but a dubious one.
93
94 -spec dubious_result(test_run_id(), term()) ->
95   ok.
96
97 dubious_result(TestRunId, ReturnValue) ->
98   gen_server:call(TestRunId, {result, {dubious, ReturnValue}}).
99
100 %% @doc Mark the end of a test with an exception.
101 %%   This means that the test function, which was called in `Pid' process,
102 %%   simply died.
103
104 -spec test_died(test_run_id(), term()) ->
105   ok.
106
107 test_died(TestRunId, Reason) ->
108   gen_server:call(TestRunId, {result, {died, Reason}}).
109
110 %% @doc Mark the test as skipped.
111
112 -spec test_skipped(test_run_id(), string()) ->
113   ok.
114
115 test_skipped(TestRunId, Reason) ->
116   gen_server:call(TestRunId, {skipped, Reason}).
117
118 %% @doc Mark the test as "TODO".
119
120 -spec test_todo(test_run_id(), term(), string()) ->
121   ok.
122
123 test_todo(TestRunId, Result, Why) ->
124   gen_server:call(TestRunId, {todo, Result, Why}).
125
126 %%%---------------------------------------------------------------------------
127 %%% supervision tree API
128 %%%---------------------------------------------------------------------------
129
130 %% @private
131 %% @doc Start the process.
132
133 -spec start(plan()) ->
134   {ok, pid()} | {error, term()}.
135
136 start(Plan) ->
137   gen_server:start(?MODULE, [Plan], []).
138
139 %% @private
140 %% @doc Start the process.
141
142 -spec start_link(plan()) ->
143   {ok, pid()} | {error, term()}.
144
145 start_link(Plan) ->
146   gen_server:start_link(?MODULE, [Plan], []).
147
148 %%%---------------------------------------------------------------------------
149 %%% gen_server callbacks
150 %%%---------------------------------------------------------------------------
151
152 %%----------------------------------------------------------
153 %% initialization/termination {{{
154
155 %% @private
156 %% @doc Initialize event handler.
157
158 init([Plan] = _Args) ->
159   case Plan of
160     no_plan -> skip;
161     C when is_integer(C) -> io:fwrite("1..~B~n", [C])
162   end,
163   State = #state{plan = Plan},
164   {ok, State}.
165
166 %% @private
167 %% @doc Clean up after event handler.
168
169 terminate(_Arg, _State) ->
170   ok.
171
172 %% }}}
173 %%----------------------------------------------------------
174 %% communication {{{
175
176 %% @private
177 %% @doc Handle {@link gen_server:call/2}.
178
179 handle_call(done = _Request, _From,
180             State = #state{plan = Plan, test = {LastTestNo, _}}) ->
181   case Plan of
182     no_plan -> io:fwrite("1..~B~n", [LastTestNo]);
183     C when is_integer(C) -> io:fwrite("# ran ~B of ~B tests~n", [LastTestNo, C])
184   end,
185   {stop, normal, ok, State};
186
187 handle_call({next, Desc} = _Request, _From,
188             State = #state{test = Test}) ->
189   NextTestNo = case Test of
190     undefined -> 1;
191     {TestNo, _} -> TestNo + 1
192   end,
193   NewState = State#state{test = {NextTestNo, Desc}},
194   {reply, ok, NewState};
195
196 handle_call({result, TestResult} = _Request, _From,
197             State = #state{test = {TestNo, TestDesc}}) ->
198   case TestResult of
199     success ->
200       io:fwrite("ok ~B - ~s~n", [TestNo, TestDesc]);
201     {failure, Reason} ->
202       io:fwrite("not ok ~B - ~s # result: ~s~n",
203                 [TestNo, TestDesc, format(Reason)]);
204     {dubious, Value} ->
205       io:fwrite("not ok ~B - ~s # dubious result: ~s~n",
206                 [TestNo, TestDesc, format(Value)]);
207     {died, Reason} ->
208       io:fwrite("not ok ~B - ~s # died: ~s~n",
209                 [TestNo, TestDesc, format(Reason)])
210   end,
211   {reply, ok, State};
212
213 handle_call({skipped, Reason} = _Request, _From,
214             State = #state{test = {TestNo, TestDesc}}) ->
215   io:fwrite("ok ~B - ~s # SKIP ~s~n", [TestNo, TestDesc, Reason]),
216   {reply, ok, State};
217
218 handle_call({todo, TestResult, Why} = _Request, _From,
219             State = #state{test = {TestNo, TestDesc}}) ->
220   case TestResult of
221     success ->
222       io:fwrite("ok ~B - ~s # TODO ~s~n", [TestNo, TestDesc, Why]);
223     {failure, _Reason} ->
224       io:fwrite("not ok ~B - ~s # TODO ~s~n", [TestNo, TestDesc, Why]);
225     {dubious, _Value} ->
226       io:fwrite("not ok ~B - ~s # TODO ~s~n", [TestNo, TestDesc, Why]);
227     {died, _Reason} ->
228       io:fwrite("not ok ~B - ~s # TODO ~s~n", [TestNo, TestDesc, Why])
229   end,
230   {reply, ok, State};
231
232 %% unknown calls
233 handle_call(_Request, _From, State) ->
234   {reply, {error, unknown_call}, State}.
235
236 %% @private
237 %% @doc Handle {@link gen_server:cast/2}.
238
239 %% unknown casts
240 handle_cast(_Request, State) ->
241   {noreply, State}.
242
243 %% @private
244 %% @doc Handle incoming messages.
245
246 %% unknown messages
247 handle_info(_Message, State) ->
248   {noreply, State}.
249
250 %% }}}
251 %%----------------------------------------------------------
252 %% code change {{{
253
254 %% @private
255 %% @doc Handle code change.
256
257 code_change(_OldVsn, State, _Extra) ->
258   {ok, State}.
259
260 %% }}}
261 %%----------------------------------------------------------
262
263 %% @doc Format term for printing to screen.
264
265 -spec format(term()) ->
266   iolist().
267
268 format(Term) ->
269   % no term should weigh 1MB
270   io_lib:print(Term, 1, 1024 * 1024, -1).
271
272 %%----------------------------------------------------------
273
274 %%%---------------------------------------------------------------------------
275 %%% vim:ft=erlang:foldmethod=marker