-module(testbldr). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch]). -export([named/2, include_passing_tests_in_output/2, output_results_to_file/2, output_results_to_stdout/1, display_test_outcome_using/2, display_ratio_using/2, run/2, test_runner_default/0]). -export_type([test/0, test_outcome/0, test_runner/0, verbosity/0, output_destination/0, test_stats/0]). -type test() :: {test, binary(), fun(() -> test_outcome())}. -type test_outcome() :: pass | {fail, binary()}. -opaque test_runner() :: {test_runner, verbosity(), gleam@set:set(output_destination()), gleam@option:option(fun((binary(), test_outcome()) -> binary())), gleam@option:option(fun((integer(), integer()) -> binary()))}. -type verbosity() :: normal | verbose. -type output_destination() :: stdout | {file, binary()}. -type test_stats() :: {test_stats, integer(), integer(), integer(), integer(), list({binary(), test_outcome()})}. -spec named(binary(), fun(() -> test_outcome())) -> test(). named(Name, New_test) -> {test, Name, New_test}. -spec include_passing_tests_in_output(test_runner(), boolean()) -> test_runner(). include_passing_tests_in_output(Runner, Input) -> Verbosity = case Input of true -> verbose; false -> normal end, erlang:setelement(2, Runner, Verbosity). -spec output_results_to_file(test_runner(), binary()) -> test_runner(). output_results_to_file(Runner, Path) -> erlang:setelement( 3, Runner, gleam@set:insert(erlang:element(3, Runner), {file, Path}) ). -spec output_results_to_stdout(test_runner()) -> test_runner(). output_results_to_stdout(Runner) -> erlang:setelement( 3, Runner, gleam@set:insert(erlang:element(3, Runner), stdout) ). -spec display_test_outcome_using( test_runner(), fun((binary(), test_outcome()) -> binary()) ) -> test_runner(). display_test_outcome_using(Runner, Printer) -> erlang:setelement(4, Runner, {some, Printer}). -spec display_ratio_using( test_runner(), fun((integer(), integer()) -> binary()) ) -> test_runner(). display_ratio_using(Runner, Printer) -> erlang:setelement(5, Runner, {some, Printer}). -spec write_stats_to_file(test_runner(), test_stats(), binary()) -> {ok, nil} | {error, simplifile:file_error()}. write_stats_to_file(Runner, Stats, Path) -> Individual = (gleam@list:fold( erlang:element(6, Stats), <<""/utf8>>, fun(Acc, _use1) -> {Name, Outcome} = _use1, case erlang:element(4, Runner) of {some, Printer} -> <<<>/binary, "\n"/utf8>>; none -> Acc end end )), Ratio = case erlang:element(5, Runner) of {some, Printer@1} -> Printer@1(erlang:element(3, Stats), erlang:element(2, Stats)); none -> <<""/utf8>> end, simplifile:write( Path, <<<>/binary, Ratio/binary>> ). -spec write_stats_to_stdout(test_runner(), test_stats()) -> nil. write_stats_to_stdout(Runner, Stats) -> (gleam@list:each( erlang:element(6, Stats), fun(_use0) -> {Name, Outcome} = _use0, case erlang:element(4, Runner) of {some, Printer} -> _pipe = Printer(Name, Outcome), gleam@io:println(_pipe); none -> nil end end )), gleam@io:println(<<""/utf8>>), case erlang:element(5, Runner) of {some, Printer@1} -> _pipe@1 = Printer@1( erlang:element(3, Stats), erlang:element(2, Stats) ), gleam@io:println(_pipe@1); none -> nil end. -spec run(test_runner(), list(test())) -> nil. run(Runner, Tests) -> Test_stats = (gleam@list:fold( Tests, {test_stats, 0, 0, 0, 0, []}, fun(Acc, _use1) -> {test, Name, Test_function} = _use1, case gleam_erlang_ffi:rescue(Test_function) of {ok, pass} -> {test_stats, erlang:element(2, Acc) + 1, erlang:element(3, Acc) + 1, erlang:element(4, Acc), erlang:element(5, Acc), [{Name, pass} | erlang:element(6, Acc)]}; {ok, {fail, Msg}} -> {test_stats, erlang:element(2, Acc) + 1, erlang:element(3, Acc), erlang:element(4, Acc) + 1, erlang:element(5, Acc), [{Name, {fail, Msg}} | erlang:element(6, Acc)]}; {error, _} -> {test_stats, erlang:element(2, Acc) + 1, erlang:element(3, Acc), erlang:element(4, Acc), erlang:element(5, Acc) + 1, [{Name, {fail, <<"Test Panicked!"/utf8>>}} | erlang:element(6, Acc)]} end end )), Filter = case erlang:element(2, Runner) of normal -> fun(X) -> case erlang:element(2, X) of pass -> false; {fail, _} -> true end end; verbose -> fun(_) -> true end end, Filtered_stats = erlang:setelement( 6, Test_stats, gleam@list:filter(erlang:element(6, Test_stats), Filter) ), (gleam@list:each( gleam@set:to_list(erlang:element(3, Runner)), fun(Dest) -> case Dest of stdout -> write_stats_to_stdout(Runner, Filtered_stats); {file, Path} -> _assert_subject = write_stats_to_file( Runner, Filtered_stats, Path ), {ok, _} = case _assert_subject of {ok, _} -> _assert_subject; _assert_fail -> erlang:error(#{gleam_error => let_assert, message => <<"Assertion pattern match failed"/utf8>>, value => _assert_fail, module => <<"testbldr"/utf8>>, function => <<"run"/utf8>>, line => 172}) end, nil end end )). -spec outcome_to_string(binary(), test_outcome()) -> binary(). outcome_to_string(Name, Outcome) -> case Outcome of pass -> <<"✅ "/utf8, Name/binary>>; {fail, Msg} -> <<<<<<<<"❌ "/utf8, Name/binary>>/binary, ": Failed ("/utf8>>/binary, Msg/binary>>/binary, ")"/utf8>> end. -spec ratio_to_string(integer(), integer()) -> binary(). ratio_to_string(Passed, Total) -> Prefix_emoji = case Passed =:= Total of true -> <<"✅"/utf8>>; false -> <<"❌"/utf8>> end, <<<<<<<<<>/binary, (gleam@int:to_string(Passed))/binary>>/binary, "/"/utf8>>/binary, (gleam@int:to_string(Total))/binary>>/binary, " tests passed"/utf8>>. -spec test_runner_default() -> test_runner(). test_runner_default() -> {test_runner, normal, gleam@set:from_list([stdout]), {some, fun outcome_to_string/2}, {some, fun ratio_to_string/2}}.