DeviceWatcher events are more strongly typed and added higher-level methods.

This commit is contained in:
Zach Klippenstein 2015-09-07 21:19:59 -07:00
parent 0aa31c0548
commit af4b3ddcf2
3 changed files with 170 additions and 87 deletions

View file

@ -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})
}
}

View file

@ -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)
}

16
devicestate_string.go Normal file
View file

@ -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]]
}