mirror of
https://github.com/priyanshujain/sanderling.git
synced 2026-10-04 03:57:09 +00:00
fix(runner): a bounded hold puts the swallow back one step later
the hold carries one action; letting the runner act again while the verifier is still skipped overwrites it, so the carried action reaches no spec. hold for as long as the verifier is skipped, and settle on a held step so the reread pair is not tighter than the window the detector was measured over.
This commit is contained in:
1 parent
71134a3347
commit
86da6ed8ee
2 files changed
+130
-37
No files matched your search
+18
-18
@@ -100,7 +100,6 @@ func Run(ctx context.Context, options Options) (Summary, error) {
|
||||
deadline := summary.StartTime.Add(options.Duration)
|
||||
stepIndex := 0
|
||||
consecutiveApplyFailures := 0
|
||||
heldSteps := 0
|
||||
var lastAction *verifier.Action
|
||||
var lastLogTime time.Time
|
||||
for time.Now().Before(deadline) {
|
||||
@@ -287,16 +286,16 @@ func Run(ctx context.Context, options Options) (Summary, error) {
|
||||
// their effects would then see an effect whose cause the runner
|
||||
// swallowed. See TestRunner_ASkippedStepDoesNotSwallowTheActionBeforeIt.
|
||||
//
|
||||
// Bounded, because a screen that never settles must not stall the whole
|
||||
// run: past the bound the runner acts anyway, which is where it was
|
||||
// before this held anything back.
|
||||
held := skippedVerification && heldSteps < maxHeldSteps
|
||||
// Unbounded, because lastAction holds exactly one action: any bound that
|
||||
// let the runner act again while the verifier was still being skipped
|
||||
// would overwrite the action the hold was carrying, and that is the same
|
||||
// swallow arriving one step later. A screen that keeps moving therefore
|
||||
// costs the run its actions rather than its soundness, and a run that
|
||||
// verified nothing says so in its outcome (internal/testrun).
|
||||
held := skippedVerification
|
||||
if held {
|
||||
heldSteps++
|
||||
logger.Warn("screen still moving; holding this step's action back",
|
||||
"step", stepIndex, "held", heldSteps)
|
||||
} else {
|
||||
heldSteps = 0
|
||||
"step", stepIndex)
|
||||
}
|
||||
|
||||
var nextAction verifier.Action
|
||||
@@ -401,7 +400,16 @@ func Run(ctx context.Context, options Options) (Summary, error) {
|
||||
// concurrent fetches observe a stable post-action state. A transient
|
||||
// apply error means nothing landed, so the idle poll has nothing to
|
||||
// settle and may itself hang on the same device condition.
|
||||
if nextErr == nil && !applySkipped && nextAction.Kind != verifier.ActionKindWait {
|
||||
//
|
||||
// A held step settles too, and it is the only case here that waits with
|
||||
// nothing applied. The reread that held it takes its two reads a round
|
||||
// trip apart, which is a tighter window than the one the detector was
|
||||
// measured over (an action and a settle); looping straight back into it
|
||||
// would compare two reads of a composing screen closer together still,
|
||||
// so the screen that most needs to settle is the one given least room.
|
||||
applied := nextErr == nil && !applySkipped &&
|
||||
nextAction.Kind != verifier.ActionKindWait
|
||||
if held || applied {
|
||||
idleCtx, idleCancel := context.WithTimeout(ctx, options.IdleTimeout)
|
||||
idleErr := options.Driver.WaitForIdle(idleCtx, options.IdleTimeout)
|
||||
if idleErr != nil && idleCtx.Err() == nil {
|
||||
@@ -1316,14 +1324,6 @@ func encodeResiduals(residuals map[string]ltl.Formula) (map[string]json.RawMessa
|
||||
// would be spent doing nothing.
|
||||
const maxConsecutiveApplyFailures = 3
|
||||
|
||||
// maxHeldSteps bounds how many steps in a row the runner will decline to act on
|
||||
// because their screen was still moving. It is a livelock bound, not a settle
|
||||
// budget: a screen that changes shape under every pair of reads (a live list, a
|
||||
// spinner that mounts and unmounts) would otherwise take the whole run without
|
||||
// the fuzzer ever touching it. Two is what the measured cases need, which came
|
||||
// one step at a time and never twice in a row.
|
||||
const maxHeldSteps = 2
|
||||
|
||||
// isWDADrop reports that the sidecar could not restart the iOS XCTest
|
||||
// runner: the channel is gone for good and the run must abort. Transient
|
||||
// drops are classified by the sidecar itself (it reconnects and surfaces
|
||||
|
||||
Reference in new issue
Block a user