mirror of
https://github.com/priyanshujain/sanderling.git
synced 2026-10-04 20:17:09 +00:00
fix: bound the pre-run app launch
launch happens before the runner starts, so --duration never covered it and a wedged driver hung with no trace and no error.
This commit is contained in:
1 parent
5017036b62
commit
68d5633d1d
2 files changed
+65
-2
No files matched your search
@@ -20,6 +20,26 @@ import (
|
|||||||
|
|
||||||
const sidecarStartupTimeout = 30 * time.Second
|
const sidecarStartupTimeout = 30 * time.Second
|
||||||
|
|
||||||
|
// launchTimeout bounds the pre-run app launch. It happens before the runner
|
||||||
|
// starts, so --duration does not cover it, and Execute's context is the bare
|
||||||
|
// signal-aware root with no deadline of its own: a driver wedged here would
|
||||||
|
// hang the run forever having printed nothing and written no trace. Generous
|
||||||
|
// enough to sit above every driver's own launch bound (the iOS clear-state path
|
||||||
|
// reinstalls the app first) so a driver-level error is what a user usually
|
||||||
|
// sees, and this stays the backstop. A variable so the timeout test can shrink
|
||||||
|
// it.
|
||||||
|
var launchTimeout = 3 * time.Minute
|
||||||
|
|
||||||
|
// launchApp starts the app under test under a bounded context.
|
||||||
|
func launchApp(ctx context.Context, activeDriver driver.DeviceDriver, options Options) error {
|
||||||
|
launchCtx, cancel := context.WithTimeout(ctx, launchTimeout)
|
||||||
|
defer cancel()
|
||||||
|
if err := activeDriver.Launch(launchCtx, options.BundleID, options.ClearData, nil); err != nil {
|
||||||
|
return fmt.Errorf("launch app: %w", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// Options are the parameters for a single test pipeline run.
|
// Options are the parameters for a single test pipeline run.
|
||||||
type Options struct {
|
type Options struct {
|
||||||
Spec string
|
Spec string
|
||||||
@@ -145,8 +165,8 @@ func Execute(ctx context.Context, options Options, stdout io.Writer) error {
|
|||||||
}
|
}
|
||||||
defer cleanup()
|
defer cleanup()
|
||||||
|
|
||||||
if err := activeDriver.Launch(ctx, options.BundleID, options.ClearData, nil); err != nil {
|
if err := launchApp(ctx, activeDriver, options); err != nil {
|
||||||
return fmt.Errorf("launch app: %w", err)
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if web, ok := activeDriver.(driver.WebDriver); ok && len(webBundle.JavaScript) > 0 {
|
if web, ok := activeDriver.(driver.WebDriver); ok && len(webBundle.JavaScript) > 0 {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package testrun
|
package testrun
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
@@ -8,6 +9,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/priyanshujain/sanderling/internal/driver"
|
||||||
"github.com/priyanshujain/sanderling/internal/runner"
|
"github.com/priyanshujain/sanderling/internal/runner"
|
||||||
"github.com/priyanshujain/sanderling/internal/verifier"
|
"github.com/priyanshujain/sanderling/internal/verifier"
|
||||||
)
|
)
|
||||||
@@ -261,3 +263,44 @@ func TestRunOutcome_ReportsViolationsOnlyUnderTheFlag(t *testing.T) {
|
|||||||
t.Errorf("count: got %d, want 1", violations.Count)
|
t.Errorf("count: got %d, want 1", violations.Count)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// wedgedLaunchDriver never returns from Launch, standing in for a driver whose
|
||||||
|
// device-side session is stuck.
|
||||||
|
type wedgedLaunchDriver struct {
|
||||||
|
driver.DeviceDriver
|
||||||
|
release chan struct{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *wedgedLaunchDriver) Launch(ctx context.Context, _ string, _ bool, _ map[string]string) error {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return ctx.Err()
|
||||||
|
case <-w.release:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestLaunchAppBoundsWedgedDriver proves the pre-run launch carries a deadline.
|
||||||
|
// It runs before the runner starts, so --duration does not cover it and
|
||||||
|
// Execute's root context has no deadline: unbounded, a wedged driver hangs the
|
||||||
|
// run forever with no trace directory and no error.
|
||||||
|
func TestLaunchAppBoundsWedgedDriver(t *testing.T) {
|
||||||
|
previous := launchTimeout
|
||||||
|
launchTimeout = 100 * time.Millisecond
|
||||||
|
defer func() { launchTimeout = previous }()
|
||||||
|
|
||||||
|
wedged := &wedgedLaunchDriver{release: make(chan struct{})}
|
||||||
|
defer close(wedged.release)
|
||||||
|
|
||||||
|
done := make(chan error, 1)
|
||||||
|
go func() { done <- launchApp(context.Background(), wedged, Options{BundleID: "com.example.app"}) }()
|
||||||
|
|
||||||
|
select {
|
||||||
|
case err := <-done:
|
||||||
|
if !errors.Is(err, context.DeadlineExceeded) {
|
||||||
|
t.Fatalf("err = %v, want a deadline-exceeded error", err)
|
||||||
|
}
|
||||||
|
case <-time.After(10 * time.Second):
|
||||||
|
t.Fatal("launchApp never returned: the pre-run launch is unbounded, so a wedged driver hangs the run forever")
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in new issue
Block a user