Class: Danger::DangerJacoco

Inherits:
Plugin
  • Object
show all
Defined in:
lib/jacoco/plugin.rb

Overview

Verify code coverage inside your projects This is done using the jacoco output Results are passed out as a table in markdown

Examples:

Verify coverage

jacoco.minimum_project_coverage_percentage = 50

Verify coverage per package

jacoco.minimum_package_coverage_map = { # optional (default is empty)
 'com/package/' => 55,
 'com/package/more/specific/' => 15
}

See Also:

  • Malinskiy/danger-jacoco

Instance Attribute Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#fail_no_coverage_data_foundObject

rubocop:disable Metrics/ClassLength



23
24
25
# File 'lib/jacoco/plugin.rb', line 23

def fail_no_coverage_data_found
  @fail_no_coverage_data_found
end

#files_extensionObject

rubocop:disable Metrics/ClassLength



23
24
25
# File 'lib/jacoco/plugin.rb', line 23

def files_extension
  @files_extension
end

#minimum_class_coverage_mapObject

rubocop:disable Metrics/ClassLength



23
24
25
# File 'lib/jacoco/plugin.rb', line 23

def minimum_class_coverage_map
  @minimum_class_coverage_map
end

#minimum_class_coverage_percentageObject

rubocop:disable Metrics/ClassLength



23
24
25
# File 'lib/jacoco/plugin.rb', line 23

def minimum_class_coverage_percentage
  @minimum_class_coverage_percentage
end

#minimum_package_coverage_mapObject

rubocop:disable Metrics/ClassLength



23
24
25
# File 'lib/jacoco/plugin.rb', line 23

def minimum_package_coverage_map
  @minimum_package_coverage_map
end

#minimum_project_coverage_percentageObject

rubocop:disable Metrics/ClassLength



23
24
25
# File 'lib/jacoco/plugin.rb', line 23

def minimum_project_coverage_percentage
  @minimum_project_coverage_percentage
end

#titleObject

rubocop:disable Metrics/ClassLength



23
24
25
# File 'lib/jacoco/plugin.rb', line 23

def title
  @title
end

Instance Method Details

#classes(delimiter) ⇒ Object

Select modified and added files in this PR



83
84
85
86
87
88
# File 'lib/jacoco/plugin.rb', line 83

def classes(delimiter)
  git = @dangerfile.git
  affected_files = git.modified_files + git.added_files
  affected_files.select { |file| files_extension.reduce(false) { |state, el| state || file.end_with?(el) } }
                .map { |file| file.split('.').first.split(delimiter)[1] }
end

#coverage_status(coverage, minimum_percentage) ⇒ Object

it returns an emoji for coverage status



138
139
140
141
142
143
# File 'lib/jacoco/plugin.rb', line 138

def coverage_status(coverage, minimum_percentage)
  if coverage < (minimum_percentage / 2) then ':skull:'
  elsif coverage < minimum_percentage then ':warning:'
  else ':white_check_mark:'
  end
end

#package_coverage(class_name) ⇒ Object

it returns the most suitable coverage by package name to class or nil



124
125
126
127
128
129
130
131
132
133
134
135
# File 'lib/jacoco/plugin.rb', line 124

def package_coverage(class_name)
  path = class_name
  package_parts = class_name.split('/')
  package_parts.reverse_each do |item|
    size = item.size
    path = path[0...-size]
    coverage = minimum_package_coverage_map[path]
    path = path[0...-1] unless path.empty?
    return coverage unless coverage.nil?
  end
  nil
end

#parse(path) ⇒ Object

Parses the xml output of jacoco to Ruby model classes This is slow since it’s basically DOM parsing



41
42
43
# File 'lib/jacoco/plugin.rb', line 41

def parse(path)
  Jacoco::DOMParser.read_path(path)
end

#report(path, report_url = '', delimiter = %r{/java/|/kotlin/|/scala/}, fail_no_coverage_data_found: true) ⇒ Object

This is a fast report based on SAX parser

changed files. We need to get the class from this path to check the Jacoco report,

e.g. src/java/com/example/SomeJavaClass.java -> com/example/SomeJavaClass e.g. src/kotlin/com/example/SomeKotlinClass.kt -> com/example/SomeKotlinClass

The default value supposes that you’re using gradle structure, that is your path to source files is something like

Java => blah/blah/java/slashed_package/Source.java Kotlin => blah/blah/kotlin/slashed_package/Source.kt



62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
# File 'lib/jacoco/plugin.rb', line 62

def report(path, report_url = '', delimiter = %r{/java/|/kotlin/|/scala/}, fail_no_coverage_data_found: true)
  @fail_no_coverage_data_found = fail_no_coverage_data_found

  setup
  classes = classes(delimiter)

  parser = Jacoco::SAXParser.new(classes)
  Nokogiri::XML::SAX::Parser.new(parser).parse(File.open(path))

  total_covered = total_coverage(path)

  report_markdown = "### #{title} Code Coverage #{total_covered[:covered]}% #{total_covered[:status]}\n"
  report_markdown += "| Class | Covered | Meta | Status |\n"
  report_markdown += "|:---|:---:|:---:|:---:|\n"
  class_coverage_above_minimum = markdown_class(parser, report_markdown, report_url)
  markdown(report_markdown)

  report_fails(class_coverage_above_minimum, total_covered)
end

#report_class(jacoco_class) ⇒ Object

It returns a specific class code coverage and an emoji status as well



91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
# File 'lib/jacoco/plugin.rb', line 91

def report_class(jacoco_class)
  report_result = {
    covered: 'No coverage data found : -',
    status: ':black_joker:',
    required_coverage_percentage: 'No coverage data found : -'
  }

  counter = coverage_counter(jacoco_class)
  unless counter.nil?
    coverage = (counter.covered.fdiv(counter.covered + counter.missed) * 100).floor
    required_coverage = required_class_coverage(jacoco_class)
    status = coverage_status(coverage, required_coverage)

    report_result = {
      covered: coverage,
      status: status,
      required_coverage_percentage: required_coverage
    }
  end

  report_result
end

#required_class_coverage(jacoco_class) ⇒ Object

Determines the required coverage for the class



115
116
117
118
119
120
121
# File 'lib/jacoco/plugin.rb', line 115

def required_class_coverage(jacoco_class)
  key = minimum_class_coverage_map.keys.detect { |k| jacoco_class.name.match(k) } || jacoco_class.name
  required_coverage = minimum_class_coverage_map[key]
  required_coverage = package_coverage(jacoco_class.name) if required_coverage.nil?
  required_coverage = minimum_class_coverage_percentage if required_coverage.nil?
  required_coverage
end

#setupObject

Initialize the plugin with configured parameters or defaults



27
28
29
30
31
32
33
34
# File 'lib/jacoco/plugin.rb', line 27

def setup
  @minimum_project_coverage_percentage = 0 unless minimum_project_coverage_percentage
  @minimum_class_coverage_percentage = 0 unless minimum_class_coverage_percentage
  @minimum_package_coverage_map = {} unless minimum_package_coverage_map
  @minimum_class_coverage_map = {} unless minimum_class_coverage_map
  @files_extension = ['.kt', '.java'] unless files_extension
  @title = 'JaCoCo' unless title
end

#total_coverage(report_path) ⇒ Object

It returns total of project code coverage and an emoji status as well



146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
# File 'lib/jacoco/plugin.rb', line 146

def total_coverage(report_path)
  jacoco_report = Nokogiri::XML(File.open(report_path))

  report = jacoco_report.xpath('report/counter').select { |item| item['type'] == 'INSTRUCTION' }
  missed_instructions = report.first['missed'].to_f
  covered_instructions = report.first['covered'].to_f
  total_instructions = missed_instructions + covered_instructions
  covered_percentage = (covered_instructions * 100 / total_instructions).round(2)
  coverage_status = coverage_status(covered_percentage, minimum_project_coverage_percentage)

  {
    covered: covered_percentage,
    status: coverage_status
  }
end