Skip to content

Commit

Permalink
Merge pull request #264 from satoren/minimum_lcov_support2
Browse files Browse the repository at this point in the history
feat: minimum support for lcov (re)
  • Loading branch information
parroty committed Jul 22, 2021
2 parents e7e71a5 + f020734 commit c7d7ea2
Show file tree
Hide file tree
Showing 5 changed files with 169 additions and 0 deletions.
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ end
- [[mix coveralls.html] Show coverage as HTML report](#mix-coverallshtml-show-coverage-as-html-report)
- [[mix coveralls.json] Show coverage as JSON report](#mix-coverallsjson-show-coverage-as-json-report)
- [[mix coveralls.xml] Show coverage as XML report](#mix-coverallsxml-show-coverage-as-xml-report)
- [[mix coveralls.lcov] Show coverage as lcov repor (Experimental)](#mix-coverallslcov-show-coverage-as-lcov-report-experimental)
- [coveralls.json](#coverallsjson)
- [Stop Words](#stop-words)
- [Exclude Files](#exclude-files)
Expand Down Expand Up @@ -357,6 +358,13 @@ Output to the shell is the same as running the command `mix coveralls` (to suppr
Output reports are written to `cover/excoveralls.xml` by default, however, the path can be specified by overwriting the `"output_dir"` coverage option.
### [mix coveralls.lcov] Show coverage as lcov report (Experimental)
This task displays coverage information at the line level formatted as a lcov.
The report follows a format supported by several code coverage services like VSCode extension(`ryanluker.vscode-coverage-gutters`).
Output to the shell is the same as running the command `mix coveralls` (to suppress this output, add `"print_summary": false` to your project's `coveralls.json` file). In a similar manner to `mix coveralls.detail`, reported source code can be filtered by specifying arguments using the `--filter` flag.
Output reports are written to `cover/lcov.info` by default, however, the path can be specified by overwriting the `"output_dir"` coverage option.
## coveralls.json
`coveralls.json` provides settings for excoveralls.
Expand Down
6 changes: 6 additions & 0 deletions lib/excoveralls.ex
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ defmodule ExCoveralls do
alias ExCoveralls.Json
alias ExCoveralls.Post
alias ExCoveralls.Xml
alias ExCoveralls.Lcov

@type_travis "travis"
@type_github "github"
Expand All @@ -30,6 +31,7 @@ defmodule ExCoveralls do
@type_json "json"
@type_post "post"
@type_xml "xml"
@type_lcov "lcov"

@doc """
This method will be called from mix to trigger coverage analysis.
Expand Down Expand Up @@ -99,6 +101,10 @@ defmodule ExCoveralls do
Json.execute(stats, options)
end

def analyze(stats, @type_lcov, options) do
Lcov.execute(stats, options)
end

def analyze(stats, @type_xml, options) do
Xml.execute(stats, options)
end
Expand Down
73 changes: 73 additions & 0 deletions lib/excoveralls/lcov.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
defmodule ExCoveralls.Lcov do
@moduledoc """
Generate lcov output for results.
"""

@file_name "lcov.info"

@doc """
Provides an entry point for the module.
"""
def execute(stats, options \\ []) do
generate_lcov(stats, Enum.into(options, %{})) |> write_file(options[:output_dir])

ExCoveralls.Local.print_summary(stats)
end

def generate_lcov(stats, _options) do
lcov = Enum.map(stats, fn stat -> generate_lcov_file(stat) end) |> Enum.join("\n")
lcov <> "\n"
end

def generate_lcov_file(stat) do
da =
stat.coverage
|> Enum.with_index(1)
|> Enum.filter(fn {k, _v} -> k != nil end)
|> Enum.map(fn {k, v} -> {Integer.to_string(v), Integer.to_string(k)} end)
|> Enum.map(fn {line, count} -> "DA:" <> line <> "," <> count end)

foundlines =
stat.coverage
|> Enum.filter(fn v -> v != nil end)

lf = foundlines |> Enum.count()
lh = foundlines |> Enum.filter(fn v -> v > 0 end) |> Enum.count()

lines =
["TN:", "SF:" <> stat.name] ++
da ++
[
"LF:" <> Integer.to_string(lf),
"LH:" <> Integer.to_string(lh),
"end_of_record"
]

Enum.join(lines, "\n")
end

defp output_dir(output_dir) do
cond do
output_dir ->
output_dir

true ->
options = ExCoveralls.Settings.get_coverage_options()

case Map.fetch(options, "output_dir") do
{:ok, val} -> val
_ -> "cover/"
end
end
end

defp write_file(content, output_dir) do
file_path = output_dir(output_dir)

unless File.exists?(file_path) do
File.mkdir_p!(file_path)
end

File.write!(Path.expand(@file_name, file_path), content)
end
end
15 changes: 15 additions & 0 deletions lib/mix/tasks.ex
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,21 @@ defmodule Mix.Tasks.Coveralls do
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.Coveralls.do_run(args, [ type: "lcov" ])
end
end

defmodule Travis do
@moduledoc """
Provides an entry point for travis's script.
Expand Down
67 changes: 67 additions & 0 deletions test/lcov_test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
defmodule ExCoveralls.LcovTest do
use ExUnit.Case
import Mock
import ExUnit.CaptureIO
alias ExCoveralls.Lcov

@file_name "lcov.info"
@file_size 67
@test_output_dir "cover_test/"

@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
}]

@stats_result "" <>
"----------------\n" <>
"COV FILE LINES RELEVANT MISSED\n" <>
" 50.0% test/fixtures/test.ex 4 2 1\n" <>
"[TOTAL] 50.0%\n" <>
"----------------\n"

setup do
path = Path.expand(@file_name, @test_output_dir)

# Assert does not exist prior to write
assert(File.exists?(path) == false)
on_exit fn ->
if File.exists?(path) do
# Ensure removed after test
File.rm!(path)
File.rmdir!(@test_output_dir)
end
end

{:ok, report: path}
end

test_with_mock "generate lcov file", %{report: report}, ExCoveralls.Settings, [],
[
get_coverage_options: fn -> %{"output_dir" => @test_output_dir} end,
get_file_col_width: fn -> 40 end,
get_print_summary: fn -> true end,
get_print_files: fn -> true end
] do

assert capture_io(fn ->
Lcov.execute(@source_info)
end) =~ @stats_result

assert(File.read!(report) =~ ~s(TN:\nSF:test/fixtures/test.ex\nDA:1,0\nDA:2,1\nLF:2\nLH:1\nend_of_record\n))
%{size: size} = File.stat! report
assert(size == @file_size)
end

test "generate json file with output_dir parameter", %{report: report} do
assert capture_io(fn ->
Lcov.execute(@source_info, [output_dir: @test_output_dir])
end) =~ @stats_result

assert(File.read!(report) =~ ~s(TN:\nSF:test/fixtures/test.ex\nDA:1,0\nDA:2,1\nLF:2\nLH:1\nend_of_record\n))
%{size: size} = File.stat! report
assert(size == @file_size)
end
end

0 comments on commit c7d7ea2

Please sign in to comment.