Cooperative hooks
Some mutants don’t fail — they hang. An inflated delay or a broken completion
condition makes a test wait forever. Mutato’s outer timeout and respawn always
catch these, but that’s the slow path. A test author can convert many of them
into fast in-process kills by referencing the optional Mutato.Testing
package and wiring hooks where the tests already control time or waiting.
The library is inert under a normal test run — the hooks only engage when the
Mutato runner is driving. Both shipped assemblies are strong-named, so
strong-named test projects don’t need NoWarn CS8002.
The API
Section titled “The API”MutationTesting.Report(magnitude)— report the size of an operation about to start (a delay in ms, a retry count, …). The baseline pass learns each test’s legitimate maximum; a mutant whose report exceeds that ceiling throws a catchable exception at the request site.MutationTesting.Checkpoint()— a wall-clock deadline check for hand-rolled wait/poll loops.MutationTesting.IsActive/IsMutantActive— gate hooks so they only engage under the runner.MutationTimeGuard.Wrap(timeProvider)— a ready-made delegatingTimeProvideradapter.
Example: guarding a fake clock
Section titled “Example: guarding a fake clock”The Polly validation uses exactly this pattern. Polly.Core.Tests adds a roughly
ten-line GuardedFakeTimeProvider : FakeTimeProvider that reports each timer
request, and its new FakeTimeProvider() call sites construct the subclass
instead:
sealed class GuardedFakeTimeProvider : FakeTimeProvider{ public override ITimer CreateTimer( TimerCallback callback, object? state, TimeSpan dueTime, TimeSpan period) { // Report the delay so an inflated-delay mutant dies at the request site // instead of hanging until the outer timeout. MutationTesting.Report(dueTime.TotalMilliseconds); return base.CreateTimer(callback, state, dueTime, period); }}Every .Advance() call and identity assertion keeps working because the guarded
provider is a FakeTimeProvider.