Skip to content

Commit f6e4c9e

Browse files
committed
Fix bug in doc Mix task and add tests for it
1 parent 8470c85 commit f6e4c9e

File tree

9 files changed

+578
-415
lines changed

9 files changed

+578
-415
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: 83 additions & 84 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,113 @@ 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-
]}
98-
end
81+
Mix.Project.in_project(:single, "test/fixtures/single", fn _mod ->
82+
source_beam = "_build/test/lib/single/ebin"
9983

100-
test "exits with 1 when there is a warning" do
101-
ExDoc.Utils.set_warned()
84+
fun = fn ->
85+
run(
86+
["Single", "1.2.3", source_beam, "--formatter=html"],
87+
&ExDoc.generate_docs/3,
88+
:stderr
89+
)
90+
end
10291

103-
fun = fn ->
104-
run(["ExDoc", "1.2.3", @ebin, "--warnings-as-errors"])
105-
end
92+
{[_html], io} = fun.()
10693

107-
io =
108-
capture_io(:stderr, fn ->
109-
assert catch_exit(fun.()) == {:shutdown, 1}
110-
end)
94+
assert io =~
95+
~s|documentation references function \"Single.bar/0\" but it is undefined or private|
11196

112-
assert io =~
113-
"Doc generation failed due to warnings while using the --warnings-as-errors option\n"
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+
end
102+
end)
114103
end
115104

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()
105+
test "exits with 1 when there are warnings with --warnings-as-errors flag" do
106+
ExDoc.Utils.unset_warned()
120107

121-
fun = fn ->
122-
run(["ExDoc", "1.2.3", @ebin, "--warnings-as-errors"])
123-
end
108+
Mix.Project.in_project(:single, "test/fixtures/single", fn _mod ->
109+
source_beam = "_build/test/lib/single/ebin"
124110

125-
io =
126-
capture_io(:stderr, fn ->
127-
assert catch_exit(fun.()) == {:shutdown, 1}
128-
end)
111+
fun = fn ->
112+
run(
113+
["Single", "1.2.3", source_beam, "--formatter=html", "--warnings-as-errors"],
114+
&ExDoc.generate_docs/3,
115+
:stderr
116+
)
117+
end
129118

130-
assert io =~
131-
"Doc generation failed due to warnings while using the --warnings-as-errors option\n"
119+
# fun.()
120+
121+
io =
122+
capture_io(:stderr, fn ->
123+
assert catch_exit(fun.()) == {:shutdown, 1}
124+
end)
125+
126+
assert io =~
127+
"Documents have been generated, but generation for html format failed due to warnings " <>
128+
"while using the --warnings-as-errors option."
129+
end)
132130
end
133131
end
134132

@@ -156,7 +154,7 @@ defmodule ExDoc.CLITest do
156154
--canonical http://example.com/project
157155
)
158156

159-
{[{project, version, opts}], _io} = run(args)
157+
{[{:ok, {project, version, opts}}], _io} = run(args)
160158
assert project == "ExDoc"
161159
assert version == "1.2.3"
162160

@@ -187,7 +185,7 @@ defmodule ExDoc.CLITest do
187185
test "loading" do
188186
File.write!("test.exs", ~s([extras: ["README.md"], formatters: ["html"]]))
189187

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

193191
assert project == "ExDoc"
@@ -208,7 +206,7 @@ defmodule ExDoc.CLITest do
208206
test "switches take precedence over config" do
209207
File.write!("test.exs", ~s([logo: "config_logo.png", formatters: ["html"]]))
210208

211-
{[{project, version, opts}], _io} =
209+
{[{:ok, {project, version, opts}}], _io} =
212210
run([
213211
"ExDoc",
214212
"--logo",
@@ -254,7 +252,8 @@ defmodule ExDoc.CLITest do
254252
test "loading" do
255253
File.write!("test.config", ~s({extras, [<<"README.md">>]}. {formatters, [<<"html">>]}.))
256254

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

259258
assert project == "ExDoc"
260259
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

test/fixtures/single/lib/single.ex

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
defmodule Single do
2+
@moduledoc """
3+
moduledoc `Single.bar/0`
4+
"""
5+
6+
@doc """
7+
doc `Single.bar/0`
8+
"""
9+
def foo(), do: :foo
10+
end

0 commit comments

Comments
 (0)