Skip to content

Commit 2718631

Browse files
ericentinJosé Valim
authored andcommitted
mix test --listen-on-stdin (#4921)
Signed-off-by: José Valim <[email protected]>
1 parent 9d9aa8b commit 2718631

File tree

3 files changed

+46
-1
lines changed

3 files changed

+46
-1
lines changed

lib/mix/lib/mix/tasks/test.ex

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,9 @@ defmodule Mix.Tasks.Test do
6767
* `--no-elixir-version-check` - do not check the Elixir version from mix.exs
6868
* `--stale` - run only tests which reference modules that changed since the
6969
last `test --stale`. You can read more about this option in the "Stale" section below.
70+
* `--listen-on-stdin` - run tests, and then listen on stdin. Receiving a newline will
71+
result in the tests being run again. Very useful when combined with `--stale` and
72+
external commands which produce output on stdout upon file system modification.
7073
7174
## Filters
7275
@@ -164,7 +167,7 @@ defmodule Mix.Tasks.Test do
164167
exclude: :keep, seed: :integer, only: :keep, compile: :boolean,
165168
start: :boolean, timeout: :integer, raise: :boolean,
166169
deps_check: :boolean, archives_check: :boolean, elixir_version_check: :boolean,
167-
stale: :boolean]
170+
stale: :boolean, listen_on_stdin: :boolean]
168171

169172
@cover [output: "cover", tool: Cover]
170173

@@ -243,6 +246,13 @@ defmodule Mix.Tasks.Test do
243246
:noop ->
244247
:ok
245248
end
249+
250+
if opts[:listen_on_stdin] do
251+
IO.gets(:stdio, "")
252+
Mix.shell.info "Restarting..."
253+
:init.restart()
254+
:timer.sleep(:infinity)
255+
end
246256
end
247257

248258
defp display_warn_test_pattern(files, pattern) do

lib/mix/test/mix/tasks/test_test.exs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,32 @@ defmodule Mix.Tasks.TestTest do
104104
end
105105
end
106106

107+
test "--listen-on-stdin: runs tests after input" do
108+
in_fixture "test_stale", fn ->
109+
port = mix_port(~w[test --stale --listen-on-stdin])
110+
111+
assert receive_until_match(port, "seed", []) =~ "2 tests"
112+
113+
:erlang.port_command(port, "\n")
114+
115+
assert receive_until_match(port, "No stale tests.", []) =~ "Restarting..."
116+
end
117+
end
118+
119+
defp receive_until_match(port, expected, acc) do
120+
receive do
121+
{^port, {:data, charlist}} ->
122+
string = to_string(charlist)
123+
acc = [acc | string]
124+
125+
if string =~ expected do
126+
IO.iodata_to_binary(acc)
127+
else
128+
receive_until_match(port, expected, acc)
129+
end
130+
end
131+
end
132+
107133
defp set_all_mtimes(time \\ {{2010, 1, 1}, {0, 0, 0}}) do
108134
Enum.each(Path.wildcard("**", match_dot: true), &File.touch!(&1, time))
109135
end

lib/mix/test/test_helper.exs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,15 @@ defmodule MixTest.Case do
136136
env: envs) |> elem(0)
137137
end
138138

139+
def mix_port(args, envs \\ []) when is_list(args) do
140+
:erlang.open_port({:spawn_executable, elixir_executable()}, [
141+
{:args, ["-r", mix_executable(), "--" | args]},
142+
{:env, envs},
143+
:use_stdio,
144+
:stderr_to_stdout
145+
])
146+
end
147+
139148
defp mix_executable do
140149
Path.expand("../../../bin/mix", __DIR__)
141150
end

0 commit comments

Comments
 (0)