deep-cover 0.1.16 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|