uncov 0.3.0 → 0.4.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 +4 -4
- data/CHANGELOG.md +13 -0
- data/CONTRIBUTING.md +18 -14
- data/PHILOSOPHY.md +55 -0
- data/README.md +52 -16
- data/bin/uncov +9 -1
- data/lib/uncov/cli.rb +7 -42
- data/lib/uncov/configuration/option.rb +50 -0
- data/lib/uncov/configuration.rb +72 -8
- data/lib/uncov/finder/file_system.rb +1 -5
- data/lib/uncov/finder/git_base.rb +2 -4
- data/lib/uncov/finder/git_diff.rb +9 -7
- data/lib/uncov/finder/simple_cov.rb +14 -11
- data/lib/uncov/finder.rb +2 -1
- data/lib/uncov/formatter/terminal.rb +40 -13
- data/lib/uncov/formatter.rb +16 -2
- data/lib/uncov/report/context.rb +18 -0
- data/lib/uncov/report/diff_lines.rb +43 -12
- data/lib/uncov/report/file/line.rb +11 -4
- data/lib/uncov/report/file.rb +28 -14
- data/lib/uncov/report.rb +33 -10
- data/lib/uncov/struct.rb +3 -0
- data/lib/uncov/version.rb +1 -1
- data/lib/uncov.rb +21 -11
- metadata +20 -3
- data/CHANGELOG_BLA.md +0 -28
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 397cffc8dced395da502cb60cc2136da4cb7f886ab0dc53636d8d3dd886b263b
|
4
|
+
data.tar.gz: 9d73dc0cbf0467f131d470f4ee486013daf64f2e73980749ffcad09ccb8fced7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5407ea15f650d927ec5887b5d1d0530caea5cefa92d3f9bbde1e0895de820cae5cf415d61a223af834d2265d717ac1ba4984b8e18ed547915ad55e1b86d04406
|
7
|
+
data.tar.gz: 21c9b8dc0af1213f24167b0a0442550444cb58fb3af667d8612951818a3301cd33c981d6151a3c69579a726a30057b62102c2f3fc207ffa56da3078495172d3f
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,18 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## 2025-05-05: [v0.4.0](https://github.com/mpapis/uncov/releases/tag/v0.4.0)
|
4
|
+
|
5
|
+
### Minor
|
6
|
+
|
7
|
+
- [#38](https://github.com/mpapis/uncov/pull/38): Read configuration from file, by [@mpapis](https://github.com/mpapis)
|
8
|
+
- [#39](https://github.com/mpapis/uncov/pull/39): Add output context lines, by [@mpapis](https://github.com/mpapis)
|
9
|
+
|
10
|
+
### Patch
|
11
|
+
|
12
|
+
- [#37](https://github.com/mpapis/uncov/pull/37): Fix exception raising without namespace, by [@mpapis](https://github.com/mpapis)
|
13
|
+
|
14
|
+
|
15
|
+
|
3
16
|
## 2025-04-30: [v0.3.0](https://github.com/mpapis/uncov/releases/tag/v0.3.0)
|
4
17
|
|
5
18
|
### Minor
|
data/CONTRIBUTING.md
CHANGED
@@ -1,26 +1,25 @@
|
|
1
1
|
# Contributing
|
2
|
-
Bug reports and pull requests are welcome on GitHub at https://github.com/mpapis/uncov.
|
3
2
|
|
4
|
-
|
3
|
+
## Want to help?
|
4
|
+
- Bug reports and pull requests are welcome on GitHub at https://github.com/mpapis/uncov.
|
5
|
+
- Do not know where to start? Help triaging issues, make sure they can be reproduced.
|
5
6
|
|
6
7
|
|
7
8
|
## Reporting a Vulnerability
|
8
|
-
|
9
9
|
If you discover a security vulnerability within uncov, please email [mpapis@gmail.com](mailto:mpapis@gmail.com).
|
10
10
|
All security vulnerabilities will be promptly addressed.
|
11
11
|
|
12
|
+
|
12
13
|
## Development
|
13
14
|
- `docker-compose build uncov` to (re-)build dev container,
|
14
15
|
- `docker-compose run uncov` to enter dev container,
|
15
|
-
- `bundle` to install dependencies,
|
16
|
-
- `rake` to run test and
|
17
|
-
- `
|
18
|
-
- `uncov` to see uncovered changes (check itself)
|
16
|
+
- `bundle` to install dependencies and uncov,
|
17
|
+
- `rake` to run lint, test and uncov - **precheck** before opening PR,
|
18
|
+
- `uncov` to test manually
|
19
19
|
|
20
20
|
|
21
21
|
## Pull requests
|
22
|
-
|
23
|
-
Open pull requests against the `develop` branch
|
22
|
+
Open pull requests against the `develop` or `release-v*` branches.
|
24
23
|
|
25
24
|
Branch prefix influences auto labeling next release:
|
26
25
|
- `ignore/` - will not trigger next release (excluded from changelog),
|
@@ -28,13 +27,21 @@ Branch prefix influences auto labeling next release:
|
|
28
27
|
- `feature/` - will bump minor version,
|
29
28
|
- `breaking/` - will bump major version.
|
30
29
|
|
31
|
-
If unsure it's not important, the release label's can be changed by maintainers.
|
30
|
+
If unsure it's not important, the release label's (`release:*`) can be changed by maintainers.
|
32
31
|
|
33
32
|
|
34
33
|
## Release Process
|
34
|
+
This gem uses GitHub Actions for continuous integration and automated releases.
|
35
35
|
|
36
|
-
This gem uses GitHub Actions for continuous integration and automated releases. Here's how the release process works:
|
37
36
|
|
37
|
+
### Check future changelog
|
38
|
+
1. Go to the "Actions" tab in the GitHub repository
|
39
|
+
2. Select the "Changelog" workflow
|
40
|
+
3. Click "Run workflow"
|
41
|
+
4. Go to "Summary" and see generated version and changelog
|
42
|
+
|
43
|
+
|
44
|
+
### Release
|
38
45
|
1. Go to the "Actions" tab in the GitHub repository
|
39
46
|
2. Select the "Release" workflow
|
40
47
|
3. Click "Run workflow"
|
@@ -46,9 +53,6 @@ This will:
|
|
46
53
|
|
47
54
|
|
48
55
|
## Security Practices
|
49
|
-
|
50
|
-
This project follows these security practices:
|
51
|
-
|
52
56
|
1. **Code Review**:
|
53
57
|
- All pull requests must be reviewed by a project maintainer
|
54
58
|
- Workflow files cannot be modified without explicit review from core maintainers
|
data/PHILOSOPHY.md
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
# What is uncov?
|
2
|
+
`uncov` stands for uncovered code.
|
3
|
+
|
4
|
+
|
5
|
+
## Why uncov
|
6
|
+
I was irritated by manually checking which files were changed in git
|
7
|
+
and then looking at each file coverage report for missing coverage.
|
8
|
+
|
9
|
+
|
10
|
+
## Why code coverage
|
11
|
+
This was tiresome but something that I thought was required to validate
|
12
|
+
if I thought about important behaviors of my code in tests.
|
13
|
+
|
14
|
+
|
15
|
+
## Code coverage is worthless
|
16
|
+
Code coverage does not indicate quality of the tests,
|
17
|
+
but lack of coverage indicates lack of testing specific behavior at all.
|
18
|
+
|
19
|
+
Despite its limitations as a quality metric, we can use coverage insights
|
20
|
+
as a starting point to develop better testing practices. Here's how:
|
21
|
+
|
22
|
+
## How to improve quality of tests
|
23
|
+
Think about testing behaviors, do not test implementation.
|
24
|
+
This implies more work preparing test data / the state of the system,
|
25
|
+
in a way that shows how the system behaves in specific conditions.
|
26
|
+
|
27
|
+
There has been a lot written about how to test, learn how others approach testing/coverage:
|
28
|
+
- https://testdesiderata.com/
|
29
|
+
- https://martinfowler.com/bliki/TestCoverage.html
|
30
|
+
- https://martinfowler.com/articles/practical-test-pyramid.html
|
31
|
+
- https://www.betterspecs.org/
|
32
|
+
- https://github.com/ch1ago/bdd
|
33
|
+
|
34
|
+
There is a lot of gold in there, but I do not agree with everything there,
|
35
|
+
you need to find your own way that works for your specific project needs.
|
36
|
+
|
37
|
+
|
38
|
+
## How uncov helps
|
39
|
+
`uncov` focuses your attention where it matters most - on recently changed code that lacks test coverage.
|
40
|
+
By analyzing git diffs and connecting them to your test coverage results, `uncov`:
|
41
|
+
- Identifies only the files you've modified, saving you time
|
42
|
+
- Shows precisely which changed lines remain untested
|
43
|
+
- Integrates with your existing test workflow through SimpleCov
|
44
|
+
- Provides context around uncovered code to help you understand what needs testing
|
45
|
+
|
46
|
+
This targeted approach helps you maintain high-quality tests for new and modified code
|
47
|
+
without getting overwhelmed by legacy coverage issues.
|
48
|
+
|
49
|
+
|
50
|
+
## Take action
|
51
|
+
Ready to improve your testing workflow?
|
52
|
+
|
53
|
+
Install uncov with `gem install uncov` and run it after making changes to immediately see which behaviors need tests.
|
54
|
+
Check the [README](README.md) for more details and
|
55
|
+
`uncov -h` for configuration options to customize uncov for your project's needs.
|
data/README.md
CHANGED
@@ -1,50 +1,86 @@
|
|
1
1
|
# Uncov
|
2
|
+
Uncov analyzes test coverage for changed files in your Git repository,
|
3
|
+
helping you ensure that all your recent changes are properly tested.
|
2
4
|
|
3
|
-
Uncov
|
5
|
+
Uncov uses `git diff` to detect changes and `simplecov` reports to detect uncovered code.
|
4
6
|
|
5
|
-
|
7
|
+
[The uncov Manifesto](PHILOSOPHY.md)
|
8
|
+
|
9
|
+

|
6
10
|
|
11
|
+
## Features
|
7
12
|
- Compare your working tree to a target branch
|
8
13
|
- Identify changed Ruby files
|
9
|
-
-
|
10
|
-
-
|
11
|
-
- Generate reports of uncovered lines in changed files
|
14
|
+
- Run tests automatically for (changed) relevant files
|
15
|
+
- Print report of uncovered lines in (changed) files
|
12
16
|
|
13
|
-
## Installation
|
14
17
|
|
18
|
+
## Installation
|
15
19
|
```bash
|
16
20
|
gem install uncov
|
17
21
|
```
|
18
|
-
|
19
22
|
Or add to your Gemfile (only for convenience):
|
20
23
|
```ruby
|
21
24
|
gem 'uncov', require: false
|
22
25
|
```
|
23
26
|
|
27
|
+
|
24
28
|
## Usage
|
25
29
|
Basic usage:
|
26
30
|
```bash
|
27
31
|
uncov
|
28
32
|
```
|
29
|
-
|
33
|
+
|
34
|
+
Display configuration options:
|
30
35
|
```bash
|
31
|
-
uncov
|
36
|
+
$ uncov -h
|
37
|
+
Usage: uncov [options]
|
38
|
+
-t, --target TARGET Target branch for comparison, default: "HEAD"
|
39
|
+
-r, --report TYPE Report type to generate, one_of: "diff_lines"(default)
|
40
|
+
-o, --output-format FORMAT Output format, one_of: "terminal"(default)
|
41
|
+
-C, --context LINES_NUMBER Additional lines context in output, default: 1
|
42
|
+
--test-command COMMAND Test command that generates SimpleCov, default: "COVERAGE=true bundle exec rake test"
|
43
|
+
--simplecov-file PATH SimpleCov results file, default: "autodetect"
|
44
|
+
--relevant-files Relevant files shell filename globing: https://ruby-doc.org/core-3.1.1/File.html#method-c-fnmatch, default: "{{bin,exe,exec}/*,{app,lib}/**/*.{rake,rb},Rakefile}"
|
45
|
+
--debug Get some insights, default: false
|
46
|
+
-v, --version Show version
|
47
|
+
-h, --help Print this help
|
48
|
+
uncov 0.3.0 by Michal Papis <mpapis@gmail.com>
|
32
49
|
```
|
33
|
-
Options
|
34
|
-
- `-t`, `--target BRANCH`: Target branch for comparison (default: main)
|
35
|
-
- `-c`, `--command COMMAND`: Test command to run (default: bundle exec rake test)
|
36
|
-
- `-p`, `--path PATH`: Path to SimpleCov results (default: coverage/.resultset.json)
|
37
|
-
- `-h`, `--help`: Display help
|
38
|
-
- `-v`, `--version`: Display version
|
39
50
|
|
40
|
-
## Requirements
|
41
51
|
|
52
|
+
## Configuration file
|
53
|
+
`.uncov` file in the directory where it's ran stores default options,
|
54
|
+
specify one argument per line - this eliminates the need for special parsing of the file.
|
55
|
+
|
56
|
+
Example:
|
57
|
+
```text
|
58
|
+
--target
|
59
|
+
develop
|
60
|
+
--test-command
|
61
|
+
COVERAGE=1 rspec
|
62
|
+
```
|
63
|
+
|
64
|
+
|
65
|
+
## Using in CI
|
66
|
+
`uncov` uses itself to check new missing code coverage [.github/workflows/ci.yml](.github/workflows/ci.yml),
|
67
|
+
no need to set minimal, always get better.
|
68
|
+
|
69
|
+
Ideas for CI:
|
70
|
+
- run `uncov` after running your tests with coverage enabled,
|
71
|
+
- be less restrictive - provide custom `--relevant-files` pattern
|
72
|
+
that excludes some paths you do not think should be always tested.
|
73
|
+
|
74
|
+
|
75
|
+
## Requirements
|
42
76
|
- Ruby 3.2+
|
43
77
|
- A Git repository
|
44
78
|
- SimpleCov for test coverage
|
45
79
|
|
80
|
+
|
46
81
|
## Contributing
|
47
82
|
Contributing, developing, pull requests, releasing, security -> [CONTRIBUTING.md](CONTRIBUTING.md).
|
48
83
|
|
84
|
+
|
49
85
|
## License
|
50
86
|
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/bin/uncov
CHANGED
data/lib/uncov/cli.rb
CHANGED
@@ -5,49 +5,14 @@ require 'optparse'
|
|
5
5
|
# provide terminal interface for uncov
|
6
6
|
class Uncov::CLI
|
7
7
|
def self.start(args)
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
def start(args)
|
12
|
-
parse_options(args)
|
13
|
-
report = Uncov::Report.new
|
8
|
+
Uncov.configure(args)
|
9
|
+
report = Uncov::Report.build
|
14
10
|
Uncov::Formatter.output(report)
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
private
|
11
|
+
!report.uncov?
|
12
|
+
rescue StandardError => e
|
13
|
+
raise if Uncov.configuration.debug
|
19
14
|
|
20
|
-
|
21
|
-
|
22
|
-
opts.banner = 'Usage: uncov [options]'
|
23
|
-
opts.on('-r', '--report TYPE', 'Report type to generate (git_diff)') do |type|
|
24
|
-
Uncov.configuration.report = type.to_sym
|
25
|
-
end
|
26
|
-
opts.on('-t', '--target BRANCH', 'Target branch for comparison') do |branch|
|
27
|
-
Uncov.configuration.git_diff_target = branch
|
28
|
-
end
|
29
|
-
opts.on('-c', '--command COMMAND', 'Test command to run SimpleCov') do |command|
|
30
|
-
Uncov.configuration.test_command = command
|
31
|
-
end
|
32
|
-
opts.on('-p', '--path PATH', 'Target filesystem path') do |path|
|
33
|
-
Uncov.configuration.path = path
|
34
|
-
end
|
35
|
-
opts.on('-f', '--formater FORMATTER', 'Formatter for output') do |formatter|
|
36
|
-
Uncov.configuration.output_format = formatter.to_sym
|
37
|
-
end
|
38
|
-
opts.on('--simplecov-path PATH', 'Path to SimpleCov results') do |path|
|
39
|
-
Uncov.configuration.simplecov_output_path = path
|
40
|
-
end
|
41
|
-
opts.on('-h', '--help', 'Print this help') do
|
42
|
-
puts opts
|
43
|
-
puts "uncov #{Uncov::VERSION} by Michal Papis <mpapis@gmail.com>"
|
44
|
-
exit
|
45
|
-
end
|
46
|
-
opts.on('-v', '--version', 'Show version') do
|
47
|
-
puts "uncov #{Uncov::VERSION} by Michal Papis <mpapis@gmail.com>"
|
48
|
-
exit
|
49
|
-
end
|
50
|
-
end
|
51
|
-
parser.parse!(args)
|
15
|
+
warn e.message
|
16
|
+
nil
|
52
17
|
end
|
53
18
|
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# configuration option
|
4
|
+
class Uncov::Configuration::Option
|
5
|
+
attr_reader :name, :description, :options, :default, :allowed_values, :value_parse, :value
|
6
|
+
|
7
|
+
def initialize(name, description, options, default, allowed_values, value_parse)
|
8
|
+
@name = name
|
9
|
+
@description = description
|
10
|
+
@options = Array(options)
|
11
|
+
@default = default.freeze
|
12
|
+
@allowed_values = allowed_values
|
13
|
+
@value_parse = value_parse
|
14
|
+
self.value = default
|
15
|
+
end
|
16
|
+
|
17
|
+
def value=(value)
|
18
|
+
if allowed_values&.none?(value)
|
19
|
+
raise \
|
20
|
+
Uncov::OptionValueNotAllowed,
|
21
|
+
"Configuration option(#{name.inspect}) tried to set: #{value.inspect}, only: #{allowed_values.inspect} allowed"
|
22
|
+
else
|
23
|
+
@value = value
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def on_parser(parser) = parser.on(*options, options_description) { |value| self.value = value_parse.call(value) }
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def options_description
|
32
|
+
if allowed_values
|
33
|
+
"#{description}, one_of: #{options_one_of.join(', ')}"
|
34
|
+
else
|
35
|
+
"#{description}, default: #{default.inspect}"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def options_one_of
|
40
|
+
allowed_values.map do |value|
|
41
|
+
if value == default
|
42
|
+
"#{value.inspect}(default)"
|
43
|
+
else
|
44
|
+
# :nocov: for now as there is no case yet in Configuration
|
45
|
+
value.inspect
|
46
|
+
# :nocov:
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
data/lib/uncov/configuration.rb
CHANGED
@@ -1,17 +1,81 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative 'formatter'
|
4
|
+
require_relative 'report'
|
5
|
+
|
3
6
|
# handle configuration for uncov
|
4
7
|
class Uncov::Configuration
|
8
|
+
CONFIG_FILE = '.uncov'
|
5
9
|
FILE_MATCH_FLAGS = File::FNM_EXTGLOB | File::FNM_PATHNAME | File::FNM_DOTMATCH
|
6
|
-
|
10
|
+
|
11
|
+
class << self
|
12
|
+
def option(name, description, options:, default:, allowed_values: nil, value_parse: ->(value) { value })
|
13
|
+
self.options << [name, description, options, default, allowed_values, value_parse]
|
14
|
+
define_method(name) { self.options[name].value }
|
15
|
+
define_method("#{name}=") { |value| self.options[name].value = value }
|
16
|
+
end
|
17
|
+
|
18
|
+
def options = @options ||= []
|
19
|
+
end
|
20
|
+
|
21
|
+
option 'target', 'Target branch for comparison', options: ['-t', '--target TARGET'], default: 'HEAD'
|
22
|
+
option 'report', 'Report type to generate', options: ['-r', '--report TYPE'], default: 'diff_lines', allowed_values: Uncov::Report.types
|
23
|
+
option 'output_format', 'Output format',
|
24
|
+
options: ['-o', '--output-format FORMAT'], default: 'terminal', allowed_values: Uncov::Formatter.formats
|
25
|
+
option 'context', 'Additional lines context in output',
|
26
|
+
options: ['-C', '--context LINES_NUMBER'], default: 1, value_parse: lambda(&:to_i)
|
27
|
+
option 'test_command', 'Test command that generates SimpleCov',
|
28
|
+
options: '--test-command COMMAND', default: 'COVERAGE=true bundle exec rake test'
|
29
|
+
option 'simplecov_file', 'SimpleCov results file', options: '--simplecov-file PATH', default: 'autodetect'
|
30
|
+
option 'relevant_files', 'Relevant files shell filename globing: https://ruby-doc.org/core-3.1.1/File.html#method-c-fnmatch',
|
31
|
+
options: '--relevant-files', default: '{{bin,exe,exec}/*,{app,lib}/**/*.{rake,rb},Rakefile}'
|
32
|
+
option 'debug', 'Get some insights', options: '--debug', default: false, value_parse: ->(_value) { true }
|
7
33
|
|
8
34
|
def initialize
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
35
|
+
define_options
|
36
|
+
parse_config
|
37
|
+
end
|
38
|
+
|
39
|
+
def parse_cli(args) = parser.parse!(args)
|
40
|
+
def options_values = options.to_h { |name, option| [name.to_sym, option.value] }
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def define_options
|
45
|
+
self.class.options.each do |name, description, options, default, allowed_values, value_parse|
|
46
|
+
self.options[name] = Option.new(name, description, options, default, allowed_values, value_parse)
|
47
|
+
end
|
16
48
|
end
|
49
|
+
|
50
|
+
def parse_config
|
51
|
+
return unless File.exist?(CONFIG_FILE)
|
52
|
+
|
53
|
+
args = File.readlines(CONFIG_FILE).map(&:strip)
|
54
|
+
parse_cli(args)
|
55
|
+
end
|
56
|
+
|
57
|
+
def parser
|
58
|
+
@parser ||=
|
59
|
+
OptionParser.new do |parser|
|
60
|
+
parser_header(parser)
|
61
|
+
options.each_value { |option| option.on_parser(parser) }
|
62
|
+
parser_footer(parser)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def parser_header(parser) = parser.banner = 'Usage: uncov [options]'
|
67
|
+
|
68
|
+
def parser_footer(parser)
|
69
|
+
parser.on('-v', '--version', 'Show version') do
|
70
|
+
puts "uncov #{Uncov::VERSION} by Michal Papis <mpapis@gmail.com>"
|
71
|
+
throw :exit, 0
|
72
|
+
end
|
73
|
+
parser.on('-h', '--help', 'Print this help') do
|
74
|
+
puts parser
|
75
|
+
puts "uncov #{Uncov::VERSION} by Michal Papis <mpapis@gmail.com>"
|
76
|
+
throw :exit, 0
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def options = @options ||= {}
|
17
81
|
end
|
@@ -9,11 +9,7 @@ class Uncov::Finder::FileSystem
|
|
9
9
|
private
|
10
10
|
|
11
11
|
def all_files
|
12
|
-
Uncov.configuration.relevant_files.
|
13
|
-
Dir
|
14
|
-
.glob(expresion, Uncov::Configuration::FILE_MATCH_FLAGS, base: Uncov.configuration.path)
|
15
|
-
.select { |f| File.file?(f) }
|
16
|
-
end
|
12
|
+
Dir.glob(Uncov.configuration.relevant_files, Uncov::Configuration::FILE_MATCH_FLAGS).select { |f| File.file?(f) }
|
17
13
|
end
|
18
14
|
|
19
15
|
def lines_proc(file_name) = -> { cache(file_name) { read_lines(file_name) } }
|
@@ -7,13 +7,11 @@ module Uncov::Finder::GitBase
|
|
7
7
|
protected
|
8
8
|
|
9
9
|
def relevant_file?(path)
|
10
|
-
Uncov.configuration.relevant_files
|
11
|
-
File.fnmatch?(pattern, path, Uncov::Configuration::FILE_MATCH_FLAGS)
|
12
|
-
end
|
10
|
+
File.fnmatch?(Uncov.configuration.relevant_files, path, Uncov::Configuration::FILE_MATCH_FLAGS)
|
13
11
|
end
|
14
12
|
|
15
13
|
def open_repo
|
16
|
-
::Git.open(
|
14
|
+
::Git.open('.')
|
17
15
|
rescue ArgumentError => e
|
18
16
|
raise Uncov::NotGitRepoError, Uncov.configuration.path if e.message.end_with?(' is not in a git working tree')
|
19
17
|
|
@@ -19,24 +19,26 @@ module Uncov::Finder::GitDiff
|
|
19
19
|
def changed_lines(file_diff)
|
20
20
|
GitDiffParser.parse(file_diff.patch).flat_map do |patch|
|
21
21
|
patch.changed_lines.map do |changed_line|
|
22
|
-
|
22
|
+
next unless changed_line.content[0] == '+'
|
23
|
+
|
24
|
+
[changed_line.number, nil]
|
23
25
|
end
|
24
26
|
end.compact.to_h
|
25
27
|
end
|
26
28
|
|
27
29
|
def git_diff
|
28
30
|
repo = open_repo
|
29
|
-
|
30
|
-
case
|
31
|
+
git_target =
|
32
|
+
case target
|
31
33
|
when 'HEAD'
|
32
|
-
|
34
|
+
target
|
33
35
|
else
|
34
|
-
repo.branches[
|
36
|
+
repo.branches[target] or raise Uncov::NotGitBranchError, target
|
35
37
|
end
|
36
38
|
|
37
|
-
repo.diff(
|
39
|
+
repo.diff(git_target)
|
38
40
|
end
|
39
41
|
|
40
|
-
def
|
42
|
+
def target = Uncov.configuration.target
|
41
43
|
end
|
42
44
|
end
|
@@ -5,7 +5,7 @@ require 'json'
|
|
5
5
|
# collect coverage information, regenerates report if any trigger_files are newer then the report
|
6
6
|
module Uncov::Finder::SimpleCov
|
7
7
|
class << self
|
8
|
-
def files(trigger_files
|
8
|
+
def files(trigger_files)
|
9
9
|
regenerate_report if requires_regeneration?(trigger_files)
|
10
10
|
raise_on_missing_coverage_path!
|
11
11
|
coverage.transform_values { |file_coverage| covered_lines(file_coverage) }
|
@@ -14,6 +14,7 @@ module Uncov::Finder::SimpleCov
|
|
14
14
|
private
|
15
15
|
|
16
16
|
def requires_regeneration?(trigger_files)
|
17
|
+
return true unless coverage_path
|
17
18
|
return true unless File.exist?(coverage_path)
|
18
19
|
return false if trigger_files.empty?
|
19
20
|
|
@@ -21,10 +22,14 @@ module Uncov::Finder::SimpleCov
|
|
21
22
|
trigger_files.any? { |file_name| File.exist?(file_name) && File.mtime(file_name) > coverage_path_mtime }
|
22
23
|
end
|
23
24
|
|
24
|
-
def regenerate_report
|
25
|
+
def regenerate_report
|
26
|
+
system(Uncov.configuration.test_command, exception: true)
|
27
|
+
rescue RuntimeError
|
28
|
+
raise Uncov::FailedToGenerateReport
|
29
|
+
end
|
25
30
|
|
26
31
|
def coverage
|
27
|
-
root_path = "#{File.absolute_path(
|
32
|
+
root_path = "#{File.absolute_path('.')}/"
|
28
33
|
parsed = JSON.parse(File.read(coverage_path))
|
29
34
|
coverage = parsed['coverage'] || parsed.values.max_by { |suite| suite['timestamp'] }['coverage']
|
30
35
|
coverage.transform_keys { |key| key.delete_prefix(root_path) }
|
@@ -32,24 +37,22 @@ module Uncov::Finder::SimpleCov
|
|
32
37
|
|
33
38
|
def covered_lines(file_coverage)
|
34
39
|
file_coverage['lines'].each_with_index.filter_map do |coverage, line_index|
|
35
|
-
[line_index + 1, coverage.positive?] if coverage
|
40
|
+
[line_index + 1, coverage.positive?] if coverage.is_a?(Integer)
|
36
41
|
end.to_h
|
37
42
|
end
|
38
43
|
|
39
44
|
def coverage_path
|
40
|
-
if Uncov.configuration.
|
41
|
-
%w[coverage/coverage.json coverage/.resultset.json]
|
42
|
-
.map { |coverage_path| File.join(Uncov.configuration.path, coverage_path) }
|
43
|
-
.find { |path| File.exist?(path) }
|
45
|
+
if Uncov.configuration.simplecov_file == 'autodetect'
|
46
|
+
%w[coverage/coverage.json coverage/.resultset.json].find { |path| File.exist?(path) }
|
44
47
|
else
|
45
|
-
|
48
|
+
Uncov.configuration.simplecov_file
|
46
49
|
end
|
47
50
|
end
|
48
51
|
|
49
52
|
def raise_on_missing_coverage_path!
|
50
|
-
return if File.exist?(coverage_path)
|
53
|
+
return if coverage_path && File.exist?(coverage_path)
|
51
54
|
|
52
|
-
raise Uncov::AutodetectSimpleCovPathError if Uncov.configuration.
|
55
|
+
raise Uncov::AutodetectSimpleCovPathError if Uncov.configuration.simplecov_file == 'autodetect'
|
53
56
|
|
54
57
|
raise Uncov::MissingSimpleCovReport, coverage_path
|
55
58
|
end
|
data/lib/uncov/finder.rb
CHANGED
@@ -8,9 +8,10 @@ class Uncov::Finder
|
|
8
8
|
def git_file?(file_name) = git_files[file_name]
|
9
9
|
def git_file_names = git_files.keys
|
10
10
|
def git_diff_file_names = git_diff_files.keys
|
11
|
-
def git_diff_file_line?(file_name, line_number) = git_diff_files.
|
11
|
+
def git_diff_file_line?(file_name, line_number) = git_diff_files[file_name].key?(line_number)
|
12
12
|
def git_diff_file_lines(file_name) = git_diff_files[file_name]
|
13
13
|
def file_system_file_line(file_name, line_number) = file_system_files[file_name]&.call&.dig(line_number)
|
14
|
+
def file_system_file_lines(file_name) = file_system_files[file_name]&.call
|
14
15
|
def no_cov_file_line?(file_name, line_number) = no_cov_files[file_name]&.call&.dig(line_number)
|
15
16
|
def simple_cov_file_line?(file_name, line_number) = simple_cov_files.dig(file_name, line_number)
|
16
17
|
|
@@ -5,6 +5,8 @@ require 'forwardable'
|
|
5
5
|
|
6
6
|
# print report to terminal with colors
|
7
7
|
class Uncov::Formatter::Terminal
|
8
|
+
include Uncov::Cache
|
9
|
+
|
8
10
|
attr_reader :report
|
9
11
|
|
10
12
|
def initialize(report)
|
@@ -12,29 +14,54 @@ class Uncov::Formatter::Terminal
|
|
12
14
|
end
|
13
15
|
|
14
16
|
def output
|
15
|
-
if report.covered?
|
16
|
-
puts 'All changed files have 100% test coverage!'.green
|
17
|
-
return
|
18
|
-
end
|
19
|
-
|
20
17
|
puts "Found #{report.uncovered_files.size} files with uncovered changes:".yellow
|
21
18
|
output_files
|
22
|
-
puts
|
19
|
+
puts
|
20
|
+
puts format('Overall coverage of changes: %.2f%%', report.coverage).yellow
|
23
21
|
end
|
24
22
|
|
25
|
-
def output_files
|
23
|
+
def output_files
|
24
|
+
report.uncovered_files.each do |file_coverage|
|
25
|
+
output_file(file_coverage)
|
26
|
+
end
|
27
|
+
end
|
26
28
|
|
27
29
|
def output_file(file_coverage)
|
30
|
+
puts
|
31
|
+
output_file_header(file_coverage)
|
32
|
+
max = number_length(file_coverage)
|
33
|
+
file_coverage.display_lines.each do |line|
|
34
|
+
output_line(line, max)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def output_file_header(file_coverage)
|
28
39
|
puts format(
|
29
|
-
|
40
|
+
'%<name>s -> %<coverage>.2f%% (%<covered_lines>d / %<relevant_lines>d) changes covered, uncovered lines:',
|
30
41
|
name: file_coverage.file_name,
|
31
|
-
coverage: file_coverage.coverage
|
42
|
+
coverage: file_coverage.coverage,
|
43
|
+
covered_lines: file_coverage.covered_lines.count,
|
44
|
+
relevant_lines: file_coverage.relevant_lines.count
|
32
45
|
).yellow
|
33
|
-
|
34
|
-
|
35
|
-
|
46
|
+
end
|
47
|
+
|
48
|
+
def output_line(line, max)
|
49
|
+
if line.uncov?
|
50
|
+
puts format_line(line, max).red
|
51
|
+
elsif line.context
|
52
|
+
puts format_line(line, max).green
|
53
|
+
else
|
54
|
+
# :nocov:
|
55
|
+
raise 'unknown display line' # unreachable code
|
56
|
+
# :nocov:
|
36
57
|
end
|
37
58
|
end
|
38
59
|
|
39
|
-
def
|
60
|
+
def format_line(line, max)
|
61
|
+
format("%#{max}d: %s", line.number, line.content)
|
62
|
+
end
|
63
|
+
|
64
|
+
def number_length(file_coverage)
|
65
|
+
file_coverage.display_lines.last.number.to_s.length
|
66
|
+
end
|
40
67
|
end
|
data/lib/uncov/formatter.rb
CHANGED
@@ -3,12 +3,26 @@
|
|
3
3
|
# chose formater to output the report
|
4
4
|
module Uncov::Formatter
|
5
5
|
class << self
|
6
|
+
def formats = %w[terminal]
|
7
|
+
|
6
8
|
def output(report)
|
9
|
+
if report.files.empty?
|
10
|
+
return puts 'No files to report.'.green
|
11
|
+
elsif !report.uncov?
|
12
|
+
return puts "All changed files(#{report.files.count}) have 100% test coverage!".green
|
13
|
+
end
|
14
|
+
|
15
|
+
output_report(report)
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def output_report(report)
|
7
21
|
case Uncov.configuration.output_format
|
8
|
-
when
|
22
|
+
when 'terminal'
|
9
23
|
Uncov::Formatter::Terminal.new(report).output
|
10
24
|
else
|
11
|
-
raise UnsupportedFormatterError, Uncov.configuration.output_format
|
25
|
+
raise Uncov::UnsupportedFormatterError, Uncov.configuration.output_format
|
12
26
|
end
|
13
27
|
end
|
14
28
|
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# calculate context for important lines,
|
4
|
+
# @return [Integer] only added context lines matching all_line_numbers and not in important_line_numbers
|
5
|
+
module Uncov::Report::Context
|
6
|
+
class << self
|
7
|
+
def calculate(all_line_numbers, important_line_number, context)
|
8
|
+
context_line_numbers = {}
|
9
|
+
important_line_number.each do |line_number|
|
10
|
+
(1..context).to_a.each do |offset|
|
11
|
+
context_line_numbers[line_number - offset] = true
|
12
|
+
context_line_numbers[line_number + offset] = true
|
13
|
+
end
|
14
|
+
end
|
15
|
+
(context_line_numbers.keys.sort & all_line_numbers) - important_line_number
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -3,28 +3,59 @@
|
|
3
3
|
# report only files lines from the diff
|
4
4
|
module Uncov::Report::DiffLines
|
5
5
|
class << self
|
6
|
-
def files(
|
7
|
-
|
6
|
+
def files(finder)
|
7
|
+
finder.git_diff_file_names.map do |file_name|
|
8
8
|
Uncov::Report::File.new(
|
9
9
|
file_name:,
|
10
10
|
git: true,
|
11
|
-
lines: lines(
|
11
|
+
lines: lines(finder, file_name)
|
12
12
|
)
|
13
13
|
end
|
14
14
|
end
|
15
15
|
|
16
16
|
private
|
17
17
|
|
18
|
-
def lines(
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
)
|
18
|
+
def lines(finder, file_name)
|
19
|
+
lines_hash = git_diff_files_lines(finder, file_name)
|
20
|
+
add_context(finder, file_name, lines_hash)
|
21
|
+
lines_hash.sort.to_h.values
|
22
|
+
end
|
23
|
+
|
24
|
+
def git_diff_files_lines(finder, file_name)
|
25
|
+
finder.git_diff_file_lines(file_name).keys.to_h do |line_number|
|
26
|
+
[line_number, new_line(finder, file_name, line_number)]
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def add_context(finder, file_name, lines_hash)
|
31
|
+
line_numbers =
|
32
|
+
lines_hash.filter_map do |line_number, line|
|
33
|
+
line_number if line.uncov?
|
34
|
+
end
|
35
|
+
all_line_numbers = finder.file_system_file_lines(file_name).keys
|
36
|
+
context_line_numbers = Uncov::Report::Context.calculate(all_line_numbers, line_numbers, Uncov.configuration.context)
|
37
|
+
context_line_numbers.each do |line_number|
|
38
|
+
context_line(finder, file_name, lines_hash, line_number)
|
27
39
|
end
|
28
40
|
end
|
41
|
+
|
42
|
+
def context_line(finder, file_name, lines_hash, line_number)
|
43
|
+
if lines_hash.key?(line_number)
|
44
|
+
lines_hash[line_number].context = true
|
45
|
+
else
|
46
|
+
lines_hash[line_number] = new_line(finder, file_name, line_number, context: true)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def new_line(finder, file_name, line_number, context: false)
|
51
|
+
Uncov::Report::File::Line.new(
|
52
|
+
number: line_number,
|
53
|
+
content: finder.file_system_file_line(file_name, line_number),
|
54
|
+
no_cov: finder.no_cov_file_line?(file_name, line_number),
|
55
|
+
simple_cov: finder.simple_cov_file_line?(file_name, line_number),
|
56
|
+
git_diff: finder.git_diff_file_line?(file_name, line_number),
|
57
|
+
context:
|
58
|
+
)
|
59
|
+
end
|
29
60
|
end
|
30
61
|
end
|
@@ -1,9 +1,16 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# represents file line coverage in report
|
4
|
-
class Uncov::Report::File
|
5
|
-
|
6
|
-
|
7
|
-
|
4
|
+
class Uncov::Report::File::Line < Uncov::Struct.new(:number, :content, :simple_cov, :no_cov, :context, :git_diff)
|
5
|
+
def uncov?
|
6
|
+
simple_cov == false && !no_cov
|
7
|
+
end
|
8
|
+
|
9
|
+
def display?
|
10
|
+
uncov? || context
|
11
|
+
end
|
12
|
+
|
13
|
+
def relevant?
|
14
|
+
!no_cov
|
8
15
|
end
|
9
16
|
end
|
data/lib/uncov/report/file.rb
CHANGED
@@ -1,17 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# represents file coverage in report
|
4
|
-
class Uncov::Report::File
|
4
|
+
class Uncov::Report::File < Uncov::Struct.new(:file_name, :lines, :git)
|
5
5
|
include Uncov::Cache
|
6
6
|
|
7
|
-
attr_reader :file_name, :lines, :git
|
8
|
-
|
9
|
-
def initialize(file_name:, lines:, git:)
|
10
|
-
@file_name = file_name
|
11
|
-
@lines = lines
|
12
|
-
@git = git
|
13
|
-
end
|
14
|
-
|
15
7
|
def coverage
|
16
8
|
cache(:coverage) do
|
17
9
|
if relevant_lines.count.zero?
|
@@ -22,9 +14,31 @@ class Uncov::Report::File
|
|
22
14
|
end
|
23
15
|
end
|
24
16
|
|
25
|
-
def
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
def
|
17
|
+
def uncov?
|
18
|
+
uncov_lines.any?
|
19
|
+
end
|
20
|
+
|
21
|
+
def uncov_lines
|
22
|
+
cache(:uncov_lines) do
|
23
|
+
lines.select(&:uncov?)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def covered_lines
|
28
|
+
cache(:covered_lines) do
|
29
|
+
lines.reject(&:uncov?)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def display_lines
|
34
|
+
cache(:display_lines) do
|
35
|
+
lines.select(&:display?)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def relevant_lines
|
40
|
+
cache(:relevant_lines) do
|
41
|
+
lines.select(&:relevant?)
|
42
|
+
end
|
43
|
+
end
|
30
44
|
end
|
data/lib/uncov/report.rb
CHANGED
@@ -1,22 +1,45 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require_relative 'cache'
|
4
|
+
require_relative 'struct'
|
4
5
|
|
5
6
|
# calculated coverage report for configured report type
|
6
|
-
class Uncov::Report
|
7
|
+
class Uncov::Report < Uncov::Struct.new(:files)
|
7
8
|
include Uncov::Cache
|
8
9
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
10
|
+
class << self
|
11
|
+
def types
|
12
|
+
%w[diff_lines]
|
13
|
+
end
|
14
|
+
|
15
|
+
def build
|
16
|
+
files =
|
17
|
+
case Uncov.configuration.report
|
18
|
+
when 'diff_lines'
|
19
|
+
finder = Uncov::Finder.new(:git_diff)
|
20
|
+
Uncov::Report::DiffLines.files(finder)
|
21
|
+
end
|
22
|
+
new(files:)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def uncovered_files
|
27
|
+
cache(:uncovered_files) do
|
28
|
+
files.select(&:uncov?)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def coverage
|
33
|
+
cache(:coverage) do
|
34
|
+
if files.empty?
|
35
|
+
100.0
|
36
|
+
else
|
37
|
+
files.sum(&:coverage) / files.size
|
15
38
|
end
|
16
39
|
end
|
17
40
|
end
|
18
41
|
|
19
|
-
def
|
20
|
-
|
21
|
-
|
42
|
+
def uncov?
|
43
|
+
uncovered_files.any?
|
44
|
+
end
|
22
45
|
end
|
data/lib/uncov/struct.rb
ADDED
data/lib/uncov/version.rb
CHANGED
data/lib/uncov.rb
CHANGED
@@ -7,22 +7,31 @@ Dir["#{File.dirname(__FILE__)}/**/*.rb"]
|
|
7
7
|
# uncover missing code coverage by tests
|
8
8
|
module Uncov
|
9
9
|
class << self
|
10
|
-
|
11
|
-
|
12
|
-
def configure
|
13
|
-
self.configuration ||= Configuration.new
|
10
|
+
def configure(args = [])
|
14
11
|
yield(configuration) if block_given?
|
12
|
+
configuration.parse_cli(args) if args.any?
|
13
|
+
warn({ configuration: configuration.options_values }.inspect) if configuration.debug
|
14
|
+
nil
|
15
|
+
end
|
16
|
+
|
17
|
+
def configuration
|
18
|
+
@configuration ||= Configuration.new
|
15
19
|
end
|
16
20
|
|
17
|
-
def
|
18
|
-
|
21
|
+
def configuration_reset!
|
22
|
+
@configuration = Configuration.new
|
19
23
|
end
|
20
24
|
end
|
21
25
|
|
22
|
-
class Error < StandardError
|
26
|
+
class Error < StandardError
|
27
|
+
def inspect = "#<#{self.class}: #{message}>"
|
28
|
+
end
|
29
|
+
|
30
|
+
class ConfigurationError < Error; end
|
23
31
|
class GitError < Error; end
|
24
32
|
class SimpleCovError < Error; end
|
25
33
|
class FormatterError < Error; end
|
34
|
+
class OptionValueNotAllowed < ConfigurationError; end
|
26
35
|
|
27
36
|
class NotGitRepoError < GitError
|
28
37
|
attr_reader :path
|
@@ -38,6 +47,10 @@ module Uncov
|
|
38
47
|
def message = "Target branch #{target_branch.inspect} not found locally or in remote"
|
39
48
|
end
|
40
49
|
|
50
|
+
class FailedToGenerateReport < SimpleCovError
|
51
|
+
def message = cause.message
|
52
|
+
end
|
53
|
+
|
41
54
|
class MissingSimpleCovReport < SimpleCovError
|
42
55
|
attr_reader :coverage_path
|
43
56
|
|
@@ -46,7 +59,7 @@ module Uncov
|
|
46
59
|
end
|
47
60
|
|
48
61
|
class AutodetectSimpleCovPathError < SimpleCovError
|
49
|
-
def
|
62
|
+
def message = 'Could not autodetect coverage report path'
|
50
63
|
end
|
51
64
|
|
52
65
|
class UnsupportedFormatterError < FormatterError
|
@@ -56,6 +69,3 @@ module Uncov
|
|
56
69
|
def message = "#{output_format.inspect} is not a supported formatter"
|
57
70
|
end
|
58
71
|
end
|
59
|
-
|
60
|
-
# Initialize with defaults
|
61
|
-
Uncov.configure
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: uncov
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Michał Papis
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-
|
11
|
+
date: 2025-05-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: colorize
|
@@ -66,6 +66,20 @@ dependencies:
|
|
66
66
|
- - "~>"
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: '1.7'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: optparse
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
69
83
|
description: uncov compares your current branch with a target branch, identifies changed
|
70
84
|
files, and reports on test coverage for those changes
|
71
85
|
email:
|
@@ -76,15 +90,16 @@ extensions: []
|
|
76
90
|
extra_rdoc_files: []
|
77
91
|
files:
|
78
92
|
- CHANGELOG.md
|
79
|
-
- CHANGELOG_BLA.md
|
80
93
|
- CONTRIBUTING.md
|
81
94
|
- LICENSE.txt
|
95
|
+
- PHILOSOPHY.md
|
82
96
|
- README.md
|
83
97
|
- bin/uncov
|
84
98
|
- lib/uncov.rb
|
85
99
|
- lib/uncov/cache.rb
|
86
100
|
- lib/uncov/cli.rb
|
87
101
|
- lib/uncov/configuration.rb
|
102
|
+
- lib/uncov/configuration/option.rb
|
88
103
|
- lib/uncov/finder.rb
|
89
104
|
- lib/uncov/finder/file_system.rb
|
90
105
|
- lib/uncov/finder/git.rb
|
@@ -95,9 +110,11 @@ files:
|
|
95
110
|
- lib/uncov/formatter.rb
|
96
111
|
- lib/uncov/formatter/terminal.rb
|
97
112
|
- lib/uncov/report.rb
|
113
|
+
- lib/uncov/report/context.rb
|
98
114
|
- lib/uncov/report/diff_lines.rb
|
99
115
|
- lib/uncov/report/file.rb
|
100
116
|
- lib/uncov/report/file/line.rb
|
117
|
+
- lib/uncov/struct.rb
|
101
118
|
- lib/uncov/version.rb
|
102
119
|
homepage: https://github.com/mpapis/uncov
|
103
120
|
licenses:
|
data/CHANGELOG_BLA.md
DELETED
@@ -1,28 +0,0 @@
|
|
1
|
-
# Changelog
|
2
|
-
|
3
|
-
## 2025-04-29: [v0.2.2](https://github.com/mpapis/uncov/releases/tag/v0.2.2)
|
4
|
-
|
5
|
-
### Patch
|
6
|
-
|
7
|
-
- [#27](https://github.com/mpapis/uncov/pull/27): Fix simplecov resultset parsing bug, by [@mpapis](https://github.com/mpapis)
|
8
|
-
|
9
|
-
|
10
|
-
## 2025-04-29: [v0.2.1](https://github.com/mpapis/uncov/releases/tag/v0.2.1)
|
11
|
-
|
12
|
-
### Patch
|
13
|
-
|
14
|
-
- [#26](https://github.com/mpapis/uncov/pull/26): Fix simplecov resultset parsing bug, by [@mpapis](https://github.com/mpapis)
|
15
|
-
|
16
|
-
|
17
|
-
## 2025-04-29: [v0.2.0](https://github.com/mpapis/uncov/releases/tag/v0.2.0)
|
18
|
-
|
19
|
-
### Minor
|
20
|
-
|
21
|
-
- [#23](https://github.com/mpapis/uncov/pull/23): Remove need for bundler, by [@mpapis](https://github.com/mpapis)
|
22
|
-
|
23
|
-
|
24
|
-
## 2025-04-28: [v0.1.1](https://github.com/mpapis/uncov/releases/tag/v0.1.1)
|
25
|
-
|
26
|
-
### Minor
|
27
|
-
|
28
|
-
`uncov -r diff_lines -f termianl`, by [@mpapis](https://github.com/mpapis)
|