Module: Mutineer::MinitestIntegration::StopAtFirstFailure

Defined in:
lib/mutineer/minitest_integration/stop_at_first_failure.rb

Overview

Stops a Minitest run at the first failing test: one failure already kills the mutant. Child-process only, like MinitestIntegration.

It sets a flag instead of unwinding the stack, so that class-level wrappers (after_all, a block-form transaction) still finish. The remaining tests and classes then return before they start.

Minitest 5 and 6 need different hook points. An unknown shape installs no hook, and the run is a full run.

Defined Under Namespace

Modules: OuterReporter5, OuterReporter6, RecordFailure, SkipAfterStop5, SkipAfterStop6

Class Attribute Summary collapse

Class Method Summary collapse

Class Attribute Details

.armed_pid ⇒ Integer?

The pid of the armed process. Forked workers inherit the other state, and the pid keeps them from acting on it.

Returns:

  • (Integer, nil)


117
118
119
# File 'lib/mutineer/minitest_integration/stop_at_first_failure.rb', line 117

def armed_pid
  @armed_pid
end

.armed_reporter ⇒ Minitest::CompositeReporter?

The outer reporter of the armed run.

Returns:

  • (Minitest::CompositeReporter, nil)


122
123
124
# File 'lib/mutineer/minitest_integration/stop_at_first_failure.rb', line 122

def armed_reporter
  @armed_reporter
end

.stopped ⇒ Boolean?

True after the outer reporter records a failure or an error.

Returns:

  • (Boolean, nil)


127
128
129
# File 'lib/mutineer/minitest_integration/stop_at_first_failure.rb', line 127

def stopped
  @stopped
end

Class Method Details

.arm!(runnables) ⇒ Boolean

Installs the hooks and arms the stop for this process. Call it after the test files load, so that every test class gets its prepend.

Parameters:

  • runnables (Array<Class>) —

    the loaded test classes.

Returns:

  • (Boolean) —

    false when the Minitest shape is unknown.



134
135
136
137
138
139
140
141
142
143
144
145
# File 'lib/mutineer/minitest_integration/stop_at_first_failure.rb', line 134

def arm!(runnables)
  outer, skip = hooks_for_loaded_minitest
  return false unless outer

  prepend_once(::Minitest::CompositeReporter, RecordFailure)
  prepend_once(::Minitest.singleton_class, outer)
  runnables.each { |klass| prepend_once(klass.singleton_class, skip) }
  self.armed_pid = Process.pid
  self.armed_reporter = nil
  self.stopped = false
  true
end

.armed_here? ⇒ Boolean

True when the run is armed in this process.

Returns:

  • (Boolean)


159
160
161
# File 'lib/mutineer/minitest_integration/stop_at_first_failure.rb', line 159

def armed_here?
  armed_pid == Process.pid
end

.disarm! ⇒ void

This method returns an undefined value.

Clears the armed state.



150
151
152
153
154
# File 'lib/mutineer/minitest_integration/stop_at_first_failure.rb', line 150

def disarm!
  self.armed_pid = nil
  self.armed_reporter = nil
  self.stopped = false
end

.hooks_for_loaded_minitest ⇒ Array(Module, Module)? (private)

Picks the hook modules for the loaded Minitest.

Returns:

  • (Array(Module, Module), nil) —

    the outer reporter hook and the skip hook, or nil for an unknown shape.



176
177
178
179
180
181
182
183
# File 'lib/mutineer/minitest_integration/stop_at_first_failure.rb', line 176

def hooks_for_loaded_minitest
  runnable = ::Minitest::Runnable
  if ::Minitest.respond_to?(:run_all_suites) && runnable.respond_to?(:run_suite)
    [OuterReporter6, SkipAfterStop6]
  elsif ::Minitest.respond_to?(:__run) && runnable.respond_to?(:run_one_method)
    [OuterReporter5, SkipAfterStop5]
  end
end

.prepend_once(target, mod) ⇒ void (private)

This method returns an undefined value.

Prepends mod on target once. A copy on a superclass does not count, because it comes after the own methods of target.

Parameters:

  • target (Module) —

    the class or singleton class.

  • mod (Module) —

    the module to prepend.



191
192
193
194
195
# File 'lib/mutineer/minitest_integration/stop_at_first_failure.rb', line 191

def prepend_once(target, mod)
  return if target.ancestors.take_while { |a| !a.equal?(target) }.include?(mod)

  target.prepend(mod)
end

.stopped_here? ⇒ Boolean

True when the armed run in this process has stopped.

Returns:

  • (Boolean)


166
167
168
# File 'lib/mutineer/minitest_integration/stop_at_first_failure.rb', line 166

def stopped_here?
  stopped == true && armed_here?
end