-
Notifications
You must be signed in to change notification settings - Fork 554
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
Implement branch coverage support for exit status modifiers #934
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,7 +7,7 @@ Feature: | |
Background: | ||
Given I'm working on the project "faked_project" | ||
|
||
Scenario: maximum_coverage_drop configured cam cause spec failure | ||
Scenario: maximum_coverage_drop configured can cause spec failure | ||
Given SimpleCov for Test/Unit is configured with: | ||
""" | ||
require 'simplecov' | ||
|
@@ -34,13 +34,13 @@ Feature: | |
|
||
When I run `bundle exec rake test` | ||
Then the exit status should not be 0 | ||
And the output should contain "Coverage has dropped by 3.31% since the last time (maximum allowed: 3.14%)." | ||
And the output should contain "Line coverage has dropped by 3.31% since the last time (maximum allowed: 3.14%)." | ||
And a file named "coverage/.last_run.json" should exist | ||
And the file "coverage/.last_run.json" should contain: | ||
""" | ||
{ | ||
"result": { | ||
"covered_percent": 88.09 | ||
"line": 88.09 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this is backwards incompatible, but I think it's fine. We should just mention it in the changelog. People might want to update their respective files to keep working after the release. One thing though, we should write a test to make sure that we don't raise an error if an "old style" file is present. That'd be a catastrophic upgrade experience for folks if simplecov now failed all the time. We could call it "covered_percent_branch" for branch coverage but it's not desirable long term, hence I'm fine with the incompatibility. |
||
} | ||
} | ||
""" | ||
|
@@ -61,7 +61,7 @@ Feature: | |
""" | ||
{ | ||
"result": { | ||
"covered_percent": 88.09 | ||
"line": 88.09 | ||
} | ||
} | ||
""" | ||
|
@@ -84,7 +84,7 @@ Feature: | |
""" | ||
{ | ||
"result": { | ||
"covered_percent": 84.78 | ||
"line": 84.78 | ||
} | ||
} | ||
""" | ||
|
@@ -110,6 +110,86 @@ Feature: | |
end | ||
""" | ||
|
||
And a file named "spec/failing_spec.rb" with: | ||
""" | ||
require "spec_helper" | ||
describe FakedProject do | ||
it "fails" do | ||
expect(false).to eq(true) | ||
end | ||
end | ||
""" | ||
And the file named "coverage/.last_run.json" with: | ||
""" | ||
{ | ||
"result": { | ||
"line": 100.0 | ||
} | ||
} | ||
""" | ||
|
||
When I run `bundle exec rspec spec` | ||
Then the exit status should be 1 | ||
And a file named "coverage/.last_run.json" should exist | ||
And the file "coverage/.last_run.json" should contain: | ||
""" | ||
{ | ||
"result": { | ||
"line": 100.0 | ||
} | ||
} | ||
""" | ||
Scenario: When the previous last_run file has covered_percent | ||
Given SimpleCov for Test/Unit is configured with: | ||
""" | ||
require 'simplecov' | ||
SimpleCov.start do | ||
add_filter 'test.rb' | ||
end | ||
""" | ||
And the file "coverage/.last_run.json" with: | ||
""" | ||
{ | ||
"result": { | ||
"covered_percent": 88.09 | ||
} | ||
} | ||
""" | ||
|
||
When I run `bundle exec rake test` | ||
Then the exit status should be 0 | ||
And a file named "coverage/.last_run.json" should exist | ||
And the file "coverage/.last_run.json" should contain: | ||
""" | ||
{ | ||
"result": { | ||
"line": 88.09 | ||
} | ||
} | ||
""" | ||
|
||
Scenario: When the previous last_run file has covered_percent does not update resultset | ||
Given SimpleCov for RSpec is configured with: | ||
""" | ||
require 'simplecov' | ||
SimpleCov.start do | ||
add_group 'Libs', 'lib/faked_project/' | ||
add_filter '/spec/' | ||
maximum_coverage_drop 0 | ||
end | ||
""" | ||
|
||
And a file named "lib/faked_project/missed.rb" with: | ||
""" | ||
class UncoveredSourceCode | ||
def foo | ||
never_reached | ||
rescue => err | ||
but no one cares about invalid ruby here | ||
end | ||
end | ||
""" | ||
|
||
And a file named "spec/failing_spec.rb" with: | ||
""" | ||
require "spec_helper" | ||
|
@@ -139,3 +219,50 @@ Feature: | |
} | ||
} | ||
""" | ||
|
||
@branch_coverage | ||
Scenario: Works together with branch coverage and line coverage, announcing both failures | ||
Given SimpleCov for Test/Unit is configured with: | ||
""" | ||
require 'simplecov' | ||
SimpleCov.start do | ||
add_filter 'test.rb' | ||
enable_coverage :branch | ||
maximum_coverage_drop line: 0, branch: 0 | ||
end | ||
""" | ||
And a file named "lib/faked_project/missed.rb" with: | ||
""" | ||
class UncoveredSourceCode | ||
def foo | ||
never_reached | ||
rescue => err | ||
but no one cares about invalid ruby here | ||
end | ||
end | ||
""" | ||
|
||
And a file named "spec/failing_spec.rb" with: | ||
""" | ||
require "spec_helper" | ||
describe FakedProject do | ||
it "fails" do | ||
false ? true : expect(false).to eq(true) | ||
end | ||
end | ||
""" | ||
And the file named "coverage/.last_run.json" with: | ||
""" | ||
{ | ||
"result": { | ||
"line": 100.0, | ||
"branch": 100.0 | ||
} | ||
} | ||
""" | ||
|
||
When I run `bundle exec rake test` | ||
Then the exit status should not be 0 | ||
And the output should contain "Line coverage has dropped by 15.22% since the last time (maximum allowed: 0.00%)." | ||
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" |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -285,7 +285,10 @@ def wait_for_other_processes | |
# @api private | ||
# | ||
def write_last_run(result) | ||
SimpleCov::LastRun.write(result: {covered_percent: round_coverage(result.covered_percent)}) | ||
SimpleCov::LastRun.write(result: | ||
result.coverage_statistics.transform_values do |stats| | ||
round_coverage(stats.percent) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this is very elegant 👌 |
||
end) | ||
end | ||
|
||
# | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -287,20 +287,22 @@ def merge_timeout(seconds = nil) | |
# | ||
# Default is 0% (disabled) | ||
# | ||
|
||
# rubocop:disable Metrics/CyclomaticComplexity | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎉 |
||
def minimum_coverage(coverage = nil) | ||
return @minimum_coverage ||= {} unless coverage | ||
|
||
coverage = {DEFAULT_COVERAGE_CRITERION => coverage} if coverage.is_a?(Numeric) | ||
|
||
raise_on_invalid_coverage(coverage, "minimum_coverage") | ||
|
||
@minimum_coverage = coverage | ||
end | ||
|
||
def raise_on_invalid_coverage(coverage, coverage_setting) | ||
coverage.each_key { |criterion| raise_if_criterion_disabled(criterion) } | ||
coverage.each_value do |percent| | ||
minimum_possible_coverage_exceeded("minimum_coverage") if percent && percent > 100 | ||
minimum_possible_coverage_exceeded(coverage_setting) if percent && percent > 100 | ||
end | ||
|
||
@minimum_coverage = coverage | ||
end | ||
# rubocop:enable Metrics/CyclomaticComplexity | ||
|
||
# | ||
# Defines the maximum coverage drop at once allowed for the testsuite to pass. | ||
|
@@ -309,7 +311,13 @@ def minimum_coverage(coverage = nil) | |
# Default is 100% (disabled) | ||
# | ||
def maximum_coverage_drop(coverage_drop = nil) | ||
@maximum_coverage_drop ||= (coverage_drop || 100).to_f.round(2) | ||
return @maximum_coverage_drop ||= {} unless coverage_drop | ||
|
||
coverage_drop = {DEFAULT_COVERAGE_CRITERION => coverage_drop} if coverage_drop.is_a?(Numeric) | ||
|
||
raise_on_invalid_coverage(coverage_drop, "maximum_coverage_drop") | ||
|
||
@maximum_coverage_drop = coverage_drop | ||
end | ||
|
||
# | ||
|
@@ -320,16 +328,23 @@ def maximum_coverage_drop(coverage_drop = nil) | |
# Default is 0% (disabled) | ||
# | ||
def minimum_coverage_by_file(coverage = nil) | ||
minimum_possible_coverage_exceeded("minimum_coverage_by_file") if coverage && coverage > 100 | ||
@minimum_coverage_by_file ||= (coverage || 0).to_f.round(2) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. mental note that we're dropping the |
||
return @minimum_coverage_by_file ||= {} unless coverage | ||
|
||
coverage = {DEFAULT_COVERAGE_CRITERION => coverage} if coverage.is_a?(Numeric) | ||
|
||
raise_on_invalid_coverage(coverage, "minimum_coverage_by_file") | ||
|
||
@minimum_coverage_by_file = coverage | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. not for right now, but all these methods look very similar. I wonder if we could store them all just in one hash with their names as the keys and if that'd make these less repetitive. Feel free to ignore that though, we can tackle this in a follow up (meaning you if you want to, or me when I get to it) |
||
end | ||
|
||
# | ||
# Refuses any coverage drop. That is, coverage is only allowed to increase. | ||
# SimpleCov will return non-zero if the coverage decreases. | ||
# | ||
def refuse_coverage_drop | ||
maximum_coverage_drop 0 | ||
def refuse_coverage_drop(*criteria) | ||
criteria = coverage_criteria if criteria.empty? | ||
|
||
maximum_coverage_drop(criteria.map { |c| [c, 0] }.to_h) | ||
end | ||
|
||
# | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for updating the docs and with nice examples to boot! 👍