blob: f9d635c4d4dec501600453f3cbd6919b506b132e [file] [log] [blame]
// Copyright 2019 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 chrome
import (
// ResetTimeout is the timeout durection to trying reset of the current precondition.
const ResetTimeout = 15 * time.Second
// LoggedIn returns a precondition that Chrome is already logged in when a test is run.
// When adding a test, the testing.Test.Pre field may be set to the value returned by this function.
// Later, in the main test function, the value returned by testing.State.PreValue may be converted
// to an already-logged-in *chrome.Chrome:
// func DoSomething(ctx context.Context, s *testing.State) {
// cr := s.PreValue().(*chrome.Chrome)
// conn, err := cr.NewConn(ctx, "")
// ...
// }
// When using this precondition, tests cannot call New.
// The Chrome instance is also shared and cannot be closed by tests.
func LoggedIn() testing.Precondition { return loggedInPre }
// LoggedInDisableSync is the same as LoggedIn with --disable-sync flag.
// When completing a fake user login apps aren't synced, see
func LoggedInDisableSync() testing.Precondition { return loggedInDisableSyncPre }
// NewPrecondition creates a new precondition that can be shared by tests
// that require an already-started Chrome object that was created with opts.
// suffix is appended to precondition's name.
func NewPrecondition(suffix string, opts ...Option) testing.Precondition {
return &preImpl{
name: "chrome_" + suffix,
timeout: ResetTimeout + LoginTimeout,
opts: opts,
var loggedInPre = NewPrecondition("logged_in")
var loggedInDisableSyncPre = NewPrecondition("logged_in_disable_sync", ExtraArgs("--disable-sync"))
// preImpl implements testing.Precondition.
type preImpl struct {
name string // testing.Precondition.String
timeout time.Duration // testing.Precondition.Timeout
cr *Chrome // underlying Chrome instance
opts []Option // Options that should be passed to New
func (p *preImpl) String() string { return }
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 *chrome.Chrome that can be used by tests.
func (p *preImpl) Prepare(ctx context.Context, s *testing.PreState) interface{} {
ctx, st := timing.Start(ctx, "prepare_"
defer st.End()
if != nil {
err := func() error {
ctx, cancel := context.WithTimeout(ctx, ResetTimeout)
defer cancel()
ctx, st := timing.Start(ctx, "reset_"
defer st.End()
if err :=; err != nil {
return errors.Wrap(err, "existing Chrome connection is unusable")
if err :=; err != nil {
return errors.Wrap(err, "failed resetting existing Chrome session")
return nil
if err == nil {
s.Log("Reusing existing Chrome session")
s.Log("Failed to reuse existing Chrome session: ", err)
p.closeInternal(ctx, s)
ctx, cancel := context.WithTimeout(ctx, LoginTimeout)
defer cancel()
var err error
if, err = New(ctx, p.opts...); err != nil {
s.Fatal("Failed to start Chrome: ", err)
// 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_"
defer st.End()
p.closeInternal(ctx, s)
// closeInternal closes and resets if non-nil.
func (p *preImpl) closeInternal(ctx context.Context, s *testing.PreState) {
if == nil {
if err :=; err != nil {
s.Log("Failed to close Chrome connection: ", err)
} = nil