undercover 0.6.4 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2a3601cf9aa4d26cee9c04f12b44273f8a67e0e8584b020e4e9bb763e03bf24e
4
- data.tar.gz: 8198c30de2bc16734a1575b7aa0213c25ccafe120848dad9c24257d688b7bca9
3
+ metadata.gz: 8c8f43820b6be7cbae834f152ed56778f8fb3540e878a012ce0bb5274be7b0e3
4
+ data.tar.gz: '0798f165b6deef05078311ff04822bc4c1dc708e3830b569b5575be2d1f8a3fe'
5
5
  SHA512:
6
- metadata.gz: 6e6a60fad9a478c37895ae24035022a9acf86f789ce0d798194b6d13b82c7db52c9de6590a9a9d3b8a84d304f220a30ef2c8e5226bab6fa58cea96f09c3856ca
7
- data.tar.gz: fd1ca272d59b29debe708bd2f8df2bf867dccc60484416002dbf004d21ea83370e408e7703098f8d6e2ff0345d2234a7e662f94254a7dcc339f7370bae9f9885
6
+ metadata.gz: 922c43acac57c8e57df9c902b9739c4f084a090e6afc9b838868e80ce8c39ea5d5e087952c40e7446fceaa97313ee686aacfef1e8a62af8dd01879d665e5abda
7
+ data.tar.gz: 3d538e5526d124c9ffba53c53de75dd2e6009b0abf1de4a333ccd3e013da195b15811217d0e1a8b1b12bd7c34d47cac47c42dd6e4838cd82e3a571602c0e5966
@@ -18,25 +18,25 @@ jobs:
18
18
  run: |
19
19
  gem install bundler undercover --no-doc
20
20
  bundle install --jobs 4 --retry 3
21
- bundle exec rake
21
+ rake
22
22
  - name: undercover (local)
23
23
  run: |
24
24
  git fetch --update-head-ok origin master:master
25
- undercover --compare master
25
+ undercover --simplecov coverage/undercover_coverage.json --compare master
26
26
  - uses: actions/upload-artifact@v4
27
27
  with:
28
- name: undercover-${{ matrix.ruby }}.lcov
29
- path: coverage/lcov/undercover.lcov
28
+ name: undercover-${{ matrix.ruby }}-coverage
29
+ path: coverage/undercover_coverage.json
30
30
  coverage:
31
31
  runs-on: ubuntu-latest
32
32
  needs: build
33
33
  steps:
34
34
  - uses: actions/download-artifact@v4
35
35
  with:
36
- name: undercover-3.4.lcov
36
+ name: undercover-3.4-coverage
37
37
  - name: Upload coverage
38
38
  run: |
39
39
  ruby -e "$(curl -s https://undercover-ci.com/uploader.rb)" -- \
40
40
  --repo grodowski/undercover \
41
41
  --commit ${{ github.event.pull_request.head.sha || github.sha }} \
42
- --lcov /home/runner/work/undercover/undercover/undercover.lcov
42
+ --simplecov /home/runner/work/undercover/undercover/undercover_coverage.json
data/.gitignore CHANGED
@@ -10,3 +10,6 @@ Gemfile.lock
10
10
 
11
11
  # rspec failure tracking
12
12
  .rspec_status
13
+
14
+ # branch ignore fixture coverage metadata
15
+ spec/fixtures/branch_ignored_coverage/.*
data/.rubocop.yml CHANGED
@@ -25,10 +25,10 @@ Metrics/BlockLength:
25
25
  - spec/**/*
26
26
 
27
27
  Metrics/CyclomaticComplexity:
28
- Max: 10
28
+ Max: 12
29
29
 
30
30
  Metrics/PerceivedComplexity:
31
- Max: 10
31
+ Max: 12
32
32
 
33
33
  Style/HashEachMethods:
34
34
  Enabled: true
data/.tool-versions CHANGED
@@ -1 +1,2 @@
1
1
  ruby 3.4.2
2
+ nodejs v24.4.0
data/CHANGELOG.md CHANGED
@@ -6,6 +6,52 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ # [0.8.0] - 2025-08-28
10
+
11
+ ### Added
12
+ - Filter out files ignored by simplecov using `add_filter` ([#234](https://github.com/grodowski/undercover/pull/234))
13
+ - Add LF and LH parsing to LcovParser for simplecov-lcov 0.9 compatibility
14
+
15
+ ### Fixed
16
+ - Fix an `Undercover::Result` edge case causing errors with ignored branches on uningnored lines
17
+
18
+ # [0.7.4] - 2025-07-13
19
+
20
+ ### Fixed
21
+ - Fix `fnmatch` for `FilterSet` to support glob braces (sets) in `--include-files` and `--exclude-files`
22
+
23
+ # [0.7.3] - 2025-07-13
24
+
25
+ ### Fixed
26
+ - Improve Options#parse with glob braces support, strip quotes properly from .undercover config files
27
+ - Fix Result#coverage_f to support ignored branches
28
+ - Fix error parsing JSON coverage with branch coverage disabled ([#231](https://github.com/grodowski/undercover/issues/231))
29
+ - Fixed NoMethodError and Errno::ENOENT that were occurring when coverage report doesn't exist ([#232](https://github.com/grodowski/undercover/issues/232))
30
+
31
+ # [0.7.2] - 2025-07-07
32
+
33
+ ### Fixed
34
+ - Resolved errors when .lcov files doesn't exist using `--lcov` CLI flag and `guess_lcov_path`
35
+
36
+ # [0.7.0] - 2025-07-03
37
+
38
+ ### Added
39
+ - New native SimpleCov formatter that generates coverage data directly without requiring LCOV conversion ([#223](https://github.com/grodowski/undercover/pull/223))
40
+ - `--simplecov -s` CLI option to specify coverage JSON file path as an alternative to LCOV and future default ([#223](https://github.com/grodowski/undercover/pull/223))
41
+ - Improved path handling for projects running in nested subdirectories or monorepo setups ([#223](https://github.com/grodowski/undercover/pull/223))
42
+
43
+ ### Fixed
44
+ - `:nocov:` support to skip coverage analysis for specific code blocks works again after a regression in `0.6+` ([#223](https://github.com/grodowski/undercover/pull/223))
45
+
46
+ # [0.6.6] - 2025-07-01
47
+
48
+ - Bugfix in `max_warnings_limit` following ([#229](https://github.com/grodowski/undercover/pull/229))
49
+
50
+ # [0.6.5] - 2025-07-01
51
+
52
+ ### Fixed
53
+ - Improved performance for large PRs with lazy diff enumeration ([#229](https://github.com/grodowski/undercover/pull/229))
54
+
9
55
  # [0.6.4] - 2025-03-29
10
56
 
11
57
  ### Fixed
@@ -148,9 +194,18 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
148
194
  ### Added
149
195
  - First release of `undercover` 🎉
150
196
 
151
- [Unreleased]: https://github.com/grodowski/undercover/compare/v0.6.3...HEAD
152
- [0.6.3]:https://github.com/grodowski/undercover/compare/v0.6.3...v0.6.0
153
- [0.6.0]: https://github.com/grodowski/undercover/compare/v0.6.0...v0.5.0
197
+ [Unreleased]: https://github.com/grodowski/undercover/compare/v0.8.0...HEAD
198
+ [0.8.0]: https://github.com/grodowski/undercover/compare/v0.7.4...v0.8.0
199
+ [0.7.4]: https://github.com/grodowski/undercover/compare/v0.7.3...v0.7.4
200
+ [0.7.3]: https://github.com/grodowski/undercover/compare/v0.7.2...v0.7.3
201
+ [0.7.2]: https://github.com/grodowski/undercover/compare/v0.7.1...v0.7.2
202
+ [0.7.1]: https://github.com/grodowski/undercover/compare/v0.7.0...v0.7.1
203
+ [0.7.0]: https://github.com/grodowski/undercover/compare/v0.6.6...v0.7.0
204
+ [0.6.6]: https://github.com/grodowski/undercover/compare/v0.6.5...0.6.6
205
+ [0.6.5]: https://github.com/grodowski/undercover/compare/v0.6.4...0.6.5
206
+ [0.6.4]: https://github.com/grodowski/undercover/compare/v0.6.3...v0.6.4
207
+ [0.6.3]: https://github.com/grodowski/undercover/compare/v0.6.0...v0.6.3
208
+ [0.6.0]: https://github.com/grodowski/undercover/compare/v0.5.0...v0.6.0
154
209
  [0.5.0]: https://github.com/grodowski/undercover/compare/v0.4.7...v0.5.0
155
210
  [0.4.7]: https://github.com/grodowski/undercover/compare/v0.4.6...v0.4.7
156
211
  [0.4.6]: https://github.com/grodowski/undercover/compare/v0.4.5...v0.4.6
data/Gemfile CHANGED
@@ -10,7 +10,8 @@ gem 'pry'
10
10
  gem 'rake', '~> 13.0'
11
11
  gem 'rspec', '~> 3.0'
12
12
  gem 'rubocop'
13
+ gem 'ruby-lsp-rspec', require: false
13
14
  gem 'simplecov'
14
15
  gem 'simplecov-html'
15
- gem 'simplecov-lcov', '~> 0.8'
16
+ gem 'simplecov_json_formatter'
16
17
  gem 'timecop'
data/README.md CHANGED
@@ -9,7 +9,6 @@ Works with any Ruby CI pipeline as well as locally as a CLI.
9
9
 
10
10
 
11
11
  [![Build Status](https://github.com/grodowski/undercover/actions/workflows/ruby.yml/badge.svg)](https://github.com/grodowski/undercover/actions)
12
- [![Maintainability](https://api.codeclimate.com/v1/badges/b403feed68a18c072ec5/maintainability)](https://codeclimate.com/github/grodowski/undercover/maintainability)
13
12
  ![Downloads](https://img.shields.io/gem/dt/undercover)
14
13
 
15
14
  A sample output of `undercover` ran before a commit may look like this:
@@ -36,34 +35,56 @@ Or install it yourself as:
36
35
 
37
36
  $ gem install undercover
38
37
 
39
- ## Setting up required LCOV reporting
38
+ ## Setting up coverage reporting
40
39
 
41
- To make your specs or tests compatible with `undercover` by providing an LCOV report, please add `simplecov` and `simplecov-lcov` to your test setup.
40
+ To make your specs or tests compatible with `undercover`, please add `undercover` to your gemfile to use the undercover formatter the test helper.
42
41
 
43
42
  ```ruby
44
43
  # Gemfile
45
44
  group :test do
46
- gem 'simplecov'
47
- gem 'simplecov-lcov'
45
+ gem 'undercover'
48
46
  end
49
47
 
50
48
  # the very top of spec_helper.rb
51
49
  require 'simplecov'
52
- require 'simplecov-lcov'
53
- SimpleCov::Formatter::LcovFormatter.config.report_with_single_file = true
54
- SimpleCov.formatter = SimpleCov::Formatter::LcovFormatter
50
+ require 'undercover/simplecov_formatter'
51
+
52
+ # optional, the filename defaults to `coverage.json` and is automatically recognised by the gem
53
+ # SimpleCov::Formatter::Undercover.output_filename = 'my_project_coverage.json'
54
+ SimpleCov.formatter = SimpleCov::Formatter::Undercover
55
+
55
56
  SimpleCov.start do
56
57
  add_filter(/^\/spec\//) # For RSpec
57
58
  add_filter(/^\/test\//) # For Minitest
58
59
  enable_coverage(:branch) # Report branch coverage to trigger branch-level undercover warnings
59
60
  end
61
+ # ...
62
+ ```
60
63
 
61
- require 'undercover'
64
+ Then run your test suite once through to generate the initial coverage file before you can run the `undercover` command.
62
65
 
63
- # ...
66
+ ## Upgrading from pre-0.7.0
67
+
68
+ If you're upgrading from an older version of undercover that used LCOV, you can migrate to the new SimpleCov formatter:
69
+
70
+ 1. Add `gem 'undercover'` to your test group
71
+ 2. Replace the LCOV formatter setup with the new SimpleCov formatter
72
+
73
+ ```ruby
74
+ # Gemfile
75
+ group :test do
76
+ gem 'undercover'
77
+ end
78
+
79
+ # spec_helper.rb
80
+ require 'simplecov'
81
+ require 'undercover/simplecov_formatter'
82
+ SimpleCov.formatter = SimpleCov::Formatter::Undercover
64
83
  ```
65
84
 
66
- Then run your test suite once through to generate the initial `coverage/lcov/*.lcov` file before you can run the `undercover` command
85
+ 3. Update CLI usage: Use `--simplecov` flag instead of `--lcov`, or rely on auto-detection of `coverage/coverage.json`
86
+
87
+ Note: LCOV support will be deprecated in a future release, but remains fully functional for existing projects.
67
88
 
68
89
  ## Usage
69
90
 
@@ -80,11 +101,10 @@ undercover --compare origin/master
80
101
  ```
81
102
 
82
103
  Check out `docs/` for CI configuration examples:
83
- - [Travis CI](docs/travis.yml)
104
+ - [GitHub Actions](docs/actions.yml)
84
105
  - [CircleCI - simple](docs/circleci_config.yml)
85
106
  - [CircleCI - advanced](docs/circleci_advanced.yml)
86
107
  - [Semaphore](docs/semaphore.yml)
87
- - [Codeship](docs/codeship.md)
88
108
 
89
109
  Merging coverage results ([sample gist](https://gist.github.com/grodowski/9744ff91034dce8df20c2a8210409fb0)) is required for parallel tests before processing with `undercover`.
90
110
 
@@ -105,11 +125,13 @@ Options can be passed when running the command from the command line:
105
125
 
106
126
  ```sh
107
127
  Usage: undercover [options]
108
- -l, --lcov path LCOV report file path
128
+ -s, --simplecov path SimpleCov JSON report file
129
+ -l, --lcov path LCOV report file path (to be deprecated)
109
130
  -p, --path path Project directory
110
131
  -g, --git-dir dir Override `.git` with a custom directory
111
132
  -c, --compare ref Generate coverage warnings for all changes after `ref`
112
133
  -r, --ruby-syntax ver Ruby syntax version, one of: current, ruby18, ruby19, ruby20, ruby21, ruby22, ruby23, ruby24, ruby25, ruby26, ruby30, ruby31, ruby32, ruby33
134
+ -w, --max-warnings limit Maximum number of warnings to generate before stopping analysis. Useful as a performance improvement for large diffs.
113
135
  -f, --include-files globs Include files matching specified glob patterns (comma separated). Defaults to '*.rb,*.rake,*.ru,Rakefile'
114
136
  -x, --exclude-files globs Skip files matching specified glob patterns (comma separated). Empty by default.
115
137
  -h, --help Prints this help
data/docs/actions.yml ADDED
@@ -0,0 +1,19 @@
1
+ name: Tests & Undercover
2
+ on: [push, pull_request]
3
+ jobs:
4
+ build:
5
+ runs-on: ubuntu-latest
6
+ steps:
7
+ - uses: actions/checkout@v4
8
+ - name: Set up Ruby 3.4
9
+ uses: ruby/setup-ruby@v1
10
+ with:
11
+ ruby-version: 3.4
12
+ - name: Build and test
13
+ env:
14
+ RAILS_ENV: test
15
+ run: |
16
+ gem install bundler
17
+ bundle install --jobs 4 --retry 3
18
+ bundle exec rake test
19
+ undercover --simplecov coverage/coverage.json --compare origin/master
@@ -20,7 +20,7 @@ jobs:
20
20
  bundle exec rspec
21
21
  - run:
22
22
  name: Store coverage report
23
- command: mv coverage/lcov/project.lcov /tmp/coverage/
23
+ command: mv coverage/coverage.json /tmp/coverage/
24
24
  - persist_to_workspace:
25
25
  root: /tmp/coverage
26
26
  paths: .
@@ -33,13 +33,13 @@ jobs:
33
33
  steps:
34
34
  - checkout
35
35
  - attach_workspace:
36
- at: /tmp/coverage # gives access to project's LCOV report
36
+ at: /tmp/coverage # gives access to project's coverage report
37
37
  - run:
38
38
  name: Check coverage
39
39
  command: |
40
40
  sudo apt-get install cmake
41
41
  gem install undercover
42
- undercover --lcov /tmp/coverage/project.lcov \
42
+ undercover --simplecov /tmp/coverage/coverage.json \
43
43
  --compare origin/master
44
44
 
45
45
  workflows:
@@ -1,5 +1,5 @@
1
1
  # Simple CircleCI config.yml example.
2
- # See workflow_config.yml for a more advanced example,
2
+ # See circleci_advanced.yml for a more advanced example,
3
3
  # that includes sharing data between containers.
4
4
 
5
5
  version: 2
@@ -8,31 +8,12 @@ module Undercover
8
8
  class Changeset
9
9
  T_ZERO = Time.strptime('0', '%s').freeze
10
10
 
11
- extend Forwardable
12
- include Enumerable
13
-
14
- attr_reader :files
15
-
16
- def_delegators :files, :each, :<=>
17
-
18
- def initialize(dir, compare_base = nil)
11
+ def initialize(dir, compare_base = nil, filter_set = nil)
19
12
  @dir = dir
20
13
  @repo = Rugged::Repository.new(dir)
21
14
  @repo.workdir = Pathname.new(dir).dirname.to_s # TODO: can replace?
22
15
  @compare_base = compare_base
23
- @files = {}
24
- end
25
-
26
- def update
27
- full_diff.each_patch do |patch|
28
- filepath = patch.delta.new_file[:path]
29
- line_nums = patch.each_hunk.map do |hunk|
30
- # TODO: optimise this to use line ranges!
31
- hunk.lines.select(&:addition?).map(&:new_lineno)
32
- end.flatten
33
- @files[filepath] = line_nums if line_nums.any?
34
- end
35
- self
16
+ @filter_set = filter_set
36
17
  end
37
18
 
38
19
  def last_modified
@@ -46,29 +27,39 @@ module Undercover
46
27
  end
47
28
 
48
29
  def file_paths
49
- files.keys.sort
30
+ full_diff.deltas.map { |d| d.new_file[:path] }.sort
50
31
  end
51
32
 
52
33
  def each_changed_line
53
- files.each do |filepath, line_numbers|
54
- line_numbers.each { |ln| yield filepath, ln }
34
+ full_diff.each_patch do |patch|
35
+ filepath = patch.delta.new_file[:path]
36
+ next if filter_set && !filter_set.include?(filepath)
37
+
38
+ patch.each_hunk do |hunk|
39
+ hunk.lines.select(&:addition?).each do |line|
40
+ yield filepath, line.new_lineno
41
+ end
42
+ end
55
43
  end
56
44
  end
57
45
 
58
- # TODO: refactor to a standalone validator (depending on changeset AND lcov)
59
46
  def validate(lcov_report_path)
60
- return :no_changes if files.empty?
47
+ return :no_changes if full_diff.deltas.empty?
61
48
 
62
49
  :stale_coverage if last_modified > File.mtime(lcov_report_path)
63
50
  end
64
51
 
52
+ def filter_with(filter_set)
53
+ @filter_set = filter_set
54
+ end
55
+
65
56
  private
66
57
 
67
58
  # Diffs `head` or `head` + `compare_base` (if exists),
68
59
  # as it makes sense to run Undercover with the most recent file versions
69
60
  def full_diff
70
61
  base = compare_base_obj || head
71
- base.diff(repo.index).merge!(repo.diff_workdir(head))
62
+ @full_diff ||= base.diff(repo.index).merge!(repo.diff_workdir(head))
72
63
  end
73
64
 
74
65
  def compare_base_obj
@@ -83,6 +74,6 @@ module Undercover
83
74
  repo.head.target
84
75
  end
85
76
 
86
- attr_reader :repo, :compare_base
77
+ attr_reader :repo, :compare_base, :filter_set
87
78
  end
88
79
  end
@@ -5,9 +5,6 @@ require 'rainbow'
5
5
 
6
6
  module Undercover
7
7
  module CLI
8
- # TODO: Report calls >parser< for each file instead of
9
- # traversing the whole project at first!
10
-
11
8
  WARNINGS_TO_S = {
12
9
  stale_coverage: Rainbow('🚨 WARNING: Coverage data is older than your ' \
13
10
  'latest changes and results might be incomplete. ' \
@@ -21,10 +18,38 @@ module Undercover
21
18
  run_report(opts)
22
19
  end
23
20
 
24
- def self.run_report(opts)
25
- report = Undercover::Report.new(changeset(opts), opts).build
21
+ def self.run_report(opts) # rubocop:disable Metrics/AbcSize,Metrics/MethodLength
22
+ coverage_path = opts.simplecov_resultset || opts.lcov
23
+ return handle_missing_coverage_path(opts) if coverage_path.nil?
24
+ return handle_missing_file(coverage_path) unless File.exist?(coverage_path)
25
+
26
+ simplecov_adapter = if opts.simplecov_resultset
27
+ SimplecovResultAdapter.parse(File.open(opts.simplecov_resultset), opts)
28
+ else
29
+ # TODO: lcov will be deprecated end of 2025 and we'll be able to refactor harder
30
+ LcovParser.parse(File.open(opts.lcov), opts)
31
+ end
32
+
33
+ changeset_obj = changeset(opts)
34
+ report = Undercover::Report.new(changeset_obj, opts, simplecov_adapter).build
35
+ handle_report_validation(report, coverage_path)
36
+ end
37
+
38
+ def self.handle_missing_coverage_path(opts)
39
+ puts Rainbow('❌ ERROR: No coverage report found. Checked default paths:').red
40
+ puts Rainbow(' - ./coverage/coverage.json (SimpleCov)').red
41
+ puts Rainbow(" - ./coverage/lcov/#{Pathname.new(File.expand_path(opts.path)).split.last}.lcov (LCOV)").red
42
+ puts Rainbow('Set a custom path with --lcov or --simplecov option').red
43
+ 1
44
+ end
45
+
46
+ def self.handle_missing_file(coverage_path)
47
+ puts Rainbow("❌ ERROR: Coverage report not found at: #{coverage_path}").red
48
+ 1
49
+ end
26
50
 
27
- error = report.validate(opts.lcov)
51
+ def self.handle_report_validation(report, coverage_path)
52
+ error = report.validate(coverage_path)
28
53
  if error
29
54
  puts(WARNINGS_TO_S[error])
30
55
  return 0 if error == :no_changes
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Undercover
4
+ class FilterSet
5
+ attr_reader :allow_filters, :reject_filters, :simplecov_filters
6
+
7
+ def initialize(allow_filters, reject_filters, simplecov_filters)
8
+ @allow_filters = allow_filters || []
9
+ @reject_filters = reject_filters || []
10
+ @simplecov_filters = simplecov_filters || []
11
+ end
12
+
13
+ def include?(filepath)
14
+ fnmatch = proc { |glob| File.fnmatch(glob, filepath, File::FNM_EXTGLOB) }
15
+
16
+ # Check if file was ignored by SimpleCov filters
17
+ return false if ignored_by_simplecov?(filepath)
18
+
19
+ # Apply Undercover's own filters
20
+ allow_filters.any?(fnmatch) && reject_filters.none?(fnmatch)
21
+ end
22
+
23
+ private
24
+
25
+ def ignored_by_simplecov?(filepath)
26
+ simplecov_filters.any? do |filter|
27
+ if filter[:string]
28
+ filepath.include?(filter[:string])
29
+ elsif filter[:regex]
30
+ filepath.match?(Regexp.new(filter[:regex]))
31
+ elsif filter[:file]
32
+ filepath == filter[:file]
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -1,19 +1,24 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'undercover/root_to_relative_paths'
4
+
3
5
  module Undercover
4
6
  LcovParseError = Class.new(StandardError)
5
7
 
6
8
  class LcovParser
9
+ include RootToRelativePaths
10
+
7
11
  attr_reader :io, :source_files
8
12
 
9
- def initialize(lcov_io)
13
+ def initialize(lcov_io, opts)
10
14
  @io = lcov_io
11
15
  @source_files = {}
16
+ @code_dir = opts&.path
12
17
  end
13
18
 
14
- def self.parse(lcov_report_path)
19
+ def self.parse(lcov_report_path, opts = nil)
15
20
  lcov_io = File.open(lcov_report_path)
16
- new(lcov_io).parse
21
+ new(lcov_io, opts).parse
17
22
  end
18
23
 
19
24
  def parse
@@ -24,7 +29,7 @@ module Undercover
24
29
 
25
30
  def coverage(filepath)
26
31
  _filename, coverage = source_files.find do |relative_path, _|
27
- relative_path == filepath
32
+ relative_path == fix_relative_filepath(filepath)
28
33
  end
29
34
  coverage || []
30
35
  end
@@ -47,6 +52,16 @@ module Undercover
47
52
  total_f.round(3)
48
53
  end
49
54
 
55
+ def skipped?(_filepath, _line_no)
56
+ # this is why lcov parser will be deprecated
57
+ false
58
+ end
59
+
60
+ def ignored_files
61
+ # supported by SimplecovResultAdapter only
62
+ []
63
+ end
64
+
50
65
  private
51
66
 
52
67
  # rubocop:disable Metrics/MethodLength, Style/SpecialGlobalVars, Metrics/AbcSize
@@ -69,6 +84,7 @@ module Undercover
69
84
  source_files[@current_filename] << [line_no.to_i, block_no.to_i, branch_no.to_i, covered.to_i]
70
85
  when /^end_of_record$/, /^$/
71
86
  @current_filename = nil
87
+ when /^LF:(\d+)|LH:(\d+)/ # lines found, lines hit; no-op
72
88
  else
73
89
  raise LcovParseError, "could not recognise '#{line}' as valid LCOV"
74
90
  end
@@ -2,6 +2,7 @@
2
2
 
3
3
  require 'optparse'
4
4
  require 'pathname'
5
+ require 'shellwords'
5
6
 
6
7
  module Undercover
7
8
  class Options # rubocop:disable Metrics/ClassLength
@@ -34,6 +35,7 @@ module Undercover
34
35
  DEFAULT_FILE_EXCLUDE_GLOBS = %w[test/* spec/* db/* config/* *_test.rb *_spec.rb].freeze
35
36
 
36
37
  attr_accessor :lcov,
38
+ :simplecov_resultset,
37
39
  :path,
38
40
  :git_dir,
39
41
  :compare,
@@ -41,7 +43,8 @@ module Undercover
41
43
  :run_mode,
42
44
  :file_scope,
43
45
  :glob_allow_filters,
44
- :glob_reject_filters
46
+ :glob_reject_filters,
47
+ :max_warnings_limit
45
48
 
46
49
  def initialize
47
50
  @run_mode = DIFF_TRIGGER_LINE
@@ -51,6 +54,7 @@ module Undercover
51
54
  self.git_dir = '.git'
52
55
  self.glob_allow_filters = DEFAULT_FILE_INCLUDE_GLOBS
53
56
  self.glob_reject_filters = DEFAULT_FILE_EXCLUDE_GLOBS
57
+ self.max_warnings_limit = nil
54
58
  end
55
59
 
56
60
  # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
@@ -66,18 +70,23 @@ module Undercover
66
70
  end
67
71
 
68
72
  opts.on_tail('--version', 'Show version') do
73
+ # :nocov:
69
74
  puts VERSION
70
75
  exit
76
+ # :nocov:
71
77
  end
72
78
 
73
79
  lcov_path_option(opts)
80
+ resultset_path_option(opts)
74
81
  project_path_option(opts)
75
82
  git_dir_option(opts)
76
83
  compare_option(opts)
77
84
  ruby_syntax_option(opts)
85
+ max_warnings_limit_option(opts)
78
86
  file_filters(opts)
79
87
  end.parse(args)
80
88
 
89
+ guess_resultset_path unless simplecov_resultset
81
90
  guess_lcov_path unless lcov
82
91
  self
83
92
  end
@@ -96,19 +105,39 @@ module Undercover
96
105
  def args_from_options_file(path)
97
106
  return [] unless File.exist?(path)
98
107
 
99
- File.read(path).split('\n').flat_map(&:split)
108
+ File.read(path).split("\n").flat_map { parse_line(_1) }
100
109
  end
101
110
 
102
111
  def project_options_file
103
112
  './.undercover'
104
113
  end
105
114
 
115
+ def parse_line(line)
116
+ line = line.strip
117
+ return [] if line.empty? || line.start_with?('#')
118
+
119
+ Shellwords.split(line)
120
+ end
121
+
122
+ def split_comma_separated_with_braces(input)
123
+ return [] if input.empty?
124
+
125
+ input.split(/,(?![^{]*})/).map(&:strip) # split on commas that are not inside braces
126
+ end
127
+
106
128
  def lcov_path_option(parser)
107
129
  parser.on('-l', '--lcov path', 'LCOV report file path') do |path|
108
130
  self.lcov = path
109
131
  end
110
132
  end
111
133
 
134
+ def resultset_path_option(parser)
135
+ desc = 'SimpleCov::Formatter::Undercover output file path (alternative to LCOV that will become default)'
136
+ parser.on('-s', '--simplecov path', desc) do |path|
137
+ self.simplecov_resultset = path
138
+ end
139
+ end
140
+
112
141
  def project_path_option(parser)
113
142
  parser.on('-p', '--path path', 'Project directory') do |path|
114
143
  self.path = path
@@ -137,21 +166,35 @@ module Undercover
137
166
  end
138
167
  end
139
168
 
169
+ def max_warnings_limit_option(parser)
170
+ desc = 'Maximum number of warnings to generate before stopping analysis'
171
+ parser.on('-w', '--max-warnings limit', Integer, desc) do |limit|
172
+ self.max_warnings_limit = limit
173
+ end
174
+ end
175
+
176
+ def guess_resultset_path
177
+ cwd = Pathname.new(File.expand_path(path))
178
+ try_path = File.join(cwd, 'coverage', 'coverage.json')
179
+ self.simplecov_resultset = try_path if File.exist?(try_path)
180
+ end
181
+
140
182
  def guess_lcov_path
141
183
  cwd = Pathname.new(File.expand_path(path))
142
- self.lcov = File.join(cwd, 'coverage', 'lcov', "#{cwd.split.last}.lcov")
184
+ try_path = File.join(cwd, 'coverage', 'lcov', "#{cwd.split.last}.lcov")
185
+ self.lcov = try_path if File.exist?(try_path)
143
186
  end
144
187
 
145
188
  def file_filters(parser)
146
189
  desc = 'Include files matching specified glob patterns (comma separated). ' \
147
190
  "Defaults to '#{DEFAULT_FILE_INCLUDE_GLOBS.join(',')}'"
148
191
  parser.on('-f', '--include-files globs', desc) do |comma_separated_globs|
149
- self.glob_allow_filters = comma_separated_globs.strip.split(',')
192
+ self.glob_allow_filters = split_comma_separated_with_braces(comma_separated_globs)
150
193
  end
151
194
 
152
195
  desc = 'Skip files matching specified glob patterns (comma separated). Empty by default.'
153
196
  parser.on('-x', '--exclude-files globs', desc) do |comma_separated_globs|
154
- self.glob_reject_filters = comma_separated_globs.strip.split(',')
197
+ self.glob_reject_filters = split_comma_separated_with_braces(comma_separated_globs)
155
198
  end
156
199
  end
157
200
  end
@@ -3,15 +3,18 @@
3
3
  require 'forwardable'
4
4
 
5
5
  module Undercover
6
- class Result
6
+ class Result # rubocop:disable Metrics/ClassLength
7
7
  extend Forwardable
8
8
 
9
- attr_reader :node, :coverage, :file_path
9
+ attr_reader :node, :coverage, :coverage_adapter, :file_path
10
10
 
11
11
  def_delegators :node, :first_line, :last_line, :name
12
+ def_delegators :coverage_adapter, :skipped?
12
13
 
13
- def initialize(node, file_cov, file_path) # rubocop:disable Metrics/MethodLength,Metrics/AbcSize
14
+ def initialize(node, coverage_adapter, file_path) # rubocop:disable Metrics/MethodLength,Metrics/AbcSize
14
15
  @node = node
16
+ @coverage_adapter = coverage_adapter
17
+ file_cov = coverage_adapter.coverage(file_path)
15
18
  @coverage = file_cov.select do |ln, _|
16
19
  if first_line == last_line
17
20
  ln == first_line
@@ -21,6 +24,7 @@ module Undercover
21
24
  ln > first_line && ln < last_line
22
25
  end
23
26
  end
27
+
24
28
  @file_path = file_path
25
29
  @flagged = false
26
30
  end
@@ -33,12 +37,13 @@ module Undercover
33
37
  @flagged
34
38
  end
35
39
 
36
- def uncovered?(line_no)
40
+ def uncovered?(line_no) # rubocop:disable Metrics/AbcSize
41
+ return false if skipped?(file_path, line_no)
37
42
  return true if coverage.empty?
38
43
 
39
44
  # check branch coverage for line_no
40
45
  coverage.each do |ln, _block, _branch, cov|
41
- return true if ln == line_no && cov && cov.zero?
46
+ return true if ln == line_no && cov && cov != 'ignored' && cov.zero?
42
47
  end
43
48
 
44
49
  # check line coverage for line_no
@@ -55,9 +60,14 @@ module Undercover
55
60
 
56
61
  lines = {}
57
62
  coverage.each do |ln, block_or_line_cov, _, branch_cov|
63
+ if skipped?(file_path, ln)
64
+ lines[ln] = 1
65
+ next
66
+ end
67
+
58
68
  lines[ln] = 1 unless lines.key?(ln)
59
69
  if branch_cov
60
- lines[ln] = 0 if branch_cov.zero?
70
+ lines[ln] = 0 if branch_cov != 'ignored' && branch_cov.zero?
61
71
  elsif block_or_line_cov.zero?
62
72
  lines[ln] = 0
63
73
  end
@@ -93,6 +103,9 @@ module Undercover
93
103
  formatted_line = "#{num.to_s.rjust(pad)}: #{line}"
94
104
  if line.strip.empty?
95
105
  Rainbow(formatted_line).darkgray.dark
106
+ elsif skipped?(file_path, num)
107
+ Rainbow(formatted_line).darkgray.dark +
108
+ Rainbow(' skipped with :nocov:').italic.darkgray.dark
96
109
  elsif covered.nil?
97
110
  Rainbow(formatted_line).darkgray.dark +
98
111
  Rainbow(' hits: n/a').italic.darkgray.dark
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Undercover
4
+ module RootToRelativePaths
5
+ # Needed if undercover is running inside nested subdirectories (e.g. in a monorepo app), where
6
+ # the git paths are rooted deeper than the paths in the coverage report.
7
+ # If that is the case, trim the git filepath to match the local relative path, assumming undercover is
8
+ # running in the correct directory (has to be equal to SimpleCov.root for paths to match)
9
+ # @param filepath[String]
10
+ # @return String
11
+ def fix_relative_filepath(filepath)
12
+ return filepath unless @code_dir
13
+
14
+ code_dir_abs = File.expand_path(@code_dir)
15
+
16
+ if Pathname.new(Dir.pwd).ascend.any? { |p| p.to_s == code_dir_abs }
17
+ prefix_to_skip = Pathname.new(Dir.pwd).relative_path_from(code_dir_abs).to_s
18
+ return filepath.delete_prefix(prefix_to_skip).gsub(/\A\//, '')
19
+ end
20
+
21
+ filepath
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,111 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'simplecov_json_formatter'
4
+
5
+ # Patch ResultExporter to allow setting a custom export_path
6
+ module SimpleCovJSONFormatter
7
+ class ResultExporter
8
+ def export_path
9
+ # :nocov:
10
+ File.join(SimpleCov.coverage_path, SimpleCov::Formatter::Undercover.output_filename || FILENAME)
11
+ # :nocov:
12
+ end
13
+ end
14
+ end
15
+
16
+ module SimpleCov
17
+ class << self
18
+ attr_accessor :filter_definitions
19
+
20
+ alias filtered_uncached filtered
21
+
22
+ def filtered(files)
23
+ @filter_definitions ||= extract_filter_definitions
24
+ original_files = files.dup
25
+ filtered_uncached(files).tap do |filtered_files|
26
+ filtered_file_paths = (original_files.map(&:filename) - filtered_files.map(&:filename))
27
+ filtered_file_paths.each do |file|
28
+ relative_path = file.delete_prefix("#{SimpleCov.root}/")
29
+ @filter_definitions << {file: relative_path} unless covered_by_serializable_filters?(relative_path)
30
+ end
31
+ end
32
+ end
33
+
34
+ private
35
+
36
+ def extract_filter_definitions
37
+ filter_array = []
38
+
39
+ filters.each do |filter|
40
+ case filter
41
+ when SimpleCov::StringFilter
42
+ filter_array << {string: filter.filter_argument}
43
+ when SimpleCov::RegexFilter
44
+ filter_array << {regex: filter.filter_argument.source}
45
+ end
46
+ end
47
+
48
+ filter_array
49
+ end
50
+
51
+ def covered_by_serializable_filters?(relative_path)
52
+ @filter_definitions.any? do |filter_def|
53
+ if filter_def[:string]
54
+ relative_path.include?(filter_def[:string])
55
+ elsif filter_def[:regex]
56
+ relative_path.match?(Regexp.new(filter_def[:regex]))
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
62
+
63
+ module Undercover
64
+ class ResultHashFormatterWithRoot < SimpleCovJSONFormatter::ResultHashFormatter
65
+ def format
66
+ formatted_result[:meta] = {timestamp: @result.created_at.to_i}
67
+ format_files
68
+ add_undercover_meta_fields
69
+ add_ignored_files
70
+ formatted_result
71
+ end
72
+
73
+ private
74
+
75
+ def add_undercover_meta_fields
76
+ formatted_result.tap do |result|
77
+ result[:meta].merge!(simplecov_root: SimpleCov.root)
78
+ end
79
+ end
80
+
81
+ def add_ignored_files
82
+ formatted_result[:meta][:ignored_files] = SimpleCov.filter_definitions || []
83
+ end
84
+
85
+ # format_files uses relative path as keys, as opposed to the superclass method
86
+ def format_files
87
+ formatted_result[:coverage] ||= {}
88
+ @result.files.each do |source_file|
89
+ path = source_file.project_filename.delete_prefix('/')
90
+ formatted_result[:coverage][path] = format_source_file(source_file)
91
+ end
92
+ end
93
+ end
94
+
95
+ class UndercoverSimplecovFormatter < SimpleCov::Formatter::JSONFormatter
96
+ class << self
97
+ attr_accessor :output_filename
98
+ end
99
+
100
+ def format_result(result)
101
+ result_hash_formater = ResultHashFormatterWithRoot.new(result)
102
+ result_hash_formater.format
103
+ end
104
+ end
105
+ end
106
+
107
+ module SimpleCov
108
+ module Formatter
109
+ Undercover = ::Undercover::UndercoverSimplecovFormatter
110
+ end
111
+ end
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'undercover/root_to_relative_paths'
4
+
5
+ module Undercover
6
+ class SimplecovResultAdapter
7
+ include RootToRelativePaths
8
+
9
+ attr_reader :simplecov_result
10
+
11
+ # @param file[File] JSON file supplied by SimpleCov::Formatter::Undercover
12
+ # @return SimplecovResultAdapter
13
+ def self.parse(file, opts = nil)
14
+ # :nocov:
15
+ result_h = JSON.parse(file.read)
16
+ raise ArgumentError, 'empty SimpleCov' if result_h.empty?
17
+
18
+ new(result_h, opts)
19
+ # :nocov:
20
+ end
21
+
22
+ # @param simplecov_result[SimpleCov::Result]
23
+ def initialize(simplecov_result, opts)
24
+ @simplecov_result = simplecov_result
25
+ @code_dir = opts&.path
26
+ end
27
+
28
+ # @param filepath[String]
29
+ # @return Array tuples (lines) and quadruples (branches) compatible with LcovParser
30
+ def coverage(filepath) # rubocop:disable Metrics/MethodLength
31
+ source_file = find_file(filepath)
32
+
33
+ return [] unless source_file
34
+
35
+ lines = source_file['lines'].map.with_index do |line_coverage, idx|
36
+ [idx + 1, line_coverage] if line_coverage
37
+ end.compact
38
+ return lines unless source_file['branches']
39
+
40
+ branch_idx = 0
41
+ branches = source_file['branches'].map do |branch|
42
+ branch_idx += 1
43
+ [branch['start_line'], 0, branch_idx, branch['coverage']]
44
+ end
45
+ lines + branches
46
+ end
47
+
48
+ def skipped?(filepath, line_no)
49
+ source_file = find_file(filepath)
50
+ return false unless source_file
51
+
52
+ source_file['lines'][line_no - 1] == 'ignored'
53
+ end
54
+
55
+ # unused for now
56
+ def total_coverage; end
57
+ def total_branch_coverage; end
58
+
59
+ def ignored_files
60
+ @ignored_files ||= simplecov_result.dig('meta', 'ignored_files') || []
61
+ end
62
+
63
+ private
64
+
65
+ def find_file(filepath)
66
+ simplecov_result['coverage'][fix_relative_filepath(filepath)]
67
+ end
68
+ end
69
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Undercover
4
- VERSION = '0.6.4'
4
+ VERSION = '0.8.0'
5
5
  end
data/lib/undercover.rb CHANGED
@@ -1,10 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  $LOAD_PATH << 'lib'
4
+ require 'json'
4
5
  require 'imagen'
5
6
  require 'rainbow'
6
7
  require 'bigdecimal'
7
8
  require 'forwardable'
9
+ require 'simplecov'
8
10
 
9
11
  require 'undercover/lcov_parser'
10
12
  require 'undercover/result'
@@ -12,38 +14,49 @@ require 'undercover/cli'
12
14
  require 'undercover/changeset'
13
15
  require 'undercover/formatter'
14
16
  require 'undercover/options'
17
+ require 'undercover/filter_set'
18
+ require 'undercover/simplecov_result_adapter'
15
19
  require 'undercover/version'
16
20
 
17
21
  module Undercover
18
22
  class Report
19
23
  extend Forwardable
24
+
20
25
  def_delegators :changeset, :validate
21
26
 
22
27
  attr_reader :changeset,
23
28
  :lcov,
29
+ :coverage_adapter,
24
30
  :results,
25
31
  :code_dir,
26
- :glob_filters
32
+ :filter_set,
33
+ :max_warnings_limit
27
34
 
28
35
  # Initializes a new Undercover::Report
29
36
  #
30
37
  # @param changeset [Undercover::Changeset]
31
38
  # @param opts [Undercover::Options]
32
- def initialize(changeset, opts)
33
- @lcov = LcovParser.parse(File.open(opts.lcov))
39
+ # @param coverage_adapter [Undercover::SimplecovResultAdapter|Undercover::LcovParser] pre-parsed coverage adapter
40
+ def initialize(changeset, opts, coverage_adapter)
41
+ @coverage_adapter = coverage_adapter
42
+
34
43
  @code_dir = opts.path
35
- @changeset = changeset.update
36
- @glob_filters = {
37
- allow: opts.glob_allow_filters,
38
- reject: opts.glob_reject_filters
39
- }
44
+ @changeset = changeset
45
+
46
+ ignored_files = coverage_adapter.ignored_files || []
47
+ @filter_set = FilterSet.new(opts.glob_allow_filters, opts.glob_reject_filters, ignored_files)
48
+ changeset.filter_with(filter_set)
49
+ @max_warnings_limit = opts.max_warnings_limit
40
50
  @loaded_files = {}
41
51
  @results = {}
42
52
  end
43
53
 
44
54
  # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
45
55
  def build
56
+ flag_count = 0
46
57
  changeset.each_changed_line do |filepath, line_no|
58
+ break if max_warnings_limit && flag_count >= max_warnings_limit
59
+
47
60
  dist_from_line_no = lambda do |res|
48
61
  return BigDecimal::INFINITY if line_no < res.first_line
49
62
 
@@ -61,7 +74,10 @@ module Undercover
61
74
  next unless loaded_files[filepath]
62
75
 
63
76
  res = loaded_files[filepath].min(&dist_from_line_no_sorter)
64
- res.flag if res&.uncovered?(line_no)
77
+ if res.uncovered?(line_no) && !res.flagged?
78
+ res.flag
79
+ flag_count += 1
80
+ end
65
81
  results[filepath] ||= Set.new
66
82
  results[filepath] << res
67
83
  end
@@ -92,15 +108,11 @@ module Undercover
92
108
 
93
109
  attr_reader :loaded_files
94
110
 
95
- # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
111
+ # rubocop:disable Metrics/AbcSize
96
112
  def load_and_parse_file(filepath)
97
113
  key = filepath.gsub(/^\.\//, '')
98
114
  return if loaded_files[key]
99
115
 
100
- coverage = lcov.coverage(filepath)
101
-
102
- return unless include_file?(filepath)
103
-
104
116
  root_ast = Imagen::Node::Root.new.build_from_file(
105
117
  File.join(code_dir, filepath)
106
118
  )
@@ -108,14 +120,9 @@ module Undercover
108
120
 
109
121
  loaded_files[key] = []
110
122
  root_ast.find_all(->(node) { !node.is_a?(Imagen::Node::Root) }).each do |imagen_node|
111
- loaded_files[key] << Result.new(imagen_node, coverage, filepath)
123
+ loaded_files[key] << Result.new(imagen_node, coverage_adapter, filepath)
112
124
  end
113
125
  end
114
- # rubocop:enable Metrics/MethodLength, Metrics/AbcSize
115
-
116
- def include_file?(filepath)
117
- fnmatch = proc { |glob| File.fnmatch(glob, filepath) }
118
- glob_filters[:allow].any?(fnmatch) && glob_filters[:reject].none?(fnmatch)
119
- end
126
+ # rubocop:enable Metrics/AbcSize
120
127
  end
121
128
  end
data/undercover.gemspec CHANGED
@@ -32,4 +32,6 @@ Gem::Specification.new do |spec|
32
32
  spec.add_dependency 'imagen', '>= 0.2.0'
33
33
  spec.add_dependency 'rainbow', '>= 2.1', '< 4.0'
34
34
  spec.add_dependency 'rugged', '>= 0.27', '< 1.10'
35
+ spec.add_dependency 'simplecov'
36
+ spec.add_dependency 'simplecov_json_formatter'
35
37
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: undercover
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.4
4
+ version: 0.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jan Grodowski
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2025-03-29 00:00:00.000000000 Z
10
+ date: 2025-08-28 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: base64
@@ -91,6 +91,34 @@ dependencies:
91
91
  - - "<"
92
92
  - !ruby/object:Gem::Version
93
93
  version: '1.10'
94
+ - !ruby/object:Gem::Dependency
95
+ name: simplecov
96
+ requirement: !ruby/object:Gem::Requirement
97
+ requirements:
98
+ - - ">="
99
+ - !ruby/object:Gem::Version
100
+ version: '0'
101
+ type: :runtime
102
+ prerelease: false
103
+ version_requirements: !ruby/object:Gem::Requirement
104
+ requirements:
105
+ - - ">="
106
+ - !ruby/object:Gem::Version
107
+ version: '0'
108
+ - !ruby/object:Gem::Dependency
109
+ name: simplecov_json_formatter
110
+ requirement: !ruby/object:Gem::Requirement
111
+ requirements:
112
+ - - ">="
113
+ - !ruby/object:Gem::Version
114
+ version: '0'
115
+ type: :runtime
116
+ prerelease: false
117
+ version_requirements: !ruby/object:Gem::Requirement
118
+ requirements:
119
+ - - ">="
120
+ - !ruby/object:Gem::Version
121
+ version: '0'
94
122
  email:
95
123
  - jgrodowski@gmail.com
96
124
  executables:
@@ -112,20 +140,23 @@ files:
112
140
  - README.md
113
141
  - Rakefile
114
142
  - bin/undercover
143
+ - docs/actions.yml
115
144
  - docs/circleci_advanced.yml
116
145
  - docs/circleci_config.yml
117
- - docs/codeship.md
118
146
  - docs/screenshot_success.png
119
147
  - docs/screenshot_warnings.png
120
148
  - docs/semaphore.yml
121
- - docs/travis.yml
122
149
  - lib/undercover.rb
123
150
  - lib/undercover/changeset.rb
124
151
  - lib/undercover/cli.rb
152
+ - lib/undercover/filter_set.rb
125
153
  - lib/undercover/formatter.rb
126
154
  - lib/undercover/lcov_parser.rb
127
155
  - lib/undercover/options.rb
128
156
  - lib/undercover/result.rb
157
+ - lib/undercover/root_to_relative_paths.rb
158
+ - lib/undercover/simplecov_formatter.rb
159
+ - lib/undercover/simplecov_result_adapter.rb
129
160
  - lib/undercover/version.rb
130
161
  - undercover.gemspec
131
162
  homepage: https://github.com/grodowski/undercover
data/docs/codeship.md DELETED
@@ -1,19 +0,0 @@
1
- Sample configuration to run `undercover` in Codeship CI. Edit these fields in **Project Settings**.
2
-
3
- **Setup commands**
4
-
5
- ```
6
- rvm use 2.5.3 --install
7
- bundle install
8
- gem install undercover
9
- ```
10
-
11
- **Test pipeline**
12
-
13
- ```
14
- bundle exec rspec --format documentation --color
15
- # fetch origin/master to have a ref to compare against
16
- git remote set-branches --add origin master
17
- git fetch
18
- undercover -c origin/master
19
- ```
data/docs/travis.yml DELETED
@@ -1,13 +0,0 @@
1
- language: ruby
2
- rvm:
3
- - 2.6.2
4
- - 2.5.5
5
- - 2.4.5
6
- - 2.3.7
7
- before_install:
8
- - gem install bundler undercover --no-doc
9
- - gem update --system
10
- script:
11
- - bundle exec rake
12
- - git fetch origin master:master
13
- - undercover --compare master