Module: SimpleCov::Formatter::CoverageJSONWriter
- Defined in:
- lib/simplecov/formatter/coverage_json_writer.rb,
sig/simplecov.rbs
Overview
Shared writer for the coverage.json artifact (JSONFormatter's report and HTMLFormatter's side file): one pretty-printed binary write plus the issue-#1171 concurrent-overwrite warning.
Constant Summary collapse
- FILENAME =
"coverage.json"- META_SCAN_BYTES =
The previous report can embed the project's entire source text, so the overwrite check bounds how much of it is read looking for the meta object before falling back to a full parse.
64 * 1024
Class Method Summary collapse
- .existing_meta(path) ⇒ Object
- .parse_meta(path) ⇒ Object
- .parse_meta_full(path) ⇒ Object
-
.parse_meta_head(path) ⇒ Object
The meta object is flat and sits at the head of every file this module writes, so the common case parses just that slice instead of a multi-megabyte report.
-
.warn_if_concurrent_overwrite(path, result) ⇒ Object
Warns when the existing coverage.json has a timestamp newer than this process's start time — a strong signal that a sibling test process (e.g., parallel_tests) wrote it while we were running, and that our write is about to clobber their data.
- .write(output_path, hash, result) ⇒ Object
Instance Method Summary collapse
-
#self?.existing_meta ⇒ { timestamp: untyped, command_name: untyped }?
timestamp stays untyped (not Time) so the comparison sites don't require the stdlib
timeextension sigs; the value is produced by Time.iso8601 at runtime. - #self?.parse_meta ⇒ Hash[Symbol, untyped]?
- #self?.parse_meta_full ⇒ Hash[Symbol, untyped]?
-
#self?.parse_meta_head ⇒ Hash[Symbol, untyped]?
Bounded head read that parses just the flat meta object of files this module wrote; nil on any miss so parse_meta falls back to a full parse.
-
#self?.warn_if_concurrent_overwrite ⇒ void
Warns when an existing coverage.json is newer than this process's start time (a concurrent sibling writer).
- #self?.write ⇒ String
Class Method Details
.existing_meta(path) ⇒ Object
57 58 59 60 61 62 63 64 65 66 67 |
# File 'lib/simplecov/formatter/coverage_json_writer.rb', line 57 def (path) return nil unless File.exist?(path) = (path) or return nil = [:timestamp] or return nil {timestamp: Time.iso8601(), command_name: [:command_name]} rescue ArgumentError # An unparseable timestamp disables the check for this file. nil end |
.parse_meta(path) ⇒ Object
69 70 71 |
# File 'lib/simplecov/formatter/coverage_json_writer.rb', line 69 def (path) (path) || (path) end |
.parse_meta_full(path) ⇒ Object
89 90 91 92 93 94 |
# File 'lib/simplecov/formatter/coverage_json_writer.rb', line 89 def (path) parsed = JSON.parse(File.read(path), symbolize_names: true) parsed.is_a?(Hash) ? parsed[:meta] : nil rescue JSON::ParserError nil end |
.parse_meta_head(path) ⇒ Object
The meta object is flat and sits at the head of every file this module writes, so the common case parses just that slice instead of a multi-megabyte report. A miss (foreign key order, a brace inside a meta string) falls back to the full parse.
77 78 79 80 81 82 83 84 85 86 87 |
# File 'lib/simplecov/formatter/coverage_json_writer.rb', line 77 def (path) head = File.read(path, META_SCAN_BYTES).to_s slice = head[/"meta"\s*:\s*(\{.*?\})/m, 1] return nil unless slice # The captured slice is always a JSON object, so a successful parse # is always a Hash. JSON.parse(slice, symbolize_names: true) rescue JSON::ParserError nil end |
.warn_if_concurrent_overwrite(path, result) ⇒ Object
Warns when the existing coverage.json has a timestamp newer than this process's start time — a strong signal that a sibling test process (e.g., parallel_tests) wrote it while we were running, and that our write is about to clobber their data.
38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
# File 'lib/simplecov/formatter/coverage_json_writer.rb', line 38 def warn_if_concurrent_overwrite(path, result) start_time = SimpleCov.process_start_time or return existing = (path) or return return unless existing[:timestamp] > start_time # Both formatters write coverage.json through this method, so when # they are configured together the file found here was just written # by our own run, not a concurrent one. A matching command_name # means the same merged result, so there's nothing to lose by # overwriting. See issue #1171. return if existing[:command_name] == result.command_name warn "simplecov: #{path} was written at #{existing[:timestamp].iso8601} — after " \ "this process started at #{start_time.iso8601}. Overwriting " \ "likely loses coverage data from a concurrent test run. For " \ "parallel test setups, use SimpleCov::ResultMerger or run a single " \ "collation step after all workers finish." end |
.write(output_path, hash, result) ⇒ Object
27 28 29 30 31 32 |
# File 'lib/simplecov/formatter/coverage_json_writer.rb', line 27 def write(output_path, hash, result) path = File.join(output_path, FILENAME) warn_if_concurrent_overwrite(path, result) AtomicFile.write(path, JSON.pretty_generate(hash), binary: true) path end |
Instance Method Details
#self?.existing_meta ⇒ { timestamp: untyped, command_name: untyped }?
timestamp stays untyped (not Time) so the comparison sites don't
require the stdlib time extension sigs; the value is produced by
Time.iso8601 at runtime.
1472 |
# File 'sig/simplecov.rbs', line 1472
def self?.existing_meta: (String path) -> ({ timestamp: untyped, command_name: untyped })?
|
#self?.parse_meta ⇒ Hash[Symbol, untyped]?
1474 |
# File 'sig/simplecov.rbs', line 1474
def self?.parse_meta: (String path) -> Hash[Symbol, untyped]?
|
#self?.parse_meta_full ⇒ Hash[Symbol, untyped]?
1481 |
# File 'sig/simplecov.rbs', line 1481
def self?.parse_meta_full: (String path) -> Hash[Symbol, untyped]?
|
#self?.parse_meta_head ⇒ Hash[Symbol, untyped]?
Bounded head read that parses just the flat meta object of files this module wrote; nil on any miss so parse_meta falls back to a full parse.
1479 |
# File 'sig/simplecov.rbs', line 1479
def self?.parse_meta_head: (String path) -> Hash[Symbol, untyped]?
|
#self?.warn_if_concurrent_overwrite ⇒ void
This method returns an undefined value.
Warns when an existing coverage.json is newer than this process's start time (a concurrent sibling writer). See issue #1171.
1467 |
# File 'sig/simplecov.rbs', line 1467
def self?.warn_if_concurrent_overwrite: (String path, SimpleCov::Result result) -> void
|
#self?.write ⇒ String
1463 |
# File 'sig/simplecov.rbs', line 1463
def self?.write: (untyped output_path, Hash[Symbol, untyped] hash, SimpleCov::Result result) -> String
|