Class: SimpleCov::Formatter::JSONFormatter

Inherits:
Base
  • Object
show all
Defined in:
lib/simplecov/formatter/json_formatter.rb,
lib/simplecov/formatter/json_formatter/errors_formatter.rb,
lib/simplecov/formatter/json_formatter/result_hash_formatter.rb,
lib/simplecov/formatter/json_formatter/source_file_formatter.rb,
sig/simplecov.rbs

Overview

Writes coverage results as JSON to coverage/coverage.json.

Defined Under Namespace

Classes: ErrorsFormatter, ResultHashFormatter, SourceFileFormatter

Constant Summary collapse

FILENAME =

Returns:

  • ("coverage.json")
"coverage.json"

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from Base

#displayable_output_path, #initialize, #output_message, #output_path, #relative_or_absolute_output_path, #stats_line

Constructor Details

This class inherits a constructor from SimpleCov::Formatter::Base

Class Method Details

.build_hash(result, include_source: SimpleCov.source_in_json) ⇒ Hash[Symbol, untyped]

The hash serialized to coverage.json ($schema, meta, total, coverage, groups, errors). include_source: defaults to SimpleCov.source_in_json; pass true to force the per-file source arrays regardless of the global setting.

Parameters:

  • result (SimpleCov::Result)
  • include_source: (Boolean) (defaults to: SimpleCov.source_in_json)

Returns:

  • (Hash[Symbol, untyped])


21
22
23
# File 'lib/simplecov/formatter/json_formatter.rb', line 21

def self.build_hash(result, include_source: SimpleCov.source_in_json)
  ResultHashFormatter.new(result, include_source: include_source).format
end

Instance Method Details

#entry_point_filenameString

Returns:

  • (String)


43
44
45
# File 'lib/simplecov/formatter/json_formatter.rb', line 43

def entry_point_filename
  FILENAME
end

#existing_meta(path) ⇒ { 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.

Parameters:

  • path (String)

Returns:

  • ({ timestamp: untyped, command_name: untyped }, nil)


1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
# File 'sig/simplecov.rbs', line 1374

def existing_meta(path)
  return nil unless File.exist?(path)

  meta = JSON.parse(File.read(path), symbolize_names: true)
  timestamp = meta.dig(:meta, :timestamp)
  return nil unless timestamp

  {timestamp: Time.iso8601(timestamp), command_name: meta.dig(:meta, :command_name)}
rescue JSON::ParserError, ArgumentError
  nil
end

#format(result) ⇒ void

This method returns an undefined value.

Parameters:



25
26
27
28
29
30
31
32
33
34
35
# File 'lib/simplecov/formatter/json_formatter.rb', line 25

def format(result)
  FileUtils.mkdir_p(output_path)
  path = File.join(output_path, FILENAME)
  warn_if_concurrent_overwrite(path, result)
  File.write(path, JSON.pretty_generate(self.class.build_hash(result)))
  # stderr, not stdout: this is a status message, not the program's
  # output. Keeps the line out of pipelines like `rspec -f json`. And
  # $stderr.puts, not `warn`: a status line should not reach
  # `Warning.warn` hooks or vanish under `-W0` (see #1225).
  $stderr.puts output_message(result) unless @silent # rubocop:disable Style/StderrPuts
end

#message_prefixString

Returns:

  • (String)


39
40
41
# File 'lib/simplecov/formatter/json_formatter.rb', line 39

def message_prefix
  "JSON "
end

#warn_if_concurrent_overwrite(path, result) ⇒ 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.

Parameters:



51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
# File 'lib/simplecov/formatter/json_formatter.rb', line 51

def warn_if_concurrent_overwrite(path, result)
  start_time = SimpleCov.process_start_time or return
  existing = existing_meta(path) or return
  return unless existing[:timestamp] > start_time

  # The HTML formatter also writes coverage.json (it shares the file as
  # a side artifact), so when both formatters are configured the file we
  # find 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