Class: Henitai::Result

Inherits:
Object
  • Object
show all
Includes:
UnparseHelper
Defined in:
lib/henitai/result.rb

Overview

Aggregates the outcome of a complete mutation testing run.

Provides metrics and the serialised Stryker mutation-testing-report-schema JSON payload. The schema version follows stryker-mutator/mutation-testing-elements.

Constant Summary collapse

SCHEMA_VERSION =
"1.0"
DEFAULT_THRESHOLDS =
{ high: 80, low: 60 }.freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(mutants:, started_at:, finished_at:, thresholds: nil, partial_rerun: false, survivor_stats: nil, session_id: SecureRandom.uuid, git_sha: nil) ⇒ Result

rubocop:disable Metrics/ParameterLists



21
22
23
24
25
26
27
28
29
30
31
32
# File 'lib/henitai/result.rb', line 21

def initialize(mutants:, started_at:, finished_at:, thresholds: nil,
               partial_rerun: false, survivor_stats: nil,
               session_id: SecureRandom.uuid, git_sha: nil)
  @mutants        = mutants
  @started_at     = started_at
  @finished_at    = finished_at
  @thresholds     = DEFAULT_THRESHOLDS.merge(thresholds || {})
  @partial_rerun  = partial_rerun
  @survivor_stats = survivor_stats
  @session_id     = session_id
  @git_sha        = git_sha
end

Instance Attribute Details

#finished_atObject (readonly)

Returns the value of attribute finished_at.



17
18
19
# File 'lib/henitai/result.rb', line 17

def finished_at
  @finished_at
end

#git_shaObject (readonly)

Returns the value of attribute git_sha.



17
18
19
# File 'lib/henitai/result.rb', line 17

def git_sha
  @git_sha
end

#mutantsObject (readonly)

Returns the value of attribute mutants.



17
18
19
# File 'lib/henitai/result.rb', line 17

def mutants
  @mutants
end

#session_idObject (readonly)

Returns the value of attribute session_id.



17
18
19
# File 'lib/henitai/result.rb', line 17

def session_id
  @session_id
end

#started_atObject (readonly)

Returns the value of attribute started_at.



17
18
19
# File 'lib/henitai/result.rb', line 17

def started_at
  @started_at
end

#survivor_statsObject (readonly)

Returns the value of attribute survivor_stats.



17
18
19
# File 'lib/henitai/result.rb', line 17

def survivor_stats
  @survivor_stats
end

#thresholdsObject (readonly)

Returns the value of attribute thresholds.



17
18
19
# File 'lib/henitai/result.rb', line 17

def thresholds
  @thresholds
end

Instance Method Details

#detectedInteger

Detected = killed + timeout + runtime_error (alle Zustände die einen Fehler beweisen)

Returns:

  • (Integer)


48
49
50
# File 'lib/henitai/result.rb', line 48

def detected
  mutants.count { |m| %i[killed timeout runtime_error].include?(m.status) }
end

#durationFloat

Returns duration in seconds.

Returns:

  • (Float)

    duration in seconds



96
97
98
# File 'lib/henitai/result.rb', line 96

def duration
  finished_at - started_at
end

#equivalentInteger

Returns number of confirmed equivalent mutants (excluded from MS).

Returns:

  • (Integer)

    number of confirmed equivalent mutants (excluded from MS)



44
# File 'lib/henitai/result.rb', line 44

def equivalent = mutants.count(&:equivalent?)

#killedInteger

Returns number of killed mutants.

Returns:

  • (Integer)

    number of killed mutants



38
# File 'lib/henitai/result.rb', line 38

def killed   = mutants.count(&:killed?)

#mutation_scoreFloat?

Mutation Score (MS) — Architektur-Formel aus Abschnitt 6.1:

MS = detected / (total  ignored  no_coverage  compile_error  equivalent)

Confirmed equivalent mutants werden aus BEIDEN Seiten der Gleichung entfernt: Sie sind weder im Zähler (nicht detektierbar) noch im Nenner (nicht testbar). Das ist der entscheidende Unterschied zum MSI.

Returns:

  • (Float, nil)

    0.0–100.0, nil wenn kein valider Mutant vorhanden



61
62
63
64
65
66
67
# File 'lib/henitai/result.rb', line 61

def mutation_score
  excluded = %i[ignored no_coverage compile_error equivalent]
  valid = mutants.reject { |m| excluded.include?(m.status) }
  return nil if valid.empty?

  ((detected.to_f / valid.count) * 100.0).round(2).to_f
end

#mutation_score_indicatorFloat?

Mutation Score Indicator (MSI) — naive Berechnung ohne Äquivalenz-Bereinigung:

MSI = killed / all_mutants

MSI ist immer ≤ MS. Der Unterschied kommuniziert die Äquivalenz-Unsicherheit. Beide Werte MÜSSEN im Report zusammen ausgewiesen werden (Anti-Pattern: nur MS).

Returns:

  • (Float, nil)


77
78
79
80
81
# File 'lib/henitai/result.rb', line 77

def mutation_score_indicator
  return nil if mutants.empty?

  ((killed.to_f / mutants.count) * 100.0).round(2).to_f
end

#partial_rerun?Boolean

rubocop:enable Metrics/ParameterLists

Returns:

  • (Boolean)


35
# File 'lib/henitai/result.rb', line 35

def partial_rerun? = @partial_rerun

#scoring_summaryObject

Compact public summary for reporters. The uncertainty note is intentionally qualitative: equivalent mutants are a known gray area, so the terminal report should communicate that uncertainty instead of pretending to be precise.



87
88
89
90
91
92
93
# File 'lib/henitai/result.rb', line 87

def scoring_summary
  {
    mutation_score: mutation_score,
    mutation_score_indicator: mutation_score_indicator,
    equivalence_uncertainty: equivalence_uncertainty
  }
end

#survivedInteger

Returns number of survived mutants.

Returns:

  • (Integer)

    number of survived mutants



41
# File 'lib/henitai/result.rb', line 41

def survived = mutants.count(&:survived?)

#to_stryker_schemaHash

Serialise to Stryker mutation-testing-report-schema JSON (schema 1.0).

Returns:

  • (Hash)


102
103
104
105
106
107
108
109
110
111
# File 'lib/henitai/result.rb', line 102

def to_stryker_schema
  schema = base_schema
  sha = @git_sha
  schema[:gitSha] = sha if sha
  return schema unless partial_rerun?

  schema[:partialRerun] = true
  schema[:unmatchedSurvivorIds] = unmatched_survivor_ids
  schema
end