deep-cover 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (99) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +13 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +10 -0
  5. data/CODE_OF_CONDUCT.md +74 -0
  6. data/Gemfile +8 -0
  7. data/LICENSE.txt +21 -0
  8. data/README.md +127 -0
  9. data/Rakefile +6 -0
  10. data/bin/console +14 -0
  11. data/bin/cov +43 -0
  12. data/bin/gemcov +8 -0
  13. data/bin/selfcov +21 -0
  14. data/bin/setup +8 -0
  15. data/bin/testall +88 -0
  16. data/deep_cover.gemspec +44 -0
  17. data/exe/deep-cover +6 -0
  18. data/future_read_me.md +108 -0
  19. data/lib/deep-cover.rb +1 -0
  20. data/lib/deep_cover.rb +11 -0
  21. data/lib/deep_cover/analyser.rb +24 -0
  22. data/lib/deep_cover/analyser/base.rb +51 -0
  23. data/lib/deep_cover/analyser/branch.rb +20 -0
  24. data/lib/deep_cover/analyser/covered_code_source.rb +31 -0
  25. data/lib/deep_cover/analyser/function.rb +12 -0
  26. data/lib/deep_cover/analyser/ignore_uncovered.rb +19 -0
  27. data/lib/deep_cover/analyser/node.rb +11 -0
  28. data/lib/deep_cover/analyser/per_char.rb +20 -0
  29. data/lib/deep_cover/analyser/per_line.rb +23 -0
  30. data/lib/deep_cover/analyser/statement.rb +31 -0
  31. data/lib/deep_cover/analyser/subset.rb +24 -0
  32. data/lib/deep_cover/auto_run.rb +49 -0
  33. data/lib/deep_cover/autoload_tracker.rb +75 -0
  34. data/lib/deep_cover/backports.rb +9 -0
  35. data/lib/deep_cover/base.rb +55 -0
  36. data/lib/deep_cover/builtin_takeover.rb +2 -0
  37. data/lib/deep_cover/cli/debugger.rb +93 -0
  38. data/lib/deep_cover/cli/deep_cover.rb +49 -0
  39. data/lib/deep_cover/cli/instrumented_clone_reporter.rb +105 -0
  40. data/lib/deep_cover/config.rb +52 -0
  41. data/lib/deep_cover/core_ext/autoload_overrides.rb +40 -0
  42. data/lib/deep_cover/core_ext/coverage_replacement.rb +26 -0
  43. data/lib/deep_cover/core_ext/load_overrides.rb +24 -0
  44. data/lib/deep_cover/core_ext/require_overrides.rb +36 -0
  45. data/lib/deep_cover/coverage.rb +198 -0
  46. data/lib/deep_cover/covered_code.rb +138 -0
  47. data/lib/deep_cover/custom_requirer.rb +93 -0
  48. data/lib/deep_cover/node.rb +8 -0
  49. data/lib/deep_cover/node/arguments.rb +50 -0
  50. data/lib/deep_cover/node/assignments.rb +250 -0
  51. data/lib/deep_cover/node/base.rb +99 -0
  52. data/lib/deep_cover/node/begin.rb +25 -0
  53. data/lib/deep_cover/node/block.rb +53 -0
  54. data/lib/deep_cover/node/boolean.rb +22 -0
  55. data/lib/deep_cover/node/branch.rb +28 -0
  56. data/lib/deep_cover/node/case.rb +94 -0
  57. data/lib/deep_cover/node/collections.rb +21 -0
  58. data/lib/deep_cover/node/const.rb +10 -0
  59. data/lib/deep_cover/node/def.rb +38 -0
  60. data/lib/deep_cover/node/empty_body.rb +21 -0
  61. data/lib/deep_cover/node/exceptions.rb +74 -0
  62. data/lib/deep_cover/node/if.rb +36 -0
  63. data/lib/deep_cover/node/keywords.rb +84 -0
  64. data/lib/deep_cover/node/literals.rb +77 -0
  65. data/lib/deep_cover/node/loops.rb +72 -0
  66. data/lib/deep_cover/node/mixin/can_augment_children.rb +65 -0
  67. data/lib/deep_cover/node/mixin/check_completion.rb +16 -0
  68. data/lib/deep_cover/node/mixin/child_can_be_empty.rb +25 -0
  69. data/lib/deep_cover/node/mixin/executed_after_children.rb +13 -0
  70. data/lib/deep_cover/node/mixin/execution_location.rb +56 -0
  71. data/lib/deep_cover/node/mixin/flow_accounting.rb +63 -0
  72. data/lib/deep_cover/node/mixin/has_child.rb +138 -0
  73. data/lib/deep_cover/node/mixin/has_child_handler.rb +73 -0
  74. data/lib/deep_cover/node/mixin/has_tracker.rb +44 -0
  75. data/lib/deep_cover/node/mixin/is_statement.rb +18 -0
  76. data/lib/deep_cover/node/mixin/rewriting.rb +32 -0
  77. data/lib/deep_cover/node/mixin/wrapper.rb +13 -0
  78. data/lib/deep_cover/node/module.rb +64 -0
  79. data/lib/deep_cover/node/root.rb +18 -0
  80. data/lib/deep_cover/node/send.rb +83 -0
  81. data/lib/deep_cover/node/splat.rb +13 -0
  82. data/lib/deep_cover/node/variables.rb +14 -0
  83. data/lib/deep_cover/parser_ext/range.rb +40 -0
  84. data/lib/deep_cover/reporter.rb +6 -0
  85. data/lib/deep_cover/reporter/istanbul.rb +151 -0
  86. data/lib/deep_cover/tools.rb +18 -0
  87. data/lib/deep_cover/tools/builtin_coverage.rb +50 -0
  88. data/lib/deep_cover/tools/camelize.rb +8 -0
  89. data/lib/deep_cover/tools/dump_covered_code.rb +32 -0
  90. data/lib/deep_cover/tools/execute_sample.rb +23 -0
  91. data/lib/deep_cover/tools/format.rb +16 -0
  92. data/lib/deep_cover/tools/format_char_cover.rb +18 -0
  93. data/lib/deep_cover/tools/format_generated_code.rb +25 -0
  94. data/lib/deep_cover/tools/number_lines.rb +18 -0
  95. data/lib/deep_cover/tools/our_coverage.rb +9 -0
  96. data/lib/deep_cover/tools/require_relative_dir.rb +10 -0
  97. data/lib/deep_cover/tools/silence_warnings.rb +15 -0
  98. data/lib/deep_cover/version.rb +3 -0
  99. metadata +326 -0
@@ -0,0 +1,25 @@
1
+ module DeepCover
2
+ class Node
3
+ class Begin < Node
4
+ has_extra_children expressions: Node,
5
+ is_statement: true
6
+
7
+ def is_statement
8
+ false
9
+ end
10
+
11
+ def executed_loc_keys
12
+ # Begin is a generic grouping used in different contexts.
13
+ case loc_hash[:begin] && loc_hash[:begin].source
14
+ when nil, '(', 'begin'
15
+ []
16
+ when 'else', '#{'
17
+ %i[begin end]
18
+ else
19
+ warn "Unknown context for Begin node"
20
+ []
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,53 @@
1
+ require_relative 'send'
2
+ require_relative 'keywords'
3
+
4
+ module DeepCover
5
+ class Node
6
+ module WithBlock
7
+ def flow_completion_count
8
+ parent.flow_completion_count
9
+ end
10
+
11
+ def execution_count
12
+ last = children_nodes.last
13
+ return last.flow_completion_count if last
14
+ super
15
+ end
16
+ end
17
+
18
+ class SendWithBlock < Node
19
+ include WithBlock
20
+ has_child receiver: [Node, nil]
21
+ has_child method_name: Symbol
22
+ has_extra_children arguments: Node
23
+ end
24
+
25
+ class SuperWithBlock < Node
26
+ include WithBlock
27
+ has_extra_children arguments: Node
28
+ end
29
+
30
+ class Block < Node
31
+ check_completion
32
+ has_tracker :body
33
+ has_child call: {send: SendWithBlock, zsuper: SuperWithBlock, super: SuperWithBlock}
34
+ has_child args: Args
35
+ has_child body: Node,
36
+ can_be_empty: -> { base_node.loc.begin.end },
37
+ rewrite: '%{body_tracker};%{local}=nil;%{node}',
38
+ flow_entry_count: :body_tracker_hits,
39
+ is_statement: true
40
+ executed_loc_keys # none
41
+
42
+ def children_nodes_in_flow_order
43
+ [call, args] # Similarly to a def, the body is actually not part of the flow of this node...
44
+ end
45
+ end
46
+
47
+ # &foo
48
+ class BlockPass < Node
49
+ has_child block: Node
50
+ # TODO
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,22 @@
1
+ require_relative 'branch'
2
+
3
+ module DeepCover
4
+ class Node
5
+ class ShortCircuit < Node
6
+ include Branch
7
+ has_tracker :conditional
8
+ has_child first: Node
9
+ has_child conditional: Node, flow_entry_count: :conditional_tracker_hits,
10
+ rewrite: '((%{conditional_tracker};%{node}))'
11
+
12
+ def branches
13
+ [
14
+ conditional,
15
+ TrivialBranch.new(first, conditional)
16
+ ]
17
+ end
18
+ end
19
+
20
+ And = Or = ShortCircuit
21
+ end
22
+ end
@@ -0,0 +1,28 @@
1
+ module DeepCover
2
+ class Node
3
+ module Branch
4
+ def flow_completion_count
5
+ branches.map(&:flow_completion_count).inject(0, :+)
6
+ end
7
+
8
+ # Define in sublasses:
9
+ def branches
10
+ raise NotImplementedError
11
+ end
12
+
13
+ # Also define flow_entry_count
14
+ end
15
+
16
+ class TrivialBranch < Struct.new(:condition, :other_branch)
17
+ def flow_entry_count
18
+ condition.flow_completion_count - other_branch.flow_entry_count
19
+ end
20
+ alias_method :flow_completion_count, :flow_entry_count
21
+ alias_method :execution_count, :flow_entry_count
22
+ def executable?
23
+ true
24
+ end
25
+ end
26
+
27
+ end
28
+ end
@@ -0,0 +1,94 @@
1
+ require_relative 'branch'
2
+
3
+ module DeepCover
4
+ class Node
5
+ class WhenCondition < Node
6
+ include Wrapper
7
+ has_tracker :entry
8
+ # Using && instead of ; solves a weird bug in jruby 9.1.7.0 and 9.1.9.0 (probably before too).
9
+ # The following will only print 'test' once
10
+ # class EqEqEq; def ===(other); puts 'test'; end; end
11
+ # eqeqeq = EqEqEq.new
12
+ # case 1; when eqeqeq; end
13
+ # case 1; when (3;eqeqeq); end
14
+ # See https://github.com/jruby/jruby/issues/4804
15
+ # This is solved in jruby 9.2.0.0, better keep the workaround
16
+ # for compatibility.
17
+ has_child condition: Node, rewrite: "(((%{entry_tracker}) && %{node}))",
18
+ flow_entry_count: :entry_tracker_hits
19
+ executed_loc_keys []
20
+
21
+ def flow_entry_count
22
+ entry_tracker_hits
23
+ end
24
+
25
+ def flow_completion_count
26
+ condition.flow_completion_count
27
+ end
28
+
29
+ def loc_hash
30
+ condition.loc_hash
31
+ end
32
+ end
33
+
34
+ class WhenSplatCondition < Node
35
+ has_tracker :entry
36
+ check_completion inner: '(%{entry_tracker};[%{node}])', outer: '*%{node}'
37
+ has_child receiver: Node
38
+
39
+ def flow_entry_count
40
+ entry_tracker_hits
41
+ end
42
+ end
43
+
44
+ class When < Node
45
+ has_tracker :body_entry
46
+ has_extra_children matches: { splat: WhenSplatCondition, Parser::AST::Node => WhenCondition }
47
+ has_child body: Node,
48
+ can_be_empty: -> { base_node.loc.expression.succ },
49
+ rewrite: ";%{body_entry_tracker};%{local}=nil;%{node}",
50
+ is_statement: true,
51
+ flow_entry_count: :body_entry_tracker_hits
52
+
53
+ def flow_entry_count
54
+ matches.first.flow_entry_count
55
+ end
56
+
57
+ def execution_count
58
+ matches.first.flow_completion_count
59
+ end
60
+
61
+ def flow_completion_count
62
+ body.flow_completion_count + next_sibling.flow_entry_count
63
+ end
64
+ end
65
+
66
+ class Case < Node
67
+ include Branch
68
+ has_tracker :else_entry
69
+ has_child evaluate: [Node, nil]
70
+ has_extra_children whens: When
71
+ has_child else: Node,
72
+ can_be_empty: -> { base_node.loc.end.begin },
73
+ rewrite: -> { "#{'else;' unless has_else?}((%{else_entry_tracker};%{local}=nil;%{node}))" },
74
+ executed_loc_keys: [:else],
75
+ is_statement: true,
76
+ flow_entry_count: :else_entry_tracker_hits
77
+
78
+ executed_loc_keys :begin, :keyword
79
+
80
+ def branches
81
+ whens.map(&:body) << self.else
82
+ end
83
+
84
+ def execution_count
85
+ return evaluate.flow_completion_count if evaluate
86
+ flow_entry_count
87
+ end
88
+
89
+ def has_else?
90
+ !!base_node.loc.to_hash[:else]
91
+ end
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,21 @@
1
+ require_relative 'splat'
2
+
3
+ module DeepCover
4
+ class Node
5
+ class Array < Node
6
+ has_extra_children elements: Node
7
+ executed_loc_keys :begin, :end
8
+ end
9
+
10
+ class Pair < Node
11
+ has_child key: Node
12
+ has_child value: Node
13
+ executed_loc_keys :operator
14
+ end
15
+
16
+ class Hash < Node
17
+ has_extra_children elements: [Pair, Kwsplat]
18
+ executed_loc_keys :begin, :end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,10 @@
1
+ module DeepCover
2
+ class Node::Const < Node
3
+ check_completion
4
+ has_child scope: [Node, nil]
5
+ has_child const_name: Symbol
6
+ end
7
+
8
+ class Node::Cbase < Node
9
+ end
10
+ end
@@ -0,0 +1,38 @@
1
+ require_relative 'arguments'
2
+
3
+ module DeepCover
4
+ class Node::Def < Node
5
+ check_completion
6
+ has_tracker :method_call
7
+ has_child method_name: Symbol
8
+ has_child signature: Args
9
+ has_child body: Node,
10
+ rewrite: '%{method_call_tracker};%{local}=nil;%{node};',
11
+ can_be_empty: -> { base_node.loc.end.begin },
12
+ is_statement: true,
13
+ flow_entry_count: :method_call_tracker_hits
14
+ executed_loc_keys :keyword, :name
15
+
16
+ def children_nodes_in_flow_order
17
+ []
18
+ end
19
+ end
20
+
21
+ class Node::Defs < Node
22
+ check_completion
23
+ has_tracker :method_call
24
+ has_child singleton: Node, rewrite: '(%{node})'
25
+ has_child method_name: Symbol
26
+ has_child signature: Args
27
+ has_child body: Node,
28
+ rewrite: '%{method_call_tracker};%{local}=nil;%{node};',
29
+ can_be_empty: -> { base_node.loc.end.begin },
30
+ is_statement: true,
31
+ flow_entry_count: :method_call_tracker_hits
32
+ executed_loc_keys :keyword, :name, :operator
33
+
34
+ def children_nodes_in_flow_order
35
+ [singleton]
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,21 @@
1
+ module DeepCover
2
+ class Node::EmptyBody < Node
3
+ def initialize(base_node, parent: raise, index: 0, position: ChildCanBeEmpty.last_empty_position)
4
+ @position = position
5
+ @position = parent.expression.begin if @position == true # Some random position... don't rewrite!
6
+ super(base_node, parent: parent, index: index, base_children: [])
7
+ end
8
+
9
+ def type
10
+ :EmptyBody
11
+ end
12
+
13
+ def loc_hash
14
+ {expression: @position}
15
+ end
16
+
17
+ def is_statement
18
+ false
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,74 @@
1
+ require_relative 'variables'
2
+ require_relative 'collections'
3
+
4
+ module DeepCover
5
+ class Node
6
+ class Resbody < Node
7
+ has_tracker :entered_body
8
+ has_child exception: [Node::Array, nil]
9
+ has_child assignment: [Lvasgn, nil], flow_entry_count: :entered_body_tracker_hits
10
+ has_child body: Node,
11
+ can_be_empty: -> { base_node.loc.expression.succ },
12
+ flow_entry_count: :entered_body_tracker_hits,
13
+ is_statement: true,
14
+ rewrite: '((%{entered_body_tracker};%{local}=nil;%{node}))'
15
+
16
+ def is_statement
17
+ false
18
+ end
19
+
20
+ def execution_count
21
+ entered_body_tracker_hits
22
+ end
23
+ end
24
+
25
+ class Rescue < Node
26
+ has_child watched_body: Node,
27
+ can_be_empty: -> { base_node.loc.expression.begin },
28
+ is_statement: true
29
+ has_extra_children resbodies: Resbody
30
+ has_child else: Node,
31
+ can_be_empty: -> { base_node.loc.expression.succ },
32
+ flow_entry_count: :execution_count,
33
+ is_statement: true
34
+ executed_loc_keys :else
35
+
36
+ def is_statement
37
+ false
38
+ end
39
+
40
+ def flow_completion_count
41
+ resbodies.map(&:flow_completion_count).inject(0, :+) + self.else.flow_completion_count
42
+ end
43
+
44
+ def execution_count
45
+ watched_body.flow_completion_count
46
+ end
47
+
48
+ def resbodies_flow_entry_count(child)
49
+ prev = child.previous_sibling
50
+
51
+ if prev.equal? watched_body
52
+ prev.flow_entry_count - prev.flow_completion_count
53
+ else # RESBODIES
54
+ # TODO is this okay?
55
+ prev.exception.flow_completion_count - prev.execution_count
56
+ end
57
+ end
58
+ end
59
+
60
+ class Ensure < Node
61
+ has_child body: Node,
62
+ can_be_empty: -> { base_node.loc.expression.begin },
63
+ is_statement: true
64
+ has_child ensure: Node,
65
+ can_be_empty: -> { base_node.loc.expression.succ },
66
+ is_statement: true,
67
+ flow_entry_count: -> { body.flow_entry_count }
68
+
69
+ def flow_completion_count
70
+ body.flow_completion_count
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,36 @@
1
+ require_relative 'branch'
2
+
3
+ module DeepCover
4
+ class Node
5
+ class If < Node
6
+ include Branch
7
+ has_tracker :truthy
8
+ has_child condition: Node, rewrite: '((%{node}) && %{truthy_tracker})'
9
+ has_child true_branch: Node,
10
+ can_be_empty: true,
11
+ executed_loc_keys: -> { :else if style == :unless },
12
+ flow_entry_count: :truthy_tracker_hits,
13
+ is_statement: true
14
+ has_child false_branch: Node,
15
+ can_be_empty: true,
16
+ executed_loc_keys: -> { [:else, :colon] if style != :unless },
17
+ flow_entry_count: -> { condition.flow_completion_count - truthy_tracker_hits },
18
+ is_statement: true
19
+ executed_loc_keys :keyword, :question
20
+
21
+ def branches
22
+ [ true_branch, false_branch ]
23
+ end
24
+
25
+ def execution_count
26
+ condition.flow_completion_count
27
+ end
28
+
29
+ # returns on of %i[ternary if unless elsif]
30
+ def style
31
+ keyword = loc_hash[:keyword]
32
+ keyword ? keyword.source.to_sym : :ternary
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,84 @@
1
+ require_relative 'variables'
2
+ require_relative 'literals'
3
+
4
+ module DeepCover
5
+ class Node
6
+ class Kwbegin < Node
7
+ has_extra_children instructions: Node,
8
+ is_statement: true
9
+
10
+ def is_statement
11
+ false
12
+ end
13
+ end
14
+
15
+ class Return < Node
16
+ has_extra_children values: Node
17
+ # TODO
18
+ end
19
+
20
+ class Super < Node
21
+ check_completion
22
+ has_extra_children arguments: Node
23
+ # TODO
24
+ end
25
+ Zsuper = Super # Zsuper is super with no parenthesis (same arguments as caller)
26
+
27
+ class Yield < Node
28
+ has_extra_children arguments: Node
29
+ # TODO
30
+ end
31
+
32
+ class Break < Node
33
+ has_extra_children arguments: Node
34
+ # TODO Anything special needed for the arguments?
35
+
36
+ def flow_completion_count
37
+ 0
38
+ end
39
+ end
40
+
41
+ class Next < Node
42
+ has_extra_children arguments: Node
43
+ # TODO Anything special needed for the arguments?
44
+
45
+ def flow_completion_count
46
+ 0
47
+ end
48
+ end
49
+
50
+ class Alias < Node
51
+ check_completion
52
+ has_child alias: [Sym, Dsym, Gvar, BackRef]
53
+ has_child original: [Sym, Dsym, Gvar, BackRef]
54
+ # TODO: test
55
+ end
56
+
57
+ class NeverEvaluated < Node
58
+ has_extra_children whatever: [:any], remap: {Parser::AST::Node => NeverEvaluated}
59
+
60
+ def executable?
61
+ false
62
+ end
63
+ end
64
+
65
+ class Defined < Node
66
+ has_child code: {Parser::AST::Node => NeverEvaluated}
67
+ # TODO: test
68
+ end
69
+
70
+ class Undef < Node
71
+ check_completion
72
+ has_extra_children arguments: [Sym, Dsym]
73
+ # TODO: test
74
+ end
75
+
76
+ class Return < Node
77
+ include ExecutedAfterChildren
78
+
79
+ def flow_completion_count
80
+ 0
81
+ end
82
+ end
83
+ end
84
+ end