Class: Mutineer::Baseline
- Inherits:
-
Object
- Object
- Mutineer::Baseline
- Defined in:
- lib/mutineer/baseline.rb
Overview
#13: CI baseline/delta gating. A baseline is literally a prior
mutineer run --format json document (KTD-1) — no bespoke format to version.
We diff the current run against it by the #10 stable survivor id (KTD-2): a
NEW survivor (id present now, absent in the baseline) OR a score drop is a
regression the CLI turns into exit 1. Pure data — stdlib json only, no fork,
no Rails — so it's testable in isolation from a canned JSON + a hand-built
AggregateResult.
Defined Under Namespace
Classes: Delta
Instance Attribute Summary collapse
-
#score ⇒ Object
readonly
Returns the value of attribute score.
Class Method Summary collapse
-
.load(path) ⇒ Object
Load a prior --format json run.
Instance Method Summary collapse
-
#diff(aggregate, epsilon: 0.0) ⇒ Object
Diff a current AggregateResult against this baseline by stable survivor id.
-
#initialize(doc) ⇒ Baseline
constructor
A new instance of Baseline.
Constructor Details
#initialize(doc) ⇒ Baseline
Returns a new instance of Baseline.
43 44 45 46 |
# File 'lib/mutineer/baseline.rb', line 43 def initialize(doc) @survivors = doc["survivors"] || [] @score = doc.dig("summary", "score") end |
Instance Attribute Details
#score ⇒ Object (readonly)
Returns the value of attribute score.
41 42 43 |
# File 'lib/mutineer/baseline.rb', line 41 def score @score end |
Class Method Details
.load(path) ⇒ Object
Load a prior --format json run. Raises ConfigError (NOT exit — R8: a data class must never kill the host) on a missing/unreadable file, unparseable JSON, or a doc that isn't a baseline shape, so the CLI maps it to exit 2 (usage) like every other bad-path flag.
30 31 32 33 34 35 36 37 38 39 |
# File 'lib/mutineer/baseline.rb', line 30 def self.load(path) doc = JSON.parse(File.read(path)) unless doc.is_a?(Hash) && doc["schema_version"] && doc["survivors"].is_a?(Array) raise ConfigError, "not a Mutineer JSON report: #{path}" end new(doc) rescue JSON::ParserError => e raise ConfigError, "invalid baseline JSON in #{path}: #{e.}" end |
Instance Method Details
#diff(aggregate, epsilon: 0.0) ⇒ Object
Diff a current AggregateResult against this baseline by stable survivor id.
epsilon tolerates float jitter on the score (default 0.0 = any drop gates).
50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 |
# File 'lib/mutineer/baseline.rb', line 50 def diff(aggregate, epsilon: 0.0) current = aggregate.surviving_mutants current_ids = current.map(&:id) baseline_ids = @survivors.map { |h| h["id"] } new_survivors = current.reject { |r| baseline_ids.include?(r.id) } fixed = @survivors.reject { |h| current_ids.include?(h["id"]) } current_score = aggregate.mutation_score # nil-score discipline (mirrors Reporter#exit_code): a score absent on either # side can't be compared — skip the drop check, keep the new-survivor check. score_drop = !@score.nil? && !current_score.nil? && current_score < @score - epsilon Delta.new(new_survivors: new_survivors, fixed_survivors: fixed, score_before: @score, score_after: current_score, score_drop: score_drop, regressed: !new_survivors.empty? || score_drop) end |