feat(cli): add --android-app-path for clear-state reinstall

Wires the APK path from the test command through to the sidecar client so
Android clear-state can reset apps on OEM builds that deny pm clear.
This commit is contained in:
pj committed 2026-06-10 15:20:41 +05:30
1 parent 015f619693
commit 921ecb910e
4 files changed
+33 -28

No files matched your search

+8 -6
View File
@@ -23,12 +23,13 @@ type testOptions struct {
platform string platform string
avd string avd string
device string device string
iosDevice string iosDevice string
iosAppPath string iosAppPath string
duration time.Duration androidAppPath string
seed int64 duration time.Duration
output string seed int64
clearData bool output string
clearData bool
} }
const topUsage = `sanderling is a property-based UI fuzzer for mobile apps. const topUsage = `sanderling is a property-based UI fuzzer for mobile apps.
@@ -56,6 +57,7 @@ func parseTestArgs(args []string, stderr io.Writer) (testOptions, error) {
flagSet.StringVar(&options.device, "device", "", "Android device serial (from `adb devices`) to target when several are connected") flagSet.StringVar(&options.device, "device", "", "Android device serial (from `adb devices`) to target when several are connected")
flagSet.StringVar(&options.iosDevice, "ios-device", "", "iOS target: a simulator name/UDID to boot, or a connected device's name, UDID, or CoreDevice id") flagSet.StringVar(&options.iosDevice, "ios-device", "", "iOS target: a simulator name/UDID to boot, or a connected device's name, UDID, or CoreDevice id")
flagSet.StringVar(&options.iosAppPath, "ios-app-path", "", "path to the .app bundle for iOS clear-state reinstall (simulator: simctl; device: devicectl)") flagSet.StringVar(&options.iosAppPath, "ios-app-path", "", "path to the .app bundle for iOS clear-state reinstall (simulator: simctl; device: devicectl)")
flagSet.StringVar(&options.androidAppPath, "android-app-path", "", "path to the .apk for Android clear-state reinstall; required to reset apps on OEM builds that deny `pm clear`")
flagSet.DurationVar(&options.duration, "duration", 5*time.Minute, "total test duration") flagSet.DurationVar(&options.duration, "duration", 5*time.Minute, "total test duration")
flagSet.Int64Var(&options.seed, "seed", 0, "RNG seed (0 = random)") flagSet.Int64Var(&options.seed, "seed", 0, "RNG seed (0 = random)")
flagSet.StringVar(&options.output, "output", "./runs", "output directory for traces") flagSet.StringVar(&options.output, "output", "./runs", "output directory for traces")
+12 -11
View File
@@ -9,16 +9,17 @@ import (
func runTestPipeline(ctx context.Context, options testOptions, stdout io.Writer) error { func runTestPipeline(ctx context.Context, options testOptions, stdout io.Writer) error {
return testrun.Execute(ctx, testrun.Options{ return testrun.Execute(ctx, testrun.Options{
Spec: options.spec, Spec: options.spec,
BundleID: options.bundleID, BundleID: options.bundleID,
Platform: options.platform, Platform: options.platform,
AVD: options.avd, AVD: options.avd,
Device: options.device, Device: options.device,
IosDevice: options.iosDevice, IosDevice: options.iosDevice,
IosAppPath: options.iosAppPath, IosAppPath: options.iosAppPath,
Duration: options.duration, AndroidAppPath: options.androidAppPath,
Seed: options.seed, Duration: options.duration,
Output: options.output, Seed: options.seed,
ClearData: options.clearData, Output: options.output,
ClearData: options.clearData,
}, stdout) }, stdout)
} }
+1
View File
@@ -170,6 +170,7 @@ func buildDriver(ctx context.Context, options Options, stdout io.Writer) (driver
return nil, nil, fmt.Errorf("dial sidecar: %w", err) return nil, nil, fmt.Errorf("dial sidecar: %w", err)
} }
driverClient.SetPlatform(options.Platform) driverClient.SetPlatform(options.Platform)
driverClient.SetClearStateReinstall(options.Device, options.AndroidAppPath, stdout)
// WaitForHealth confirms the gRPC sidecar is up. For iOS, the WDA warmup // WaitForHealth confirms the gRPC sidecar is up. For iOS, the WDA warmup
// (absorbing the XCUITest startup race) runs inside IosDriverBackend.init // (absorbing the XCUITest startup race) runs inside IosDriverBackend.init
// in the sidecar - no additional sleep needed here. // in the sidecar - no additional sleep needed here.
+12 -11
View File
@@ -22,17 +22,18 @@ const sidecarStartupTimeout = 30 * time.Second
// 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
BundleID string BundleID string
Platform string Platform string
AVD string AVD string
Device string Device string
IosDevice string IosDevice string
IosAppPath string IosAppPath string
Duration time.Duration AndroidAppPath string
Seed int64 Duration time.Duration
Output string Seed int64
ClearData bool Output string
ClearData bool
// iosUDID, iosIsSimulator, and iosCoreDeviceID are filled by Execute after // iosUDID, iosIsSimulator, and iosCoreDeviceID are filled by Execute after
// resolving the iOS target, then read by buildDriver to choose the simulator // resolving the iOS target, then read by buildDriver to choose the simulator