Skip to content
Merged
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: 5 additions & 3 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,10 @@ jobs:
- '3.4'
- '4.0'
- ruby-head
- jruby
- jruby-9.4
- jruby-10.0
- jruby-head
- truffleruby

steps:
- uses: actions/checkout@v6
Expand All @@ -30,8 +32,8 @@ jobs:
with:
ruby-version: ${{ matrix.ruby-version }}
bundler-cache: true
continue-on-error: ${{ (matrix.ruby-version == 'ruby-head') || (matrix.ruby-version == 'jruby-head') }}
continue-on-error: ${{ (matrix.ruby-version == 'ruby-head') || (matrix.ruby-version == 'jruby-head') || (matrix.ruby-version == 'truffleruby') }}

- run: |
bundle exec rake
continue-on-error: ${{ (matrix.ruby-version == 'ruby-head') || (matrix.ruby-version == 'jruby-head') }}
continue-on-error: ${{ (matrix.ruby-version == 'ruby-head') || (matrix.ruby-version == 'jruby-head') || (matrix.ruby-version == 'truffleruby') }}
13 changes: 13 additions & 0 deletions .mutant.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
usage: opensource
integration:
name: minitest
includes:
- lib
- test
requires:
- simplecov
- simplecov-html
matcher:
subjects:
- SimpleCov::Formatter::HTMLFormatter#coverage_css_class
- SimpleCov::Formatter::HTMLFormatter#strength_css_class
4 changes: 4 additions & 0 deletions .rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ Metrics/BlockNesting:
Max: 2

Metrics/ClassLength:
Max: 110
CountAsOne:
- array
- hash
Expand Down Expand Up @@ -59,3 +60,6 @@ Style/TrailingCommaInHashLiteral:

Style/TrailingCommaInArrayLiteral:
EnforcedStyleForMultiline: "comma"

Gemspec/RequireMFA:
Enabled: false
4 changes: 2 additions & 2 deletions Gemfile.lock
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
GIT
remote: https://github.com/simplecov-ruby/simplecov
revision: 6bc01182d366be51cbe3a590bede1bd41ac71db8
revision: 3d8d723874ff14b06f30dee5d1453f9acc92fdf9
specs:
simplecov (0.22.0)
docile (~> 1.1)
simplecov-html (~> 0.11)
simplecov-html (~> 0.12)
simplecov_json_formatter (~> 0.1)

PATH
Expand Down
2 changes: 1 addition & 1 deletion assets/stylesheets/application.css
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
//= require ./reset.css
//= require_directory ./plugins/
//= require ./screen.css
//= require ./screen.css
5 changes: 5 additions & 0 deletions assets/stylesheets/screen.css
Original file line number Diff line number Diff line change
Expand Up @@ -488,3 +488,8 @@ thead th {
#dark-mode-toggle:hover {
opacity: 0.8;
}

.t-missed-method-summary ul {
margin: 0;
padding-left: 2em;
}
42 changes: 27 additions & 15 deletions lib/simplecov-html.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ class HTMLFormatter
}.freeze

def initialize
@branchable_result = SimpleCov.branch_coverage?
@branch_coverage = SimpleCov.branch_coverage?
@method_coverage = SimpleCov.method_coverage?
@templates = {}
@inline_assets = !ENV["SIMPLECOV_INLINE_ASSETS"].nil?
@public_assets_dir = File.join(File.dirname(__FILE__), "../public/")
Expand All @@ -44,25 +45,32 @@ def format(result)

private

def branchable_result?
def branch_coverage?
# cached in initialize because we truly look it up a whole bunch of times
# and it's easier to cache here then in SimpleCov because there we might
# still enable/disable branch coverage criterion
@branchable_result
@branch_coverage
end

def method_coverage?
# cached in initialize because we truly look it up a whole bunch of times
# and it's easier to cache here then in SimpleCov because there we might
# still enable/disable branch coverage criterion
@method_coverage
end

def line_status?(source_file, line)
branchable_result? && source_file.line_with_missed_branch?(line.number) ? "missed-branch" : line.status
branch_coverage? && source_file.line_with_missed_branch?(line.number) ? "missed-branch" : line.status
end

def output_message(result)
output = "Coverage report generated for #{result.command_name} to #{output_path}."
output += "\nLine Coverage: #{result.covered_percent.floor(2)}% (#{result.covered_lines} / #{result.total_lines})"
parts = []
parts << "Coverage report generated for #{result.command_name} to #{output_path}"
parts << "Line coverage: #{render_stats(result, :line)}"
parts << "Branch coverage: #{render_stats(result, :branch)}" if branch_coverage?
parts << "Method coverage: #{render_stats(result, :method)}" if method_coverage?

if branchable_result?
output += "\nBranch Coverage: #{result.coverage_statistics[:branch].percent.floor(2)}% (#{result.covered_branches} / #{result.total_branches})"
end
output
parts.join("\n")
end

# Returns the an erb instance for the template of given name
Expand All @@ -86,6 +94,10 @@ def assets_path(name)
File.join("./assets", SimpleCov::Formatter::HTMLFormatter::VERSION, name)
end

def to_id(value)
value.gsub(/^[^a-zA-Z]+/, "").gsub(/[^a-zA-Z0-9\-_]/, "")
end

def asset_inline(name)
path = File.join(@public_assets_dir, name)
# Equivalent to `Base64.strict_encode64(File.read(path))` but without depending on Base64
Expand All @@ -102,11 +114,6 @@ def formatted_source_file(source_file)

# Returns a table containing the given source files
def formatted_file_list(title, source_files)
title_id = title.gsub(/^[^a-zA-Z]+/, "").gsub(/[^a-zA-Z0-9\-_]/, "")
# Silence a warning by using the following variable to assign to itself:
# "warning: possibly useless use of a variable in void context"
# The variable is used by ERB via binding.
title_id = title_id # rubocop:disable Lint/SelfAssignment
template("file_list").result(binding)
end

Expand Down Expand Up @@ -150,6 +157,11 @@ def shortened_filename(source_file)
def link_to_source_file(source_file)
%(<a href="##{id source_file}" class="src_link" title="#{shortened_filename source_file}">#{shortened_filename source_file}</a>)
end

def render_stats(result, criterion)
stats = result.coverage_statistics.fetch(criterion)
Kernel.format("%<covered>d / %<total>d (%<percent>.2f%%)", covered: stats.covered, total: stats.total, percent: stats.percent)
end
end
end
end
Expand Down
2 changes: 1 addition & 1 deletion public/application.css

Large diffs are not rendered by default.

50 changes: 50 additions & 0 deletions test/test_mutation.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# frozen_string_literal: true

require "helper"

# Focused tests for mutation testing — exercises private methods directly.
# Uses cover() to explicitly tell mutant which methods each test covers.
class TestMutation < Minitest::Test
cover "SimpleCov::Formatter::HTMLFormatter#coverage_css_class" if respond_to?(:cover)

def test_coverage_css_class_green
assert_equal "green", formatter.send(:coverage_css_class, 100)
assert_equal "green", formatter.send(:coverage_css_class, 91)
assert_equal "green", formatter.send(:coverage_css_class, 90.01)
end

def test_coverage_css_class_yellow
assert_equal "yellow", formatter.send(:coverage_css_class, 90)
assert_equal "yellow", formatter.send(:coverage_css_class, 81)
assert_equal "yellow", formatter.send(:coverage_css_class, 80.01)
end

def test_coverage_css_class_red
assert_equal "red", formatter.send(:coverage_css_class, 80)
assert_equal "red", formatter.send(:coverage_css_class, 50)
assert_equal "red", formatter.send(:coverage_css_class, 0)
end

cover "SimpleCov::Formatter::HTMLFormatter#strength_css_class" if respond_to?(:cover)

def test_strength_css_class_green
assert_equal "green", formatter.send(:strength_css_class, 2)
assert_equal "green", formatter.send(:strength_css_class, 1.01)
end

def test_strength_css_class_yellow
assert_equal "yellow", formatter.send(:strength_css_class, 1)
assert_equal "yellow", formatter.send(:strength_css_class, 1.0)
end

def test_strength_css_class_red
assert_equal "red", formatter.send(:strength_css_class, 0.99)
assert_equal "red", formatter.send(:strength_css_class, 0)
end

private

def formatter
@formatter ||= SimpleCov::Formatter::HTMLFormatter.new
end
end
9 changes: 9 additions & 0 deletions test/test_simple_cov-html.rb
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,15 @@ def test_output
assert_branch_coverages(html_doc) if RUBY_ENGINE != "jruby"
end

def test_output_with_method_coverage
return unless SimpleCov.method_coverage_supported?

SimpleCov.enable_coverage(:method)
html_doc = format_results("sample.rb" => CoverageFixtures::SAMPLE_RB)

assert html_doc.at_css("div#AllFiles div.t-method-summary")
end

def test_output_without_branch_coverage
SimpleCov.clear_coverage_criteria
html_doc = format_results("sample.rb" => CoverageFixtures::SAMPLE_RB)
Expand Down
30 changes: 25 additions & 5 deletions views/file_list.erb
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<div class="file_list_container" id="<%= title_id %>">
<div class="file_list_container" id="<%= to_id(title) %>">
<h2>
<span class="group_name"><%= title %></span>
(<span class="covered_percent">
Expand All @@ -13,7 +13,7 @@
)
</h2>

<a name="<%= title_id %>"></a>
<a name="<%= to_id(title) %>"></a>

<div>
<b><%= source_files.length %></b> files in total.
Expand All @@ -25,13 +25,21 @@
<span class="red"><b><%= source_files.missed_lines %></b> lines missed. </span>
(<%= covered_percent(source_files.covered_percent) %>)
</div>
<%- if branchable_result? -%>
<%- if branch_coverage? -%>
<div class="t-branch-summary">
<span><b><%= source_files.total_branches %></b> total branches, </span>
<span class="green"><b><%= source_files.covered_branches %></b> branches covered</span> and
<span class="red"><b><%= source_files.missed_branches %></b> branches missed.</span>
(<%= covered_percent(source_files.branch_covered_percent) %>)
</div>
<%- end -%>
<%- if method_coverage? -%>
<div class="t-method-summary">
<span><b><%= source_files.total_methods %></b> total methods, </span>
<span class="green"><b><%= source_files.covered_methods %></b> methods covered</span> and
<span class="red"><b><%= source_files.missed_methods %></b> methods missed.</span>
(<%= covered_percent(source_files.method_covered_percent) %>)
</div>
<%- end -%>
<div class="file_list--responsive">
<table class="file_list">
Expand All @@ -44,11 +52,17 @@
<th class="cell--number">Lines covered</th>
<th class="cell--number">Lines missed</th>
<th class="cell--number">Avg. Hits / Line</th>
<%- if branchable_result? -%>
<%- if branch_coverage? -%>
<th class="cell--number">Branch Coverage</th>
<th class="cell--number">Branches</th>
<th class="cell--number">Covered branches</th>
<th class="cell--number">Missed branches </th>
<%- end -%>
<%- if method_coverage? -%>
<th class="cell--number">Method Coverage</th>
<th class="cell--number">Methods</th>
<th class="cell--number">Covered methods</th>
<th class="cell--number">Missed methods </th>
<%- end -%>
</tr>
</thead>
Expand All @@ -62,11 +76,17 @@
<td class="cell--number"><%= source_file.covered_lines.count %></td>
<td class="cell--number"><%= source_file.missed_lines.count %></td>
<td class="cell--number"><%= sprintf("%.2f", source_file.covered_strength) %></td>
<%- if branchable_result? -%>
<%- if branch_coverage? -%>
<td class="strong cell--number t-file__branch-coverage"><%= covered_percent(source_file.branches_coverage_percent) %></td>
<td class="cell--number"><%= source_file.total_branches.count %></td>
<td class="cell--number"><%= source_file.covered_branches.count %></td>
<td class="cell--number"><%= source_file.missed_branches.count %></td>
<%- end -%>
<%- if method_coverage? -%>
<td class="strong cell--number t-file__method-coverage"><%= covered_percent(source_file.methods_coverage_percent) %></td>
<td class="cell--number"><%= source_file.methods.count %></td>
<td class="cell--number"><%= source_file.covered_methods.count %></td>
<td class="cell--number"><%= source_file.missed_methods.count %></td>
<%- end -%>
</tr>
<%- end -%>
Expand Down
2 changes: 1 addition & 1 deletion views/layout.erb
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
</script>
</head>

<body<%= ' data-branch-coverage=true' if branchable_result? %>>
<body<%= ' data-branch-coverage=true' if branch_coverage? %>>
<button id="dark-mode-toggle"></button>
<div id="loading">
<img src="<%= assets_path('loading.gif') %>" alt="loading"/>
Expand Down
29 changes: 26 additions & 3 deletions views/source_file.erb
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,46 @@
<%= covered_percent(source_file.covered_percent) %>
lines covered
</h4>
<%- if branchable_result? -%>
<%- if branch_coverage? -%>
<h4>
<%= covered_percent(source_file.branches_coverage_percent) %>
branches covered
</h4>
<%- end -%>
<%- if method_coverage? -%>
<h4>
<%= covered_percent(source_file.methods_coverage_percent) %>
methods covered
</h4>
<%- end -%>
<div class="t-line-summary">
<b><%= source_file.lines_of_code %></b> relevant lines.
<span class="green"><b><%= source_file.covered_lines.count %></b> lines covered</span> and
<span class="red"><b><%= source_file.missed_lines.count %></b> lines missed.</span>
</div>
<%- if branchable_result? -%>
<%- if branch_coverage? -%>
<div class="t-branch-summary">
<span><b><%= source_file.total_branches.count %></b> total branches, </span>
<span class="green"><b><%= source_file.covered_branches.count %></b> branches covered</span> and
<span class="red"><b><%= source_file.missed_branches.count %></b> branches missed.</span>
</div>
<%- end -%>
<%- if method_coverage? -%>
<div class="t-method-summary">
<span><b><%= source_file.methods.count %></b> total methods, </span>
<span class="green"><b><%= source_file.covered_methods.count %></b> methods covered</span> and
<span class="red"><b><%= source_file.missed_methods.count %></b> methods missed.</span>
</div>
<%- end -%>
<%- if method_coverage? && source_file.missed_methods.any? -%>
<div class="t-missed-method-summary">
<span>Missed methods:</span>
<ul>
<%- source_file.missed_methods.each do |missed_method| -%>
<li><tt><%= ERB::Util.html_escape(missed_method.to_s) %></tt></li>
<%- end -%>
</ul>
</div>
<%- end -%>
</div>
<pre>
Expand All @@ -33,7 +56,7 @@
<%- elsif line.skipped? -%>
<span class="hits">skipped</span>
<%- end -%>
<%- if branchable_result? -%>
<%- if branch_coverage? -%>
<%- source_file.branches_for_line(line.number).each do |branch_type, hit_count| -%>
<span class="hits" title="<%= branch_type%> branch hit <%= hit_count %> times"><%= branch_type %>: <%= hit_count %></span>
<%- end -%>
Expand Down