covhura 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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: []