Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add method coverage support #987

Open
wants to merge 39 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
693af60
add some todos
tycooon Aug 22, 2020
2521b5c
add method coverage criterion
tycooon Aug 22, 2020
d68a977
add some temp stuff
tycooon Aug 22, 2020
1f40180
add basic method coverage support
tycooon Aug 22, 2020
d609d16
fix serialization
tycooon Aug 22, 2020
f00fdf2
some fixes and fix all specs
tycooon Sep 2, 2020
31b95e7
add methods combiner
tycooon Sep 2, 2020
fa3902a
update ci config
tycooon Sep 2, 2020
b929673
Merge remote-tracking branch 'upstream/main' into add-method-coverage…
tycooon Sep 2, 2020
a78b5ed
add some backwards compability to result serialization
tycooon Sep 3, 2020
b16b6e5
add some methods for html report
tycooon Sep 3, 2020
3ae4b18
Merge remote-tracking branch 'upstream/main' into add-method-coverage…
tycooon Sep 22, 2020
66e6e0b
revert simplecov-html version
tycooon Sep 22, 2020
d79cb80
fix rubocop issues
tycooon Sep 22, 2020
4904192
fix flacky specs
tycooon Sep 22, 2020
03d1180
Merge remote-tracking branch 'upstream/main' into add-method-coverage…
tycooon Oct 27, 2020
3eff8b7
minor refactor
tycooon Apr 23, 2021
efb3e20
Merge remote-tracking branch 'upstream/main' into add-method-coverage…
tycooon Apr 23, 2021
9fafd3c
fix results collation
tycooon Apr 23, 2021
6420688
update test app gemfile
tycooon Apr 23, 2021
ddccb3c
add specs
tycooon Apr 23, 2021
15c03b2
optimize result merger
tycooon Apr 24, 2021
9a8b324
add specs for method cov merging
tycooon Apr 24, 2021
dc754e4
add feature specs for method coverage
tycooon Apr 30, 2021
b5c29b4
fix indentation in features
tycooon Apr 30, 2021
bb5aad1
skip method coverage features if not supported
tycooon Apr 30, 2021
0501b73
fix build
tycooon Apr 30, 2021
ed40a9d
add SimpleCov::Utils
tycooon Nov 16, 2021
a9978b0
update bundler
tycooon Nov 16, 2021
5cdbe21
update nokogiri
tycooon Jan 7, 2023
b5fc6e9
Merge remote-tracking branch 'upstream/main' into add-method-coverage…
tycooon Jan 7, 2023
a07dd23
update gemfiles
tycooon Jan 7, 2023
bb0fd38
fix gh workflow
tycooon Jan 7, 2023
ed4fb86
fix spec
tycooon Jan 7, 2023
15ab790
add some todos
tycooon Jan 9, 2023
7dca89d
Merge remote-tracking branch 'upstream/main' into add-method-coverage…
tycooon Dec 25, 2023
c13f0b1
fix rubocop issue
tycooon Dec 25, 2023
037a493
revert bundler version
tycooon Dec 25, 2023
8ede952
update simplecov-html
tycooon Dec 25, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/stable.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ jobs:
env:
BUNDLE_WITHOUT: "benchmark"
JRUBY_OPTS: "--debug"
SIMPLECOV_HTML_MODE: "methods" # TODO: remove after simplecov-html release

steps:
- uses: actions/checkout@v4
Expand All @@ -21,7 +22,6 @@ jobs:
uses: ruby/setup-ruby@v1
with:
ruby-version: ${{ matrix.ruby }}
rubygems: latest
bundler-cache: true # 'bundle install' and cache

- name: Run tests
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ tmp
## PROJECT::SPECIFIC

.yardoc
.ruby-version
spec/fixtures/coverage
spec/fixtures/frameworks/coverage
spec/fixtures/eval_test/coverage
Expand Down
15 changes: 10 additions & 5 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,16 @@

source "https://rubygems.org"

# Uncomment this to use local copy of simplecov-html in development when checked out
# gem "simplecov-html", path: File.join(__dir__, "../simplecov-html")

# Uncomment this to use development version of html formatter from github
# gem "simplecov-html", github: "simplecov-ruby/simplecov-html"
case ENV["SIMPLECOV_HTML_MODE"]
when "local"
# Use local copy of simplecov-html in development when checked out
gem "simplecov-html", path: File.join(__dir__, "../simplecov-html")
when "github"
# Use development version of html formatter from github
gem "simplecov-html", github: "simplecov-ruby/simplecov-html"
when "methods" # TODO: remove after simplecov-html release
gem "simplecov-html", github: "umbrellio/simplecov-html", branch: "add-method-coverage-support"
end

gem "matrix"

Expand Down
9 changes: 8 additions & 1 deletion Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,13 @@ GIT
capybara (~> 3.13, < 4)
websocket-driver (>= 0.6.5)

GIT
remote: https://github.com/umbrellio/simplecov-html.git
revision: 54879bd1080865cf8013bcda12e0c03ac687d7a9
branch: add-method-coverage-support
specs:
simplecov-html (0.12.3)

PATH
remote: .
specs:
Expand Down Expand Up @@ -154,7 +161,6 @@ GEM
rubocop-ast (1.24.1)
parser (>= 3.1.1.0)
ruby-progressbar (1.11.0)
simplecov-html (0.12.3)
simplecov_json_formatter (0.1.3)
spoon (0.0.6)
ffi
Expand Down Expand Up @@ -197,6 +203,7 @@ DEPENDENCIES
rspec (~> 3.2)
rubocop
simplecov!
simplecov-html!
test-unit
webrick

Expand Down
2 changes: 1 addition & 1 deletion features/branch_coverage.feature
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ Feature:
And I should see a line coverage summary of 56/61
And I should see a branch coverage summary of 2/4
And I should see the source files:
| name | coverage | branch coverage |
| name | coverage | branch coverage |
| lib/faked_project.rb | 100.00 % | 100.00 % |
| lib/faked_project/some_class.rb | 80.00 % | 50.00 % |
| lib/faked_project/framework_specific.rb | 75.00 % | 100.00 % |
Expand Down
1 change: 0 additions & 1 deletion features/maximum_coverage_drop.feature
Original file line number Diff line number Diff line change
Expand Up @@ -314,7 +314,6 @@ Feature:

When I run `bundle exec rake test`
Then the exit status should not be 0
And the output should not contain "Line coverage"
And the output should contain "Branch coverage has dropped by 50.00% since the last time (maximum allowed: 0.00%)."
And the output should contain "SimpleCov failed with exit 3"

Expand Down
40 changes: 40 additions & 0 deletions features/method_coverage.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
@rspec @method_coverage
Feature:

Simply executing method coverage gives ok results.

Background:
Given I'm working on the project "faked_project"

Scenario:
Given SimpleCov for RSpec is configured with:
"""
require 'simplecov'

SimpleCov.start do
enable_coverage :method
end
"""
When I open the coverage report generated with `bundle exec rspec spec`
Then I should see the groups:
| name | coverage | files |
| All Files | 91.8% | 7 |
And I should see a line coverage summary of 56/61
And I should see a method coverage summary of 10/13
And I should see the source files:
| name | coverage | method coverage |
| lib/faked_project.rb | 100.00 % | 100.00 % |
| lib/faked_project/some_class.rb | 80.00 % | 75.00 % |
| lib/faked_project/framework_specific.rb | 75.00 % | 33.33 % |
| lib/faked_project/meta_magic.rb | 100.00 % | 100.00 % |
| spec/forking_spec.rb | 100.00 % | 100.00 % |
| spec/meta_magic_spec.rb | 100.00 % | 100.00 % |
| spec/some_class_spec.rb | 100.00 % | 100.00 % |

When I open the detailed view for "lib/faked_project/framework_specific.rb"
Then I should see a line coverage summary of 6/8 for the file
And I should see a method coverage summary of 1/3 for the file
And I should see missed methods list:
| name |
| #<Class:FrameworkSpecific>#test_unit |
| #<Class:FrameworkSpecific>#cucumber |
1 change: 0 additions & 1 deletion features/minimum_coverage.feature
Original file line number Diff line number Diff line change
Expand Up @@ -84,5 +84,4 @@ Feature:
When I run `bundle exec rake test`
Then the exit status should not be 0
And the output should contain "Branch coverage (50.00%) is below the expected minimum coverage (80.00%)."
And the output should not contain "Line coverage"
And the output should contain "SimpleCov failed with exit 2"
1 change: 0 additions & 1 deletion features/minimum_coverage_by_file.feature
Original file line number Diff line number Diff line change
Expand Up @@ -68,5 +68,4 @@ Feature:
When I run `bundle exec rake test`
Then the exit status should not be 0
And the output should contain "Branch coverage by file (50.00%) is below the expected minimum coverage (70.00%)."
And the output should not contain "Line coverage"
And the output should contain "SimpleCov failed with exit 2"
8 changes: 8 additions & 0 deletions features/step_definitions/html_steps.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
available_source_files = all(".t-file", visible: true, count: expected_files.count)

include_branch_coverage = table.column_names.include?("branch coverage")
include_method_coverage = table.column_names.include?("method coverage")

# Find all filenames and their coverage present in coverage report
files = available_source_files.map do |file_row|
Expand All @@ -35,6 +36,7 @@
}

coverage_data["branch coverage"] = file_row.find(".t-file__branch-coverage").text if include_branch_coverage
coverage_data["method coverage"] = file_row.find(".t-file__method-coverage").text if include_method_coverage

coverage_data
end
Expand Down Expand Up @@ -68,3 +70,9 @@
Then /^I should see coverage branch data like "(.+)"$/ do |text|
expect(find(".hits", visible: true, text: text)).to be_truthy
end

Then /^I should see missed methods list:$/ do |table|
expected_list = table.hashes.map { |x| x.fetch("name") }
list = all(".t-missed-method-summary li", visible: true).map(&:text)
expect(list).to eq(expected_list)
end
2 changes: 1 addition & 1 deletion features/step_definitions/simplecov_steps.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
steps %(
Given a file named "#{framework_dir}/simplecov_config.rb" with:
"""
#{config_body}
#{config_body.indent(6)}
"""
)
end
Expand Down
4 changes: 4 additions & 0 deletions features/support/env.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@
skip_this_scenario unless SimpleCov.branch_coverage_supported?
end

Before("@method_coverage") do
skip_this_scenario unless SimpleCov.method_coverage_supported?
end

Before("@rails6") do
# Rails 6 only supports Ruby 2.5+
skip_this_scenario if Gem::Version.new(RUBY_VERSION) < Gem::Version.new("2.5")
Expand Down
17 changes: 7 additions & 10 deletions lib/simplecov.rb
Original file line number Diff line number Diff line change
Expand Up @@ -285,18 +285,10 @@ def wait_for_other_processes
def write_last_run(result)
SimpleCov::LastRun.write(result:
result.coverage_statistics.transform_values do |stats|
round_coverage(stats.percent)
SimpleCov::Utils.round_coverage(stats.percent)
end)
end

#
# @api private
#
# Rounding down to be extra strict, see #679
def round_coverage(coverage)
coverage.floor(2)
end

private

def initial_setup(profile, &block)
Expand Down Expand Up @@ -358,7 +350,8 @@ def start_coverage_with_criteria

CRITERION_TO_RUBY_COVERAGE = {
branch: :branches,
line: :lines
line: :lines,
method: :methods
}.freeze
def lookup_corresponding_ruby_coverage_name(criterion)
CRITERION_TO_RUBY_COVERAGE.fetch(criterion)
Expand Down Expand Up @@ -447,6 +440,7 @@ def probably_running_parallel_tests?
require_relative "simplecov/profiles"
require_relative "simplecov/source_file/line"
require_relative "simplecov/source_file/branch"
require_relative "simplecov/source_file/method"
require_relative "simplecov/source_file"
require_relative "simplecov/file_list"
require_relative "simplecov/result"
Expand All @@ -455,16 +449,19 @@ def probably_running_parallel_tests?
require_relative "simplecov/last_run"
require_relative "simplecov/lines_classifier"
require_relative "simplecov/result_merger"
require_relative "simplecov/result_serialization"
require_relative "simplecov/command_guesser"
require_relative "simplecov/version"
require_relative "simplecov/result_adapter"
require_relative "simplecov/combine"
require_relative "simplecov/combine/branches_combiner"
require_relative "simplecov/combine/methods_combiner"
require_relative "simplecov/combine/files_combiner"
require_relative "simplecov/combine/lines_combiner"
require_relative "simplecov/combine/results_combiner"
require_relative "simplecov/useless_results_remover"
require_relative "simplecov/simulate_coverage"
require_relative "simplecov/utils"

# Load default config
require_relative "simplecov/defaults" unless ENV["SIMPLECOV_NO_DEFAULTS"]
2 changes: 1 addition & 1 deletion lib/simplecov/combine/branches_combiner.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ module BranchesCombiner
module_function

#
# Return merged branches or the existed brach if other is missing.
# Return merged branches or the existing branch if other is missing.
#
# Branches inside files are always same if they exist, the difference only in coverage count.
# Branch coverage report for any conditional case is built from hash, it's key is a condition and
Expand Down
16 changes: 13 additions & 3 deletions lib/simplecov/combine/files_combiner.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,19 @@ module FilesCombiner
#
# @return [Hash]
#
def combine(coverage_a, coverage_b)
combination = {"lines" => Combine.combine(LinesCombiner, coverage_a["lines"], coverage_b["lines"])}
combination["branches"] = Combine.combine(BranchesCombiner, coverage_a["branches"], coverage_b["branches"]) if SimpleCov.branch_coverage?
def combine(cov_a, cov_b)
combination = {}

combination[:lines] = Combine.combine(LinesCombiner, cov_a[:lines], cov_b[:lines])

if SimpleCov.branch_coverage? # rubocop:disable Style/IfUnlessModifier
combination[:branches] = Combine.combine(BranchesCombiner, cov_a[:branches], cov_b[:branches])
end

if SimpleCov.method_coverage? # rubocop:disable Style/IfUnlessModifier
combination[:methods] = Combine.combine(MethodsCombiner, cov_a[:methods], cov_b[:methods])
end

combination
end
end
Expand Down
31 changes: 31 additions & 0 deletions lib/simplecov/combine/methods_combiner.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# frozen_string_literal: true

module SimpleCov
module Combine
#
# Combine different method coverage results on single file.
#
# Should be called through `SimpleCov.combine`.
module MethodsCombiner
module_function

#
# Combine method coverage from 2 sources
#
# @return [Hash]
#
def combine(coverage_a, coverage_b)
result_coverage = {}

keys = (coverage_a.keys + coverage_b.keys).uniq

keys.each do |method_name|
result_coverage[method_name] =
coverage_a.fetch(method_name, 0) + coverage_b.fetch(method_name, 0)
end

result_coverage
end
end
end
end
1 change: 1 addition & 0 deletions lib/simplecov/combine/results_combiner.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ module ResultsCombiner
# ==> FileCombiner: collect result of next combine levels lines and branches.
# ===> LinesCombiner: combine lines results.
# ===> BranchesCombiner: combine branches results.
# ===> MethodsCombiner: combine methods results.
#
# @return [Hash]
#
Expand Down
22 changes: 12 additions & 10 deletions lib/simplecov/configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -377,7 +377,7 @@ def add_group(group_name, filter_argument = nil, &filter_proc)
groups[group_name] = parse_filter(filter_argument, &filter_proc)
end

SUPPORTED_COVERAGE_CRITERIA = %i[line branch].freeze
SUPPORTED_COVERAGE_CRITERIA = %i[line branch method].freeze
DEFAULT_COVERAGE_CRITERION = :line
#
# Define which coverage criterion should be evaluated.
Expand Down Expand Up @@ -429,9 +429,12 @@ def branch_coverage?
branch_coverage_supported? && coverage_criterion_enabled?(:branch)
end

def method_coverage?
method_coverage_supported? && coverage_criterion_enabled?(:method)
end

def coverage_start_arguments_supported?
# safe to cache as within one process this value should never
# change
# safe to cache as within one process this value should never change
return @coverage_start_arguments_supported if defined?(@coverage_start_arguments_supported)

@coverage_start_arguments_supported = begin
Expand All @@ -444,6 +447,8 @@ def branch_coverage_supported?
coverage_start_arguments_supported? && RUBY_ENGINE != "jruby"
end

alias method_coverage_supported? branch_coverage_supported?

def coverage_for_eval_supported?
require "coverage"
defined?(Coverage.supported?) && Coverage.supported?(:eval)
Expand All @@ -465,19 +470,16 @@ def enable_coverage_for_eval

def raise_if_criterion_disabled(criterion)
raise_if_criterion_unsupported(criterion)
# rubocop:disable Style/IfUnlessModifier
unless coverage_criterion_enabled?(criterion)
raise "Coverage criterion #{criterion}, is disabled! Please enable it first through enable_coverage #{criterion} (if supported)"

unless coverage_criterion_enabled?(criterion) # rubocop:disable Style/IfUnlessModifier
raise "Coverage criterion #{criterion} is disabled! Please enable it first through enable_coverage #{criterion} (if supported)"
end
# rubocop:enable Style/IfUnlessModifier
end

def raise_if_criterion_unsupported(criterion)
# rubocop:disable Style/IfUnlessModifier
unless SUPPORTED_COVERAGE_CRITERIA.member?(criterion)
unless SUPPORTED_COVERAGE_CRITERIA.member?(criterion) # rubocop:disable Style/IfUnlessModifier
raise "Unsupported coverage criterion #{criterion}, supported values are #{SUPPORTED_COVERAGE_CRITERIA}"
end
# rubocop:enable Style/IfUnlessModifier
end

def minimum_possible_coverage_exceeded(coverage_option)
Expand Down