diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 631ba3b..862a44b 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -10,13 +10,12 @@ jobs: matrix: ruby-version: - - '2.5' - '2.6' - '2.7' - '3.0' - '3.1' - ruby-head - - jruby + - jruby-9.3 - jruby-head steps: diff --git a/.gitignore b/.gitignore index 6e7d7c2..1ac1afb 100644 --- a/.gitignore +++ b/.gitignore @@ -24,3 +24,5 @@ coverage rdoc pkg .sass-cache + +.ruby-version diff --git a/.rubocop.yml b/.rubocop.yml index 96312fc..0f18633 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -5,9 +5,6 @@ AllCops: Bundler/DuplicatedGem: Enabled: false -Gemspec/RequiredRubyVersion: - Enabled: false - Layout/AccessModifierIndentation: EnforcedStyle: outdent @@ -74,3 +71,156 @@ Style/TrailingCommaInHashLiteral: Style/TrailingCommaInArrayLiteral: EnforcedStyleForMultiline: "comma" + +Gemspec/DateAssignment: # new in 1.10 + Enabled: true + +Layout/LineEndStringConcatenationIndentation: # new in 1.18 + Enabled: true + +Layout/SpaceBeforeBrackets: # new in 1.7 + Enabled: true + +Lint/AmbiguousAssignment: # new in 1.7 + Enabled: true + +Lint/AmbiguousOperatorPrecedence: # new in 1.21 + Enabled: true + +Lint/AmbiguousRange: # new in 1.19 + Enabled: true + +Lint/DeprecatedConstants: # new in 1.8 + Enabled: true + +Lint/DuplicateBranch: # new in 1.3 + Enabled: true + +Lint/DuplicateRegexpCharacterClassElement: # new in 1.1 + Enabled: true + +Lint/EmptyBlock: # new in 1.1 + Enabled: true + +Lint/EmptyClass: # new in 1.3 + Enabled: true + +Lint/EmptyInPattern: # new in 1.16 + Enabled: true + +Lint/IncompatibleIoSelectWithFiberScheduler: # new in 1.21 + Enabled: true + +Lint/LambdaWithoutLiteralBlock: # new in 1.8 + Enabled: true + +Lint/NoReturnInBeginEndBlocks: # new in 1.2 + Enabled: true + +Lint/NumberedParameterAssignment: # new in 1.9 + Enabled: true + +Lint/OrAssignmentToConstant: # new in 1.9 + Enabled: true + +Lint/RedundantDirGlobSort: # new in 1.8 + Enabled: true + +Lint/RequireRelativeSelfPath: # new in 1.22 + Enabled: true + +Lint/SymbolConversion: # new in 1.9 + Enabled: true + +Lint/ToEnumArguments: # new in 1.1 + Enabled: true + +Lint/TripleQuotes: # new in 1.9 + Enabled: true + +Lint/UnexpectedBlockArity: # new in 1.5 + Enabled: true + +Lint/UnmodifiedReduceAccumulator: # new in 1.1 + Enabled: true + +Lint/UselessRuby2Keywords: # new in 1.23 + Enabled: true + +Naming/BlockForwarding: # new in 1.24 + Enabled: true + +Security/IoMethods: # new in 1.22 + Enabled: true + +Style/ArgumentsForwarding: # new in 1.1 + Enabled: true + +Style/CollectionCompact: # new in 1.2 + Enabled: true + +Style/DocumentDynamicEvalDefinition: # new in 1.1 + Enabled: true + +Style/EndlessMethod: # new in 1.8 + Enabled: true + +Style/FileRead: # new in 1.24 + Enabled: true + +Style/FileWrite: # new in 1.24 + Enabled: true + +Style/HashConversion: # new in 1.10 + Enabled: true + +Style/HashExcept: # new in 1.7 + Enabled: true + +Style/IfWithBooleanLiteralBranches: # new in 1.9 + Enabled: true + +Style/InPatternThen: # new in 1.16 + Enabled: true + +Style/MapToHash: # new in 1.24 + Enabled: true + +Style/MultilineInPatternThen: # new in 1.16 + Enabled: true + +Style/NegatedIfElseCondition: # new in 1.2 + Enabled: true + +Style/NestedFileDirname: # new in 1.26 + Enabled: true + +Style/NilLambda: # new in 1.3 + Enabled: true + +Style/NumberedParameters: # new in 1.22 + Enabled: true + +Style/NumberedParametersLimit: # new in 1.22 + Enabled: true + +Style/OpenStructUse: # new in 1.23 + Enabled: true + +Style/QuotedSymbols: # new in 1.16 + Enabled: true + +Style/RedundantArgument: # new in 1.4 + Enabled: true + +Style/RedundantSelfAssignmentBranch: # new in 1.19 + Enabled: true + +Style/SelectByRegexp: # new in 1.22 + Enabled: true + +Style/StringChars: # new in 1.12 + Enabled: true + +Style/SwapValues: # new in 1.1 + Enabled: true diff --git a/Gemfile.lock b/Gemfile.lock index ca7a7be..e66b526 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -63,4 +63,4 @@ DEPENDENCIES yui-compressor BUNDLED WITH - 2.3.10 + 2.4.3 diff --git a/assets/stylesheets/application.css b/assets/stylesheets/application.css index 0858ab1..4f33644 100644 --- a/assets/stylesheets/application.css +++ b/assets/stylesheets/application.css @@ -1,3 +1,3 @@ //= require ./reset.css //= require_directory ./plugins/ -//= require ./screen.css \ No newline at end of file +//= require ./screen.css diff --git a/assets/stylesheets/screen.css b/assets/stylesheets/screen.css index 81b0127..65aad48 100644 --- a/assets/stylesheets/screen.css +++ b/assets/stylesheets/screen.css @@ -319,3 +319,8 @@ thead th { .source_table .missed-branch:nth-child(even) { background-color: #cc6e6e; } + +.t-missed-method-summary ul { + margin: 0; + padding-left: 2em; +} diff --git a/lib/simplecov-html.rb b/lib/simplecov-html.rb index 7d54fd3..ad96c32 100644 --- a/lib/simplecov-html.rb +++ b/lib/simplecov-html.rb @@ -11,14 +11,15 @@ 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`" + "Please update with `gem install simplecov` or `bundle update simplecov`" end module SimpleCov module Formatter - class HTMLFormatter + class HTMLFormatter # rubocop:disable Metrics/ClassLength def initialize - @branchable_result = SimpleCov.branch_coverage? + @branch_coverage = SimpleCov.branch_coverage? + @method_coverage = SimpleCov.method_coverage? @templates = {} @inline_assets = !ENV["SIMPLECOV_INLINE_ASSETS"].nil? @public_assets_dir = File.join(File.dirname(__FILE__), "../public/") @@ -38,20 +39,31 @@ def format(result) end def output_message(result) - str = "Coverage report generated for #{result.command_name} to #{output_path}. #{result.covered_lines} / #{result.total_lines} LOC (#{result.covered_percent.round(2)}%) covered." - str += " #{result.covered_branches} / #{result.total_branches} branches (#{result.coverage_statistics[:branch].percent.round(2)}%) covered." if branchable_result? - str + parts = [] + parts << "Coverage report generated for #{result.command_name} to #{output_path}" + parts << "Line coverage: #{render_stats(result, :line)}" + parts << "Branch coverage: #{render_stats(result, :branch)}" if branch_coverage? + parts << "Method coverage: #{render_stats(result, :method)}" if method_coverage? + + parts.join("\n") + end + + def branch_coverage? + # 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 + @branch_coverage end - def branchable_result? + def method_coverage? # 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 + @method_coverage end def line_status?(source_file, line) - if branchable_result? && source_file.line_with_missed_branch?(line.number) + if branch_coverage? && source_file.line_with_missed_branch?(line.number) "missed-branch" else line.status @@ -83,6 +95,10 @@ def assets_path(name) File.join("./assets", SimpleCov::Formatter::HTMLFormatter::VERSION, name) end + def to_id(value) + value.gsub(/^[^a-zA-Z]+/, "").gsub(/[^a-zA-Z0-9\-_]/, "") + end + def asset_inline(name) path = File.join(@public_assets_dir, name) @@ -94,7 +110,7 @@ def asset_inline(name) ".css" => "text/css", }[File.extname(name)] - base64_content = Base64.strict_encode64 File.open(path).read + base64_content = Base64.strict_encode64 File.read(path) "data:#{content_type};base64,#{base64_content}" end @@ -107,7 +123,6 @@ def formatted_source_file(source_file) # 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\-_]/, "") template("file_list").result(binding) end @@ -151,6 +166,17 @@ def shortened_filename(source_file) def link_to_source_file(source_file) %(#{shortened_filename source_file}) end + + def render_stats(result, criterion) + stats = result.coverage_statistics.fetch(criterion) + + Kernel.format( + "%d / %d (%.2f%%)", + covered: stats.covered, + total: stats.total, + percent: stats.percent + ) + end end end end diff --git a/simplecov-html.gemspec b/simplecov-html.gemspec index b2d75d8..10b297e 100644 --- a/simplecov-html.gemspec +++ b/simplecov-html.gemspec @@ -14,7 +14,7 @@ Gem::Specification.new do |gem| gem.summary = gem.description gem.license = "MIT" - gem.required_ruby_version = ">= 2.4" + gem.required_ruby_version = ">= 2.6" gem.files = `git ls-files`.split("\n") gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n") diff --git a/views/file_list.erb b/views/file_list.erb index 0cd97b6..e8f362f 100644 --- a/views/file_list.erb +++ b/views/file_list.erb @@ -1,4 +1,4 @@ -
+

<%= title %> ( @@ -13,7 +13,7 @@ )

- +
<%= source_files.length %> files in total. @@ -26,7 +26,7 @@ (<%= covered_percent(source_files.covered_percent) %>)
- <% if branchable_result? %> + <% if branch_coverage? %>
<%= source_files.total_branches %> total branches, <%= source_files.covered_branches %> branches covered and @@ -35,6 +35,15 @@
<% end %> + <% if method_coverage? %> +
+ <%= source_files.total_methods %> total methods, + <%= source_files.covered_methods %> methods covered and + <%= source_files.missed_methods %> methods missed. + (<%= covered_percent(source_files.method_covered_percent) %>) +
+ <% end %> +
@@ -46,12 +55,18 @@ - <% if branchable_result? %> + <% if branch_coverage? %> <% end %> + <% if method_coverage? %> + + + + + <% end %> @@ -64,12 +79,18 @@ - <% if branchable_result? %> + <% if branch_coverage? %> <% end %> + <% if method_coverage? %> + + + + + <% end %> <% end %> diff --git a/views/layout.erb b/views/layout.erb index 72c60af..2c77744 100644 --- a/views/layout.erb +++ b/views/layout.erb @@ -8,7 +8,7 @@ " /> - > + >
loading
diff --git a/views/source_file.erb b/views/source_file.erb index e4f3a71..7189ed8 100644 --- a/views/source_file.erb +++ b/views/source_file.erb @@ -6,20 +6,28 @@ lines covered - <% if branchable_result? %> + <% if branch_coverage? %>

<%= covered_percent(source_file.branches_coverage_percent) %> branches covered

<% end %> + <% if method_coverage? %> +

+ <%= covered_percent(source_file.methods_coverage_percent) %> + methods covered +

+ <% end %> + +
<%= source_file.lines_of_code %> relevant lines. <%= source_file.covered_lines.count %> lines covered and <%= source_file.missed_lines.count %> lines missed.
- <% if branchable_result? %> + <% if branch_coverage? %>
<%= source_file.total_branches.count %> total branches, <%= source_file.covered_branches.count %> branches covered and @@ -27,6 +35,25 @@
<% end %> + <% if method_coverage? %> +
+ <%= source_file.total_methods.count %> total methods, + <%= source_file.covered_methods.count %> methods covered and + <%= source_file.missed_methods.count %> methods missed. +
+ <% end %> + + <% if method_coverage? && source_file.missed_methods.any? %> +
+ Missed methods: +
    + <% source_file.missed_methods.each do |missed_method| %> +
  • <%= CGI.escapeHTML(missed_method.to_s) %>
  • + <% end %> +
+
+ <% end %> +
@@ -40,7 +67,7 @@
               skipped
             <% end %>
 
-            <% if branchable_result? %>
+            <% if branch_coverage? %>
               <% source_file.branches_for_line(line.number).each do |branch_type, hit_count| %>
                 
                   <%= branch_type %>: <%= hit_count %>
Lines covered Lines missed Avg. Hits / LineBranch Coverage Branches Covered branches Missed branches Method CoverageMethodsCovered methodsMissed methods
<%= source_file.covered_lines.count %> <%= source_file.missed_lines.count %> <%= sprintf("%.2f", source_file.covered_strength.round(2)) %><%= sprintf("%.2f", source_file.branches_coverage_percent.round(2)) %> % <%= source_file.total_branches.count %> <%= source_file.covered_branches.count %> <%= source_file.missed_branches.count %><%= sprintf("%.2f", source_file.methods_coverage_percent.round(2)) %> %<%= source_file.total_methods.count %><%= source_file.covered_methods.count %><%= source_file.missed_methods.count %>