Class: RailsErrorDashboard::Services::CoverageTracker

Inherits:
Object
  • Object
show all
Defined in:
lib/rails_error_dashboard/services/coverage_tracker.rb

Overview

Diagnostic-mode code path coverage using Ruby’s Coverage API.

Operator enables coverage via dashboard button, reproduces the error, views source code with executed-line overlay, then disables. Zero overhead when off. Uses oneshot_lines mode (each line fires once).

SAFETY:

  • Coverage is process-global (not thread-local) — data blends across threads

  • oneshot_lines mode has near-zero ongoing overhead

  • peek_result is read-only, does not mutate state

  • Every method rescue-wrapped (never raises)

  • Ruby 3.2+ required for Coverage.setup with oneshot_lines

Class Method Summary collapse

Class Method Details

.active?Boolean

Whether coverage is currently being collected

Returns:

  • (Boolean)


87
88
89
# File 'lib/rails_error_dashboard/services/coverage_tracker.rb', line 87

def active?
  @active
end

.disable!Object

Stop coverage collection and clear all data



63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
# File 'lib/rails_error_dashboard/services/coverage_tracker.rb', line 63

def disable!
  return unless @active

  @mutex.synchronize do
    return unless @active
    # Only call Coverage.result if WE started the session
    # Don't kill another tool's coverage (e.g. SimpleCov)
    if @we_started
      begin
        Coverage.result
      rescue RuntimeError
        # Already stopped
      end
    end
    @active = false
    @we_started = false
  end
rescue => e
  Rails.logger.error("[RailsErrorDashboard] CoverageTracker.disable! failed: #{e.class}: #{e.message}")
  @active = false
  @we_started = false
end

.enable!Boolean

Start coverage collection in oneshot_lines mode

Returns:

  • (Boolean)

    true if successfully enabled



32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
# File 'lib/rails_error_dashboard/services/coverage_tracker.rb', line 32

def enable!
  return false unless supported?
  return true if @active

  @mutex.synchronize do
    return true if @active

    Coverage.setup(oneshot_lines: true)
    Coverage.resume
    @we_started = true
    @active = true
  end

  true
rescue => e
  # Coverage.setup raises RuntimeError if another session is active (e.g. SimpleCov).
  # In that case, we piggyback on the existing session for peek_result.
  if e.is_a?(RuntimeError) && coverage_running?
    @mutex.synchronize do
      @we_started = false
      @active = true
    end
    true
  else
    Rails.logger.error("[RailsErrorDashboard] CoverageTracker.enable! failed: #{e.class}: #{e.message}")
    @active = false
    false
  end
end

.peek(file_path) ⇒ Hash{Integer => Boolean}

Get executed line numbers for a specific file

Parameters:

  • file_path (String)

    absolute path to the source file

Returns:

  • (Hash{Integer => Boolean})

    line_number => executed?, or nil if inactive



94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
# File 'lib/rails_error_dashboard/services/coverage_tracker.rb', line 94

def peek(file_path)
  return nil if file_path.nil?
  return nil unless @active

  result = Coverage.peek_result
  file_data = result[file_path]
  return {} unless file_data

  # Coverage result format varies by mode:
  # - oneshot_lines: { oneshot_lines: [nil, 0, nil, 1, ...] }
  # - lines (SimpleCov): { lines: [0, 1, nil, 2, ...] } or just [0, 1, nil, 2, ...]
  # where index = line_number - 1, nil = not executable, 0 = not hit, N>0 = hit
  lines_data = if file_data.is_a?(Hash)
    file_data[:oneshot_lines] || file_data[:lines]
  elsif file_data.is_a?(Array)
    file_data
  end
  return {} unless lines_data.is_a?(Array)

  executed = {}
  lines_data.each_with_index do |val, idx|
    next if val.nil? # not an executable line

    line_number = idx + 1
    executed[line_number] = val > 0
  end

  executed
rescue => e
  Rails.logger.error("[RailsErrorDashboard] CoverageTracker.peek failed: #{e.class}: #{e.message}")
  {}
end

.supported?Boolean

Check if the current Ruby version supports Coverage with oneshot_lines

Returns:

  • (Boolean)


26
27
28
# File 'lib/rails_error_dashboard/services/coverage_tracker.rb', line 26

def supported?
  RUBY_VERSION >= "3.2"
end