Skip to content

Commit

Permalink
Implement branch coverage support for exit status modifiers (#934)
Browse files Browse the repository at this point in the history
Implement branch coverage support for exit status modifiers
  • Loading branch information
jemmaissroff authored Dec 22, 2020
1 parent 1088149 commit 542cd72
Show file tree
Hide file tree
Showing 11 changed files with 310 additions and 74 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
Unreleased
==========

## Enhancements
* Can now define the minimum_coverage_by_file, maximum_coverage_drop and refuse_coverage_drop by branch as well as line coverage

0.20.0 (2020-11-29)
==========

Expand Down
18 changes: 12 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -787,30 +787,36 @@ to help ensure coverage is relatively consistent, rather than being skewed by pa

```ruby
SimpleCov.minimum_coverage_by_file 80
# same as above (the default is to check line coverage by file)
SimpleCov.minimum_coverage_by_file line: 80
# check for a minimum line coverage by file of 90% and minimum 80% branch coverage
SimpleCov.minimum_coverage_by_file line: 90, branch: 80
```

(not yet supported for branch coverage)

### Maximum coverage drop

You can define the maximum coverage drop percentage at once. SimpleCov will return non-zero if exceeded.

```ruby
SimpleCov.maximum_coverage_drop 5
# same as above (the default is to check line drop)
SimpleCov.maximum_coverage_drop line: 5
# check for a maximum line drop of 5% and maximum 10% branch drop
SimpleCov.maximum_coverage_drop line: 5, branch: 10
```

(not yet supported for branch coverage)

### Refuse dropping coverage

You can also entirely refuse dropping coverage between test runs:

```ruby
SimpleCov.refuse_coverage_drop
# same as above (the default is to only refuse line drop)
SimpleCov.refuse_coverage_drop :line
# refuse drop for line and branch
SimpleCov.refuse_coverage_drop :line, :branch
```

(not yet supported for branch coverage)

## Using your own formatter

You can use your own formatter with:
Expand Down
137 changes: 132 additions & 5 deletions features/maximum_coverage_drop.feature
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand All @@ -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
}
}
"""
Expand All @@ -61,7 +61,7 @@ Feature:
"""
{
"result": {
"covered_percent": 88.09
"line": 88.09
}
}
"""
Expand All @@ -84,7 +84,7 @@ Feature:
"""
{
"result": {
"covered_percent": 84.78
"line": 84.78
}
}
"""
Expand All @@ -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"
Expand Down Expand Up @@ -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"
10 changes: 5 additions & 5 deletions features/refuse_coverage_drop.feature
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ Feature:
"""
{
"result": {
"covered_percent": 88.09
"line": 88.09
}
}
"""
Expand All @@ -42,13 +42,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: 0.00%)."
And the output should contain "Line coverage has dropped by 3.31% since the last time (maximum allowed: 0.00%)."
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
}
}
"""
Expand All @@ -69,7 +69,7 @@ Feature:
"""
{
"result": {
"covered_percent": 88.09
"line": 88.09
}
}
"""
Expand All @@ -92,7 +92,7 @@ Feature:
"""
{
"result": {
"covered_percent": 84.78
"line": 84.78
}
}
"""
5 changes: 4 additions & 1 deletion lib/simplecov.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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)
end)
end

#
Expand Down
37 changes: 26 additions & 11 deletions lib/simplecov/configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -287,20 +287,22 @@ def merge_timeout(seconds = nil)
#
# Default is 0% (disabled)
#

# rubocop:disable Metrics/CyclomaticComplexity
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.
Expand All @@ -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

#
Expand All @@ -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)
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
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

#
Expand Down
Loading

0 comments on commit 542cd72

Please sign in to comment.