Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,12 @@ Optionally, the `ignore_paths` option can be a list of path prefixes to ignore w
]
```

Optionally, the `ignore_modules` option can be a list of module names as atoms and regular expressions that are matched against the module names to ignore when generating the coverage report.

```elixir
test_coverage: [tool: LcovEx, output: "cover", ignore_modules: [MyApp.IgnoreModule, ~r/.*Test/]],
```

## TODOs

- Add missing `FN` lines, for the sake of completion.
11 changes: 11 additions & 0 deletions example_project/lib/example_project/example_ignore_module.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
defmodule ExampleProject.ExampleIgnoreModule do
@moduledoc false

def cover() do
get_value()
end

defp get_value() do
:covered
end
end
11 changes: 11 additions & 0 deletions example_project/lib/example_project/example_ignore_regex_module.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
defmodule ExampleProject.ExampleIgnoreRegexModule do
@moduledoc false

def cover() do
get_value()
end

defp get_value() do
:covered
end
end
32 changes: 21 additions & 11 deletions lib/lcov_ex.ex
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,18 @@ defmodule LcovEx do

output = opts[:output]
caller_cwd = opts[:cwd] || File.cwd!()
ignored_paths = Keyword.get(opts, :ignore_paths, [])
ignored_paths = opts[:ignore_paths] || []
ignored_modules = opts[:ignore_modules] || []

fn ->
log_info("\nGenerating lcov file...")

lcov =
:cover.modules()
|> Enum.sort()
|> Enum.map(&calculate_module_coverage(&1, ignored_paths, caller_cwd))
|> Enum.reject(&ignored_module?(&1, ignored_modules))
|> Enum.reject(&ignored_path?(&1, ignored_paths, caller_cwd))
|> Enum.map(&calculate_module_coverage(&1, caller_cwd))

File.mkdir_p!(output)
path = "#{output}/lcov.info"
Expand All @@ -41,17 +44,24 @@ defmodule LcovEx do
end
end

defp calculate_module_coverage(mod, ignored_paths, cwd) do
defp ignored_module?(mod, ignored_modules),
do: Enum.any?(ignored_modules, &ignored_any?(mod, &1))

defp ignored_any?(mod, %Regex{} = re), do: Regex.match?(re, inspect(mod))
defp ignored_any?(mod, other), do: mod == other

# Ignore compiled modules with path:
# - not relative to the app (e.g. generated by umbrella dependencies)
# - ignored by configuration
defp ignored_path?(mod, ignored_paths, cwd) do
path = mod.module_info(:compile)[:source] |> to_string() |> Path.relative_to(cwd)

# Ignore compiled modules with path:
# - not relative to the app (e.g. generated by umbrella dependencies)
# - ignored by configuration
if Path.type(path) != :relative or Enum.any?(ignored_paths, &String.starts_with?(path, &1)) do
[]
else
calculate_and_format_coverage(mod, path)
end
Path.type(path) != :relative or Enum.any?(ignored_paths, &String.starts_with?(path, &1))
end
defp calculate_module_coverage(mod, cwd) do
path = mod.module_info(:compile)[:source] |> to_string() |> Path.relative_to(cwd)

calculate_and_format_coverage(mod, path)
end

defp calculate_and_format_coverage(mod, path) do
Expand Down
8 changes: 7 additions & 1 deletion test/lcov_ex_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,13 @@ defmodule LcovExTest do
setup do
mix_path = "#{File.cwd!()}/example_project/mix.exs" |> String.replace("//", "/")
MixFileHelper.backup(mix_path)
config = [test_coverage: [tool: LcovEx, ignore_paths: ["deps/"]]]
config = [
test_coverage: [
tool: LcovEx,
ignore_paths: ["deps/"],
ignore_modules: [ExampleProject.ExampleIgnoreModule, ~r/.*ExampleIgnoreRegex.*/]
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer to do a separate test for the new feature, and in that one assert that the module is not in the coverage file (instead of testing the omission in the print).

]
]
MixFileHelper.update_project_config(mix_path, config)

on_exit(fn ->
Expand Down
22 changes: 22 additions & 0 deletions test/tasks/lcov_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,28 @@ defmodule LcovEx.Tasks.LcovTest do
LF:1
LH:1
end_of_record
TN:Elixir.ExampleProject.ExampleIgnoreModule
SF:lib/example_project/example_ignore_module.ex
FNDA:0,cover/0
FNDA:0,get_value/0
FNF:2
FNH:0
DA:5,0
DA:8,0
LF:2
LH:0
end_of_record
TN:Elixir.ExampleProject.ExampleIgnoreRegexModule
SF:lib/example_project/example_ignore_regex_module.ex
FNDA:0,cover/0
FNDA:0,get_value/0
FNF:2
FNH:0
DA:5,0
DA:8,0
LF:2
LH:0
end_of_record
TN:Elixir.ExampleProject.ExampleModule
SF:lib/example_project/example_module.ex
FNDA:1,cover/0
Expand Down