Skip to content

Commit

Permalink
Split ADB-related code into own package (#55)
Browse files Browse the repository at this point in the history
* Split ADB-related code into own package

* Add tests
  • Loading branch information
ofalvai authored Nov 29, 2024
1 parent 9e5f0e3 commit 6311920
Show file tree
Hide file tree
Showing 5 changed files with 245 additions and 144 deletions.
102 changes: 102 additions & 0 deletions adb/adb.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
package adb

import (
"bufio"
"fmt"
"path/filepath"
"regexp"
"strings"

"github.com/bitrise-io/go-utils/v2/command"
"github.com/bitrise-io/go-utils/v2/log"
)

type ADB struct {
androidHome string
cmdFactory command.Factory
logger log.Logger
}

func New(androidHome string, cmdFactory command.Factory, logger log.Logger) ADB {
return ADB{
androidHome: androidHome,
cmdFactory: cmdFactory,
logger: logger,
}
}

const DeviceStateConnected = "device"

// Key: device serial number
// Value: device state
type Devices map[string]string

// Devices returns a map of connected Android devices and their states.
func (a *ADB) Devices() (Devices, error) {
cmd := a.cmdFactory.Create(
filepath.Join(a.androidHome, "platform-tools", "adb"),
[]string{"devices"},
nil,
)
out, err := cmd.RunAndReturnTrimmedCombinedOutput()
if err != nil {
a.logger.Printf(out)
return map[string]string{}, fmt.Errorf("adb devices: %s", err)
}

a.logger.Debugf("$ %s", cmd.PrintableCommandArgs())
a.logger.Debugf("%s", out)

// List of devices attached
// emulator-5554 device
deviceListItemPattern := `^(?P<emulator>emulator-\d*)[\s+](?P<state>.*)`
deviceListItemRegexp := regexp.MustCompile(deviceListItemPattern)

deviceStateMap := map[string]string{}

scanner := bufio.NewScanner(strings.NewReader(out))
for scanner.Scan() {
line := scanner.Text()
matches := deviceListItemRegexp.FindStringSubmatch(line)
if len(matches) == 3 {
serial := matches[1]
state := matches[2]

deviceStateMap[serial] = state
}

}
if scanner.Err() != nil {
return map[string]string{}, fmt.Errorf("scan adb devices output: %s", err)
}

return deviceStateMap, nil
}

// FindNewDevice returns the serial number of a newly connected device compared
// to the previous state of running devices.
// If no new device is found, an empty string is returned.
func (a *ADB) FindNewDevice(previousDeviceState Devices) (string, error) {
devicesNow, err := a.Devices()
if err != nil {
return "", err
}

newDeviceSerial := ""
for serial := range devicesNow {
_, found := previousDeviceState[serial]
if !found {
newDeviceSerial = serial
break
}
}

if len(newDeviceSerial) > 0 {
state := devicesNow[newDeviceSerial]
if state == DeviceStateConnected {
return newDeviceSerial, nil
}
}

return "", nil
}
61 changes: 61 additions & 0 deletions adb/adb_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package adb

import (
"testing"

"github.com/bitrise-io/go-utils/v2/log"
"github.com/bitrise-steplib/steps-avd-manager/test"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestFindNewDevice(t *testing.T) {
androidHome := "/fake/android/home"
logger := log.NewLogger()

tests := []struct {
name string
previousDevices Devices
adbOutput string
expectedSerial string
}{
{
name: "no new device",
previousDevices: Devices{
"emulator-5554": "device",
},
adbOutput: "List of devices attached\nemulator-5554\tdevice\n",
expectedSerial: "",
},
{
name: "new device connected",
previousDevices: Devices{
"emulator-5554": "device",
},
adbOutput: "List of devices attached\nemulator-5554\tdevice\nemulator-5556\tdevice\n",
expectedSerial: "emulator-5556",
},
{
name: "new device not connected",
previousDevices: Devices{
"emulator-5554": "device",
},
adbOutput: "List of devices attached\nemulator-5554\tdevice\nemulator-5556\toffline\n",
expectedSerial: "",
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
cmdFactory := test.FakeCommandFactory{
Stdout: tt.adbOutput,
ExitCode: 0,
}
adb := New(androidHome, cmdFactory, logger)

newDevice, err := adb.FindNewDevice(tt.previousDevices)
require.NoError(t, err)
assert.Equal(t, tt.expectedSerial, newDevice)
})
}
}
69 changes: 4 additions & 65 deletions emuinstaller/install_test.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
package emuinstaller

import (
"fmt"
"os"
"path/filepath"
"strings"
"testing"

"github.com/bitrise-steplib/steps-avd-manager/test"
"github.com/bitrise-io/go-utils/v2/command"
"github.com/bitrise-io/go-utils/v2/env"
"github.com/stretchr/testify/require"
Expand Down Expand Up @@ -75,9 +74,9 @@ This program is a derivative of the QEMU CPU emulator (www.qemu.org).

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
cmdFactory := fakeCommandFactory{
stdout: tt.versionOutput,
exitCode: 0,
cmdFactory := test.FakeCommandFactory{
Stdout: tt.versionOutput,
ExitCode: 0,
}

installer := EmuInstaller{
Expand Down Expand Up @@ -155,63 +154,3 @@ func TestBackupEmuDir(t *testing.T) {
})
}
}

type fakeCommandFactory struct {
stdout string
exitCode int
}

func (f fakeCommandFactory) Create(name string, args []string, _ *command.Opts) command.Command {
return fakeCommand{
command: fmt.Sprintf("%s %s", name, strings.Join(args, " ")),
stdout: f.stdout,
exitCode: f.exitCode,
}
}

type fakeCommand struct {
command string
stdout string
stderr string
exitCode int
}

func (c fakeCommand) PrintableCommandArgs() string {
return c.command
}

func (c fakeCommand) Run() error {
if c.exitCode != 0 {
return fmt.Errorf("exit code %d", c.exitCode)
}
return nil
}

func (c fakeCommand) RunAndReturnExitCode() (int, error) {
if c.exitCode != 0 {
return c.exitCode, fmt.Errorf("exit code %d", c.exitCode)
}
return c.exitCode, nil
}

func (c fakeCommand) RunAndReturnTrimmedOutput() (string, error) {
if c.exitCode != 0 {
return "", fmt.Errorf("exit code %d", c.exitCode)
}
return c.stdout, nil
}

func (c fakeCommand) RunAndReturnTrimmedCombinedOutput() (string, error) {
if c.exitCode != 0 {
return "", fmt.Errorf("exit code %d", c.exitCode)
}
return fmt.Sprintf("%s%s", c.stdout, c.stderr), nil
}

func (c fakeCommand) Start() error {
return nil
}

func (c fakeCommand) Wait() error {
return nil
}
Loading

0 comments on commit 6311920

Please sign in to comment.