From af4b3ddcf232050c58081a06518715b54933be6d Mon Sep 17 00:00:00 2001 From: Zach Klippenstein Date: Mon, 7 Sep 2015 21:19:59 -0700 Subject: [PATCH] DeviceWatcher events are more strongly typed and added higher-level methods. --- device_watcher.go | 56 ++++++++++--- device_watcher_test.go | 185 ++++++++++++++++++++++++----------------- devicestate_string.go | 16 ++++ 3 files changed, 170 insertions(+), 87 deletions(-) create mode 100644 devicestate_string.go diff --git a/device_watcher.go b/device_watcher.go index df2e0ad..0db05c6 100644 --- a/device_watcher.go +++ b/device_watcher.go @@ -18,10 +18,42 @@ type DeviceWatcher struct { *deviceWatcherImpl } +// DeviceStateChangedEvent represents a device state transition. type DeviceStateChangedEvent struct { Serial string - OldState string - NewState string + OldState DeviceState + NewState DeviceState +} + +// CameOnline returns true if this event represents a device coming online. +func (s DeviceStateChangedEvent) CameOnline() bool { + return s.OldState != StateOnline && s.NewState == StateOnline +} + +// WentOffline returns true if this event represents a device going offline. +func (s DeviceStateChangedEvent) WentOffline() bool { + return s.OldState == StateOnline && s.NewState != StateOnline +} + +// DeviceState represents one of the 3 possible states adb will report devices. +// A device can be communicated with when it's in StateOnline. +// A USB device will transition from StateDisconnected->StateOffline->StateOnline when +// plugged in, and then StateOnline->StateDisconnected when unplugged. +// If code doesn't care about specific states, DeviceStateChangedEvent provides methods +// to query at a higher level. +//go:generate stringer -type=DeviceState +type DeviceState int8 + +const ( + StateDisconnected DeviceState = iota + StateOffline + StateOnline +) + +var deviceStateStrings = map[string]DeviceState{ + "": StateDisconnected, + "offline": StateOffline, + "device": StateOnline, } type deviceWatcherImpl struct { @@ -96,7 +128,7 @@ and abort. If true, report no error and stop. func publishDevices(watcher *deviceWatcherImpl) { defer close(watcher.eventChan) - var lastKnownStates map[string]string + var lastKnownStates map[string]DeviceState finished := false for { @@ -148,7 +180,7 @@ func connectToTrackDevices(dialer Dialer) (wire.Scanner, error) { return conn, nil } -func publishDevicesUntilError(scanner wire.Scanner, eventChan chan<- DeviceStateChangedEvent, lastKnownStates *map[string]string) (finished bool, err error) { +func publishDevicesUntilError(scanner wire.Scanner, eventChan chan<- DeviceStateChangedEvent, lastKnownStates *map[string]DeviceState) (finished bool, err error) { for { msg, err := scanner.ReadMessage() if err != nil { @@ -167,8 +199,8 @@ func publishDevicesUntilError(scanner wire.Scanner, eventChan chan<- DeviceState } } -func parseDeviceStates(msg string) (states map[string]string, err error) { - states = make(map[string]string) +func parseDeviceStates(msg string) (states map[string]DeviceState, err error) { + states = make(map[string]DeviceState) for lineNum, line := range strings.Split(msg, "\n") { if len(line) == 0 { @@ -181,14 +213,18 @@ func parseDeviceStates(msg string) (states map[string]string, err error) { return } - serial, state := fields[0], fields[1] + serial, stateString := fields[0], fields[1] + state, ok := deviceStateStrings[stateString] + if !ok { + err = util.Errorf(util.ParseError, "invalid device state: %s", state) + } states[serial] = state } return } -func calculateStateDiffs(oldStates, newStates map[string]string) (events []DeviceStateChangedEvent) { +func calculateStateDiffs(oldStates, newStates map[string]DeviceState) (events []DeviceStateChangedEvent) { for serial, oldState := range oldStates { newState, ok := newStates[serial] @@ -198,7 +234,7 @@ func calculateStateDiffs(oldStates, newStates map[string]string) (events []Devic events = append(events, DeviceStateChangedEvent{serial, oldState, newState}) } else { // Device only present in old list: device removed. - events = append(events, DeviceStateChangedEvent{serial, oldState, ""}) + events = append(events, DeviceStateChangedEvent{serial, oldState, StateDisconnected}) } } } @@ -206,7 +242,7 @@ func calculateStateDiffs(oldStates, newStates map[string]string) (events []Devic for serial, newState := range newStates { if _, ok := oldStates[serial]; !ok { // Device only present in new list: device added. - events = append(events, DeviceStateChangedEvent{serial, "", newState}) + events = append(events, DeviceStateChangedEvent{serial, StateDisconnected, newState}) } } diff --git a/device_watcher_test.go b/device_watcher_test.go index f52485b..df58595 100644 --- a/device_watcher_test.go +++ b/device_watcher_test.go @@ -2,7 +2,6 @@ package goadb import ( "log" - "reflect" "testing" "github.com/stretchr/testify/assert" @@ -11,27 +10,27 @@ import ( ) func TestParseDeviceStatesSingle(t *testing.T) { - states, err := parseDeviceStates(`192.168.56.101:5555 emulator-state + states, err := parseDeviceStates(`192.168.56.101:5555 offline `) assert.NoError(t, err) assert.Len(t, states, 1) - assert.Equal(t, "emulator-state", states["192.168.56.101:5555"]) + assert.Equal(t, StateOffline, states["192.168.56.101:5555"]) } func TestParseDeviceStatesMultiple(t *testing.T) { - states, err := parseDeviceStates(`192.168.56.101:5555 emulator-state -0x0x0x0x usb-state + states, err := parseDeviceStates(`192.168.56.101:5555 offline +0x0x0x0x device `) assert.NoError(t, err) assert.Len(t, states, 2) - assert.Equal(t, "emulator-state", states["192.168.56.101:5555"]) - assert.Equal(t, "usb-state", states["0x0x0x0x"]) + assert.Equal(t, StateOffline, states["192.168.56.101:5555"]) + assert.Equal(t, StateOnline, states["0x0x0x0x"]) } func TestParseDeviceStatesMalformed(t *testing.T) { - _, err := parseDeviceStates(`192.168.56.101:5555 emulator-state + _, err := parseDeviceStates(`192.168.56.101:5555 offline 0x0x0x0x `) @@ -40,8 +39,8 @@ func TestParseDeviceStatesMalformed(t *testing.T) { } func TestCalculateStateDiffsUnchangedEmpty(t *testing.T) { - oldStates := map[string]string{} - newStates := map[string]string{} + oldStates := map[string]DeviceState{} + newStates := map[string]DeviceState{} diffs := calculateStateDiffs(oldStates, newStates) @@ -49,13 +48,13 @@ func TestCalculateStateDiffsUnchangedEmpty(t *testing.T) { } func TestCalculateStateDiffsUnchangedNonEmpty(t *testing.T) { - oldStates := map[string]string{ - "1": "device", - "2": "device", + oldStates := map[string]DeviceState{ + "1": StateOnline, + "2": StateOnline, } - newStates := map[string]string{ - "1": "device", - "2": "device", + newStates := map[string]DeviceState{ + "1": StateOnline, + "2": StateOnline, } diffs := calculateStateDiffs(oldStates, newStates) @@ -64,131 +63,147 @@ func TestCalculateStateDiffsUnchangedNonEmpty(t *testing.T) { } func TestCalculateStateDiffsOneAdded(t *testing.T) { - oldStates := map[string]string{} - newStates := map[string]string{ - "serial": "added", + oldStates := map[string]DeviceState{} + newStates := map[string]DeviceState{ + "serial": StateOffline, } diffs := calculateStateDiffs(oldStates, newStates) - assert.Equal(t, []DeviceStateChangedEvent{ - DeviceStateChangedEvent{"serial", "", "added"}, + assertContainsOnly(t, []DeviceStateChangedEvent{ + DeviceStateChangedEvent{"serial", StateDisconnected, StateOffline}, }, diffs) } func TestCalculateStateDiffsOneRemoved(t *testing.T) { - oldStates := map[string]string{ - "serial": "removed", + oldStates := map[string]DeviceState{ + "serial": StateOffline, } - newStates := map[string]string{} + newStates := map[string]DeviceState{} diffs := calculateStateDiffs(oldStates, newStates) - assert.Equal(t, []DeviceStateChangedEvent{ - DeviceStateChangedEvent{"serial", "removed", ""}, + assertContainsOnly(t, []DeviceStateChangedEvent{ + DeviceStateChangedEvent{"serial", StateOffline, StateDisconnected}, }, diffs) } func TestCalculateStateDiffsOneAddedOneUnchanged(t *testing.T) { - oldStates := map[string]string{ - "1": "device", + oldStates := map[string]DeviceState{ + "1": StateOnline, } - newStates := map[string]string{ - "1": "device", - "2": "added", + newStates := map[string]DeviceState{ + "1": StateOnline, + "2": StateOffline, } diffs := calculateStateDiffs(oldStates, newStates) - assert.Equal(t, []DeviceStateChangedEvent{ - DeviceStateChangedEvent{"2", "", "added"}, + assertContainsOnly(t, []DeviceStateChangedEvent{ + DeviceStateChangedEvent{"2", StateDisconnected, StateOffline}, }, diffs) } func TestCalculateStateDiffsOneRemovedOneUnchanged(t *testing.T) { - oldStates := map[string]string{ - "1": "removed", - "2": "device", + oldStates := map[string]DeviceState{ + "1": StateOffline, + "2": StateOnline, } - newStates := map[string]string{ - "2": "device", + newStates := map[string]DeviceState{ + "2": StateOnline, } diffs := calculateStateDiffs(oldStates, newStates) - assert.Equal(t, []DeviceStateChangedEvent{ - DeviceStateChangedEvent{"1", "removed", ""}, + assertContainsOnly(t, []DeviceStateChangedEvent{ + DeviceStateChangedEvent{"1", StateOffline, StateDisconnected}, }, diffs) } func TestCalculateStateDiffsOneAddedOneRemoved(t *testing.T) { - oldStates := map[string]string{ - "1": "removed", + oldStates := map[string]DeviceState{ + "1": StateOffline, } - newStates := map[string]string{ - "2": "added", + newStates := map[string]DeviceState{ + "2": StateOffline, } diffs := calculateStateDiffs(oldStates, newStates) - assert.Equal(t, []DeviceStateChangedEvent{ - DeviceStateChangedEvent{"1", "removed", ""}, - DeviceStateChangedEvent{"2", "", "added"}, + assertContainsOnly(t, []DeviceStateChangedEvent{ + DeviceStateChangedEvent{"1", StateOffline, StateDisconnected}, + DeviceStateChangedEvent{"2", StateDisconnected, StateOffline}, }, diffs) } func TestCalculateStateDiffsOneChangedOneUnchanged(t *testing.T) { - oldStates := map[string]string{ - "1": "oldState", - "2": "device", + oldStates := map[string]DeviceState{ + "1": StateOffline, + "2": StateOnline, } - newStates := map[string]string{ - "1": "newState", - "2": "device", + newStates := map[string]DeviceState{ + "1": StateOnline, + "2": StateOnline, } diffs := calculateStateDiffs(oldStates, newStates) - assert.Equal(t, []DeviceStateChangedEvent{ - DeviceStateChangedEvent{"1", "oldState", "newState"}, + assertContainsOnly(t, []DeviceStateChangedEvent{ + DeviceStateChangedEvent{"1", StateOffline, StateOnline}, }, diffs) } -func TestCalculateStateDiffsMultipleChangedMultipleUnchanged(t *testing.T) { - oldStates := map[string]string{ - "1": "oldState", - "2": "oldState", +func TestCalculateStateDiffsMultipleChanged(t *testing.T) { + oldStates := map[string]DeviceState{ + "1": StateOffline, + "2": StateOnline, } - newStates := map[string]string{ - "1": "newState", - "2": "newState", + newStates := map[string]DeviceState{ + "1": StateOnline, + "2": StateOffline, } diffs := calculateStateDiffs(oldStates, newStates) - assert.True(t, reflect.DeepEqual([]DeviceStateChangedEvent{ - DeviceStateChangedEvent{"1", "oldState", "newState"}, - DeviceStateChangedEvent{"2", "oldState", "newState"}, - }, diffs)) + assertContainsOnly(t, []DeviceStateChangedEvent{ + DeviceStateChangedEvent{"1", StateOffline, StateOnline}, + DeviceStateChangedEvent{"2", StateOnline, StateOffline}, + }, diffs) } func TestCalculateStateDiffsOneAddedOneRemovedOneChanged(t *testing.T) { - oldStates := map[string]string{ - "1": "oldState", - "2": "removed", + oldStates := map[string]DeviceState{ + "1": StateOffline, + "2": StateOffline, } - newStates := map[string]string{ - "1": "newState", - "3": "added", + newStates := map[string]DeviceState{ + "1": StateOnline, + "3": StateOffline, } diffs := calculateStateDiffs(oldStates, newStates) - assert.True(t, reflect.DeepEqual([]DeviceStateChangedEvent{ - DeviceStateChangedEvent{"1", "oldState", "newState"}, - DeviceStateChangedEvent{"2", "removed", ""}, - DeviceStateChangedEvent{"3", "", "added"}, - }, diffs)) + assertContainsOnly(t, []DeviceStateChangedEvent{ + DeviceStateChangedEvent{"1", StateOffline, StateOnline}, + DeviceStateChangedEvent{"2", StateOffline, StateDisconnected}, + DeviceStateChangedEvent{"3", StateDisconnected, StateOffline}, + }, diffs) +} + +func TestCameOnline(t *testing.T) { + assert.True(t, DeviceStateChangedEvent{"", StateDisconnected, StateOnline}.CameOnline()) + assert.True(t, DeviceStateChangedEvent{"", StateOffline, StateOnline}.CameOnline()) + assert.False(t, DeviceStateChangedEvent{"", StateOnline, StateOffline}.CameOnline()) + assert.False(t, DeviceStateChangedEvent{"", StateOnline, StateDisconnected}.CameOnline()) + assert.False(t, DeviceStateChangedEvent{"", StateOffline, StateDisconnected}.CameOnline()) +} + +func TestWentOffline(t *testing.T) { + assert.True(t, DeviceStateChangedEvent{"", StateOnline, StateDisconnected}.WentOffline()) + assert.True(t, DeviceStateChangedEvent{"", StateOnline, StateOffline}.WentOffline()) + assert.False(t, DeviceStateChangedEvent{"", StateOffline, StateOnline}.WentOffline()) + assert.False(t, DeviceStateChangedEvent{"", StateDisconnected, StateOnline}.WentOffline()) + assert.False(t, DeviceStateChangedEvent{"", StateOffline, StateDisconnected}.WentOffline()) } func TestPublishDevicesRestartsServer(t *testing.T) { @@ -231,3 +246,19 @@ func (s *MockServerStarter) StartServer() error { return s.err } } + +func assertContainsOnly(t *testing.T, expected, actual []DeviceStateChangedEvent) { + assert.Len(t, actual, len(expected)) + for _, expectedEntry := range expected { + assertContains(t, expectedEntry, actual) + } +} + +func assertContains(t *testing.T, expectedEntry DeviceStateChangedEvent, actual []DeviceStateChangedEvent) { + for _, actualEntry := range actual { + if expectedEntry == actualEntry { + return + } + } + assert.Fail(t, "expected to find %+v in %+v", expectedEntry, actual) +} diff --git a/devicestate_string.go b/devicestate_string.go new file mode 100644 index 0000000..8164dce --- /dev/null +++ b/devicestate_string.go @@ -0,0 +1,16 @@ +// generated by stringer -type=DeviceState; DO NOT EDIT + +package goadb + +import "fmt" + +const _DeviceState_name = "StateDisconnectedStateOfflineStateOnline" + +var _DeviceState_index = [...]uint8{0, 17, 29, 40} + +func (i DeviceState) String() string { + if i < 0 || i+1 >= DeviceState(len(_DeviceState_index)) { + return fmt.Sprintf("DeviceState(%d)", i) + } + return _DeviceState_name[_DeviceState_index[i]:_DeviceState_index[i+1]] +}