blob: e277c645f8f4741d76b6aff36a88b5cf1bcc2919 [file] [log] [blame]
// Copyright 2020 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 drivefs
import (
"context"
"time"
"chromiumos/tast/local/chrome"
"chromiumos/tast/testing"
"chromiumos/tast/timing"
)
// PreData holds information made available to tests that specify preconditions.
type PreData struct {
// Chrome is a connection to an already-started Chrome instance.
// It cannot be closed by tests.
Chrome *chrome.Chrome
// The path that DriveFS has mounted at.
MountPath string
// The API connection to the Test extension, reused by tests.
TestAPIConn *chrome.TestConn
// The APIClient singleton.
APIClient *APIClient
}
// NewPrecondition creates a new drivefs precondition for tests that need different logins.
func NewPrecondition(name string, gaia *GaiaVars, opts ...chrome.Option) testing.Precondition {
pre := &preImpl{
name: name,
timeout: chrome.GAIALoginTimeout,
gaia: gaia,
opts: opts,
}
return pre
}
// GaiaVars holds the secret variables for username and password for a GAIA login.
type GaiaVars struct {
UserVar string // the secret variable for the GAIA username
PassVar string // the secret variable for the GAIA password
}
// preImpl implements testing.Precondition.
type preImpl struct {
name string // testing.Precondition.String
timeout time.Duration // testing.Precondition.Timeout
gaia *GaiaVars // a struct containing GAIA secret variables
opts []chrome.Option // extra options passed to Chrome
mountPath string // The path where Drivefs is mounted
cr *chrome.Chrome
tconn *chrome.TestConn
APIClient *APIClient
}
func (p *preImpl) String() string { return p.name }
func (p *preImpl) Timeout() time.Duration { return p.timeout }
// Prepare is called by the test framework at the beginning of every test using this precondition.
// It returns a PreData containing objects that can be used by the test.
func (p *preImpl) Prepare(ctx context.Context, s *testing.PreState) interface{} {
ctx, st := timing.Start(ctx, "prepare_"+p.name)
defer st.End()
// If mountPath exists and API client is not nil, check if Drive has stabilized and return early if it has.
if p.mountPath != "" && p.APIClient != nil {
mountPath, err := WaitForDriveFs(ctx, p.cr.NormalizedUser())
if err != nil {
s.Log("Failed waiting for DriveFS to stabilize: ", err)
chrome.Unlock()
p.cleanUp(ctx, s)
} else {
p.mountPath = mountPath
return p.buildPreData(ctx, s)
}
}
// If initialization fails, this defer is used to clean-up the partially-initialized pre.
// Stolen verbatim from arc/pre.go
shouldClose := true
defer func() {
if shouldClose {
p.cleanUp(ctx, s)
}
}()
func() {
ctx, cancel := context.WithTimeout(ctx, chrome.LoginTimeout)
defer cancel()
if p.gaia != nil {
var err error
username := s.RequiredVar(p.gaia.UserVar)
password := s.RequiredVar(p.gaia.PassVar)
p.cr, err = chrome.New(ctx, append(p.opts, chrome.GAIALogin(chrome.Creds{User: username, Pass: password}), chrome.ARCDisabled())...)
if err != nil {
s.Fatal("Failed to start Chrome: ", err)
}
} else {
s.Fatal("Failed to start Chrome with a GAIA login")
}
}()
mountPath, err := WaitForDriveFs(ctx, p.cr.NormalizedUser())
if err != nil {
s.Fatal("Failed waiting for DriveFS to start: ", err)
}
s.Log("drivefs fully started")
p.mountPath = mountPath
tconn, err := p.cr.TestAPIConn(ctx)
if err != nil {
s.Fatal("Failed creating test API connection: ", err)
}
p.tconn = tconn
jsonCredentials := s.RequiredVar("filemanager.drive_credentials")
// Perform Drive API authentication.
APIClient, err := CreateAPIClient(ctx, p.cr, jsonCredentials)
if err != nil {
s.Fatal("Failed creating a APIClient instance: ", err)
}
p.APIClient = APIClient
// Lock Chrome and make sure deferred function does not run cleanup.
chrome.Lock()
shouldClose = false
return p.buildPreData(ctx, s)
}
// Close is called by the test framework after the last test that uses this precondition.
func (p *preImpl) Close(ctx context.Context, s *testing.PreState) {
ctx, st := timing.Start(ctx, "close_"+p.name)
defer st.End()
chrome.Unlock()
p.cleanUp(ctx, s)
}
// buildPreData is a helper function that resets Chrome state and returns required data.
func (p *preImpl) buildPreData(ctx context.Context, s *testing.PreState) PreData {
if err := p.cr.ResetState(ctx); err != nil {
s.Fatal("Failed to reset chrome's state: ", err)
}
return PreData{p.cr, p.mountPath, p.tconn, p.APIClient}
}
// cleanUp closes Chrome, resets the mountPath to empty string and sets tconn to nil.
func (p *preImpl) cleanUp(ctx context.Context, s *testing.PreState) {
p.tconn = nil
p.APIClient = nil
p.mountPath = ""
if p.cr != nil {
if err := p.cr.Close(ctx); err != nil {
s.Log("Failed closing chrome: ", err)
}
p.cr = nil
}
}