-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, silent/1, new/1, add_suites/2, add_suites_with_config/3, has_failures/1, run/1]). -export_type([test_info/0, 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" ). -type test_info() :: {test_info, binary(), list(binary()), list(binary()), dream_test@types:test_kind()}. -opaque run_builder(JSL) :: {run_builder, list(suite_run(JSL)), dream_test@parallel:parallel_config(), gleam@option:option(fun((test_info()) -> boolean())), boolean(), gleam@option:option(dream_test@reporters@progress:progress_reporter()), list(dream_test@reporters@types:results_reporter()), gleam@option:option(output())}. -type suite_run(JSM) :: {suite_run, dream_test@types:root(JSM), gleam@option:option(dream_test@parallel:parallel_config())}. -type output() :: {output, fun((binary()) -> nil), fun((binary()) -> nil)}. -file("src/dream_test/runner.gleam", 350). ?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(JTB), integer()) -> run_builder(JTB). 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)}. -file("src/dream_test/runner.gleam", 409). ?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(JTE), integer()) -> run_builder(JTE). 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)}. -file("src/dream_test/runner.gleam", 465). ?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(JTH)) -> run_builder(JTH). 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)}. -file("src/dream_test/runner.gleam", 473). ?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(JTK), dream_test@reporters@progress:progress_reporter() ) -> run_builder(JTK). 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)}. -file("src/dream_test/runner.gleam", 484). ?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(JTN), list(dream_test@reporters@types:results_reporter()) ) -> run_builder(JTN). 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)}. -file("src/dream_test/runner.gleam", 508). ?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(JTR), output()) -> run_builder(JTR). 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}}. -file("src/dream_test/runner.gleam", 575). ?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(JTX), fun((test_info()) -> boolean())) -> run_builder(JTX). 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)}. -file("src/dream_test/runner.gleam", 677). -spec default_output() -> output(). default_output() -> {output, fun gleam_stdlib:print/1, fun gleam_stdlib:print_error/1}. -file("src/dream_test/runner.gleam", 685). -spec discard_output(binary()) -> nil. discard_output(_) -> nil. -file("src/dream_test/runner.gleam", 681). -spec silent_output() -> output(). silent_output() -> {output, fun discard_output/1, fun discard_output/1}. -file("src/dream_test/runner.gleam", 516). ?DOC(" Disable all reporter output (still returns results from `runner.run()`).\n"). -spec silent(run_builder(JTU)) -> run_builder(JTU). 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()}}. -file("src/dream_test/runner.gleam", 874). -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", 917). -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", 903). -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", 927). -spec output_out(output()) -> fun((binary()) -> nil). output_out(Output) -> {output, Out, _} = Output, Out. -file("src/dream_test/runner.gleam", 831). -spec run_suites_with_progress( list(suite_run(any())), dream_test@parallel:parallel_config(), gleam@option:option(dream_test@reporters@progress:progress_reporter()), output(), integer(), integer(), list(dream_test@types:test_result()) ) -> {list(dream_test@types:test_result()), integer()}. run_suites_with_progress( Suite_runs, Default_config, Progress_reporter, Output, Total, Completed, Acc ) -> case Suite_runs of [] -> {Acc, Completed}; [{suite_run, Suite, Override} | Rest] -> 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} ), {run_root_parallel_with_reporter_result, Results, Completed_after_suite, Next_progress_reporter} = Parallel_result, run_suites_with_progress( Rest, Default_config, Next_progress_reporter, Output, Total, Completed_after_suite, lists:append(Acc, Results) ) end. -file("src/dream_test/runner.gleam", 884). -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", 895). -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", 964). -spec suites_to_suite_runs_default( list(dream_test@types:root(JWH)), list(suite_run(JWH)) ) -> list(suite_run(JWH)). 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", 226). ?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(JSN))) -> run_builder(JSN). 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", 978). -spec suites_to_suite_runs_with_override( dream_test@parallel:parallel_config(), list(dream_test@types:root(JWO)), list(suite_run(JWO)) ) -> list(suite_run(JWO)). 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", 993). -spec append_suite_runs_default( list(suite_run(JWV)), list(dream_test@types:root(JWV)) ) -> list(suite_run(JWV)). append_suite_runs_default(Existing, Suites) -> lists:append(Existing, suites_to_suite_runs_default(Suites, [])). -file("src/dream_test/runner.gleam", 255). ?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(JSR), list(dream_test@types:root(JSR))) -> run_builder(JSR). 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)}. -file("src/dream_test/runner.gleam", 1000). -spec append_suite_runs_with_override( list(suite_run(JXC)), dream_test@parallel:parallel_config(), list(dream_test@types:root(JXC)) ) -> list(suite_run(JXC)). 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", 287). ?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(JSW), dream_test@parallel:parallel_config(), list(dream_test@types:root(JSW)) ) -> run_builder(JSW). 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)}. -file("src/dream_test/runner.gleam", 1054). ?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", 689). -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", 952). -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", 944). -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", 936). -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", 932). -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", 793). -spec filter_children( list(dream_test@types:node_(JVB)), list(binary()), list(binary()), fun((test_info()) -> boolean()), list(dream_test@types:node_(JVB)), boolean() ) -> {list(dream_test@types:node_(JVB)), 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", 740). -spec filter_node( dream_test@types:node_(JUV), list(binary()), list(binary()), fun((test_info()) -> boolean()) ) -> {gleam@option:option(dream_test@types:node_(JUV)), boolean()}. filter_node(Node, Scope, Inherited_tags, Predicate) -> case Node of {test, Name, Tags, Kind, Run, Timeout_ms} -> Full_name = lists:append(Scope, [Name]), Effective_tags = lists:append(Inherited_tags, Tags), Info = {test_info, Name, Full_name, Effective_tags, Kind}, case Predicate(Info) of true -> {{some, {test, Name, Tags, Kind, Run, Timeout_ms}}, 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", 728). -spec filter_root(dream_test@types:root(JUR), fun((test_info()) -> boolean())) -> {gleam@option:option(dream_test@types:root(JUR)), 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", 707). -spec filter_suite_runs( list(suite_run(JUK)), fun((test_info()) -> boolean()), list(suite_run(JUK)) ) -> list(suite_run(JUK)). 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", 697). -spec apply_test_filter( list(suite_run(JUE)), gleam@option:option(fun((test_info()) -> boolean())) ) -> list(suite_run(JUE)). 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", 623). ?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), Output@1 = case erlang:element(8, Builder) of {some, Output} -> Output; none -> default_output() end, Completed0 = 0, Completed1 = case erlang:element(6, Builder) of none -> Completed0; {some, Progress_reporter} -> write_progress_event( Progress_reporter, {run_started, Total}, Output@1 ), Completed0 end, {Results, Completed2} = run_suites_with_progress( Selected_runs, erlang:element(3, Builder), erlang:element(6, Builder), Output@1, Total, Completed1, [] ), case erlang:element(6, Builder) of none -> nil; {some, Progress_reporter@1} -> write_progress_event( Progress_reporter@1, {run_finished, Completed2, Total, Results}, Output@1 ) end, write_results_reporters(erlang:element(7, Builder), Results, Output@1), maybe_exit_on_failure(erlang:element(5, Builder), Results), Results.