Class: Mutineer::Reporter

Inherits:
Object
  • Object
show all
Defined in:
lib/mutineer/reporter.rb

Overview

Renders an AggregateResult: the summary block, mutation score, and per-file survivor diffs. Stream discipline (R14): the report goes to out (stdout), diagnostics/warnings go to err (stderr), so mutineer ... > report.txt captures only the report.

source_map is { file_path => raw source string }, used to extract the containing source line for each survivor diff.

Instance Method Summary collapse

Constructor Details

#initialize(aggregate, source_map) ⇒ Reporter

Returns a new instance of Reporter.



17
18
19
20
# File 'lib/mutineer/reporter.rb', line 17

def initialize(aggregate, source_map)
  @agg = aggregate
  @source_map = source_map
end

Instance Method Details

#exit_code(threshold:) ⇒ Object

0 pass / 1 below threshold or untestable-with-errors. Usage errors (exit 2) are the CLI's job. When the score is nil (nothing killed or survived), pure no_coverage / all-ignored / empty still skip the gate; if any mutant was errored, timed out, or uncapturable, fail the gate so a broken harness cannot green CI under --threshold.



77
78
79
80
81
82
83
84
85
86
87
88
89
# File 'lib/mutineer/reporter.rb', line 77

def exit_code(threshold:)
  return 0 if threshold.nil? || threshold <= 0

  score = @agg.mutation_score
  if score.nil?
    broken = @agg.errored_count + @agg.timeout_count + @agg.uncapturable_count
    return 1 if @agg.total.positive? && broken.positive?

    return 0 # pure no_coverage / ignored / empty — gate skipped
  end

  score >= threshold ? 0 : 1
end

#human_report(out, err, threshold) ⇒ void

This method returns an undefined value.

Renders the human report.

Parameters:

  • out (IO)

    output stream.

  • err (IO)

    error stream.

  • threshold (Float)

    score threshold.



53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
# File 'lib/mutineer/reporter.rb', line 53

def human_report(out, err, threshold)
  if @agg.total.zero?
    err.puts "No mutations generated — verify target files contain in-scope " \
             "operators and are reached by the suite."
    return
  end

  out.puts "Mutineer — Mutation Results"
  out.puts "========================="
  out.puts
  summary(out)
  out.puts
  score_line(out, err)
  per_source(out)

  survivors(out)
  verdict(out, threshold) if threshold && threshold.positive?
end

#report(out: $stdout, err: $stderr, threshold: 0.0, format: "human", output: nil, baseline: nil) ⇒ Object

Single entry point (R20/R21). Branches on format ("human" | "json") and routes the rendered report to output (a file, with a stderr confirmation) or to out. Diagnostics always go to err.



25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
# File 'lib/mutineer/reporter.rb', line 25

def report(out: $stdout, err: $stderr, threshold: 0.0, format: "human", output: nil, baseline: nil)
  rendered =
    if format == "json"
      json_report(baseline)
    elsif format == "html"
      html_report
    else
      sio = StringIO.new
      human_report(sio, err, threshold)
      baseline_section(sio, baseline) if baseline
      sio.string
    end

  if output
    abs = File.expand_path(output)
    File.write(abs, rendered)
    err.puts "Report written to #{abs}"
  else
    out.print rendered
  end
end