blob: 74bf45a7dc387ac9bd5efd304bc6c498fc26f6c1 [file] [log] [blame]
// Copyright 2021 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Package mtp implements the fixture for setting up the connected android device.
package mtp
import (
"context"
"io/ioutil"
"os"
"path/filepath"
"strings"
"time"
"chromiumos/tast/common/android/adb"
"chromiumos/tast/common/testexec"
"chromiumos/tast/errors"
localadb "chromiumos/tast/local/android/adb"
"chromiumos/tast/local/arc"
"chromiumos/tast/local/chrome"
"chromiumos/tast/local/chrome/uiauto/filesapp"
"chromiumos/tast/testing"
)
// resetTimeout is the timeout duration of trying to reset the current fixture.
const resetTimeout = 30 * time.Second
// NewMTPFixture creates a new implementation of MTP fixture with an Android device.
func NewMTPFixture(User, Password string, opts ...chrome.Option) testing.FixtureImpl {
return &mtpFixture{
opts: opts,
User: User,
Password: Password,
}
}
func init() {
testing.AddFixture(&testing.Fixture{
Name: "mtpWithAndroid",
Desc: "User login with ARC enabled and secondary connected Android phone setup in MTP mode",
Contacts: []string{"rnanjappan@google.com", "arc-storage@google.com"},
Impl: NewMTPFixture("arc.MTP.user", "arc.MTP.password", chrome.ARCEnabled(), chrome.ExtraArgs(arc.DisableSyncFlags()...)),
Vars: []string{
"arc.MTP.user",
"arc.MTP.password",
},
SetUpTimeout: chrome.GAIALoginTimeout + arc.BootTimeout,
ResetTimeout: resetTimeout,
TearDownTimeout: resetTimeout,
PreTestTimeout: resetTimeout,
PostTestTimeout: resetTimeout,
})
}
type mtpFixture struct {
cr *chrome.Chrome
opts []chrome.Option
User string
Password string
}
// FixtData holds information made available to tests that specify this Fixture.
type FixtData struct {
Chrome *chrome.Chrome
TestConn *chrome.TestConn
}
func (f *mtpFixture) SetUp(ctx context.Context, s *testing.FixtState) interface{} {
User := s.RequiredVar(f.User)
Pass := s.RequiredVar(f.Password)
f.opts = append(f.opts, chrome.GAIALogin(chrome.Creds{
User: User,
Pass: Pass,
}))
cr, err := chrome.New(ctx, f.opts...)
if err != nil {
s.Fatal("Failed to start Chrome: ", err)
}
tconn, err := cr.TestAPIConn(ctx)
if err != nil {
s.Fatal("Creating test API connection failed: ", err)
}
f.cr = cr
fixtData := &FixtData{
Chrome: cr,
TestConn: tconn,
}
// Setup adb and connect to the Android phone.
adbDevice, err := ADBSetUp(ctx)
if err != nil {
s.Fatal("Failed to setup an adb device: ", err)
}
testing.ContextLog(ctx, "Set to MTP Mode")
mtpCmd := adbDevice.ShellCommand(ctx, "svc", "usb", "setFunctions", "mtp", "true")
if _, err := mtpCmd.Output(); err != nil {
s.Fatal("Failed to set the device to MTP mode: ", err)
}
testing.ContextLog(ctx, "Enable adb root")
rootCmd := adbDevice.Command(ctx, "root")
// adb requires wait for restart as root for first run after a new build is flashed.
if err := rootCmd.Run(testexec.DumpLogOnError); err != nil {
s.Log("Wait until adb restarts as root: ", err)
_, err := adb.WaitForDevice(ctx, func(device *adb.Device) bool { return !strings.HasPrefix(device.Serial, "emulator-") }, 10*time.Second)
if err != nil {
s.Fatal("Failed to restart adb as root: ", err)
}
}
// Set up the test file.
const textFile = "storage.txt"
testFileLocation := filepath.Join(filesapp.DownloadPath, textFile)
if err := ioutil.WriteFile(testFileLocation, []byte("this is a test"), 0777); err != nil {
s.Fatalf("Creating file %s failed: %s", testFileLocation, err)
}
defer os.Remove(testFileLocation)
if err := adbDevice.PushFile(ctx, testFileLocation, "/mnt/sdcard/Download/"); err != nil {
s.Fatal("Failed to push file to MTP: ", err)
}
chrome.Lock()
return fixtData
}
func (f *mtpFixture) TearDown(ctx context.Context, s *testing.FixtState) {
chrome.Unlock()
if err := f.cr.Close(ctx); err != nil {
s.Log("Failed to close Chrome connection: ", err)
}
f.cr = nil
}
func (f *mtpFixture) Reset(ctx context.Context) error {
if err := f.cr.Responded(ctx); err != nil {
return errors.Wrap(err, "existing Chrome connection is unusable")
}
if err := f.cr.ResetState(ctx); err != nil {
return errors.Wrap(err, "failed resetting existing Chrome session")
}
return nil
}
func (f *mtpFixture) PreTest(ctx context.Context, s *testing.FixtTestState) {}
func (f *mtpFixture) PostTest(ctx context.Context, s *testing.FixtTestState) {}
// ADBSetUp configures adb and connects to the Android device.
func ADBSetUp(ctx context.Context) (*adb.Device, error) {
// Load the ARC adb vendor key, which must be pre-loaded on the Android device to allow adb over usb without requiring UI interaction.
if err := localadb.LaunchServer(ctx); err != nil {
return nil, errors.Wrap(err, "failed to launch adb server")
}
// Wait for the first available device, since we are assuming only a single Android device is connected to each CrOS device.
adbDevice, err := adb.WaitForDevice(ctx, func(device *adb.Device) bool { return !strings.HasPrefix(device.Serial, "emulator-") }, 10*time.Second)
if err != nil {
return nil, errors.Wrap(err, "failed to list adb devices")
}
return adbDevice, nil
}