Class: DeepCover::CoveredCode

Inherits:
Object
  • Object
show all
Defined in:
lib/deep_cover/covered_code.rb

Constant Summary collapse

DEFAULT_TRACKER_GLOBAL =
'$_cov'
@@counter =
0
@@globals =

rubocop:disable Security/Eval

Hash.new { |h, global| h[global] = eval("#{global} ||= {}") }

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(path: nil, source: nil, lineno: 1, tracker_global: DEFAULT_TRACKER_GLOBAL, local_var: '_temp', name: nil) ⇒ CoveredCode

Returns a new instance of CoveredCode.



11
12
13
14
15
16
17
18
19
20
21
# File 'lib/deep_cover/covered_code.rb', line 11

def initialize(path: nil, source: nil, lineno: 1, tracker_global: DEFAULT_TRACKER_GLOBAL, local_var: '_temp', name: nil)
  raise 'Must provide either path or source' unless path || source

  @buffer = ::Parser::Source::Buffer.new(path, lineno)
  @buffer.source = source ||= File.read(path)
  @tracker_count = 0
  @tracker_global = tracker_global
  @local_var = local_var
  @name = name || (source ? '(source)' : File.basename(path))
  @covered_source = instrument_source
end

Instance Attribute Details

#bufferObject

Returns the value of attribute buffer.



7
8
9
# File 'lib/deep_cover/covered_code.rb', line 7

def buffer
  @buffer
end

#covered_sourceObject

Returns the value of attribute covered_source.



7
8
9
# File 'lib/deep_cover/covered_code.rb', line 7

def covered_source
  @covered_source
end

#local_varObject

Returns the value of attribute local_var.



7
8
9
# File 'lib/deep_cover/covered_code.rb', line 7

def local_var
  @local_var
end

#nameObject

Returns the value of attribute name.



7
8
9
# File 'lib/deep_cover/covered_code.rb', line 7

def name
  @name
end

#tracker_globalObject

Returns the value of attribute tracker_global.



7
8
9
# File 'lib/deep_cover/covered_code.rb', line 7

def tracker_global
  @tracker_global
end

Instance Method Details

#allocate_trackers(nb_needed) ⇒ Object

Returns a range of tracker ids



72
73
74
75
76
# File 'lib/deep_cover/covered_code.rb', line 72

def allocate_trackers(nb_needed)
  prev = @tracker_count
  @tracker_count += nb_needed if nb_needed > 0 # Avoid error if frozen and called with 0.
  prev...@tracker_count
end

#char_cover(**options) ⇒ Object



62
63
64
65
# File 'lib/deep_cover/covered_code.rb', line 62

def char_cover(**options)
  must_have_executed
  Analyser::PerChar.new(self, **options).results
end

#coverObject



47
48
49
50
# File 'lib/deep_cover/covered_code.rb', line 47

def cover
  must_have_executed
  global[nb]
end

#covered_astObject



90
91
92
# File 'lib/deep_cover/covered_code.rb', line 90

def covered_ast
  root.main
end

#each_node(*args, &block) ⇒ Object



101
102
103
# File 'lib/deep_cover/covered_code.rb', line 101

def each_node(*args, &block)
  covered_ast.each_node(*args, &block)
end

#execute_code(binding: DeepCover::GLOBAL_BINDING.dup) ⇒ Object



40
41
42
43
44
45
# File 'lib/deep_cover/covered_code.rb', line 40

def execute_code(binding: DeepCover::GLOBAL_BINDING.dup)
  return if has_executed?
  global[nb] = Array.new(@tracker_count, 0)
  eval(@covered_source, binding, @buffer.name || '<raw_code>', lineno) # rubocop:disable Security/Eval
  self
end

#freezeObject



127
128
129
130
131
132
133
134
# File 'lib/deep_cover/covered_code.rb', line 127

def freeze
  unless frozen? # Guard against reentrance
    must_have_executed
    super
    root.each_node(&:freeze)
  end
  self
end

#has_executed?Boolean

Returns:

  • (Boolean)


123
124
125
# File 'lib/deep_cover/covered_code.rb', line 123

def has_executed?
  global[nb] != nil
end

#instrument_sourceObject



105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
# File 'lib/deep_cover/covered_code.rb', line 105

def instrument_source
  rewriter = ::Parser::Source::Rewriter.new(@buffer)
  covered_ast.each_node(:postorder) do |node|
    node.rewriting_rules.each do |range, rule|
      prefix, _node, suffix = rule.partition('%{node}')
      unless prefix.empty?
        prefix = yield prefix, node, range.begin, :prefix if block_given?
        rewriter.insert_before_multi range, prefix
      end
      unless suffix.empty?
        suffix = yield suffix, node, range.end, :suffix if block_given?
        rewriter.insert_after_multi  range, suffix
      end
    end
  end
  rewriter.process
end

#line_coverage(**options) ⇒ Object



52
53
54
55
# File 'lib/deep_cover/covered_code.rb', line 52

def line_coverage(**options)
  must_have_executed
  Analyser::PerLine.new(self, **options).results
end

#linenoObject



27
28
29
# File 'lib/deep_cover/covered_code.rb', line 27

def lineno
  @buffer.first_line
end

#nbObject



67
68
69
# File 'lib/deep_cover/covered_code.rb', line 67

def nb
  @nb ||= (@@counter += 1)
end

#nb_linesObject



31
32
33
34
35
36
37
38
# File 'lib/deep_cover/covered_code.rb', line 31

def nb_lines
  lines = buffer.source_lines
  if lines.empty?
    0
  else
    lines.size - (lines.last.empty? ? 1 : 0)
  end
end

#pathObject



23
24
25
# File 'lib/deep_cover/covered_code.rb', line 23

def path
  @buffer.name || "(source: '#{@buffer.source[0..20]}...')"
end

#rootObject



94
95
96
97
98
99
# File 'lib/deep_cover/covered_code.rb', line 94

def root
  @root ||= begin
    ast = DeepCover.parser.parse(@buffer)
    Node::Root.new(ast, self)
  end
end

#to_istanbul(**options) ⇒ Object



57
58
59
60
# File 'lib/deep_cover/covered_code.rb', line 57

def to_istanbul(**options)
  must_have_executed
  Reporter::Istanbul.new(self, **options).convert
end

#tracker_hits(tracker_id) ⇒ Object



86
87
88
# File 'lib/deep_cover/covered_code.rb', line 86

def tracker_hits(tracker_id)
  cover.fetch(tracker_id)
end

#tracker_source(tracker_id) ⇒ Object



78
79
80
# File 'lib/deep_cover/covered_code.rb', line 78

def tracker_source(tracker_id)
  "#{tracker_global}[#{nb}][#{tracker_id}]+=1"
end

#trackers_setup_sourceObject



82
83
84
# File 'lib/deep_cover/covered_code.rb', line 82

def trackers_setup_source
  "(#{tracker_global}||={})[#{nb}]||=Array.new(#{@tracker_count},0)"
end