diff --git a/README.md b/README.md index 749a66ec..7ca96cba 100644 --- a/README.md +++ b/README.md @@ -73,8 +73,9 @@ end - [Exclude Files](#exclude-files) - [Terminal Report Output](#terminal-report-output) - [Coverage Options](#coverage-options) - - [Notes](#notes) - - [Todo](#todo) + - [Ignore Lines](#ignore-lines) + - [Notes](#notes) + - [Todo](#todo) ### [mix coveralls] Show coverage Run the `MIX_ENV=test mix coveralls` command to show coverage information on localhost. @@ -349,6 +350,22 @@ If you want to change the column width used for file names add the `file_column_ } ``` +### Ignore Lines + +Use comments `coveralls-ignore-start` and `coveralls-ignore-stop` to ignore certain lines from code coverage calculation. + +```elixir +defmodule MyModule do + def covered do + end + + # coveralls-ignore-start + def ignored do + end + # coveralls-ignore-stop +end +``` + ### Notes - If mock library is used, it will show some warnings during execution. - https://github.com/eproxus/meck/pull/17 @@ -359,4 +376,3 @@ If you want to change the column width used for file names add the `file_column_ ### Todo - It might not work well on projects which handle multiple project (Mix.Project) files. - Needs improvement on file-path handling. - diff --git a/lib/excoveralls/ignore.ex b/lib/excoveralls/ignore.ex new file mode 100644 index 00000000..24a4c1b5 --- /dev/null +++ b/lib/excoveralls/ignore.ex @@ -0,0 +1,51 @@ +defmodule ExCoveralls.Ignore do + @moduledoc """ + Handles comments to start/stop ignoring lines from coverage. + """ + + @doc """ + Filters out lines between start and end comment. + """ + def filter(info) do + Enum.map(info, &do_filter/1) + end + + defp do_filter(%{name: name, source: source, coverage: coverage}) do + lines = String.split(source, "\n") + list = Enum.zip(lines, coverage) + |> Enum.map_reduce(false, &check_and_swap/2) + |> elem(0) + |> List.zip + |> Enum.map(&Tuple.to_list(&1)) + + [source, coverage] = parse_filter_list(list) + %{name: name, source: source, coverage: coverage} + end + + defp check_and_swap({line, coverage}, ignore) do + { + coverage_for_line({line, coverage}, ignore), + ignore_next?(line, ignore) + } + end + + defp parse_filter_list([]), do: ["", []] + defp parse_filter_list([lines, coverage]), do: [Enum.join(lines, "\n"), coverage] + + defp coverage_for_line({line, coverage}, ignore) do + if ignore == false do + {line, coverage} + else + {line, nil} + end + end + + defp ignore_next?(line, ignore) do + case Regex.run(~r/coveralls-ignore-(start|stop)/, line, capture: :all_but_first) do + ["start"] -> true + ["stop"] -> false + sth -> ignore + end + end + +end diff --git a/lib/excoveralls/stats.ex b/lib/excoveralls/stats.ex index 7af11a1c..ccbc1e4f 100644 --- a/lib/excoveralls/stats.ex +++ b/lib/excoveralls/stats.ex @@ -30,6 +30,7 @@ defmodule ExCoveralls.Stats do |> generate_source_info |> skip_files |> ExCoveralls.StopWords.filter + |> ExCoveralls.Ignore.filter end @doc """ diff --git a/test/ignore_test.exs b/test/ignore_test.exs new file mode 100644 index 00000000..7567255e --- /dev/null +++ b/test/ignore_test.exs @@ -0,0 +1,26 @@ +defmodule ExCoveralls.IgnoreTest do + use ExUnit.Case + alias ExCoveralls.Ignore + + @content """ + defmodule Test do + def test do + end + #coveralls-ignore-start + def test_ignored do + end + #coveralls-ignore-stop + end + """ + @counts [0, 0, 0, nil, 0, 0, nil, 0, 0] + @source_info [%{name: "test/fixtures/test.ex", + source: @content, + coverage: @counts + }] + + test "filter ignored lines returns valid list" do + info = Ignore.filter(@source_info) |> Enum.at(0) + assert(info[:source] == @content) + assert(info[:coverage] == [0, 0, 0, nil, nil, nil, nil, 0, 0]) + end +end