Class: Ibex::Coverage::Collector

Inherits:
Object
  • Object
show all
Defined in:
lib/ibex/coverage/collector.rb,
sig/ibex/coverage/collector.rbs

Overview

Strictly turns one or more complete parse sessions into a coverage report.

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeCollector

Returns a new instance of Collector.

RBS:

  • () -> void



23
24
25
26
27
28
29
30
31
32
33
34
# File 'lib/ibex/coverage/collector.rb', line 23

def initialize
  @grammar_digest = nil
  @table_format_version = nil
  @state_count = nil
  @production_count = nil
  @sessions = 0
  @event_count = 0
  @state_hits = Hash.new(0)
  @production_hits = Hash.new(0)
  @in_session = false
  @expected_sequence = 1
end

Class Method Details

.collect_file(path) ⇒ Report

RBS:

  • (String path) -> Report

Parameters:

  • path (String)

Returns:



37
38
39
40
41
42
43
# File 'lib/ibex/coverage/collector.rb', line 37

def self.collect_file(path)
  collector = new
  EventStream.each_file(path) do |document, line|
    collector.consume(document, source: path, line: line)
  end
  collector.finish(source: path)
end

Instance Method Details

#consume(document, source:, line:) ⇒ void

This method returns an undefined value.

RBS:

  • (Hash[String, json_value] document, source: String, line: Integer) -> void

Parameters:

  • document (Hash[String, json_value])
  • source: (String)
  • line: (Integer)


46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
# File 'lib/ibex/coverage/collector.rb', line 46

def consume(document, source:, line:)
  event = document.fetch("event")
  sequence = document.fetch("sequence")
  data = document.fetch("data")
  unless event.is_a?(String) && data.is_a?(Hash) && data.keys.all?(String)
    invalid(source, line, "event document contains invalid session data")
  end
  event_data = data #: Hash[String, json_value]
  if event == "start"
    start_session(event_data, sequence, source, line)
  else
    consume_session_event(event, event_data, sequence, source, line)
  end
  @event_count += 1
end

#consume_session_event(event, data, sequence, source, line) ⇒ void

This method returns an undefined value.

RBS:

  • (String event, Hash[String, json_value] data, json_value sequence, String source, Integer line) -> void

Parameters:

  • event (String)
  • data (Hash[String, json_value])
  • sequence (json_value)
  • source (String)
  • line (Integer)


97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
# File 'lib/ibex/coverage/collector.rb', line 97

def consume_session_event(event, data, sequence, source, line)
  invalid(source, line, "event appears outside a parse session") unless @in_session
  unless sequence == @expected_sequence
    invalid(source, line, "expected event sequence #{@expected_sequence}, got #{sequence.inspect}")
  end

  case event
  when "shift", "recover" then hit_state(data["state"], source, line)
  when "reduce"
    hit_production(data["production_id"], source, line)
    hit_state(data["goto_state"], source, line)
  end
  if %w[accept reject].include?(event)
    @in_session = false
    @expected_sequence = 1
  else
    @expected_sequence += 1
  end
end

#establish_metadata(metadata, source, line) ⇒ void

This method returns an undefined value.

RBS:

  • ([String, Integer, Integer, Integer] metadata, String source, Integer line) -> void

Parameters:

  • metadata ([ String, Integer, Integer, Integer ])
  • source (String)
  • line (Integer)


133
134
135
136
137
138
139
140
141
# File 'lib/ibex/coverage/collector.rb', line 133

def (, source, line)
  unless @grammar_digest
    @grammar_digest, @table_format_version, @state_count, @production_count = 
    return
  end

  expected = [@grammar_digest, @table_format_version, @state_count, @production_count]
  invalid(source, line, "parse sessions use different parser metadata") unless  == expected
end

#finish(source:) ⇒ Report

RBS:

  • (source: String) -> Report

Parameters:

  • source: (String)

Returns:



63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
# File 'lib/ibex/coverage/collector.rb', line 63

def finish(source:)
  invalid(source, 1, "event stream ended before accept or reject") if @in_session
  invalid(source, 1, "event stream does not contain a parse session") if @sessions.zero?
  Report.new(
    grammar_digest: (@grammar_digest),
    table_format_version: (@table_format_version),
    state_count: (@state_count),
    production_count: (@production_count),
    sessions: @sessions,
    event_count: @event_count,
    state_hits: @state_hits,
    production_hits: @production_hits
  )
rescue ArgumentError => e
  raise Ibex::Error, "#{source}:1:1: #{e.message}"
end

#hit_production(id, source, line) ⇒ void

This method returns an undefined value.

RBS:

  • (json_value id, String source, Integer line) -> void

Parameters:

  • id (json_value)
  • source (String)
  • line (Integer)


168
169
170
171
172
173
# File 'lib/ibex/coverage/collector.rb', line 168

def hit_production(id, source, line)
  total = (@production_count)
  invalid(source, line, "production id #{id.inspect} is outside 0...#{total}") unless valid_id?(id, total)
  production_id = id #: Integer
  increment(@production_hits, production_id, "production", source, line)
end

#hit_state(id, source, line) ⇒ void

This method returns an undefined value.

RBS:

  • (json_value id, String source, Integer line) -> void

Parameters:

  • id (json_value)
  • source (String)
  • line (Integer)


160
161
162
163
164
165
# File 'lib/ibex/coverage/collector.rb', line 160

def hit_state(id, source, line)
  total = (@state_count)
  invalid(source, line, "state id #{id.inspect} is outside 0...#{total}") unless valid_id?(id, total)
  state_id = id #: Integer
  increment(@state_hits, state_id, "state", source, line)
end

#increment(hits, id, kind, source, line) ⇒ void

This method returns an undefined value.

RBS:

  • (Hash[Integer, Integer] hits, Integer id, String kind, String source, Integer line) -> void

Parameters:

  • hits (Hash[Integer, Integer])
  • id (Integer)
  • kind (String)
  • source (String)
  • line (Integer)


181
182
183
184
185
# File 'lib/ibex/coverage/collector.rb', line 181

def increment(hits, id, kind, source, line)
  count = hits[id] + 1
  invalid(source, line, "#{kind} hit count exceeds the supported count") if count > Report::MAX_COUNT
  hits[id] = count
end

#invalid(source, line, message) ⇒ bot

RBS:

  • (String source, Integer line, String message) -> bot

Parameters:

  • source (String)
  • line (Integer)
  • message (String)

Returns:

  • (bot)


193
194
195
# File 'lib/ibex/coverage/collector.rb', line 193

def invalid(source, line, message)
  raise Ibex::Error, "#{source}:#{line}:1: #{message}"
end

#required_metadata(value) ⇒ void

This method returns an undefined value.

RBS:

  • [T] (T? value) -> T



188
189
190
# File 'lib/ibex/coverage/collector.rb', line 188

def (value)
  value || raise(ArgumentError, "coverage metadata is unavailable")
end

#session_metadata(data, source, line) ⇒ [ [ String, Integer, Integer, Integer ], Integer ]

RBS:

  • (Hash[String, json_value] data, String source, Integer line) -> [[String, Integer, Integer, Integer], Integer]

Parameters:

  • data (Hash[String, json_value])
  • source (String)
  • line (Integer)

Returns:

  • ([ [ String, Integer, Integer, Integer ], Integer ])


119
120
121
122
123
124
125
126
127
128
129
130
# File 'lib/ibex/coverage/collector.rb', line 119

def (data, source, line)
  digest = data["grammar_digest"]
  format = data["table_format_version"]
  states = data["state_count"]
  productions = data["production_count"]
  initial_state = data["initial_state"]
  unless digest.is_a?(String) && format.is_a?(Integer) && states.is_a?(Integer) &&
         productions.is_a?(Integer) && initial_state.is_a?(Integer)
    invalid(source, line, "start event lacks generated parser coverage metadata")
  end
  [[digest, format, states, productions], initial_state]
end

#start_session(data, sequence, source, line) ⇒ void

This method returns an undefined value.

RBS:

  • (Hash[String, json_value] data, json_value sequence, String source, Integer line) -> void

Parameters:

  • data (Hash[String, json_value])
  • sequence (json_value)
  • source (String)
  • line (Integer)


83
84
85
86
87
88
89
90
91
92
93
94
# File 'lib/ibex/coverage/collector.rb', line 83

def start_session(data, sequence, source, line)
  invalid(source, line, "new start event before prior session ended") if @in_session
  invalid(source, line, "start event sequence must be 1") unless sequence == 1

  , initial_state = (data, source, line)
  (, source, line)
  validate_metadata!(source, line)
  hit_state(initial_state, source, line)
  @sessions += 1
  @in_session = true
  @expected_sequence = 2
end

#valid_id?(id, total) ⇒ Boolean

RBS:

  • (json_value id, Integer total) -> bool

Parameters:

  • id (json_value)
  • total (Integer)

Returns:

  • (Boolean)


176
177
178
# File 'lib/ibex/coverage/collector.rb', line 176

def valid_id?(id, total)
  id.is_a?(Integer) && id >= 0 && id < total
end

#validate_metadata!(source, line) ⇒ void

This method returns an undefined value.

RBS:

  • (String source, Integer line) -> void

Parameters:

  • source (String)
  • line (Integer)


144
145
146
147
148
149
150
151
152
153
154
155
156
157
# File 'lib/ibex/coverage/collector.rb', line 144

def validate_metadata!(source, line)
  Report.new(
    grammar_digest: (@grammar_digest),
    table_format_version: (@table_format_version),
    state_count: (@state_count),
    production_count: (@production_count),
    sessions: 1,
    event_count: 1,
    state_hits: {},
    production_hits: {}
  )
rescue ArgumentError => e
  invalid(source, line, e.message)
end