Skip to content

Commit

Permalink
feat: add gcov format support
Browse files Browse the repository at this point in the history
  • Loading branch information
mrexox committed Mar 3, 2023
1 parent 9f9288a commit 5624622
Show file tree
Hide file tree
Showing 14 changed files with 184 additions and 10 deletions.
1 change: 1 addition & 0 deletions .ameba.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,6 @@ Metrics/CyclomaticComplexity:
MaxComplexity: 10
Excluded:
- src/coverage_reporter/parsers/lcov_parser.cr
- src/coverage_reporter/parsers/gcov_parser.cr
Enabled: true
Severity: Warning
2 changes: 1 addition & 1 deletion spec/coverage_reporter/parser_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ Spectator.describe CoverageReporter::Parser do
it "returns reports for all files" do
reports = subject.parse

expect(reports.size).to eq 6
expect(reports.size).to eq 7
end
end
end
Expand Down
34 changes: 34 additions & 0 deletions spec/coverage_reporter/parsers/gcov_parser_spec.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
require "../../spec_helper"

Spectator.describe CoverageReporter::GcovParser do
subject { described_class.new(base_path) }

let(base_path) { nil }

describe "#parse" do
let(filename) { "spec/fixtures/gcov/main.c.gcov" }

it "parses gcov" do
result = subject.parse(filename)

expect(result[0].to_h).to eq({
:name => "main.c",
:coverage => [nil, nil, 2, nil, 2, 1, 1, 1, nil, 0, nil, 2, nil, nil, 1, nil, 1, 1, 1, nil],
})
end

context "with base_path" do
let(base_path) { "spec/fixtures/gcov" }

it "parses gcov with source digest" do
result = subject.parse(filename)

expect(result[0].to_h).to eq({
:name => "spec/fixtures/gcov/main.c",
:coverage => [nil, nil, 2, nil, 2, 1, 1, 1, nil, 0, nil, 2, nil, nil, 1, nil, 1, 1, 1, nil],
:source_digest => "da803fdb1b06abe64c3b806d861a5baa",
})
end
end
end
end
10 changes: 6 additions & 4 deletions spec/coverage_reporter/parsers/lcov_parser_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,9 @@ Spectator.describe CoverageReporter::LcovParser do

expect(reports.size).to eq 1
expect(reports[0].to_h).to eq({
:name => "spec/fixtures/test.js",
:coverage => coverage,
:name => "spec/fixtures/test.js",
:coverage => coverage,
:source_digest => "6e7aea5aa7198489561a44359dc7e1a4",
})
end

Expand All @@ -42,8 +43,9 @@ Spectator.describe CoverageReporter::LcovParser do

expect(reports.size).to eq 1
expect(reports[0].to_h).to eq({
:name => "spec/fixtures/test.js",
:coverage => coverage,
:name => "spec/fixtures/test.js",
:coverage => coverage,
:source_digest => "6e7aea5aa7198489561a44359dc7e1a4",
})
end
end
Expand Down
4 changes: 4 additions & 0 deletions spec/fixtures/gcov/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
main
main.o
main.gcda
main.gcno
5 changes: 5 additions & 0 deletions spec/fixtures/gcov/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
coverage:
gcc -fPIC -fprofile-arcs -ftest-coverage -c -Wall -Werror main.c
gcc -fPIC -fprofile-arcs -ftest-coverage -o main main.o
./main
gcov main.c
20 changes: 20 additions & 0 deletions spec/fixtures/gcov/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#include <stdio.h>

void numbers(int num)
{
if (num == 1) {
printf("Number is 1\n");
} else if (num == 2){
printf("Number is 2\n");
} else {
printf("Number is %d\n", num);
}
}


int main(void)
{
numbers(1);
numbers(2);
return 0;
}
24 changes: 24 additions & 0 deletions spec/fixtures/gcov/main.c.gcov
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
-: 0:Source:main.c
-: 0:Graph:main.gcno
-: 0:Data:main.gcda
-: 0:Runs:1
-: 1:#include <stdio.h>
-: 2:
2: 3:void numbers(int num)
-: 4:{
2: 5: if (num == 1) {
1: 6: printf("Number is 1\n");
1: 7: } else if (num == 2){
1: 8: printf("Number is 2\n");
-: 9: } else {
#####: 10: printf("Number is %d\n", num);
-: 11: }
2: 12:}
-: 13:
-: 14:
1: 15:int main(void)
-: 16:{
1: 17: numbers(1);
1: 18: numbers(2);
1: 19: return 0;
-: 20:}
10 changes: 6 additions & 4 deletions src/coverage_reporter/file_report.cr
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,17 @@ module CoverageReporter
def initialize(
@name : String,
@coverage : Array(Int64?),
@branches : Array(Int64?) | Nil = nil
@branches : Array(Int64?) | Nil = nil,
@source_digest : String | Nil = nil
)
end

def to_h : Hash(Symbol, String | Array(Int64?))
{
:name => @name,
:coverage => @coverage,
:branches => @branches,
:name => @name,
:coverage => @coverage,
:branches => @branches,
:source_digest => @source_digest,
}.compact
end
end
Expand Down
1 change: 1 addition & 0 deletions src/coverage_reporter/parser.cr
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ module CoverageReporter
PARSERS = {
LcovParser,
SimplecovParser,
GcovParser,
}

def initialize(@file : String?, base_path : String?)
Expand Down
8 changes: 8 additions & 0 deletions src/coverage_reporter/parsers/base_parser.cr
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
require "../file_report"
require "digest"

module CoverageReporter
# Coverage report parser interface.
Expand Down Expand Up @@ -35,6 +36,13 @@ module CoverageReporter
#
# Existing parsers can be used as a reference.
abstract class BaseParser
# Returns MD5 hashsum of a file.
def self.file_digest(filename : String) : String | Nil
return unless File.exists?(filename)

Digest::MD5.hexdigest(File.read(filename))
end

# Initializes the parser.
#
# *base_path* can be used to join with all paths in coverage report in order
Expand Down
72 changes: 72 additions & 0 deletions src/coverage_reporter/parsers/gcov_parser.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
require "./base_parser"
require "digest"

module CoverageReporter
class GcovParser < BaseParser
# Use *base_path* to join with paths found in reports.
def initialize(@base_path : String?)
end

def globs : Array(String)
[
"*.gcov",
"**/*/*.gcov",
]
end

def matches?(filename : String) : Bool
filename.ends_with?(".gcov")
end

def parse(filename : String) : Array(FileReport)
base_path = @base_path
coverage = {} of Int64 => Int64?
name : String? = nil
source_digest : String? = nil
File.each_line(filename, chomp: true) do |line|
match = /^\s*([0-9]+|-|#####):\s*([0-9]+):(.*)/.match(line).try(&.to_a)
next if !match || !match.try(&.size) == 4

count, number, text = match[1..3]
next unless number && text && count

number = number.to_i64

if number == 0
match = /([^:]+):(.*)$/.match(text).try(&.to_a)
next if !match || match.try(&.size) < 2

key, val = match[1..2]
if key == "Source" && val
val = base_path ? File.join(base_path, val) : val
name = val.sub(Dir.current, "")
source_digest = BaseParser.file_digest(val)
end
else
coverage[number - 1] = case count.strip
when "-"
nil
when "#####"
if text.strip == "}"
nil
else
0.to_i64
end
else
count.to_i64
end
end
end

return [] of FileReport unless name

[
FileReport.new(
name: name,
coverage: coverage.keys.sort!.map { |i| coverage[i]? },
source_digest: source_digest,
),
]
end
end
end
1 change: 1 addition & 0 deletions src/coverage_reporter/parsers/lcov_parser.cr
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ module CoverageReporter
name: filename.sub(Dir.current, ""),
coverage: coverage,
branches: branches,
source_digest: BaseParser.file_digest(filename),
)
end
end
Expand Down
2 changes: 1 addition & 1 deletion src/coverage_reporter/parsers/simplecov_parser.cr
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ module CoverageReporter
FileReport.new(
name: name.sub(Dir.current, ""),
coverage: coverage,
branches: branches,
source_digest: BaseParser.file_digest(name),
)
)
end
Expand Down

0 comments on commit 5624622

Please sign in to comment.