Skip to content

Commit

Permalink
✨ Add t command to toggle tracing on/off (#117)
Browse files Browse the repository at this point in the history
  • Loading branch information
randycoulman authored Sep 22, 2024
1 parent 13c5e55 commit ce55d50
Show file tree
Hide file tree
Showing 10 changed files with 142 additions and 47 deletions.
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ which tests should be run with a few keystrokes.
It allows you to easily switch between running all tests, stale tests, or failed
tests. Or, you can run only the tests whose filenames contain a substring. You
can also control which tags are included or excluded, modify the maximum number
of failures allowed, and specify the test seed to use. Includes an optional
"watch mode" which runs tests after every file change.
of failures allowed, specify the test seed to use, and toggle tracing on and
off. Includes an optional "watch mode" which runs tests after every file change.

## Installation

Expand Down Expand Up @@ -120,6 +120,8 @@ will run.
- `q`: Exit the program. (Can also use `Ctrl-D`.)
- `s`: Run only test files that reference modules that have changed since the
last run (equivalent to the `--stale` option of `mix test`).
- `t`: Turn test tracing on or off (equivalent to the `--trace` option of `mix
test).
- `x <tags...>`: Exclude tests tagged with the listed tags (equivalent to the
`--exclude` option of `mix test`).
- `x`: Clear any excluded tags.
Expand Down
7 changes: 5 additions & 2 deletions lib/mix/tasks/test/interactive.ex
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@ defmodule Mix.Tasks.Test.Interactive do
`mix test.interactive` allows you to easily switch between running all tests,
stale tests, or failed tests. Or, you can run only the tests whose filenames
contain a substring. You can also control which tags are included or excluded,
modify the maximum number of failures allowed, and specify the test seed to use.
Includes an optional "watch mode" which runs tests after every file change.
modify the maximum number of failures allowed, specify the test seed to use,
and toggle tracing on and off. Includes an optional "watch mode" which runs
tests after every file change.
## Usage
Expand Down Expand Up @@ -95,6 +96,8 @@ defmodule Mix.Tasks.Test.Interactive do
- `q`: Exit the program. (Can also use `Ctrl-D`.)
- `s`: Run only test files that reference modules that have changed since the
last run (equivalent to the `--stale` option of `mix test`).
- `t`: Turn test tracing on or off (equivalent to the `--trace` option of `mix
test).
- `x <tags...>`: Exclude tests tagged with the listed tags (equivalent to the
`--exclude` option of `mix test`).
- `x`: Clear any excluded tags.
Expand Down
19 changes: 19 additions & 0 deletions lib/mix_test_interactive/command/toggle_tracing.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
defmodule MixTestInteractive.Command.ToggleTracing do
@moduledoc """
Toggle test tracing on or off.
Runs the tests in trace mode when tracing is on and normally when off.
Corresponds to `mix test --trace`.
"""

use MixTestInteractive.Command, command: "t", desc: "turn test tracing on/off"

alias MixTestInteractive.Command
alias MixTestInteractive.Settings

@impl Command
def run(_args, settings) do
{:ok, Settings.toggle_tracing(settings)}
end
end
2 changes: 2 additions & 0 deletions lib/mix_test_interactive/command_line_parser.ex
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,7 @@ defmodule MixTestInteractive.CommandLineParser do
{max_failures, mix_test_opts} = Keyword.pop(mix_test_opts, :max_failures)
{seed, mix_test_opts} = Keyword.pop(mix_test_opts, :seed)
{stale?, mix_test_opts} = Keyword.pop(mix_test_opts, :stale, false)
{trace?, mix_test_opts} = Keyword.pop(mix_test_opts, :trace, false)
watching? = Keyword.get(mti_opts, :watch, true)

%Settings{
Expand All @@ -182,6 +183,7 @@ defmodule MixTestInteractive.CommandLineParser do
patterns: patterns,
seed: seed && to_string(seed),
stale?: no_patterns? && !failed? && stale?,
tracing?: trace?,
watching?: watching?
}
end
Expand Down
2 changes: 2 additions & 0 deletions lib/mix_test_interactive/command_processor.ex
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ defmodule MixTestInteractive.CommandProcessor do
alias MixTestInteractive.Command.RunTests
alias MixTestInteractive.Command.Seed
alias MixTestInteractive.Command.Stale
alias MixTestInteractive.Command.ToggleTracing
alias MixTestInteractive.Command.ToggleWatchMode
alias MixTestInteractive.Settings

Expand All @@ -34,6 +35,7 @@ defmodule MixTestInteractive.CommandProcessor do
RunTests,
Seed,
Stale,
ToggleTracing,
ToggleWatchMode
]

Expand Down
37 changes: 28 additions & 9 deletions lib/mix_test_interactive/settings.ex
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ defmodule MixTestInteractive.Settings do
field :patterns, [String.t()], default: []
field :seed, String.t()
field :stale?, boolean(), default: false
field :tracing?, boolean(), default: false
field :watching?, boolean(), default: true
end

Expand Down Expand Up @@ -157,8 +158,9 @@ defmodule MixTestInteractive.Settings do
end

with_seed
|> append_max_failures(settings)
|> append_tag_filters(settings)
|> append_max_failures(settings)
|> append_tracing(settings)
end

defp append_max_failures(summary, %__MODULE__{max_failures: nil} = _settings) do
Expand All @@ -180,12 +182,34 @@ defmodule MixTestInteractive.Settings do
|> Enum.join("\n")
end

defp append_tracing(summary, %__MODULE__{tracing?: false}), do: summary

defp append_tracing(summary, %__MODULE__{tracing?: true}) do
summary <> "\nTracing: ON"
end

defp tag_filters(_label, []), do: nil

defp tag_filters(label, tags) do
label <> ": " <> inspect(tags)
end

@doc """
Toggle test tracing on or off.
"""
@spec toggle_tracing(t()) :: t()
def toggle_tracing(%__MODULE__{} = settings) do
%{settings | tracing?: !settings.tracing?}
end

@doc """
Toggle file-watching mode on or off.
"""
@spec toggle_watch_mode(t()) :: t()
def toggle_watch_mode(%__MODULE__{} = settings) do
%{settings | watching?: !settings.watching?}
end

@doc """
Exclude tests with the specified tags.
Expand Down Expand Up @@ -236,14 +260,6 @@ defmodule MixTestInteractive.Settings do
%{settings | seed: seed}
end

@doc """
Toggle file-watching mode on or off.
"""
@spec toggle_watch_mode(t()) :: t()
def toggle_watch_mode(%__MODULE__{} = settings) do
%{settings | watching?: !settings.watching?}
end

defp args_from_settings(%__MODULE{} = settings) do
with {:ok, files} <- files_from_patterns(settings) do
{:ok, opts_from_settings(settings) ++ files}
Expand Down Expand Up @@ -292,5 +308,8 @@ defmodule MixTestInteractive.Settings do
defp opts_from_single_setting({:stale?, false}), do: []
defp opts_from_single_setting({:stale?, true}), do: ["--stale"]

defp opts_from_single_setting({:tracing?, false}), do: []
defp opts_from_single_setting({:tracing?, true}), do: ["--trace"]

defp opts_from_single_setting({_key, _value}), do: []
end
42 changes: 24 additions & 18 deletions test/mix_test_interactive/command_line_parser_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -186,8 +186,8 @@ defmodule MixTestInteractive.CommandLineParserTest do

describe "mix test arguments" do
test "records initial `mix test` arguments" do
{:ok, %{settings: settings}} = CommandLineParser.parse(["--trace", "--raise"])
assert settings.initial_cli_args == ["--trace", "--raise"]
{:ok, %{settings: settings}} = CommandLineParser.parse(["--color", "--raise"])
assert settings.initial_cli_args == ["--color", "--raise"]
end

test "records no `mix test` arguments by default" do
Expand All @@ -207,7 +207,7 @@ defmodule MixTestInteractive.CommandLineParserTest do
"--",
"--exclude",
"tag1",
"--trace",
"--color",
"--exclude",
"tag2",
"--failed",
Expand All @@ -217,21 +217,21 @@ defmodule MixTestInteractive.CommandLineParserTest do
])

assert settings.excludes == ["tag1", "tag2", "tag3"]
assert settings.initial_cli_args == ["--trace", "--raise"]
assert settings.initial_cli_args == ["--color", "--raise"]
end

test "extracts failed flag from arguments" do
{:ok, %{settings: settings}} = CommandLineParser.parse(["--trace", "--failed", "--raise"])
{:ok, %{settings: settings}} = CommandLineParser.parse(["--color", "--failed", "--raise"])
assert settings.failed?
assert settings.initial_cli_args == ["--trace", "--raise"]
assert settings.initial_cli_args == ["--color", "--raise"]
end

test "extracts includes from arguments" do
{:ok, %{settings: settings}} =
CommandLineParser.parse([
"--include",
"tag1",
"--trace",
"--color",
"--include",
"tag2",
"--failed",
Expand All @@ -241,39 +241,45 @@ defmodule MixTestInteractive.CommandLineParserTest do
])

assert settings.includes == ["tag1", "tag2", "tag3"]
assert settings.initial_cli_args == ["--trace", "--raise"]
assert settings.initial_cli_args == ["--color", "--raise"]
end

test "extracts max-failures from arguments" do
{:ok, %{settings: settings}} = CommandLineParser.parse(["--trace", "--max-failures", "7", "--raise"])
{:ok, %{settings: settings}} = CommandLineParser.parse(["--color", "--max-failures", "7", "--raise"])
assert settings.max_failures == "7"
assert settings.initial_cli_args == ["--trace", "--raise"]
assert settings.initial_cli_args == ["--color", "--raise"]
end

test "extracts only from arguments" do
{:ok, %{settings: settings}} =
CommandLineParser.parse(["--only", "tag1", "--trace", "--only", "tag2", "--failed", "--raise", "--only", "tag3"])
CommandLineParser.parse(["--only", "tag1", "--color", "--only", "tag2", "--failed", "--raise", "--only", "tag3"])

assert settings.only == ["tag1", "tag2", "tag3"]
assert settings.initial_cli_args == ["--trace", "--raise"]
assert settings.initial_cli_args == ["--color", "--raise"]
end

test "extracts seed from arguments" do
{:ok, %{settings: settings}} = CommandLineParser.parse(["--trace", "--seed", "5432", "--raise"])
{:ok, %{settings: settings}} = CommandLineParser.parse(["--color", "--seed", "5432", "--raise"])
assert settings.seed == "5432"
assert settings.initial_cli_args == ["--trace", "--raise"]
assert settings.initial_cli_args == ["--color", "--raise"]
end

test "extracts stale setting from arguments" do
{:ok, %{settings: settings}} = CommandLineParser.parse(["--trace", "--stale", "--raise"])
{:ok, %{settings: settings}} = CommandLineParser.parse(["--color", "--stale", "--raise"])
assert settings.stale?
assert settings.initial_cli_args == ["--trace", "--raise"]
assert settings.initial_cli_args == ["--color", "--raise"]
end

test "extracts trace flag from arguments" do
{:ok, %{settings: settings}} = CommandLineParser.parse(["--color", "--trace", "--raise"])
assert settings.tracing?
assert settings.initial_cli_args == ["--color", "--raise"]
end

test "extracts patterns from arguments" do
{:ok, %{settings: settings}} = CommandLineParser.parse(["pattern1", "--trace", "pattern2"])
{:ok, %{settings: settings}} = CommandLineParser.parse(["pattern1", "--color", "pattern2"])
assert settings.patterns == ["pattern1", "pattern2"]
assert settings.initial_cli_args == ["--trace"]
assert settings.initial_cli_args == ["--color"]
end

test "failed takes precedence over stale" do
Expand Down
7 changes: 7 additions & 0 deletions test/mix_test_interactive/command_processor_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,13 @@ defmodule MixTestInteractive.CommandProcessorTest do
assert {:ok, ^expected} = process_command("s", settings)
end

test "t toggles tracing" do
settings = %Settings{}
expected = Settings.toggle_tracing(settings)

assert {:ok, ^expected} = process_command("t", settings)
end

test "w toggles watch mode" do
settings = %Settings{}
expected = Settings.toggle_watch_mode(settings)
Expand Down
10 changes: 10 additions & 0 deletions test/mix_test_interactive/end_to_end_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,16 @@ defmodule MixTestInteractive.EndToEndTest do
assert_ran_tests()
end

test "trace on/off workflow", %{pid: pid} do
assert_ran_tests()

assert :ok = InteractiveMode.process_command(pid, "t")
assert_ran_tests(["--trace"])

assert :ok = InteractiveMode.process_command(pid, "t")
assert_ran_tests()
end

test "watch on/off workflow", %{pid: pid} do
assert_ran_tests()

Expand Down
Loading

0 comments on commit ce55d50

Please sign in to comment.