Current section
Files
Jump to
Current section
Files
src/dream_test@runner.erl
-module(dream_test@runner).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/dream_test/runner.gleam").
-export([max_concurrency/2, default_timeout_ms/2, exit_on_failure/1, progress_reporter/2, results_reporters/2, output/2, filter_tests/2, before_each_test/2, after_each_test/2, before_each_suite/2, after_each_suite/2, before_all_suites/2, after_all_suites/2, silent/1, new/1, add_suites/2, add_suites_with_config/3, has_failures/1, run/1]).
-export_type([run_builder/1, suite_run/1, output/0]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
?MODULEDOC(
" Test runner for dream_test.\n"
"\n"
" This module provides a **pipe-friendly builder API** for running suites and\n"
" collecting `dream_test/types.TestResult` values.\n"
"\n"
" ## When should I use this?\n"
"\n"
" - Always: the runner is how you execute suites built with `dream_test/unit`,\n"
" `dream_test/unit_context`, or `dream_test/gherkin/feature`.\n"
"\n"
" ## What does the runner do?\n"
"\n"
" - Runs groups sequentially, tests in parallel (bounded by `max_concurrency`)\n"
" - Sandboxes tests and hooks (timeouts + crash isolation)\n"
" - Optionally drives an event-based reporter\n"
"\n"
" ## Example\n"
"\n"
" ```gleam\n"
" import dream_test/matchers.{be_equal, or_fail_with, should}\n"
" import dream_test/parallel\n"
" import dream_test/reporters/bdd\n"
" import dream_test/reporters/progress\n"
" import dream_test/runner\n"
" import dream_test/unit.{describe, it}\n"
"\n"
" pub fn tests() {\n"
" describe(\"Example\", [\n"
" it(\"works\", fn() {\n"
" 1 + 1\n"
" |> should\n"
" |> be_equal(2)\n"
" |> or_fail_with(\"math should work\")\n"
" }),\n"
" ])\n"
" }\n"
"\n"
" pub fn main() {\n"
" let db_config =\n"
" parallel.ParallelConfig(max_concurrency: 1, default_timeout_ms: 60_000)\n"
"\n"
" runner.new([])\n"
" |> runner.add_suites([tests()])\n"
" |> runner.add_suites_with_config(db_config, [db_tests()])\n"
" |> runner.progress_reporter(progress.new())\n"
" |> runner.results_reporters([bdd.new()])\n"
" |> runner.exit_on_failure()\n"
" |> runner.run()\n"
" }\n"
" ```\n"
).
-opaque run_builder(JZL) :: {run_builder,
list(suite_run(JZL)),
dream_test@parallel:parallel_config(),
gleam@option:option(fun((dream_test@types:test_info()) -> boolean())),
boolean(),
gleam@option:option(dream_test@reporters@progress:progress_reporter()),
list(dream_test@reporters@types:results_reporter()),
gleam@option:option(output()),
list(fun((dream_test@types:test_info(), JZL) -> {ok, JZL} |
{error, binary()})),
list(fun((dream_test@types:test_info(), JZL) -> {ok, nil} |
{error, binary()})),
list(fun((dream_test@types:suite_info()) -> {ok, nil} |
{error, binary()})),
list(fun((dream_test@types:suite_info()) -> {ok, nil} |
{error, binary()})),
list(fun((list(dream_test@types:suite_info())) -> {ok, nil} |
{error, binary()})),
list(fun((list(dream_test@types:suite_info())) -> {ok, nil} |
{error, binary()}))}.
-type suite_run(JZM) :: {suite_run,
dream_test@types:root(JZM),
gleam@option:option(dream_test@parallel:parallel_config())}.
-type output() :: {output, fun((binary()) -> nil), fun((binary()) -> nil)}.
-file("src/dream_test/runner.gleam", 320).
?DOC(
" Set the maximum number of concurrently running tests.\n"
"\n"
" - `1` gives fully sequential test execution.\n"
" - Higher values increase parallelism.\n"
"\n"
" ## Example\n"
"\n"
" ```gleam\n"
" import dream_test/matchers.{be_equal, or_fail_with, should}\n"
" import dream_test/reporters/bdd\n"
" import dream_test/reporters/progress\n"
" import dream_test/runner\n"
" import dream_test/unit.{describe, it}\n"
" import gleam/io\n"
"\n"
" pub fn tests() {\n"
" describe(\"Sequential tests\", [\n"
" it(\"first test\", fn() {\n"
" // When tests share external resources, run them sequentially\n"
" 1 + 1\n"
" |> should\n"
" |> be_equal(2)\n"
" |> or_fail_with(\"Math works\")\n"
" }),\n"
" it(\"second test\", fn() {\n"
" 2 + 2\n"
" |> should\n"
" |> be_equal(4)\n"
" |> or_fail_with(\"Math still works\")\n"
" }),\n"
" ])\n"
" }\n"
"\n"
" pub fn main() {\n"
" // Sequential execution for tests with shared state\n"
" runner.new([tests()])\n"
" |> runner.max_concurrency(1)\n"
" |> runner.default_timeout_ms(30_000)\n"
" |> runner.progress_reporter(progress.new())\n"
" |> runner.results_reporters([bdd.new()])\n"
" |> runner.exit_on_failure()\n"
" |> runner.run()\n"
" }\n"
" ```\n"
"\n"
" ## Parameters\n"
"\n"
" - `builder`: the runner builder you’re configuring\n"
" - `max`: maximum number of concurrently running tests (use `1` for fully sequential)\n"
"\n"
" ## Returns\n"
"\n"
" The updated `RunBuilder(ctx)`.\n"
).
-spec max_concurrency(run_builder(KAB), integer()) -> run_builder(KAB).
max_concurrency(Builder, Max) ->
{parallel_config, _, Timeout} = erlang:element(3, Builder),
{run_builder,
erlang:element(2, Builder),
{parallel_config, Max, Timeout},
erlang:element(4, Builder),
erlang:element(5, Builder),
erlang:element(6, Builder),
erlang:element(7, Builder),
erlang:element(8, Builder),
erlang:element(9, Builder),
erlang:element(10, Builder),
erlang:element(11, Builder),
erlang:element(12, Builder),
erlang:element(13, Builder),
erlang:element(14, Builder)}.
-file("src/dream_test/runner.gleam", 379).
?DOC(
" Set the default timeout (milliseconds) applied to tests without an explicit timeout.\n"
"\n"
" Tests that exceed the timeout are killed and reported as `TimedOut`.\n"
"\n"
" ## Example\n"
"\n"
" ```gleam\n"
" import dream_test/matchers.{be_equal, or_fail_with, should}\n"
" import dream_test/reporters/bdd\n"
" import dream_test/reporters/progress\n"
" import dream_test/runner\n"
" import dream_test/unit.{describe, it}\n"
" import gleam/io\n"
"\n"
" pub fn tests() {\n"
" describe(\"Runner config demo\", [\n"
" it(\"runs with custom config\", fn() {\n"
" 1 + 1\n"
" |> should\n"
" |> be_equal(2)\n"
" |> or_fail_with(\"Math works\")\n"
" }),\n"
" ])\n"
" }\n"
"\n"
" pub fn main() {\n"
" runner.new([tests()])\n"
" |> runner.max_concurrency(8)\n"
" |> runner.default_timeout_ms(10_000)\n"
" |> runner.progress_reporter(progress.new())\n"
" |> runner.results_reporters([bdd.new()])\n"
" |> runner.exit_on_failure()\n"
" |> runner.run()\n"
" }\n"
" ```\n"
"\n"
" ## Parameters\n"
"\n"
" - `builder`: the runner builder you’re configuring\n"
" - `timeout_ms`: timeout in milliseconds applied to tests without an explicit timeout\n"
"\n"
" ## Returns\n"
"\n"
" The updated `RunBuilder(ctx)`.\n"
).
-spec default_timeout_ms(run_builder(KAE), integer()) -> run_builder(KAE).
default_timeout_ms(Builder, Timeout_ms) ->
{parallel_config, Max, _} = erlang:element(3, Builder),
{run_builder,
erlang:element(2, Builder),
{parallel_config, Max, Timeout_ms},
erlang:element(4, Builder),
erlang:element(5, Builder),
erlang:element(6, Builder),
erlang:element(7, Builder),
erlang:element(8, Builder),
erlang:element(9, Builder),
erlang:element(10, Builder),
erlang:element(11, Builder),
erlang:element(12, Builder),
erlang:element(13, Builder),
erlang:element(14, Builder)}.
-file("src/dream_test/runner.gleam", 435).
?DOC(
" Exit the BEAM with a non-zero code if any tests fail.\n"
"\n"
" Useful for CI pipelines.\n"
"\n"
" ## Example\n"
"\n"
" ```gleam\n"
" import dream_test/matchers.{be_equal, or_fail_with, should}\n"
" import dream_test/reporters/bdd\n"
" import dream_test/reporters/progress\n"
" import dream_test/runner\n"
" import dream_test/unit.{describe, it}\n"
" import gleam/io\n"
"\n"
" pub fn tests() {\n"
" describe(\"Example\", [\n"
" it(\"works\", fn() {\n"
" 1 + 1\n"
" |> should\n"
" |> be_equal(2)\n"
" |> or_fail_with(\"math should work\")\n"
" }),\n"
" ])\n"
" }\n"
"\n"
" pub fn main() {\n"
" runner.new([tests()])\n"
" |> runner.progress_reporter(progress.new())\n"
" |> runner.results_reporters([bdd.new()])\n"
" |> runner.exit_on_failure()\n"
" |> runner.run()\n"
" }\n"
" ```\n"
"\n"
" ## Parameters\n"
"\n"
" - `builder`: the runner builder you’re configuring\n"
"\n"
" ## Returns\n"
"\n"
" The updated `RunBuilder(ctx)`.\n"
).
-spec exit_on_failure(run_builder(KAH)) -> run_builder(KAH).
exit_on_failure(Builder) ->
{run_builder,
erlang:element(2, Builder),
erlang:element(3, Builder),
erlang:element(4, Builder),
true,
erlang:element(6, Builder),
erlang:element(7, Builder),
erlang:element(8, Builder),
erlang:element(9, Builder),
erlang:element(10, Builder),
erlang:element(11, Builder),
erlang:element(12, Builder),
erlang:element(13, Builder),
erlang:element(14, Builder)}.
-file("src/dream_test/runner.gleam", 443).
?DOC(
" Attach a progress reporter (live output during the run).\n"
"\n"
" This reporter is driven by `TestFinished` events in completion order.\n"
" It is intended for a single in-place progress bar UI.\n"
).
-spec progress_reporter(
run_builder(KAK),
dream_test@reporters@progress:progress_reporter()
) -> run_builder(KAK).
progress_reporter(Builder, Reporter) ->
{run_builder,
erlang:element(2, Builder),
erlang:element(3, Builder),
erlang:element(4, Builder),
erlang:element(5, Builder),
{some, Reporter},
erlang:element(7, Builder),
erlang:element(8, Builder),
erlang:element(9, Builder),
erlang:element(10, Builder),
erlang:element(11, Builder),
erlang:element(12, Builder),
erlang:element(13, Builder),
erlang:element(14, Builder)}.
-file("src/dream_test/runner.gleam", 454).
?DOC(
" Attach results reporters (printed at the end, in the order provided).\n"
"\n"
" Results reporters receive the full traversal-ordered results list from the\n"
" `RunFinished` event, so their output is deterministic under parallel execution.\n"
).
-spec results_reporters(
run_builder(KAN),
list(dream_test@reporters@types:results_reporter())
) -> run_builder(KAN).
results_reporters(Builder, Reporters) ->
{run_builder,
erlang:element(2, Builder),
erlang:element(3, Builder),
erlang:element(4, Builder),
erlang:element(5, Builder),
erlang:element(6, Builder),
Reporters,
erlang:element(8, Builder),
erlang:element(9, Builder),
erlang:element(10, Builder),
erlang:element(11, Builder),
erlang:element(12, Builder),
erlang:element(13, Builder),
erlang:element(14, Builder)}.
-file("src/dream_test/runner.gleam", 478).
?DOC(
" Configure output sinks for `runner.run()`.\n"
"\n"
" This is how you route reporter output (stdout vs stderr, capturing output in\n"
" tests, etc).\n"
"\n"
" ## Example\n"
"\n"
" ```gleam\n"
" import dream_test/runner\n"
" import gleam/io\n"
"\n"
" pub fn main() {\n"
" runner.new([tests()])\n"
" |> runner.output(runner.Output(out: io.print, error: io.eprint))\n"
" |> runner.run()\n"
" }\n"
" ```\n"
).
-spec output(run_builder(KAR), output()) -> run_builder(KAR).
output(Builder, Output) ->
{run_builder,
erlang:element(2, Builder),
erlang:element(3, Builder),
erlang:element(4, Builder),
erlang:element(5, Builder),
erlang:element(6, Builder),
erlang:element(7, Builder),
{some, Output},
erlang:element(9, Builder),
erlang:element(10, Builder),
erlang:element(11, Builder),
erlang:element(12, Builder),
erlang:element(13, Builder),
erlang:element(14, Builder)}.
-file("src/dream_test/runner.gleam", 545).
?DOC(
" Filter which tests are executed.\n"
"\n"
" The predicate receives `TestInfo` (name, full name, effective tags, kind).\n"
" Tags include inherited group tags.\n"
"\n"
" Groups with no selected tests in their entire subtree are skipped entirely,\n"
" including hooks.\n"
"\n"
" ## Example\n"
"\n"
" ```gleam\n"
" import dream_test/matchers.{be_equal, or_fail_with, should, succeed}\n"
" import dream_test/reporters/bdd\n"
" import dream_test/reporters/progress\n"
" import dream_test/runner.{type TestInfo}\n"
" import dream_test/unit.{describe, it, with_tags}\n"
" import gleam/io\n"
" import gleam/list\n"
"\n"
" pub fn tests() {\n"
" describe(\"Filtering tests\", [\n"
" it(\"smoke\", fn() {\n"
" 1 + 1\n"
" |> should\n"
" |> be_equal(2)\n"
" |> or_fail_with(\"math should work\")\n"
" })\n"
" |> with_tags([\"smoke\"]),\n"
" it(\"slow\", fn() { Ok(succeed()) })\n"
" |> with_tags([\"slow\"]),\n"
" ])\n"
" }\n"
"\n"
" pub fn only_smoke(info: TestInfo) -> Bool {\n"
" list.contains(info.tags, \"smoke\")\n"
" }\n"
"\n"
" pub fn main() {\n"
" runner.new([tests()])\n"
" |> runner.filter_tests(only_smoke)\n"
" |> runner.progress_reporter(progress.new())\n"
" |> runner.results_reporters([bdd.new()])\n"
" |> runner.exit_on_failure()\n"
" |> runner.run()\n"
" }\n"
" ```\n"
"\n"
" ## Parameters\n"
"\n"
" - `builder`: the runner builder you’re configuring\n"
" - `predicate`: function that decides whether a test should run\n"
"\n"
" ## Returns\n"
"\n"
" The updated `RunBuilder(ctx)`.\n"
).
-spec filter_tests(
run_builder(KAX),
fun((dream_test@types:test_info()) -> boolean())
) -> run_builder(KAX).
filter_tests(Builder, Predicate) ->
{run_builder,
erlang:element(2, Builder),
erlang:element(3, Builder),
{some, Predicate},
erlang:element(5, Builder),
erlang:element(6, Builder),
erlang:element(7, Builder),
erlang:element(8, Builder),
erlang:element(9, Builder),
erlang:element(10, Builder),
erlang:element(11, Builder),
erlang:element(12, Builder),
erlang:element(13, Builder),
erlang:element(14, Builder)}.
-file("src/dream_test/runner.gleam", 572).
?DOC(
" Register a runner-level hook that runs before each test.\n"
"\n"
" The hook receives `types.TestInfo` and the current context value.\n"
" If it returns `Error(\"...\")`, the test is marked `SetupFailed`\n"
" and the test body does not run.\n"
"\n"
" ## Example\n"
"\n"
" ```gleam\n"
" import dream_test/runner\n"
" import dream_test/types.{type TestInfo}\n"
"\n"
" fn log_test(info: TestInfo, ctx: Nil) {\n"
" io.println(\"starting \" <> string.join(info.full_name, \" > \"))\n"
" Ok(ctx)\n"
" }\n"
"\n"
" runner.new([suite])\n"
" |> runner.before_each_test(log_test)\n"
" ```\n"
).
-spec before_each_test(
run_builder(KBA),
fun((dream_test@types:test_info(), KBA) -> {ok, KBA} | {error, binary()})
) -> run_builder(KBA).
before_each_test(Builder, Hook) ->
{run_builder,
erlang:element(2, Builder),
erlang:element(3, Builder),
erlang:element(4, Builder),
erlang:element(5, Builder),
erlang:element(6, Builder),
erlang:element(7, Builder),
erlang:element(8, Builder),
lists:append(erlang:element(9, Builder), [Hook]),
erlang:element(10, Builder),
erlang:element(11, Builder),
erlang:element(12, Builder),
erlang:element(13, Builder),
erlang:element(14, Builder)}.
-file("src/dream_test/runner.gleam", 586).
?DOC(
" Register a runner-level hook that runs after each test.\n"
"\n"
" The hook receives `types.TestInfo` and the current context value.\n"
" If it returns `Error(\"...\")`, the test is marked `Failed`.\n"
).
-spec after_each_test(
run_builder(KBF),
fun((dream_test@types:test_info(), KBF) -> {ok, nil} | {error, binary()})
) -> run_builder(KBF).
after_each_test(Builder, Hook) ->
{run_builder,
erlang:element(2, Builder),
erlang:element(3, Builder),
erlang:element(4, Builder),
erlang:element(5, Builder),
erlang:element(6, Builder),
erlang:element(7, Builder),
erlang:element(8, Builder),
erlang:element(9, Builder),
lists:append(erlang:element(10, Builder), [Hook]),
erlang:element(11, Builder),
erlang:element(12, Builder),
erlang:element(13, Builder),
erlang:element(14, Builder)}.
-file("src/dream_test/runner.gleam", 601).
?DOC(
" Register a runner-level hook that runs before each suite.\n"
"\n"
" The hook receives `types.SuiteInfo` for the suite about to run.\n"
" If it returns `Error(\"...\")`, all tests in that suite are marked `Failed`\n"
" and the suite is skipped.\n"
).
-spec before_each_suite(
run_builder(KBK),
fun((dream_test@types:suite_info()) -> {ok, nil} | {error, binary()})
) -> run_builder(KBK).
before_each_suite(Builder, Hook) ->
{run_builder,
erlang:element(2, Builder),
erlang:element(3, Builder),
erlang:element(4, Builder),
erlang:element(5, Builder),
erlang:element(6, Builder),
erlang:element(7, Builder),
erlang:element(8, Builder),
erlang:element(9, Builder),
erlang:element(10, Builder),
lists:append(erlang:element(11, Builder), [Hook]),
erlang:element(12, Builder),
erlang:element(13, Builder),
erlang:element(14, Builder)}.
-file("src/dream_test/runner.gleam", 617).
?DOC(
" Register a runner-level hook that runs after each suite.\n"
"\n"
" The hook receives `types.SuiteInfo` for the suite that just ran.\n"
" If it returns `Error(\"...\")`, a synthetic failure result is appended.\n"
).
-spec after_each_suite(
run_builder(KBP),
fun((dream_test@types:suite_info()) -> {ok, nil} | {error, binary()})
) -> run_builder(KBP).
after_each_suite(Builder, Hook) ->
{run_builder,
erlang:element(2, Builder),
erlang:element(3, Builder),
erlang:element(4, Builder),
erlang:element(5, Builder),
erlang:element(6, Builder),
erlang:element(7, Builder),
erlang:element(8, Builder),
erlang:element(9, Builder),
erlang:element(10, Builder),
erlang:element(11, Builder),
lists:append(erlang:element(12, Builder), [Hook]),
erlang:element(13, Builder),
erlang:element(14, Builder)}.
-file("src/dream_test/runner.gleam", 632).
?DOC(
" Register a runner-level hook that runs before all suites.\n"
"\n"
" The hook receives the full list of `SuiteInfo` after filtering.\n"
" If it returns `Error(\"...\")`, all selected tests are marked `Failed`\n"
" and the run short-circuits.\n"
).
-spec before_all_suites(
run_builder(KBU),
fun((list(dream_test@types:suite_info())) -> {ok, nil} | {error, binary()})
) -> run_builder(KBU).
before_all_suites(Builder, Hook) ->
{run_builder,
erlang:element(2, Builder),
erlang:element(3, Builder),
erlang:element(4, Builder),
erlang:element(5, Builder),
erlang:element(6, Builder),
erlang:element(7, Builder),
erlang:element(8, Builder),
erlang:element(9, Builder),
erlang:element(10, Builder),
erlang:element(11, Builder),
erlang:element(12, Builder),
lists:append(erlang:element(13, Builder), [Hook]),
erlang:element(14, Builder)}.
-file("src/dream_test/runner.gleam", 648).
?DOC(
" Register a runner-level hook that runs after all suites.\n"
"\n"
" The hook receives the full list of `SuiteInfo` after filtering.\n"
" If it returns `Error(\"...\")`, a synthetic failure result is appended.\n"
).
-spec after_all_suites(
run_builder(KCA),
fun((list(dream_test@types:suite_info())) -> {ok, nil} | {error, binary()})
) -> run_builder(KCA).
after_all_suites(Builder, Hook) ->
{run_builder,
erlang:element(2, Builder),
erlang:element(3, Builder),
erlang:element(4, Builder),
erlang:element(5, Builder),
erlang:element(6, Builder),
erlang:element(7, Builder),
erlang:element(8, Builder),
erlang:element(9, Builder),
erlang:element(10, Builder),
erlang:element(11, Builder),
erlang:element(12, Builder),
erlang:element(13, Builder),
lists:append(erlang:element(14, Builder), [Hook])}.
-file("src/dream_test/runner.gleam", 821).
-spec suite_name_from_tree(dream_test@types:node_(any())) -> binary().
suite_name_from_tree(Tree) ->
case Tree of
{group, Name, _, _} ->
Name;
_ ->
<<"<unknown-suite>"/utf8>>
end.
-file("src/dream_test/runner.gleam", 912).
-spec all_sources_match(binary(), list(dream_test@types:test_info())) -> boolean().
all_sources_match(Value, Rest) ->
case Rest of
[] ->
true;
[{test_info, _, _, _, _, Source} | Next] ->
case Source of
{some, S} ->
case S =:= Value of
true ->
all_sources_match(Value, Next);
false ->
false
end;
none ->
false
end
end.
-file("src/dream_test/runner.gleam", 898).
-spec suite_source_from_tests_with_first(
gleam@option:option(binary()),
list(dream_test@types:test_info())
) -> gleam@option:option(binary()).
suite_source_from_tests_with_first(First, Rest) ->
case First of
none ->
none;
{some, Value} ->
case all_sources_match(Value, Rest) of
true ->
{some, Value};
false ->
none
end
end.
-file("src/dream_test/runner.gleam", 882).
-spec suite_source_from_tests(list(dream_test@types:test_info())) -> gleam@option:option(binary()).
suite_source_from_tests(Tests) ->
case Tests of
[] ->
none;
[{test_info, _, _, _, _, Source} | Rest] ->
suite_source_from_tests_with_first(Source, Rest)
end.
-file("src/dream_test/runner.gleam", 936).
-spec run_before_all_suites_hooks(
list(fun((list(dream_test@types:suite_info())) -> {ok, nil} |
{error, binary()})),
list(dream_test@types:suite_info())
) -> {ok, nil} | {error, binary()}.
run_before_all_suites_hooks(Hooks, Suites) ->
case Hooks of
[] ->
{ok, nil};
[Hook | Rest] ->
case Hook(Suites) of
{ok, _} ->
run_before_all_suites_hooks(Rest, Suites);
{error, Message} ->
{error, Message}
end
end.
-file("src/dream_test/runner.gleam", 950).
-spec run_before_each_suite_hooks(
list(fun((dream_test@types:suite_info()) -> {ok, nil} | {error, binary()})),
dream_test@types:suite_info()
) -> {ok, nil} | {error, binary()}.
run_before_each_suite_hooks(Hooks, Suite) ->
case Hooks of
[] ->
{ok, nil};
[Hook | Rest] ->
case Hook(Suite) of
{ok, _} ->
run_before_each_suite_hooks(Rest, Suite);
{error, Message} ->
{error, Message}
end
end.
-file("src/dream_test/runner.gleam", 964).
-spec run_after_each_suite_hooks(
list(fun((dream_test@types:suite_info()) -> {ok, nil} | {error, binary()})),
dream_test@types:suite_info()
) -> {ok, nil} | {error, binary()}.
run_after_each_suite_hooks(Hooks, Suite) ->
case Hooks of
[] ->
{ok, nil};
[Hook | Rest] ->
case Hook(Suite) of
{ok, _} ->
run_after_each_suite_hooks(Rest, Suite);
{error, Message} ->
{error, Message}
end
end.
-file("src/dream_test/runner.gleam", 978).
-spec run_after_all_suites_hooks(
list(fun((list(dream_test@types:suite_info())) -> {ok, nil} |
{error, binary()})),
list(dream_test@types:suite_info())
) -> {ok, nil} | {error, binary()}.
run_after_all_suites_hooks(Hooks, Suites) ->
case Hooks of
[] ->
{ok, nil};
[Hook | Rest] ->
case Hook(Suites) of
{ok, _} ->
run_after_all_suites_hooks(Rest, Suites);
{error, Message} ->
{error, Message}
end
end.
-file("src/dream_test/runner.gleam", 1233).
-spec hook_failure(binary(), binary()) -> dream_test@types:assertion_failure().
hook_failure(Operator, Message) ->
{assertion_failure, Operator, Message, none}.
-file("src/dream_test/runner.gleam", 1182).
-spec failed_result_for_test_info(
dream_test@types:test_info(),
binary(),
binary()
) -> dream_test@types:test_result().
failed_result_for_test_info(Info, Operator, Message) ->
{test_info, Name, Full_name, Tags, Kind, _} = Info,
{test_result,
Name,
Full_name,
failed,
0,
Tags,
[hook_failure(Operator, Message)],
Kind}.
-file("src/dream_test/runner.gleam", 1166).
-spec build_failed_results_from_tests_rev(
list(dream_test@types:test_info()),
binary(),
binary(),
list(dream_test@types:test_result())
) -> list(dream_test@types:test_result()).
build_failed_results_from_tests_rev(Tests, Operator, Message, Acc_rev) ->
case Tests of
[] ->
Acc_rev;
[Test_info | Rest] ->
build_failed_results_from_tests_rev(
Rest,
Operator,
Message,
[failed_result_for_test_info(Test_info, Operator, Message) |
Acc_rev]
)
end.
-file("src/dream_test/runner.gleam", 1134).
-spec build_failed_results_from_suites_rev(
list(dream_test@types:suite_info()),
binary(),
binary(),
list(dream_test@types:test_result())
) -> list(dream_test@types:test_result()).
build_failed_results_from_suites_rev(Suites, Operator, Message, Acc_rev) ->
case Suites of
[] ->
Acc_rev;
[Suite | Rest] ->
build_failed_results_from_suites_rev(
Rest,
Operator,
Message,
build_failed_results_from_tests_rev(
erlang:element(3, Suite),
Operator,
Message,
Acc_rev
)
)
end.
-file("src/dream_test/runner.gleam", 1125).
-spec build_failed_results_from_suites(
list(dream_test@types:suite_info()),
binary(),
binary()
) -> list(dream_test@types:test_result()).
build_failed_results_from_suites(Suites, Operator, Message) ->
_pipe = build_failed_results_from_suites_rev(Suites, Operator, Message, []),
lists:reverse(_pipe).
-file("src/dream_test/runner.gleam", 1157).
-spec build_failed_results_from_tests(
list(dream_test@types:test_info()),
binary(),
binary()
) -> list(dream_test@types:test_result()).
build_failed_results_from_tests(Tests, Operator, Message) ->
_pipe = build_failed_results_from_tests_rev(Tests, Operator, Message, []),
lists:reverse(_pipe).
-file("src/dream_test/runner.gleam", 1205).
-spec after_all_suites_failure_result(binary()) -> dream_test@types:test_result().
after_all_suites_failure_result(Message) ->
{test_result,
<<"<after_all_suites>"/utf8>>,
[<<"<after_all_suites>"/utf8>>],
failed,
0,
[],
[hook_failure(<<"after_all_suites"/utf8>>, Message)],
unit}.
-file("src/dream_test/runner.gleam", 1113).
-spec apply_after_all_suites_hooks(
list(fun((list(dream_test@types:suite_info())) -> {ok, nil} |
{error, binary()})),
list(dream_test@types:suite_info()),
list(dream_test@types:test_result())
) -> list(dream_test@types:test_result()).
apply_after_all_suites_hooks(Hooks, Suites, Results) ->
case run_after_all_suites_hooks(Hooks, Suites) of
{ok, _} ->
Results;
{error, Message} ->
lists:append(Results, [after_all_suites_failure_result(Message)])
end.
-file("src/dream_test/runner.gleam", 1217).
-spec after_each_suite_failure_result(dream_test@types:suite_info(), binary()) -> dream_test@types:test_result().
after_each_suite_failure_result(Suite_info, Message) ->
{suite_info, Name, _, _} = Suite_info,
{test_result,
<<"<after_each_suite>"/utf8>>,
[Name, <<"<after_each_suite>"/utf8>>],
failed,
0,
[],
[hook_failure(<<"after_each_suite"/utf8>>, Message)],
unit}.
-file("src/dream_test/runner.gleam", 1099).
-spec apply_after_each_suite_hooks(
list(fun((dream_test@types:suite_info()) -> {ok, nil} | {error, binary()})),
dream_test@types:suite_info(),
list(dream_test@types:test_result())
) -> list(dream_test@types:test_result()).
apply_after_each_suite_hooks(Hooks, Suite_info, Results) ->
case run_after_each_suite_hooks(Hooks, Suite_info) of
{ok, _} ->
Results;
{error, Message} ->
lists:append(
Results,
[after_each_suite_failure_result(Suite_info, Message)]
)
end.
-file("src/dream_test/runner.gleam", 1288).
-spec default_output() -> output().
default_output() ->
{output, fun gleam_stdlib:print/1, fun gleam_stdlib:print_error/1}.
-file("src/dream_test/runner.gleam", 1296).
-spec discard_output(binary()) -> nil.
discard_output(_) ->
nil.
-file("src/dream_test/runner.gleam", 1292).
-spec silent_output() -> output().
silent_output() ->
{output, fun discard_output/1, fun discard_output/1}.
-file("src/dream_test/runner.gleam", 486).
?DOC(" Disable all reporter output (still returns results from `runner.run()`).\n").
-spec silent(run_builder(KAU)) -> run_builder(KAU).
silent(Builder) ->
{run_builder,
erlang:element(2, Builder),
erlang:element(3, Builder),
erlang:element(4, Builder),
erlang:element(5, Builder),
erlang:element(6, Builder),
erlang:element(7, Builder),
{some, silent_output()},
erlang:element(9, Builder),
erlang:element(10, Builder),
erlang:element(11, Builder),
erlang:element(12, Builder),
erlang:element(13, Builder),
erlang:element(14, Builder)}.
-file("src/dream_test/runner.gleam", 1451).
-spec suite_run_config(
dream_test@parallel:parallel_config(),
gleam@option:option(dream_test@parallel:parallel_config())
) -> dream_test@parallel:parallel_config().
suite_run_config(Default_config, Override) ->
case Override of
none ->
Default_config;
{some, Config} ->
Config
end.
-file("src/dream_test/runner.gleam", 1494).
-spec render_results_reporter(
dream_test@reporters@types:results_reporter(),
list(dream_test@types:test_result())
) -> binary().
render_results_reporter(Reporter, Results) ->
case Reporter of
{bdd, Config} ->
dream_test@reporters@bdd:render(Config, Results);
{json, Config@1} ->
dream_test@reporters@json:render(Config@1, Results)
end.
-file("src/dream_test/runner.gleam", 1480).
-spec write_results_reporters_loop(
list(dream_test@reporters@types:results_reporter()),
list(dream_test@types:test_result()),
fun((binary()) -> nil)
) -> nil.
write_results_reporters_loop(Reporters, Results, Write) ->
case Reporters of
[] ->
nil;
[Reporter | Rest] ->
Write(render_results_reporter(Reporter, Results)),
write_results_reporters_loop(Rest, Results, Write)
end.
-file("src/dream_test/runner.gleam", 1504).
-spec output_out(output()) -> fun((binary()) -> nil).
output_out(Output) ->
{output, Out, _} = Output,
Out.
-file("src/dream_test/runner.gleam", 1461).
-spec write_progress_event(
dream_test@reporters@progress:progress_reporter(),
dream_test@reporters@types:reporter_event(),
output()
) -> nil.
write_progress_event(Reporter, Event, Output) ->
case dream_test@reporters@progress:handle_event(Reporter, Event) of
none ->
nil;
{some, Text} ->
(output_out(Output))(Text)
end.
-file("src/dream_test/runner.gleam", 1237).
-spec emit_test_finished_progress(
gleam@option:option(dream_test@reporters@progress:progress_reporter()),
output(),
integer(),
integer(),
dream_test@types:test_result()
) -> integer().
emit_test_finished_progress(Progress_reporter, Output, Completed, Total, Result) ->
Next_completed = Completed + 1,
case Progress_reporter of
none ->
Next_completed;
{some, Reporter} ->
write_progress_event(
Reporter,
{test_finished, Next_completed, Total, Result},
Output
),
Next_completed
end.
-file("src/dream_test/runner.gleam", 1262).
-spec emit_test_finished_progress_results(
gleam@option:option(dream_test@reporters@progress:progress_reporter()),
output(),
integer(),
integer(),
list(dream_test@types:test_result())
) -> integer().
emit_test_finished_progress_results(
Progress_reporter,
Output,
Completed,
Total,
Results
) ->
case Results of
[] ->
Completed;
[Result | Rest] ->
emit_test_finished_progress_results(
Progress_reporter,
Output,
emit_test_finished_progress(
Progress_reporter,
Output,
Completed,
Total,
Result
),
Total,
Rest
)
end.
-file("src/dream_test/runner.gleam", 992).
-spec run_suites_with_hooks(
list(suite_run(KEJ)),
list(dream_test@types:suite_info()),
dream_test@parallel:parallel_config(),
gleam@option:option(dream_test@reporters@progress:progress_reporter()),
output(),
integer(),
integer(),
list(fun((dream_test@types:suite_info()) -> {ok, nil} | {error, binary()})),
list(fun((dream_test@types:suite_info()) -> {ok, nil} | {error, binary()})),
list(fun((dream_test@types:test_info(), KEJ) -> {ok, KEJ} |
{error, binary()})),
list(fun((dream_test@types:test_info(), KEJ) -> {ok, nil} |
{error, binary()})),
list(dream_test@types:test_result())
) -> {list(dream_test@types:test_result()),
integer(),
gleam@option:option(dream_test@reporters@progress:progress_reporter())}.
run_suites_with_hooks(
Suite_runs,
Suite_infos,
Default_config,
Progress_reporter,
Output,
Total,
Completed,
Before_each_suite_hooks,
After_each_suite_hooks,
Runner_before_each_test,
Runner_after_each_test,
Acc
) ->
case {Suite_runs, Suite_infos} of
{[], []} ->
{Acc, Completed, Progress_reporter};
{[{suite_run, Suite, Override} | Rest], [Suite_info | Rest_infos]} ->
Before_result = run_before_each_suite_hooks(
Before_each_suite_hooks,
Suite_info
),
case Before_result of
{error, Message} ->
Failures = build_failed_results_from_tests(
erlang:element(3, Suite_info),
<<"before_each_suite"/utf8>>,
Message
),
Completed_after_failures = emit_test_finished_progress_results(
Progress_reporter,
Output,
Completed,
Total,
Failures
),
Acc_with_failures = lists:append(Acc, Failures),
Acc_after_after_each = apply_after_each_suite_hooks(
After_each_suite_hooks,
Suite_info,
Acc_with_failures
),
run_suites_with_hooks(
Rest,
Rest_infos,
Default_config,
Progress_reporter,
Output,
Total,
Completed_after_failures,
Before_each_suite_hooks,
After_each_suite_hooks,
Runner_before_each_test,
Runner_after_each_test,
Acc_after_after_each
);
{ok, _} ->
Suite_config = suite_run_config(Default_config, Override),
Parallel_result = dream_test@parallel:run_root_parallel_with_reporter(
{run_root_parallel_with_reporter_config,
Suite_config,
Suite,
Progress_reporter,
output_out(Output),
Total,
Completed,
Runner_before_each_test,
Runner_after_each_test}
),
{run_root_parallel_with_reporter_result,
Results,
Completed_after_suite,
Next_progress_reporter} = Parallel_result,
Acc_with_results = lists:append(Acc, Results),
Acc_after_after_each@1 = apply_after_each_suite_hooks(
After_each_suite_hooks,
Suite_info,
Acc_with_results
),
run_suites_with_hooks(
Rest,
Rest_infos,
Default_config,
Next_progress_reporter,
Output,
Total,
Completed_after_suite,
Before_each_suite_hooks,
After_each_suite_hooks,
Runner_before_each_test,
Runner_after_each_test,
Acc_after_after_each@1
)
end;
{_, _} ->
{Acc, Completed, Progress_reporter}
end.
-file("src/dream_test/runner.gleam", 1472).
-spec write_results_reporters(
list(dream_test@reporters@types:results_reporter()),
list(dream_test@types:test_result()),
output()
) -> nil.
write_results_reporters(Reporters, Results, Output) ->
write_results_reporters_loop(Reporters, Results, output_out(Output)).
-file("src/dream_test/runner.gleam", 1541).
-spec suites_to_suite_runs_default(
list(dream_test@types:root(KIA)),
list(suite_run(KIA))
) -> list(suite_run(KIA)).
suites_to_suite_runs_default(Suites, Acc_rev) ->
case Suites of
[] ->
lists:reverse(Acc_rev);
[Suite | Rest] ->
suites_to_suite_runs_default(
Rest,
[{suite_run, Suite, none} | Acc_rev]
)
end.
-file("src/dream_test/runner.gleam", 190).
?DOC(
" Create a new runner builder for a list of suites.\n"
"\n"
" The type parameter `ctx` is the suite context type. For `dream_test/unit`\n"
" suites this is `Nil`. For `dream_test/unit_context` suites it is your custom\n"
" context type.\n"
"\n"
" ## Example\n"
"\n"
" ```gleam\n"
" import dream_test/matchers.{be_equal, or_fail_with, should}\n"
" import dream_test/reporters/bdd\n"
" import dream_test/reporters/progress\n"
" import dream_test/runner\n"
" import dream_test/unit.{describe, it}\n"
" import gleam/io\n"
"\n"
" pub fn tests() {\n"
" describe(\"Example\", [\n"
" it(\"works\", fn() {\n"
" 1 + 1\n"
" |> should\n"
" |> be_equal(2)\n"
" |> or_fail_with(\"math should work\")\n"
" }),\n"
" ])\n"
" }\n"
"\n"
" pub fn main() {\n"
" runner.new([tests()])\n"
" |> runner.progress_reporter(progress.new())\n"
" |> runner.results_reporters([bdd.new()])\n"
" |> runner.exit_on_failure()\n"
" |> runner.run()\n"
" }\n"
" ```\n"
"\n"
" ## Parameters\n"
"\n"
" - `suites`: the test suites you want to run (often just `[tests()]`)\n"
"\n"
" ## Returns\n"
"\n"
" A `RunBuilder(ctx)` you can pipe through configuration helpers and finally\n"
" `runner.run()`.\n"
).
-spec new(list(dream_test@types:root(JZN))) -> run_builder(JZN).
new(Suites) ->
Config = dream_test@parallel:default_config(),
{run_builder,
suites_to_suite_runs_default(Suites, []),
Config,
none,
false,
none,
[],
none,
[],
[],
[],
[],
[],
[]}.
-file("src/dream_test/runner.gleam", 1555).
-spec suites_to_suite_runs_with_override(
dream_test@parallel:parallel_config(),
list(dream_test@types:root(KIH)),
list(suite_run(KIH))
) -> list(suite_run(KIH)).
suites_to_suite_runs_with_override(Config, Suites, Acc_rev) ->
case Suites of
[] ->
lists:reverse(Acc_rev);
[Suite | Rest] ->
suites_to_suite_runs_with_override(
Config,
Rest,
[{suite_run, Suite, {some, Config}} | Acc_rev]
)
end.
-file("src/dream_test/runner.gleam", 1570).
-spec append_suite_runs_default(
list(suite_run(KIO)),
list(dream_test@types:root(KIO))
) -> list(suite_run(KIO)).
append_suite_runs_default(Existing, Suites) ->
lists:append(Existing, suites_to_suite_runs_default(Suites, [])).
-file("src/dream_test/runner.gleam", 225).
?DOC(
" Append suites to the run, using the builder’s current execution config.\n"
"\n"
" This is useful when you want to build up your suite list incrementally\n"
" (especially when some suites need a different execution config).\n"
"\n"
" Suites added with `add_suites` will run using the runner’s current execution\n"
" config (configured via `max_concurrency` / `default_timeout_ms`).\n"
"\n"
" ## Example\n"
"\n"
" ```gleam\n"
" runner.new([])\n"
" |> runner.add_suites([unit_suite()])\n"
" |> runner.add_suites([integration_suite()])\n"
" |> runner.run()\n"
" ```\n"
).
-spec add_suites(run_builder(JZR), list(dream_test@types:root(JZR))) -> run_builder(JZR).
add_suites(Builder, Suites) ->
Appended = append_suite_runs_default(erlang:element(2, Builder), Suites),
{run_builder,
Appended,
erlang:element(3, Builder),
erlang:element(4, Builder),
erlang:element(5, Builder),
erlang:element(6, Builder),
erlang:element(7, Builder),
erlang:element(8, Builder),
erlang:element(9, Builder),
erlang:element(10, Builder),
erlang:element(11, Builder),
erlang:element(12, Builder),
erlang:element(13, Builder),
erlang:element(14, Builder)}.
-file("src/dream_test/runner.gleam", 1577).
-spec append_suite_runs_with_override(
list(suite_run(KIV)),
dream_test@parallel:parallel_config(),
list(dream_test@types:root(KIV))
) -> list(suite_run(KIV)).
append_suite_runs_with_override(Existing, Config, Suites) ->
lists:append(
Existing,
suites_to_suite_runs_with_override(Config, Suites, [])
).
-file("src/dream_test/runner.gleam", 257).
?DOC(
" Append suites to the run, using an explicit execution config override.\n"
"\n"
" This lets you run suites with different concurrency/timeout policies in a\n"
" single runner invocation (for example: DB suites sequential, unit suites\n"
" parallel).\n"
"\n"
" The override applies only to the suites added by this call. Reporting, output,\n"
" filtering, and exit behavior remain global runner settings.\n"
"\n"
" ## Example\n"
"\n"
" ```gleam\n"
" import dream_test/parallel\n"
"\n"
" let db_config =\n"
" parallel.ParallelConfig(max_concurrency: 1, default_timeout_ms: 60_000)\n"
"\n"
" runner.new([])\n"
" |> runner.add_suites([unit_suite()])\n"
" |> runner.add_suites_with_config(db_config, [db_suite()])\n"
" |> runner.max_concurrency(50)\n"
" |> runner.default_timeout_ms(5_000)\n"
" |> runner.run()\n"
" ```\n"
).
-spec add_suites_with_config(
run_builder(JZW),
dream_test@parallel:parallel_config(),
list(dream_test@types:root(JZW))
) -> run_builder(JZW).
add_suites_with_config(Builder, Config, Suites) ->
Appended = append_suite_runs_with_override(
erlang:element(2, Builder),
Config,
Suites
),
{run_builder,
Appended,
erlang:element(3, Builder),
erlang:element(4, Builder),
erlang:element(5, Builder),
erlang:element(6, Builder),
erlang:element(7, Builder),
erlang:element(8, Builder),
erlang:element(9, Builder),
erlang:element(10, Builder),
erlang:element(11, Builder),
erlang:element(12, Builder),
erlang:element(13, Builder),
erlang:element(14, Builder)}.
-file("src/dream_test/runner.gleam", 1631).
?DOC(
" Return `True` if the list contains any failing statuses.\n"
"\n"
" This treats `Failed`, `SetupFailed`, and `TimedOut` as failures.\n"
"\n"
" ## Example\n"
"\n"
" ```gleam\n"
" import dream_test/matchers.{be_equal, or_fail_with, should, succeed}\n"
" import dream_test/runner\n"
" import dream_test/unit.{describe, it}\n"
"\n"
" pub fn tests() {\n"
" describe(\"has_failures\", [\n"
" it(\"passes\", fn() { Ok(succeed()) }),\n"
" ])\n"
" }\n"
"\n"
" fn failing_suite() {\n"
" describe(\"failing suite\", [\n"
" it(\"fails\", fn() {\n"
" 1\n"
" |> should\n"
" |> be_equal(2)\n"
" |> or_fail_with(\"intentional failure for has_failures example\")\n"
" }),\n"
" ])\n"
" }\n"
"\n"
" pub fn main() {\n"
" let results = runner.new([failing_suite()]) |> runner.run()\n"
"\n"
" results\n"
" |> runner.has_failures()\n"
" |> should\n"
" |> be_equal(True)\n"
" |> or_fail_with(\"expected failures to be present\")\n"
" }\n"
" ```\n"
"\n"
" ## Parameters\n"
"\n"
" - `results`: list of `TestResult` values returned by `runner.run`\n"
"\n"
" ## Returns\n"
"\n"
" `True` when any result has status `Failed`, `SetupFailed`, or `TimedOut`.\n"
).
-spec has_failures(list(dream_test@types:test_result())) -> boolean().
has_failures(Results) ->
case Results of
[] ->
false;
[R | Rest] ->
case erlang:element(4, R) of
failed ->
true;
setup_failed ->
true;
timed_out ->
true;
_ ->
has_failures(Rest)
end
end.
-file("src/dream_test/runner.gleam", 1300).
-spec maybe_exit_on_failure(boolean(), list(dream_test@types:test_result())) -> nil.
maybe_exit_on_failure(Should_exit, Results) ->
Should_halt = Should_exit andalso has_failures(Results),
case Should_halt of
true ->
erlang:halt(1);
false ->
nil
end.
-file("src/dream_test/runner.gleam", 864).
-spec collect_test_infos_children(
list(dream_test@types:node_(any())),
list(binary()),
list(binary()),
list(dream_test@types:test_info())
) -> list(dream_test@types:test_info()).
collect_test_infos_children(Children, Scope, Inherited_tags, Acc_rev) ->
case Children of
[] ->
Acc_rev;
[Child | Rest] ->
collect_test_infos_children(
Rest,
Scope,
Inherited_tags,
collect_test_infos(Child, Scope, Inherited_tags, Acc_rev)
)
end.
-file("src/dream_test/runner.gleam", 828).
-spec collect_test_infos(
dream_test@types:node_(any()),
list(binary()),
list(binary()),
list(dream_test@types:test_info())
) -> list(dream_test@types:test_info()).
collect_test_infos(Node, Scope, Inherited_tags, Acc_rev) ->
case Node of
{test, Name, Tags, Kind, _, _, Source} ->
Full_name = lists:append(Scope, [Name]),
Effective_tags = lists:append(Inherited_tags, Tags),
Info = {test_info, Name, Full_name, Effective_tags, Kind, Source},
[Info | Acc_rev];
{group, Name@1, Tags@1, Children} ->
Next_scope = lists:append(Scope, [Name@1]),
Next_tags = lists:append(Inherited_tags, Tags@1),
collect_test_infos_children(
Children,
Next_scope,
Next_tags,
Acc_rev
);
_ ->
Acc_rev
end.
-file("src/dream_test/runner.gleam", 809).
-spec suite_info_from_suite(dream_test@types:root(any())) -> dream_test@types:suite_info().
suite_info_from_suite(Suite) ->
{root, _, Tree} = Suite,
Tests_rev = collect_test_infos(Tree, [], [], []),
Tests = lists:reverse(Tests_rev),
Source = suite_source_from_tests(Tests),
{suite_info, suite_name_from_tree(Tree), Tests, Source}.
-file("src/dream_test/runner.gleam", 798).
-spec suite_infos_from_runs(
list(suite_run(any())),
list(dream_test@types:suite_info())
) -> list(dream_test@types:suite_info()).
suite_infos_from_runs(Suite_runs, Acc_rev) ->
case Suite_runs of
[] ->
lists:reverse(Acc_rev);
[{suite_run, Suite, _} | Rest] ->
suite_infos_from_runs(
Rest,
[suite_info_from_suite(Suite) | Acc_rev]
)
end.
-file("src/dream_test/runner.gleam", 1529).
-spec count_tests_in_children(list(dream_test@types:node_(any())), integer()) -> integer().
count_tests_in_children(Children, Acc) ->
case Children of
[] ->
Acc;
[Child | Rest] ->
count_tests_in_children(Rest, Acc + count_tests_in_suite(Child))
end.
-file("src/dream_test/runner.gleam", 1521).
-spec count_tests_in_suite(dream_test@types:node_(any())) -> integer().
count_tests_in_suite(Node) ->
case Node of
{test, _, _, _, _, _, _} ->
1;
{group, _, _, Children} ->
count_tests_in_children(Children, 0);
_ ->
0
end.
-file("src/dream_test/runner.gleam", 1513).
-spec count_total_tests_from_list(list(suite_run(any())), integer()) -> integer().
count_total_tests_from_list(Suite_runs, Acc) ->
case Suite_runs of
[] ->
Acc;
[{suite_run, Suite, _} | Rest] ->
count_total_tests_from_list(
Rest,
Acc + count_tests_in_suite(erlang:element(3, Suite))
)
end.
-file("src/dream_test/runner.gleam", 1509).
-spec count_total_tests(list(suite_run(any()))) -> integer().
count_total_tests(Suite_runs) ->
count_total_tests_from_list(Suite_runs, 0).
-file("src/dream_test/runner.gleam", 1413).
-spec filter_children(
list(dream_test@types:node_(KHA)),
list(binary()),
list(binary()),
fun((dream_test@types:test_info()) -> boolean()),
list(dream_test@types:node_(KHA)),
boolean()
) -> {list(dream_test@types:node_(KHA)), boolean()}.
filter_children(Children, Scope, Inherited_tags, Predicate, Acc_rev, Has_tests) ->
case Children of
[] ->
{lists:reverse(Acc_rev), Has_tests};
[Child | Rest] ->
{Maybe_child, Child_has_tests} = filter_node(
Child,
Scope,
Inherited_tags,
Predicate
),
Next_has_tests = Has_tests orelse Child_has_tests,
case Maybe_child of
none ->
filter_children(
Rest,
Scope,
Inherited_tags,
Predicate,
Acc_rev,
Next_has_tests
);
{some, Kept} ->
filter_children(
Rest,
Scope,
Inherited_tags,
Predicate,
[Kept | Acc_rev],
Next_has_tests
)
end
end.
-file("src/dream_test/runner.gleam", 1351).
-spec filter_node(
dream_test@types:node_(KGU),
list(binary()),
list(binary()),
fun((dream_test@types:test_info()) -> boolean())
) -> {gleam@option:option(dream_test@types:node_(KGU)), boolean()}.
filter_node(Node, Scope, Inherited_tags, Predicate) ->
case Node of
{test, Name, Tags, Kind, Run, Timeout_ms, Source} ->
Full_name = lists:append(Scope, [Name]),
Effective_tags = lists:append(Inherited_tags, Tags),
Info = {test_info, Name, Full_name, Effective_tags, Kind, Source},
case Predicate(Info) of
true ->
{{some, {test, Name, Tags, Kind, Run, Timeout_ms, Source}},
true};
false ->
{none, false}
end;
{group, Name@1, Tags@1, Children} ->
Next_scope = lists:append(Scope, [Name@1]),
Next_tags = lists:append(Inherited_tags, Tags@1),
{Filtered_children, Has_tests} = filter_children(
Children,
Next_scope,
Next_tags,
Predicate,
[],
false
),
case Has_tests of
true ->
{{some, {group, Name@1, Tags@1, Filtered_children}}, true};
false ->
{none, false}
end;
{before_all, _} ->
{{some, Node}, false};
{before_each, _} ->
{{some, Node}, false};
{after_each, _} ->
{{some, Node}, false};
{after_all, _} ->
{{some, Node}, false}
end.
-file("src/dream_test/runner.gleam", 1339).
-spec filter_root(
dream_test@types:root(KGQ),
fun((dream_test@types:test_info()) -> boolean())
) -> {gleam@option:option(dream_test@types:root(KGQ)), boolean()}.
filter_root(Suite, Predicate) ->
{root, Seed, Tree} = Suite,
{Maybe_tree, Has_tests} = filter_node(Tree, [], [], Predicate),
case Maybe_tree of
none ->
{none, Has_tests};
{some, Next_tree} ->
{{some, {root, Seed, Next_tree}}, Has_tests}
end.
-file("src/dream_test/runner.gleam", 1318).
-spec filter_suite_runs(
list(suite_run(KGJ)),
fun((dream_test@types:test_info()) -> boolean()),
list(suite_run(KGJ))
) -> list(suite_run(KGJ)).
filter_suite_runs(Suite_runs, Predicate, Acc_rev) ->
case Suite_runs of
[] ->
lists:reverse(Acc_rev);
[{suite_run, Suite, Override} | Rest] ->
{Maybe, _} = filter_root(Suite, Predicate),
case Maybe of
none ->
filter_suite_runs(Rest, Predicate, Acc_rev);
{some, Filtered} ->
filter_suite_runs(
Rest,
Predicate,
[{suite_run, Filtered, Override} | Acc_rev]
)
end
end.
-file("src/dream_test/runner.gleam", 1308).
-spec apply_test_filter(
list(suite_run(KGD)),
gleam@option:option(fun((dream_test@types:test_info()) -> boolean()))
) -> list(suite_run(KGD)).
apply_test_filter(Suite_runs, Predicate) ->
case Predicate of
none ->
Suite_runs;
{some, P} ->
filter_suite_runs(Suite_runs, P, [])
end.
-file("src/dream_test/runner.gleam", 699).
?DOC(
" Run all suites and return a list of `TestResult`.\n"
"\n"
" If a progress reporter is attached, the runner will emit progress output during\n"
" the run. Results reporters print at the end of the run.\n"
"\n"
" ## Example\n"
"\n"
" ```gleam\n"
" import dream_test/matchers.{be_equal, or_fail_with, should}\n"
" import dream_test/reporters/bdd\n"
" import dream_test/reporters/progress\n"
" import dream_test/runner\n"
" import dream_test/unit.{describe, it}\n"
"\n"
" pub fn tests() {\n"
" describe(\"Example\", [\n"
" it(\"works\", fn() {\n"
" 1 + 1\n"
" |> should\n"
" |> be_equal(2)\n"
" |> or_fail_with(\"math should work\")\n"
" }),\n"
" ])\n"
" }\n"
"\n"
" pub fn main() {\n"
" runner.new([tests()])\n"
" |> runner.progress_reporter(progress.new())\n"
" |> runner.results_reporters([bdd.new()])\n"
" |> runner.exit_on_failure()\n"
" |> runner.run()\n"
" }\n"
" ```\n"
"\n"
" ## Parameters\n"
"\n"
" - `builder`: the fully configured runner builder\n"
"\n"
" ## Returns\n"
"\n"
" A list of `TestResult` values, in deterministic order.\n"
).
-spec run(run_builder(any())) -> list(dream_test@types:test_result()).
run(Builder) ->
Selected_runs = apply_test_filter(
erlang:element(2, Builder),
erlang:element(4, Builder)
),
Total = count_total_tests(Selected_runs),
Suite_infos = suite_infos_from_runs(Selected_runs, []),
Output@1 = case erlang:element(8, Builder) of
{some, Output} ->
Output;
none ->
default_output()
end,
Completed0 = 0,
Progress_reporter0 = erlang:element(6, Builder),
Completed1 = case Progress_reporter0 of
none ->
Completed0;
{some, Progress_reporter} ->
write_progress_event(
Progress_reporter,
{run_started, Total},
Output@1
),
Completed0
end,
Before_all_result = run_before_all_suites_hooks(
erlang:element(13, Builder),
Suite_infos
),
{Results, Completed2, Progress_reporter1} = case Before_all_result of
{ok, _} ->
run_suites_with_hooks(
Selected_runs,
Suite_infos,
erlang:element(3, Builder),
Progress_reporter0,
Output@1,
Total,
Completed1,
erlang:element(11, Builder),
erlang:element(12, Builder),
erlang:element(9, Builder),
erlang:element(10, Builder),
[]
);
{error, Message} ->
Failures = build_failed_results_from_suites(
Suite_infos,
<<"before_all_suites"/utf8>>,
Message
),
Completed_after_failures = emit_test_finished_progress_results(
Progress_reporter0,
Output@1,
Completed1,
Total,
Failures
),
Results_with_after_all = apply_after_all_suites_hooks(
erlang:element(14, Builder),
Suite_infos,
Failures
),
{Results_with_after_all,
Completed_after_failures,
Progress_reporter0}
end,
Final_results = case Before_all_result of
{ok, _} ->
apply_after_all_suites_hooks(
erlang:element(14, Builder),
Suite_infos,
Results
);
{error, _} ->
Results
end,
case Progress_reporter1 of
none ->
nil;
{some, Progress_reporter@1} ->
write_progress_event(
Progress_reporter@1,
{run_finished, Completed2, Total, Final_results},
Output@1
)
end,
write_results_reporters(erlang:element(7, Builder), Final_results, Output@1),
maybe_exit_on_failure(erlang:element(5, Builder), Final_results),
Final_results.