ultra-pro-pkg 0.0.1

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.
Files changed (117) hide show
  1. checksums.yaml +7 -0
  2. data/simplecov-1.0.0/LICENSE +20 -0
  3. data/simplecov-1.0.0/README.md +1524 -0
  4. data/simplecov-1.0.0/doc/alternate-formatters.md +66 -0
  5. data/simplecov-1.0.0/doc/commercial-services.md +25 -0
  6. data/simplecov-1.0.0/doc/editor-integration.md +18 -0
  7. data/simplecov-1.0.0/exe/simplecov +11 -0
  8. data/simplecov-1.0.0/lib/minitest/simplecov_plugin.rb +171 -0
  9. data/simplecov-1.0.0/lib/simplecov/autostart.rb +11 -0
  10. data/simplecov-1.0.0/lib/simplecov/cli/clean.rb +47 -0
  11. data/simplecov-1.0.0/lib/simplecov/cli/coverage.rb +91 -0
  12. data/simplecov-1.0.0/lib/simplecov/cli/diff.rb +151 -0
  13. data/simplecov-1.0.0/lib/simplecov/cli/dotfile.rb +100 -0
  14. data/simplecov-1.0.0/lib/simplecov/cli/merge.rb +116 -0
  15. data/simplecov-1.0.0/lib/simplecov/cli/open.rb +50 -0
  16. data/simplecov-1.0.0/lib/simplecov/cli/report.rb +84 -0
  17. data/simplecov-1.0.0/lib/simplecov/cli/run.rb +36 -0
  18. data/simplecov-1.0.0/lib/simplecov/cli/serve.rb +139 -0
  19. data/simplecov-1.0.0/lib/simplecov/cli/uncovered.rb +107 -0
  20. data/simplecov-1.0.0/lib/simplecov/cli.rb +150 -0
  21. data/simplecov-1.0.0/lib/simplecov/color.rb +74 -0
  22. data/simplecov-1.0.0/lib/simplecov/combine/branches_combiner.rb +54 -0
  23. data/simplecov-1.0.0/lib/simplecov/combine/files_combiner.rb +30 -0
  24. data/simplecov-1.0.0/lib/simplecov/combine/lines_combiner.rb +45 -0
  25. data/simplecov-1.0.0/lib/simplecov/combine/methods_combiner.rb +26 -0
  26. data/simplecov-1.0.0/lib/simplecov/combine/results_combiner.rb +61 -0
  27. data/simplecov-1.0.0/lib/simplecov/combine.rb +30 -0
  28. data/simplecov-1.0.0/lib/simplecov/command_guesser.rb +78 -0
  29. data/simplecov-1.0.0/lib/simplecov/configuration/coverage.rb +171 -0
  30. data/simplecov-1.0.0/lib/simplecov/configuration/coverage_criteria.rb +156 -0
  31. data/simplecov-1.0.0/lib/simplecov/configuration/filters.rb +197 -0
  32. data/simplecov-1.0.0/lib/simplecov/configuration/formatting.rb +119 -0
  33. data/simplecov-1.0.0/lib/simplecov/configuration/ignored_entries.rb +63 -0
  34. data/simplecov-1.0.0/lib/simplecov/configuration/merging.rb +164 -0
  35. data/simplecov-1.0.0/lib/simplecov/configuration/thresholds.rb +174 -0
  36. data/simplecov-1.0.0/lib/simplecov/configuration.rb +184 -0
  37. data/simplecov-1.0.0/lib/simplecov/coverage_statistics.rb +59 -0
  38. data/simplecov-1.0.0/lib/simplecov/coverage_violations.rb +148 -0
  39. data/simplecov-1.0.0/lib/simplecov/defaults.rb +60 -0
  40. data/simplecov-1.0.0/lib/simplecov/deprecation.rb +45 -0
  41. data/simplecov-1.0.0/lib/simplecov/directive.rb +162 -0
  42. data/simplecov-1.0.0/lib/simplecov/exit_codes/exit_code_handling.rb +35 -0
  43. data/simplecov-1.0.0/lib/simplecov/exit_codes/maximum_coverage_drop_check.rb +45 -0
  44. data/simplecov-1.0.0/lib/simplecov/exit_codes/maximum_overall_coverage_check.rb +45 -0
  45. data/simplecov-1.0.0/lib/simplecov/exit_codes/minimum_coverage_by_file_check.rb +44 -0
  46. data/simplecov-1.0.0/lib/simplecov/exit_codes/minimum_coverage_by_group_check.rb +41 -0
  47. data/simplecov-1.0.0/lib/simplecov/exit_codes/minimum_overall_coverage_check.rb +70 -0
  48. data/simplecov-1.0.0/lib/simplecov/exit_codes.rb +18 -0
  49. data/simplecov-1.0.0/lib/simplecov/exit_handling.rb +158 -0
  50. data/simplecov-1.0.0/lib/simplecov/file_list.rb +164 -0
  51. data/simplecov-1.0.0/lib/simplecov/filter.rb +139 -0
  52. data/simplecov-1.0.0/lib/simplecov/formatter/base.rb +101 -0
  53. data/simplecov-1.0.0/lib/simplecov/formatter/html_formatter/public/application.css +1 -0
  54. data/simplecov-1.0.0/lib/simplecov/formatter/html_formatter/public/application.js +18 -0
  55. data/simplecov-1.0.0/lib/simplecov/formatter/html_formatter/public/favicon_green.png +0 -0
  56. data/simplecov-1.0.0/lib/simplecov/formatter/html_formatter/public/favicon_red.png +0 -0
  57. data/simplecov-1.0.0/lib/simplecov/formatter/html_formatter/public/favicon_yellow.png +0 -0
  58. data/simplecov-1.0.0/lib/simplecov/formatter/html_formatter/public/index.html +56 -0
  59. data/simplecov-1.0.0/lib/simplecov/formatter/html_formatter.rb +79 -0
  60. data/simplecov-1.0.0/lib/simplecov/formatter/json_formatter/errors_formatter.rb +84 -0
  61. data/simplecov-1.0.0/lib/simplecov/formatter/json_formatter/result_hash_formatter.rb +128 -0
  62. data/simplecov-1.0.0/lib/simplecov/formatter/json_formatter/source_file_formatter.rb +99 -0
  63. data/simplecov-1.0.0/lib/simplecov/formatter/json_formatter.rb +87 -0
  64. data/simplecov-1.0.0/lib/simplecov/formatter/multi_formatter.rb +31 -0
  65. data/simplecov-1.0.0/lib/simplecov/formatter/simple_formatter.rb +23 -0
  66. data/simplecov-1.0.0/lib/simplecov/formatter.rb +14 -0
  67. data/simplecov-1.0.0/lib/simplecov/last_run.rb +35 -0
  68. data/simplecov-1.0.0/lib/simplecov/lines_classifier.rb +60 -0
  69. data/simplecov-1.0.0/lib/simplecov/load_global_config.rb +13 -0
  70. data/simplecov-1.0.0/lib/simplecov/no_defaults.rb +4 -0
  71. data/simplecov-1.0.0/lib/simplecov/parallel_adapters/base.rb +61 -0
  72. data/simplecov-1.0.0/lib/simplecov/parallel_adapters/generic.rb +44 -0
  73. data/simplecov-1.0.0/lib/simplecov/parallel_adapters/parallel_tests.rb +93 -0
  74. data/simplecov-1.0.0/lib/simplecov/parallel_adapters.rb +83 -0
  75. data/simplecov-1.0.0/lib/simplecov/parallel_coordination.rb +129 -0
  76. data/simplecov-1.0.0/lib/simplecov/process.rb +37 -0
  77. data/simplecov-1.0.0/lib/simplecov/profiles/bundler_filter.rb +5 -0
  78. data/simplecov-1.0.0/lib/simplecov/profiles/hidden_filter.rb +5 -0
  79. data/simplecov-1.0.0/lib/simplecov/profiles/rails.rb +32 -0
  80. data/simplecov-1.0.0/lib/simplecov/profiles/root_filter.rb +11 -0
  81. data/simplecov-1.0.0/lib/simplecov/profiles/strict.rb +32 -0
  82. data/simplecov-1.0.0/lib/simplecov/profiles/test_frameworks.rb +5 -0
  83. data/simplecov-1.0.0/lib/simplecov/profiles.rb +64 -0
  84. data/simplecov-1.0.0/lib/simplecov/result/missing_source_files_reporter.rb +49 -0
  85. data/simplecov-1.0.0/lib/simplecov/result/source_file_builder.rb +51 -0
  86. data/simplecov-1.0.0/lib/simplecov/result.rb +183 -0
  87. data/simplecov-1.0.0/lib/simplecov/result_adapter.rb +92 -0
  88. data/simplecov-1.0.0/lib/simplecov/result_merger/legacy_format_adapter.rb +28 -0
  89. data/simplecov-1.0.0/lib/simplecov/result_merger/resultset_file.rb +38 -0
  90. data/simplecov-1.0.0/lib/simplecov/result_merger/resultset_store.rb +50 -0
  91. data/simplecov-1.0.0/lib/simplecov/result_merger.rb +162 -0
  92. data/simplecov-1.0.0/lib/simplecov/result_processing.rb +179 -0
  93. data/simplecov-1.0.0/lib/simplecov/simulate_coverage.rb +75 -0
  94. data/simplecov-1.0.0/lib/simplecov/source_file/branch.rb +82 -0
  95. data/simplecov-1.0.0/lib/simplecov/source_file/branch_builder.rb +114 -0
  96. data/simplecov-1.0.0/lib/simplecov/source_file/builder_context.rb +28 -0
  97. data/simplecov-1.0.0/lib/simplecov/source_file/line.rb +77 -0
  98. data/simplecov-1.0.0/lib/simplecov/source_file/line_builder.rb +43 -0
  99. data/simplecov-1.0.0/lib/simplecov/source_file/method.rb +52 -0
  100. data/simplecov-1.0.0/lib/simplecov/source_file/method_builder.rb +58 -0
  101. data/simplecov-1.0.0/lib/simplecov/source_file/ruby_data_parser.rb +88 -0
  102. data/simplecov-1.0.0/lib/simplecov/source_file/skip_chunks.rb +75 -0
  103. data/simplecov-1.0.0/lib/simplecov/source_file/source_loader.rb +63 -0
  104. data/simplecov-1.0.0/lib/simplecov/source_file/statistics.rb +57 -0
  105. data/simplecov-1.0.0/lib/simplecov/source_file.rb +189 -0
  106. data/simplecov-1.0.0/lib/simplecov/static_coverage_extractor/method_collector.rb +55 -0
  107. data/simplecov-1.0.0/lib/simplecov/static_coverage_extractor/visitor.rb +171 -0
  108. data/simplecov-1.0.0/lib/simplecov/static_coverage_extractor.rb +110 -0
  109. data/simplecov-1.0.0/lib/simplecov/useless_results_remover.rb +27 -0
  110. data/simplecov-1.0.0/lib/simplecov/version.rb +5 -0
  111. data/simplecov-1.0.0/lib/simplecov-html.rb +4 -0
  112. data/simplecov-1.0.0/lib/simplecov.rb +255 -0
  113. data/simplecov-1.0.0/lib/simplecov_json_formatter.rb +4 -0
  114. data/simplecov-1.0.0/schemas/coverage-v1.0.schema.json +306 -0
  115. data/simplecov-1.0.0/schemas/coverage.schema.json +306 -0
  116. data/ultra-pro-pkg.gemspec +12 -0
  117. metadata +156 -0
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SimpleCov
4
+ module ExitCodes
5
+ # Fails when the overall (project-wide) coverage for any criterion is
6
+ # below the configured minimum.
7
+ class MinimumOverallCoverageCheck
8
+ def initialize(result, minimum_coverage)
9
+ @result = result
10
+ @minimum_coverage = minimum_coverage
11
+ end
12
+
13
+ def failing?
14
+ violations.any?
15
+ end
16
+
17
+ def report
18
+ violations.each { |violation| report_violation(violation) }
19
+ end
20
+
21
+ def exit_code
22
+ SimpleCov::ExitCodes::MINIMUM_COVERAGE
23
+ end
24
+
25
+ private
26
+
27
+ WORST_FILES_LIMIT = 5
28
+ private_constant :WORST_FILES_LIMIT
29
+
30
+ def violations
31
+ @violations ||= SimpleCov::CoverageViolations.minimum_overall(@result, @minimum_coverage)
32
+ end
33
+
34
+ def report_violation(violation)
35
+ criterion = violation.fetch(:criterion)
36
+ actual = violation.fetch(:actual)
37
+ warn format(
38
+ "%<criterion>s coverage (%<actual>s) is below the expected minimum coverage (%<expected>.2f%%).",
39
+ criterion: criterion.capitalize,
40
+ actual: SimpleCov::Color.colorize_percent(actual),
41
+ expected: violation.fetch(:expected)
42
+ )
43
+ report_worst_files(criterion)
44
+ end
45
+
46
+ def report_worst_files(criterion)
47
+ worst = worst_files_for(criterion)
48
+ return if worst.empty?
49
+
50
+ warn " Lowest-coverage files (#{criterion}):"
51
+ worst.each do |path, percent|
52
+ warn format(
53
+ " %<percent>s %<path>s",
54
+ percent: SimpleCov::Color.colorize_percent(percent, format("%6.2f%%", percent)),
55
+ path: path
56
+ )
57
+ end
58
+ end
59
+
60
+ def worst_files_for(criterion)
61
+ stats_key = SimpleCov.coverage_statistics_key(criterion)
62
+ with_stats = @result.files.filter_map do |source_file|
63
+ stats = source_file.coverage_statistics[stats_key]
64
+ [source_file.project_filename, stats.percent] if stats
65
+ end
66
+ with_stats.sort_by { |_path, percent| percent }.first(WORST_FILES_LIMIT)
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SimpleCov
4
+ module ExitCodes
5
+ SUCCESS = 0
6
+ EXCEPTION = 1
7
+ MINIMUM_COVERAGE = 2
8
+ MAXIMUM_COVERAGE_DROP = 3
9
+ MAXIMUM_COVERAGE = 4
10
+ end
11
+ end
12
+
13
+ require_relative "exit_codes/exit_code_handling"
14
+ require_relative "exit_codes/maximum_coverage_drop_check"
15
+ require_relative "exit_codes/maximum_overall_coverage_check"
16
+ require_relative "exit_codes/minimum_coverage_by_file_check"
17
+ require_relative "exit_codes/minimum_coverage_by_group_check"
18
+ require_relative "exit_codes/minimum_overall_coverage_check"
@@ -0,0 +1,158 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "English"
4
+
5
+ # `at_exit` orchestration: post-suite report generation, threshold
6
+ # checks, deferral when a sibling subprocess already wrote a fresher
7
+ # report, and exit-status propagation.
8
+ module SimpleCov
9
+ class << self
10
+ # @api private
11
+ CoverageLimits = Data.define(
12
+ :minimum_coverage,
13
+ :minimum_coverage_by_file,
14
+ :minimum_coverage_by_file_overrides,
15
+ :minimum_coverage_by_group,
16
+ :maximum_coverage,
17
+ :maximum_coverage_drop
18
+ )
19
+
20
+ def at_exit_behavior
21
+ # If we are in a different process than called start, don't interfere.
22
+ return if SimpleCov.pid != Process.pid
23
+
24
+ # If Coverage is no longer running (e.g. someone manually stopped it
25
+ # or a test consumed the result) then don't run exit tasks.
26
+ return unless Coverage.running?
27
+
28
+ # Stand down when we'd only clobber a fresher report. See
29
+ # `defer_to_existing_report?` and issue #581.
30
+ return if defer_to_existing_report?
31
+
32
+ SimpleCov.run_exit_tasks!
33
+ end
34
+
35
+ # Returns true when our process has no coverage data to contribute
36
+ # (after the resultset merge) and a newer report already exists on
37
+ # disk. Typically fires when `SimpleCov.start` ran in a parent
38
+ # process — e.g. a Rakefile or Rails' `Bundler.require` — that
39
+ # shelled out to the test runner. See issue #581.
40
+ def defer_to_existing_report?
41
+ return false unless existing_report_newer_than_us?
42
+
43
+ res = result
44
+ empty = res.nil? || res.files.empty?
45
+ warn_about_deferred_report if empty
46
+ empty
47
+ end
48
+
49
+ def existing_report_newer_than_us?
50
+ return false unless process_start_time
51
+
52
+ last_run_path = File.join(coverage_path, ".last_run.json")
53
+ File.exist?(last_run_path) && File.mtime(last_run_path) > process_start_time
54
+ end
55
+
56
+ def warn_about_deferred_report
57
+ return unless print_errors
58
+
59
+ warn SimpleCov::Color.colorize(
60
+ "Skipping SimpleCov report — this process tracked no application code and a newer " \
61
+ "report already exists at #{coverage_path}. This usually means SimpleCov.start ran in a " \
62
+ "parent process (e.g. a Rakefile or Rails' Bundler.require) that shelled out to the test " \
63
+ "runner. See https://github.com/simplecov-ruby/simplecov/issues/581.",
64
+ :yellow
65
+ )
66
+ end
67
+
68
+ # @api private — called from the at_exit block.
69
+ def run_exit_tasks!
70
+ error_exit_status = exit_status_from_exception
71
+
72
+ at_exit.call
73
+
74
+ exit_and_report_previous_error(error_exit_status) if previous_error?(error_exit_status)
75
+ process_results_and_report_error if ready_to_process_results?
76
+ end
77
+
78
+ # @api private — returns the exit status from the exit exception.
79
+ def exit_status_from_exception
80
+ @exit_exception = $ERROR_INFO
81
+ return nil unless @exit_exception
82
+
83
+ if @exit_exception.is_a?(SystemExit)
84
+ @exit_exception.status
85
+ else
86
+ SimpleCov::ExitCodes::EXCEPTION
87
+ end
88
+ end
89
+
90
+ # @api private — strict boolean so rspec-mocks 4's predicate matcher
91
+ # accepts it. test_unit sets status 0 on success, so SUCCESS must
92
+ # also be treated as "not a previous error".
93
+ def previous_error?(error_exit_status)
94
+ !!(error_exit_status && error_exit_status != SimpleCov::ExitCodes::SUCCESS)
95
+ end
96
+
97
+ # @api private
98
+ def exit_and_report_previous_error(exit_status)
99
+ if print_errors
100
+ warn SimpleCov::Color.colorize(
101
+ "Stopped processing SimpleCov as a previous error not related to SimpleCov has been detected",
102
+ :yellow
103
+ )
104
+ end
105
+ Kernel.exit(exit_status)
106
+ end
107
+
108
+ # @api private — the process that owns final merge processing is the
109
+ # only one that reports against thresholds, and only when its
110
+ # `wait_for_other_processes` confirmed every sibling reported.
111
+ # When the wait times out, the merged total is partial and
112
+ # comparing it against `minimum_coverage` / `maximum_coverage`
113
+ # would surface a spurious "below minimum" violation about the
114
+ # missing slice rather than a real shortfall.
115
+ def ready_to_process_results?
116
+ merge_finalization_owner? && final_result_process? && result? &&
117
+ (collating_result? || parallel_results_complete?)
118
+ end
119
+
120
+ def process_results_and_report_error
121
+ exit_status = process_result(result)
122
+
123
+ # Force exit with stored status (see github issue #5)
124
+ return unless exit_status.positive?
125
+
126
+ if print_errors
127
+ warn SimpleCov::Color.colorize(
128
+ "SimpleCov failed with exit #{exit_status} due to a coverage related error", :red
129
+ )
130
+ end
131
+ Kernel.exit exit_status
132
+ end
133
+
134
+ # @api private — `exit_status = SimpleCov.process_result(SimpleCov.result)`.
135
+ def process_result(result)
136
+ result_exit_status = result_exit_status(result)
137
+ write_last_run(result) if result_exit_status == SimpleCov::ExitCodes::SUCCESS
138
+ result_exit_status
139
+ end
140
+
141
+ def result_exit_status(result)
142
+ ExitCodes::ExitCodeHandling.call(result, coverage_limits: build_coverage_limits)
143
+ end
144
+
145
+ private
146
+
147
+ def build_coverage_limits
148
+ CoverageLimits.new(
149
+ minimum_coverage: minimum_coverage,
150
+ minimum_coverage_by_file: minimum_coverage_by_file,
151
+ minimum_coverage_by_file_overrides: minimum_coverage_by_file_overrides,
152
+ minimum_coverage_by_group: minimum_coverage_by_group,
153
+ maximum_coverage: maximum_coverage,
154
+ maximum_coverage_drop: maximum_coverage_drop
155
+ )
156
+ end
157
+ end
158
+ end
@@ -0,0 +1,164 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SimpleCov
4
+ # An array of SimpleCov SourceFile instances with additional collection helper
5
+ # methods for calculating coverage across them etc.
6
+ class FileList
7
+ include Enumerable
8
+ extend Forwardable
9
+
10
+ def_delegators :@files,
11
+ # For Enumerable
12
+ :each,
13
+ # also delegating methods implemented in Enumerable as they have
14
+ # custom Array implementations which are presumably better/more
15
+ # resource efficient
16
+ :size, :map, :count,
17
+ # surprisingly not in Enumerable
18
+ :empty?, :length,
19
+ # still act like we're kinda an array
20
+ :to_a, :to_ary
21
+
22
+ def initialize(files)
23
+ @files = files
24
+ end
25
+
26
+ # The per-criterion coverage statistics across all files. With no argument
27
+ # returns the `{line:, branch:, method:}` Hash; pass a criterion symbol
28
+ # (`:line` / `:branch` / `:method`) to get that one CoverageStatistics.
29
+ def coverage_statistics(criterion = nil)
30
+ @coverage_statistics ||= compute_coverage_statistics
31
+ criterion ? @coverage_statistics[criterion] : @coverage_statistics
32
+ end
33
+
34
+ def coverage_statistics_by_file
35
+ @coverage_statistics_by_file ||= compute_coverage_statistics_by_file
36
+ end
37
+
38
+ # Returns the count of lines that have coverage
39
+ def covered_lines
40
+ coverage_statistics[:line]&.covered
41
+ end
42
+
43
+ # Returns the count of lines that have been missed
44
+ def missed_lines
45
+ coverage_statistics[:line]&.missed
46
+ end
47
+
48
+ # Returns the count of lines that are not relevant for coverage
49
+ def never_lines
50
+ return 0.0 if empty?
51
+
52
+ sum { |f| f.never_lines.size }
53
+ end
54
+
55
+ # Returns the count of skipped lines
56
+ def skipped_lines
57
+ return 0.0 if empty?
58
+
59
+ sum { |f| f.skipped_lines.size }
60
+ end
61
+
62
+ # Computes the coverage based upon lines covered and lines missed for each file
63
+ # Returns an array with all coverage percentages
64
+ def covered_percentages
65
+ map(&:covered_percent)
66
+ end
67
+
68
+ # Finds the least covered file and returns that file's name
69
+ def least_covered_file
70
+ min_by(&:covered_percent).filename
71
+ end
72
+
73
+ # Returns the overall amount of relevant lines of code across all files in this list
74
+ def lines_of_code
75
+ coverage_statistics[:line]&.total
76
+ end
77
+
78
+ # The coverage across all files in percent, for the given criterion (line
79
+ # by default). Returns nil if the criterion was not measured.
80
+ # @return [Float, nil]
81
+ def covered_percent(criterion = :line)
82
+ coverage_statistics(criterion)&.percent
83
+ end
84
+
85
+ # The strength (average hits per relevant unit) for the given criterion
86
+ # (line by default).
87
+ # @return [Float, nil]
88
+ def covered_strength(criterion = :line)
89
+ coverage_statistics(criterion)&.strength
90
+ end
91
+
92
+ # Return total count of branches in all files
93
+ def total_branches
94
+ coverage_statistics[:branch]&.total
95
+ end
96
+
97
+ # Return total count of covered branches
98
+ def covered_branches
99
+ coverage_statistics[:branch]&.covered
100
+ end
101
+
102
+ # Return total count of covered branches
103
+ def missed_branches
104
+ coverage_statistics[:branch]&.missed
105
+ end
106
+
107
+ def branch_covered_percent
108
+ coverage_statistics[:branch]&.percent
109
+ end
110
+
111
+ # Return total count of methods in all files
112
+ def total_methods
113
+ coverage_statistics[:method]&.total
114
+ end
115
+
116
+ # Return total count of covered methods
117
+ def covered_methods
118
+ coverage_statistics[:method]&.covered
119
+ end
120
+
121
+ # Return total count of missed methods
122
+ def missed_methods
123
+ coverage_statistics[:method]&.missed
124
+ end
125
+
126
+ def method_covered_percent
127
+ coverage_statistics[:method]&.percent
128
+ end
129
+
130
+ private
131
+
132
+ # Seed the result hash with one entry per criterion the user
133
+ # enabled — so an empty FileList (e.g. a group with no files) still
134
+ # yields the right shape — then fold each file's stats into the
135
+ # matching bucket. `SourceFile#coverage_statistics` always reports
136
+ # all three criteria; FileList is the layer that filters to the
137
+ # enabled set so disabled criteria don't surface in totals, JSON,
138
+ # or the HTML report.
139
+ def compute_coverage_statistics_by_file
140
+ seed = enabled_criteria_for_reporting.to_h { |criterion| [criterion, []] }
141
+ @files.each_with_object(seed) do |file, together|
142
+ file.coverage_statistics.each do |criterion, stats|
143
+ together[criterion] << stats if together.key?(criterion)
144
+ end
145
+ end
146
+ end
147
+
148
+ def compute_coverage_statistics
149
+ coverage_statistics_by_file.transform_values { |stats| CoverageStatistics.from(stats) }
150
+ end
151
+
152
+ # `:line` (or its `:oneshot_line` synonym) is reported when either
153
+ # criterion is enabled; the JRuby-gated branch/method criteria are
154
+ # reported when they pass their own engine-support check.
155
+ def enabled_criteria_for_reporting
156
+ criteria = []
157
+ criteria << :line if SimpleCov.coverage_criterion_enabled?(:line) ||
158
+ SimpleCov.coverage_criterion_enabled?(:oneshot_line)
159
+ criteria << :branch if SimpleCov.branch_coverage?
160
+ criteria << :method if SimpleCov.method_coverage?
161
+ criteria
162
+ end
163
+ end
164
+ end
@@ -0,0 +1,139 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SimpleCov
4
+ #
5
+ # Base filter class. Inherit from this to create custom filters,
6
+ # and overwrite the matches?(source_file) instance method
7
+ #
8
+ # # A sample class that rejects all source files.
9
+ # class StupidFilter < SimpleCov::Filter
10
+ # def matches?(source_file)
11
+ # false
12
+ # end
13
+ # end
14
+ #
15
+ class Filter
16
+ attr_reader :filter_argument
17
+
18
+ def initialize(filter_argument)
19
+ @filter_argument = filter_argument
20
+ end
21
+
22
+ def matches?(_source_file)
23
+ raise NotImplementedError, "The base filter class is not intended for direct use"
24
+ end
25
+
26
+ def self.build_filter(filter_argument)
27
+ return filter_argument if filter_argument.is_a?(SimpleCov::Filter)
28
+
29
+ class_for_argument(filter_argument).new(filter_argument)
30
+ end
31
+
32
+ def self.class_for_argument(filter_argument)
33
+ filter_classes_by_argument_type.find { |type, _| filter_argument.is_a?(type) }&.last ||
34
+ raise(SimpleCov::ConfigurationError, "You have provided an unrecognized filter type")
35
+ end
36
+
37
+ def self.filter_classes_by_argument_type
38
+ @filter_classes_by_argument_type ||= {
39
+ String => SimpleCov::StringFilter,
40
+ Regexp => SimpleCov::RegexFilter,
41
+ Array => SimpleCov::ArrayFilter,
42
+ Proc => SimpleCov::BlockFilter
43
+ }.freeze
44
+ end
45
+ private_class_method :filter_classes_by_argument_type
46
+ end
47
+
48
+ # Filter that matches when the source file's project path contains the
49
+ # configured string at a path-segment boundary.
50
+ class StringFilter < SimpleCov::Filter
51
+ # Returns true when the given source file's filename matches the
52
+ # string configured when initializing this Filter with StringFilter.new('somestring').
53
+ # Matching is path-segment-aware: the argument must appear immediately after a "/"
54
+ # and be followed by "/" or end-of-string, so "lib" matches "/lib/foo.rb" but not
55
+ # "/app/models/library.rb".
56
+ def matches?(source_file)
57
+ source_file.project_filename.match?(segment_pattern)
58
+ end
59
+
60
+ private
61
+
62
+ def segment_pattern
63
+ @segment_pattern ||= compute_segment_pattern
64
+ end
65
+
66
+ def compute_segment_pattern
67
+ normalized = filter_argument.delete_prefix("/")
68
+ escaped = Regexp.escape(normalized)
69
+ boundary = '(?:\A|/)'
70
+
71
+ if normalized.include?(".")
72
+ # Filename pattern (e.g. "test.rb" matches "faked_test.rb"): allow
73
+ # substring match within the last path segment, anchored to a
74
+ # segment boundary.
75
+ %r{#{boundary}[^/]*#{escaped}}
76
+ elsif normalized.end_with?("/")
77
+ # Trailing slash signals directory-only matching.
78
+ /#{boundary}#{escaped}/
79
+ else
80
+ # Directory or path: require a segment-boundary match so "lib"
81
+ # matches "lib/" but not "library/".
82
+ %r{#{boundary}#{escaped}(?=[/.]|\z)}
83
+ end
84
+ end
85
+ end
86
+
87
+ # Filter that matches when the source file's project path matches the
88
+ # configured Regexp.
89
+ class RegexFilter < SimpleCov::Filter
90
+ # Returns true when the given source file's filename matches the
91
+ # regex configured when initializing this Filter with RegexFilter.new(/someregex/).
92
+ # Uses `Regexp#match?` so the predicate returns a real boolean — `=~`
93
+ # would return the match position (an Integer or nil), which trips
94
+ # rspec-mocks 4's stricter predicate-matcher type check.
95
+ def matches?(source_file)
96
+ filter_argument.match?(source_file.project_filename)
97
+ end
98
+ end
99
+
100
+ # Filter that matches when the configured block returns truthy for the
101
+ # source file.
102
+ class BlockFilter < SimpleCov::Filter
103
+ # Returns true if the block given when initializing this filter with BlockFilter.new {|src_file| ... }
104
+ # returns true for the given source file.
105
+ def matches?(source_file)
106
+ filter_argument.call(source_file)
107
+ end
108
+ end
109
+
110
+ # Filter that matches when the source file's project path matches the
111
+ # configured shell glob (e.g. "lib/**/*.rb"). Used by `cover` and
112
+ # `skip` when callers want glob semantics instead of the substring
113
+ # match of `StringFilter`.
114
+ class GlobFilter < SimpleCov::Filter
115
+ def matches?(source_file)
116
+ File.fnmatch?(filter_argument, source_file.project_filename, File::FNM_PATHNAME | File::FNM_EXTGLOB)
117
+ end
118
+ end
119
+
120
+ # Filter that matches when any of its component filters (built from the
121
+ # array's elements) match the source file.
122
+ class ArrayFilter < SimpleCov::Filter
123
+ def initialize(filter_argument)
124
+ filter_objects = filter_argument.map do |arg|
125
+ Filter.build_filter(arg)
126
+ end
127
+
128
+ super(filter_objects)
129
+ end
130
+
131
+ # Returns true if any of the filters in the array match the given source file.
132
+ # Configure this Filter like StringFilter.new(['some/path', /^some_regex/, Proc.new {|src_file| ... }])
133
+ def matches?(source_files_list)
134
+ filter_argument.any? do |arg|
135
+ arg.matches?(source_files_list)
136
+ end
137
+ end
138
+ end
139
+ end
@@ -0,0 +1,101 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "pathname"
4
+
5
+ module SimpleCov
6
+ module Formatter
7
+ # @api private
8
+ #
9
+ # Shared scaffolding for formatters that write a coverage report to
10
+ # an output directory and emit a "Coverage report generated for X
11
+ # to Y" summary on stderr (it's a status message, not data).
12
+ # Subclasses override `format` to do their actual writing, and may
13
+ # override `message_prefix` (e.g. JSON prepends "JSON ").
14
+ class Base
15
+ # `output_dir` defaults to `SimpleCov.coverage_path` so the at_exit
16
+ # pipeline keeps working unchanged. Pass it explicitly to write
17
+ # somewhere else (handy for tests that don't want to clobber the
18
+ # project's `coverage/` directory).
19
+ def initialize(silent: false, output_dir: nil)
20
+ @silent = silent
21
+ @output_dir = output_dir
22
+ end
23
+
24
+ private
25
+
26
+ # Subclasses override to prepend a marker (e.g. "JSON ") to the
27
+ # summary line. Default empty for the HTML formatter, which has
28
+ # historically been the unmarked default.
29
+ def message_prefix
30
+ ""
31
+ end
32
+
33
+ def output_path
34
+ @output_dir || SimpleCov.coverage_path
35
+ end
36
+
37
+ # The path shown in the "Coverage report generated for X to Y"
38
+ # status line. Renders relative to cwd when `output_path` lives
39
+ # inside cwd (e.g. `coverage` instead of `/Users/me/proj/coverage`)
40
+ # and appends the formatter's `entry_point_filename` so the line
41
+ # points at a concrete file the user (or a terminal that
42
+ # hyperlinks paths) can act on — e.g. `coverage/index.html`
43
+ # instead of the bare directory `coverage`. Paths outside cwd
44
+ # stay absolute; a `../../../tmp/cov` display would be more
45
+ # confusing than the absolute form. See issue #197.
46
+ def displayable_output_path
47
+ directory = relative_or_absolute_output_path
48
+ entry_point_filename ? File.join(directory, entry_point_filename) : directory
49
+ end
50
+
51
+ def relative_or_absolute_output_path
52
+ absolute = output_path
53
+ relative = Pathname.new(absolute).relative_path_from(Pathname.pwd).to_s
54
+ relative.start_with?("..") ? absolute : relative
55
+ rescue ArgumentError
56
+ # Pathname#relative_path_from raises across mixed absolute/
57
+ # relative inputs (and across Windows drives) — keep the
58
+ # absolute form on any unresolvable case.
59
+ output_path
60
+ end
61
+
62
+ # Subclasses override to name the report's entry-point file
63
+ # (e.g. `index.html` for HTML, `coverage.json` for JSON), which
64
+ # gets appended to the directory in the status line. Default nil
65
+ # leaves the bare directory in place for any third-party formatter
66
+ # that has no single canonical entry point.
67
+ def entry_point_filename
68
+ nil
69
+ end
70
+
71
+ # Emit one summary line per criterion that the run actually
72
+ # measured. The header line ("Coverage report generated for X
73
+ # to Y") is always first; per-criterion lines follow in the
74
+ # order of `result.coverage_statistics` (which is the same
75
+ # insertion order as `SourceFile#coverage_statistics`, which in
76
+ # turn reflects what the user enabled).
77
+ def output_message(result)
78
+ header = "#{message_prefix}Coverage report generated for #{result.command_name} to #{displayable_output_path}"
79
+ body = result.coverage_statistics.filter_map { |criterion, stat| stats_line(criterion, stat) }
80
+ [header, *body].join("\n")
81
+ end
82
+
83
+ # Returns nil for branch/method criteria that have nothing to
84
+ # measure (e.g. a file with no branches under branch coverage).
85
+ # Showing "Branch coverage: 0 / 0 (100.00%)" is noise; the older
86
+ # output specifically suppressed it.
87
+ def stats_line(criterion, stat)
88
+ return if criterion != :line && !stat.total.positive?
89
+
90
+ percent = SimpleCov.round_coverage(stat.percent)
91
+ Kernel.format(
92
+ "%<label>s coverage: %<covered>d / %<total>d (%<percent>s)",
93
+ label: criterion.to_s.capitalize,
94
+ covered: stat.covered,
95
+ total: stat.total,
96
+ percent: SimpleCov::Color.colorize_percent(percent)
97
+ )
98
+ end
99
+ end
100
+ end
101
+ end