From 747c4abb7ff395ad473577c7b7cc93290a031fea Mon Sep 17 00:00:00 2001 From: Tai Groot Date: Wed, 8 Apr 2026 10:38:56 +0000 Subject: [PATCH] fix(errors): implement filterErr with common adb stderr patterns - Implement filterErr to detect device-not-found, offline, unauthorized, connection-refused, and multiple-device errors from stderr output - Add ErrDeviceNotFound, ErrDeviceOffline, ErrDeviceUnauthorized, ErrConnectionRefused, ErrMoreThanOneDevice sentinel errors - Remove log.Printf in CaptureSequence (was marked TODO for removal) - Fix ConnString to use local variable instead of mutating value receiver - Update go directive to 1.26.2 - Update README: check implemented features, fix intro typo, add root/keyevent/getevent to supported functions list - Expand filterErr tests for all new error patterns --- README.md | 28 ++++++++++++++++------------ adb.go | 7 ++++--- capture.go | 6 +----- errors.go | 10 ++++++++++ go.mod | 2 +- util.go | 20 ++++++++++++++++++-- util_test.go | 14 ++++++++++---- 7 files changed, 60 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index c16e8e9..17032a0 100644 --- a/README.md +++ b/README.md @@ -4,8 +4,8 @@ This library aims at providing idiomatic `adb` bindings for go developers, in order to make it easier to write system tooling using golang. This tool tries to take guesswork out of arbitrarily shelling out to `adb` by providing a structured, thoroughly-tested wrapper for the `adb` functions most-likely to be used in a system program. -If `adb` must be installed in your path `PATH`. At this time, while this library may work on Windows or MacOS, only Linux is supported. -If you would like to add support for Windows, MacOS, *BSD..., please [Submit a Pull Request](https://github.com/taigrr/adb/pulls). +`adb` must be installed and available in your `PATH`. At this time, while this library may work on Windows or macOS, only Linux is supported. +If you would like to add support for Windows, macOS, *BSD, etc., please [Submit a Pull Request](https://github.com/taigrr/adb/pulls). ## What is adb @@ -14,17 +14,21 @@ If you would like to add support for Windows, MacOS, *BSD..., please [Submit a P ## Supported adb functions -- [ ] `adb connect` -- [ ] `adb disconnect` -- [ ] `adb shell s` -- [ ] `adb kill-server` -- [ ] `adb device` -- [ ] `adb pull` +- [x] `adb connect` +- [x] `adb disconnect` +- [x] `adb shell ` +- [x] `adb kill-server` +- [x] `adb devices` +- [x] `adb pull` - [ ] `adb install` -- [ ] `adb push` -- [ ] `adb reboot` -- [ ] `adb shell input tap X Y` -- [ ] `adb shell input swipe X1 Y1 X2 Y2 duration` +- [x] `adb push` +- [x] `adb reboot` +- [x] `adb root` +- [x] `adb shell input tap X Y` +- [x] `adb shell input swipe X1 Y1 X2 Y2 duration` +- [x] `adb shell input keyevent` (home, back, app switch) +- [x] `adb shell wm size` (screen resolution) +- [x] `adb shell getevent` (capture and replay tap sequences) Please note that as this is a purpose-driven project library, not all functionality of ADB is currently supported, but if you need functionality that's not currently supported, Feel free to [Open an Issue](https://github.com/taigrr/adb/issues) or [Submit a Pull Request](https://github.com/taigrr/adb/pulls) diff --git a/adb.go b/adb.go index 3b80b1f..a7d8136 100644 --- a/adb.go +++ b/adb.go @@ -51,10 +51,11 @@ func Connect(ctx context.Context, opts ConnOptions) (Device, error) { } func (d Device) ConnString() string { - if d.Port == 0 { - d.Port = 5555 + port := d.Port + if port == 0 { + port = 5555 } - return d.IP.String() + ":" + strconv.Itoa(int(d.Port)) + return d.IP.String() + ":" + strconv.Itoa(int(port)) } // Connect to a previously discovered device. diff --git a/capture.go b/capture.go index a0da9db..cf773d8 100644 --- a/capture.go +++ b/capture.go @@ -4,7 +4,6 @@ import ( "context" "encoding/json" "errors" - "log" "regexp" "strconv" "strings" @@ -236,10 +235,7 @@ func (d Device) CaptureSequence(ctx context.Context) (t TapSequence, err error) if errors.Is(err, ErrUnspecified) { err = nil } - if errCode != 130 && errCode != -1 && errCode != 1 { - // TODO remove log output here - log.Printf("Expected error code 130 or -1, but got %d\n", errCode) - } + _ = errCode if stdout == "" { return TapSequence{}, ErrStdoutEmpty diff --git a/errors.go b/errors.go index df377d9..7776620 100644 --- a/errors.go +++ b/errors.go @@ -17,6 +17,16 @@ var ( ErrResolutionParseFail = errors.New("failed to parse screen size from input text") // ErrDestExists is returned when a pull destination file already exists. ErrDestExists = errors.New("destination file already exists") + // ErrDeviceNotFound is returned when no device is connected or the target device cannot be found. + ErrDeviceNotFound = errors.New("device not found") + // ErrDeviceOffline is returned when the target device is offline. + ErrDeviceOffline = errors.New("device offline") + // ErrDeviceUnauthorized is returned when the device has not authorized USB debugging. + ErrDeviceUnauthorized = errors.New("device unauthorized; check the confirmation dialog on the device") + // ErrConnectionRefused is returned when the connection to a device is refused. + ErrConnectionRefused = errors.New("connection refused") + // ErrMoreThanOneDevice is returned when multiple devices are connected and no serial is specified. + ErrMoreThanOneDevice = errors.New("more than one device/emulator; use -s to specify a device") // ErrUnspecified is returned when the exact error cannot be determined. ErrUnspecified = errors.New("an unknown error has occurred, please open an issue on GitHub") ) diff --git a/go.mod b/go.mod index b3a0646..81f0c84 100644 --- a/go.mod +++ b/go.mod @@ -1,5 +1,5 @@ module github.com/taigrr/adb -go 1.26.1 +go 1.26.2 require github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 diff --git a/util.go b/util.go index 2d0da1d..e863360 100644 --- a/util.go +++ b/util.go @@ -5,6 +5,7 @@ import ( "context" "fmt" "os/exec" + "strings" "sync" ) @@ -56,7 +57,22 @@ func execute(ctx context.Context, args []string) (string, string, int, error) { // filterErr matches known output strings against the stderr. // // The inferred error type is then returned. -// TODO: implement func filterErr(stderr string) error { - return nil + if stderr == "" { + return nil + } + switch { + case strings.Contains(stderr, "device not found"): + return ErrDeviceNotFound + case strings.Contains(stderr, "device offline"): + return ErrDeviceOffline + case strings.Contains(stderr, "device unauthorized"): + return ErrDeviceUnauthorized + case strings.Contains(stderr, "Connection refused"): + return ErrConnectionRefused + case strings.Contains(stderr, "more than one device"): + return ErrMoreThanOneDevice + default: + return nil + } } diff --git a/util_test.go b/util_test.go index 43fb30c..76e5396 100644 --- a/util_test.go +++ b/util_test.go @@ -1,6 +1,7 @@ package adb import ( + "errors" "testing" ) @@ -8,15 +9,20 @@ func Test_filterErr(t *testing.T) { tests := []struct { name string stderr string - wantErr bool + wantErr error }{ - {name: "empty stderr", stderr: "", wantErr: false}, - {name: "random output", stderr: "some warning text", wantErr: false}, + {name: "empty stderr", stderr: "", wantErr: nil}, + {name: "random output", stderr: "some warning text", wantErr: nil}, + {name: "device not found", stderr: "error: device not found", wantErr: ErrDeviceNotFound}, + {name: "device offline", stderr: "error: device offline", wantErr: ErrDeviceOffline}, + {name: "device unauthorized", stderr: "error: device unauthorized.\nThis adb server's $ADB_VENDOR_KEYS is not set", wantErr: ErrDeviceUnauthorized}, + {name: "connection refused", stderr: "cannot connect to daemon at tcp:5037: Connection refused", wantErr: ErrConnectionRefused}, + {name: "more than one device", stderr: "error: more than one device/emulator", wantErr: ErrMoreThanOneDevice}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { err := filterErr(tt.stderr) - if (err != nil) != tt.wantErr { + if !errors.Is(err, tt.wantErr) { t.Errorf("filterErr() error = %v, wantErr %v", err, tt.wantErr) } })