From 264f4e2a3bfc8bb3df7eb141e923b6d7b87a4dc7 Mon Sep 17 00:00:00 2001 From: Benjamin Fleischer Date: Mon, 17 Jul 2017 21:04:09 -0500 Subject: [PATCH] Refactor SimpleCov result/report generator --- .simplecov | 36 +++++++--------- scripts/coverage_report.rb | 88 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 104 insertions(+), 20 deletions(-) create mode 100755 scripts/coverage_report.rb diff --git a/.simplecov b/.simplecov index 25521e7f..31657578 100644 --- a/.simplecov +++ b/.simplecov @@ -2,16 +2,9 @@ # see https://github.com/colszowka/simplecov/blob/master/lib/simplecov/defaults.rb # vim: set ft=ruby -## DEFINE VARIABLES -@minimum_coverage = 100.0 ENV['FULL_BUILD'] ||= ENV['CI'] -@running_ci = !!(ENV['FULL_BUILD'] =~ /\Atrue\z/i) -@generate_report = @running_ci || !!(ENV['COVERAGE'] =~ /\Atrue\z/i) -@output = STDOUT -# rubocop:enable Style/DoubleNegation ## CONFIGURE SIMPLECOV - SimpleCov.profiles.define 'app' do coverage_dir 'coverage' load_profile 'test_frameworks' @@ -35,18 +28,21 @@ SimpleCov.profiles.define 'app' do add_filter '/.bundle/' end -if @generate_report - SimpleCov.start 'app' - if @running_ci - require 'codeclimate-test-reporter' - @output.puts '[COVERAGE] Running with SimpleCov Simple Formatter and CodeClimate Test Reporter' - formatters = [ - SimpleCov::Formatter::SimpleFormatter, - CodeClimate::TestReporter::Formatter - ] - else - @output.puts '[COVERAGE] Running with SimpleCov HTML Formatter' - formatters = [SimpleCov::Formatter::HTMLFormatter] +generate_report = !!(ENV['COVERAGE'] =~ /\Atrue\z/i) +running_ci = !!(ENV['FULL_BUILD'] =~ /\Atrue\z/i) +generate_result = running_ci || generate_report +require_relative 'scripts/coverage_report' +reporter = CoverageReport.new +if generate_report + reporter.configure_to_generate_report! + SimpleCov.at_exit do + reporter.generate_report! end - SimpleCov.formatters = formatters end +if generate_result + # only start when generating a result + SimpleCov.start 'app' + STDERR.puts '[COVERAGE] Running' + reporter.configure_to_generate_result! +end +SimpleCov.formatters = reporter.formatters diff --git a/scripts/coverage_report.rb b/scripts/coverage_report.rb new file mode 100755 index 00000000..287e6d51 --- /dev/null +++ b/scripts/coverage_report.rb @@ -0,0 +1,88 @@ +#!/usr/bin/env ruby + +require 'json' +class CoverageReport + attr_reader :formatters + + def initialize + @formatters = [] + end + + def configure_to_generate_result! + SimpleCov.configure do + # use_merging true + minimum_coverage 0.0 # disable + maximum_coverage_drop 100.0 # disable + end + SimpleCov.at_exit do + STDERR.puts "[COVERAGE] creating #{File.join(SimpleCov.coverage_dir, '.resultset.json')}" + SimpleCov.result.format! + end + end + + def configure_to_generate_report! + @minimum_coverage = ENV.fetch('COVERAGE_MINIMUM') { 100.0 }.to_f.round(2) + SimpleCov.configure do + minimum_coverage @minimum_coverage + # minimum_coverage_by_file 60 + # maximum_coverage_drop 1 + refuse_coverage_drop + end + + @formatters = [SimpleCov::Formatter::HTMLFormatter] + end + + def generate_report! + report_dir = SimpleCov.coverage_dir + file = File.join(report_dir, '.resultset.json') + if File.exist?(file) + json = JSON.parse(File.read(file)) + result = SimpleCov::Result.from_hash(json) + results = [result] + merged_result = SimpleCov::ResultMerger.merge_results(*results) + merged_result.format! + STDERR.puts "[COVERAGE] merged #{file}; processing..." + process_result(merged_result) + else + abort "No files found to report: #{Dir.glob(report_dir)}" + end + end + + # https://github.com/colszowka/simplecov/blob/v0.14.1/lib/simplecov/defaults.rb#L71-L98 + def process_result(result) + @exit_status = SimpleCov::ExitCodes::SUCCESS + covered_percent = result.covered_percent.round(2) + covered_percentages = result.covered_percentages.map { |p| p.round(2) } + + if @exit_status == SimpleCov::ExitCodes::SUCCESS # No other errors + if covered_percent < SimpleCov.minimum_coverage # rubocop:disable Metrics/BlockNesting + $stderr.printf("Coverage (%.2f%%) is below the expected minimum coverage (%.2f%%).\n", covered_percent, SimpleCov.minimum_coverage) + @exit_status = SimpleCov::ExitCodes::MINIMUM_COVERAGE + elsif covered_percentages.any? { |p| p < SimpleCov.minimum_coverage_by_file } # rubocop:disable Metrics/BlockNesting + $stderr.printf("File (%s) is only (%.2f%%) covered. This is below the expected minimum coverage per file of (%.2f%%).\n", result.least_covered_file, covered_percentages.min, SimpleCov.minimum_coverage_by_file) + @exit_status = SimpleCov::ExitCodes::MINIMUM_COVERAGE + elsif (last_run = SimpleCov::LastRun.read) # rubocop:disable Metrics/BlockNesting + coverage_diff = last_run["result"]["covered_percent"] - covered_percent + if coverage_diff > SimpleCov.maximum_coverage_drop # rubocop:disable Metrics/BlockNesting + $stderr.printf("Coverage has dropped by %.2f%% since the last time (maximum allowed: %.2f%%).\n", coverage_diff, SimpleCov.maximum_coverage_drop) + @exit_status = SimpleCov::ExitCodes::MAXIMUM_COVERAGE_DROP + end + end + end + + # Don't overwrite last_run file if refuse_coverage_drop option is enabled and the coverage has dropped + unless @exit_status == SimpleCov::ExitCodes::MAXIMUM_COVERAGE_DROP + SimpleCov::LastRun.write(:result => {:covered_percent => covered_percent}) + end + + # Force exit with stored status (see github issue #5) + # unless it's nil or 0 (see github issue #281) + Kernel.exit @exit_status if @exit_status && @exit_status > 0 + end +end +if __FILE__ == $0 + require 'simplecov' + reporter = CoverageReport.new + reporter.configure_to_generate_report! + reporter.generate_report! +end