simple_cov-formatter-terminal 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 0037c0f75039602b73a5c02b56091392b253bbd14d297f44fe6a0a1463dfa844
4
+ data.tar.gz: a91aee34a3560e838481318fc56680682b01a3a635ca7f18ae0d83049aeea298
5
+ SHA512:
6
+ metadata.gz: 5bc1c96efcf14e290abdeca72bf850a4762239f8a01760c7db162c7cfb2418093e42c34c2fd4482379a3565b1eec292254b097e5a63a9def8dbf431703e9933b
7
+ data.tar.gz: cc5c94164de7385f4bb770017b657d548aff92e516fbc9fc580b192f2e387b190997118cc11a89460e925a7d6cbc318f165f2fa3df3a3957fd020130d6738427
@@ -0,0 +1,3 @@
1
+ ---
2
+ git: true
3
+ rubygems: true
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
data/.rubocop.yml ADDED
@@ -0,0 +1,5 @@
1
+ inherit_gem:
2
+ runger_style:
3
+ - rulesets/default.yml # gem 'rubocop'
4
+ - rulesets/performance.yml # gem 'rubocop-performance'
5
+ - rulesets/rspec.yml # gem 'rubocop-rspec'
data/CHANGELOG.md ADDED
@@ -0,0 +1,5 @@
1
+ ## [Unreleased]
2
+
3
+ ## [0.1.0] - 2022-10-07
4
+
5
+ - Initial release
data/Gemfile ADDED
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ ruby '3.2.0'
4
+
5
+ source 'https://rubygems.org'
6
+
7
+ git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
8
+
9
+ # Specify your gem's dependencies in fcom.gemspec
10
+ gemspec
11
+
12
+ group :development, :test do
13
+ gem 'bundler', require: false
14
+ gem 'pry', require: false
15
+ gem 'pry-byebug', require: false
16
+ gem 'rubocop', require: false
17
+ gem 'rubocop-performance', require: false
18
+ gem 'rubocop-rspec', require: false
19
+ gem 'runger_style', github: 'davidrunger/runger_style', require: false
20
+ end
21
+
22
+ group :test do
23
+ gem 'climate_control', require: false
24
+ gem 'codecov', require: false
25
+ gem 'rspec', require: false
26
+ gem 'simplecov', require: false
27
+ end
28
+
29
+ group :development do
30
+ gem 'rake', require: false
31
+ gem 'release_assistant', require: false, git: 'https://github.com/davidrunger/release_assistant'
32
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,131 @@
1
+ GIT
2
+ remote: https://github.com/davidrunger/release_assistant
3
+ revision: 4a6db008f26327bbee9758ddd2bad070b14d2e88
4
+ specs:
5
+ release_assistant (0.3.3.alpha)
6
+ activesupport (>= 6, < 8)
7
+ colorize (~> 0.8)
8
+ memoist (~> 0.16)
9
+ slop (~> 4.8)
10
+
11
+ GIT
12
+ remote: https://github.com/davidrunger/runger_style
13
+ revision: 4a68671e36d5734cb50331f904fb5a1a7ab9b429
14
+ specs:
15
+ runger_style (0.2.22.alpha)
16
+ rubocop (>= 1.38.0, < 2)
17
+
18
+ PATH
19
+ remote: .
20
+ specs:
21
+ simple_cov-formatter-terminal (0.1.0)
22
+ activesupport (>= 7.0.4, < 8)
23
+ memoist (>= 0.16.2, < 1)
24
+ rouge (>= 4.0.0, < 5)
25
+ rspec-core (>= 3.11.0, < 4)
26
+ simplecov (>= 0.21.2, < 1)
27
+
28
+ GEM
29
+ remote: https://rubygems.org/
30
+ specs:
31
+ activesupport (7.0.4)
32
+ concurrent-ruby (~> 1.0, >= 1.0.2)
33
+ i18n (>= 1.6, < 2)
34
+ minitest (>= 5.1)
35
+ tzinfo (~> 2.0)
36
+ ast (2.4.2)
37
+ byebug (11.1.3)
38
+ climate_control (1.2.0)
39
+ codecov (0.6.0)
40
+ simplecov (>= 0.15, < 0.22)
41
+ coderay (1.1.3)
42
+ colorize (0.8.1)
43
+ concurrent-ruby (1.1.10)
44
+ diff-lcs (1.5.0)
45
+ docile (1.4.0)
46
+ i18n (1.12.0)
47
+ concurrent-ruby (~> 1.0)
48
+ json (2.6.3)
49
+ memoist (0.16.2)
50
+ method_source (1.0.0)
51
+ minitest (5.16.3)
52
+ parallel (1.22.1)
53
+ parser (3.2.0.0)
54
+ ast (~> 2.4.1)
55
+ pry (0.14.1)
56
+ coderay (~> 1.1)
57
+ method_source (~> 1.0)
58
+ pry-byebug (3.10.1)
59
+ byebug (~> 11.0)
60
+ pry (>= 0.13, < 0.15)
61
+ rainbow (3.1.1)
62
+ rake (13.0.6)
63
+ regexp_parser (2.6.1)
64
+ rexml (3.2.5)
65
+ rouge (4.0.0)
66
+ rspec (3.11.0)
67
+ rspec-core (~> 3.11.0)
68
+ rspec-expectations (~> 3.11.0)
69
+ rspec-mocks (~> 3.11.0)
70
+ rspec-core (3.11.0)
71
+ rspec-support (~> 3.11.0)
72
+ rspec-expectations (3.11.1)
73
+ diff-lcs (>= 1.2.0, < 2.0)
74
+ rspec-support (~> 3.11.0)
75
+ rspec-mocks (3.11.1)
76
+ diff-lcs (>= 1.2.0, < 2.0)
77
+ rspec-support (~> 3.11.0)
78
+ rspec-support (3.11.1)
79
+ rubocop (1.42.0)
80
+ json (~> 2.3)
81
+ parallel (~> 1.10)
82
+ parser (>= 3.1.2.1)
83
+ rainbow (>= 2.2.2, < 4.0)
84
+ regexp_parser (>= 1.8, < 3.0)
85
+ rexml (>= 3.2.5, < 4.0)
86
+ rubocop-ast (>= 1.24.1, < 2.0)
87
+ ruby-progressbar (~> 1.7)
88
+ unicode-display_width (>= 1.4.0, < 3.0)
89
+ rubocop-ast (1.24.1)
90
+ parser (>= 3.1.1.0)
91
+ rubocop-performance (1.15.0)
92
+ rubocop (>= 1.7.0, < 2.0)
93
+ rubocop-ast (>= 0.4.0)
94
+ rubocop-rspec (2.13.2)
95
+ rubocop (~> 1.33)
96
+ ruby-progressbar (1.11.0)
97
+ simplecov (0.21.2)
98
+ docile (~> 1.1)
99
+ simplecov-html (~> 0.11)
100
+ simplecov_json_formatter (~> 0.1)
101
+ simplecov-html (0.12.3)
102
+ simplecov_json_formatter (0.1.4)
103
+ slop (4.9.3)
104
+ tzinfo (2.0.5)
105
+ concurrent-ruby (~> 1.0)
106
+ unicode-display_width (2.4.2)
107
+
108
+ PLATFORMS
109
+ ruby
110
+
111
+ DEPENDENCIES
112
+ bundler
113
+ climate_control
114
+ codecov
115
+ pry
116
+ pry-byebug
117
+ rake
118
+ release_assistant!
119
+ rspec
120
+ rubocop
121
+ rubocop-performance
122
+ rubocop-rspec
123
+ runger_style!
124
+ simple_cov-formatter-terminal!
125
+ simplecov
126
+
127
+ RUBY VERSION
128
+ ruby 3.2.0p0
129
+
130
+ BUNDLED WITH
131
+ 2.3.23
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2022 David Runger
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,123 @@
1
+ # SimpleCov::Formatter::Terminal
2
+
3
+ ![image](https://user-images.githubusercontent.com/8197963/195740768-e2cbb99d-7cf2-42bf-a178-2f78eb653dd3.png)
4
+
5
+ ## Installation
6
+
7
+ *Note:* This gem depends upon RSpec. If you aren't using RSpec to run your tests, it won't work!
8
+
9
+ Add the gem to your application's `Gemfile`. Because the gem is not released via RubyGems, you will
10
+ need to install it from GitHub.
11
+
12
+ ```rb
13
+ group :test do
14
+ gem 'simple_cov-formatter-terminal', github: 'davidrunger/simple_cov-formatter-terminal'
15
+ end
16
+ ```
17
+
18
+ Then execute:
19
+
20
+ ```
21
+ $ bundle install
22
+ ```
23
+
24
+ ## Usage
25
+
26
+ Add something like the following to your `spec/spec_helper.rb` file:
27
+
28
+ ```rb
29
+ require 'simplecov'
30
+ if RSpec.configuration.files_to_run.one?
31
+ require 'simple_cov/formatter/terminal'
32
+ SimpleCov.formatter = SimpleCov::Formatter::Terminal
33
+ end
34
+ SimpleCov.start
35
+ ```
36
+
37
+ **Note** that this setup only uses `SimpleCov::Formatter::Terminal` when specs are run with a single
38
+ spec file (e.g. `bin/rspec spec/models/user_spec.rb`) and not when multiple specs are executed (e.g.
39
+ when simply running `bin/rspec` without any argument).
40
+
41
+ ### Modifying the `spec_to_app_file_map`
42
+
43
+ `SimpleCov::Formatter::Terminal` has a default hash that is used to map spec files to their
44
+ corresponding application file.
45
+
46
+ For gems (determined from whether there is a `.gemspec` file at the top level of the project), the
47
+ default mapping is very simple:
48
+
49
+ ```rb
50
+ SimpleCov::Formatter::Terminal.config.spec_to_app_file_map = {
51
+ %r{\Aspec/} => 'lib/',
52
+ }
53
+ ```
54
+
55
+ This is the default used for non-gems; it is optimized for Rails applications using Active Admin:
56
+
57
+ ```rb
58
+ SimpleCov::Formatter::Terminal.config.spec_to_app_file_map = {
59
+ %r{\Aspec/lib/} => 'lib/',
60
+ %r{\Aspec/controllers/admin/(.*)_controller_spec.rb} => 'app/admin/\1.rb',
61
+ %r{
62
+ \Aspec/
63
+ (
64
+ actions|
65
+ channels|
66
+ controllers|
67
+ decorators|
68
+ helpers|
69
+ mailboxes|
70
+ mailers|
71
+ models|
72
+ policies|
73
+ serializers|
74
+ views|
75
+ workers
76
+ )
77
+ /
78
+ }x => 'app/\1/',
79
+ }
80
+ ```
81
+
82
+ If needed for your application, you can add to this hash in your `spec/spec_helper.rb`, e.g.:
83
+
84
+ ```rb
85
+ SimpleCov::Formatter::Terminal.config.spec_to_app_file_map.merge!(
86
+ %r{\Aspec/my_special/directory_structure/} => 'my_special/app_directory/',
87
+ )
88
+ ```
89
+
90
+ Or you can override the default mapping completely:
91
+
92
+ ```rb
93
+ SimpleCov::Formatter::Terminal.config.spec_to_app_file_map = {
94
+ %r{\Aspec/my_special/directory_structure/} => 'my_special/app_directory/',
95
+ }
96
+ ```
97
+
98
+ ## Branch coverage
99
+
100
+ If you enable branch coverage for SimpleCov (via `enable_coverage(:branch)` within your
101
+ `SimpleCov.start` block; read more [here][simple-cov-branch-coverage]), then branch coverage info
102
+ will be included in SimpleCov::Formatter::Terminal's output. Missed branches will be indicated with
103
+ white text on a red background at the end of the line, and the line number will be printed in red
104
+ text on a yellow background, as seen in the screenshot at the top of this README (where a `then`
105
+ branch is not covered on line 18).
106
+
107
+ [simple-cov-branch-coverage]: https://github.com/simplecov-ruby/simplecov#branch-coverage-ruby--25
108
+
109
+ ## Development
110
+
111
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `bin/rspec` to run
112
+ the tests. You can also run `bin/console` for an interactive prompt that will allow you to
113
+ experiment.
114
+
115
+ ## Contributing
116
+
117
+ Bug reports and pull requests are welcome on GitHub at
118
+ https://github.com/davidrunger/simple_cov-formatter-terminal.
119
+
120
+ ## License
121
+
122
+ The gem is available as open source under the terms of the [MIT
123
+ License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/gem_tasks'
4
+ require 'rspec/core/rake_task'
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ require 'rubocop/rake_task'
9
+
10
+ RuboCop::RakeTask.new
11
+
12
+ task default: %i[spec rubocop]
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'memoist'
4
+
5
+ module SimpleCov::Formatter::Terminal::BranchCoverage
6
+ extend Memoist
7
+
8
+ private
9
+
10
+ memoize \
11
+ def uncovered_branches(sourcefile)
12
+ sourcefile.branches.
13
+ reject(&:covered?).
14
+ reject do |branch|
15
+ line = sourcefile.lines[branch.start_line - 1]
16
+ source_code = line.src
17
+ line.coverage == 0 || source_code.match?(/# :nocov-(#{branch.type}):/)
18
+ end
19
+ end
20
+
21
+ def missed_branch_info(line, sourcefile)
22
+ uncovered_branches(sourcefile).
23
+ select { _1.start_line == line.line_number }.
24
+ map { _1.type.to_s }.
25
+ join(', ').
26
+ presence
27
+ end
28
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SimpleCov::Formatter::Terminal::ColorPrinting
4
+ # rubocop:disable Metrics/CyclomaticComplexity
5
+ def color(message, color)
6
+ case color
7
+ when :white then "\e[0;37;49m#{message}\e[0m"
8
+ when :red then "\e[0;31m#{message}\e[0m"
9
+ when :green then "\e[1;32;49m#{message}\e[0m"
10
+ when :yellow then "\e[0;33m#{message}\e[0m"
11
+ when :white_on_green then "\e[1;39;102m#{message}\e[0m"
12
+ when :red_on_yellow then "\e[0;31;103m#{message}\e[0m"
13
+ when :white_on_red then "\e[1;37;41m#{message}\e[0m"
14
+ else raise("Unknown color format '#{color}'.")
15
+ end
16
+ end
17
+ # rubocop:enable Metrics/CyclomaticComplexity
18
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative './spec_to_app_mapping'
4
+ require 'memoist'
5
+
6
+ class SimpleCov::Formatter::Terminal::Config
7
+ extend Memoist
8
+ include SimpleCov::Formatter::Terminal::SpecToAppMapping
9
+
10
+ attr_accessor :spec_to_app_file_map, :unmappable_spec_regexes
11
+
12
+ def initialize
13
+ @spec_to_app_file_map =
14
+ SimpleCov::Formatter::Terminal::SpecToAppMapping.default_spec_to_app_map
15
+ @unmappable_spec_regexes =
16
+ SimpleCov::Formatter::Terminal::SpecToAppMapping::DEFAULT_UNMAPPABLE_SPEC_REGEXES
17
+ end
18
+
19
+ memoize \
20
+ def write_target_info_file?
21
+ ENV.fetch('SIMPLECOV_WRITE_TARGET_TO_FILE', nil) == '1'
22
+ end
23
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'memoist'
4
+
5
+ class SimpleCov::Formatter::Terminal::FileDeterminer
6
+ extend Memoist
7
+
8
+ memoize \
9
+ def executed_spec_file
10
+ if executed_spec_files.size == 1
11
+ executed_spec_files.first
12
+ else
13
+ raise(<<~ERROR)
14
+ Multiple spec files were executed (#{executed_spec_files}), but
15
+ SimpleCov::Formatter::Terminal only works when a single spec file is executed.
16
+ ERROR
17
+ end
18
+ end
19
+
20
+ memoize \
21
+ def targeted_application_file
22
+ env_variable_file = ENV.fetch('SIMPLECOV_TARGET_FILE', nil)
23
+ if !env_variable_file.nil?
24
+ puts('Determined targeted application file from SIMPLECOV_TARGET_FILE environment variable!!')
25
+ return env_variable_file
26
+ end
27
+
28
+ return nil if unmappable_spec_regexes.any? { executed_spec_file.match?(_1) }
29
+
30
+ spec_to_app_file_map.lazy.filter_map do |spec_file_regex, app_file_substitution|
31
+ if executed_spec_file.match?(spec_file_regex)
32
+ executed_spec_file.sub(spec_file_regex, app_file_substitution)
33
+ end
34
+ end.first&.sub(/_spec\.rb\z/, '.rb') ||
35
+ raise("Could not map executed spec file #{executed_spec_file} to application file!")
36
+ end
37
+
38
+ memoize \
39
+ def executed_spec_files
40
+ SimpleCov::Formatter::Terminal::RSpecIntegration.executed_spec_files
41
+ end
42
+
43
+ memoize \
44
+ def spec_to_app_file_map
45
+ SimpleCov::Formatter::Terminal.config.spec_to_app_file_map ||
46
+ SimpleCov::Formatter::Terminal::SpecToAppMapping.default_spec_to_app_map
47
+ end
48
+
49
+ memoize \
50
+ def unmappable_spec_regexes
51
+ SimpleCov::Formatter::Terminal.config.unmappable_spec_regexes
52
+ end
53
+ end
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'memoist'
4
+
5
+ class SimpleCov::Formatter::Terminal::LinePrinter
6
+ extend Memoist
7
+ include SimpleCov::Formatter::Terminal::BranchCoverage
8
+ include SimpleCov::Formatter::Terminal::ColorPrinting
9
+
10
+ def initialize(targeted_application_file)
11
+ @targeted_application_file = targeted_application_file
12
+ end
13
+
14
+ def colored_line(line, sourcefile)
15
+ colored_source_code = syntax_highlighted_source_lines[line.line_number - 1]
16
+ line_number = line.line_number
17
+
18
+ case
19
+ when line.skipped?
20
+ numbered_line_output(line_number, :white, colored_source_code)
21
+ when line.coverage == 0
22
+ numbered_line_output(line_number, :white_on_red, colored_source_code)
23
+ when (missed_branch_info = missed_branch_info(line, sourcefile))
24
+ numbered_line_output(line_number, :red_on_yellow, colored_source_code, missed_branch_info)
25
+ when line.coverage.nil?
26
+ numbered_line_output(line_number, :white, colored_source_code, missed_branch_info)
27
+ else
28
+ numbered_line_output(line_number, :green, colored_source_code, missed_branch_info)
29
+ end
30
+ end
31
+
32
+ # rubocop:disable Style/StringConcatenation
33
+ def numbered_line_output(line_number, color, source_code, missed_branch_info = nil)
34
+ colored_space =
35
+ case color
36
+ when :red_on_yellow, :white_on_red then color(' ', color)
37
+ else color(' ', :white_on_green)
38
+ end
39
+
40
+ line_number_string =
41
+ if SimpleCov::Formatter::Terminal.config.write_target_info_file?
42
+ ":::#{line_number}".rjust(6, ' ')
43
+ else
44
+ line_number.to_s.rjust(3, ' ')
45
+ end
46
+
47
+ output =
48
+ colored_space +
49
+ color(line_number_string, color) +
50
+ colored_space +
51
+ ' ' +
52
+ source_code
53
+
54
+ if missed_branch_info
55
+ output << " #{color(missed_branch_info, :white_on_red)}"
56
+ end
57
+
58
+ output
59
+ end
60
+ # rubocop:enable Style/StringConcatenation
61
+
62
+ memoize \
63
+ def syntax_highlighted_source_lines
64
+ source = File.read(@targeted_application_file)
65
+ formatter = Rouge::Formatters::Terminal256.new(Rouge::Themes::Base16.mode(:dark).new)
66
+ lexer = Rouge::Lexers::Ruby.new
67
+ highlighted_source = formatter.format(lexer.lex(source))
68
+ highlighted_source.split("\n")
69
+ end
70
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SimpleCov::Formatter::Terminal::RSpecIntegration
4
+ class << self
5
+ attr_accessor :executed_spec_files, :failure_occurred
6
+
7
+ def setup_rspec
8
+ return if @rspec_is_set_up # :nocov-else:
9
+
10
+ # We can't easily test this, since we use this library in its own RSpec tests,
11
+ # so we'd be setting it up twice if we tested it, which would be a bit of a problem.
12
+ # :nocov:
13
+ _setup_rspec
14
+ @rspec_is_set_up = true
15
+ # :nocov:
16
+ end
17
+
18
+ def failure_occurred?
19
+ !!@failure_occurred
20
+ end
21
+
22
+ private
23
+
24
+ def _setup_rspec
25
+ RSpec.configure do |config|
26
+ config.before(:suite) do
27
+ SimpleCov::Formatter::Terminal::RSpecIntegration.failure_occurred = false
28
+ end
29
+
30
+ config.after(:suite) do
31
+ examples = RSpec.world.filtered_examples.values.flatten
32
+ SimpleCov::Formatter::Terminal::RSpecIntegration.failure_occurred =
33
+ examples.any?(&:exception)
34
+ SimpleCov::Formatter::Terminal::RSpecIntegration.executed_spec_files =
35
+ examples.map { _1.file_path.delete_prefix('./') }.uniq
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SimpleCov::Formatter::Terminal::RailsAwareness
4
+ class << self
5
+ def rails?
6
+ !!defined?(Rails)
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,127 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative './branch_coverage'
4
+ require_relative './color_printing'
5
+ require_relative './line_printer'
6
+ require_relative './target_file_writer'
7
+
8
+ class SimpleCov::Formatter::Terminal::ResultPrinter
9
+ extend Forwardable
10
+ extend Memoist
11
+ include SimpleCov::Formatter::Terminal::BranchCoverage
12
+ include SimpleCov::Formatter::Terminal::ColorPrinting
13
+
14
+ def_delegators(:@file_determiner, :executed_spec_file, :targeted_application_file)
15
+
16
+ def initialize(file_determiner)
17
+ @file_determiner = file_determiner
18
+ end
19
+
20
+ def print_coverage_info(result)
21
+ sourcefile = result.files.find { _1.filename.end_with?(targeted_application_file) }
22
+ force_coverage = ENV.fetch('SIMPLECOV_FORCE_DETAILS', nil) == '1'
23
+
24
+ if sourcefile.nil?
25
+ print_no_coverage_info_found
26
+ elsif failure_occurred? && !force_coverage
27
+ print_coverage_summary(sourcefile, 'Not showing detailed coverage because an example failed.')
28
+ elsif sourcefile.covered_percent < 100 || uncovered_branches(sourcefile).any? || force_coverage
29
+ print_coverage_details(sourcefile)
30
+ else
31
+ print_coverage_summary(sourcefile)
32
+ end
33
+ end
34
+
35
+ def print_coverage_summary(sourcefile, log_addendum = nil)
36
+ summary = +"-- Coverage for #{targeted_application_file} --\n"
37
+ summary << "Line coverage: #{colorized_coverage(sourcefile.covered_percent)}"
38
+ if SimpleCov.branch_coverage?
39
+ summary << ' '
40
+ summary << <<~LOG
41
+ | Uncovered branches: #{colorized_uncovered_branches(uncovered_branches(sourcefile).size)}
42
+ LOG
43
+ else
44
+ summary << "\n"
45
+ end
46
+ summary << log_addendum if log_addendum
47
+ puts(summary)
48
+ end
49
+
50
+ def print_coverage_details(sourcefile)
51
+ if SimpleCov::Formatter::Terminal.config.write_target_info_file?
52
+ target_file_writer.write_target_info_file
53
+ end
54
+
55
+ puts("---- Coverage for #{targeted_application_file} ".ljust(80, '-').rstrip)
56
+ sourcefile.lines.each do |line|
57
+ puts(line_printer.colored_line(line, sourcefile))
58
+ end
59
+ puts(<<~LOG.squish)
60
+ ----
61
+ Line coverage: #{colorized_coverage(sourcefile.covered_percent)}
62
+ |
63
+ Uncovered branches: #{colorized_uncovered_branches(uncovered_branches(sourcefile).size)}
64
+ ----
65
+ LOG
66
+ end
67
+
68
+ def print_no_coverage_info_found
69
+ puts(<<~LOG.squish)
70
+ No code coverage info was found for "#{targeted_application_file}". Try stopping and
71
+ disabling `spring`, if you are using it, and then rerun the spec.
72
+ LOG
73
+ end
74
+
75
+ def print_info_for_no_executed_specs
76
+ puts('Not showing test coverage details because no specs were executed successfully.')
77
+ end
78
+
79
+ def print_info_for_undetermined_application_target
80
+ puts(<<~LOG.squish)
81
+ Not showing test coverage details because "#{executed_spec_file}" cannot
82
+ be mapped to a single application file.
83
+ LOG
84
+ puts(<<~LOG.squish)
85
+ Tip: you can specify a file manually via a SIMPLECOV_TARGET_FILE environment variable.
86
+ LOG
87
+ end
88
+
89
+ def print_info_for_nonexistent_application_target
90
+ puts(<<~LOG.squish)
91
+ Cannot show code coverage. Looked for application file "#{targeted_application_file}",
92
+ but it does not exist.
93
+ LOG
94
+ end
95
+
96
+ private
97
+
98
+ def failure_occurred?
99
+ SimpleCov::Formatter::Terminal::RSpecIntegration.failure_occurred?
100
+ end
101
+
102
+ memoize \
103
+ def target_file_writer
104
+ SimpleCov::Formatter::Terminal::TargetFileWriter.new(targeted_application_file)
105
+ end
106
+
107
+ def colorized_coverage(covered_percent)
108
+ case
109
+ when covered_percent >= 100 then color("#{covered_percent.round(2)}%", :green)
110
+ when covered_percent >= 80 then color("#{covered_percent.round(2)}%", :yellow)
111
+ else color("#{covered_percent.round(2)}%", :red)
112
+ end
113
+ end
114
+
115
+ def colorized_uncovered_branches(num_uncovered_branches)
116
+ case num_uncovered_branches
117
+ when 0 then color(num_uncovered_branches.to_s, :green)
118
+ when (1..3) then color(num_uncovered_branches.to_s, :yellow)
119
+ else color(num_uncovered_branches.to_s, :red)
120
+ end
121
+ end
122
+
123
+ memoize \
124
+ def line_printer
125
+ SimpleCov::Formatter::Terminal::LinePrinter.new(targeted_application_file)
126
+ end
127
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative './rails_awareness'
4
+
5
+ module SimpleCov::Formatter::Terminal::SpecToAppMapping
6
+ # rubocop:disable Lint/OrAssignmentToConstant
7
+ DEFAULT_UNMAPPABLE_SPEC_REGEXES ||= [
8
+ %r{\Aspec/features/},
9
+ ].freeze
10
+ SPEC_TO_GEM_DEFAULT_MAP ||= {
11
+ %r{\Aspec/} => 'lib/',
12
+ }.freeze
13
+ SPEC_TO_RAILS_DEFAULT_MAP ||= {
14
+ %r{\Aspec/lib/} => 'lib/',
15
+ %r{\Aspec/controllers/admin/(.*)_controller_spec.rb} => 'app/admin/\1.rb',
16
+ %r{
17
+ \Aspec/
18
+ (
19
+ actions|
20
+ channels|
21
+ controllers|
22
+ decorators|
23
+ helpers|
24
+ mailboxes|
25
+ mailers|
26
+ models|
27
+ policies|
28
+ serializers|
29
+ views|
30
+ workers
31
+ )
32
+ /
33
+ }x => 'app/\1/',
34
+ }.freeze
35
+ # rubocop:enable Lint/OrAssignmentToConstant
36
+
37
+ class << self
38
+ def default_spec_to_app_map
39
+ # dup the maps because the maps are frozen but we want to allow the user to customize them
40
+ if SimpleCov::Formatter::Terminal::RailsAwareness.rails?
41
+ SPEC_TO_RAILS_DEFAULT_MAP.dup
42
+ else
43
+ SPEC_TO_GEM_DEFAULT_MAP.dup
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'memoist'
4
+
5
+ class SimpleCov::Formatter::Terminal::TargetFileWriter
6
+ def initialize(targeted_application_file)
7
+ @targeted_application_file = targeted_application_file
8
+ end
9
+
10
+ def write_target_info_file
11
+ directory = 'tmp/simple_cov/formatter/terminal'
12
+ FileUtils.mkdir_p(directory)
13
+ File.write("#{directory}/target.txt", "#{@targeted_application_file}\n")
14
+ end
15
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SimpleCov; end
4
+ module SimpleCov::Formatter; end
5
+
6
+ # rubocop:disable Style/StaticClass
7
+ class SimpleCov::Formatter::Terminal
8
+ VERSION = '0.1.0'
9
+ end
10
+ # rubocop:enable Style/StaticClass
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ class SimpleCov::Formatter::Terminal ; end # rubocop:disable Lint/EmptyClass
4
+
5
+ require_relative 'terminal/config'
6
+ require_relative 'terminal/file_determiner'
7
+ require_relative 'terminal/r_spec_integration'
8
+ require_relative 'terminal/result_printer'
9
+ require_relative 'terminal/version'
10
+ require 'active_support/core_ext/object/blank'
11
+ require 'active_support/core_ext/string/filters'
12
+ require 'memoist'
13
+ require 'rouge'
14
+ require 'rspec/core'
15
+ require 'simplecov'
16
+
17
+ class SimpleCov::Formatter::Terminal
18
+ extend Forwardable
19
+ extend Memoist
20
+
21
+ def_delegators(
22
+ :file_determiner,
23
+ :executed_spec_file,
24
+ :executed_spec_files,
25
+ :targeted_application_file,
26
+ )
27
+ def_delegators(
28
+ :result_printer,
29
+ :print_coverage_info,
30
+ :print_info_for_no_executed_specs,
31
+ :print_info_for_nonexistent_application_target,
32
+ :print_info_for_undetermined_application_target,
33
+ )
34
+
35
+ class << self
36
+ extend Memoist
37
+
38
+ memoize \
39
+ def config
40
+ Config.new
41
+ end
42
+ end
43
+
44
+ def format(result)
45
+ if executed_spec_files.nil?
46
+ print_info_for_no_executed_specs
47
+ elsif targeted_application_file.nil?
48
+ print_info_for_undetermined_application_target
49
+ elsif File.exist?(targeted_application_file)
50
+ print_coverage_info(result)
51
+ else
52
+ print_info_for_nonexistent_application_target
53
+ end
54
+ end
55
+
56
+ private
57
+
58
+ memoize \
59
+ def file_determiner
60
+ SimpleCov::Formatter::Terminal::FileDeterminer.new
61
+ end
62
+
63
+ memoize \
64
+ def result_printer
65
+ SimpleCov::Formatter::Terminal::ResultPrinter.new(file_determiner)
66
+ end
67
+ end
68
+
69
+ SimpleCov::Formatter::Terminal::RSpecIntegration.setup_rspec
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'lib/simple_cov/formatter/terminal/version'
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'simple_cov-formatter-terminal'
7
+ spec.version = SimpleCov::Formatter::Terminal::VERSION
8
+ spec.authors = ['David Runger']
9
+ spec.email = ['davidjrunger@gmail.com']
10
+
11
+ spec.summary = 'Print detailed code coverage info to the terminal'
12
+ spec.description = 'Print detailed code coverage info to the terminal'
13
+ spec.homepage = 'https://github.com/davidrunger/simplecov-formatter-terminal'
14
+ spec.license = 'MIT'
15
+ spec.required_ruby_version = '>= 3.2.0'
16
+
17
+ spec.metadata['homepage_uri'] = spec.homepage
18
+ spec.metadata['source_code_uri'] = 'https://github.com/davidrunger/simplecov-formatter-terminal'
19
+ spec.metadata['changelog_uri'] =
20
+ 'https://github.com/davidrunger/simplecov-formatter-terminal/blob/master/CHANGELOG.md'
21
+
22
+ # Specify which files should be added to the gem when it is released.
23
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
24
+ spec.files =
25
+ Dir.chdir(__dir__) do
26
+ `git ls-files -z`.split("\x0").reject do |f|
27
+ (f == __FILE__) || f.match(%r{\A(?:(?:bin|test|spec|features)/|\.(?:git))})
28
+ end
29
+ end
30
+ spec.bindir = 'exe'
31
+ spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
32
+ spec.require_paths = ['lib']
33
+
34
+ # Uncomment to register a new dependency of your gem
35
+ spec.add_dependency('activesupport', '>= 7.0.4', '< 8')
36
+ spec.add_dependency('memoist', '>= 0.16.2', '< 1')
37
+ spec.add_dependency('rouge', '>= 4.0.0', '< 5')
38
+ spec.add_dependency('rspec-core', '>= 3.11.0', '< 4')
39
+ spec.add_dependency('simplecov', '>= 0.21.2', '< 1')
40
+
41
+ # For more information and examples about making a new gem, check out our
42
+ # guide at: https://bundler.io/guides/creating_gem.html
43
+ spec.metadata['rubygems_mfa_required'] = 'true'
44
+ end
metadata ADDED
@@ -0,0 +1,169 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: simple_cov-formatter-terminal
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - David Runger
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2023-01-05 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activesupport
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 7.0.4
20
+ - - "<"
21
+ - !ruby/object:Gem::Version
22
+ version: '8'
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ version: 7.0.4
30
+ - - "<"
31
+ - !ruby/object:Gem::Version
32
+ version: '8'
33
+ - !ruby/object:Gem::Dependency
34
+ name: memoist
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: 0.16.2
40
+ - - "<"
41
+ - !ruby/object:Gem::Version
42
+ version: '1'
43
+ type: :runtime
44
+ prerelease: false
45
+ version_requirements: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - ">="
48
+ - !ruby/object:Gem::Version
49
+ version: 0.16.2
50
+ - - "<"
51
+ - !ruby/object:Gem::Version
52
+ version: '1'
53
+ - !ruby/object:Gem::Dependency
54
+ name: rouge
55
+ requirement: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - ">="
58
+ - !ruby/object:Gem::Version
59
+ version: 4.0.0
60
+ - - "<"
61
+ - !ruby/object:Gem::Version
62
+ version: '5'
63
+ type: :runtime
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ version: 4.0.0
70
+ - - "<"
71
+ - !ruby/object:Gem::Version
72
+ version: '5'
73
+ - !ruby/object:Gem::Dependency
74
+ name: rspec-core
75
+ requirement: !ruby/object:Gem::Requirement
76
+ requirements:
77
+ - - ">="
78
+ - !ruby/object:Gem::Version
79
+ version: 3.11.0
80
+ - - "<"
81
+ - !ruby/object:Gem::Version
82
+ version: '4'
83
+ type: :runtime
84
+ prerelease: false
85
+ version_requirements: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: 3.11.0
90
+ - - "<"
91
+ - !ruby/object:Gem::Version
92
+ version: '4'
93
+ - !ruby/object:Gem::Dependency
94
+ name: simplecov
95
+ requirement: !ruby/object:Gem::Requirement
96
+ requirements:
97
+ - - ">="
98
+ - !ruby/object:Gem::Version
99
+ version: 0.21.2
100
+ - - "<"
101
+ - !ruby/object:Gem::Version
102
+ version: '1'
103
+ type: :runtime
104
+ prerelease: false
105
+ version_requirements: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - ">="
108
+ - !ruby/object:Gem::Version
109
+ version: 0.21.2
110
+ - - "<"
111
+ - !ruby/object:Gem::Version
112
+ version: '1'
113
+ description: Print detailed code coverage info to the terminal
114
+ email:
115
+ - davidjrunger@gmail.com
116
+ executables: []
117
+ extensions: []
118
+ extra_rdoc_files: []
119
+ files:
120
+ - ".release_assistant.yml"
121
+ - ".rspec"
122
+ - ".rubocop.yml"
123
+ - CHANGELOG.md
124
+ - Gemfile
125
+ - Gemfile.lock
126
+ - LICENSE.txt
127
+ - README.md
128
+ - Rakefile
129
+ - lib/simple_cov/formatter/terminal.rb
130
+ - lib/simple_cov/formatter/terminal/branch_coverage.rb
131
+ - lib/simple_cov/formatter/terminal/color_printing.rb
132
+ - lib/simple_cov/formatter/terminal/config.rb
133
+ - lib/simple_cov/formatter/terminal/file_determiner.rb
134
+ - lib/simple_cov/formatter/terminal/line_printer.rb
135
+ - lib/simple_cov/formatter/terminal/r_spec_integration.rb
136
+ - lib/simple_cov/formatter/terminal/rails_awareness.rb
137
+ - lib/simple_cov/formatter/terminal/result_printer.rb
138
+ - lib/simple_cov/formatter/terminal/spec_to_app_mapping.rb
139
+ - lib/simple_cov/formatter/terminal/target_file_writer.rb
140
+ - lib/simple_cov/formatter/terminal/version.rb
141
+ - simple_cov-formatter-terminal.gemspec
142
+ homepage: https://github.com/davidrunger/simplecov-formatter-terminal
143
+ licenses:
144
+ - MIT
145
+ metadata:
146
+ homepage_uri: https://github.com/davidrunger/simplecov-formatter-terminal
147
+ source_code_uri: https://github.com/davidrunger/simplecov-formatter-terminal
148
+ changelog_uri: https://github.com/davidrunger/simplecov-formatter-terminal/blob/master/CHANGELOG.md
149
+ rubygems_mfa_required: 'true'
150
+ post_install_message:
151
+ rdoc_options: []
152
+ require_paths:
153
+ - lib
154
+ required_ruby_version: !ruby/object:Gem::Requirement
155
+ requirements:
156
+ - - ">="
157
+ - !ruby/object:Gem::Version
158
+ version: 3.2.0
159
+ required_rubygems_version: !ruby/object:Gem::Requirement
160
+ requirements:
161
+ - - ">="
162
+ - !ruby/object:Gem::Version
163
+ version: '0'
164
+ requirements: []
165
+ rubygems_version: 3.4.2
166
+ signing_key:
167
+ specification_version: 4
168
+ summary: Print detailed code coverage info to the terminal
169
+ test_files: []