1
%% This library is free software; you can redistribute it and/or modify
2
%% it under the terms of the GNU Lesser General Public License as
3
%% published by the Free Software Foundation; either version 2 of the
4
%% License, or (at your option) any later version.
6
%% This library is distributed in the hope that it will be useful, but
7
%% WITHOUT ANY WARRANTY; without even the implied warranty of
8
%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
9
%% Lesser General Public License for more details.
11
%% You should have received a copy of the GNU Lesser General Public
12
%% License along with this library; if not, write to the Free Software
13
%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
18
%% @author Richard Carlsson <richardc@it.uu.se>
19
%% @copyright 2006 Richard Carlsson
22
%% @doc Test running functionality
26
-export([run_testfun/1, function_wrapper/2, enter_context/4,
27
browse_context/2, multi_setup/1]).
30
-include("eunit.hrl").
31
-include("eunit_internal.hrl").
34
%% ---------------------------------------------------------------------
35
%% Getting a cleaned up stack trace. (We don't want it to include
36
%% eunit's own internal functions. This complicates self-testing
37
%% somewhat, but you can't have everything.) Note that we assume that
38
%% this particular module is the boundary between eunit and user code.
44
eunit_lib:uniq(prune_trace(erlang:get_stacktrace(), Ts)).
46
prune_trace([{?MODULE, _, _} | _Rest], Tail) ->
48
prune_trace([T | Ts], Tail) ->
49
[T | prune_trace(Ts, Tail)];
50
prune_trace([], Tail) ->
54
%% ---------------------------------------------------------------------
57
%% @spec ((any()) -> any()) -> {ok, Value} | {error, eunit_lib:exception()}
58
%% @throws wrapperError()
66
{eunit_internal, Term} ->
67
%% Internally generated: re-throw Term (lose the trace)
70
{error, {Class, Reason, get_stacktrace()}}
78
{?LINE, F} = ?_test(undefined),
79
{ok, undefined} = run_testfun(F)
82
{?LINE, F} = ?_assert(true),
83
{ok, ok} = run_testfun(F)
86
{?LINE, F} = ?_assert(false),
87
{error,{error,{assertion_failed,
97
{?LINE, F} = ?_assert([]),
98
{error,{error,{assertion_failed,
103
{value,{not_a_boolean,[]}}]},
108
{?LINE, F} = ?_assertNot(false),
109
{ok, ok} = run_testfun(F)
112
{?LINE, F} = ?_assertNot(true),
113
{error,{error,{assertion_failed,
123
{?LINE, F} = ?_assertMatch(ok, ok),
124
{ok, ok} = run_testfun(F)
127
{?LINE, F} = ?_assertMatch([_], []),
128
{error,{error,{assertMatch_failed,
138
{?LINE, F} = ?_assertEqual(ok, ok),
139
{ok, ok} = run_testfun(F)
142
{?LINE, F} = ?_assertEqual(3, 1+1),
143
{error,{error,{assertEqual_failed,
153
{?LINE, F} = ?_assertException(error, badarith,
154
erlang:error(badarith)),
155
{ok, ok} = run_testfun(F)
158
{?LINE, F} = ?_assertException(error, badarith, ok),
159
{error,{error,{assertException_failed,
164
{unexpected_success,ok}]},
169
{?LINE, F} = ?_assertException(error, badarg,
170
erlang:error(badarith)),
171
{error,{error,{assertException_failed,
176
{unexpected_exception,
177
{error,badarith,_}}]},
185
%% ---------------------------------------------------------------------
186
%% Wrapper for simple "named function" tests ({M,F}), which provides
187
%% better error reporting when the function is missing at test time.
189
%% Note that the wrapper fun is usually called by run_testfun/1, and the
190
%% special exceptions thrown here are expected to be handled there.
192
%% @throws {eunit_internal, wrapperError()}
194
%% @type wrapperError() = {no_such_function, mfa()}
195
%% | {module_not_found, moduleName()}
197
function_wrapper(M, F) ->
202
%% Check if it was M:F/0 that was undefined
203
case erlang:module_loaded(M) of
205
fail({module_not_found, M});
207
case erlang:function_exported(M, F, 0) of
209
fail({no_such_function, {M,F,0}});
211
rethrow(error, undef, [{M,F,0}])
217
rethrow(Class, Reason, Trace) ->
218
erlang:raise(Class, Reason, get_stacktrace(Trace)).
221
throw({eunit_internal, Term}).
226
{"error handling in function wrapper",
227
[?_assertException(throw, {module_not_found, eunit_nonexisting},
228
run_testfun(function_wrapper(eunit_nonexisting,test))),
229
?_assertException(throw,
230
{no_such_function, {?MODULE,nonexisting_test,0}},
231
run_testfun(function_wrapper(?MODULE,nonexisting_test))),
232
?_test({error, {error, undef, _T}}
233
= run_testfun(function_wrapper(?MODULE,wrapper_test_exported_)))
236
%% this must be exported (done automatically by the autoexport transform)
237
wrapper_test_exported_() ->
238
{ok, ?MODULE:nonexisting_function()}.
242
%% ---------------------------------------------------------------------
243
%% Entering a setup-context, with guaranteed cleanup.
245
%% @spec (Setup, Cleanup, Instantiate, Callback) -> any()
246
%% Setup = () -> any()
247
%% Cleanup = (any()) -> any()
248
%% Instantiate = (any()) -> tests()
249
%% Callback = (tests()) -> any()
250
%% @throws {context_error, Error, eunit_lib:exception()}
251
%% Error = setup_failed | instantiation_failed | cleanup_failed
253
enter_context(Setup, Cleanup, Instantiate, Callback) ->
256
try Instantiate(R) of
258
try Callback(T) %% call back to client code
260
%% Always run cleanup; client may be an idiot
264
context_error(cleanup_failed, Class, Term)
269
context_error(instantiation_failed, Class, Term)
273
context_error(setup_failed, Class, Term)
276
context_error(Type, Class, Term) ->
277
throw({context_error, Type, {Class, Term, get_stacktrace()}}).
279
%% Instantiates a context with dummy values to make browsing possible
280
%% @throws {context_error, instantiation_failed, eunit_lib:exception()}
282
browse_context(I, F) ->
283
%% Browse: dummy setup/cleanup and a wrapper for the instantiator
285
try eunit_lib:browse_fun(I) of
289
context_error(instantiation_failed, Class, Term)
292
enter_context(fun ok/0, fun ok/1, I1, F).
297
%% This generates single setup/cleanup functions from a list of tuples
298
%% on the form {Tag, Setup, Cleanup}, where the setup function always
299
%% backs out correctly from partial completion.
302
{SetupAll, CleanupAll} = multi_setup(List, fun ok/1),
303
%% must reverse back and forth here in order to present the list in
304
%% "natural" order to the test instantiation function
305
{fun () -> lists:reverse(SetupAll([])) end,
306
fun (Rs) -> CleanupAll(lists:reverse(Rs)) end}.
308
multi_setup([{Tag, S, C} | Es], CleanupPrev) ->
309
Cleanup = fun ([R | Rs]) ->
314
throw({Tag, {Class, Term, get_stacktrace()}})
317
{SetupRest, CleanupAll} = multi_setup(Es, Cleanup),
325
throw({Tag, {Class, Term, get_stacktrace()}})
329
multi_setup([{Tag, S} | Es], CleanupPrev) ->
330
multi_setup([{Tag, S, fun ok/1} | Es], CleanupPrev);
331
multi_setup([], CleanupAll) ->
332
{fun (Rs) -> Rs end, CleanupAll}.