Class: Danger::DangerShroud

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

Overview

Parse a Jacoco report to enforce code coverage on CI. Results are passed out as a table in markdown.

Shroud depends on having a Jacoco coverage report generated for your project. For Android projects, [jacoco-android-gradle-plugin](github.com/arturdm/jacoco-android-gradle-plugin) works well.

Examples:

Running shroud with default values


# Report coverage of modified files, fail if either total project coverage
# or any modified file's coverage is under 90%
shroud.report 'path/to/jacoco/report.xml'

Running shroud with custom coverage thresholds


# Report coverage of modified files, fail if total project coverage is under 80%,
# or if any modified file's coverage is under 95%
shroud.report 'path/to/jacoco/report.xml', 80, 95

Warn on builds instead of fail


# Report coverage of modified files the same as the above example, except the
# builds will only warn instead of fail if below thresholds
shroud.report 'path/to/jacoco/report.xml', 80, 95, false

Instance Method Summary collapse

Instance Method Details

#report(file, totalProjectThreshold = 90, modifiedFileThreshold = 90, failIfUnderThreshold = true) ⇒ void

This method returns an undefined value.

Report coverage on diffed files, as well as overall coverage.

Parameters:

  • file (String)

    file path to a Jacoco xml coverage report.

  • totalProjectThreshold (Integer) (defaults to: 90)

    defines the required percentage of total project coverage for a passing build. default 90.

  • modifiedFileThreshold (Integer) (defaults to: 90)

    defines the required percentage of files modified in a PR for a passing build. default 90.

  • failIfUnderThreshold (Boolean) (defaults to: true)

    if true, will fail builds that are under the provided thresholds. if false, will only warn. default true.



50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
# File 'lib/shroud/plugin.rb', line 50

def report(file, totalProjectThreshold = 90, modifiedFileThreshold = 90, failIfUnderThreshold = true)
  raise "Please specify file name." if file.empty?
  raise "No jacoco xml report found at #{file}" unless File.exist? file
  rawXml = File.read(file)
  parsedXml = Nokogiri::XML.parse(rawXml)
  totalInstructionCoverage = parsedXml.xpath("/report/counter[@type='INSTRUCTION']")
  missed = totalInstructionCoverage.attr("missed").value.to_i
  covered = totalInstructionCoverage.attr("covered").value.to_i
  total = missed + covered
  coveragePercent = (covered / total.to_f) * 100

  # get array of files names touched by this PR (modified + added)
  touchedFileNames = @dangerfile.git.modified_files.map { |file| File.basename(file) }
  touchedFileNames += @dangerfile.git.added_files.map { |file| File.basename(file) }

  # used to later report files that were modified but not included in the jacoco report
  fileNamesNotInJacocoReport = []

  # hash for keeping track of coverage per filename: {filename => coverage percent}
  touchedFilesHash = {}

  touchedFileNames.each do |touchedFileName|
    xmlForFileName = parsedXml.xpath("//class[@sourcefilename='#{touchedFileName}']/counter[@type='INSTRUCTION']")

    if (xmlForFileName.length > 0)
      missed = 0
      covered = 0
      xmlForFileName.each do |classCountXml|
        missed += classCountXml.attr("missed").to_i
        covered += classCountXml.attr("covered").to_i
      end
      touchedFilesHash[touchedFileName] = (covered.to_f / (missed + covered)) * 100
    else
      fileNamesNotInJacocoReport << touchedFileName
    end
  end

  puts "Here are unreported files"
  puts fileNamesNotInJacocoReport.to_s
  puts "Here is the touched files coverage hash"
  puts touchedFilesHash

  output = "## 🧛 Project Code Coverage: **`#{'%.2f' % coveragePercent}%`**\n"

  output << "### Coverage of Modified Files:\n"
  output << "File | Coverage\n"
  output << ":-----|:-----:\n"

  # go through each file:
  touchedFilesHash.sort.each do |fileName, coveragePercent|
    output << "`#{fileName}` | **`#{'%.2f' % coveragePercent}%`**\n"

    # warn or fail if under specified file threshold:
    if (coveragePercent < modifiedFileThreshold)
      warningMessage = "Uh oh! #{fileName} is under #{modifiedFileThreshold}% coverage!"
      if (failIfUnderThreshold)
        fail warningMessage
      else 
        warn warningMessage
      end
    end
  end

  output << "### Modified Files Not Found In Coverage Report:\n"
  fileNamesNotInJacocoReport.sort.each do |unreportedFileName| 
    output << "#{unreportedFileName}\n"
  end

  output << '> Codebase cunningly covered by count [Shroud 🧛](https://github.com/livefront/livefront-shroud-android/)'
  markdown output

  # warn or fail if total coverage is under specified threshold
  if (coveragePercent < totalProjectThreshold)
    totalCoverageWarning = "Uh oh! Your project is under #{totalProjectThreshold}% coverage!"
    if (failIfUnderThreshold) 
      fail totalCoverageWarning
    else 
      warn totalCoverageWarning
    end
  end
end