Skip to content

Commit

Permalink
branch coverage
Browse files Browse the repository at this point in the history
  • Loading branch information
grosser committed Feb 15, 2018
1 parent ead3a05 commit 88b238b
Show file tree
Hide file tree
Showing 4 changed files with 66 additions and 10 deletions.
2 changes: 2 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
bundler_args: ""
branches:
only: master
rvm:
- 2.0
- 2.1
Expand Down
5 changes: 3 additions & 2 deletions Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,15 @@ Actionable code coverage.
- Get actionable feedback on every successful test run
- Only 2-5% runtime overhead on small files compared to 50% for `SimpleCov`
- No more PRs with bad test coverage
- Branch coverage on ruby 2.5+

```Ruby
# Gemfile
gem 'single_cov', group: :test

# spec/spec_helper.rb ... load before loading rails / minitest / libraries
require 'single_cov'
SingleCov.setup :rspec
SingleCov.setup :rspec, branches: true

# spec/foobar_spec.rb ... add covered! call to every test file
require 'spec_helper'
Expand All @@ -36,7 +37,7 @@ lib/foobar.rb:23
Call setup before loading minitest.
```Ruby
SingleCov.setup :minitest
SingleCov.setup :minitest, branches: true
require 'minitest/autorun'
```
Expand Down
34 changes: 31 additions & 3 deletions lib/single_cov.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,17 @@ def covered!(file: nil, uncovered: 0)
end

def all_covered?(result)
result = result[:lines] || result

errors = COVERAGES.map do |file, expected_uncovered|
if coverage = result["#{root}/#{file}"]
uncovered_lines = coverage.each_with_index.map { |c, i| "#{file}:#{i+1}" if c == 0 }.compact
lines = (coverage.is_a?(Hash) ? coverage.fetch(:lines) : coverage)
uncovered_lines = line_coverage(file, lines)

if branches = (coverage.is_a?(Hash) && coverage[:branches])
uncovered_lines += branch_coverage(file, branches)
end

next if uncovered_lines.size == expected_uncovered
warn_about_bad_coverage(file, expected_uncovered, uncovered_lines)
else
Expand Down Expand Up @@ -58,10 +66,16 @@ def assert_tested(files: glob('{app,lib}/**/*.rb'), tests: default_tests, untest
end
end

def setup(framework, root: nil)
def setup(framework, root: nil, branches: false)
if defined?(SimpleCov)
raise "Load SimpleCov after SingleCov"
end
if branches && RUBY_VERSION < "2.5.0"
warn "Branch coverage needs ruby 2.5.0"
@branches = false
else
@branches = branches
end

@root = root if root

Expand Down Expand Up @@ -89,6 +103,16 @@ def disable

private

def line_coverage(file, coverage)
coverage.each_with_index.map { |c, i| "#{file}:#{i + 1}" if c == 0 }.compact
end

def branch_coverage(file, coverage)
coverage.each_value.flat_map do |branch_part|
branch_part.select { |_k, v| v == 0 }.map { |k, _| "#{file}:#{k[2]} branch #{k[2]}:#{k[3]+1}-#{k[4]}:#{k[5]+1}" }
end
end

def default_tests
glob("{test,spec}/**/*_{test,spec}.rb")
end
Expand All @@ -110,7 +134,11 @@ def coverage_results
# SimpleCov might start coverage again, but that does not hurt ...
def start_coverage_recording
require 'coverage'
Coverage.start
if @branches
Coverage.start(lines: true, branches: true)
else
Coverage.start
end
end

# not running rake or a whole folder
Expand Down
35 changes: 30 additions & 5 deletions specs/single_cov_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,15 @@
SingleCov.instance_variable_set(:@root, File.expand_path("../fixtures/minitest", __FILE__))

describe SingleCov do
def self.it_does_not_complain_when_everything_is_covered
it "does not complain when everything is covered" do
result = sh "ruby test/a_test.rb"
assert_tests_finished_normally(result)
expect(result).to_not include "uncovered"
end
end


it "has a VERSION" do
expect(SingleCov::VERSION).to match(/^[\.\da-z]+$/)
end
Expand All @@ -12,11 +21,7 @@

around { |test| Dir.chdir("specs/fixtures/minitest", &test) }

it "does not complain when everything is covered" do
result = sh "ruby test/a_test.rb"
assert_tests_finished_normally(result)
expect(result).to_not include "uncovered"
end
it_does_not_complain_when_everything_is_covered

it "is silent" do
result = sh "ruby test/a_test.rb"
Expand Down Expand Up @@ -177,6 +182,26 @@
end
end
end

describe "branch coverage" do
around { |t| change_file("test/a_test.rb", "root: root", "root: root, branches: true", &t) }

it_does_not_complain_when_everything_is_covered

describe "with branches" do
around { |t| change_file("lib/a.rb", "1", "2.times { |i| rand if i == 0 }", &t) }

it_does_not_complain_when_everything_is_covered

it "complains when coverage is missing" do
change_file("lib/a.rb", "i == 0", "i != i") do
result = sh "ruby test/a_test.rb", fail: true
expect(result).to include ".lib/a.rb new uncovered lines introduced (1 current vs 0 configured)"
expect(result).to include "lib/a.rb:3 branch 3:19-3:23"
end
end
end
end if RUBY_VERSION >= "2.5.0"
end

describe "rspec" do
Expand Down

0 comments on commit 88b238b

Please sign in to comment.