simplecov 0.22.0 → 1.3.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/LICENSE +1 -1
- data/README.md +173 -875
- data/exe/simplecov +8 -0
- data/lib/minitest/simplecov_plugin.rb +18 -7
- data/lib/simplecov/atomic_file.rb +67 -0
- data/lib/simplecov/autostart.rb +9 -0
- data/lib/simplecov/baseline/parser.rb +58 -0
- data/lib/simplecov/baseline.rb +170 -0
- data/lib/simplecov/cli/affected/changed_files.rb +57 -0
- data/lib/simplecov/cli/affected/selection.rb +117 -0
- data/lib/simplecov/cli/affected.rb +148 -0
- data/lib/simplecov/cli/annotations.rb +166 -0
- data/lib/simplecov/cli/badge/svg.rb +70 -0
- data/lib/simplecov/cli/badge.rb +62 -0
- data/lib/simplecov/cli/clean.rb +86 -0
- data/lib/simplecov/cli/command_helpers.rb +91 -0
- data/lib/simplecov/cli/completions/scripts.rb +114 -0
- data/lib/simplecov/cli/completions.rb +83 -0
- data/lib/simplecov/cli/coverage.rb +83 -0
- data/lib/simplecov/cli/coverage_file.rb +109 -0
- data/lib/simplecov/cli/dead_code/output.rb +100 -0
- data/lib/simplecov/cli/dead_code.rb +139 -0
- data/lib/simplecov/cli/diff/output.rb +45 -0
- data/lib/simplecov/cli/diff.rb +115 -0
- data/lib/simplecov/cli/dotfile.rb +156 -0
- data/lib/simplecov/cli/git.rb +41 -0
- data/lib/simplecov/cli/history/output.rb +135 -0
- data/lib/simplecov/cli/history.rb +67 -0
- data/lib/simplecov/cli/merge.rb +115 -0
- data/lib/simplecov/cli/open.rb +45 -0
- data/lib/simplecov/cli/patch/changed_lines.rb +149 -0
- data/lib/simplecov/cli/patch/output.rb +151 -0
- data/lib/simplecov/cli/patch.rb +175 -0
- data/lib/simplecov/cli/ratchet/output.rb +44 -0
- data/lib/simplecov/cli/ratchet.rb +83 -0
- data/lib/simplecov/cli/report.rb +91 -0
- data/lib/simplecov/cli/run.rb +35 -0
- data/lib/simplecov/cli/serve/report_preparer.rb +30 -0
- data/lib/simplecov/cli/serve/static_file_handler.rb +128 -0
- data/lib/simplecov/cli/serve.rb +88 -0
- data/lib/simplecov/cli/show/annotator.rb +87 -0
- data/lib/simplecov/cli/show/sweep.rb +36 -0
- data/lib/simplecov/cli/show.rb +133 -0
- data/lib/simplecov/cli/status/facts.rb +87 -0
- data/lib/simplecov/cli/status.rb +93 -0
- data/lib/simplecov/cli/tests/redundancy.rb +78 -0
- data/lib/simplecov/cli/tests.rb +148 -0
- data/lib/simplecov/cli/uncovered/misses.rb +33 -0
- data/lib/simplecov/cli/uncovered.rb +127 -0
- data/lib/simplecov/cli/usage.rb +153 -0
- data/lib/simplecov/cli/watch/live_report.rb +73 -0
- data/lib/simplecov/cli/watch/narrator.rb +55 -0
- data/lib/simplecov/cli/watch/poller.rb +45 -0
- data/lib/simplecov/cli/watch/session.rb +134 -0
- data/lib/simplecov/cli/watch/test_plan.rb +43 -0
- data/lib/simplecov/cli/watch.rb +85 -0
- data/lib/simplecov/cli.rb +114 -0
- data/lib/simplecov/color.rb +65 -0
- data/lib/simplecov/combine/branches_combiner.rb +44 -21
- data/lib/simplecov/combine/coverage_accumulator.rb +175 -0
- data/lib/simplecov/combine/identity_interner.rb +27 -0
- data/lib/simplecov/combine/interned_counts.rb +28 -0
- data/lib/simplecov/combine/lines_combiner.rb +35 -29
- data/lib/simplecov/combine/methods_combiner.rb +48 -0
- data/lib/simplecov/combine/results_combiner.rb +9 -49
- data/lib/simplecov/combine.rb +0 -24
- data/lib/simplecov/command_guesser.rb +99 -42
- data/lib/simplecov/configuration/baseline.rb +31 -0
- data/lib/simplecov/configuration/coverage.rb +184 -0
- data/lib/simplecov/configuration/coverage_criteria.rb +143 -0
- data/lib/simplecov/configuration/deprecations.rb +21 -0
- data/lib/simplecov/configuration/eval_coverage.rb +37 -0
- data/lib/simplecov/configuration/filters.rb +131 -0
- data/lib/simplecov/configuration/formatting.rb +129 -0
- data/lib/simplecov/configuration/groups.rb +48 -0
- data/lib/simplecov/configuration/history.rb +44 -0
- data/lib/simplecov/configuration/ignored_entries.rb +72 -0
- data/lib/simplecov/configuration/merging.rb +197 -0
- data/lib/simplecov/configuration/missed_caps.rb +51 -0
- data/lib/simplecov/configuration/production.rb +23 -0
- data/lib/simplecov/configuration/test_tracking.rb +48 -0
- data/lib/simplecov/configuration/thresholds.rb +150 -0
- data/lib/simplecov/configuration/view_coverage.rb +37 -0
- data/lib/simplecov/configuration.rb +100 -443
- data/lib/simplecov/configuration_error.rb +5 -0
- data/lib/simplecov/context_map/union.rb +81 -0
- data/lib/simplecov/context_map.rb +187 -0
- data/lib/simplecov/coverage_json.rb +21 -0
- data/lib/simplecov/coverage_statistics.rb +24 -28
- data/lib/simplecov/coverage_violations.rb +254 -0
- data/lib/simplecov/current_run.rb +61 -0
- data/lib/simplecov/defaults.rb +20 -28
- data/lib/simplecov/deprecation.rb +63 -0
- data/lib/simplecov/directive/erb.rb +44 -0
- data/lib/simplecov/directive/haml.rb +33 -0
- data/lib/simplecov/directive/indented_template.rb +37 -0
- data/lib/simplecov/directive/slim.rb +35 -0
- data/lib/simplecov/directive/template.rb +29 -0
- data/lib/simplecov/directive.rb +152 -0
- data/lib/simplecov/exit_codes/baseline_check.rb +46 -0
- data/lib/simplecov/exit_codes/check.rb +39 -0
- data/lib/simplecov/exit_codes/exit_code_handling.rb +24 -5
- data/lib/simplecov/exit_codes/maximum_coverage_drop_check.rb +17 -67
- data/lib/simplecov/exit_codes/maximum_missed_check.rb +26 -0
- data/lib/simplecov/exit_codes/maximum_missed_per_file_check.rb +34 -0
- data/lib/simplecov/exit_codes/maximum_overall_coverage_check.rb +27 -0
- data/lib/simplecov/exit_codes/minimum_coverage_by_file_check.rb +20 -38
- data/lib/simplecov/exit_codes/minimum_coverage_by_group_check.rb +28 -0
- data/lib/simplecov/exit_codes/minimum_overall_coverage_check.rb +37 -34
- data/lib/simplecov/exit_codes.rb +26 -0
- data/lib/simplecov/exit_handling.rb +163 -0
- data/lib/simplecov/file_list.rb +63 -48
- data/lib/simplecov/filter.rb +98 -42
- data/lib/simplecov/formatter/base.rb +100 -0
- data/lib/simplecov/formatter/baseline_formatter.rb +98 -0
- data/lib/simplecov/formatter/coverage_json_writer.rb +95 -0
- data/lib/simplecov/formatter/html_formatter/public/index.html +93 -0
- data/lib/simplecov/formatter/html_formatter/viewer_data_validator.rb +153 -0
- data/lib/simplecov/formatter/html_formatter.rb +88 -0
- data/lib/simplecov/formatter/json_formatter/errors_formatter.rb +122 -0
- data/lib/simplecov/formatter/json_formatter/production_section_formatter.rb +48 -0
- data/lib/simplecov/formatter/json_formatter/result_hash_formatter.rb +132 -0
- data/lib/simplecov/formatter/json_formatter/source_file_formatter.rb +97 -0
- data/lib/simplecov/formatter/json_formatter.rb +40 -0
- data/lib/simplecov/formatter/multi_formatter.rb +8 -10
- data/lib/simplecov/formatter/simple_formatter.rb +12 -15
- data/lib/simplecov/formatter.rb +21 -1
- data/lib/simplecov/group_names.rb +33 -0
- data/lib/simplecov/history.rb +115 -0
- data/lib/simplecov/last_run.rb +16 -5
- data/lib/simplecov/lines_classifier.rb +67 -19
- data/lib/simplecov/load_global_config.rb +12 -4
- data/lib/simplecov/parallel_adapters/base.rb +50 -0
- data/lib/simplecov/parallel_adapters/generic.rb +33 -0
- data/lib/simplecov/parallel_adapters/parallel_tests.rb +72 -0
- data/lib/simplecov/parallel_adapters.rb +54 -0
- data/lib/simplecov/parallel_coordination.rb +124 -0
- data/lib/simplecov/parallel_result_merger/worker_payload.rb +33 -0
- data/lib/simplecov/parallel_result_merger.rb +171 -0
- data/lib/simplecov/process.rb +34 -12
- data/lib/simplecov/production/error.rb +11 -0
- data/lib/simplecov/production/file_sink.rb +130 -0
- data/lib/simplecov/production.rb +249 -0
- data/lib/simplecov/profiles/bundler_filter.rb +1 -1
- data/lib/simplecov/profiles/hidden_filter.rb +1 -1
- data/lib/simplecov/profiles/rails.rb +13 -10
- data/lib/simplecov/profiles/root_filter.rb +2 -5
- data/lib/simplecov/profiles/strict.rb +22 -0
- data/lib/simplecov/profiles/test_frameworks.rb +1 -4
- data/lib/simplecov/profiles.rb +26 -19
- data/lib/simplecov/report_deferral.rb +45 -0
- data/lib/simplecov/report_stamp.rb +25 -0
- data/lib/simplecov/result/filter_config.rb +19 -0
- data/lib/simplecov/result/missing_source_files_reporter.rb +48 -0
- data/lib/simplecov/result/serialization.rb +43 -0
- data/lib/simplecov/result/source_file_builder.rb +46 -0
- data/lib/simplecov/result.rb +123 -47
- data/lib/simplecov/result_adapter.rb +130 -11
- data/lib/simplecov/result_merger/contexts.rb +22 -0
- data/lib/simplecov/result_merger/legacy_format_adapter.rb +24 -0
- data/lib/simplecov/result_merger/resultset_file.rb +67 -0
- data/lib/simplecov/result_merger/resultset_run_identity.rb +65 -0
- data/lib/simplecov/result_merger/resultset_store.rb +62 -0
- data/lib/simplecov/result_merger/unloaded_files.rb +95 -0
- data/lib/simplecov/result_merger.rb +120 -124
- data/lib/simplecov/result_processing.rb +188 -0
- data/lib/simplecov/run_identity.rb +75 -0
- data/lib/simplecov/simulate_coverage.rb +72 -17
- data/lib/simplecov/source_file/branch.rb +8 -35
- data/lib/simplecov/source_file/branch_builder.rb +95 -0
- data/lib/simplecov/source_file/builder_context.rb +28 -0
- data/lib/simplecov/source_file/line.rb +17 -32
- data/lib/simplecov/source_file/line_builder.rb +37 -0
- data/lib/simplecov/source_file/method.rb +54 -0
- data/lib/simplecov/source_file/method_builder.rb +51 -0
- data/lib/simplecov/source_file/ruby_data_parser.rb +103 -0
- data/lib/simplecov/source_file/skip_chunks.rb +72 -0
- data/lib/simplecov/source_file/source_loader.rb +89 -0
- data/lib/simplecov/source_file/statistics.rb +76 -0
- data/lib/simplecov/source_file.rb +64 -260
- data/lib/simplecov/static_coverage_extractor/condition_folding.rb +155 -0
- data/lib/simplecov/static_coverage_extractor/location_conventions.rb +191 -0
- data/lib/simplecov/static_coverage_extractor/method_collector.rb +45 -0
- data/lib/simplecov/static_coverage_extractor/prism_compat.rb +46 -0
- data/lib/simplecov/static_coverage_extractor/value_position.rb +61 -0
- data/lib/simplecov/static_coverage_extractor/visitor.rb +163 -0
- data/lib/simplecov/static_coverage_extractor.rb +78 -0
- data/lib/simplecov/test_tracker/accessors.rb +34 -0
- data/lib/simplecov/test_tracker/constant_watch.rb +49 -0
- data/lib/simplecov/test_tracker/delta.rb +69 -0
- data/lib/simplecov/test_tracker/framework_hooks.rb +130 -0
- data/lib/simplecov/test_tracker.rb +162 -0
- data/lib/simplecov/unloaded_file_injector.rb +62 -0
- data/lib/simplecov/useless_results_remover.rb +17 -7
- data/lib/simplecov/version.rb +1 -1
- data/lib/simplecov/view_coverage/template_compiler.rb +83 -0
- data/lib/simplecov/view_coverage.rb +55 -0
- data/lib/simplecov-html.rb +5 -0
- data/lib/simplecov.rb +143 -394
- data/lib/simplecov_json_formatter.rb +5 -0
- data/man/simplecov.1 +354 -0
- data/sig/simplecov.rbs +1710 -0
- metadata +183 -61
- data/CHANGELOG.md +0 -191
- data/doc/alternate-formatters.md +0 -71
- data/doc/commercial-services.md +0 -25
- data/doc/editor-integration.md +0 -18
- data/lib/simplecov/combine/files_combiner.rb +0 -24
- data/lib/simplecov/default_formatter.rb +0 -20
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Every coverage criterion enabled and held to 100%. An opt-in profile rather
|
|
4
|
+
# than the default (#1061), so a team that has paid down the debt can flip the
|
|
5
|
+
# strict switch without re-typing the threshold trio every time.
|
|
6
|
+
#
|
|
7
|
+
# JRuby gracefully degrades: `enable_coverage :branch` / `:method` are accepted
|
|
8
|
+
# but the runtime cannot measure them, and `CoverageViolations` skips threshold
|
|
9
|
+
# lookups for criteria not in the stats, so only `:line` is enforced there.
|
|
10
|
+
#
|
|
11
|
+
# `:eval` is guarded on `coverage_for_eval_supported?` so the profile stays
|
|
12
|
+
# quiet on engines without it, where enabling it would warn every time it loads.
|
|
13
|
+
|
|
14
|
+
SimpleCov.profiles.define "strict" do
|
|
15
|
+
enable_coverage :branch
|
|
16
|
+
enable_coverage :method
|
|
17
|
+
# simplecov:disable branch — dogfood branch coverage runs on CRuby only,
|
|
18
|
+
# so the else arm (eval coverage not supported) is unreachable from CI.
|
|
19
|
+
enable_coverage :eval if coverage_for_eval_supported?
|
|
20
|
+
# simplecov:enable
|
|
21
|
+
minimum_coverage line: 100, branch: 100, method: 100
|
|
22
|
+
end
|
data/lib/simplecov/profiles.rb
CHANGED
|
@@ -1,35 +1,42 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module SimpleCov
|
|
4
|
-
#
|
|
5
|
-
#
|
|
6
|
-
# loaded using SimpleCov.start :rails and defined using
|
|
7
|
-
# SimpleCov.profiles.define :foo do
|
|
8
|
-
# # SimpleCov configuration here, same as in SimpleCov.configure
|
|
9
|
-
# end
|
|
10
|
-
#
|
|
4
|
+
# Configuration procs loaded with `SimpleCov.start :rails` and defined with
|
|
5
|
+
# `SimpleCov.profiles.define`.
|
|
11
6
|
class Profiles < Hash
|
|
12
|
-
#
|
|
13
|
-
# Define a SimpleCov profile:
|
|
14
|
-
# SimpleCov.profiles.define 'rails' do
|
|
15
|
-
# # Same as SimpleCov.configure do .. here
|
|
16
|
-
# end
|
|
17
|
-
#
|
|
18
7
|
def define(name, &blk)
|
|
19
8
|
name = name.to_sym
|
|
20
|
-
raise "SimpleCov Profile '#{name}' is already defined" unless self[name].nil?
|
|
9
|
+
raise ConfigurationError, "SimpleCov Profile '#{name}' is already defined" unless self[name].nil?
|
|
21
10
|
|
|
22
11
|
self[name] = blk
|
|
23
12
|
end
|
|
24
13
|
|
|
25
|
-
#
|
|
26
|
-
# Applies the profile of given name on SimpleCov.configure
|
|
27
|
-
#
|
|
28
14
|
def load(name)
|
|
15
|
+
SimpleCov.configure(&fetch_proc(name))
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# Lookup order: already registered via #define, then
|
|
19
|
+
# `require "simplecov/profiles/<name>"` for the bundled profiles, then
|
|
20
|
+
# `require "simplecov-profile-<name>"` for third-party plugin gems.
|
|
21
|
+
def fetch_proc(name)
|
|
29
22
|
name = name.to_sym
|
|
30
|
-
|
|
23
|
+
autoload_profile(name) unless key?(name)
|
|
24
|
+
return fetch(name) if key?(name)
|
|
25
|
+
|
|
26
|
+
raise ConfigurationError, "Could not find SimpleCov Profile called '#{name}'"
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
private
|
|
31
30
|
|
|
32
|
-
|
|
31
|
+
def autoload_profile(name)
|
|
32
|
+
require "simplecov/profiles/#{name}"
|
|
33
|
+
rescue LoadError
|
|
34
|
+
begin
|
|
35
|
+
require "simplecov-profile-#{name}"
|
|
36
|
+
rescue LoadError
|
|
37
|
+
# Neither a bundled profile nor a plugin gem; `fetch_proc` raises the
|
|
38
|
+
# user-facing error.
|
|
39
|
+
end
|
|
33
40
|
end
|
|
34
41
|
end
|
|
35
42
|
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# The clobber-prevention backstop: an empty process stands down instead of
|
|
4
|
+
# formatting over a fresher report a sibling produced. Typically fires when
|
|
5
|
+
# `SimpleCov.start` ran in a parent process that shelled out to the test runner
|
|
6
|
+
# (#581).
|
|
7
|
+
|
|
8
|
+
module SimpleCov
|
|
9
|
+
class << self
|
|
10
|
+
def defer_to_existing_report?
|
|
11
|
+
return false unless existing_report_newer_than_us?
|
|
12
|
+
|
|
13
|
+
res = result
|
|
14
|
+
empty = res.nil? || res.files.empty?
|
|
15
|
+
warn_about_deferred_report if empty
|
|
16
|
+
empty
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# `.last_run.json` only exists after fully successful runs, so alone it left
|
|
20
|
+
# the backstop inert when the child run failed, the case where clobbering its
|
|
21
|
+
# report hurts most. The report stamp is touched by every formatting process
|
|
22
|
+
# regardless of exit status. The rescue covers a file vanishing mid-at_exit.
|
|
23
|
+
def existing_report_newer_than_us?
|
|
24
|
+
return false unless process_start_time
|
|
25
|
+
|
|
26
|
+
[LastRun.last_run_path, ReportStamp.path].any? do |path|
|
|
27
|
+
File.mtime(path) > process_start_time
|
|
28
|
+
rescue SystemCallError
|
|
29
|
+
false
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def warn_about_deferred_report
|
|
34
|
+
return unless print_errors
|
|
35
|
+
|
|
36
|
+
ExitCodes.print_error Color.colorize(
|
|
37
|
+
"Skipping SimpleCov report — this process tracked no application code and a newer " \
|
|
38
|
+
"report already exists at #{coverage_path}. This usually means SimpleCov.start ran in a " \
|
|
39
|
+
"parent process (e.g. a Rakefile or Rails' Bundler.require) that shelled out to the test " \
|
|
40
|
+
"runner. See https://github.com/simplecov-ruby/simplecov/issues/581.",
|
|
41
|
+
:yellow
|
|
42
|
+
)
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "fileutils"
|
|
4
|
+
|
|
5
|
+
module SimpleCov
|
|
6
|
+
# Touches coverage/.report_stamp whenever a report is formatted, no matter how
|
|
7
|
+
# the run ends. The clobber-prevention backstop needs an on-disk signal that a
|
|
8
|
+
# fresher report exists, and `.last_run.json` can't be it alone: that file is
|
|
9
|
+
# only written after fully successful runs.
|
|
10
|
+
module ReportStamp
|
|
11
|
+
class << self
|
|
12
|
+
def path
|
|
13
|
+
File.join(SimpleCov.coverage_path, ".report_stamp")
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# A read-only or vanished coverage dir must not crash reporting; the stamp
|
|
17
|
+
# only powers the deferral heuristic.
|
|
18
|
+
def touch
|
|
19
|
+
FileUtils.touch(path)
|
|
20
|
+
rescue SystemCallError
|
|
21
|
+
nil
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SimpleCov
|
|
4
|
+
class Result
|
|
5
|
+
# The filter and grouping configuration a Result applies to its source files
|
|
6
|
+
# after building them. Each field defaults to the SimpleCov singleton's
|
|
7
|
+
# configuration, so ordinary callers never construct one; tests pass a custom
|
|
8
|
+
# instance to opt out of the project's filters or groups.
|
|
9
|
+
class FilterConfig
|
|
10
|
+
attr_reader :filters, :cover_filters, :groups
|
|
11
|
+
|
|
12
|
+
def initialize(filters: SimpleCov.filters, cover_filters: SimpleCov.cover_filters, groups: SimpleCov.groups)
|
|
13
|
+
@filters = filters
|
|
14
|
+
@cover_filters = cover_filters
|
|
15
|
+
@groups = groups
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SimpleCov
|
|
4
|
+
class Result
|
|
5
|
+
# When a resultset references source files that don't exist on the local
|
|
6
|
+
# filesystem they're silently dropped, which produces an empty
|
|
7
|
+
# `0 / 0 (100.00%)` report that looks like success but isn't. This emits a
|
|
8
|
+
# single warning summarizing the drop and, when every entry was lost, points
|
|
9
|
+
# at the typical cause: `SimpleCov.collate` invoked from a machine or path
|
|
10
|
+
# different from where the resultsets were generated (#980).
|
|
11
|
+
class MissingSourceFilesReporter
|
|
12
|
+
def initialize(missing_paths, every_entry_dropped:)
|
|
13
|
+
@missing_paths = missing_paths
|
|
14
|
+
@every_entry_dropped = every_entry_dropped
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def warn!
|
|
18
|
+
warn Color.colorize(message, :yellow)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def message
|
|
22
|
+
@every_entry_dropped ? all_missing_warning : partial_missing_warning
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
private
|
|
26
|
+
|
|
27
|
+
def all_missing_warning
|
|
28
|
+
"SimpleCov dropped all #{@missing_paths.size} source file(s) from the result — " \
|
|
29
|
+
"none of the paths in the resultset exist on this filesystem: " \
|
|
30
|
+
"#{summary}. If you're running `SimpleCov.collate`, the source " \
|
|
31
|
+
"files must be available at the same absolute paths as when the individual resultsets " \
|
|
32
|
+
"were generated."
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def partial_missing_warning
|
|
36
|
+
"SimpleCov dropped #{@missing_paths.size} source file(s) from the result because " \
|
|
37
|
+
"they don't exist on this filesystem: #{summary}. They were " \
|
|
38
|
+
"tracked in the resultset but have since moved or been removed."
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def summary
|
|
42
|
+
sample = @missing_paths.first(5).join(", ")
|
|
43
|
+
remaining = @missing_paths.size - 5
|
|
44
|
+
remaining.positive? ? "#{sample} (+#{remaining} more)" : sample
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SimpleCov
|
|
4
|
+
class Result
|
|
5
|
+
module Serialization
|
|
6
|
+
def to_hash
|
|
7
|
+
data = {"coverage" => coverage, "timestamp" => created_at.to_f} #: Hash[String, untyped]
|
|
8
|
+
data["run_id"] = run_id if run_id
|
|
9
|
+
data["worker_id"] = worker_id if worker_id
|
|
10
|
+
data["tracked_files"] = tracked_files unless tracked_files.empty?
|
|
11
|
+
append_contexts(data)
|
|
12
|
+
{command_name => data}
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
private
|
|
16
|
+
|
|
17
|
+
# Serialized whenever a map was recorded, even an empty one: for the merge,
|
|
18
|
+
# the key's presence is the signal that this result tracked tests, which is
|
|
19
|
+
# what lets it tell "tracked and covered nothing" from "never tracked". The
|
|
20
|
+
# map's files are restricted to this result's post-filter universe.
|
|
21
|
+
def append_contexts(data)
|
|
22
|
+
map = contexts
|
|
23
|
+
return unless map
|
|
24
|
+
|
|
25
|
+
data["contexts"] = map.to_h(only: context_filenames)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def context_filenames
|
|
29
|
+
Set.new(filenames)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# A live result's criterion keys are Symbols while entries parsed back from
|
|
33
|
+
# `.resultset.json` carry Strings, and the combiners read only String keys.
|
|
34
|
+
# Serializing with String keys is what lets a live result merged against a
|
|
35
|
+
# stored entry contribute its counts instead of being dropped.
|
|
36
|
+
def coverage
|
|
37
|
+
original_result.slice(*filenames).transform_values do |file_coverage|
|
|
38
|
+
file_coverage.transform_keys(&:to_s)
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SimpleCov
|
|
4
|
+
class Result
|
|
5
|
+
class SourceFileBuilder
|
|
6
|
+
attr_reader :missing_source_files
|
|
7
|
+
|
|
8
|
+
def initialize(original_result, not_loaded_files:)
|
|
9
|
+
@original_result = original_result
|
|
10
|
+
@not_loaded_files = not_loaded_files
|
|
11
|
+
@missing_source_files = []
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def call
|
|
15
|
+
FileList.new(
|
|
16
|
+
@original_result
|
|
17
|
+
.filter_map { |filename, coverage| build_source_file(filename, coverage) }
|
|
18
|
+
.sort_by(&:filename)
|
|
19
|
+
)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
private
|
|
23
|
+
|
|
24
|
+
def build_source_file(filename, coverage)
|
|
25
|
+
unless File.file?(filename)
|
|
26
|
+
@missing_source_files << filename
|
|
27
|
+
return
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
SourceFile.new(
|
|
31
|
+
filename,
|
|
32
|
+
stringify_outer_keys(coverage),
|
|
33
|
+
loaded: !@not_loaded_files.include?(filename)
|
|
34
|
+
)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# `Coverage.result` returns symbol keys; resultsets loaded from disk are
|
|
38
|
+
# already string-keyed. SourceFile reads with strings and handles both Array
|
|
39
|
+
# and stringified-Array branch/method keys itself, so only the outer hash
|
|
40
|
+
# needs normalizing.
|
|
41
|
+
def stringify_outer_keys(coverage)
|
|
42
|
+
coverage.transform_keys(&:to_s)
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
data/lib/simplecov/result.rb
CHANGED
|
@@ -2,93 +2,169 @@
|
|
|
2
2
|
|
|
3
3
|
require "digest/sha1"
|
|
4
4
|
require "forwardable"
|
|
5
|
+
require_relative "result/filter_config"
|
|
6
|
+
require_relative "result/missing_source_files_reporter"
|
|
7
|
+
require_relative "result/serialization"
|
|
8
|
+
require_relative "result/source_file_builder"
|
|
5
9
|
|
|
6
10
|
module SimpleCov
|
|
7
11
|
#
|
|
8
|
-
# A
|
|
9
|
-
# library generates
|
|
12
|
+
# A code coverage result, initialized from the Hash Ruby's built-in coverage
|
|
13
|
+
# library generates.
|
|
10
14
|
#
|
|
11
15
|
class Result
|
|
12
16
|
extend Forwardable
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
#
|
|
19
|
-
|
|
20
|
-
#
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
17
|
+
include Serialization
|
|
18
|
+
|
|
19
|
+
attr_reader :original_result, :files
|
|
20
|
+
# Every path the producing process was told to track, loaded or not.
|
|
21
|
+
# Carried into the resultset so a merge elsewhere can inject the ones
|
|
22
|
+
# nobody loaded without needing that process's config (#1250).
|
|
23
|
+
attr_reader :tracked_files
|
|
24
|
+
# Used only for parallel-result coordination. They do not change which
|
|
25
|
+
# fresh suites are merged.
|
|
26
|
+
attr_reader :run_id, :worker_id
|
|
27
|
+
alias_method :source_files, :files
|
|
28
|
+
# The `ContextMap` recorded under `track_tests`, or nil when this result
|
|
29
|
+
# carries none: tracking was off, or a merge dropped the map because not
|
|
30
|
+
# every merged result recorded one.
|
|
31
|
+
attr_reader :contexts
|
|
32
|
+
|
|
33
|
+
# The distinct run names behind this result, set by a merge so presentation
|
|
34
|
+
# can say "A and N other runs" instead of reading the joined
|
|
35
|
+
# `command_name` aloud in full. Splitting that string back apart would
|
|
36
|
+
# misread a run name that itself contains a comma.
|
|
37
|
+
attr_writer :created_at, :command_name, :command_names
|
|
38
|
+
|
|
39
|
+
def_delegators :files, :covered_percent, :covered_percentages, :least_covered_file, :covered_strength,
|
|
40
|
+
:covered_lines, :missed_lines,
|
|
41
|
+
:total_branches, :covered_branches, :missed_branches,
|
|
42
|
+
:total_methods, :covered_methods, :missed_methods,
|
|
43
|
+
:coverage_statistics, :coverage_statistics_by_file
|
|
24
44
|
def_delegator :files, :lines_of_code, :total_lines
|
|
25
45
|
|
|
26
|
-
#
|
|
27
|
-
#
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
46
|
+
# `filter_config` defaults to the SimpleCov singleton's filter / group
|
|
47
|
+
# configuration. Pass a custom FilterConfig to opt out, which is useful for
|
|
48
|
+
# tests that build synthetic Results.
|
|
49
|
+
#
|
|
50
|
+
# `tracked_files` accepts any collection that answers `to_a` (the merge
|
|
51
|
+
# passes a Set), and nil for a run that tracked nothing.
|
|
52
|
+
def initialize(original_result, command_name: nil, created_at: nil, not_loaded_files: Set.new,
|
|
53
|
+
tracked_files: nil, run_id: nil, worker_id: nil, contexts: nil, report: false,
|
|
54
|
+
filter_config: FilterConfig.new)
|
|
55
|
+
@original_result = original_result.freeze
|
|
31
56
|
@command_name = command_name
|
|
32
57
|
@created_at = created_at
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
58
|
+
initialize_resultset_metadata(tracked_files, run_id, worker_id, contexts)
|
|
59
|
+
@groups_config = filter_config.groups
|
|
60
|
+
builder = SourceFileBuilder.new(original_result, not_loaded_files: not_loaded_files)
|
|
61
|
+
@files = builder.call
|
|
62
|
+
warn_about_missing_source_files(builder.missing_source_files) if report
|
|
63
|
+
apply_cover_filters!(filter_config.cover_filters)
|
|
64
|
+
apply_filters!(filter_config.filters)
|
|
37
65
|
end
|
|
38
66
|
|
|
39
|
-
# Returns all filenames for source files contained in this result
|
|
40
67
|
def filenames
|
|
41
68
|
files.map(&:filename)
|
|
42
69
|
end
|
|
43
70
|
|
|
44
|
-
#
|
|
71
|
+
# The path is resolved against SimpleCov.root, so callers can pass either
|
|
72
|
+
# an absolute path or a project-relative one.
|
|
73
|
+
def source_file_for(path)
|
|
74
|
+
target = File.expand_path(path, SimpleCov.root)
|
|
75
|
+
files.find { |file| file.filename.eql?(target) }
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# Path resolution as in `source_file_for`.
|
|
79
|
+
def coverage_for(path)
|
|
80
|
+
source_file_for(path)&.coverage_statistics
|
|
81
|
+
end
|
|
82
|
+
|
|
45
83
|
def groups
|
|
46
|
-
@groups ||= SimpleCov.grouped(files)
|
|
84
|
+
@groups ||= SimpleCov.grouped(files, groups: @groups_config)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# True for a group the configuration defines, whether or not any file
|
|
88
|
+
# matched it; `groups` omits the ones that matched nothing.
|
|
89
|
+
def configured_group?(group_name)
|
|
90
|
+
@groups_config.key?(group_name)
|
|
47
91
|
end
|
|
48
92
|
|
|
49
|
-
#
|
|
93
|
+
# Returns nil if formatting has been opted out of (`SimpleCov.formatter
|
|
94
|
+
# false` / `SimpleCov.formatters []`), the cheap path for non-final
|
|
95
|
+
# processes in a parallel CI run, which only need their `.resultset.json`
|
|
96
|
+
# on disk (#964).
|
|
50
97
|
def format!
|
|
51
|
-
SimpleCov.formatter
|
|
98
|
+
formatter = SimpleCov.formatter
|
|
99
|
+
return nil if formatter.nil?
|
|
100
|
+
|
|
101
|
+
formatted = Formatter.format(formatter, self)
|
|
102
|
+
# Recorded regardless of how the run ends, so a parent process's
|
|
103
|
+
# clobber-prevention backstop can tell a report was produced even when
|
|
104
|
+
# this run's checks or tests failed, unlike .last_run.json.
|
|
105
|
+
ReportStamp.touch
|
|
106
|
+
formatted
|
|
52
107
|
end
|
|
53
108
|
|
|
54
|
-
# Defines when this result has been created. Defaults to Time.now
|
|
55
109
|
def created_at
|
|
56
110
|
@created_at ||= Time.now
|
|
57
111
|
end
|
|
58
112
|
|
|
59
|
-
# The command name that launched this result.
|
|
60
|
-
# Delegated to SimpleCov.command_name if not set manually
|
|
61
113
|
def command_name
|
|
62
114
|
@command_name ||= SimpleCov.command_name
|
|
63
115
|
end
|
|
64
116
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
{
|
|
68
|
-
command_name => {
|
|
69
|
-
"coverage" => coverage,
|
|
70
|
-
"timestamp" => created_at.to_i
|
|
71
|
-
}
|
|
72
|
-
}
|
|
117
|
+
def command_names
|
|
118
|
+
@command_names ||= [command_name]
|
|
73
119
|
end
|
|
74
120
|
|
|
75
|
-
# Loads a SimpleCov::Result#to_hash dump
|
|
76
121
|
def self.from_hash(hash)
|
|
77
122
|
hash.map do |command_name, data|
|
|
78
|
-
new(data.fetch("coverage"), command_name: command_name, created_at: Time.at(data
|
|
123
|
+
new(data.fetch("coverage"), command_name: command_name, created_at: Time.at(data.fetch("timestamp")),
|
|
124
|
+
tracked_files: data["tracked_files"], run_id: data["run_id"],
|
|
125
|
+
worker_id: data["worker_id"], contexts: ContextMap.from_hash(data["contexts"]))
|
|
79
126
|
end
|
|
80
127
|
end
|
|
81
128
|
|
|
82
|
-
|
|
129
|
+
private
|
|
130
|
+
|
|
131
|
+
def initialize_resultset_metadata(tracked_files, run_id, worker_id, contexts)
|
|
132
|
+
@tracked_files = tracked_files.to_a
|
|
133
|
+
@run_id = run_id
|
|
134
|
+
@worker_id = worker_id
|
|
135
|
+
@contexts = contexts
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
def warn_about_missing_source_files(missing)
|
|
139
|
+
return if missing.empty?
|
|
140
|
+
|
|
141
|
+
# Emit only from the process that writes the final report: the merged
|
|
142
|
+
# result is rebuilt in every parallel worker, so without this gate the
|
|
143
|
+
# warning prints once per worker. Intentionally not gated on
|
|
144
|
+
# print_errors, because the default at_fork sets print_errors false on
|
|
145
|
+
# workers and in many parallel runners the final-report process is itself
|
|
146
|
+
# a worker (#980, #1171).
|
|
147
|
+
return unless SimpleCov.final_result_process?
|
|
83
148
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
149
|
+
# Every built file survives (only the missing ones are dropped), so an
|
|
150
|
+
# empty file list is exactly the "nothing was found" case.
|
|
151
|
+
MissingSourceFilesReporter.new(missing, every_entry_dropped: @files.empty?).warn!
|
|
87
152
|
end
|
|
88
153
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
154
|
+
def apply_filters!(filters)
|
|
155
|
+
filters.each do |filter|
|
|
156
|
+
@files = FileList.new(@files.reject { |source_file| filter.matches?(source_file) })
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
# With no cover matchers configured this is a no-op, preserving the
|
|
161
|
+
# historical "everything required, then filtered" universe.
|
|
162
|
+
def apply_cover_filters!(cover_filters)
|
|
163
|
+
return if cover_filters.empty?
|
|
164
|
+
|
|
165
|
+
@files = FileList.new(
|
|
166
|
+
@files.select { |source_file| cover_filters.any? { |filter| filter.matches?(source_file) } }
|
|
167
|
+
)
|
|
92
168
|
end
|
|
93
169
|
end
|
|
94
170
|
end
|