defmodule Mix.Tasks.Chaps do @moduledoc """ A Mix task for gathering and displaying the current project's code coverage. """ use Mix.Task @shortdoc "Gather and display the test coverage" @preferred_cli_env :test defmodule Runner do def run(task, args) do Mix.Task.run(task, args) end end def run(args) do {options, _, _} = OptionParser.parse(args, switches: [help: :boolean], aliases: [h: :help]) if options[:help] do Chaps.Task.Util.print_help_message() else do_run(args, type: "local") end end @doc """ Provides the logic to switch the parameters for Chaps.run/3. """ def do_run(args, options) do if Mix.Project.config()[:test_coverage][:tool] != Chaps do raise Chaps.InvalidConfigError, message: "Please specify 'test_coverage: [tool: Chaps]' in the 'project' section of mix.exs" end switches = [ filter: :string, umbrella: :boolean, verbose: :boolean, pro: :boolean, parallel: :boolean, sort: :string, output_dir: :string ] aliases = [f: :filter, u: :umbrella, v: :verbose, o: :output_dir] {args, common_options} = parse_common_options(args, switches: switches, aliases: aliases) all_options = options ++ common_options test_task = Mix.Project.config()[:test_coverage][:test_task] || "test" all_options = if all_options[:umbrella] do sub_apps = Chaps.SubApps.parse(Mix.Dep.Umbrella.loaded()) all_options ++ [sub_apps: sub_apps, apps_path: Mix.Project.config()[:apps_path]] else all_options end Chaps.ConfServer.start() Chaps.ConfServer.set(all_options ++ [args: args]) Chaps.StatServer.start() Runner.run(test_task, ["--cover"] ++ args) if all_options[:umbrella] do type = options[:type] || "local" Chaps.StatServer.get() |> MapSet.to_list() |> get_stats(all_options) |> Chaps.analyze(type, options) end end def parse_common_options(args, common_options) do common_switches = Keyword.get(common_options, :switches, []) common_aliases = Keyword.get(common_options, :aliases, []) {common_options, _remaining, _invalid} = OptionParser.parse(args, common_options) # the switches that chaps supports supported_switches = Enum.map(Keyword.keys(common_switches), fn s -> String.replace("--#{s}", "_", "-") end) ++ Enum.map(Keyword.keys(common_aliases), fn s -> "-#{s}" end) # Get the remaining args to pass onto cover, excluding Chaps-specific args. # Not using OptionParser for this because it splits things up in unfortunate ways. {remaining, _} = List.foldl(args, {[], nil}, fn arg, {acc, last} -> cond do # don't include switches for Chaps Enum.member?(supported_switches, arg) -> {acc, arg} # also drop any values that follow Chaps switches !String.starts_with?(arg, "-") && Enum.member?(supported_switches, last) -> {acc, nil} # leaving just the switches and values intended for cover true -> {acc ++ [arg], nil} end end) sub_dir_set? = common_options[:subdir] not in [nil, ""] root_dir_set? = common_options[:rootdir] not in [nil, ""] if sub_dir_set? and root_dir_set? do raise Chaps.InvalidOptionError, message: "subdir and rootdir options are exclusive. please specify only one of them." end {remaining, common_options} end def get_stats(stats, options) do sub_dir_set? = options[:subdir] not in [nil, ""] root_dir_set? = options[:rootdir] not in [nil, ""] cond do sub_dir_set? -> stats |> Enum.map(fn m -> %{m | name: options[:subdir] <> Map.get(m, :name)} end) root_dir_set? -> stats |> Enum.map(fn m -> %{m | name: String.trim_leading(Map.get(m, :name), options[:rootdir])} end) true -> stats end end defmodule Detail do @moduledoc """ Provides an entry point for displaying coveralls information with source code details. """ use Mix.Task @shortdoc "Display the test coverage with source detail" @preferred_cli_env :test def run(args) do Mix.Tasks.Chaps.do_run(args, type: "local", detail: true) end end defmodule Html do @moduledoc """ Provides an entry point for displaying coveralls information with source code details as an HTML report. """ use Mix.Task @shortdoc "Display the test coverage with source detail as an HTML report" @preferred_cli_env :test def run(args) do Mix.Tasks.Chaps.do_run(args, type: "html") end end defmodule Xml do @moduledoc """ Provides an entry point for outputting coveralls information as a XML file. """ use Mix.Task @shortdoc "Output the test coverage as a XML file" @preferred_cli_env :test def run(args) do Mix.Tasks.Chaps.do_run(args, type: "xml") end end defmodule Json do @moduledoc """ Provides an entry point for outputting coveralls information as a JSON file. """ use Mix.Task @shortdoc "Output the test coverage as a JSON file" @preferred_cli_env :test def run(args) do Mix.Tasks.Chaps.do_run(args, type: "json") end end defmodule Lcov do @moduledoc """ Provides an entry point for outputting coveralls information as a Lcov file. """ use Mix.Task @shortdoc "Output the test coverage as a Lcov file" @preferred_cli_env :test def run(args) do Mix.Tasks.Chaps.do_run(args, type: "lcov") end end end