1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164
|
# frozen_string_literal: true
require "erb"
require "fileutils"
require "digest/sha1"
require "time"
# Ensure we are using a compatible version of SimpleCov
major, minor, patch = SimpleCov::VERSION.scan(/\d+/).first(3).map(&:to_i)
if major < 0 || minor < 9 || patch < 0
raise "The version of SimpleCov you are using is too old. " \
"Please update with `gem install simplecov` or `bundle update simplecov`"
end
module SimpleCov
module Formatter
class HTMLFormatter
# Only have a few content types, just hardcode them
CONTENT_TYPES = {
".js" => "text/javascript",
".png" => "image/png",
".gif" => "image/gif",
".css" => "text/css",
}.freeze
def initialize
@branchable_result = SimpleCov.branch_coverage?
@templates = {}
@inline_assets = !ENV["SIMPLECOV_INLINE_ASSETS"].nil?
@public_assets_dir = File.join('/usr/share/ruby-simplecov-html/public/')
end
def format(result)
unless @inline_assets
Dir[File.join(@public_assets_dir, "*")].each do |path|
FileUtils.cp_r(path, asset_output_path, remove_destination: true)
end
end
File.open(File.join(output_path, "index.html"), "wb") do |file|
file.puts template("layout").result(binding)
end
puts output_message(result)
end
private
def branchable_result?
# cached in initialize because we truly look it up a whole bunch of times
# and it's easier to cache here then in SimpleCov because there we might
# still enable/disable branch coverage criterion
@branchable_result
end
def line_status?(source_file, line)
if branchable_result? && source_file.line_with_missed_branch?(line.number)
"missed-branch"
else
line.status
end
end
def output_message(result)
output = "Coverage report generated for #{result.command_name} to #{output_path}."
output += "\nLine Coverage: #{result.covered_percent.round(2)}% (#{result.covered_lines} / #{result.total_lines})"
output += "\nBranch Coverage: #{result.coverage_statistics[:branch].percent.round(2)}% (#{result.covered_branches} / #{result.total_branches})" if branchable_result?
output
end
# Returns the an erb instance for the template of given name
def template(name)
@templates[name] ||= ERB.new(File.read(File.join('/usr/share/ruby-simplecov-html/views/', "#{name}.erb")))
end
def output_path
SimpleCov.coverage_path
end
def asset_output_path
return @asset_output_path if defined?(@asset_output_path) && @asset_output_path
@asset_output_path = File.join(output_path, "assets", SimpleCov::Formatter::HTMLFormatter::VERSION)
FileUtils.mkdir_p(@asset_output_path)
@asset_output_path
end
def assets_path(name)
return asset_inline(name) if @inline_assets
File.join("./assets", SimpleCov::Formatter::HTMLFormatter::VERSION, name)
end
def asset_inline(name)
path = File.join(@public_assets_dir, name)
# Equivalent to `Base64.strict_encode64(File.read(path))` but without depending on Base64
base64_content = [File.read(path)].pack("m0")
content_type = CONTENT_TYPES[File.extname(name)]
"data:#{content_type};base64,#{base64_content}"
end
# Returns the html for the given source_file
def formatted_source_file(source_file)
template("source_file").result(binding)
rescue Encoding::CompatibilityError => e
puts "Encoding problems with file #{source_file.filename}. Simplecov/ERB can't handle non ASCII characters in filenames. Error: #{e.message}."
end
# Returns a table containing the given source files
def formatted_file_list(title, source_files)
title_id = title.gsub(/^[^a-zA-Z]+/, "").gsub(/[^a-zA-Z0-9\-_]/, "")
# Silence a warning by using the following variable to assign to itself:
# "warning: possibly useless use of a variable in void context"
# The variable is used by ERB via binding.
title_id = title_id # rubocop:disable Lint/SelfAssignment
template("file_list").result(binding)
end
def covered_percent(percent)
template("covered_percent").result(binding)
end
def coverage_css_class(covered_percent)
if covered_percent > 90
"green"
elsif covered_percent > 80
"yellow"
else
"red"
end
end
def strength_css_class(covered_strength)
if covered_strength > 1
"green"
elsif covered_strength == 1
"yellow"
else
"red"
end
end
# Return a (kind of) unique id for the source file given. Uses SHA1 on path for the id
def id(source_file)
Digest::SHA1.hexdigest(source_file.filename)
end
def timeago(time)
"<abbr class=\"timeago\" title=\"#{time.iso8601}\">#{time.iso8601}</abbr>"
end
def shortened_filename(source_file)
source_file.filename.sub(SimpleCov.root, ".").gsub(/^\.\//, "")
end
def link_to_source_file(source_file)
%(<a href="##{id source_file}" class="src_link" title="#{shortened_filename source_file}">#{shortened_filename source_file}</a>)
end
end
end
end
require "simplecov-html/version"
|