Current section
Files
Jump to
Current section
Files
src/dream_test_test_discovery_ffi.erl
-module(dream_test_test_discovery_ffi).
-export([discover_test_modules/1, call_tests/1]).
module_str_to_rel_gleam_path(ModuleStr) ->
lists:map(fun(C) ->
case C of
$@ -> $/;
_ -> C
end
end,
ModuleStr)
++ ".gleam".
has_matching_test_source(ModuleStr) ->
%% Discovery is used from `gleam test` runners; in that context the CWD is the
%% project root, and test sources live under ./test/.
case file:get_cwd() of
{ok, Cwd} ->
Rel = module_str_to_rel_gleam_path(ModuleStr),
Path = filename:join([Cwd, "test", Rel]),
filelib:is_file(Path);
_ ->
%% If we can't determine the CWD, fall back to the older behavior.
true
end.
%% Discover compiled test modules matching a beam filename glob.
%%
%% - beam_glob: a glob like "unit@*_test.beam" (basename pattern)
%%
%% Returns: {ok, [<<"unit@foo_test">>, ...]} or {error, <<"message">>}
discover_test_modules(BeamGlob) ->
try
GlobStr = binary_to_list(BeamGlob),
Paths = code:get_path(),
Files =
lists:append(
lists:map(fun(Dir) ->
filelib:wildcard(
filename:join(Dir, GlobStr))
end,
Paths)),
Modules =
lists:filtermap(fun(Path) ->
File = filename:basename(Path),
case lists:suffix(".beam", File) of
false -> false;
true ->
ModuleStr = lists:sublist(File, length(File) - 5),
%% Avoid running stale compiled modules that no longer have a
%% corresponding .gleam file under ./test/.
case has_matching_test_source(ModuleStr) of
false -> false;
true ->
Module = list_to_atom(ModuleStr),
case code:ensure_loaded(Module) of
{module, _} ->
case erlang:function_exported(Module,
tests,
0)
of
true ->
{true, list_to_binary(ModuleStr)};
false -> false
end;
_ -> false
end
end
end
end,
Files),
{ok, lists:usort(Modules)}
catch
_:_ ->
{error, <<"discover_test_modules_failed">>}
end.
%% Dynamically call tests() on a discovered module name.
%%
%% Returns: {ok, Suite} or {error, <<"message">>}
call_tests(ModuleName) ->
try
Module = list_to_atom(binary_to_list(ModuleName)),
case erlang:function_exported(Module, tests, 0) of
true ->
{ok, Module:tests()};
false ->
{error, <<"no_tests_function">>}
end
catch
_:_ ->
{error, <<"call_tests_failed">>}
end.