func TestPublishDevicesRestartsServer(t *testing.T) { starter := &MockServerStarter{} dialer := &MockServer{ Status: wire.StatusSuccess, Errs: []error{ nil, nil, nil, // Successful dial. util.Errorf(util.ConnectionResetError, "failed first read"), util.Errorf(util.ServerNotAvailable, "failed redial"), }, } watcher := deviceWatcherImpl{ config: ClientConfig{dialer}, eventChan: make(chan DeviceStateChangedEvent), startServer: starter.StartServer, } publishDevices(&watcher) assert.Empty(t, dialer.Errs) assert.Equal(t, []string{"host:track-devices"}, dialer.Requests) assert.Equal(t, []string{"Dial", "SendMessage", "ReadStatus", "ReadMessage", "Dial"}, dialer.Trace) err := watcher.err.Load().(*util.Err) assert.Equal(t, util.ServerNotAvailable, err.Code) assert.Equal(t, 1, starter.startCount) }
func (c *CachingDeviceClient) Stat(name string, log *LogEntry) (*goadb.DirEntry, error) { dir := path.Dir(name) base := path.Base(name) if dir == base { // Don't ask the cache for the root stat, we never cache the root. return c.DeviceClient.Stat(name, log) } if entries, found := c.Cache.Get(dir); found { log.CacheUsed(true) if entry, found := entries.ByName[base]; found { return entry, nil } // Cached directory list doesn't have name, so as far as we're concerned the // file doesn't exist. return nil, util.Errorf(util.FileNoExistError, "name '%s' does not exist in cached directory listing", base) } log.CacheUsed(false) // The directory doesn't exist in the cache, so perform a one-off lookup on the device. return c.DeviceClient.Stat(name, log) }
func TestFileBufferSync(t *testing.T) { dev := &delegateDeviceClient{ stat: statFiles(&goadb.DirEntry{ Name: "/file", Mode: 0664, }), openRead: openReadString("hello"), } file := newTestFileBuffer(t, O_RDONLY, FileBufferOptions{ Path: "/file", Client: dev, }) assert.Equal(t, "hello", file.Contents()) // Success. dev.openRead = openReadString("world") err := file.Sync(&LogEntry{}) assert.NoError(t, err) assert.Equal(t, "world", file.Contents()) // Failure. dev.openRead = openReadError(util.Errorf(util.NetworkError, "fail")) err = file.Sync(&LogEntry{}) assert.Equal(t, `NetworkError: error opening file stream on device caused by NetworkError: fail`, util.ErrorWithCauseChain(err)) assert.Equal(t, "world", file.Contents()) }
func readStat(s wire.SyncScanner) (entry *DirEntry, err error) { mode, err := s.ReadFileMode() if err != nil { err = util.WrapErrf(err, "error reading file mode: %v", err) return } size, err := s.ReadInt32() if err != nil { err = util.WrapErrf(err, "error reading file size: %v", err) return } mtime, err := s.ReadTime() if err != nil { err = util.WrapErrf(err, "error reading file time: %v", err) return } // adb doesn't indicate when a file doesn't exist, but will return all zeros. // Theoretically this could be an actual file, but that's very unlikely. if mode == os.FileMode(0) && size == 0 && mtime == zeroTime { return nil, util.Errorf(util.FileNoExistError, "file doesn't exist") } entry = &DirEntry{ Mode: mode, Size: size, ModifiedAt: mtime, } return }
func parseDeviceShort(line string) (*DeviceInfo, error) { fields := strings.Fields(line) if len(fields) != 2 { return nil, util.Errorf(util.ParseError, "malformed device line, expected 2 fields but found %d", len(fields)) } return newDevice(fields[0], map[string]string{}) }
func statFiles(entries ...*goadb.DirEntry) func(string) (*goadb.DirEntry, error) { return func(path string) (*goadb.DirEntry, error) { for _, entry := range entries { if entry.Name == path { return entry, nil } } return nil, util.Errorf(util.FileNoExistError, "%s", path) } }
func parseDeviceLong(line string) (*DeviceInfo, error) { fields := strings.Fields(line) if len(fields) < 5 { return nil, util.Errorf(util.ParseError, "malformed device line, expected at least 5 fields but found %d", len(fields)) } attrs := parseDeviceAttributes(fields[2:]) return newDevice(fields[0], attrs) }
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 { continue } fields := strings.Split(line, "\t") if len(fields) != 2 { err = util.Errorf(util.ParseError, "invalid device state line %d: %s", lineNum, line) return } 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 stat(conn *wire.SyncConn, path string) (*DirEntry, error) { if err := conn.SendOctetString("STAT"); err != nil { return nil, err } if err := conn.SendString(path); err != nil { return nil, err } id, err := conn.ReadOctetString() if err != nil { return nil, err } if id != "STAT" { return nil, util.Errorf(util.AssertionError, "expected stat ID 'STAT', but got '%s'", id) } return readStat(conn) }
func TestAdbFile_Fsync(t *testing.T) { fileBuf := testSingleRegularFileBuffer(t, "hello") file := getAdbFile(NewAdbFile(AdbFileOpenOptions{ FileBuffer: fileBuf, })) assert.Equal(t, "hello", fileBuf.Contents()) // Success. fileBuf.Client.(*delegateDeviceClient).openRead = openReadString("world") status := file.Fsync(0) assertStatusOk(t, status) assert.Equal(t, "world", fileBuf.Contents()) // Failure. fileBuf.Client.(*delegateDeviceClient).openRead = openReadError(util.Errorf(util.NetworkError, "")) status = file.Fsync(0) assert.Equal(t, fuse.EIO, status) assert.Equal(t, "world", fileBuf.Contents()) }
// prepareCommandLine validates the command and argument strings, quotes // arguments if required, and joins them into a valid adb command string. func prepareCommandLine(cmd string, args ...string) (string, error) { if isBlank(cmd) { return "", util.AssertionErrorf("command cannot be empty") } for i, arg := range args { if strings.ContainsRune(arg, '"') { return "", util.Errorf(util.ParseError, "arg at index %d contains an invalid double quote: %s", i, arg) } if containsWhitespace(arg) { args[i] = fmt.Sprintf("\"%s\"", arg) } } // Prepend the command to the args array. if len(args) > 0 { cmd = fmt.Sprintf("%s %s", cmd, strings.Join(args, " ")) } return cmd, nil }
func TestCachingDeviceClientStat_Miss(t *testing.T) { client := &CachingDeviceClient{ DeviceClient: &delegateDeviceClient{ stat: func(path string) (*goadb.DirEntry, error) { if path == "/foo/bar" { return &goadb.DirEntry{Name: "baz"}, nil } return nil, util.Errorf(util.FileNoExistError, "") }, }, Cache: &delegateDirEntryCache{ DoGet: func(path string) (entries *CachedDirEntries, found bool) { return nil, false }, }, } entry, err := client.Stat("/foo/bar", &LogEntry{}) assert.NoError(t, err) assert.Equal(t, "baz", entry.Name) }
func (c *DeviceClient) GetDeviceInfo() (*DeviceInfo, error) { // Adb doesn't actually provide a way to get this for an individual device, // so we have to just list devices and find ourselves. serial, err := c.GetSerial() if err != nil { return nil, wrapClientError(err, c, "GetDeviceInfo(GetSerial)") } devices, err := c.deviceListFunc() if err != nil { return nil, wrapClientError(err, c, "GetDeviceInfo(ListDevices)") } for _, deviceInfo := range devices { if deviceInfo.Serial == serial { return deviceInfo, nil } } err = util.Errorf(util.DeviceNotFound, "device list doesn't contain serial %s", serial) return nil, wrapClientError(err, c, "GetDeviceInfo") }