Class: Mutineer::MinitestIntegration

Inherits:
Object
  • Object
show all
Defined in:
lib/mutineer/minitest_integration.rb,
lib/mutineer/minitest_integration/stop_at_first_failure.rb

Overview

Child-process-only: loads a test file in the current process and runs it programmatically, returning an exit status integer (0 = all passed, 1 = any failure/error).

Never call this in the parent — it manipulates global Minitest state (autorun, runnables) that only makes sense in a throwaway forked child.

A missing minitest is rescued as FrameworkUnavailable (Isolation.run still turns that raise into exit 2). There is no rescue around the suite run itself: Isolation.run's fork block is the single exception boundary for unexpected errors. Swallowing those here would create a second exit-2 path and break this method's 0/1 return contract.

Defined Under Namespace

Modules: StopAtFirstFailure

Constant Summary collapse

STOP_AT_FIRST_FAILURE_SEED =

The seed of a run that stops at the first failure, unless SEED is set. With the stop, the test order can decide killed vs timeout; a fixed order keeps the verdict stable for a --baseline gate.

1

Class Method Summary collapse

Class Method Details

.run(test_files, stop_at_first_failure: false) ⇒ Integer

Tested via runner_test.rb, not in isolation — a direct unit test would require forking and duplicate isolation_test's coverage.

test_files is one path or an Array of paths (coverage selection passes the covering subset); each is loaded before the single Minitest.run.

Parameters:

  • test_files (String, Array<String>) —

    one file or many files.

  • stop_at_first_failure (Boolean) (defaults to: false) —

    end the run at the first failing test. Only the mutant path passes true.

Returns:

  • (Integer) —

    0 on success, 1 on failure.



35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
# File 'lib/mutineer/minitest_integration.rb', line 35

def self.run(test_files, stop_at_first_failure: false)
  begin
    require "minitest"
  rescue LoadError
    raise Mutineer::FrameworkUnavailable,
          "minitest is not available — add minitest to the project under test, " \
          "or use --framework rspec"
  end

  # minitest is required LAZILY (never at load time) so Mutineer keeps
  # zero runtime gem deps and loads fine in an rspec-only project; a missing
  # minitest raises a clear Mutineer error rather than a LoadError backtrace.

  # Neutralise autorun so a test file's `require "minitest/autorun"`
  # registers no at_exit hook.
  def Minitest.autorun; end # rubocop:disable Lint/NestedMethodDefinition

  # Drop runnables inherited from the parent suite (this is the child's
  # private copy — the parent is unaffected) so only the target test runs.
  Minitest::Runnable.reset
  Array(test_files).each { |f| load f }

  args = []
  if stop_at_first_failure && StopAtFirstFailure.arm!(Minitest::Runnable.runnables)
    # Pin the seed only when the stop is armed; an unknown Minitest shape
    # gets the normal full, randomly ordered run.
    args = ["--seed", STOP_AT_FIRST_FAILURE_SEED.to_s] unless ENV["SEED"]
  end
  # No silencing here: the fork boundary that calls this method has already
  # pointed stdout at File::NULL (see ChildStdout).
  passed = Minitest.run(args)

  # A plugin can replace the summary reporter, so a stop decides by itself.
  passed && !StopAtFirstFailure.stopped_here? ? 0 : 1
ensure
  StopAtFirstFailure.disarm!
end