deep-cover 0.1.16 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +2 -0
- data/.rubocop.yml +3 -8
- data/.travis.yml +4 -4
- data/CHANGELOG.md +10 -0
- data/Gemfile +3 -1
- data/README.md +9 -4
- data/Rakefile +6 -3
- data/deep_cover.gemspec +2 -2
- data/lib/deep_cover.rb +10 -0
- data/lib/deep_cover/analyser.rb +1 -2
- data/lib/deep_cover/analyser/base.rb +32 -0
- data/lib/deep_cover/analyser/branch.rb +19 -4
- data/lib/deep_cover/analyser/node.rb +52 -0
- data/lib/deep_cover/analyser/per_char.rb +18 -1
- data/lib/deep_cover/analyser/stats.rb +54 -0
- data/lib/deep_cover/backports.rb +1 -0
- data/lib/deep_cover/base.rb +17 -1
- data/lib/deep_cover/builtin_takeover.rb +5 -0
- data/lib/deep_cover/cli/deep_cover.rb +2 -1
- data/lib/deep_cover/cli/instrumented_clone_reporter.rb +12 -10
- data/lib/deep_cover/config.rb +22 -8
- data/lib/deep_cover/core_ext/coverage_replacement.rb +22 -18
- data/lib/deep_cover/coverage.rb +3 -203
- data/lib/deep_cover/coverage/analysis.rb +36 -0
- data/lib/deep_cover/coverage/base.rb +91 -0
- data/lib/deep_cover/coverage/istanbul.rb +34 -0
- data/lib/deep_cover/coverage/persistence.rb +93 -0
- data/lib/deep_cover/covered_code.rb +12 -22
- data/lib/deep_cover/custom_requirer.rb +6 -2
- data/lib/deep_cover/node/base.rb +1 -1
- data/lib/deep_cover/node/case.rb +13 -2
- data/lib/deep_cover/node/exceptions.rb +2 -2
- data/lib/deep_cover/node/if.rb +21 -2
- data/lib/deep_cover/node/mixin/flow_accounting.rb +1 -0
- data/lib/deep_cover/node/send.rb +9 -2
- data/lib/deep_cover/node/short_circuit.rb +10 -0
- data/lib/deep_cover/parser_ext/range.rb +4 -4
- data/lib/deep_cover/reporter/html.rb +15 -0
- data/lib/deep_cover/reporter/html/base.rb +14 -0
- data/lib/deep_cover/reporter/html/index.rb +78 -0
- data/lib/deep_cover/reporter/html/site.rb +78 -0
- data/lib/deep_cover/reporter/html/source.rb +136 -0
- data/lib/deep_cover/reporter/html/template/assets/32px.png +0 -0
- data/lib/deep_cover/reporter/html/template/assets/40px.png +0 -0
- data/lib/deep_cover/reporter/html/template/assets/deep_cover.css.sass +338 -0
- data/lib/deep_cover/reporter/html/template/assets/jquery-3.2.1.min.js +4 -0
- data/lib/deep_cover/reporter/html/template/assets/jquery-3.2.1.min.map +1 -0
- data/lib/deep_cover/reporter/html/template/assets/jstree.css +1108 -0
- data/lib/deep_cover/reporter/html/template/assets/jstree.js +8424 -0
- data/lib/deep_cover/reporter/html/template/assets/jstreetable.js +1069 -0
- data/lib/deep_cover/reporter/html/template/assets/throbber.gif +0 -0
- data/lib/deep_cover/reporter/html/template/index.html.erb +75 -0
- data/lib/deep_cover/reporter/html/template/source.html.erb +35 -0
- data/lib/deep_cover/reporter/html/tree.rb +55 -0
- data/lib/deep_cover/tools/content_tag.rb +11 -0
- data/lib/deep_cover/tools/covered.rb +9 -0
- data/lib/deep_cover/tools/merge.rb +16 -0
- data/lib/deep_cover/tools/render_template.rb +13 -0
- data/lib/deep_cover/tools/transform_keys.rb +9 -0
- data/lib/deep_cover/version.rb +1 -1
- metadata +33 -7
- data/lib/deep_cover/analyser/ignore_uncovered.rb +0 -21
- data/lib/deep_cover/analyser/optionally_covered.rb +0 -19
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: de76a03a06ec4a16d33c5a24a9c510678f83b33a
|
4
|
+
data.tar.gz: db775778d2b7d2c65ce0c299f3ff4f3900796e02
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 246c598d3da7d0fca5d5c4e453dc134f2e36cc59e7529f4148fa159c8e31c3288316e731faec5d08be37167fdd605454f0155b12cea3fdcbd22235f3e9ad9df3
|
7
|
+
data.tar.gz: 96fea731d9cd93158718857999a9da092b9ec619126dc0814b339b8e3c460a224dca594194b35d686b8dbf5c5c4cde03de69bd17a1bcd482e92ab1851bbb4ee7
|
data/.gitignore
CHANGED
data/.rubocop.yml
CHANGED
@@ -103,14 +103,6 @@ Naming/FileName:
|
|
103
103
|
Style/FrozenStringLiteralComment:
|
104
104
|
EnforcedStyle: always
|
105
105
|
|
106
|
-
Style/InverseMethods:
|
107
|
-
InverseMethods:
|
108
|
-
:present?: :blank?,
|
109
|
-
:include?: :exclude?
|
110
|
-
Exclude:
|
111
|
-
- bin/*
|
112
|
-
- gemfiles/*
|
113
|
-
|
114
106
|
Style/NegatedIf:
|
115
107
|
Enabled: false
|
116
108
|
|
@@ -225,3 +217,6 @@ Style/Lambda:
|
|
225
217
|
|
226
218
|
Style/StructInheritance:
|
227
219
|
Enabled: false
|
220
|
+
|
221
|
+
Security/YAMLLoad:
|
222
|
+
Enabled: false
|
data/.travis.yml
CHANGED
@@ -11,9 +11,9 @@ before_install:
|
|
11
11
|
- gem install bundler -v 1.15.4
|
12
12
|
- npm install -g nyc
|
13
13
|
before_script:
|
14
|
-
- rake dev:install
|
14
|
+
- bundle exec rake dev:install
|
15
15
|
script:
|
16
16
|
- rake test:all
|
17
|
-
|
18
|
-
|
19
|
-
|
17
|
+
matrix:
|
18
|
+
allow_failures:
|
19
|
+
- rvm: jruby-9.1.9.0
|
data/CHANGELOG.md
ADDED
data/Gemfile
CHANGED
@@ -7,5 +7,7 @@ git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
|
|
7
7
|
# Specify your gem's dependencies in deep_cover.gemspec
|
8
8
|
gemspec
|
9
9
|
|
10
|
-
|
10
|
+
eval_gemfile 'Gemfile.local' if File.exist?('Gemfile.local')
|
11
|
+
|
12
|
+
gem 'parser', git: 'https://github.com/marcandre/parser.git', branch: 'tree_rewriter_release'
|
11
13
|
gem 'ruby-prof', platforms: :mri
|
data/README.md
CHANGED
@@ -34,13 +34,16 @@ def test_foo
|
|
34
34
|
end
|
35
35
|
```
|
36
36
|
|
37
|
-
##
|
37
|
+
## Examples
|
38
38
|
|
39
|
-
|
39
|
+
These examples are direct outputs from our HTML reporter:
|
40
|
+
|
41
|
+
* [Rails' `activesupport`](https://deep-cover.github.io/rails-cover/activesupport/)
|
42
|
+
* [Rails' `activerecord`](https://deep-cover.github.io/rails-cover/activerecord/)
|
40
43
|
|
41
|
-
|
44
|
+
## Installation
|
42
45
|
|
43
|
-
|
46
|
+
gem install deep-cover
|
44
47
|
|
45
48
|
### Command line interface (for a Rails app or a Gem):
|
46
49
|
|
@@ -50,6 +53,8 @@ An easy way to check coverage, without any configuration needed:
|
|
50
53
|
|
51
54
|
This assumes your project has a `Gemfile`, and that your default `rake` task is set to execute all tests (otherwise set the `--command` option)
|
52
55
|
|
56
|
+
It also uses our builtin HTML reporter. Check the produced `coverage/index.html`.
|
57
|
+
|
53
58
|
### Builtin Coverage (including SimpleCov) users
|
54
59
|
|
55
60
|
Add to your Gemfile `gem 'deep-cover'`, then run `bundle`.
|
data/Rakefile
CHANGED
@@ -26,12 +26,15 @@ namespace :dev do
|
|
26
26
|
commands << 'bundle install --gemfile=spec/full_usage/rails51_project/Gemfile'
|
27
27
|
end
|
28
28
|
commands << 'bundle install --gemfile=spec/cli_fixtures/simple_rails42_app/Gemfile'
|
29
|
+
commands << 'bundle install --gemfile=spec/cli_fixtures/rails_like_gem/Gemfile'
|
29
30
|
|
30
31
|
commands.each do |command|
|
31
32
|
puts "Running: #{command}"
|
32
|
-
|
33
|
-
|
34
|
-
|
33
|
+
Bundler.with_clean_env do
|
34
|
+
unless system(command)
|
35
|
+
puts "Failed to run `#{command}`, see above for details. When it is fixed, try running this rake task again."
|
36
|
+
exit(1)
|
37
|
+
end
|
35
38
|
end
|
36
39
|
puts "Command succeeded: #{command}"
|
37
40
|
end
|
data/deep_cover.gemspec
CHANGED
@@ -12,7 +12,7 @@ Gem::Specification.new do |spec|
|
|
12
12
|
|
13
13
|
spec.summary = 'In depth coverage of your Ruby code.'
|
14
14
|
spec.description = 'expression and branch coverage for Ruby.'
|
15
|
-
spec.homepage = '
|
15
|
+
spec.homepage = 'https://github.com/deep-cover/deep-cover'
|
16
16
|
spec.license = 'MIT'
|
17
17
|
|
18
18
|
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
@@ -45,7 +45,7 @@ Gem::Specification.new do |spec|
|
|
45
45
|
spec.add_development_dependency 'activesupport', '~> 4.0'
|
46
46
|
spec.add_development_dependency 'bundler', '~> 1.15'
|
47
47
|
spec.add_development_dependency 'psych', '>= 2.0'
|
48
|
-
spec.add_development_dependency 'rake', '~>
|
48
|
+
spec.add_development_dependency 'rake', '~> 12.0'
|
49
49
|
spec.add_development_dependency 'rspec', '~> 3.0'
|
50
50
|
spec.add_development_dependency 'rubocop'
|
51
51
|
end
|
data/lib/deep_cover.rb
CHANGED
@@ -2,12 +2,22 @@
|
|
2
2
|
|
3
3
|
# rubocop:disable Style/MixinUsage (See https://github.com/bbatsov/rubocop/issues/5055)
|
4
4
|
module DeepCover
|
5
|
+
# External dependencies (ex parser)
|
5
6
|
require 'parser'
|
6
7
|
require 'term/ansicolor'
|
7
8
|
require 'pry'
|
9
|
+
|
10
|
+
# Bootstrapping
|
8
11
|
require_relative 'deep_cover/backports'
|
9
12
|
require_relative 'deep_cover/tools'
|
13
|
+
|
14
|
+
# Parser
|
15
|
+
silence_warnings do
|
16
|
+
require 'parser/current'
|
17
|
+
end
|
10
18
|
require_relative_dir 'deep_cover/parser_ext'
|
19
|
+
|
20
|
+
# Main
|
11
21
|
require_relative_dir 'deep_cover', except: %w[auto_run builtin_takeover]
|
12
22
|
|
13
23
|
extend Base
|
data/lib/deep_cover/analyser.rb
CHANGED
@@ -2,6 +2,8 @@
|
|
2
2
|
|
3
3
|
module DeepCover
|
4
4
|
module Analyser::Base
|
5
|
+
include Tools::Covered
|
6
|
+
|
5
7
|
attr_reader :source, :options
|
6
8
|
|
7
9
|
def initialize(source, **options)
|
@@ -19,6 +21,10 @@ module DeepCover
|
|
19
21
|
@source.node_runs(node)
|
20
22
|
end
|
21
23
|
|
24
|
+
def node_covered?(node)
|
25
|
+
covered?(node_runs(node))
|
26
|
+
end
|
27
|
+
|
22
28
|
def node_runs_map
|
23
29
|
each_node.map do |node|
|
24
30
|
[node, node_runs(node)]
|
@@ -30,6 +36,32 @@ module DeepCover
|
|
30
36
|
node_runs_map
|
31
37
|
end
|
32
38
|
|
39
|
+
def node_stat_type(node)
|
40
|
+
return :not_executable unless node.executable?
|
41
|
+
case node_runs(node)
|
42
|
+
when nil
|
43
|
+
:ignored
|
44
|
+
when 0
|
45
|
+
:not_executed
|
46
|
+
else
|
47
|
+
:executed
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def node_stat_contributions(nodes)
|
52
|
+
if respond_to? :node_stat_contribution
|
53
|
+
nodes.sum { |n| node_stat_contribution(n) }
|
54
|
+
else
|
55
|
+
nodes.size
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def stats
|
60
|
+
st = each_node.group_by { |n| node_stat_type(n) }
|
61
|
+
.transform_values { |nodes| node_stat_contributions(nodes) }
|
62
|
+
Analyser::Stats.new(**st)
|
63
|
+
end
|
64
|
+
|
33
65
|
# Iterates on nodes in the subset.
|
34
66
|
# Yields the node and it's children (within the subset)
|
35
67
|
def each_node(from = covered_code.root, &block)
|
@@ -4,19 +4,34 @@ require_relative 'subset'
|
|
4
4
|
|
5
5
|
module DeepCover
|
6
6
|
class Analyser::Branch < Analyser
|
7
|
+
def self.human_name
|
8
|
+
'Branches'
|
9
|
+
end
|
7
10
|
include Analyser::Subset
|
8
11
|
SUBSET_CLASSES = [Node::Branch].freeze
|
9
12
|
|
13
|
+
def node_runs(node)
|
14
|
+
runs = super
|
15
|
+
if node.is_a?(Node::Branch) && covered?(runs)
|
16
|
+
worst = worst_branch_runs(node)
|
17
|
+
runs = worst unless covered?(worst)
|
18
|
+
end
|
19
|
+
runs
|
20
|
+
end
|
21
|
+
|
10
22
|
def results
|
11
23
|
each_node.map do |node|
|
12
|
-
branches_runs = node.branches.map { |
|
24
|
+
branches_runs = node.branches.map { |jump| [jump, source.node_runs(jump)] }.to_h
|
13
25
|
[node, branches_runs]
|
14
26
|
end.to_h
|
15
27
|
end
|
16
28
|
|
17
|
-
|
18
|
-
|
19
|
-
|
29
|
+
private
|
30
|
+
|
31
|
+
def worst_branch_runs(fork)
|
32
|
+
fork.branches.map { |jump| source.node_runs(jump) }
|
33
|
+
.sort_by { |runs| runs == 0 ? -2 : runs || -1 }
|
34
|
+
.first
|
20
35
|
end
|
21
36
|
end
|
22
37
|
end
|
@@ -2,6 +2,26 @@
|
|
2
2
|
|
3
3
|
module DeepCover
|
4
4
|
class Analyser::Node < Analyser
|
5
|
+
include Analyser::Subset
|
6
|
+
|
7
|
+
def self.human_name
|
8
|
+
'Nodes'
|
9
|
+
end
|
10
|
+
|
11
|
+
def initialize(source, ignore_uncovered: [], **options)
|
12
|
+
@cache = {}.compare_by_identity
|
13
|
+
super
|
14
|
+
@allow_filters = Array(ignore_uncovered).map { |kind| method(:"is_#{kind}?") }
|
15
|
+
end
|
16
|
+
|
17
|
+
def node_runs(node)
|
18
|
+
@cache.fetch(node) do
|
19
|
+
runs = super
|
20
|
+
runs = nil if runs == 0 && should_be_ignored?(node)
|
21
|
+
@cache[node] = runs
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
5
25
|
def is_raise?(node)
|
6
26
|
node.is_a?(Node::Send) && (node.message == :raise || node.message == :exit)
|
7
27
|
end
|
@@ -15,10 +35,42 @@ module DeepCover
|
|
15
35
|
node.is_a?(Node::EmptyBody) && parent.is_a?(Node::Case) && !parent.has_else?
|
16
36
|
end
|
17
37
|
|
38
|
+
def in_subset?(node, _parent)
|
39
|
+
node.executable?
|
40
|
+
end
|
41
|
+
|
42
|
+
def is_trivial_if?(node)
|
43
|
+
# Supports only node being a branch or the fork itself
|
44
|
+
node.parent.is_a?(Node::If) && node.parent.condition.is_a?(Node::SingletonLiteral)
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.optionally_covered
|
48
|
+
@optionally_covered ||= instance_methods(false).map do |method|
|
49
|
+
method =~ /^is_(.*)\?$/
|
50
|
+
Regexp.last_match(1)
|
51
|
+
end.compact.map(&:to_sym).freeze
|
52
|
+
end
|
53
|
+
|
18
54
|
protected
|
19
55
|
|
20
56
|
def convert(node, **)
|
21
57
|
Analyser::CoveredCodeSource.new(node)
|
22
58
|
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
def should_be_ignored?(node)
|
63
|
+
@allow_filters.any? { |f| f[node] } || is_ignored?(node.parent)
|
64
|
+
end
|
65
|
+
|
66
|
+
def is_ignored?(node)
|
67
|
+
if node == nil
|
68
|
+
false
|
69
|
+
elsif node.executable?
|
70
|
+
node_runs(node).nil?
|
71
|
+
else
|
72
|
+
is_ignored?(node.parent)
|
73
|
+
end
|
74
|
+
end
|
23
75
|
end
|
24
76
|
end
|
@@ -2,10 +2,13 @@
|
|
2
2
|
|
3
3
|
module DeepCover
|
4
4
|
class Analyser::PerChar < Analyser
|
5
|
+
def self.human_name
|
6
|
+
'Chars'
|
7
|
+
end
|
8
|
+
|
5
9
|
# Returns an array of characters for each line of code.
|
6
10
|
# Each character is either ' ' (executed), '-' (not executable) or 'x' (not covered)
|
7
11
|
def results
|
8
|
-
buffer = covered_code.buffer
|
9
12
|
bc = buffer.source_lines.map { |line| '-' * line.size }
|
10
13
|
each_node do |node|
|
11
14
|
runs = node_runs(node)
|
@@ -17,5 +20,19 @@ module DeepCover
|
|
17
20
|
bc.zip(buffer.source_lines) { |cov, line| cov[line.size..-1] = '' } # remove extraneous character for end lines, in any
|
18
21
|
bc
|
19
22
|
end
|
23
|
+
|
24
|
+
def node_stat_contribution(node)
|
25
|
+
node.executed_locs.sum(&:size)
|
26
|
+
end
|
27
|
+
|
28
|
+
def stats
|
29
|
+
s = super
|
30
|
+
actual_total = buffer.source.size
|
31
|
+
s.with not_executable: actual_total - s.total
|
32
|
+
end
|
33
|
+
|
34
|
+
def buffer
|
35
|
+
covered_code.buffer
|
36
|
+
end
|
20
37
|
end
|
21
38
|
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DeepCover
|
4
|
+
class Analyser::StatsBase
|
5
|
+
DECIMALS = 2
|
6
|
+
include Memoize
|
7
|
+
memoize :to_h, :total
|
8
|
+
|
9
|
+
VALUES = %i[executed not_executed not_executable ignored].freeze # All are exclusive
|
10
|
+
|
11
|
+
attr_reader(*VALUES)
|
12
|
+
|
13
|
+
def to_h
|
14
|
+
VALUES.map { |val| [val, public_send(val)] }.to_h
|
15
|
+
end
|
16
|
+
|
17
|
+
def initialize(executed: 0, not_executed: 0, not_executable: 0, ignored: 0)
|
18
|
+
@executed = executed
|
19
|
+
@not_executed = not_executed
|
20
|
+
@not_executable = not_executable
|
21
|
+
@ignored = ignored
|
22
|
+
freeze
|
23
|
+
end
|
24
|
+
|
25
|
+
def +(other)
|
26
|
+
self.class.new(to_h.merge(other.to_h) { |k, a, b| a + b })
|
27
|
+
end
|
28
|
+
|
29
|
+
def total
|
30
|
+
to_h.values.inject(:+)
|
31
|
+
end
|
32
|
+
|
33
|
+
def with(**values)
|
34
|
+
self.class.new(to_h.merge(values))
|
35
|
+
end
|
36
|
+
|
37
|
+
def potentially_executable
|
38
|
+
total - not_executable
|
39
|
+
end
|
40
|
+
|
41
|
+
def percent_covered
|
42
|
+
return 100 if potentially_executable == 0
|
43
|
+
(100 * (1 - not_executed.fdiv(potentially_executable))).round(DECIMALS)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
class Analyser::Stats < Analyser::StatsBase
|
48
|
+
memoize :percent
|
49
|
+
|
50
|
+
def percent
|
51
|
+
Analyser::StatsBase.new(to_h.transform_values { |v| (100 * v).fdiv(total).round(DECIMALS) })
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
data/lib/deep_cover/backports.rb
CHANGED
data/lib/deep_cover/base.rb
CHANGED
@@ -2,6 +2,10 @@
|
|
2
2
|
|
3
3
|
module DeepCover
|
4
4
|
module Base
|
5
|
+
def running?
|
6
|
+
@started
|
7
|
+
end
|
8
|
+
|
5
9
|
def start
|
6
10
|
return if @started
|
7
11
|
if defined?(RUBY_ENGINE) && RUBY_ENGINE == 'jruby'
|
@@ -30,11 +34,16 @@ module DeepCover
|
|
30
34
|
coverage.covered_code(handle_relative_filename(filename))
|
31
35
|
end
|
32
36
|
|
33
|
-
def cover
|
37
|
+
def cover(paths: nil)
|
38
|
+
if paths
|
39
|
+
prev = config.paths
|
40
|
+
config.paths(paths)
|
41
|
+
end
|
34
42
|
start
|
35
43
|
yield
|
36
44
|
ensure
|
37
45
|
stop
|
46
|
+
config.paths(prev) if paths
|
38
47
|
end
|
39
48
|
|
40
49
|
def config_changed(what)
|
@@ -44,6 +53,13 @@ module DeepCover
|
|
44
53
|
end
|
45
54
|
end
|
46
55
|
|
56
|
+
def reset
|
57
|
+
stop if @started
|
58
|
+
@coverage = @custom_requirer = @autoload_tracker = nil
|
59
|
+
config.reset
|
60
|
+
self
|
61
|
+
end
|
62
|
+
|
47
63
|
def coverage
|
48
64
|
@coverage ||= Coverage.new
|
49
65
|
end
|