Module: Mutineer::MutantId
- Defined in:
- lib/mutineer/mutant_id.rb
Overview
Content-based stable id for a mutant — NOT byte offsets. Pure function, reused
by the Runner (matching the ignore list), the Reporter (emitting a copy-
pasteable id per survivor), and #13 baseline gating (diffing id-sets run to
run). digest is stdlib, so zero new deps.
Offset-free by design: keyed on the subject's qualified_name (a method, not a byte position) + operator + the normalized mutated token + an occurrence ordinal among same-(operator, token) twins WITHIN the subject. So it survives any edit outside the subject method — where raw start/end offsets shift on every edit earlier in the file and would silently stop matching.
Class Method Summary collapse
-
.for(subject, mutation, source, occurrence = 0) ⇒ Object
NUL-joined so token delimiters (
||=, spaces,::,#) can never collide with the separator; SHA256 gives a fixed-length, copy-pasteable key. -
.for_subject(subject, source, mutations) ⇒ Object
Ids for a subject's full mutation list, in input order, assigning each its 0-based occurrence among twins sharing the same (operator, token).
-
.normalized_token(mutation, source) ⇒ Object
The exact code being mutated, whitespace-collapsed — same normalization the Reporter's diff_for uses for its token label.
Class Method Details
.for(subject, mutation, source, occurrence = 0) ⇒ Object
NUL-joined so token delimiters (||=, spaces, ::, #) can never collide
with the separator; SHA256 gives a fixed-length, copy-pasteable key.
21 22 23 24 25 26 |
# File 'lib/mutineer/mutant_id.rb', line 21 def for(subject, mutation, source, occurrence = 0) Digest::SHA256.hexdigest( [subject.qualified_name, mutation.operator, normalized_token(mutation, source), occurrence].join("\x00") )[0, 12] end |
.for_subject(subject, source, mutations) ⇒ Object
Ids for a subject's full mutation list, in input order, assigning each its
0-based occurrence among twins sharing the same (operator, token). This is
what disambiguates a + b + c's two + mutants without an offset.
31 32 33 34 35 36 37 38 39 |
# File 'lib/mutineer/mutant_id.rb', line 31 def for_subject(subject, source, mutations) seen = Hash.new(0) mutations.map do |m| key = [m.operator, normalized_token(m, source)] occ = seen[key] seen[key] += 1 self.for(subject, m, source, occ) end end |
.normalized_token(mutation, source) ⇒ Object
The exact code being mutated, whitespace-collapsed — same normalization the Reporter's diff_for uses for its token label.
43 44 45 |
# File 'lib/mutineer/mutant_id.rb', line 43 def normalized_token(mutation, source) source.byteslice(mutation.start_offset...mutation.end_offset).gsub(/\s+/, " ").strip end |