Packages

Gleam TestContainers wrapper around Elixir TestContainers

Current section

Files

Jump to
testcontainers_gleam src internal integration_ffi.erl
Raw

src/internal/integration_ffi.erl

-module(integration_ffi).
-export([run/1]).
%% Custom test runner that wraps each test function with an individual timeout.
%%
%% eunit's default per-test timeout is 5 seconds. gleeunit's
%% `{timeout, 60, Tests}` only overrides the GROUP timeout, not the
%% per-test timeout. By discovering every _test/0 function and wrapping
%% each one in `{timeout, T, {M, F}}` we ensure slow containers like
%% Cassandra or Ceph get enough time.
run(Timeout) ->
Files = filelib:wildcard("**/*.{erl,gleam}", "test"),
Modules = lists:map(fun(F) ->
Name = gleam_module_name(list_to_binary(F)),
binary_to_atom(Name, utf8)
end, Files),
Tests = lists:flatmap(fun(M) -> test_funs(M, Timeout) end, Modules),
Options = [verbose, no_tty,
{report, {gleeunit_progress, [{colored, true}]}}],
case eunit:test(Tests, Options) of
ok -> {ok, nil};
error -> {error, nil};
{error, Term} -> {error, Term}
end.
%% Return a list of `{timeout, T, {M, F}}` for every exported _test/0
%% function in Module.
test_funs(Module, Timeout) ->
try Module:module_info(exports) of
Exports ->
Funs = [F || {F, 0} <- Exports, is_test_fun(F)],
[{timeout, Timeout, {Module, F}} || F <- Funs]
catch
_:_ -> []
end.
is_test_fun(Name) ->
Str = atom_to_list(Name),
lists:suffix("_test", Str) orelse lists:suffix("_test_", Str).
gleam_module_name(Path) ->
case binary:match(Path, <<".gleam">>) of
nomatch ->
%% Plain .erl file: take the basename without extension
Parts = binary:split(Path, <<"/">>, [global]),
Last = lists:last(Parts),
binary:part(Last, 0, byte_size(Last) - 4);
_ ->
%% Gleam file: strip .gleam, replace / with @
WithoutExt = binary:part(Path, 0, byte_size(Path) - 6),
binary:replace(WithoutExt, <<"/">>, <<"@">>, [global])
end.