class SimpleCov::Formatter::HTMLFormatter
coverage.json and coverage_data.js from the same in-memory hash.
Uses JSONFormatter.build_hash to serialize the result, then writes both
alongside pre-compiled static assets (index.html, application.js/css).
Generates an HTML coverage report by writing a coverage_data.js file
def atomic_write(dest, content)
/nix/store) are replaced cleanly instead of triggering EACCES
- read-only existing files (e.g. assets shipped at 0444 from
- parallel writers can't race on an unlink-then-write window, and
atomic and overwrite-safe, so:
same directory, then `File.rename` onto the final path. rename is
Write `content` at `dest` via a uniquely-named temp file in the
def atomic_write(dest, content) temp = "#{dest}.#{Process.pid}.#{rand(2**32).to_s(36)}" begin File.binwrite(temp, content) File.rename(temp, dest) ensure FileUtils.rm_f(temp) end end
def copy_static_assets(dest_dir = output_path)
def copy_static_assets(dest_dir = output_path) Dir[File.join(public_dir, "*")].each do |src| atomic_write(File.join(dest_dir, File.basename(src)), File.binread(src)) end end
def entry_point_filename
def entry_point_filename "index.html" end
def format(result)
def format(result) # `coverage_data.js` feeds the client-side viewer, which renders # source from the embedded array — it always needs `source`, # regardless of `SimpleCov.source_in_json`. The side-file # `coverage.json` honors the setting so downstream tools that # read source from disk can opt into a smaller payload. When # the setting is at its default (true), the two files share a # single serialization. FileUtils.mkdir_p(output_path) viewer_json = JSON.pretty_generate(JSONFormatter.build_hash(result, include_source: true)) coverage_json = SimpleCov.source_in_json ? viewer_json : JSON.pretty_generate(JSONFormatter.build_hash(result)) atomic_write(File.join(output_path, JSONFormatter::FILENAME), coverage_json) atomic_write(File.join(output_path, DATA_FILENAME), "window.SIMPLECOV_DATA = #{viewer_json};\n") copy_static_assets # 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
def format_from_json(json_path, output_dir)
Generate HTML from a pre-existing coverage.json file without
def format_from_json(json_path, output_dir) FileUtils.mkdir_p(output_dir) json = File.read(json_path) atomic_write(File.join(output_dir, DATA_FILENAME), "window.SIMPLECOV_DATA = #{json};\n") copy_static_assets(output_dir) end
def public_dir
def public_dir # `.to_s` collapses `__dir__`'s nil arm (only possible under eval, # which can't happen for a file on disk) so the path is a String. File.join(__dir__.to_s, "html_formatter/public/") end