diff --git a/lib/ex_unit/lib/ex_unit/runner.ex b/lib/ex_unit/lib/ex_unit/runner.ex index ff4c3b218f..ff0cdc42c7 100644 --- a/lib/ex_unit/lib/ex_unit/runner.ex +++ b/lib/ex_unit/lib/ex_unit/runner.ex @@ -44,12 +44,27 @@ defmodule ExUnit.Runner do config = configure(opts, manager, self(), stats_pid) :erlang.system_flag(:backtrace_depth, Keyword.fetch!(opts, :stacktrace_depth)) - start_time = System.monotonic_time() - EM.suite_started(config.manager, opts) - modules_to_restore = if Keyword.fetch!(opts, :repeat_until_failure) > 0, do: {[], []}, else: nil + modules_to_restore = + if not opts[:dry_run] do + do_run(config, modules_to_restore, opts, load_us) + else + nil + end + + stats = ExUnit.RunnerStats.stats(stats_pid) + EM.stop(config.manager) + after_suite_callbacks = Application.fetch_env!(:ex_unit, :after_suite) + Enum.each(after_suite_callbacks, fn callback -> callback.(stats) end) + {stats, modules_to_restore} + end + + defp do_run(config, modules_to_restore, opts, load_us) do + start_time = System.monotonic_time() + EM.suite_started(config.manager, opts) + {async_stop_time, modules_to_restore} = async_loop(config, %{}, false, modules_to_restore) stop_time = System.monotonic_time() @@ -65,11 +80,7 @@ defmodule ExUnit.Runner do times_us = %{async: async_us, load: load_us, run: run_us} EM.suite_finished(config.manager, times_us) - stats = ExUnit.RunnerStats.stats(stats_pid) - EM.stop(config.manager) - after_suite_callbacks = Application.fetch_env!(:ex_unit, :after_suite) - Enum.each(after_suite_callbacks, fn callback -> callback.(stats) end) - {stats, modules_to_restore} + modules_to_restore end defp configure(opts, manager, runner_pid, stats_pid) do @@ -88,7 +99,8 @@ defmodule ExUnit.Runner do seed: opts[:seed], stats_pid: stats_pid, timeout: opts[:timeout], - trace: opts[:trace] + trace: opts[:trace], + dry_run: opts[:dry_run] } end diff --git a/lib/mix/lib/mix/tasks/test.ex b/lib/mix/lib/mix/tasks/test.ex index ad32028f91..f6eb1a44d0 100644 --- a/lib/mix/lib/mix/tasks/test.ex +++ b/lib/mix/lib/mix/tasks/test.ex @@ -121,6 +121,10 @@ defmodule Mix.Tasks.Test do * `--cover` - runs coverage tool. See "Coverage" section below + * `--dry-run` *(since v1.19.0)* - prints which tests would be run based on current options, + but does not actually run any tests. This combines with all other options + like `--stale`, `--only`, `--exclude`, and so on. + * `--exclude` - excludes tests that match the filter. This option may be given several times to apply different filters, such as `--exclude ci --exclude slow` @@ -494,7 +498,8 @@ defmodule Mix.Tasks.Test do warnings_as_errors: :boolean, profile_require: :string, exit_status: :integer, - repeat_until_failure: :integer + repeat_until_failure: :integer, + dry_run: :boolean ] @cover [output: "cover", tool: Mix.Tasks.Test.Coverage] @@ -847,7 +852,8 @@ defmodule Mix.Tasks.Test do :only_test_ids, :test_location_relative_path, :exit_status, - :repeat_until_failure + :repeat_until_failure, + :dry_run ] @doc false diff --git a/lib/mix/test/mix/tasks/test_test.exs b/lib/mix/test/mix/tasks/test_test.exs index b62409f6e0..f8b0fe554f 100644 --- a/lib/mix/test/mix/tasks/test_test.exs +++ b/lib/mix/test/mix/tasks/test_test.exs @@ -770,6 +770,42 @@ defmodule Mix.Tasks.TestTest do end end + # TODO: Get to pass after deciding on implementation + @tag :skip + describe "--dry-run" do + test "prints which tests would run without executing them" do + in_fixture("test_stale", fn -> + File.write!("test/dry_run_test.exs", """ + defmodule DryRunTest do + use ExUnit.Case + + test "passing test" do + assert true + end + + test "failing test" do + assert false + end + end + """) + + assert {output, 0} = mix_code(["test", "--dry-run", "--stale"]) + assert output =~ "DRY RUN" + assert output =~ "test/dry_run_test.exs" + refute output =~ "Finished in" + end) + end + + test "prints message when no tests would run" do + in_fixture("test_stale", fn -> + assert {output, 0} = mix_code(["test", "--dry-run", "non_existent_test.exs"]) + assert output =~ "DRY RUN" + assert output =~ "No tests would run" + refute output =~ "Finished in" + end) + end + end + defp receive_until_match(port, expected, acc) do receive do {^port, {:data, output}} ->