29
29
-include_lib("kernel/include/file.hrl").
31
-export([list/1, iter_init/2, iter_next/2, iter_prev/2, iter_id/1,
32
list_size/1, enter_context/3]).
31
-export([iter_init/2, iter_next/1, iter_prev/1, iter_id/1,
32
enter_context/3, get_module_tests/1]).
34
34
-import(lists, [foldr/3]).
98
98
%% @type moduleName() = atom()
99
99
%% @type functionName() = atom()
100
%% @type arity() = integer()
100
101
%% @type appName() = atom()
101
102
%% @type fileName() = string()
104
%% TODO: Can we mark up tests as known-failures?
105
%% TODO: Is it possible to handle known timout/setup failures?
106
%% TODO: Add diagnostic tests which never fail, but may cause warnings?
103
108
%% ---------------------------------------------------------------------
104
109
%% Abstract test set iterator
384
375
check_arity(A, 1, T),
385
{data, [fun () -> A(X) end, {with, X, As1}]};
376
{data, [{eunit_lib:fun_parent(A), fun () -> A(X) end},
389
381
parse({S, T1} = T) when is_list(S) ->
390
382
case eunit_lib:is_string(S) of
392
group(#group{tests = T1, desc = S});
384
group(#group{tests = T1, desc = list_to_binary(S)});
388
parse({S, T1}) when is_binary(S) ->
389
group(#group{tests = T1, desc = S});
396
390
parse(T) when is_tuple(T), size(T) > 2, is_list(element(1, T)) ->
397
391
[S | Es] = tuple_to_list(T),
398
392
parse({S, list_to_tuple(Es)});
393
parse(T) when is_tuple(T), size(T) > 2, is_binary(element(1, T)) ->
394
[S | Es] = tuple_to_list(T),
395
parse({S, list_to_tuple(Es)});
399
396
parse(M) when is_atom(M) ->
400
397
parse({module, M});
401
398
parse(T) when is_list(T) ->
418
415
parse_simple({L, F}) when is_integer(L), L >= 0 ->
419
416
(parse_simple(F))#test{line = L};
417
parse_simple({{M,N,A}=Loc, F}) when is_atom(M), is_atom(N), is_integer(A) ->
418
(parse_simple(F))#test{location = Loc};
420
419
parse_simple(F) ->
421
420
parse_function(F).
423
422
parse_function(F) when is_function(F) ->
424
423
check_arity(F, 0, F),
425
{module, M} = erlang:fun_info(F, module),
426
#test{f = F, module = M, name = eunit_lib:fun_parent(F)};
424
#test{f = F, location = eunit_lib:fun_parent(F)};
427
425
parse_function({M,F}) when is_atom(M), is_atom(F) ->
428
#test{f = eunit_test:function_wrapper(M, F), module = M, name = F};
426
#test{f = eunit_test:function_wrapper(M, F), location = {M, F, 0}};
429
427
parse_function(F) ->
546
544
%% @throws {module_not_found, moduleName()}
548
546
get_module_tests(M) ->
549
TestSuffix = ?DEFAULT_TEST_SUFFIX,
550
GeneratorSuffix = ?DEFAULT_GENERATOR_SUFFIX,
551
547
try M:module_info(exports) of
553
Fs = testfuns(Es, M, TestSuffix, GeneratorSuffix),
554
Name = atom_to_list(M),
555
case lists:suffix(?DEFAULT_TESTMODULE_SUFFIX, Name) of
557
Name1 = Name ++ ?DEFAULT_TESTMODULE_SUFFIX,
558
M1 = list_to_atom(Name1),
559
try get_module_tests(M1) of
561
Fs ++ [{"module '" ++ Name1 ++ "'", Fs1}]
563
{module_not_found, M1} ->
549
Fs = get_module_tests_1(M, Es),
550
W = ?DEFAULT_MODULE_WRAPPER_NAME,
551
case lists:member({W,1}, Es) of
553
true -> {generator, fun () -> M:W(Fs) end}
571
557
throw({module_not_found, M})
560
get_module_tests_1(M, Es) ->
561
Fs = testfuns(Es, M, ?DEFAULT_TEST_SUFFIX,
562
?DEFAULT_GENERATOR_SUFFIX),
563
Name = atom_to_list(M),
564
case lists:suffix(?DEFAULT_TESTMODULE_SUFFIX, Name) of
566
Name1 = Name ++ ?DEFAULT_TESTMODULE_SUFFIX,
567
M1 = list_to_atom(Name1),
568
try get_module_tests(M1) of
570
Fs ++ [{"module '" ++ Name1 ++ "'", Fs1}]
572
{module_not_found, M1} ->
574
579
testfuns(Es, M, TestSuffix, GeneratorSuffix) ->
575
580
foldr(fun ({F, 0}, Fs) ->
576
581
N = atom_to_list(F),
673
678
eunit_test:enter_context(S, C, I, F1).
676
%% ---------------------------------------------------------------------
677
%% Returns a symbolic listing of a set of tests
679
%% @type testInfoList() = [Entry]
680
%% Entry = {item, testId(), Description, testName()}
681
%% | {group, testId(), Description, testInfoList}
682
%% Description = string()
683
%% @type testId() = [integer()]
684
%% @type testName() = {moduleName(), functionName()}
685
%% | {moduleName(), functionName(), lineNumber()}
686
%% @type lineNumber() = integer(). Proper line numbers are always >= 1.
688
%% @throws {bad_test, term()}
689
%% | {generator_failed, exception()}
690
%% | {no_such_function, eunit_lib:mfa()}
691
%% | {module_not_found, moduleName()}
692
%% | {application_not_found, appName()}
693
%% | {file_read_error, {Reason::atom(), Message::string(),
695
%% | {context_error, instantiation_failed, eunit_lib:exception()}
701
list_loop(iter_init(T, ParentID)).
704
case iter_next(I, fun (R) -> throw({error, R}) end) of
709
Name = case T#test.line of
710
0 -> {T#test.module, T#test.name};
711
Line -> {T#test.module, T#test.name, Line}
713
[{item, Id, desc_string(T#test.desc), Name}
715
#group{context = Context} ->
716
[{group, Id, desc_string(T#group.desc),
717
list_context(Context, T#group.tests, Id)}
724
desc_string(undefined) -> "";
727
list_context(undefined, T, ParentId) ->
729
list_context(#context{process = local}, T, ParentId) ->
730
browse_context(T, fun (T) -> list(T, ParentId) end);
731
list_context(#context{process = spawn}, T, ParentId) ->
732
browse_context(T, fun (T) -> list({spawn, T}, ParentId) end);
733
list_context(#context{process = {spawn, N}}, T, ParentId) ->
734
browse_context(T, fun (T) -> list({spawn, N, T}, ParentId) end).
736
browse_context(T, F) ->
737
eunit_test:browse_context(T, F).
739
list_size({item, _, _, _}) -> 1;
740
list_size({group, _, _, Es}) -> list_size(Es);
741
list_size(Es) when is_list(Es) ->
742
lists:foldl(fun (E, N) -> N + list_size(E) end, 0, Es).
745
682
generator_exported_() ->