Skip to content

Commit 9b431a8

Browse files
author
José Valim
committed
Speed up CLI tests by avoiding booting Elixir
1 parent d53509e commit 9b431a8

File tree

2 files changed

+105
-75
lines changed

2 files changed

+105
-75
lines changed

lib/elixir/lib/kernel/cli.ex

Lines changed: 80 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,11 @@ defmodule Kernel.CLI do
1111
def main(argv) do
1212
argv = for arg <- argv, do: IO.chardata_to_string(arg)
1313

14-
{config, argv} = process_argv(argv, @blank_config)
14+
{config, argv} = parse_argv(argv)
1515
System.argv(argv)
1616

1717
run fn ->
18-
command_results = Enum.map(Enum.reverse(config.commands), &process_command(&1, config))
19-
command_errors = for {:error, msg} <- command_results, do: msg
20-
errors = Enum.reverse(config.errors) ++ command_errors
18+
errors = process_commands(config)
2119

2220
if errors != [] do
2321
Enum.each(errors, &IO.puts(:stderr, &1))
@@ -55,6 +53,23 @@ defmodule Kernel.CLI do
5553
end
5654
end
5755

56+
@doc """
57+
Parses ARGV returning the CLI config and trailing args.
58+
"""
59+
def parse_argv(argv) do
60+
parse_argv(argv, @blank_config)
61+
end
62+
63+
@doc """
64+
Process commands according to the parsed config from `parse_argv/1`.
65+
Returns all errors.
66+
"""
67+
def process_commands(config) do
68+
results = Enum.map(Enum.reverse(config.commands), &process_command(&1, config))
69+
errors = for {:error, msg} <- results, do: msg
70+
Enum.reverse(config.errors, errors)
71+
end
72+
5873
## Helpers
5974

6075
defp at_exit(status) do
@@ -75,7 +90,7 @@ defmodule Kernel.CLI do
7590
end
7691

7792
defp shared_option?(list, config, callback) do
78-
case process_shared(list, config) do
93+
case parse_shared(list, config) do
7994
{[h|hs], _} when h == hd(list) ->
8095
new_config = %{config | errors: ["#{h} : Unknown option" | config.errors]}
8196
callback.(hs, new_config)
@@ -106,77 +121,77 @@ defmodule Kernel.CLI do
106121
[]
107122
end
108123

109-
# Process shared options
124+
# Parse shared options
110125

111-
defp process_shared([opt|_t], _config) when opt in ["-v", "--version"] do
126+
defp parse_shared([opt|_t], _config) when opt in ["-v", "--version"] do
112127
IO.puts "Elixir #{System.version}"
113128
System.halt 0
114129
end
115130

116-
defp process_shared(["-pa", h|t], config) do
131+
defp parse_shared(["-pa", h|t], config) do
117132
Enum.each Path.wildcard(Path.expand(h)), &Code.prepend_path(&1)
118-
process_shared t, config
133+
parse_shared t, config
119134
end
120135

121-
defp process_shared(["-pz", h|t], config) do
136+
defp parse_shared(["-pz", h|t], config) do
122137
Enum.each Path.wildcard(Path.expand(h)), &Code.append_path(&1)
123-
process_shared t, config
138+
parse_shared t, config
124139
end
125140

126-
defp process_shared(["--app", h|t], config) do
127-
process_shared t, %{config | commands: &[{:app, h}|&1]}
141+
defp parse_shared(["--app", h|t], config) do
142+
parse_shared t, %{config | commands: &[{:app, h}|&1]}
128143
end
129144

130-
defp process_shared(["--no-halt"|t], config) do
131-
process_shared t, %{config | halt: false}
145+
defp parse_shared(["--no-halt"|t], config) do
146+
parse_shared t, %{config | halt: false}
132147
end
133148

134-
defp process_shared(["-e", h|t], config) do
135-
process_shared t, %{config | commands: [{:eval, h} | config.commands]}
149+
defp parse_shared(["-e", h|t], config) do
150+
parse_shared t, %{config | commands: [{:eval, h} | config.commands]}
136151
end
137152

138-
defp process_shared(["-r", h|t], config) do
139-
process_shared t, %{config | commands: [{:require, h} | config.commands]}
153+
defp parse_shared(["-r", h|t], config) do
154+
parse_shared t, %{config | commands: [{:require, h} | config.commands]}
140155
end
141156

142-
defp process_shared(["-pr", h|t], config) do
143-
process_shared t, %{config | commands: [{:parallel_require, h} | config.commands]}
157+
defp parse_shared(["-pr", h|t], config) do
158+
parse_shared t, %{config | commands: [{:parallel_require, h} | config.commands]}
144159
end
145160

146-
defp process_shared([erl, _|t], config) when erl in ["--erl", "--sname", "--name", "--cookie"] do
147-
process_shared t, config
161+
defp parse_shared([erl, _|t], config) when erl in ["--erl", "--sname", "--name", "--cookie"] do
162+
parse_shared t, config
148163
end
149164

150-
defp process_shared([erl|t], config) when erl in ["--detached", "--hidden", "--gen-debug"] do
151-
process_shared t, config
165+
defp parse_shared([erl|t], config) when erl in ["--detached", "--hidden", "--gen-debug"] do
166+
parse_shared t, config
152167
end
153168

154-
defp process_shared(list, config) do
169+
defp parse_shared(list, config) do
155170
{list, config}
156171
end
157172

158173
# Process init options
159174

160-
defp process_argv(["--"|t], config) do
175+
defp parse_argv(["--"|t], config) do
161176
{config, t}
162177
end
163178

164-
defp process_argv(["+elixirc"|t], config) do
165-
process_compiler t, config
179+
defp parse_argv(["+elixirc"|t], config) do
180+
parse_compiler t, config
166181
end
167182

168-
defp process_argv(["+iex"|t], config) do
169-
process_iex t, config
183+
defp parse_argv(["+iex"|t], config) do
184+
parse_iex t, config
170185
end
171186

172-
defp process_argv(["-S", h|t], config) do
187+
defp parse_argv(["-S", h|t], config) do
173188
{%{config | commands: [{:script, h} | config.commands]}, t}
174189
end
175190

176-
defp process_argv([h|t] = list, config) do
191+
defp parse_argv([h|t] = list, config) do
177192
case h do
178193
"-" <> _ ->
179-
shared_option? list, config, &process_argv(&1, &2)
194+
shared_option? list, config, &parse_argv(&1, &2)
180195
_ ->
181196
if Keyword.has_key?(config.commands, :eval) do
182197
{config, list}
@@ -186,84 +201,84 @@ defmodule Kernel.CLI do
186201
end
187202
end
188203

189-
defp process_argv([], config) do
204+
defp parse_argv([], config) do
190205
{config, []}
191206
end
192207

193-
# Process compiler options
208+
# Parse compiler options
194209

195-
defp process_compiler(["--"|t], config) do
210+
defp parse_compiler(["--"|t], config) do
196211
{config, t}
197212
end
198213

199-
defp process_compiler(["-o", h|t], config) do
200-
process_compiler t, %{config | output: h}
214+
defp parse_compiler(["-o", h|t], config) do
215+
parse_compiler t, %{config | output: h}
201216
end
202217

203-
defp process_compiler(["--no-docs"|t], config) do
204-
process_compiler t, %{config | compiler_options: [{:docs, false} | config.compiler_options]}
218+
defp parse_compiler(["--no-docs"|t], config) do
219+
parse_compiler t, %{config | compiler_options: [{:docs, false} | config.compiler_options]}
205220
end
206221

207-
defp process_compiler(["--no-debug-info"|t], config) do
208-
process_compiler t, %{config | compiler_options: [{:debug_info, false} | config.compiler_options]}
222+
defp parse_compiler(["--no-debug-info"|t], config) do
223+
parse_compiler t, %{config | compiler_options: [{:debug_info, false} | config.compiler_options]}
209224
end
210225

211-
defp process_compiler(["--ignore-module-conflict"|t], config) do
212-
process_compiler t, %{config | compiler_options: [{:ignore_module_conflict, true} | config.compiler_options]}
226+
defp parse_compiler(["--ignore-module-conflict"|t], config) do
227+
parse_compiler t, %{config | compiler_options: [{:ignore_module_conflict, true} | config.compiler_options]}
213228
end
214229

215-
defp process_compiler(["--warnings-as-errors"|t], config) do
216-
process_compiler t, %{config | compiler_options: [{:warnings_as_errors, true} | config.compiler_options]}
230+
defp parse_compiler(["--warnings-as-errors"|t], config) do
231+
parse_compiler t, %{config | compiler_options: [{:warnings_as_errors, true} | config.compiler_options]}
217232
end
218233

219-
defp process_compiler(["--verbose"|t], config) do
220-
process_compiler t, %{config | verbose_compile: true}
234+
defp parse_compiler(["--verbose"|t], config) do
235+
parse_compiler t, %{config | verbose_compile: true}
221236
end
222237

223-
defp process_compiler([h|t] = list, config) do
238+
defp parse_compiler([h|t] = list, config) do
224239
case h do
225240
"-" <> _ ->
226-
shared_option? list, config, &process_compiler(&1, &2)
241+
shared_option? list, config, &parse_compiler(&1, &2)
227242
_ ->
228243
pattern = if :filelib.is_dir(h), do: "#{h}/**/*.ex", else: h
229-
process_compiler t, %{config | compile: [pattern | config.compile]}
244+
parse_compiler t, %{config | compile: [pattern | config.compile]}
230245
end
231246
end
232247

233-
defp process_compiler([], config) do
248+
defp parse_compiler([], config) do
234249
{%{config | commands: [{:compile, config.compile}|config.commands]}, []}
235250
end
236251

237-
# Process iex options
252+
# Parse iex options
238253

239-
defp process_iex(["--"|t], config) do
254+
defp parse_iex(["--"|t], config) do
240255
{config, t}
241256
end
242257

243-
# This clause is here so that Kernel.CLI does not error out with "unknown
244-
# option"
245-
defp process_iex(["--dot-iex", _|t], config) do
246-
process_iex t, config
258+
# This clause is here so that Kernel.CLI does not
259+
# error out with "unknown option"
260+
defp parse_iex(["--dot-iex", _|t], config) do
261+
parse_iex t, config
247262
end
248263

249-
defp process_iex([opt, _|t], config) when opt in ["--remsh"] do
250-
process_iex t, config
264+
defp parse_iex([opt, _|t], config) when opt in ["--remsh"] do
265+
parse_iex t, config
251266
end
252267

253-
defp process_iex(["-S", h|t], config) do
268+
defp parse_iex(["-S", h|t], config) do
254269
{%{config | commands: [{:script, h} | config.commands]}, t}
255270
end
256271

257-
defp process_iex([h|t] = list, config) do
272+
defp parse_iex([h|t] = list, config) do
258273
case h do
259274
"-" <> _ ->
260-
shared_option? list, config, &process_iex(&1, &2)
275+
shared_option? list, config, &parse_iex(&1, &2)
261276
_ ->
262277
{%{config | commands: [{:file, h} | config.commands]}, t}
263278
end
264279
end
265280

266-
defp process_iex([], config) do
281+
defp parse_iex([], config) do
267282
{config, []}
268283
end
269284

lib/elixir/test/elixir/kernel/cli_test.exs

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,37 @@ Code.require_file "../test_helper.exs", __DIR__
22

33
import PathHelpers
44

5-
defmodule Kernel.CLI.InitTest do
5+
defmodule Kernel.CLI.ARGVTest do
66
use ExUnit.Case, async: true
77

8-
test "handles code on initialization" do
9-
assert elixir('-e "IO.puts [?3]"') == '3\n'
10-
assert elixir('#{fixture_path("init_sample.exs")}') == '3\n'
8+
import ExUnit.CaptureIO
9+
10+
defp run(argv) do
11+
{config, argv} = Kernel.CLI.parse_argv(argv)
12+
assert Kernel.CLI.process_commands(config) == []
13+
argv
1114
end
1215

1316
test "argv handling" do
14-
expected = '#{inspect ["sample.exs", "-o", "1", "2"]}\n'
15-
assert elixir('-e "IO.puts inspect(System.argv)" sample.exs -o 1 2') == expected
16-
assert elixir('-e "IO.puts inspect(System.argv)" -- sample.exs -o 1 2') == expected
17-
assert elixir('-e "IO.puts inspect(System.argv)" --hidden sample.exs -o 1 2') == expected
17+
assert capture_io(fn ->
18+
assert run(["-e", "IO.puts :ok", "sample.exs", "-o", "1", "2"]) ==
19+
["sample.exs", "-o", "1", "2"]
20+
end) == "ok\n"
21+
22+
assert capture_io(fn ->
23+
assert run(["-e", "IO.puts :ok", "--", "sample.exs", "-o", "1", "2"]) ==
24+
["sample.exs", "-o", "1", "2"]
25+
end) == "ok\n"
26+
27+
assert capture_io(fn ->
28+
assert run(["-e", "IO.puts :ok", "--hidden", "sample.exs", "-o", "1", "2"]) ==
29+
["sample.exs", "-o", "1", "2"]
30+
end) == "ok\n"
1831

19-
result = elixir('-e "IO.puts inspect(System.argv)" -- --hidden sample.exs -o 1 2')
20-
assert result == '#{inspect ["--hidden", "sample.exs", "-o", "1", "2"]}\n'
32+
assert capture_io(fn ->
33+
assert run(["-e", "IO.puts :ok", "--", "--hidden", "sample.exs", "-o", "1", "2"]) ==
34+
["--hidden", "sample.exs", "-o", "1", "2"]
35+
end) == "ok\n"
2136
end
2237
end
2338

0 commit comments

Comments
 (0)