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,129 @@
1
+ # frozen_string_literal: true
2
+
3
+ # simplecov:disable
4
+ # Methods below only fire under a parallel test runner; not reachable
5
+ # from a single-process rspec run. Cucumber's test_projects exercise
6
+ # the parallel_tests integration end-to-end in subprocesses, but
7
+ # those subprocesses don't merge their Coverage data back into the
8
+ # parent this dogfood report measures.
9
+ module SimpleCov
10
+ class << self
11
+ # @api private
12
+ def final_result_process?
13
+ adapter = SimpleCov::ParallelAdapters.current
14
+ # No recognized parallel-test adapter. A subprocess forked while
15
+ # coverage was running is never the final reporter — the process that
16
+ # spawned it merges every slice and produces the report. Without this,
17
+ # fork-based runners that don't set TEST_ENV_NUMBER (e.g. Minitest's
18
+ # `parallelize`) have every worker produce the final report and its
19
+ # warnings. See issue #1171.
20
+ return !forked_subprocess? unless adapter
21
+
22
+ adapter.first_worker?
23
+ end
24
+
25
+ # @api private
26
+ def wait_for_other_processes
27
+ adapter = SimpleCov::ParallelAdapters.current
28
+ return unless adapter && final_result_process?
29
+
30
+ # Native synchronization first (adapters that wrap a runner with a
31
+ # real "wait" primitive — parallel_tests'
32
+ # `wait_for_other_processes_to_finish` — implement this; adapters
33
+ # without a native API no-op and rely on the polling fallback below).
34
+ adapter.wait_for_siblings
35
+
36
+ # The native wait can return before sibling at_exit handlers finish
37
+ # writing resultsets, and adapters without a native wait have
38
+ # nothing else. Either way, poll the resultset cache until all
39
+ # expected workers have reported or a timeout is reached. Capture
40
+ # the outcome so `ready_to_process_results?` can suppress min/max
41
+ # threshold checks against a partial total.
42
+ @parallel_results_complete =
43
+ wait_for_parallel_results(adapter.expected_worker_count, native_wait: adapter.native_wait?)
44
+ end
45
+
46
+ # @api private — true when every sibling reported its resultset
47
+ # before the wait deadline. Defaults to true outside a parallel
48
+ # run (when `wait_for_other_processes` is a no-op).
49
+ def parallel_results_complete?
50
+ defined?(@parallel_results_complete) ? @parallel_results_complete : true
51
+ end
52
+
53
+ # @api private — seconds the resultset count must hold steady, after a
54
+ # native wait, before we accept fewer than `expected` workers as final.
55
+ PARALLEL_RESULTS_SETTLE = 0.5
56
+ private_constant :PARALLEL_RESULTS_SETTLE
57
+
58
+ # @api private — returns true when the reporting worker has every
59
+ # resultset it's going to get, false on timeout. Single-process runs
60
+ # (expected <= 1) short-circuit to true with no waiting.
61
+ #
62
+ # Normally we poll until `expected` (= PARALLEL_TEST_GROUPS) workers have
63
+ # reported or `SimpleCov.parallel_wait_timeout` elapses; raise that setting
64
+ # when a slow worker routinely finishes well after the others.
65
+ #
66
+ # When a native wait already confirmed every sibling PROCESS exited
67
+ # (`native_wait`), no further resultset will appear, so a count below
68
+ # `expected` just means some workers produced none — e.g. parallel_test
69
+ # groups that got no spec file on a machine with more cores than files.
70
+ # Once the count then holds steady for `PARALLEL_RESULTS_SETTLE` we accept
71
+ # it as final rather than blocking for the whole timeout. Without a native
72
+ # wait (GenericAdapter) we can't tell an idle worker from a slow one, so we
73
+ # keep waiting the full timeout.
74
+ def wait_for_parallel_results(expected, native_wait: false)
75
+ return true unless expected > 1 # simplecov:disable branch — only false in real parallel runs
76
+
77
+ deadline = monotonic_time + parallel_wait_timeout
78
+ tracker = {count: 0, since: monotonic_time}
79
+ loop do
80
+ seen = SimpleCov::ResultMerger.read_resultset.size
81
+ return true if seen >= expected
82
+ return true if native_wait && resultset_count_settled?(tracker, seen)
83
+ return false if parallel_wait_timed_out?(deadline, expected, seen)
84
+
85
+ sleep 0.1
86
+ end
87
+ end
88
+
89
+ # Track whether the resultset count has held steady (and positive) for
90
+ # `PARALLEL_RESULTS_SETTLE` seconds. `tracker` carries the last count and
91
+ # the time it last changed across poll iterations.
92
+ def resultset_count_settled?(tracker, count)
93
+ if count > tracker[:count]
94
+ tracker[:count] = count
95
+ tracker[:since] = monotonic_time
96
+ return false
97
+ end
98
+
99
+ count.positive? && (monotonic_time - tracker[:since]) >= PARALLEL_RESULTS_SETTLE
100
+ end
101
+
102
+ def monotonic_time
103
+ Process.clock_gettime(Process::CLOCK_MONOTONIC)
104
+ end
105
+
106
+ # @api private — true once the wait deadline has passed; warns on
107
+ # the first timeout so the user knows the merged total is partial.
108
+ def parallel_wait_timed_out?(deadline, expected, seen)
109
+ return false unless monotonic_time > deadline
110
+
111
+ warn_about_incomplete_parallel_results(expected, seen)
112
+ true
113
+ end
114
+
115
+ # @api private
116
+ def warn_about_incomplete_parallel_results(expected, seen)
117
+ return unless print_errors
118
+
119
+ warn SimpleCov::Color.colorize(
120
+ "Only #{seen} of #{expected} parallel-test workers reported within " \
121
+ "#{parallel_wait_timeout}s, so coverage totals are partial and minimum / " \
122
+ "maximum coverage checks are skipped for this run. Increase " \
123
+ "SimpleCov.parallel_wait_timeout if a worker routinely needs longer.",
124
+ :yellow
125
+ )
126
+ end
127
+ end
128
+ end
129
+ # simplecov:enable
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Hooks `Process._fork` (Ruby 3.1+) so child processes inherit SimpleCov's
4
+ # coverage tracking when `SimpleCov.enable_for_subprocesses?` is set.
5
+ #
6
+ # `Process._fork` is the official extension point: `Kernel#fork`,
7
+ # `Process.fork`, `IO.popen("-")`, and similar all funnel through it.
8
+ # Hooking `_fork` (instead of redefining `Process.fork`) composes
9
+ # correctly with other libraries doing the same — they each prepend
10
+ # their own module and chain via `super`.
11
+
12
+ module SimpleCov
13
+ # Prepended onto Process's singleton class so every fork — direct or
14
+ # via Kernel#fork / IO.popen — re-runs SimpleCov's at_fork callback in
15
+ # the child.
16
+ module ProcessForkHook
17
+ def _fork
18
+ active = defined?(SimpleCov) && Coverage.running?
19
+ # Assign the next serial in the PARENT, before the fork, so the child
20
+ # inherits its own stable ordinal via copy-on-write. The default
21
+ # at_fork uses it (instead of the child pid) to name the subprocess's
22
+ # result, keeping that name identical across runs. See issue #1171.
23
+ SimpleCov.next_subprocess_serial! if active
24
+ pid = super
25
+ if pid.zero? && active
26
+ # Mark the child here, independent of whatever custom at_fork block
27
+ # the user installed, so `final_result_process?` can keep forked
28
+ # workers from each producing the final report. See issue #1171.
29
+ SimpleCov.mark_forked_subprocess!
30
+ SimpleCov.at_fork.call(::Process.pid)
31
+ end
32
+ pid
33
+ end
34
+ end
35
+ end
36
+
37
+ Process.singleton_class.prepend(SimpleCov::ProcessForkHook)
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ SimpleCov.profiles.define "bundler_filter" do
4
+ skip "/vendor/bundle/"
5
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ SimpleCov.profiles.define "hidden_filter" do
4
+ skip(/\A\..*/)
5
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ SimpleCov.profiles.define "rails" do
4
+ load_profile "test_frameworks"
5
+
6
+ skip %r{\Aconfig/}
7
+ skip %r{\Adb/}
8
+
9
+ group "Controllers", "app/controllers"
10
+ group "Channels", "app/channels"
11
+ group "Models", "app/models"
12
+ group "Mailers", "app/mailers"
13
+ group "Helpers", "app/helpers"
14
+ group "Jobs", %w[app/jobs app/workers]
15
+ group "Libraries", "lib/"
16
+
17
+ # Preserve the legacy `track_files` semantics (additive disk-discovery
18
+ # without restricting the report's universe): write the ivar directly
19
+ # so loading the profile doesn't emit the public-API deprecation. Users
20
+ # migrating their own configs should prefer `cover "{app,lib}/**/*.rb"`,
21
+ # which both injects unloaded files and scopes the report to the match
22
+ # set — usually the intended behavior for a Rails project.
23
+ @tracked_files = "{app,lib}/**/*.rb"
24
+
25
+ # `parallelize(workers: ...)` forks worker processes that each run a
26
+ # slice of the suite. Without subprocess support, the workers' coverage
27
+ # is dropped on the floor and the parent records 0% for everything they
28
+ # touched. Hooking `Process._fork` makes each worker re-call
29
+ # `SimpleCov.start` with a unique command_name so the resultsets merge
30
+ # correctly.
31
+ merge_subprocesses true
32
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ SimpleCov.profiles.define "root_filter" do
4
+ # Exclude all files outside of simplecov root. Shares the regex with
5
+ # SimpleCov::UselessResultsRemover so the root-prefix logic lives in one
6
+ # place; this profile is the user-facing entry point that tools like
7
+ # `SimpleCov.filtered` apply.
8
+ skip do |src|
9
+ src.filename !~ SimpleCov::UselessResultsRemover.root_regex
10
+ end
11
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ # A "no shortfalls allowed" profile: every coverage criterion is
4
+ # enabled and held to 100%. Per
5
+ # https://github.com/simplecov-ruby/simplecov/issues/1061, this lives as
6
+ # an opt-in profile rather than the default — most projects can't
7
+ # realistically start at 100% on day one — so a team that has paid down
8
+ # the debt can flip the strict switch without re-typing the threshold
9
+ # trio every time.
10
+ #
11
+ # JRuby gracefully degrades: `enable_coverage :branch` / `:method` are
12
+ # accepted (they just add to the configured criteria set), but the
13
+ # runtime can't actually measure them, so the corresponding stats never
14
+ # materialize. `CoverageViolations` skips threshold lookups for
15
+ # criteria not in the stats, so the `:branch` / `:method` clauses below
16
+ # silently no-op on JRuby and only `:line` is enforced.
17
+ #
18
+ # `:eval` widens the universe of code held to 100% to include strings
19
+ # passed through `Kernel#eval` (typically ERB templates, when the user
20
+ # sets `ERB#filename=`). Guarded on `coverage_for_eval_supported?` so
21
+ # the profile stays quiet on Ruby < 3.2, where `enable_coverage :eval`
22
+ # would otherwise warn about the missing runtime support every time
23
+ # the profile loads.
24
+ SimpleCov.profiles.define "strict" do
25
+ enable_coverage :branch
26
+ enable_coverage :method
27
+ # simplecov:disable branch — dogfood runs on Ruby >= 3.2 only, so
28
+ # the else arm (eval coverage not supported) is unreachable from CI.
29
+ enable_coverage :eval if coverage_for_eval_supported?
30
+ # simplecov:enable
31
+ minimum_coverage line: 100, branch: 100, method: 100
32
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ SimpleCov.profiles.define "test_frameworks" do
4
+ skip %r{\A(test|features|spec|autotest)/}
5
+ end
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SimpleCov
4
+ #
5
+ # Profiles are SimpleCov configuration procs that can be easily
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
+ #
11
+ 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
+ def define(name, &blk)
19
+ name = name.to_sym
20
+ raise SimpleCov::ConfigurationError, "SimpleCov Profile '#{name}' is already defined" unless self[name].nil?
21
+
22
+ self[name] = blk
23
+ end
24
+
25
+ #
26
+ # Applies the profile of given name on SimpleCov.configure
27
+ #
28
+ def load(name)
29
+ SimpleCov.configure(&fetch_proc(name))
30
+ end
31
+
32
+ #
33
+ # Returns the proc registered for the given profile name, autoloading
34
+ # bundled or plugin-gem profiles on first lookup. Raises if the profile
35
+ # cannot be located.
36
+ #
37
+ # Lookup order:
38
+ # 1. already registered via #define
39
+ # 2. require "simplecov/profiles/<name>" (bundled profiles)
40
+ # 3. require "simplecov-profile-<name>" (third-party plugin gems)
41
+ #
42
+ def fetch_proc(name)
43
+ name = name.to_sym
44
+ autoload_profile(name) unless key?(name)
45
+ return self[name] if key?(name)
46
+
47
+ raise SimpleCov::ConfigurationError, "Could not find SimpleCov Profile called '#{name}'"
48
+ end
49
+
50
+ private
51
+
52
+ def autoload_profile(name)
53
+ require "simplecov/profiles/#{name}"
54
+ rescue LoadError
55
+ begin
56
+ # simplecov:disable — third-party gem fallback (no such gem in test env)
57
+ require "simplecov-profile-#{name}"
58
+ # simplecov:enable
59
+ rescue LoadError
60
+ # fall through; #fetch_proc raises the user-facing error
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,49 @@
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 `0 / 0
7
+ # (100.00%)` report that looks like success but isn't. Emit a single
8
+ # warning summarizing the drop and, when every entry was lost, point at
9
+ # the typical cause (`SimpleCov.collate` invoked from a machine or path
10
+ # different from where the resultsets were generated). See #980.
11
+ class MissingSourceFilesReporter
12
+ def initialize(missing_paths, input_size:, every_entry_dropped:)
13
+ @missing_paths = missing_paths
14
+ @input_size = input_size
15
+ @every_entry_dropped = every_entry_dropped
16
+ end
17
+
18
+ def warn!
19
+ warn SimpleCov::Color.colorize(message, :yellow)
20
+ end
21
+
22
+ def message
23
+ @every_entry_dropped ? all_missing_warning : partial_missing_warning
24
+ end
25
+
26
+ private
27
+
28
+ def all_missing_warning
29
+ "SimpleCov dropped all #{@missing_paths.size} source file(s) from the result — " \
30
+ "none of the paths in the resultset exist on this filesystem: " \
31
+ "#{summary}. If you're running `SimpleCov.collate`, the source " \
32
+ "files must be available at the same absolute paths as when the individual resultsets " \
33
+ "were generated."
34
+ end
35
+
36
+ def partial_missing_warning
37
+ "SimpleCov dropped #{@missing_paths.size} source file(s) from the result because " \
38
+ "they don't exist on this filesystem: #{summary}. They were " \
39
+ "tracked in the resultset but have since moved or been removed."
40
+ end
41
+
42
+ def summary
43
+ sample = @missing_paths.first(5).join(", ")
44
+ remaining = @missing_paths.size - 5
45
+ remaining.positive? ? "#{sample} (+#{remaining} more)" : sample
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SimpleCov
4
+ class Result
5
+ # Constructs `SimpleCov::SourceFile` instances from a raw coverage
6
+ # hash, sorts them by filename, and surfaces filenames whose source
7
+ # is no longer present on disk so the caller can warn about the
8
+ # silent drop (see #980).
9
+ class SourceFileBuilder
10
+ attr_reader :missing_source_files
11
+
12
+ def initialize(original_result, not_loaded_files:)
13
+ @original_result = original_result
14
+ @not_loaded_files = not_loaded_files
15
+ @missing_source_files = []
16
+ end
17
+
18
+ def call
19
+ SimpleCov::FileList.new(
20
+ @original_result
21
+ .filter_map { |filename, coverage| build_source_file(filename, coverage) }
22
+ .sort_by(&:filename)
23
+ )
24
+ end
25
+
26
+ private
27
+
28
+ def build_source_file(filename, coverage)
29
+ unless File.file?(filename)
30
+ @missing_source_files << filename
31
+ return
32
+ end
33
+
34
+ SimpleCov::SourceFile.new(
35
+ filename,
36
+ stringify_outer_keys(coverage),
37
+ loaded: !@not_loaded_files.include?(filename)
38
+ )
39
+ end
40
+
41
+ # `Coverage.result` returns symbol keys (`:lines`, `:branches`,
42
+ # `:methods`); resultsets loaded from disk are already string-keyed.
43
+ # SourceFile reads with strings, and handles both Array and
44
+ # stringified-Array branch/method keys via `restore_ruby_data_structure`,
45
+ # so only the outer hash needs normalizing.
46
+ def stringify_outer_keys(coverage)
47
+ coverage.transform_keys(&:to_s)
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,183 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "digest/sha1"
4
+ require "forwardable"
5
+ require_relative "result/missing_source_files_reporter"
6
+ require_relative "result/source_file_builder"
7
+
8
+ module SimpleCov
9
+ #
10
+ # A simplecov code coverage result, initialized from the Hash Ruby's built-in coverage
11
+ # library generates (Coverage.result).
12
+ #
13
+ class Result
14
+ extend Forwardable
15
+
16
+ # Returns the original Coverage.result used for this instance of SimpleCov::Result
17
+ attr_reader :original_result
18
+ # Returns all files that are applicable to this result (sans filters!) as instances of
19
+ # SimpleCov::SourceFile. Aliased as :source_files
20
+ attr_reader :files
21
+ alias source_files files
22
+ # Explicitly set the Time this result has been created
23
+ attr_writer :created_at
24
+ # Explicitly set the command name that was used for this coverage result. Defaults to SimpleCov.command_name
25
+ attr_writer :command_name
26
+
27
+ def_delegators :files, :covered_percent, :covered_percentages, :least_covered_file, :covered_strength,
28
+ :covered_lines, :missed_lines,
29
+ :total_branches, :covered_branches, :missed_branches,
30
+ :total_methods, :covered_methods, :missed_methods,
31
+ :coverage_statistics, :coverage_statistics_by_file
32
+ def_delegator :files, :lines_of_code, :total_lines
33
+
34
+ # Bundles the filter and grouping configuration a Result applies to its
35
+ # source files after building them. Each field defaults to the SimpleCov
36
+ # singleton's configuration, so ordinary callers never construct one;
37
+ # tests pass a custom instance to opt out of (or extend) the project's
38
+ # filters or groups (e.g. `filters: []` to keep every file). Grouping the
39
+ # three together keeps Result#initialize's parameter list small.
40
+ class FilterConfig
41
+ attr_reader :filters, :cover_filters, :groups
42
+
43
+ def initialize(filters: SimpleCov.filters, cover_filters: SimpleCov.cover_filters, groups: SimpleCov.groups)
44
+ @filters = filters
45
+ @cover_filters = cover_filters
46
+ @groups = groups
47
+ end
48
+ end
49
+
50
+ # Initialize a new SimpleCov::Result from given Coverage.result (a Hash of filenames each containing an array of
51
+ # coverage data).
52
+ #
53
+ # `filter_config` defaults to the SimpleCov singleton's filter / group
54
+ # configuration so existing call sites are unchanged. Pass a custom
55
+ # FilterConfig to opt out — useful for tests that build synthetic Results
56
+ # and don't want the project's filters or groups applied.
57
+ def initialize(original_result, command_name: nil, created_at: nil, not_loaded_files: Set.new,
58
+ report: false, filter_config: FilterConfig.new)
59
+ @original_result = original_result.freeze
60
+ @command_name = command_name
61
+ @created_at = created_at
62
+ @groups_config = filter_config.groups
63
+ builder = SourceFileBuilder.new(original_result, not_loaded_files: not_loaded_files)
64
+ @files = builder.call
65
+ warn_about_missing_source_files(builder.missing_source_files, original_result.size) if report
66
+ apply_cover_filters!(filter_config.cover_filters)
67
+ apply_filters!(filter_config.filters)
68
+ end
69
+
70
+ # Returns all filenames for source files contained in this result
71
+ def filenames
72
+ files.map(&:filename)
73
+ end
74
+
75
+ # Returns the SimpleCov::SourceFile for the given path, or nil if no
76
+ # matching file is in this result. The path is resolved against
77
+ # SimpleCov.root, so callers can pass either an absolute path or a
78
+ # project-relative one.
79
+ def source_file_for(path)
80
+ target = File.expand_path(path, SimpleCov.root)
81
+ files.find { |file| file.filename == target }
82
+ end
83
+
84
+ # Returns the {line:/branch:/method:} coverage_statistics hash for the
85
+ # given file path, or nil if no matching source file is in this
86
+ # result. See SimpleCov::Result#source_file_for for path resolution.
87
+ def coverage_for(path)
88
+ source_file_for(path)&.coverage_statistics
89
+ end
90
+
91
+ # Returns a Hash of groups for this result. Define groups using SimpleCov.group 'Models', 'app/models'
92
+ def groups
93
+ @groups ||= SimpleCov.grouped(files, groups: @groups_config)
94
+ end
95
+
96
+ # Applies the configured SimpleCov.formatter on this result. Returns
97
+ # nil if formatting has been opted out of (`SimpleCov.formatter false`
98
+ # / `SimpleCov.formatters []`) — the cheap path for non-final
99
+ # processes in a parallel CI run, which only need their
100
+ # `.resultset.json` on disk. See #964.
101
+ def format!
102
+ formatter = SimpleCov.formatter
103
+ return nil if formatter.nil?
104
+
105
+ formatter.new.format(self)
106
+ end
107
+
108
+ # Defines when this result has been created. Defaults to Time.now
109
+ def created_at
110
+ @created_at ||= Time.now
111
+ end
112
+
113
+ # The command name that launched this result.
114
+ # Delegated to SimpleCov.command_name if not set manually
115
+ def command_name
116
+ @command_name ||= SimpleCov.command_name
117
+ end
118
+
119
+ # Returns a hash representation of this Result that can be used for marshalling it into JSON
120
+ def to_hash
121
+ {
122
+ command_name => {
123
+ "coverage" => coverage,
124
+ "timestamp" => created_at.to_i
125
+ }
126
+ }
127
+ end
128
+
129
+ # Loads a SimpleCov::Result#to_hash dump
130
+ def self.from_hash(hash)
131
+ hash.map do |command_name, data|
132
+ new(data.fetch("coverage"), command_name: command_name, created_at: Time.at(data["timestamp"]))
133
+ end
134
+ end
135
+
136
+ private
137
+
138
+ def warn_about_missing_source_files(missing, input_size)
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 (each one stores its own
143
+ # slice), so without this gate the warning prints once per worker — this
144
+ # is the same signal SimpleCov uses to pick the process that runs the
145
+ # report and threshold checks. It's intentionally not gated on
146
+ # print_errors: the default at_fork sets print_errors false on workers,
147
+ # and in many parallel runners the final-report process is itself a
148
+ # worker, so a print_errors gate would suppress the one warning we want.
149
+ # See issues #980 and #1171.
150
+ return unless SimpleCov.final_result_process?
151
+
152
+ MissingSourceFilesReporter.new(
153
+ missing,
154
+ input_size: input_size,
155
+ every_entry_dropped: @files.empty? && missing.size == input_size
156
+ ).warn!
157
+ end
158
+
159
+ def coverage
160
+ original_result.slice(*filenames)
161
+ end
162
+
163
+ # Applies the given filter chain to `@files`, dropping each source
164
+ # file that any filter matches.
165
+ def apply_filters!(filters)
166
+ filters.each do |filter|
167
+ @files = SimpleCov::FileList.new(@files.reject { |source_file| filter.matches?(source_file) })
168
+ end
169
+ end
170
+
171
+ # When any `cover` matcher is configured, restrict `@files` to source
172
+ # files matching at least one of them. With no cover matchers configured
173
+ # this is a no-op, preserving the historical "everything required, then
174
+ # filtered" universe.
175
+ def apply_cover_filters!(cover_filters)
176
+ return if cover_filters.empty?
177
+
178
+ @files = SimpleCov::FileList.new(
179
+ @files.select { |source_file| cover_filters.any? { |filter| filter.matches?(source_file) } }
180
+ )
181
+ end
182
+ end
183
+ end