covhura 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: cf3df102e2f0a35ce06e33bca52a82744a522968
4
+ data.tar.gz: 23410c79deefd695d11625cd95e4e2168262c982
5
+ SHA512:
6
+ metadata.gz: e77c29aba5b090b40754cd4e7b4b159ee32961cb8e993408fe970b6d8e444448b493a55f04bea0450b782c440e6a96df63699bb6141e555338278461b34ecd3e
7
+ data.tar.gz: 356c62803069ea720f097de41641bc0be5d3abc38734f9b11765595979bafbda70fab33ee5853e4b62e350d02061ae58ae86250c1fca8811288784887398dcf3
data/bin/covhura.rb ADDED
@@ -0,0 +1,5 @@
1
+ #! /usr/bin/env ruby
2
+
3
+ require_relative "../lib/covhura"
4
+
5
+ puts Covhura.new.translate(File.read(ARGV.first)).to_json()
data/lib/covhura.rb ADDED
@@ -0,0 +1,82 @@
1
+ require "json"
2
+ require "nokogiri"
3
+
4
+ require_relative "report"
5
+ require_relative "report/clover"
6
+ require_relative "report/lcov"
7
+ require_relative "report/cobertura"
8
+ require_relative "report/simplecov"
9
+
10
+ class Covhura
11
+ def translate(doc)
12
+ lines = doc.lines
13
+ first_line = lines
14
+ .detect { |line| line.strip() != "" }
15
+ .strip()
16
+
17
+ if xml?(doc)
18
+ doc = Nokogiri::XML(doc)
19
+ if clover?(doc)
20
+ translator = Report::Clover
21
+ elsif cobertura?(doc)
22
+ translator = Report::Cobertura
23
+ end
24
+ elsif json?(first_line)
25
+ doc = JSON.parse(doc)
26
+ translator = Report::SimpleCov
27
+ elsif lcov?(first_line)
28
+ translator = Report::LCov
29
+ end
30
+
31
+ raise "Coverage Format Not Supported" if !defined?(translator) || translator.nil?
32
+
33
+ translator.new.translate(doc)
34
+ end
35
+
36
+ def merge(old, new)
37
+ new.reduce(old) do |acc, (file_path, file_coverage)|
38
+ acc[file_path] ||= {}
39
+ acc[file_path][:lines] ||= {}
40
+
41
+ old_coverage = acc.dig(file_path, :lines)
42
+
43
+ file_coverage[:lines].each do |line_number, line|
44
+ acc[file_path][:lines][line_number] = old_coverage.has_key?(line_number) ?
45
+ merge_line(old_coverage[line_number], line) :
46
+ line
47
+ end
48
+
49
+ acc
50
+ end
51
+ end
52
+
53
+ private
54
+
55
+ def clover?(doc)
56
+ doc.xpath("/coverage")&.attr("clover") != nil
57
+ end
58
+
59
+ def cobertura?(doc)
60
+ doc.internal_subset.to_s.match(/cobertura/i) != nil
61
+ end
62
+
63
+ def xml?(first_line)
64
+ first_line[0...5] == "<?xml"
65
+ end
66
+
67
+ def json?(first_line)
68
+ first_line[0] == "{" || first_line[0] == "["
69
+ end
70
+
71
+ def lcov?(first_line)
72
+ first_line[0...3] == "TN:"
73
+ end
74
+
75
+ def merge_line(old, new)
76
+ {
77
+ number: old[:number],
78
+ hits: (old[:hits] || 0) + (new[:hits] || 0),
79
+ type: old[:type] || new[:type]
80
+ }
81
+ end
82
+ end
@@ -0,0 +1,20 @@
1
+ class Report::Clover
2
+ def translate(doc)
3
+ doc
4
+ .xpath("//file")
5
+ .reduce({}) do |acc, file|
6
+ acc[file.attr("path")] ||= {}
7
+ acc[file.attr("path")][:lines] = file
8
+ .xpath("line")
9
+ .reduce({}) do |bcc, line|
10
+ line_num = line.attr("num").to_i
11
+ bcc[line_num] = {
12
+ hits: line.attr("count").to_i,
13
+ type: line.attr("type") == "cond" ? :condition : :statement
14
+ }
15
+ bcc
16
+ end
17
+ acc
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,20 @@
1
+ class Report::Cobertura
2
+ def translate(doc)
3
+ doc
4
+ .xpath("//class")
5
+ .reduce({}) do |acc, file|
6
+ acc[file.attr("filename")] ||= {}
7
+ acc[file.attr("filename")][:lines] = file
8
+ .xpath("lines/line")
9
+ .reduce({}) do |bcc, line|
10
+ line_num = line.attr("number").to_i
11
+ bcc[line_num] = {
12
+ hits: line.attr("hits").to_i,
13
+ type: line.attr("branch") == "true" ? :condition : :statement
14
+ }
15
+ bcc
16
+ end
17
+ acc
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,77 @@
1
+ # TN: -> start of record
2
+ # SF: -> file path
3
+ # FN: -> function declaration (line#,func_name)
4
+ # FNDA: function coverage (count,func_name)
5
+ # BRDA: branch coverage (line#,block#,branch#,count) count = - for none
6
+ # DA: line coverage
7
+ # LF: statements
8
+ # LH: statements covered
9
+ # FNF: functions
10
+ # FNH: functions covered
11
+ # BRF: branches / conditionals
12
+ # BRH: branches / conditions covered
13
+ # end_of_record: -> end of record
14
+ class Report::LCov
15
+ def translate(doc)
16
+ files(doc).reduce({}) do |acc, file|
17
+ file.shift()
18
+
19
+ lines = lines(file)
20
+ branches = branches(file)
21
+
22
+ path = file.first[3..-1]
23
+ acc[path] = {lines: merge(lines, branches)}
24
+
25
+ acc
26
+ end
27
+ end
28
+
29
+ private
30
+
31
+ def files(doc)
32
+ files = doc
33
+ .lines
34
+ .map(&:chomp)
35
+ .reduce([[]]) do |acc, line|
36
+ if line == "end_of_record"
37
+ acc << []
38
+ else
39
+ acc.last << line
40
+ end
41
+ acc
42
+ end
43
+ files.pop()
44
+ files
45
+ end
46
+
47
+ def lines(file)
48
+ file.reduce({}) do |acc, line|
49
+ if line[0...3] == "DA:"
50
+ coverage = line[3..-1].split(",")
51
+ acc[coverage.first.to_i] = coverage.last.to_i
52
+ end
53
+ acc
54
+ end
55
+ end
56
+
57
+ def branches(file)
58
+ file.reduce({}) do |acc, line|
59
+ if line[0...5] == "BRDA:"
60
+ coverage = line[5..-1].split(",")
61
+ acc[coverage.first.to_i] = true
62
+ end
63
+ acc
64
+ end
65
+ end
66
+
67
+ # TODO: Technically this misses branches that aren't covered in DA.
68
+ def merge(lines, branches)
69
+ lines.reduce({}) do |acc, (num, count)|
70
+ acc[num] = {
71
+ hits: count,
72
+ type: branches.has_key?(num) ? :condition : :statement
73
+ }
74
+ acc
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,33 @@
1
+ class Report::SimpleCov
2
+ def translate(json)
3
+ json
4
+ .reduce({}) do |acc, (_test_runner, results)|
5
+ results["coverage"].each do |file_path, file_coverage|
6
+ acc[file_path] ||= {}
7
+ file_coverage.each_with_index do |line_coverage, i|
8
+ line_num = i + 1
9
+ if line_coverage.nil?
10
+ acc[file_path][line_num] = nil
11
+ next
12
+ end
13
+ old_coverage = acc[file_path][line_num]
14
+ acc[file_path][line_num] = old_coverage.nil? ?
15
+ line_coverage :
16
+ line_coerage + old_coverage
17
+ end
18
+ end
19
+ acc
20
+ end
21
+ .reduce({}) do |acc, (file_name, lines)|
22
+ acc[file_name] ||= {}
23
+ acc[file_name][:lines] = lines.reduce({}) do |bcc, (line_num, hits)|
24
+ bcc[line_num] = {
25
+ hits: hits,
26
+ type: :unknown
27
+ }
28
+ bcc
29
+ end
30
+ acc
31
+ end
32
+ end
33
+ end
data/lib/report.rb ADDED
@@ -0,0 +1,2 @@
1
+ module Report
2
+ end
metadata ADDED
@@ -0,0 +1,51 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: covhura
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ platform: ruby
6
+ authors:
7
+ - Brian Yahn
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2018-10-31 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Translate code coverage reports into a universal format.
14
+ email: yahn007@outlook.com
15
+ executables:
16
+ - covhura.rb
17
+ extensions: []
18
+ extra_rdoc_files: []
19
+ files:
20
+ - bin/covhura.rb
21
+ - lib/covhura.rb
22
+ - lib/report.rb
23
+ - lib/report/clover.rb
24
+ - lib/report/cobertura.rb
25
+ - lib/report/lcov.rb
26
+ - lib/report/simplecov.rb
27
+ homepage: https://github.com/cuzzo/covhura
28
+ licenses:
29
+ - BSD-2-Clause
30
+ metadata: {}
31
+ post_install_message:
32
+ rdoc_options: []
33
+ require_paths:
34
+ - lib
35
+ required_ruby_version: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: '0'
40
+ required_rubygems_version: !ruby/object:Gem::Requirement
41
+ requirements:
42
+ - - ">="
43
+ - !ruby/object:Gem::Version
44
+ version: '0'
45
+ requirements: []
46
+ rubyforge_project:
47
+ rubygems_version: 2.5.2
48
+ signing_key:
49
+ specification_version: 4
50
+ summary: Coverage Report Translation
51
+ test_files: []