From e7b3e2ba9cf5f063c8671d53df851fc38d394c1a Mon Sep 17 00:00:00 2001 From: PJ Date: Sat, 18 Apr 2026 00:51:39 +0700 Subject: [PATCH] refactor(runner): caller manages app launch/terminate Removes Launch + Terminate from runner.Run so the CLI can launch the app first, wait for the SDK to connect, then start the loop. The previous shape forced runner to launch internally which fought with the SDK-must-be-connected-first ordering. BundleID/ClearState fields go away too since runner no longer launches; the CLI keeps them on its testOptions struct. --- internal/runner/runner.go | 14 ++++---------- internal/runner/runner_test.go | 9 --------- 2 files changed, 4 insertions(+), 19 deletions(-) diff --git a/internal/runner/runner.go b/internal/runner/runner.go index af04f0c..05cbf9c 100644 --- a/internal/runner/runner.go +++ b/internal/runner/runner.go @@ -15,8 +15,6 @@ import ( ) type Options struct { - BundleID string - ClearState bool Duration time.Duration SnapshotTimeout time.Duration IdleTimeout time.Duration @@ -39,20 +37,16 @@ type ViolationRecord struct { Properties []string } +// Run drives the snapshot/evaluate/release/act loop until the duration +// elapses or the context is canceled. The caller is responsible for +// launching the app and connecting the SDK before Run is called, and for +// terminating the app afterwards. func Run(ctx context.Context, options Options) (Summary, error) { if err := validate(options); err != nil { return Summary{}, err } summary := Summary{StartTime: time.Now()} - - if err := options.Driver.Launch(ctx, options.BundleID, options.ClearState); err != nil { - return summary, fmt.Errorf("launch: %w", err) - } - defer func() { - _ = options.Driver.Terminate(context.Background()) - }() - deadline := summary.StartTime.Add(options.Duration) stepIndex := 0 for time.Now().Before(deadline) { diff --git a/internal/runner/runner_test.go b/internal/runner/runner_test.go index 78e040f..fc241c1 100644 --- a/internal/runner/runner_test.go +++ b/internal/runner/runner_test.go @@ -129,7 +129,6 @@ func TestRunner_HappyPathStepsAndTraces(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() summary, err := Run(ctx, Options{ - BundleID: "com.fixture", Duration: 100 * time.Millisecond, SnapshotTimeout: 2 * time.Second, IdleTimeout: 50 * time.Millisecond, @@ -149,15 +148,9 @@ func TestRunner_HappyPathStepsAndTraces(t *testing.T) { } actions := state.mock.Actions() - if !containsAction(actions, mockdriver.ActionLaunch, "com.fixture") { - t.Errorf("expected Launch with com.fixture, got %v", actions) - } if !containsAction(actions, mockdriver.ActionTapSelector, "id:next") { t.Errorf("expected TapSelector with id:next, got %v", actions) } - if !containsAction(actions, mockdriver.ActionTerminate, "") { - t.Errorf("expected Terminate, got %v", actions) - } } func TestRunner_ViolationSurfacesInSummary(t *testing.T) { @@ -173,7 +166,6 @@ func TestRunner_ViolationSurfacesInSummary(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() summary, err := Run(ctx, Options{ - BundleID: "com.fixture", Duration: 100 * time.Millisecond, SnapshotTimeout: 2 * time.Second, IdleTimeout: 50 * time.Millisecond, @@ -223,7 +215,6 @@ func TestRunner_RecordsScreenFieldFromSnapshot(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() if _, err := Run(ctx, Options{ - BundleID: "com.fixture", Duration: 100 * time.Millisecond, SnapshotTimeout: 2 * time.Second, IdleTimeout: 50 * time.Millisecond,