all_ok() returns more meaningful error report.
[erlang-estap.git] / src / estap.erl
1 %%%---------------------------------------------------------------------------
2 %%% @doc
3 %%%   Functions to use in test cases.
4 %%%
5 %%%   Test passes if it returns or throws (`throw()'): `ok', `{ok, Value}', or
6 %%%   `true'.
7 %%%
8 %%%   Test fails if it returns or throws `error', `{error, Reason}', `false',
9 %%%   or calls `exit(...)' or `erlang:error(...)' (or simply dies).
10 %%%
11 %%%   Any other returned value is also considered a failure, but a dubious
12 %%%   one. Stick to saying explicitly that the test failed.
13 %%%
14 %%%   All test functions return the value they were passed (an exception to
15 %%%   this rule are {@link pass/1} and {@link fail/1} functions, for obvious
16 %%%   reason). This allows test to be added around preparation function call
17 %%%   for more complex test cases.
18 %%% @end
19 %%%---------------------------------------------------------------------------
20
21 -module(estap).
22
23 %% public interface
24 -export([ok/2, not_ok/2, error/2]).
25 -export([pass/1, fail/1]).
26 -export([is/3, isnt/3, eq/3, ne/3, cmp/4]).
27 -export([like/3, unlike/3, matches/3]).
28 -export([bail_out/1, no_plan/0, plan/1, all_ok/0]).
29 -export([diag/1, diag/2, info/1, info/2, explain/1]).
30 -export([test_dir/0, test_dir/1]).
31
32 -export_type([value/0, cmp/0, regexp/0, match_fun/0]).
33
34 %%%---------------------------------------------------------------------------
35 %%% types {{{
36
37 -type value() :: term().
38
39 -type cmp() :: '<' | '>' | '=<' | '>=' | '/=' | '=/=' | '==' | '=:='.
40
41 -type regexp() :: iolist().
42
43 -type match_fun() :: fun((value()) -> any()).
44
45 -type message() :: iolist().
46
47 -type description() :: iolist().
48
49 -type info() :: atom() | iolist() |
50                 {FieldName :: atom() | iolist(), Value :: atom() | iolist()}.
51
52 %%% }}}
53 %%%---------------------------------------------------------------------------
54 %%% public interface
55 %%%---------------------------------------------------------------------------
56
57 %% @doc Check if `Value' is any of the recognized truth values.
58 %%
59 %% @see not_ok/2
60 %% @see error/2
61
62 -spec ok(value(), description()) ->
63   Value :: value().
64
65 ok(Value, Description) ->
66   report(estap_test:success_or_failure(Value), Description),
67   Value.
68
69 %% @doc Check if `Value' is none of the recognized truth values.
70 %%
71 %%   Dubious `Value' is also a success here.
72 %%
73 %% @see ok/2
74 %% @see error/2
75
76 -spec not_ok(value(), description()) ->
77   Value :: value().
78
79 not_ok(Value, Description) ->
80   case estap_test:success_or_failure(Value) of
81     {success, _} -> report({failure, Value}, Description);
82     {failure, _} -> report({success, Value}, Description);
83     {dubious, _} -> report({success, Value}, Description)
84   end,
85   Value.
86
87 %% @doc Check if `Value' is any of the recognized falsity values.
88 %%
89 %% @see ok/2
90 %% @see not_ok/2
91
92 -spec error(value(), description()) ->
93   Value :: value().
94
95 error(Value, Description) ->
96   case estap_test:success_or_failure(Value) of
97     {success, _} -> report({failure, Value}, Description);
98     {failure, _} -> report({success, Value}, Description);
99     {dubious, _} -> report({dubious, Value}, Description)
100   end,
101   Value.
102
103 %% @doc Mark the test as a success unconditionally.
104
105 -spec pass(description()) ->
106   true.
107
108 pass(Description) ->
109   report({success, explicit}, Description),
110   true.
111
112 %% @doc Mark the test as a failure unconditionally.
113
114 -spec fail(description()) ->
115   false.
116
117 fail(Description) ->
118   report({failure, explicit}, Description),
119   false.
120
121 %% @doc Check if `Value' is the same as `Expected'.
122
123 -spec is(value(), value(), description()) ->
124   Value :: value().
125
126 is(Value, Expected, Description) ->
127   case Value of
128     Expected -> report({success, Value}, Description);
129     _        -> report({failure, Value}, Description)
130   end,
131   Value.
132
133 %% @doc Check if `Value' is different than `Expected'.
134
135 -spec isnt(value(), value(), description()) ->
136   Value :: value().
137
138 isnt(Value, Expected, Description) ->
139   case Value of
140     Expected -> report({failure, Value}, Description);
141     _        -> report({success, Value}, Description)
142   end,
143   Value.
144
145 %% @doc Check if `Value' is equal (`==') to `Expected'.
146
147 -spec eq(value(), value(), description()) ->
148   Value :: value().
149
150 eq(Value, Expected, Description) ->
151   cmp(Value, '==', Expected, Description).
152
153 %% @doc Check if `Value' is not equal (`/=') to `Expected'.
154
155 -spec ne(value(), value(), description()) ->
156   Value :: value().
157
158 ne(Value, Expected, Description) ->
159   cmp(Value, '/=', Expected, Description).
160
161 %% @doc Compare `Value' and `Expected' using comparison operator.
162
163 -spec cmp(value(), cmp(), value(), description()) ->
164   Value :: value().
165
166 cmp(Value, Cmp, Expected, Description) ->
167   CmpResult = case Cmp of
168     '<'   -> Value <   Expected;
169     '>'   -> Value >   Expected;
170     '=<'  -> Value =<  Expected;
171     '>='  -> Value >=  Expected;
172     '/='  -> Value /=  Expected;
173     '=/=' -> Value =/= Expected;
174     '=='  -> Value ==  Expected;
175     '=:=' -> Value =:= Expected
176   end,
177   % FIXME: better reporting for failures
178   case CmpResult of
179     true  -> report({success, Value}, Description);
180     false -> report({failure, Value}, Description)
181   end,
182   Value.
183
184 %% @doc Check if `Value' matches a regexp.
185
186 -spec like(value(), regexp(), description()) ->
187   Value :: value().
188
189 like(Value, Expected, Description) ->
190   % XXX: regular expression may be invalid, so prepare estap_server before
191   % running the regexp
192   TestRun = get_test_run(),
193   estap_server:running(TestRun, Description),
194   case re:run(Value, Expected) of
195     {match, _Capture} -> estap_server:report_result(TestRun, {success, Value});
196     nomatch           -> estap_server:report_result(TestRun, {failure, Value})
197   end,
198   Value.
199
200 %% @doc Check if `Value' not matches a regexp.
201
202 -spec unlike(value(), regexp(), description()) ->
203   Value :: value().
204
205 unlike(Value, Expected, Description) ->
206   % XXX: regular expression may be invalid, so prepare estap_server before
207   % running the regexp
208   TestRun = get_test_run(),
209   estap_server:running(TestRun, Description),
210   case re:run(Value, Expected) of
211     {match, _Capture} -> estap_server:report_result(TestRun, {failure, Value});
212     nomatch           -> estap_server:report_result(TestRun, {success, Value})
213   end,
214   Value.
215
216 %% @doc Check if `Value' pattern-matches.
217 %%   Pattern is specified as a fun that has clauses defined only for what
218 %%   should match, i.e., calling the fun should fail with `function_clause'
219 %%   error. Return value of the fun is ignored.
220
221 -spec matches(value(), match_fun(), description()) ->
222   Value :: value().
223
224 matches(Value, MatchSpec, Description) ->
225   TestRun = get_test_run(),
226   estap_server:running(TestRun, Description),
227   try
228     MatchSpec(Value),
229     estap_server:report_result(TestRun, {success, Value})
230   catch
231     error:function_clause ->
232       estap_server:report_result(TestRun, {failure, Value})
233   end,
234   Value.
235
236 %%%---------------------------------------------------------------------------
237
238 %% @doc Stop testing whatsoever because something terrible happened.
239 %%
240 %%   Note that bailing out is a very severe operation. It aborts all test
241 %%   cases, including the ones in other scripts that were not executed yet.
242 %%   It should be only used when an error that occurred renders whole test
243 %%   suite unusable before it's fixed.
244
245 -spec bail_out(message()) ->
246   no_return().
247
248 bail_out(Message) ->
249   TestRun = get_test_run_or_parent(),
250   estap_server:bail_out(TestRun, Message),
251   exit('BAIL_OUT').
252
253 %% @doc Set the "no plan" plan for sub-tests.
254 %%   Calling this function may be safely skipped.
255 %%
256 %% @see all_ok/0
257
258 -spec no_plan() ->
259   ok.
260
261 no_plan() ->
262   _TestRun = get_test_run(),
263   ok.
264
265 %% @doc Set expected number of sub-tests.
266
267 -spec plan(pos_integer()) ->
268   ok.
269
270 plan(TestCount) when is_integer(TestCount) ->
271   TestRun = estap_server:subplan(TestCount, 1),
272   set_test_run(TestRun),
273   ok.
274
275 %% @doc Check if all the current sub-tests were OK.
276 %%   Function intended to be called at the end of a sequence of sub-tests, to
277 %%   indicate that the test sequence passed or failed.
278
279 -spec all_ok() ->
280   ok | {error, term()}.
281
282 all_ok() ->
283   TestRun = get_test_run(),
284   {Planned, Total, Failed, _TODO} = estap_server:get_status(TestRun),
285   estap_server:done(TestRun), % this ends estap_server, so it goes last
286   case Failed of
287     0 when Planned == undefined ->
288       ok;
289     0 when Planned == Total ->
290       ok;
291     0 when Planned /= Total ->
292       {error, {bad_plan, [{plan, Planned}, {run, Total}]}};
293     _ ->
294       {error, {failures, [{failed, Failed}, {plan, Planned}, {run, Total}]}}
295   end.
296
297 %%%---------------------------------------------------------------------------
298
299 %% @doc Get a directory containing this test script.
300 %%
301 %%   <b>NOTE</b>: This function doesn't work in processes spawned from test
302 %%   function. You need to get the directory in parent process and pass it as
303 %%   an argument.
304
305 test_dir() ->
306   case get(test_dir) of
307     undefined -> erlang:error({undefined, test_dir});
308     Directory -> Directory
309   end.
310
311 %% @doc Get a subdirectory of the directory containing this test script.
312 %%
313 %%   <b>NOTE</b>: This function doesn't work in processes spawned from test
314 %%   function. You need to get the directory in parent process and pass it as
315 %%   an argument.
316
317 test_dir(Subdir) ->
318   filename:join(test_dir(), Subdir).
319
320 %%%---------------------------------------------------------------------------
321
322 %% @doc Print a diagnostic message.
323 %%   Typically, diagnostic message is a warning, but may be notice important
324 %%   enough to print it along with test progress by TAP consumer.
325 %%
326 %%   Before first call to {@link plan/1}, {@link no_plan/0} or test functions
327 %%   ({@link ok/2}, {@link is/3} etc.) message is printed at the level of
328 %%   parent test. After any of those, it's printed at sub-test level.
329 %%
330 %%   Normally diagnostic output goes to <i>STDERR</i>, but under TODO tests it
331 %%   goes to <i>STDOUT</i>.
332 %%
333 %% @TODO Make the diagnostic output go to <i>STDOUT</i> under TODO
334
335 -spec diag(message()) ->
336   ok.
337
338 diag(Message) ->
339   TestRun = get_test_run_or_parent(),
340   estap_server:warning(TestRun, Message).
341
342 %% @doc Print a warning with some context.
343 %%   Typically, diagnostic message is a warning, but may be notice important
344 %%   enough to print it along with test progress by TAP consumer.
345 %%
346 %%   Before first call to {@link plan/1}, {@link no_plan/0} or test functions
347 %%   ({@link ok/2}, {@link is/3} etc.) message is printed at the level of
348 %%   parent test. After any of those, it's printed at sub-test level.
349 %%
350 %%   Normally diagnostic output goes to <i>STDERR</i>, but under TODO tests it
351 %%   goes to <i>STDOUT</i>.
352 %%
353 %% @TODO Make the diagnostic output go to <i>STDOUT</i> under TODO
354
355 -spec diag(message(), [info()]) ->
356   ok.
357
358 diag(Message, Info) ->
359   TestRun = get_test_run_or_parent(),
360   InfoLines = [["  ", format_info(I), "\n"] || I <- Info],
361   estap_server:warning(TestRun, [Message, "\n", InfoLines]).
362
363 %% @doc Print a message.
364 %%
365 %%   Before first call to {@link plan/1}, {@link no_plan/0} or test functions
366 %%   ({@link ok/2}, {@link is/3} etc.) message is printed at the level of
367 %%   parent test. After any of those, it's printed at sub-test level.
368
369 -spec info(message()) ->
370   ok.
371
372 info(Message) ->
373   TestRun = get_test_run_or_parent(),
374   estap_server:info(TestRun, Message).
375
376 %% @doc Print a message with some context.
377 %%
378 %%   Before first call to {@link plan/1}, {@link no_plan/0} or test functions
379 %%   ({@link ok/2}, {@link is/3} etc.) message is printed at the level of
380 %%   parent test. After any of those, it's printed at sub-test level.
381
382 -spec info(message(), [info()]) ->
383   ok.
384
385 info(Message, Info) ->
386   TestRun = get_test_run_or_parent(),
387   InfoLines = [["  ", format_info(I), "\n"] || I <- Info],
388   estap_server:info(TestRun, [Message, "\n", InfoLines]).
389
390 %% @doc Format a single info entry for printing it on screen.
391
392 -spec format_info(info()) ->
393   binary().
394
395 format_info(Info) when is_list(Info); is_binary(Info) ->
396   iolist_to_binary(Info);
397 format_info(Info) when is_atom(Info) ->
398   atom_to_binary(Info, unicode);
399 format_info({K, V} = _Info) ->
400   <<(format_info(K))/binary, ": ", (format_info(V))/binary>>.
401
402 %% @doc Format term so it can be printed to screen.
403 %%   Convenience wrapper for {@link io_lib:format/2}.
404 %%
405 %% @see info/2
406 %% @see diag/2
407
408 -spec explain(term()) ->
409   iolist().
410
411 explain(Term) ->
412   % no term should weigh 1MB
413   io_lib:print(Term, 1, 1024 * 1024, -1).
414
415 %%%---------------------------------------------------------------------------
416
417 %% @doc Set previously started {@link estap_server}.
418
419 set_test_run(TestRun) ->
420   put(estap_server, TestRun).
421
422 %% @doc Get associated {@link estap_server}, starting it if necessary.
423
424 get_test_run() ->
425   case get(estap_server) of
426     undefined ->
427       TestRun = estap_server:subplan(no_plan, 1),
428       put(estap_server, TestRun),
429       TestRun;
430     TestRun when is_pid(TestRun) ->
431       TestRun
432   end.
433
434 %% @doc Get associated {@link estap_server} or parent one if none is started
435 %%   yet. Necessary for top-level {@link info/1} and {@link diag/1} to work.
436
437 get_test_run_or_parent() ->
438   case get(estap_server) of
439     undefined ->
440       % XXX: this must be set in `estap_test:run()'
441       get(estap_server_parent);
442     TestRun when is_pid(TestRun) ->
443       TestRun
444   end.
445
446 %% @doc Send a test report to {@link estap_server}.
447
448 -spec report({success | failure | dubious, Value :: value()}, description()) ->
449   ok.
450
451 report({T,_V} = Report, Description)
452 when T == success; T == failure; T == dubious ->
453   TestRun = get_test_run(),
454   estap_server:running(TestRun, Description),
455   estap_server:report_result(TestRun, Report),
456   ok.
457
458 %%%---------------------------------------------------------------------------
459 %%% vim:ft=erlang:foldmethod=marker