mirror of
https://github.com/priyanshujain/sanderling.git
synced 2026-10-02 19:17:10 +00:00
feat(test): add --device flag to target a specific Android device by serial
This commit is contained in:
1 parent
da6612890f
commit
24be870896
6 files changed
+64
-5
No files matched your search
@@ -22,6 +22,7 @@ type testOptions struct {
|
||||
bundleID string
|
||||
platform string
|
||||
avd string
|
||||
device string
|
||||
iosDevice string
|
||||
iosAppPath string
|
||||
duration time.Duration
|
||||
@@ -52,6 +53,7 @@ func parseTestArgs(args []string, stderr io.Writer) (testOptions, error) {
|
||||
flagSet.StringVar(&options.bundleID, "bundle-id", "", "target app bundle ID (required)")
|
||||
flagSet.StringVar(&options.platform, "platform", "android", "target platform: android, ios, web")
|
||||
flagSet.StringVar(&options.avd, "avd", "", "Android AVD name to boot if no device is 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.iosAppPath, "ios-app-path", "", "path to the .app bundle for iOS clear-state reinstall (simulator: simctl; device: devicectl)")
|
||||
flagSet.DurationVar(&options.duration, "duration", 5*time.Minute, "total test duration")
|
||||
|
||||
@@ -13,6 +13,7 @@ func runTestPipeline(ctx context.Context, options testOptions, stdout io.Writer)
|
||||
BundleID: options.bundleID,
|
||||
Platform: options.platform,
|
||||
AVD: options.avd,
|
||||
Device: options.device,
|
||||
IosDevice: options.iosDevice,
|
||||
IosAppPath: options.iosAppPath,
|
||||
Duration: options.duration,
|
||||
|
||||
@@ -17,17 +17,22 @@ import (
|
||||
|
||||
// EnsureDevice makes sure an Android device is ready for adb commands.
|
||||
// Resolution order:
|
||||
// - if an adb device is already online, use it;
|
||||
// - if serial is set, require that exact device to be online;
|
||||
// - else if an adb device is already online, use it;
|
||||
// - else if avdName is set, validate and boot it;
|
||||
// - else if exactly one AVD exists locally, boot it;
|
||||
// - else fail with a helpful message listing the available AVDs.
|
||||
func EnsureDevice(ctx context.Context, avdName string, stdout io.Writer) error {
|
||||
func EnsureDevice(ctx context.Context, serial, avdName string, stdout io.Writer) error {
|
||||
devices, err := listAdbDevices(ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("list adb devices: %w", err)
|
||||
}
|
||||
if len(devices) > 0 {
|
||||
fmt.Fprintf(stdout, "using connected device: %s\n", devices[0])
|
||||
chosen, found, err := pickDevice(serial, devices)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if found {
|
||||
fmt.Fprintf(stdout, "using connected device: %s\n", chosen)
|
||||
return nil
|
||||
}
|
||||
avds, err := listAVDs(ctx)
|
||||
@@ -191,6 +196,22 @@ func parseAVDList(output string) []string {
|
||||
return avds
|
||||
}
|
||||
|
||||
// pickDevice resolves which connected device to drive. A requested serial must
|
||||
// be online; with no request the first connected device is used, else found is
|
||||
// false so the caller falls back to booting an AVD.
|
||||
func pickDevice(requested string, connected []string) (serial string, found bool, err error) {
|
||||
if requested != "" {
|
||||
if !slices.Contains(connected, requested) {
|
||||
return "", false, fmt.Errorf("device %q is not connected (online devices: %s)", requested, strings.Join(connected, ", "))
|
||||
}
|
||||
return requested, true, nil
|
||||
}
|
||||
if len(connected) > 0 {
|
||||
return connected[0], true, nil
|
||||
}
|
||||
return "", false, nil
|
||||
}
|
||||
|
||||
func pickAVD(requested string, available []string) (string, error) {
|
||||
if requested != "" {
|
||||
if !slices.Contains(available, requested) {
|
||||
|
||||
@@ -28,6 +28,37 @@ func TestParseAdbDevices_Empty(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestPickDevice_RequestedOnline(t *testing.T) {
|
||||
serial, found, err := pickDevice("physical-abc", []string{"emulator-5554", "physical-abc"})
|
||||
if err != nil || !found || serial != "physical-abc" {
|
||||
t.Fatalf("got (%q, %v, %v), want (physical-abc, true, nil)", serial, found, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPickDevice_RequestedNotConnected(t *testing.T) {
|
||||
_, found, err := pickDevice("physical-abc", []string{"emulator-5554"})
|
||||
if err == nil {
|
||||
t.Fatal("expected error for a serial that is not connected")
|
||||
}
|
||||
if found {
|
||||
t.Fatal("found must be false when the requested device is absent")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPickDevice_NoRequestUsesFirst(t *testing.T) {
|
||||
serial, found, err := pickDevice("", []string{"emulator-5554", "physical-abc"})
|
||||
if err != nil || !found || serial != "emulator-5554" {
|
||||
t.Fatalf("got (%q, %v, %v), want (emulator-5554, true, nil)", serial, found, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPickDevice_NoneConnectedFallsBackToAVD(t *testing.T) {
|
||||
serial, found, err := pickDevice("", nil)
|
||||
if err != nil || found || serial != "" {
|
||||
t.Fatalf("got (%q, %v, %v), want (\"\", false, nil)", serial, found, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseAVDList_DropsInfoLines(t *testing.T) {
|
||||
output := `INFO | Storing crashdata in: /tmp/x
|
||||
Medium_Phone_API_36.0
|
||||
|
||||
@@ -145,6 +145,9 @@ func buildDriver(ctx context.Context, options Options, stdout io.Writer) (driver
|
||||
"--port", strconv.Itoa(sidecarPort),
|
||||
"--platform", options.Platform,
|
||||
}
|
||||
if options.Device != "" {
|
||||
sidecarArgs = append(sidecarArgs, "--serial", options.Device)
|
||||
}
|
||||
sidecarCommand := exec.CommandContext(ctx, "java", sidecarArgs...)
|
||||
sidecarCommand.Stdout = stdout
|
||||
sidecarCommand.Stderr = stdout
|
||||
|
||||
@@ -26,6 +26,7 @@ type Options struct {
|
||||
BundleID string
|
||||
Platform string
|
||||
AVD string
|
||||
Device string
|
||||
IosDevice string
|
||||
IosAppPath string
|
||||
Duration time.Duration
|
||||
@@ -46,7 +47,7 @@ type Options struct {
|
||||
func Execute(ctx context.Context, options Options, stdout io.Writer) error {
|
||||
switch options.Platform {
|
||||
case "android":
|
||||
if err := android.EnsureDevice(ctx, options.AVD, stdout); err != nil {
|
||||
if err := android.EnsureDevice(ctx, options.Device, options.AVD, stdout); err != nil {
|
||||
return err
|
||||
}
|
||||
case "ios":
|
||||
|
||||
Reference in new issue
Block a user