diff --git a/.gitignore b/.gitignore index 1a2bf195c..c7b87c7b8 100644 --- a/.gitignore +++ b/.gitignore @@ -24,6 +24,8 @@ ex_doc-*.tar node_modules/ /test/fixtures/umbrella/_build/ +/test/fixtures/single/_build/ +/test/fixtures/single/doc/ /test/tmp/ /tmp/ /npm-debug.log diff --git a/lib/ex_doc/cli.ex b/lib/ex_doc/cli.ex index 18ba6e90c..7e3852420 100644 --- a/lib/ex_doc/cli.ex +++ b/lib/ex_doc/cli.ex @@ -40,20 +40,32 @@ defmodule ExDoc.CLI do ] ) - cond do - List.keymember?(opts, :version, 0) -> - IO.puts("ExDoc v#{ExDoc.version()}") + if List.keymember?(opts, :version, 0) do + IO.puts("ExDoc v#{ExDoc.version()}") + else + results = generate(args, opts, generator) + error_results = Enum.filter(results, &(elem(&1, 0) == :error)) - opts[:warnings_as_errors] == true and ExDoc.Utils.warned?() -> - IO.puts( - :stderr, - "Doc generation failed due to warnings while using the --warnings-as-errors option" - ) + if error_results == [] do + results + else + formatters = Enum.map(error_results, &elem(&1, 1).formatter) - exit({:shutdown, 1}) + format_message = + case formatters do + [formatter] -> "#{formatter} format" + _ -> "#{Enum.join(formatters, ", ")} formats" + end + + message = + "Documents have been generated, but generation for #{format_message} failed due to warnings while using the --warnings-as-errors option." + + message_formatted = IO.ANSI.format([:red, message, :reset]) + + IO.puts(:stderr, message_formatted) - true -> - generate(args, opts, generator) + exit({:shutdown, 1}) + end end end @@ -82,7 +94,11 @@ defmodule ExDoc.CLI do quiet? || IO.puts(IO.ANSI.format([:green, "View #{inspect(formatter)} docs at #{inspect(index)}"])) - index + if opts[:warnings_as_errors] == true and ExDoc.Utils.warned?() do + {:error, %{reason: :warnings_as_errors, formatter: formatter}} + else + {:ok, index} + end end end diff --git a/lib/mix/tasks/docs.ex b/lib/mix/tasks/docs.ex index 2ad41c32d..bf260c030 100644 --- a/lib/mix/tasks/docs.ex +++ b/lib/mix/tasks/docs.ex @@ -400,26 +400,52 @@ defmodule Mix.Tasks.Docs do |> normalize_formatters() |> put_package(config) + Code.prepend_path(options[:source_beam]) + + for path <- Keyword.get_values(options, :paths), + path <- Path.wildcard(path) do + Code.prepend_path(path) + end + Mix.shell().info("Generating docs...") - for formatter <- options[:formatters] do - index = generator.(project, version, Keyword.put(options, :formatter, formatter)) - Mix.shell().info([:green, "View #{inspect(formatter)} docs at #{inspect(index)}"]) + results = + for formatter <- options[:formatters] do + index = generator.(project, version, Keyword.put(options, :formatter, formatter)) + Mix.shell().info([:green, "View #{inspect(formatter)} docs at #{inspect(index)}"]) + + if cli_opts[:open] do + browser_open(index) + end - if cli_opts[:open] do - browser_open(index) + if options[:warnings_as_errors] == true and ExDoc.Utils.warned?() do + {:error, %{reason: :warnings_as_errors, formatter: formatter}} + else + {:ok, index} + end end - if options[:warnings_as_errors] == true and ExDoc.Utils.warned?() do - Mix.shell().info([ - :red, - "Doc generation failed due to warnings while using the --warnings-as-errors option" - ]) + error_results = Enum.filter(results, &(elem(&1, 0) == :error)) - exit({:shutdown, 1}) - else - index - end + if error_results == [] do + results + else + formatters = Enum.map(error_results, &elem(&1, 1).formatter) + + format_message = + case formatters do + [formatter] -> "#{formatter} format" + _ -> "#{Enum.join(formatters, ", ")} formats" + end + + message = + "Documents have been generated, but generation for #{format_message} failed due to warnings while using the --warnings-as-errors option." + + message_formatted = IO.ANSI.format([:red, message, :reset]) + + IO.puts(:stderr, message_formatted) + + exit({:shutdown, 1}) end end diff --git a/test/ex_doc/cli_test.exs b/test/ex_doc/cli_test.exs index 9f601a461..7bd5e8502 100644 --- a/test/ex_doc/cli_test.exs +++ b/test/ex_doc/cli_test.exs @@ -5,11 +5,11 @@ defmodule ExDoc.CLITest do @ebin "_build/test/lib/ex_doc/ebin" - defp run(args) do + defp run(args, generator \\ &{&1, &2, &3}, io_device \\ :stdio) do # TODO: Use with_io on Elixir v1.13 io = - capture_io(fn -> - send(self(), ExDoc.CLI.main(args, &{&1, &2, &3})) + capture_io(io_device, fn -> + send(self(), ExDoc.CLI.main(args, generator)) end) assert_receive response @@ -20,115 +20,116 @@ defmodule ExDoc.CLITest do {[html, epub], _io} = run(["ExDoc", "1.2.3", @ebin]) assert html == - {"ExDoc", "1.2.3", - [ - formatter: "html", - formatters: ["html", "epub"], - apps: [:ex_doc], - source_beam: @ebin - ]} + {:ok, + {"ExDoc", "1.2.3", + [ + formatter: "html", + formatters: ["html", "epub"], + apps: [:ex_doc], + source_beam: @ebin + ]}} assert epub == - {"ExDoc", "1.2.3", - [ - formatter: "epub", - formatters: ["html", "epub"], - apps: [:ex_doc], - source_beam: @ebin - ]} + {:ok, + {"ExDoc", "1.2.3", + [ + formatter: "epub", + formatters: ["html", "epub"], + apps: [:ex_doc], + source_beam: @ebin + ]}} end test "formatter option" do {[epub, html], _io} = run(["ExDoc", "1.2.3", @ebin, "-f", "epub", "-f", "html"]) assert epub == - {"ExDoc", "1.2.3", - [ - formatter: "epub", - formatters: ["epub", "html"], - apps: [:ex_doc], - source_beam: @ebin - ]} + {:ok, + {"ExDoc", "1.2.3", + [ + formatter: "epub", + formatters: ["epub", "html"], + apps: [:ex_doc], + source_beam: @ebin + ]}} assert html == - {"ExDoc", "1.2.3", - [ - formatter: "html", - formatters: ["epub", "html"], - apps: [:ex_doc], - source_beam: @ebin - ]} + {:ok, + {"ExDoc", "1.2.3", + [ + formatter: "html", + formatters: ["epub", "html"], + apps: [:ex_doc], + source_beam: @ebin + ]}} end test "version" do {_, io} = run(["--version"]) - assert io == "ExDoc v#{ExDoc.version()}\n" + assert io =~ "ExDoc v#{ExDoc.version()}\n" {_, io} = run(["--version"]) - assert io == "ExDoc v#{ExDoc.version()}\n" + assert io =~ "ExDoc v#{ExDoc.version()}\n" end describe "--warnings-as-errors" do @describetag :warnings - test "exits with code 0 when no warnings" do + test "exits with 0 when there are warnings and --warnings-as-errors flag is not set" do ExDoc.Utils.unset_warned() - {[html, epub], _io} = run(["ExDoc", "1.2.3", @ebin, "--warnings-as-errors"]) - - assert html == - {"ExDoc", "1.2.3", - [ - formatter: "html", - formatters: ["html", "epub"], - apps: [:ex_doc], - source_beam: @ebin, - warnings_as_errors: true - ]} - - assert epub == - {"ExDoc", "1.2.3", - [ - formatter: "epub", - formatters: ["html", "epub"], - apps: [:ex_doc], - source_beam: @ebin, - warnings_as_errors: true - ]} + Mix.Project.in_project(:single, "test/fixtures/single", fn _mod -> + source_beam = "_build/test/lib/single/ebin" + + fun = fn -> + run( + ["Single", "1.2.3", source_beam, "--formatter=html"], + &ExDoc.generate_docs/3, + :stderr + ) + end + + {[_html], io} = fun.() + + assert io =~ + ~s|documentation references function \"Single.bar/0\" but it is undefined or private| + + # TODO: remove check when we require Elixir v1.16 + if Version.match?(System.version(), ">= 1.16.0-rc") do + assert io =~ ~S|moduledoc `Single.bar/0`| + assert io =~ ~S|doc `Single.bar/0`| + else + assert io =~ ~R|lib/single\.ex:\d+: Single \(module\)| + assert io =~ ~R|lib/single\.ex:\d+: Single\.foo/0| + end + end) end - test "exits with 1 when there is a warning" do - ExDoc.Utils.set_warned() + test "exits with 1 when there are warnings with --warnings-as-errors flag" do + ExDoc.Utils.unset_warned() - fun = fn -> - run(["ExDoc", "1.2.3", @ebin, "--warnings-as-errors"]) - end + Mix.Project.in_project(:single, "test/fixtures/single", fn _mod -> + source_beam = "_build/test/lib/single/ebin" - io = - capture_io(:stderr, fn -> - assert catch_exit(fun.()) == {:shutdown, 1} - end) + fun = fn -> + run( + ["Single", "1.2.3", source_beam, "--formatter=html", "--warnings-as-errors"], + &ExDoc.generate_docs/3, + :stderr + ) + end - assert io =~ - "Doc generation failed due to warnings while using the --warnings-as-errors option\n" - end + # fun.() - test "exits with 1 when there are multiple warnings" do - ExDoc.Utils.set_warned() - ExDoc.Utils.set_warned() - ExDoc.Utils.set_warned() + io = + capture_io(:stderr, fn -> + assert catch_exit(fun.()) == {:shutdown, 1} + end) - fun = fn -> - run(["ExDoc", "1.2.3", @ebin, "--warnings-as-errors"]) - end - - io = - capture_io(:stderr, fn -> - assert catch_exit(fun.()) == {:shutdown, 1} - end) - - assert io =~ - "Doc generation failed due to warnings while using the --warnings-as-errors option\n" + assert io =~ + "Documents have been generated, but generation for html format failed due to warnings " <> + "while using the --warnings-as-errors option." + end) end end @@ -156,7 +157,7 @@ defmodule ExDoc.CLITest do --canonical http://example.com/project ) - {[{project, version, opts}], _io} = run(args) + {[{:ok, {project, version, opts}}], _io} = run(args) assert project == "ExDoc" assert version == "1.2.3" @@ -187,7 +188,7 @@ defmodule ExDoc.CLITest do test "loading" do File.write!("test.exs", ~s([extras: ["README.md"], formatters: ["html"]])) - {[{project, version, opts}], _io} = + {[{:ok, {project, version, opts}}], _io} = run(["ExDoc", "--extra-section", "Guides", "1.2.3", @ebin, "-c", "test.exs"]) assert project == "ExDoc" @@ -208,7 +209,7 @@ defmodule ExDoc.CLITest do test "switches take precedence over config" do File.write!("test.exs", ~s([logo: "config_logo.png", formatters: ["html"]])) - {[{project, version, opts}], _io} = + {[{:ok, {project, version, opts}}], _io} = run([ "ExDoc", "--logo", @@ -254,7 +255,8 @@ defmodule ExDoc.CLITest do test "loading" do File.write!("test.config", ~s({extras, [<<"README.md">>]}. {formatters, [<<"html">>]}.)) - {[{project, version, opts}], _io} = run(["ExDoc", "1.2.3", @ebin, "-c", "test.config"]) + {[{:ok, {project, version, opts}}], _io} = + run(["ExDoc", "1.2.3", @ebin, "-c", "test.config"]) assert project == "ExDoc" assert version == "1.2.3" diff --git a/test/ex_doc/formatter/html_test.exs b/test/ex_doc/formatter/html_test.exs index 80a0aa947..63d9a231e 100644 --- a/test/ex_doc/formatter/html_test.exs +++ b/test/ex_doc/formatter/html_test.exs @@ -137,7 +137,8 @@ defmodule ExDoc.Formatter.HTMLTest do generate_docs(doc_config(context, skip_undefined_reference_warnings_on: [])) end) - assert out =~ ~s|documentation references function "Warnings.bar/0" but| + assert out =~ + ~s|documentation references function "Warnings.bar/0" but it is undefined or private| # TODO: remove check when we require Elixir v1.16 if Version.match?(System.version(), ">= 1.16.0-rc") do diff --git a/test/fixtures/single/lib/single.ex b/test/fixtures/single/lib/single.ex new file mode 100644 index 000000000..65cb4059e --- /dev/null +++ b/test/fixtures/single/lib/single.ex @@ -0,0 +1,10 @@ +defmodule Single do + @moduledoc """ + moduledoc `Single.bar/0` + """ + + @doc """ + doc `Single.bar/0` + """ + def foo(), do: :foo +end diff --git a/test/fixtures/single/mix.exs b/test/fixtures/single/mix.exs new file mode 100644 index 000000000..15c2510e8 --- /dev/null +++ b/test/fixtures/single/mix.exs @@ -0,0 +1,15 @@ +defmodule Single.MixProject do + use Mix.Project + + def project do + [ + app: :single, + version: "0.1.0", + elixir: "~> 1.12", + start_permanent: Mix.env() == :prod, + deps: [] + ] + end + + def application, do: [extra_applications: [:logger]] +end diff --git a/test/mix/tasks/docs_test.exs b/test/mix/tasks/docs_test.exs index 47112ce33..17f77cd42 100644 --- a/test/mix/tasks/docs_test.exs +++ b/test/mix/tasks/docs_test.exs @@ -5,79 +5,101 @@ defmodule Mix.Tasks.DocsTest do # Cannot run concurrently due to Mix compile/deps calls use ExUnit.Case, async: false + import ExUnit.CaptureIO + @moduletag :tmp_dir - def run(context, args, opts) do + def run(context, args, opts, generator \\ &{&1, &2, &3}) do + opts = Keyword.put_new(opts, :output, context[:tmp_dir]) + Mix.Tasks.Docs.run(args, opts, generator) + end + + defp run_task(context, args, opts, generator) do opts = Keyword.put_new(opts, :output, context[:tmp_dir]) - Mix.Tasks.Docs.run(args, opts, &{&1, &2, &3}) + + # TODO: Use with_io on Elixir v1.13 + io = + capture_io(fn -> + send(self(), Mix.Tasks.Docs.run(args, opts, generator)) + end) + + assert_receive response + + {response, io} end test "inflects values from app and version", context do assert [ - {"ex_doc", "0.1.0", - [ - formatter: "html", - formatters: ["html", "epub"], - deps: _, - apps: _, - source_beam: _, - proglang: :elixir - ]}, - {"ex_doc", "0.1.0", - [ - formatter: "epub", - formatters: ["html", "epub"], - deps: _, - apps: _, - source_beam: _, - proglang: :elixir - ]} + {:ok, + {"ex_doc", "0.1.0", + [ + formatter: "html", + formatters: ["html", "epub"], + deps: _, + apps: _, + source_beam: _, + proglang: :elixir + ]}}, + {:ok, + {"ex_doc", "0.1.0", + [ + formatter: "epub", + formatters: ["html", "epub"], + deps: _, + apps: _, + source_beam: _, + proglang: :elixir + ]}} ] = run(context, [], app: :ex_doc, version: "0.1.0") end test "accepts multiple formatters from CLI", context do assert [ - {"ex_doc", "0.1.0", - [ - formatter: "html", - formatters: ["html", "epub"], - deps: _, - apps: _, - source_beam: _, - proglang: :elixir - ]}, - {"ex_doc", "0.1.0", - [ - formatter: "epub", - formatters: ["html", "epub"], - deps: _, - apps: _, - source_beam: _, - proglang: :elixir - ]} + {:ok, + {"ex_doc", "0.1.0", + [ + formatter: "html", + formatters: ["html", "epub"], + deps: _, + apps: _, + source_beam: _, + proglang: :elixir + ]}}, + {:ok, + {"ex_doc", "0.1.0", + [ + formatter: "epub", + formatters: ["html", "epub"], + deps: _, + apps: _, + source_beam: _, + proglang: :elixir + ]}} ] = run(context, ["-f", "html", "-f", "epub"], app: :ex_doc, version: "0.1.0") end test "accepts multiple formatters from config", context do assert [ - {"ex_doc", "0.1.0", - [ - formatter: "html", - formatters: ["html", "epub"], - deps: _, - apps: _, - source_beam: _, - proglang: :elixir - ]}, - {"ex_doc", "0.1.0", - [ - formatter: "epub", - formatters: ["html", "epub"], - deps: _, - apps: _, - source_beam: _, - proglang: :elixir - ]} + {:ok, + {"ex_doc", "0.1.0", + [ + formatter: "html", + formatters: ["html", "epub"], + deps: _, + apps: _, + source_beam: _, + proglang: :elixir + ]}}, + {:ok, + {"ex_doc", "0.1.0", + [ + formatter: "epub", + formatters: ["html", "epub"], + deps: _, + apps: _, + source_beam: _, + proglang: :elixir + ]}} ] = run(context, [], app: :ex_doc, @@ -88,102 +110,110 @@ defmodule Mix.Tasks.DocsTest do test "uses the given name", context do assert [ - {"ExDoc", "0.1.0", - [ - formatter: "html", - formatters: _, - deps: _, - apps: _, - source_beam: _, - proglang: :elixir - ]}, - {"ExDoc", "0.1.0", - [ - formatter: "epub", - formatters: _, - deps: _, - apps: _, - source_beam: _, - proglang: :elixir - ]} + {:ok, + {"ExDoc", "0.1.0", + [ + formatter: "html", + formatters: _, + deps: _, + apps: _, + source_beam: _, + proglang: :elixir + ]}}, + {:ok, + {"ExDoc", "0.1.0", + [ + formatter: "epub", + formatters: _, + deps: _, + apps: _, + source_beam: _, + proglang: :elixir + ]}} ] = run(context, [], app: :ex_doc, version: "0.1.0", name: "ExDoc") end test "accepts modules in :main", context do assert [ - {"ex_doc", "dev", - [ - formatter: "html", - formatters: _, - deps: _, - main: "Sample", - apps: _, - source_beam: _, - proglang: :elixir - ]}, - {"ex_doc", "dev", - [ - formatter: "epub", - formatters: _, - deps: _, - main: "Sample", - apps: _, - source_beam: _, - proglang: :elixir - ]} + {:ok, + {"ex_doc", "dev", + [ + formatter: "html", + formatters: _, + deps: _, + main: "Sample", + apps: _, + source_beam: _, + proglang: :elixir + ]}}, + {:ok, + {"ex_doc", "dev", + [ + formatter: "epub", + formatters: _, + deps: _, + main: "Sample", + apps: _, + source_beam: _, + proglang: :elixir + ]}} ] = run(context, [], app: :ex_doc, docs: [main: Sample]) end test "accepts files in :main", context do assert [ - {"ex_doc", "dev", - [ - formatter: "html", - formatters: _, - deps: _, - apps: _, - source_beam: _, - main: "another", - proglang: :elixir - ]}, - {"ex_doc", "dev", - [ - formatter: "epub", - formatters: _, - deps: _, - apps: _, - source_beam: _, - main: "another", - proglang: :elixir - ]} + {:ok, + {"ex_doc", "dev", + [ + formatter: "html", + formatters: _, + deps: _, + apps: _, + source_beam: _, + main: "another", + proglang: :elixir + ]}}, + {:ok, + {"ex_doc", "dev", + [ + formatter: "epub", + formatters: _, + deps: _, + apps: _, + source_beam: _, + main: "another", + proglang: :elixir + ]}} ] = run(context, [], app: :ex_doc, docs: [main: "another"]) end test "accepts output in :output", %{tmp_dir: tmp_dir} = context do - [{_, _, html_options}, {_, _, epub_options}] = + [{:ok, {_, _, html_options}}, {:ok, {_, _, epub_options}}] = run_results = run(context, [], app: :ex_doc, docs: [output: tmp_dir <> "/hello"]) assert [ - {"ex_doc", "dev", - [ - formatter: "html", - formatters: _, - deps: _, - apps: _, - source_beam: _, - output: _, - proglang: :elixir - ]}, - {"ex_doc", "dev", - [ - formatter: "epub", - formatters: _, - deps: _, - apps: _, - source_beam: _, - output: _, - proglang: :elixir - ]} + {:ok, + {"ex_doc", "dev", + [ + formatter: "html", + formatters: _, + deps: _, + apps: _, + source_beam: _, + output: _, + proglang: :elixir + ]}}, + {:ok, + {"ex_doc", "dev", + [ + formatter: "epub", + formatters: _, + deps: _, + apps: _, + source_beam: _, + output: _, + proglang: :elixir + ]}} ] = run_results assert html_options[:output] == "#{tmp_dir}/hello" @@ -193,30 +223,32 @@ defmodule Mix.Tasks.DocsTest do test "parses output with lower preference than options", %{tmp_dir: tmp_dir} = context do output = tmp_dir <> "/world" - [{_, _, html_options}, {_, _, epub_options}] = + [{:ok, {_, _, html_options}}, {:ok, {_, _, epub_options}}] = run_results = run(context, ["-o", "#{output}"], app: :ex_doc, docs: [output: output]) assert [ - {"ex_doc", "dev", - [ - formatter: "html", - formatters: _, - deps: _, - apps: _, - source_beam: _, - output: _, - proglang: :elixir - ]}, - {"ex_doc", "dev", - [ - formatter: "epub", - formatters: _, - deps: _, - apps: _, - source_beam: _, - output: _, - proglang: :elixir - ]} + {:ok, + {"ex_doc", "dev", + [ + formatter: "html", + formatters: _, + deps: _, + apps: _, + source_beam: _, + output: _, + proglang: :elixir + ]}}, + {:ok, + {"ex_doc", "dev", + [ + formatter: "epub", + formatters: _, + deps: _, + apps: _, + source_beam: _, + output: _, + proglang: :elixir + ]}} ] = run_results assert html_options[:output] == "#{tmp_dir}/world" @@ -225,24 +257,26 @@ defmodule Mix.Tasks.DocsTest do test "includes dependencies", context do assert [ - {"ex_doc", "dev", - [ - formatter: "html", - formatters: _, - deps: deps, - apps: _, - source_beam: _, - proglang: :elixir - ]}, - {"ex_doc", "dev", - [ - formatter: "epub", - formatters: _, - deps: deps, - apps: _, - source_beam: _, - proglang: :elixir - ]} + {:ok, + {"ex_doc", "dev", + [ + formatter: "html", + formatters: _, + deps: deps, + apps: _, + source_beam: _, + proglang: :elixir + ]}}, + {:ok, + {"ex_doc", "dev", + [ + formatter: "epub", + formatters: _, + deps: deps, + apps: _, + source_beam: _, + proglang: :elixir + ]}} ] = run(context, [], app: :ex_doc, docs: []) assert List.keyfind(deps, :earmark_parser, 0) == @@ -252,24 +286,26 @@ defmodule Mix.Tasks.DocsTest do test "allows custom dependency paths", context do assert [ - {"ex_doc", "dev", - [ - formatter: "html", - formatters: _, - deps: deps, - apps: _, - source_beam: _, - proglang: :elixir - ]}, - {"ex_doc", "dev", - [ - formatter: "epub", - formatters: _, - deps: deps, - apps: _, - source_beam: _, - proglang: :elixir - ]} + {:ok, + {"ex_doc", "dev", + [ + formatter: "html", + formatters: _, + deps: deps, + apps: _, + source_beam: _, + proglang: :elixir + ]}}, + {:ok, + {"ex_doc", "dev", + [ + formatter: "epub", + formatters: _, + deps: deps, + apps: _, + source_beam: _, + proglang: :elixir + ]}} ] = run(context, [], app: :ex_doc, docs: [deps: [earmark_parser: "foo"]]) assert List.keyfind(deps, :earmark_parser, 0) == @@ -278,54 +314,58 @@ defmodule Mix.Tasks.DocsTest do test "accepts lazy docs", context do assert [ - {"ex_doc", "dev", - [ - formatter: "html", - formatters: _, - deps: _, - apps: _, - source_beam: _, - main: "another", - proglang: :elixir - ]}, - {"ex_doc", "dev", - [ - formatter: "epub", - formatters: _, - deps: _, - apps: _, - source_beam: _, - main: "another", - proglang: :elixir - ]} + {:ok, + {"ex_doc", "dev", + [ + formatter: "html", + formatters: _, + deps: _, + apps: _, + source_beam: _, + main: "another", + proglang: :elixir + ]}}, + {:ok, + {"ex_doc", "dev", + [ + formatter: "epub", + formatters: _, + deps: _, + apps: _, + source_beam: _, + main: "another", + proglang: :elixir + ]}} ] = run(context, [], app: :ex_doc, docs: fn -> [main: "another"] end) end test "accepts options from root", context do # accepted options are: `app`, `name`, `source_url`, `homepage_url`, `version` assert [ - {"ExDoc", "1.2.3-dev", - [ - formatter: "html", - formatters: _, - deps: _, - apps: _, - source_beam: _, - homepage_url: "https://elixir-lang.org", - source_url: "https://github.com/elixir-lang/ex_doc", - proglang: :elixir - ]}, - {"ExDoc", "1.2.3-dev", - [ - formatter: "epub", - formatters: _, - deps: _, - apps: _, - source_beam: _, - homepage_url: "https://elixir-lang.org", - source_url: "https://github.com/elixir-lang/ex_doc", - proglang: :elixir - ]} + {:ok, + {"ExDoc", "1.2.3-dev", + [ + formatter: "html", + formatters: _, + deps: _, + apps: _, + source_beam: _, + homepage_url: "https://elixir-lang.org", + source_url: "https://github.com/elixir-lang/ex_doc", + proglang: :elixir + ]}}, + {:ok, + {"ExDoc", "1.2.3-dev", + [ + formatter: "epub", + formatters: _, + deps: _, + apps: _, + source_beam: _, + homepage_url: "https://elixir-lang.org", + source_url: "https://github.com/elixir-lang/ex_doc", + proglang: :elixir + ]}} ] = run(context, [], app: :ex_doc, @@ -336,30 +376,33 @@ defmodule Mix.Tasks.DocsTest do proglang: :elixir ) - assert [{"ex_doc", "dev", _}, {"ex_doc", "dev", _}] = run(context, [], app: :ex_doc) + assert [{:ok, {"ex_doc", "dev", _}}, {:ok, {"ex_doc", "dev", _}}] = + run(context, [], app: :ex_doc) end test "supports umbrella project", context do Mix.Project.in_project(:umbrella, "test/fixtures/umbrella", fn _mod -> assert [ - {"umbrella", "dev", - [ - formatter: "html", - formatters: _, - deps: _, - apps: [:bar, :foo], - source_beam: _, - proglang: :elixir - ]}, - {"umbrella", "dev", - [ - formatter: "epub", - formatters: _, - deps: _, - apps: [:bar, :foo], - source_beam: _, - proglang: :elixir - ]} + {:ok, + {"umbrella", "dev", + [ + formatter: "html", + formatters: _, + deps: _, + apps: [:bar, :foo], + source_beam: _, + proglang: :elixir + ]}}, + {:ok, + {"umbrella", "dev", + [ + formatter: "epub", + formatters: _, + deps: _, + apps: [:bar, :foo], + source_beam: _, + proglang: :elixir + ]}} ] = run(context, [], app: :umbrella, apps_path: "apps/", docs: []) end) end @@ -367,68 +410,102 @@ defmodule Mix.Tasks.DocsTest do test "supports umbrella project with ignore_apps", context do Mix.Project.in_project(:umbrella, "test/fixtures/umbrella", fn _mod -> assert [ - {"umbrella", "dev", - [ - formatter: "html", - formatters: _, - deps: _, - apps: [:bar], - source_beam: _, - ignore_apps: [:foo], - proglang: :elixir - ]}, - {"umbrella", "dev", - [ - formatter: "epub", - formatters: _, - deps: _, - apps: [:bar], - source_beam: _, - ignore_apps: [:foo], - proglang: :elixir - ]} + {:ok, + {"umbrella", "dev", + [ + formatter: "html", + formatters: _, + deps: _, + apps: [:bar], + source_beam: _, + ignore_apps: [:foo], + proglang: :elixir + ]}}, + {:ok, + {"umbrella", "dev", + [ + formatter: "epub", + formatters: _, + deps: _, + apps: [:bar], + source_beam: _, + ignore_apps: [:foo], + proglang: :elixir + ]}} ] = run(context, [], app: :umbrella, apps_path: "apps/", docs: [ignore_apps: [:foo]]) end) end test "accepts warnings_as_errors in :warnings_as_errors", context do assert [ - {"ex_doc", "dev", - [ - formatter: "html", - formatters: ["html", "epub"], - deps: _, - apps: [:ex_doc], - source_beam: _, - warnings_as_errors: false, - proglang: :elixir - ]}, - {"ex_doc", "dev", - [ - formatter: "epub", - formatters: ["html", "epub"], - deps: _, - apps: [:ex_doc], - source_beam: _, - warnings_as_errors: false, - proglang: :elixir - ]} + {:ok, + {"ex_doc", "dev", + [ + formatter: "html", + formatters: ["html", "epub"], + deps: _, + apps: [:ex_doc], + source_beam: _, + warnings_as_errors: false, + proglang: :elixir + ]}}, + {:ok, + {"ex_doc", "dev", + [ + formatter: "epub", + formatters: ["html", "epub"], + deps: _, + apps: [:ex_doc], + source_beam: _, + warnings_as_errors: false, + proglang: :elixir + ]}} ] = run(context, [], app: :ex_doc, docs: [warnings_as_errors: false]) end - test "exits with 1 due to warning, with flag --warnings_as_errors", context do - ExDoc.Utils.set_warned() + @tag :tmp_dir + test "exits with 1 due to warnings, with flag --warnings_as_errors", context do + ExDoc.Utils.unset_warned() - assert catch_exit(run(context, [], app: :ex_doc, docs: [warnings_as_errors: true])) == - {:shutdown, 1} - end + Mix.Project.in_project(:single, "test/fixtures/single", fn _mod -> + source_beam = "_build/test/lib/single/ebin" + + fun = fn -> + run_task( + context, + [], + [ + app: :single, + docs: [ + source_beam: source_beam, + warnings_as_errors: true, + formatter: "html", + deps: [] + ], + version: "0.1.0" + ], + &ExDoc.generate_docs/3 + ) + end + + io = + capture_io(:stderr, fn -> + assert catch_exit(fun.()) == {:shutdown, 1} + end) + + assert io =~ + "Documents have been generated, but generation for html format failed due to warnings " <> + "while using the --warnings-as-errors option." + end) - test "exits with 1 due to multiple warnings, with flag --warnings_as_errors", context do - ExDoc.Utils.set_warned() - ExDoc.Utils.set_warned() - ExDoc.Utils.set_warned() + output = + capture_io(:stderr, fn -> + assert catch_exit(run(context, [], app: :single, docs: [warnings_as_errors: true])) == + {:shutdown, 1} + end) - assert catch_exit(run(context, [], app: :ex_doc, docs: [warnings_as_errors: true])) == - {:shutdown, 1} + assert output =~ + "Documents have been generated, but generation for html, epub formats failed due to warnings " <> + "while using the --warnings-as-errors option." end end diff --git a/test/test_helper.exs b/test/test_helper.exs index a555ba53f..62e1ab099 100644 --- a/test/test_helper.exs +++ b/test/test_helper.exs @@ -11,7 +11,9 @@ ExUnit.start(exclude: Enum.filter(exclude, &elem(&1, 1))) # Prepare module fixtures File.rm_rf!("test/tmp") +File.rm_rf!("test/fixtures/single/_build/") File.mkdir_p!("test/tmp/beam") +File.mkdir_p!("test/fixtures/single/_build/test/lib/single/ebin") Code.prepend_path("test/tmp/beam") # Compile module fixtures @@ -19,6 +21,21 @@ Code.prepend_path("test/tmp/beam") |> Path.wildcard() |> Kernel.ParallelCompiler.compile_to_path("test/tmp/beam") +# Compile fixture :single app module +"test/fixtures/single/lib/*.ex" +|> Path.wildcard() +|> Kernel.ParallelCompiler.compile_to_path("test/fixtures/single/_build/test/lib/single/ebin") + +File.write!("test/fixtures/single/_build/test/lib/single/ebin/single.app", ~S""" +{application,single, + [{optional_applications,[]}, + {applications,[kernel,stdlib,elixir,logger]}, + {description,"single"}, + {modules,['Elixir.Single']}, + {registered,[]}, + {vsn,"0.1.0"}]}. +""") + defmodule TestHelper do def elixirc(context, filename \\ "nofile", code) do dir = context.tmp_dir