Skip to content

Commit 86b29a6

Browse files
committed
Fix bug in mix task, and add tests for it.
1 parent 8470c85 commit 86b29a6

File tree

9 files changed

+583
-417
lines changed

9 files changed

+583
-417
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ ex_doc-*.tar
2424

2525
node_modules/
2626
/test/fixtures/umbrella/_build/
27+
/test/fixtures/single/_build/
28+
/test/fixtures/single/doc/
2729
/test/tmp/
2830
/tmp/
2931
/npm-debug.log

lib/ex_doc/cli.ex

Lines changed: 28 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -40,20 +40,32 @@ defmodule ExDoc.CLI do
4040
]
4141
)
4242

43-
cond do
44-
List.keymember?(opts, :version, 0) ->
45-
IO.puts("ExDoc v#{ExDoc.version()}")
43+
if List.keymember?(opts, :version, 0) do
44+
IO.puts("ExDoc v#{ExDoc.version()}")
45+
else
46+
results = generate(args, opts, generator)
47+
error_results = Enum.filter(results, &(elem(&1, 0) == :error))
4648

47-
opts[:warnings_as_errors] == true and ExDoc.Utils.warned?() ->
48-
IO.puts(
49-
:stderr,
50-
"Doc generation failed due to warnings while using the --warnings-as-errors option"
51-
)
49+
if error_results == [] do
50+
results
51+
else
52+
formatters = Enum.map(error_results, &elem(&1, 1).formatter)
5253

53-
exit({:shutdown, 1})
54+
format_message =
55+
case formatters do
56+
[formatter] -> "#{formatter} format"
57+
_ -> "#{Enum.join(formatters, ", ")} formats"
58+
end
59+
60+
message =
61+
"Documents have been generated, but generation for #{format_message} failed due to warnings while using the --warnings-as-errors option."
62+
63+
message_formatted = IO.ANSI.format([:red, message, :reset])
64+
65+
IO.puts(:stderr, message_formatted)
5466

55-
true ->
56-
generate(args, opts, generator)
67+
exit({:shutdown, 1})
68+
end
5769
end
5870
end
5971

@@ -82,7 +94,11 @@ defmodule ExDoc.CLI do
8294
quiet? ||
8395
IO.puts(IO.ANSI.format([:green, "View #{inspect(formatter)} docs at #{inspect(index)}"]))
8496

85-
index
97+
if opts[:warnings_as_errors] == true and ExDoc.Utils.warned?() do
98+
{:error, %{reason: :warnings_as_errors, formatter: formatter}}
99+
else
100+
{:ok, index}
101+
end
86102
end
87103
end
88104

lib/mix/tasks/docs.ex

Lines changed: 40 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -400,26 +400,52 @@ defmodule Mix.Tasks.Docs do
400400
|> normalize_formatters()
401401
|> put_package(config)
402402

403+
Code.prepend_path(options[:source_beam])
404+
405+
for path <- Keyword.get_values(options, :paths),
406+
path <- Path.wildcard(path) do
407+
Code.prepend_path(path)
408+
end
409+
403410
Mix.shell().info("Generating docs...")
404411

405-
for formatter <- options[:formatters] do
406-
index = generator.(project, version, Keyword.put(options, :formatter, formatter))
407-
Mix.shell().info([:green, "View #{inspect(formatter)} docs at #{inspect(index)}"])
412+
results =
413+
for formatter <- options[:formatters] do
414+
index = generator.(project, version, Keyword.put(options, :formatter, formatter))
415+
Mix.shell().info([:green, "View #{inspect(formatter)} docs at #{inspect(index)}"])
416+
417+
if cli_opts[:open] do
418+
browser_open(index)
419+
end
408420

409-
if cli_opts[:open] do
410-
browser_open(index)
421+
if options[:warnings_as_errors] == true and ExDoc.Utils.warned?() do
422+
{:error, %{reason: :warnings_as_errors, formatter: formatter}}
423+
else
424+
{:ok, index}
425+
end
411426
end
412427

413-
if options[:warnings_as_errors] == true and ExDoc.Utils.warned?() do
414-
Mix.shell().info([
415-
:red,
416-
"Doc generation failed due to warnings while using the --warnings-as-errors option"
417-
])
428+
error_results = Enum.filter(results, &(elem(&1, 0) == :error))
418429

419-
exit({:shutdown, 1})
420-
else
421-
index
422-
end
430+
if error_results == [] do
431+
results
432+
else
433+
formatters = Enum.map(error_results, &elem(&1, 1).formatter)
434+
435+
format_message =
436+
case formatters do
437+
[formatter] -> "#{formatter} format"
438+
_ -> "#{Enum.join(formatters, ", ")} formats"
439+
end
440+
441+
message =
442+
"Documents have been generated, but generation for #{format_message} failed due to warnings while using the --warnings-as-errors option."
443+
444+
message_formatted = IO.ANSI.format([:red, message, :reset])
445+
446+
IO.puts(:stderr, message_formatted)
447+
448+
exit({:shutdown, 1})
423449
end
424450
end
425451

test/ex_doc/cli_test.exs

Lines changed: 88 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@ defmodule ExDoc.CLITest do
55

66
@ebin "_build/test/lib/ex_doc/ebin"
77

8-
defp run(args) do
8+
defp run(args, generator \\ &{&1, &2, &3}, io_device \\ :stdio) do
99
# TODO: Use with_io on Elixir v1.13
1010
io =
11-
capture_io(fn ->
12-
send(self(), ExDoc.CLI.main(args, &{&1, &2, &3}))
11+
capture_io(io_device, fn ->
12+
send(self(), ExDoc.CLI.main(args, generator))
1313
end)
1414

1515
assert_receive response
@@ -20,115 +20,116 @@ defmodule ExDoc.CLITest do
2020
{[html, epub], _io} = run(["ExDoc", "1.2.3", @ebin])
2121

2222
assert html ==
23-
{"ExDoc", "1.2.3",
24-
[
25-
formatter: "html",
26-
formatters: ["html", "epub"],
27-
apps: [:ex_doc],
28-
source_beam: @ebin
29-
]}
23+
{:ok,
24+
{"ExDoc", "1.2.3",
25+
[
26+
formatter: "html",
27+
formatters: ["html", "epub"],
28+
apps: [:ex_doc],
29+
source_beam: @ebin
30+
]}}
3031

3132
assert epub ==
32-
{"ExDoc", "1.2.3",
33-
[
34-
formatter: "epub",
35-
formatters: ["html", "epub"],
36-
apps: [:ex_doc],
37-
source_beam: @ebin
38-
]}
33+
{:ok,
34+
{"ExDoc", "1.2.3",
35+
[
36+
formatter: "epub",
37+
formatters: ["html", "epub"],
38+
apps: [:ex_doc],
39+
source_beam: @ebin
40+
]}}
3941
end
4042

4143
test "formatter option" do
4244
{[epub, html], _io} = run(["ExDoc", "1.2.3", @ebin, "-f", "epub", "-f", "html"])
4345

4446
assert epub ==
45-
{"ExDoc", "1.2.3",
46-
[
47-
formatter: "epub",
48-
formatters: ["epub", "html"],
49-
apps: [:ex_doc],
50-
source_beam: @ebin
51-
]}
47+
{:ok,
48+
{"ExDoc", "1.2.3",
49+
[
50+
formatter: "epub",
51+
formatters: ["epub", "html"],
52+
apps: [:ex_doc],
53+
source_beam: @ebin
54+
]}}
5255

5356
assert html ==
54-
{"ExDoc", "1.2.3",
55-
[
56-
formatter: "html",
57-
formatters: ["epub", "html"],
58-
apps: [:ex_doc],
59-
source_beam: @ebin
60-
]}
57+
{:ok,
58+
{"ExDoc", "1.2.3",
59+
[
60+
formatter: "html",
61+
formatters: ["epub", "html"],
62+
apps: [:ex_doc],
63+
source_beam: @ebin
64+
]}}
6165
end
6266

6367
test "version" do
6468
{_, io} = run(["--version"])
65-
assert io == "ExDoc v#{ExDoc.version()}\n"
69+
assert io =~ "ExDoc v#{ExDoc.version()}\n"
6670

6771
{_, io} = run(["--version"])
68-
assert io == "ExDoc v#{ExDoc.version()}\n"
72+
assert io =~ "ExDoc v#{ExDoc.version()}\n"
6973
end
7074

7175
describe "--warnings-as-errors" do
7276
@describetag :warnings
7377

74-
test "exits with code 0 when no warnings" do
78+
test "exits with 0 when there are warnings and --warnings-as-errors flag is not set" do
7579
ExDoc.Utils.unset_warned()
7680

77-
{[html, epub], _io} = run(["ExDoc", "1.2.3", @ebin, "--warnings-as-errors"])
78-
79-
assert html ==
80-
{"ExDoc", "1.2.3",
81-
[
82-
formatter: "html",
83-
formatters: ["html", "epub"],
84-
apps: [:ex_doc],
85-
source_beam: @ebin,
86-
warnings_as_errors: true
87-
]}
88-
89-
assert epub ==
90-
{"ExDoc", "1.2.3",
91-
[
92-
formatter: "epub",
93-
formatters: ["html", "epub"],
94-
apps: [:ex_doc],
95-
source_beam: @ebin,
96-
warnings_as_errors: true
97-
]}
81+
Mix.Project.in_project(:single, "test/fixtures/single", fn _mod ->
82+
source_beam = "_build/test/lib/single/ebin"
83+
84+
fun = fn ->
85+
run(
86+
["Single", "1.2.3", source_beam, "--formatter=html"],
87+
&ExDoc.generate_docs/3,
88+
:stderr
89+
)
90+
end
91+
92+
{[_html], io} = fun.()
93+
94+
assert io =~
95+
~s|documentation references function \"Single.bar/0\" but it is undefined or private|
96+
97+
# TODO: remove check when we require Elixir v1.16
98+
if Version.match?(System.version(), ">= 1.16.0-rc") do
99+
assert io =~ ~S|moduledoc `Single.bar/0`|
100+
assert io =~ ~S|doc `Single.bar/0`|
101+
else
102+
assert io =~ ~R|lib/single\.ex:\d+: Single \(module\)|
103+
assert io =~ ~R|lib/single\.ex:\d+: Single\.foo/0|
104+
end
105+
end)
98106
end
99107

100-
test "exits with 1 when there is a warning" do
101-
ExDoc.Utils.set_warned()
108+
test "exits with 1 when there are warnings with --warnings-as-errors flag" do
109+
ExDoc.Utils.unset_warned()
102110

103-
fun = fn ->
104-
run(["ExDoc", "1.2.3", @ebin, "--warnings-as-errors"])
105-
end
111+
Mix.Project.in_project(:single, "test/fixtures/single", fn _mod ->
112+
source_beam = "_build/test/lib/single/ebin"
106113

107-
io =
108-
capture_io(:stderr, fn ->
109-
assert catch_exit(fun.()) == {:shutdown, 1}
110-
end)
114+
fun = fn ->
115+
run(
116+
["Single", "1.2.3", source_beam, "--formatter=html", "--warnings-as-errors"],
117+
&ExDoc.generate_docs/3,
118+
:stderr
119+
)
120+
end
111121

112-
assert io =~
113-
"Doc generation failed due to warnings while using the --warnings-as-errors option\n"
114-
end
122+
# fun.()
115123

116-
test "exits with 1 when there are multiple warnings" do
117-
ExDoc.Utils.set_warned()
118-
ExDoc.Utils.set_warned()
119-
ExDoc.Utils.set_warned()
124+
io =
125+
capture_io(:stderr, fn ->
126+
assert catch_exit(fun.()) == {:shutdown, 1}
127+
end)
120128

121-
fun = fn ->
122-
run(["ExDoc", "1.2.3", @ebin, "--warnings-as-errors"])
123-
end
124-
125-
io =
126-
capture_io(:stderr, fn ->
127-
assert catch_exit(fun.()) == {:shutdown, 1}
128-
end)
129-
130-
assert io =~
131-
"Doc generation failed due to warnings while using the --warnings-as-errors option\n"
129+
assert io =~
130+
"Documents have been generated, but generation for html format failed due to warnings " <>
131+
"while using the --warnings-as-errors option."
132+
end)
132133
end
133134
end
134135

@@ -156,7 +157,7 @@ defmodule ExDoc.CLITest do
156157
--canonical http://example.com/project
157158
)
158159

159-
{[{project, version, opts}], _io} = run(args)
160+
{[{:ok, {project, version, opts}}], _io} = run(args)
160161
assert project == "ExDoc"
161162
assert version == "1.2.3"
162163

@@ -187,7 +188,7 @@ defmodule ExDoc.CLITest do
187188
test "loading" do
188189
File.write!("test.exs", ~s([extras: ["README.md"], formatters: ["html"]]))
189190

190-
{[{project, version, opts}], _io} =
191+
{[{:ok, {project, version, opts}}], _io} =
191192
run(["ExDoc", "--extra-section", "Guides", "1.2.3", @ebin, "-c", "test.exs"])
192193

193194
assert project == "ExDoc"
@@ -208,7 +209,7 @@ defmodule ExDoc.CLITest do
208209
test "switches take precedence over config" do
209210
File.write!("test.exs", ~s([logo: "config_logo.png", formatters: ["html"]]))
210211

211-
{[{project, version, opts}], _io} =
212+
{[{:ok, {project, version, opts}}], _io} =
212213
run([
213214
"ExDoc",
214215
"--logo",
@@ -254,7 +255,8 @@ defmodule ExDoc.CLITest do
254255
test "loading" do
255256
File.write!("test.config", ~s({extras, [<<"README.md">>]}. {formatters, [<<"html">>]}.))
256257

257-
{[{project, version, opts}], _io} = run(["ExDoc", "1.2.3", @ebin, "-c", "test.config"])
258+
{[{:ok, {project, version, opts}}], _io} =
259+
run(["ExDoc", "1.2.3", @ebin, "-c", "test.config"])
258260

259261
assert project == "ExDoc"
260262
assert version == "1.2.3"

test/ex_doc/formatter/html_test.exs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,8 @@ defmodule ExDoc.Formatter.HTMLTest do
137137
generate_docs(doc_config(context, skip_undefined_reference_warnings_on: []))
138138
end)
139139

140-
assert out =~ ~s|documentation references function "Warnings.bar/0" but|
140+
assert out =~
141+
~s|documentation references function "Warnings.bar/0" but it is undefined or private|
141142

142143
# TODO: remove check when we require Elixir v1.16
143144
if Version.match?(System.version(), ">= 1.16.0-rc") do

0 commit comments

Comments
 (0)