Module: SixthSense::Runners::CheckedCoverageTrace

Defined in:
lib/sixth_sense/runners/checked_coverage_trace.rb

Class Method Summary collapse

Class Method Details

.assertion_method?(method_id) ⇒ Boolean

Returns:

  • (Boolean)


50
51
52
53
54
55
56
# File 'lib/sixth_sense/runners/checked_coverage_trace.rb', line 50

def assertion_method?(method_id)
  name = method_id.to_s
  return true if %w[expect should should_not flunk].include?(name)
  return true if name.start_with?("assert", "refute", "must_", "wont_")

  false
end

.assertion_stackObject



66
67
68
# File 'lib/sixth_sense/runners/checked_coverage_trace.rb', line 66

def assertion_stack
  Thread.current[:sixth_sense_assertion_stack].to_i
end

.assertion_stack=(value) ⇒ Object



70
71
72
# File 'lib/sixth_sense/runners/checked_coverage_trace.rb', line 70

def assertion_stack=(value)
  Thread.current[:sixth_sense_assertion_stack] = value
end

.captureObject

Paper basis: Schuler/Zeller 2011 (doi:10.1109/ICST.2011.32). This captures a TracePoint approximation of checked coverage rather than their full VM-level dynamic backward slicing.



14
15
16
17
18
19
20
21
22
23
# File 'lib/sixth_sense/runners/checked_coverage_trace.rb', line 14

def capture
  executed = Hash.new { |hash, key| hash[key] = Set.new }
  checked = Hash.new { |hash, key| hash[key] = Set.new }
  trace = tracepoint(executed, checked)
  trace.enable
  yield
ensure
  trace.disable if trace
  write_snapshot(executed, checked) if executed && checked
end

.clear_recent_linesObject



85
86
87
# File 'lib/sixth_sense/runners/checked_coverage_trace.rb', line 85

def clear_recent_lines
  Thread.current[:sixth_sense_recent_sut_lines] = []
end

.mark_recent_lines(checked) ⇒ Object



79
80
81
82
83
# File 'lib/sixth_sense/runners/checked_coverage_trace.rb', line 79

def mark_recent_lines(checked)
  recent_lines.each do |path, line|
    checked[path] << line
  end
end

.recent_linesObject



89
90
91
# File 'lib/sixth_sense/runners/checked_coverage_trace.rb', line 89

def recent_lines
  Thread.current[:sixth_sense_recent_sut_lines] ||= []
end

.recent_windowObject



93
94
95
# File 'lib/sixth_sense/runners/checked_coverage_trace.rb', line 93

def recent_window
  ENV.fetch("SIXTH_SENSE_CHECKED_WINDOW", "20").to_i
end

.remember_line(path, line) ⇒ Object



74
75
76
77
# File 'lib/sixth_sense/runners/checked_coverage_trace.rb', line 74

def remember_line(path, line)
  recent_lines << [path, line]
  recent_lines.shift while recent_lines.length > recent_window
end

.sut_path?(path) ⇒ Boolean

Returns:

  • (Boolean)


58
59
60
# File 'lib/sixth_sense/runners/checked_coverage_trace.rb', line 58

def sut_path?(path)
  sut_paths.include?(File.expand_path(path))
end

.sut_pathsObject



62
63
64
# File 'lib/sixth_sense/runners/checked_coverage_trace.rb', line 62

def sut_paths
  @sut_paths ||= ENV.fetch("SIXTH_SENSE_SUT_PATHS", "").split(File::PATH_SEPARATOR).map { |path| File.expand_path(path) }.to_set
end

.tracepoint(executed, checked) ⇒ Object

Paper basis: Schuler/Zeller 2011; assertion calls are treated as oracle boundaries, and SUT lines observed inside that boundary are checked.



27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
# File 'lib/sixth_sense/runners/checked_coverage_trace.rb', line 27

def tracepoint(executed, checked)
  TracePoint.new(:line, :call, :c_call, :return, :c_return) do |event|
    case event.event
    when :line
      next unless sut_path?(event.path)

      executed[event.path] << event.lineno
      remember_line(event.path, event.lineno)
      checked[event.path] << event.lineno if assertion_stack.positive?
    when :call, :c_call
      next unless assertion_method?(event.method_id)

      mark_recent_lines(checked)
      self.assertion_stack += 1
    when :return, :c_return
      next unless assertion_method?(event.method_id) && assertion_stack.positive?

      self.assertion_stack -= 1
      clear_recent_lines
    end
  end
end

.write_snapshot(executed, checked) ⇒ Object



97
98
99
100
101
102
103
104
105
106
107
# File 'lib/sixth_sense/runners/checked_coverage_trace.rb', line 97

def write_snapshot(executed, checked)
  File.write(
    ENV.fetch("SIXTH_SENSE_CHECKED_OUTPUT"),
    JSON.generate(
      {
        executed_lines: executed.transform_values(&:to_a),
        checked_lines: checked.transform_values(&:to_a)
      }
    )
  )
end