From a530769539adf87eddf691110df1dc3a3abe974d Mon Sep 17 00:00:00 2001 From: Roman Date: Fri, 31 May 2024 01:38:27 +0200 Subject: [PATCH] Detect and warn about incorrectly used ignore-comments (#325) * Detect and warn about incorrectly used ignore-comments Resolves #197. * Keep existing ignoring behavior * Improve formatting * Test more ignore-related warnings * Remove warning in the case of ignore-next-line at the EOF * Test the warning output * Add a changelog entry * Adjust test descriptions --------- Co-authored-by: Roman <205906+RKushnir@users.noreply.github.com> --- CHANGELOG.md | 6 ++ lib/excoveralls/circle.ex | 3 +- lib/excoveralls/drone.ex | 3 +- lib/excoveralls/github.ex | 3 +- lib/excoveralls/gitlab.ex | 3 +- lib/excoveralls/ignore.ex | 186 ++++++++++++++++++++++++++++------- lib/excoveralls/json.ex | 3 +- lib/excoveralls/local.ex | 7 ++ lib/excoveralls/post.ex | 3 +- lib/excoveralls/semaphore.ex | 3 +- lib/excoveralls/stats.ex | 8 ++ lib/excoveralls/travis.ex | 5 +- test/circle_test.exs | 3 +- test/cobertura_test.exs | 18 +++- test/drone_test.exs | 3 +- test/github_test.exs | 7 +- test/gitlab_test.exs | 6 +- test/html_test.exs | 3 +- test/ignore_test.exs | 130 +++++++++++++++++++----- test/json_test.exs | 3 +- test/lcov_test.exs | 3 +- test/local_test.exs | 29 +++++- test/post_test.exs | 3 +- test/semaphore_test.exs | 6 +- test/travis_test.exs | 3 +- test/xml_test.exs | 3 +- 26 files changed, 365 insertions(+), 88 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a5b0557d..14d4134e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +Unreleased +------ +#### Enhancements +- Print warnings about incorrectly used ignore-markers (#325), such as start-marker +without a corresponding stop-marker, or two start-markers without a stop-marker in-between etc. + 0.18.1 ------ #### Changes diff --git a/lib/excoveralls/circle.ex b/lib/excoveralls/circle.ex index 627f6dc4..6c4bffa3 100644 --- a/lib/excoveralls/circle.ex +++ b/lib/excoveralls/circle.ex @@ -3,6 +3,7 @@ defmodule ExCoveralls.Circle do Handles circle-ci integration with coveralls. """ alias ExCoveralls.Poster + alias ExCoveralls.Stats def execute(stats, options) do json = generate_json(stats, Enum.into(options, %{})) @@ -20,7 +21,7 @@ defmodule ExCoveralls.Circle do service_number: get_number(), service_job_id: get_job_id(), service_pull_request: get_pull_request(), - source_files: stats, + source_files: Stats.serialize(stats), git: generate_git_info(), parallel: options[:parallel], flag_name: options[:flagname] diff --git a/lib/excoveralls/drone.ex b/lib/excoveralls/drone.ex index 6dbd45f6..610c1ea3 100644 --- a/lib/excoveralls/drone.ex +++ b/lib/excoveralls/drone.ex @@ -3,6 +3,7 @@ defmodule ExCoveralls.Drone do Handles drone-ci integration with coveralls. """ alias ExCoveralls.Poster + alias ExCoveralls.Stats def execute(stats, options) do json = generate_json(stats, Enum.into(options, %{})) @@ -20,7 +21,7 @@ defmodule ExCoveralls.Drone do service_number: get_build_num(), service_job_id: get_build_num(), service_pull_request: get_pull_request(), - source_files: stats, + source_files: Stats.serialize(stats), git: generate_git_info(), parallel: options[:parallel], flag_name: options[:flagname] diff --git a/lib/excoveralls/github.ex b/lib/excoveralls/github.ex index f5a04fb3..4c84b4ae 100644 --- a/lib/excoveralls/github.ex +++ b/lib/excoveralls/github.ex @@ -3,6 +3,7 @@ defmodule ExCoveralls.Github do Handles GitHub Actions integration with coveralls. """ alias ExCoveralls.Poster + alias ExCoveralls.Stats def execute(stats, options) do json = generate_json(stats, Enum.into(options, %{})) @@ -20,7 +21,7 @@ defmodule ExCoveralls.Github do %{ repo_token: get_env("GITHUB_TOKEN"), service_name: "github", - source_files: stats, + source_files: Stats.serialize(stats), parallel: options[:parallel], flag_name: options[:flagname], git: git_info() diff --git a/lib/excoveralls/gitlab.ex b/lib/excoveralls/gitlab.ex index b801d970..0dda681e 100644 --- a/lib/excoveralls/gitlab.ex +++ b/lib/excoveralls/gitlab.ex @@ -3,6 +3,7 @@ defmodule ExCoveralls.Gitlab do Handles gitlab-ci integration with coveralls. """ alias ExCoveralls.Poster + alias ExCoveralls.Stats def execute(stats, options) do json = generate_json(stats, Enum.into(options, %{})) @@ -23,7 +24,7 @@ defmodule ExCoveralls.Gitlab do service_number: get_number(), service_job_id: get_job_id(), service_pull_request: get_pull_request(), - source_files: stats, + source_files: Stats.serialize(stats), git: generate_git_info(), parallel: options[:parallel], flag_name: options[:flagname] diff --git a/lib/excoveralls/ignore.ex b/lib/excoveralls/ignore.ex index 4d6465bd..3663a75f 100644 --- a/lib/excoveralls/ignore.ex +++ b/lib/excoveralls/ignore.ex @@ -10,51 +10,167 @@ defmodule ExCoveralls.Ignore do Enum.map(info, &do_filter/1) end + defmodule State do + defstruct ignore_mode: :no_ignore, + coverage: [], + coverage_buffer: [], + warnings: [], + last_marker_index: nil + end + defp do_filter(%{name: name, source: source, coverage: coverage}) do - lines = String.split(source, "\n") - list = Enum.zip(lines, coverage) - |> Enum.map_reduce(:no_ignore, &check_and_swap/2) - |> elem(0) - |> List.zip - |> Enum.map(&Tuple.to_list(&1)) + source_lines = String.split(source, "\n") - [source, coverage] = parse_filter_list(list) - %{name: name, source: source, coverage: coverage} - end + processing_result = + Enum.zip(source_lines, coverage) + |> Enum.with_index() + |> Enum.reduce(%State{}, &process_line/2) + |> process_end_of_file() - defp check_and_swap({line, coverage}, ignore) do - { - coverage_for_line({line, coverage}, ignore), - ignore_next?(line, ignore) - } + updated_coverage = processing_result.coverage |> List.flatten() |> Enum.reverse() + warnings = Enum.sort_by(processing_result.warnings, &elem(&1, 0)) + %{name: name, source: source, coverage: updated_coverage, warnings: warnings} 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 == :no_ignore do - {line, coverage} - else - {line, nil} + defp process_line({{source_line, coverage_line}, index}, state) do + case detect_ignore_marker(source_line) do + :none -> process_regular_line(coverage_line, index, state) + :start -> process_start_marker(coverage_line, index, state) + :stop -> process_stop_marker(coverage_line, index, state) + :next_line -> process_next_line_marker(coverage_line, index, state) end end - defp ignore_next?(line, ignore) do + defp detect_ignore_marker(line) do case Regex.run(~r/coveralls-ignore-(start|stop|next-line)/, line, capture: :all_but_first) do - ["start"] -> :ignore_block - ["stop"] -> :no_ignore - ["next-line"] -> - case ignore do - :ignore_block -> ignore - _sth -> :ignore_line - end - _sth -> - case ignore do - :ignore_line -> :no_ignore - _sth -> ignore - end + ["start"] -> :start + ["stop"] -> :stop + ["next-line"] -> :next_line + _sth -> :none end end + defp process_regular_line( + coverage_line, + _index, + state = %{ignore_mode: :no_ignore, coverage_buffer: []} + ) do + %{state | coverage: [coverage_line | state.coverage]} + end + + defp process_regular_line(_coverage_line, _index, state = %{ignore_mode: :ignore_line}) do + %{state | ignore_mode: :no_ignore, coverage: [nil | state.coverage]} + end + + defp process_regular_line(_coverage_line, _index, state = %{ignore_mode: :ignore_block}) do + %{state | coverage: [nil | state.coverage]} + end + + defp process_start_marker( + _coverage_line, + index, + state = %{ignore_mode: :no_ignore} + ) do + %{ + state + | ignore_mode: :ignore_block, + coverage: [nil | state.coverage], + last_marker_index: index + } + end + + defp process_start_marker(_coverage_line, index, state = %{ignore_mode: :ignore_block}) do + warning = {index, "unexpected ignore-start or missing previous ignore-stop"} + + %{ + state + | coverage: [nil | state.coverage], + warnings: [warning | state.warnings], + last_marker_index: index + } + end + + defp process_start_marker(_coverage_line, index, state = %{ignore_mode: :ignore_line}) do + warning = {state.last_marker_index, "redundant ignore-next-line right before an ignore-start"} + + %{ + state + | ignore_mode: :ignore_block, + coverage: [nil | state.coverage], + warnings: [warning | state.warnings], + last_marker_index: index + } + end + + defp process_stop_marker(_coverage_line, index, state = %{ignore_mode: :ignore_block}) do + %{ + state + | ignore_mode: :no_ignore, + coverage: [nil | state.coverage], + last_marker_index: index + } + end + + defp process_stop_marker(_coverage_line, index, state) do + warning = {index, "unexpected ignore-stop or missing previous ignore-start"} + + %{ + state + | ignore_mode: :no_ignore, + coverage: [nil | state.coverage], + warnings: [warning | state.warnings], + last_marker_index: index + } + end + + defp process_next_line_marker( + _coverage_line, + index, + state = %{ignore_mode: :no_ignore} + ) do + %{ + state + | ignore_mode: :ignore_line, + coverage: [nil | state.coverage], + last_marker_index: index + } + end + + defp process_next_line_marker( + _coverage_line, + index, + state = %{ignore_mode: :ignore_block} + ) do + warning = {index, "redundant ignore-next-line inside ignore block"} + + %{ + state + | coverage: [nil | state.coverage], + warnings: [warning | state.warnings] + } + end + + defp process_next_line_marker( + _coverage_line, + index, + state = %{ignore_mode: :ignore_line} + ) do + warning = {index, "duplicated ignore-next-line"} + + %{ + state + | coverage: [nil | state.coverage], + warnings: [warning | state.warnings], + last_marker_index: index + } + end + + defp process_end_of_file(state = %{ignore_mode: :ignore_block}) do + warning = + {state.last_marker_index, "ignore-start without a corresponding ignore-stop"} + + %{state | warnings: [warning | state.warnings]} + end + + defp process_end_of_file(state), do: state end diff --git a/lib/excoveralls/json.ex b/lib/excoveralls/json.ex index 6b8bc542..633bc046 100644 --- a/lib/excoveralls/json.ex +++ b/lib/excoveralls/json.ex @@ -2,6 +2,7 @@ defmodule ExCoveralls.Json do @moduledoc """ Generate JSON output for results. """ + alias ExCoveralls.Stats @file_name "excoveralls.json" @@ -16,7 +17,7 @@ defmodule ExCoveralls.Json do def generate_json(stats, _options) do Jason.encode!(%{ - source_files: stats + source_files: Stats.serialize(stats) }) end diff --git a/lib/excoveralls/local.ex b/lib/excoveralls/local.ex index f262d793..ee2a41f3 100644 --- a/lib/excoveralls/local.ex +++ b/lib/excoveralls/local.ex @@ -46,6 +46,7 @@ defmodule ExCoveralls.Local do enabled = ExCoveralls.Settings.get_print_summary if enabled and not ExCoveralls.ConfServer.summary_printed?() do coverage(stats, options) |> IO.puts() + warnings(stats) |> IO.write() ExCoveralls.ConfServer.summary_printed() end end @@ -92,6 +93,12 @@ defmodule ExCoveralls.Local do end end + def warnings(stats) do + for stat <- stats, {line_num, message} <- stat[:warnings], into: "" do + print_string("\e[33mwarning:\e[m ~s\n ~s:~b\n", [message, stat[:name], line_num + 1]) + end + end + defp sort(count_info, options) do if options[:sort] do sort_order = parse_sort_options(options) diff --git a/lib/excoveralls/post.ex b/lib/excoveralls/post.ex index 0f546f8f..e5982e58 100644 --- a/lib/excoveralls/post.ex +++ b/lib/excoveralls/post.ex @@ -3,6 +3,7 @@ defmodule ExCoveralls.Post do Handles general-purpose CI integration with coveralls. """ alias ExCoveralls.Poster + alias ExCoveralls.Stats def execute(stats, options) do json = generate_json(stats, options) @@ -17,7 +18,7 @@ defmodule ExCoveralls.Post do repo_token: options[:token], service_name: options[:service_name], service_number: options[:service_number], - source_files: source_info, + source_files: Stats.serialize(source_info), parallel: options[:parallel], flag_name: options[:flagname], git: generate_git_info(options) diff --git a/lib/excoveralls/semaphore.ex b/lib/excoveralls/semaphore.ex index 5a91fc06..024c1721 100644 --- a/lib/excoveralls/semaphore.ex +++ b/lib/excoveralls/semaphore.ex @@ -3,6 +3,7 @@ defmodule ExCoveralls.Semaphore do Handles semaphore-ci integration with coveralls. """ alias ExCoveralls.Poster + alias ExCoveralls.Stats def execute(stats, options) do json = generate_json(stats, Enum.into(options, %{})) @@ -20,7 +21,7 @@ defmodule ExCoveralls.Semaphore do service_number: get_build_num(), service_job_id: get_build_num(), service_pull_request: get_pull_request(), - source_files: stats, + source_files: Stats.serialize(stats), git: generate_git_info(), parallel: options[:parallel], flag_name: options[:flagname] diff --git a/lib/excoveralls/stats.ex b/lib/excoveralls/stats.ex index eb504ee9..7fe4a03e 100644 --- a/lib/excoveralls/stats.ex +++ b/lib/excoveralls/stats.ex @@ -223,6 +223,14 @@ defmodule ExCoveralls.Stats do %Line{coverage: Enum.at(coverage, i) , source: line} end + @doc """ + Converts coverage stats to a map, which can be serialized to JSON + for posting it to Coveralls or writing to excoveralls.json. + """ + def serialize(stats) do + Enum.map(stats, &Map.take(&1, [:name, :source, :coverage])) + end + @doc """ Exit the process with a status of 1 if coverage is below the minimum. """ diff --git a/lib/excoveralls/travis.ex b/lib/excoveralls/travis.ex index 966b02ca..00e54b22 100644 --- a/lib/excoveralls/travis.ex +++ b/lib/excoveralls/travis.ex @@ -3,6 +3,7 @@ defmodule ExCoveralls.Travis do Handles travis-ci integration with coveralls. """ alias ExCoveralls.Poster + alias ExCoveralls.Stats def execute(stats, options) do json = generate_json(stats, Enum.into(options, %{})) @@ -18,7 +19,7 @@ defmodule ExCoveralls.Travis do service_job_id: get_job_id(), service_name: "travis-pro", repo_token: get_repo_token(), - source_files: stats, + source_files: Stats.serialize(stats), git: generate_git_info() }) end @@ -26,7 +27,7 @@ defmodule ExCoveralls.Travis do Jason.encode!(%{ service_job_id: get_job_id(), service_name: "travis-ci", - source_files: stats, + source_files: Stats.serialize(stats), git: generate_git_info() }) end diff --git a/test/circle_test.exs b/test/circle_test.exs index 8c40471d..a29ddc29 100644 --- a/test/circle_test.exs +++ b/test/circle_test.exs @@ -7,7 +7,8 @@ defmodule ExCoveralls.CircleTest do @counts [0, 1, nil, nil] @source_info [%{name: "test/fixtures/test.ex", source: @content, - coverage: @counts + coverage: @counts, + warnings: [] }] setup do diff --git a/test/cobertura_test.exs b/test/cobertura_test.exs index 4a78234a..9176a2e8 100644 --- a/test/cobertura_test.exs +++ b/test/cobertura_test.exs @@ -9,7 +9,11 @@ defmodule ExCoveralls.CoberturaTest do @content "defmodule Test do\n def test do\n end\nend\n" @counts [0, 1, nil, nil] - @source_info [%{name: "test/fixtures/test.ex", source: @content, coverage: @counts}] + @source_info [%{name: "test/fixtures/test.ex", + source: @content, + coverage: @counts, + warnings: [] + }] @stats_result "" <> "----------------\n" <> @@ -206,7 +210,11 @@ defmodule ExCoveralls.CoberturaTest do get_print_files: fn -> true end do content = "defprotocol TestProtocol do\n def test(value)\nend\n" counts = [0, 1, nil, nil] - source_info = [%{name: "test/fixtures/test_protocol.ex", source: content, coverage: counts}] + source_info = [%{name: "test/fixtures/test_protocol.ex", + source: content, + coverage: counts, + warnings: [] + }] stats_result = "" <> @@ -226,7 +234,11 @@ defmodule ExCoveralls.CoberturaTest do get_print_files: fn -> true end do content = "defimpl TestProtocol, for: Integer do\n def test(value), do: \"integer!\" \nend\n" counts = [0, 1, nil, nil] - source_info = [%{name: "test/fixtures/test_impl.ex", source: content, coverage: counts}] + source_info = [%{name: "test/fixtures/test_impl.ex", + source: content, + coverage: counts, + warnings: [] + }] stats_result = "" <> diff --git a/test/drone_test.exs b/test/drone_test.exs index 37cbe7e1..0d790041 100644 --- a/test/drone_test.exs +++ b/test/drone_test.exs @@ -7,7 +7,8 @@ defmodule ExCoveralls.DroneTest do @counts [0, 1, nil, nil] @source_info [%{name: "test/fixtures/test.ex", source: @content, - coverage: @counts + coverage: @counts, + warnings: [] }] setup do diff --git a/test/github_test.exs b/test/github_test.exs index 429ccf6e..94b86270 100644 --- a/test/github_test.exs +++ b/test/github_test.exs @@ -5,7 +5,12 @@ defmodule ExCoveralls.GithubTest do @content "defmodule Test do\n def test do\n end\nend\n" @counts [0, 1, nil, nil] - @source_info [%{name: "test/fixtures/test.ex", source: @content, coverage: @counts}] + @source_info [%{name: "test/fixtures/test.ex", + source: @content, + coverage: @counts, + warnings: [] + }] + setup do # No additional context github_event_path = System.get_env("GITHUB_EVENT_PATH") diff --git a/test/gitlab_test.exs b/test/gitlab_test.exs index 5d819725..674ca74b 100644 --- a/test/gitlab_test.exs +++ b/test/gitlab_test.exs @@ -5,7 +5,11 @@ defmodule ExCoveralls.GitlabTest do @content "defmodule Test do\n def test do\n end\nend\n" @counts [0, 1, nil, nil] - @source_info [%{name: "test/fixtures/test.ex", source: @content, coverage: @counts}] + @source_info [%{name: "test/fixtures/test.ex", + source: @content, + coverage: @counts, + warnings: [] + }] setup do # Capture existing values diff --git a/test/html_test.exs b/test/html_test.exs index c78ae79c..9b785e7e 100644 --- a/test/html_test.exs +++ b/test/html_test.exs @@ -13,7 +13,8 @@ defmodule ExCoveralls.HtmlTest do @counts [0, 1, nil, nil] @source_info [%{name: "test/fixtures/test.ex", source: @content, - coverage: @counts + coverage: @counts, + warnings: [] }] @stats_result "" <> diff --git a/test/ignore_test.exs b/test/ignore_test.exs index c661f5bb..f3587309 100644 --- a/test/ignore_test.exs +++ b/test/ignore_test.exs @@ -2,7 +2,7 @@ defmodule ExCoveralls.IgnoreTest do use ExUnit.Case alias ExCoveralls.Ignore - @block_content """ + @content """ defmodule Test do def test do end @@ -12,13 +12,20 @@ defmodule ExCoveralls.IgnoreTest do #coveralls-ignore-stop end """ - @block_counts [0, 0, 0, nil, 0, 0, nil, 0, 0] - @block_source_info [%{name: "test/fixtures/test.ex", - source: @block_content, - coverage: @block_counts + @counts [0, 0, 0, nil, 0, 0, nil, 0, 0] + @source_info [%{name: "test/fixtures/test.ex", + source: @content, + coverage: @counts }] - @single_line_content """ + test "filter ignored lines with start/stop block 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]) + assert(info[:warnings] == []) + end + + @content """ defmodule Test do def test do end @@ -29,13 +36,20 @@ defmodule ExCoveralls.IgnoreTest do end end """ - @single_line_counts [0, 0, 0, nil, 0, 0, 0, 0, 0, 0] - @single_line_source_info [%{name: "test/fixtures/test.ex", - source: @single_line_content, - coverage: @single_line_counts + @counts [0, 0, 0, nil, 0, 0, 0, 0, 0, 0] + @source_info [%{name: "test/fixtures/test.ex", + source: @content, + coverage: @counts }] - @mixed_content """ + test "filter ignored lines with next-line returns valid list" do + info = Ignore.filter(@source_info) |> Enum.at(0) + assert(info[:source] == @content) + assert(info[:coverage] == [0, 0, 0, nil, nil, 0, 0, 0, 0, 0]) + assert(info[:warnings] == []) + end + + @content """ defmodule Test do def test do end @@ -48,27 +62,89 @@ defmodule ExCoveralls.IgnoreTest do end end """ - @mixed_counts [0, 0, 0, nil, 0, nil, 0, nil, 0, 0, 0, 0] - @mixed_source_info [%{name: "test/fixtures/test.ex", - source: @mixed_content, - coverage: @mixed_counts + @counts [0, 0, 0, nil, 0, nil, 0, nil, 0, 0, 0, 0] + @source_info [%{name: "test/fixtures/test.ex", + source: @content, + coverage: @counts }] - test "filter ignored lines with start/stop block returns valid list" do - info = Ignore.filter(@block_source_info) |> Enum.at(0) - assert(info[:source] == @block_content) - assert(info[:coverage] == [0, 0, 0, nil, nil, nil, nil, 0, 0]) + test "next-line marker inside start/stop block produces warning" do + info = Ignore.filter(@source_info) |> Enum.at(0) + assert(info[:source] == @content) + assert(info[:coverage] == [0, 0, 0, nil, nil, nil, nil, nil, 0, 0, 0, 0]) + assert(info[:warnings] == [{5, "redundant ignore-next-line inside ignore block"}]) end - test "filter ignored lines with next-line returns valid list" do - info = Ignore.filter(@single_line_source_info) |> Enum.at(0) - assert(info[:source] == @single_line_content) - assert(info[:coverage] == [0, 0, 0, nil, nil, 0, 0, 0, 0, 0]) + @content """ + defmodule Test do + #coveralls-ignore-next-line + #coveralls-ignore-next-line end + """ + @counts [0, nil, nil, 0, 0] + @source_info [%{name: "test/fixtures/test.ex", + source: @content, + coverage: @counts + }] - test "filter ignored lines with next-line inside start/stop block returns valid list" do - info = Ignore.filter(@mixed_source_info) |> Enum.at(0) - assert(info[:source] == @mixed_content) - assert(info[:coverage] == [0, 0, 0, nil, nil, nil, nil, nil, 0, 0, 0, 0]) + test "next-line marker right after another next-line marker produces warning" do + info = Ignore.filter(@source_info) |> Enum.at(0) + assert(info[:source] == @content) + assert(info[:coverage] == [0, nil, nil, nil, 0]) + assert(info[:warnings] == [{2, "duplicated ignore-next-line"}]) + end + + @content """ + defmodule Test do + def test do + end + #coveralls-ignore-start + def test do + end + #coveralls-ignore-stop + def test_not_ignored do + end + #coveralls-ignore-start + def test_missing_stop + end + end + """ + @counts [0, 0, 0, nil, 0, 0, nil, 0, 0, nil, 0, 0, 0, 0] + @source_info [%{name: "test/fixtures/test.ex", + source: @content, + coverage: @counts + }] + + test "start marker without a stop marker produces warning" 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, nil, nil, nil, nil, nil]) + assert(info[:warnings] == [{9, "ignore-start without a corresponding ignore-stop"}]) + end + + @content """ + defmodule Test do + def test do + end + #coveralls-ignore-start + def test do + end + #coveralls-ignore-start + def test_ignore + end + #coveralls-ignore-stop + end + """ + @counts [0, 0, 0, nil, 0, 0, nil, 0, 0, nil, 0, 0] + @source_info [%{name: "test/fixtures/test.ex", + source: @content, + coverage: @counts + }] + + test "start marker followed by another start marker produces warning" do + info = Ignore.filter(@source_info) |> Enum.at(0) + assert(info[:source] == @content) + assert(info[:coverage] == [0, 0, 0, nil, nil, nil, nil, nil, nil, nil, 0, 0]) + assert(info[:warnings] == [{6, "unexpected ignore-start or missing previous ignore-stop"}]) end end diff --git a/test/json_test.exs b/test/json_test.exs index d3848706..92d6f169 100644 --- a/test/json_test.exs +++ b/test/json_test.exs @@ -12,7 +12,8 @@ defmodule ExCoveralls.JsonTest do @counts [0, 1, nil, nil] @source_info [%{name: "test/fixtures/test.ex", source: @content, - coverage: @counts + coverage: @counts, + warnings: [] }] @stats_result "" <> diff --git a/test/lcov_test.exs b/test/lcov_test.exs index 25c59ec2..ac52896b 100644 --- a/test/lcov_test.exs +++ b/test/lcov_test.exs @@ -12,7 +12,8 @@ defmodule ExCoveralls.LcovTest do @test_file_name "test/fixtures/test.ex" @source_info [%{name: @test_file_name, source: @content, - coverage: @counts + coverage: @counts, + warnings: [] }] @stats_result "" <> diff --git a/test/local_test.exs b/test/local_test.exs index 27cfc7fb..db74d572 100644 --- a/test/local_test.exs +++ b/test/local_test.exs @@ -8,21 +8,32 @@ defmodule ExCoveralls.LocalTest do @counts [0, 1, nil, nil] @source_info [%{name: "test/fixtures/test.ex", source: @content, - coverage: @counts + coverage: @counts, + warnings: [] }] @invalid_counts [0, 1, nil, "invalid"] @invalid_source_info [%{name: "test/fixtures/test.ex", source: @content, - coverage: @invalid_counts + coverage: @invalid_counts, + warnings: [] }] @empty_counts [nil, nil, nil, nil] @empty_source_info [%{name: "test/fixtures/test.ex", source: @content, - coverage: @empty_counts + coverage: @empty_counts, + warnings: [] }] + @warning_source_info [%{ + name: "test/fixtures/test.ex", + source: @content, + coverage: @counts, + warnings: [{2, "this is a test"}, {4, "another test"}] + }] + + @stats_result "" <> "----------------\n" <> "COV FILE LINES RELEVANT MISSED\n" <> @@ -38,6 +49,12 @@ defmodule ExCoveralls.LocalTest do " end\n" <> "end" + @warning_result "" <> + "\n\e[33mwarning:\e[m this is a test\n" <> + " test/fixtures/test.ex:3\n" <> + "\e[33mwarning:\e[m another test\n" <> + " test/fixtures/test.ex:5\n" + setup do ExCoveralls.ConfServer.clear() on_exit(fn -> ExCoveralls.ConfServer.clear() end) @@ -74,6 +91,12 @@ defmodule ExCoveralls.LocalTest do end end + test "display warnings" do + assert capture_io(fn -> + Local.execute(@warning_source_info) + end) =~ @warning_result + end + test "Empty (no relevant lines) file is calculated as 0.0%" do assert String.contains?(Local.coverage(@empty_source_info), "[TOTAL] 0.0%") end diff --git a/test/post_test.exs b/test/post_test.exs index fd62b8e9..85b5274f 100644 --- a/test/post_test.exs +++ b/test/post_test.exs @@ -7,7 +7,8 @@ defmodule ExCoveralls.PostTest do @counts [0, 1, nil, nil] @source_info [%{name: "test/fixtures/test.ex", source: @content, - coverage: @counts + coverage: @counts, + warnings: [] }] test_with_mock "execute", ExCoveralls.Poster, [execute: fn(_, _) -> "result" end] do diff --git a/test/semaphore_test.exs b/test/semaphore_test.exs index 161674af..7ff49177 100644 --- a/test/semaphore_test.exs +++ b/test/semaphore_test.exs @@ -5,7 +5,11 @@ defmodule ExCoveralls.SemaphoreTest do @content "defmodule Test do\n def test do\n end\nend\n" @counts [0, 1, nil, nil] - @source_info [%{name: "test/fixtures/test.ex", source: @content, coverage: @counts}] + @source_info [%{name: "test/fixtures/test.ex", + source: @content, + coverage: @counts, + warnings: [] + }] setup do # Capture existing values diff --git a/test/travis_test.exs b/test/travis_test.exs index caf40686..269db90d 100644 --- a/test/travis_test.exs +++ b/test/travis_test.exs @@ -7,7 +7,8 @@ defmodule ExCoveralls.TravisTest do @counts [0, 1, nil, nil] @source_info [%{name: "test/fixtures/test.ex", source: @content, - coverage: @counts + coverage: @counts, + warnings: [] }] test_with_mock "execute", ExCoveralls.Poster, [execute: fn(_) -> "result" end] do diff --git a/test/xml_test.exs b/test/xml_test.exs index ad0e6019..279ebe5e 100644 --- a/test/xml_test.exs +++ b/test/xml_test.exs @@ -11,7 +11,8 @@ defmodule ExCoveralls.XmlTest do @counts [0, 1, nil, nil] @source_info [%{name: "test/fixtures/test.ex", source: @content, - coverage: @counts + coverage: @counts, + warnings: [] }] @stats_result "" <>