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 *deviceWatcherImpl
} }
// DeviceStateChangedEvent represents a device state transition.
type DeviceStateChangedEvent struct { type DeviceStateChangedEvent struct {
Serial string Serial string
OldState string OldState DeviceState
NewState string 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 { type deviceWatcherImpl struct {
@ -96,7 +128,7 @@ and abort. If true, report no error and stop.
func publishDevices(watcher *deviceWatcherImpl) { func publishDevices(watcher *deviceWatcherImpl) {
defer close(watcher.eventChan) defer close(watcher.eventChan)
var lastKnownStates map[string]string var lastKnownStates map[string]DeviceState
finished := false finished := false
for { for {
@ -148,7 +180,7 @@ func connectToTrackDevices(dialer Dialer) (wire.Scanner, error) {
return conn, nil 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 { for {
msg, err := scanner.ReadMessage() msg, err := scanner.ReadMessage()
if err != nil { 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) { func parseDeviceStates(msg string) (states map[string]DeviceState, err error) {
states = make(map[string]string) states = make(map[string]DeviceState)
for lineNum, line := range strings.Split(msg, "\n") { for lineNum, line := range strings.Split(msg, "\n") {
if len(line) == 0 { if len(line) == 0 {
@ -181,14 +213,18 @@ func parseDeviceStates(msg string) (states map[string]string, err error) {
return 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 states[serial] = state
} }
return return
} }
func calculateStateDiffs(oldStates, newStates map[string]string) (events []DeviceStateChangedEvent) { func calculateStateDiffs(oldStates, newStates map[string]DeviceState) (events []DeviceStateChangedEvent) {
for serial, oldState := range oldStates { for serial, oldState := range oldStates {
newState, ok := newStates[serial] newState, ok := newStates[serial]
@ -198,7 +234,7 @@ func calculateStateDiffs(oldStates, newStates map[string]string) (events []Devic
events = append(events, DeviceStateChangedEvent{serial, oldState, newState}) events = append(events, DeviceStateChangedEvent{serial, oldState, newState})
} else { } else {
// Device only present in old list: device removed. // 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 { for serial, newState := range newStates {
if _, ok := oldStates[serial]; !ok { if _, ok := oldStates[serial]; !ok {
// Device only present in new list: device added. // 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 ( import (
"log" "log"
"reflect"
"testing" "testing"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
@ -11,27 +10,27 @@ import (
) )
func TestParseDeviceStatesSingle(t *testing.T) { 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.NoError(t, err)
assert.Len(t, states, 1) 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) { func TestParseDeviceStatesMultiple(t *testing.T) {
states, err := parseDeviceStates(`192.168.56.101:5555 emulator-state states, err := parseDeviceStates(`192.168.56.101:5555 offline
0x0x0x0x usb-state 0x0x0x0x device
`) `)
assert.NoError(t, err) assert.NoError(t, err)
assert.Len(t, states, 2) assert.Len(t, states, 2)
assert.Equal(t, "emulator-state", states["192.168.56.101:5555"]) assert.Equal(t, StateOffline, states["192.168.56.101:5555"])
assert.Equal(t, "usb-state", states["0x0x0x0x"]) assert.Equal(t, StateOnline, states["0x0x0x0x"])
} }
func TestParseDeviceStatesMalformed(t *testing.T) { func TestParseDeviceStatesMalformed(t *testing.T) {
_, err := parseDeviceStates(`192.168.56.101:5555 emulator-state _, err := parseDeviceStates(`192.168.56.101:5555 offline
0x0x0x0x 0x0x0x0x
`) `)
@ -40,8 +39,8 @@ func TestParseDeviceStatesMalformed(t *testing.T) {
} }
func TestCalculateStateDiffsUnchangedEmpty(t *testing.T) { func TestCalculateStateDiffsUnchangedEmpty(t *testing.T) {
oldStates := map[string]string{} oldStates := map[string]DeviceState{}
newStates := map[string]string{} newStates := map[string]DeviceState{}
diffs := calculateStateDiffs(oldStates, newStates) diffs := calculateStateDiffs(oldStates, newStates)
@ -49,13 +48,13 @@ func TestCalculateStateDiffsUnchangedEmpty(t *testing.T) {
} }
func TestCalculateStateDiffsUnchangedNonEmpty(t *testing.T) { func TestCalculateStateDiffsUnchangedNonEmpty(t *testing.T) {
oldStates := map[string]string{ oldStates := map[string]DeviceState{
"1": "device", "1": StateOnline,
"2": "device", "2": StateOnline,
} }
newStates := map[string]string{ newStates := map[string]DeviceState{
"1": "device", "1": StateOnline,
"2": "device", "2": StateOnline,
} }
diffs := calculateStateDiffs(oldStates, newStates) diffs := calculateStateDiffs(oldStates, newStates)
@ -64,131 +63,147 @@ func TestCalculateStateDiffsUnchangedNonEmpty(t *testing.T) {
} }
func TestCalculateStateDiffsOneAdded(t *testing.T) { func TestCalculateStateDiffsOneAdded(t *testing.T) {
oldStates := map[string]string{} oldStates := map[string]DeviceState{}
newStates := map[string]string{ newStates := map[string]DeviceState{
"serial": "added", "serial": StateOffline,
} }
diffs := calculateStateDiffs(oldStates, newStates) diffs := calculateStateDiffs(oldStates, newStates)
assert.Equal(t, []DeviceStateChangedEvent{ assertContainsOnly(t, []DeviceStateChangedEvent{
DeviceStateChangedEvent{"serial", "", "added"}, DeviceStateChangedEvent{"serial", StateDisconnected, StateOffline},
}, diffs) }, diffs)
} }
func TestCalculateStateDiffsOneRemoved(t *testing.T) { func TestCalculateStateDiffsOneRemoved(t *testing.T) {
oldStates := map[string]string{ oldStates := map[string]DeviceState{
"serial": "removed", "serial": StateOffline,
} }
newStates := map[string]string{} newStates := map[string]DeviceState{}
diffs := calculateStateDiffs(oldStates, newStates) diffs := calculateStateDiffs(oldStates, newStates)
assert.Equal(t, []DeviceStateChangedEvent{ assertContainsOnly(t, []DeviceStateChangedEvent{
DeviceStateChangedEvent{"serial", "removed", ""}, DeviceStateChangedEvent{"serial", StateOffline, StateDisconnected},
}, diffs) }, diffs)
} }
func TestCalculateStateDiffsOneAddedOneUnchanged(t *testing.T) { func TestCalculateStateDiffsOneAddedOneUnchanged(t *testing.T) {
oldStates := map[string]string{ oldStates := map[string]DeviceState{
"1": "device", "1": StateOnline,
} }
newStates := map[string]string{ newStates := map[string]DeviceState{
"1": "device", "1": StateOnline,
"2": "added", "2": StateOffline,
} }
diffs := calculateStateDiffs(oldStates, newStates) diffs := calculateStateDiffs(oldStates, newStates)
assert.Equal(t, []DeviceStateChangedEvent{ assertContainsOnly(t, []DeviceStateChangedEvent{
DeviceStateChangedEvent{"2", "", "added"}, DeviceStateChangedEvent{"2", StateDisconnected, StateOffline},
}, diffs) }, diffs)
} }
func TestCalculateStateDiffsOneRemovedOneUnchanged(t *testing.T) { func TestCalculateStateDiffsOneRemovedOneUnchanged(t *testing.T) {
oldStates := map[string]string{ oldStates := map[string]DeviceState{
"1": "removed", "1": StateOffline,
"2": "device", "2": StateOnline,
} }
newStates := map[string]string{ newStates := map[string]DeviceState{
"2": "device", "2": StateOnline,
} }
diffs := calculateStateDiffs(oldStates, newStates) diffs := calculateStateDiffs(oldStates, newStates)
assert.Equal(t, []DeviceStateChangedEvent{ assertContainsOnly(t, []DeviceStateChangedEvent{
DeviceStateChangedEvent{"1", "removed", ""}, DeviceStateChangedEvent{"1", StateOffline, StateDisconnected},
}, diffs) }, diffs)
} }
func TestCalculateStateDiffsOneAddedOneRemoved(t *testing.T) { func TestCalculateStateDiffsOneAddedOneRemoved(t *testing.T) {
oldStates := map[string]string{ oldStates := map[string]DeviceState{
"1": "removed", "1": StateOffline,
} }
newStates := map[string]string{ newStates := map[string]DeviceState{
"2": "added", "2": StateOffline,
} }
diffs := calculateStateDiffs(oldStates, newStates) diffs := calculateStateDiffs(oldStates, newStates)
assert.Equal(t, []DeviceStateChangedEvent{ assertContainsOnly(t, []DeviceStateChangedEvent{
DeviceStateChangedEvent{"1", "removed", ""}, DeviceStateChangedEvent{"1", StateOffline, StateDisconnected},
DeviceStateChangedEvent{"2", "", "added"}, DeviceStateChangedEvent{"2", StateDisconnected, StateOffline},
}, diffs) }, diffs)
} }
func TestCalculateStateDiffsOneChangedOneUnchanged(t *testing.T) { func TestCalculateStateDiffsOneChangedOneUnchanged(t *testing.T) {
oldStates := map[string]string{ oldStates := map[string]DeviceState{
"1": "oldState", "1": StateOffline,
"2": "device", "2": StateOnline,
} }
newStates := map[string]string{ newStates := map[string]DeviceState{
"1": "newState", "1": StateOnline,
"2": "device", "2": StateOnline,
} }
diffs := calculateStateDiffs(oldStates, newStates) diffs := calculateStateDiffs(oldStates, newStates)
assert.Equal(t, []DeviceStateChangedEvent{ assertContainsOnly(t, []DeviceStateChangedEvent{
DeviceStateChangedEvent{"1", "oldState", "newState"}, DeviceStateChangedEvent{"1", StateOffline, StateOnline},
}, diffs) }, diffs)
} }
func TestCalculateStateDiffsMultipleChangedMultipleUnchanged(t *testing.T) { func TestCalculateStateDiffsMultipleChanged(t *testing.T) {
oldStates := map[string]string{ oldStates := map[string]DeviceState{
"1": "oldState", "1": StateOffline,
"2": "oldState", "2": StateOnline,
} }
newStates := map[string]string{ newStates := map[string]DeviceState{
"1": "newState", "1": StateOnline,
"2": "newState", "2": StateOffline,
} }
diffs := calculateStateDiffs(oldStates, newStates) diffs := calculateStateDiffs(oldStates, newStates)
assert.True(t, reflect.DeepEqual([]DeviceStateChangedEvent{ assertContainsOnly(t, []DeviceStateChangedEvent{
DeviceStateChangedEvent{"1", "oldState", "newState"}, DeviceStateChangedEvent{"1", StateOffline, StateOnline},
DeviceStateChangedEvent{"2", "oldState", "newState"}, DeviceStateChangedEvent{"2", StateOnline, StateOffline},
}, diffs)) }, diffs)
} }
func TestCalculateStateDiffsOneAddedOneRemovedOneChanged(t *testing.T) { func TestCalculateStateDiffsOneAddedOneRemovedOneChanged(t *testing.T) {
oldStates := map[string]string{ oldStates := map[string]DeviceState{
"1": "oldState", "1": StateOffline,
"2": "removed", "2": StateOffline,
} }
newStates := map[string]string{ newStates := map[string]DeviceState{
"1": "newState", "1": StateOnline,
"3": "added", "3": StateOffline,
} }
diffs := calculateStateDiffs(oldStates, newStates) diffs := calculateStateDiffs(oldStates, newStates)
assert.True(t, reflect.DeepEqual([]DeviceStateChangedEvent{ assertContainsOnly(t, []DeviceStateChangedEvent{
DeviceStateChangedEvent{"1", "oldState", "newState"}, DeviceStateChangedEvent{"1", StateOffline, StateOnline},
DeviceStateChangedEvent{"2", "removed", ""}, DeviceStateChangedEvent{"2", StateOffline, StateDisconnected},
DeviceStateChangedEvent{"3", "", "added"}, DeviceStateChangedEvent{"3", StateDisconnected, StateOffline},
}, diffs)) }, 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) { func TestPublishDevicesRestartsServer(t *testing.T) {
@ -231,3 +246,19 @@ func (s *MockServerStarter) StartServer() error {
return s.err 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]]
}