Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,11 @@ jobs:
timeout-minutes: 5 # _should_ finish in under a minute

- name: Report coverage
if: ${{ matrix.os == 'ubuntu-latest' && github.event_name != 'pull_request' &&
!startsWith(matrix.ruby, 'truffleruby') && !startsWith(matrix.ruby, 'jruby') }}
if: ${{ matrix.os == 'ubuntu-latest' &&
github.event_name != 'pull_request' &&
matrix.ruby != "ruby-head" &&
!startsWith(matrix.ruby, 'truffleruby') &&
!startsWith(matrix.ruby, 'jruby') }}
run: bundle exec rake coverage:report
timeout-minutes: 1 # _should_ finish in under a second
continue-on-error: true
2 changes: 0 additions & 2 deletions .simplecov
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
# frozen_string_literal: true

SimpleCov.configure do
formatter SimpleCov::Formatter::HTMLFormatter

enable_coverage :branch
enable_coverage :method
enable_coverage :eval
Expand Down
56 changes: 53 additions & 3 deletions Rakefile
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,63 @@ end

task :default => :test

desc "Output coverage data report, and error when threshholds aren't met"
desc "Output HTML coverage data report, and error when threshholds aren't met"
task "coverage:report" do
require "simplecov"
SimpleCov.collate "coverage/.resultset.json" do
formatter SimpleCov::Formatter::HTMLFormatter

coverage(:line) do
minimum 90
minimum_per_file 40
minimum 95

minimum_per_group 98, only: "Config"
minimum_per_group 97, only: "StringPrep"
minimum_per_group 97, only: "SASL"
minimum_per_group 95, only: "Data Types"
minimum_per_group 94, only: "Parser"
minimum_per_group 92, only: "Client"

minimum_per_file 90
minimum_per_file 87, only: "lib/net/imap/sasl/authenticators.rb"
minimum_per_file 86, only: "lib/net/imap/config/attr_type_coercion.rb"
minimum_per_file 84, only: "lib/net/imap/authenticators.rb"
minimum_per_file 80, only: "lib/net/imap/response_data.rb"
minimum_per_file 55, only: "lib/net/imap/search_result.rb"
end

coverage(:branch) do
minimum 80

minimum_per_group 92, only: "Data Types"
minimum_per_group 87, only: "Config"
minimum_per_group 83, only: "Client"
minimum_per_group 81, only: "Parser"
minimum_per_group 76, only: "SASL"
minimum_per_group 73, only: "StringPrep"

minimum_per_file 65
minimum_per_file 62, only: "lib/net/imap/sasl/scram_authenticator.rb"
minimum_per_file 50, only: "lib/net/imap/sasl/authenticators.rb"
minimum_per_file 50, only: "lib/net/imap/config/attr_accessors.rb"
end

coverage(:method) do
minimum 88

minimum_per_group 100, only: "Config"
minimum_per_group 92, only: "Data Types"
minimum_per_group 90, only: "StringPrep"
minimum_per_group 88, only: "Client"
minimum_per_group 83, only: "Parser"
minimum_per_group 82, only: "SASL"

minimum_per_file 66
minimum_per_file 64, only: "lib/net/imap/response_parser/parser_utils.rb"
minimum_per_file 57, only: "lib/net/imap/sasl/authenticators.rb"
minimum_per_file 50, only: "lib/net/imap/authenticators.rb"
minimum_per_file 50, only: "lib/net/imap/sasl/anonymous_authenticator.rb"
minimum_per_file 36, only: "lib/net/imap/sasl/protocol_adapters.rb"
minimum_per_file 20, only: "lib/net/imap/response_data.rb"
end
end
end
4 changes: 4 additions & 0 deletions test/lib/helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@

require "simplecov"

# run `rake coverage:report` for HTML/JSON formatter
require_relative "simplecov_markdown_table_formatter"
SimpleCov.formatter = SimpleCovMarkdownTableFormatter

SimpleCov.start do
command_name "Net::IMAP tests"
end
Expand Down
88 changes: 88 additions & 0 deletions test/lib/simplecov_markdown_table_formatter.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
# frozen_string_literal: true

require "simplecov"

# This formatter is much faster than the HTML formatter, and it prints much more
# useful info to stderr than any of the bundled formatters.
#
# TODO: extract this to its own gem? it needs some basic tests
# TODO: output to ENV["GITHUB_STEP_OUTPUT"]
class SimpleCovMarkdownTableFormatter < SimpleCov::Formatter::Base
def format(result)
return if @silent
output = "### Coverage report for #{result.command_name}\n"
output << format_markdown_table(result)
$stderr.puts output
output
end

def format_markdown_table(result)
groups = { "All files" => result }.merge(result.groups)
.transform_values(&:coverage_statistics)

name_size = groups.keys.map(&:length).max
criteria_sizes = groups
.values.map { _1.transform_values(&:total) } #=> Array[Hash[name, total]]
.reduce { _1.merge(_2) {|_, a, b| [a, b].max } } #=> Hash[name, max]
.transform_values { _1.to_s.length } #=> Hash[name, strlen]

rows = format_markdown_table_header(name_size, criteria_sizes)

rows.concat groups.map {|name, stats|
format_row_cells(name, stats, name_size, criteria_sizes)
}
.map { format_markdown_table_row _1 }

rows.join("\n")
end

private

def format_markdown_table_header(name_size, criteria_sizes)
heading_cells = format_markdown_table_row([
"Group".center(name_size),
*criteria_sizes.map {|name, size|
"#{name.to_s.capitalize} coverage".center(column_width(size))
}
])
border_line = format_row_cells("", name_size, criteria_sizes)
.then { format_markdown_table_row _1 }
.then { _1.tr(" ", "-") }
[heading_cells, border_line]
end

def format_markdown_table_row(cells) = "| #{cells.join(" | ")} |"

def format_row_cells(name, stats = {}, name_size, criteria_sizes)
name = name.ljust(name_size)
cols = criteria_sizes.map {|criterion, size|
format_stat_column(stats[criterion], size)
}
[name, *cols]
end

def column_width(size) = 10 + size * 2 + 1 # "000.00% = " + 2*size + "/"

def colorize_percent(percent)
formatted = "%6.2f%%" % [percent]
color = SimpleCov::Color.for_percent(percent)
SimpleCov::Color.colorize(formatted, color)
end

def format_stat_column(stat, size)
if stat
"%%s = %%%{size}s/%%%{size}s" % {size:} % stat_values(stat)
else
""
end
.ljust(column_width(size))
end

# converts SimpleCov::CoverageStatistics to [String, String, String]
def stat_values(stat)
return ["0", "0", colorize_percent(0.0)] unless stat
percent = colorize_percent SimpleCov.round_coverage(stat.percent)
[percent, stat.covered, stat.total]
end

end