blob: 0dff0c76eab697905f15beb554d2b0ff44126cda [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 chrome
import (
"context"
"io/ioutil"
"os"
"path/filepath"
"chromiumos/tast/errors"
"chromiumos/tast/local/chrome/cdputil"
"chromiumos/tast/local/chrome/internal/config"
"chromiumos/tast/local/chrome/internal/driver"
"chromiumos/tast/local/chrome/internal/extension"
"chromiumos/tast/local/chrome/jslog"
"chromiumos/tast/local/logsaver"
"chromiumos/tast/testing"
"chromiumos/tast/timing"
)
// tryReuseSession checks if the exiting chrome session can be reuse, and returns a
// Chrome instance if reuse criteria is met.
func tryReuseSession(ctx context.Context, cfg *config.Config) (cr *Chrome, retErr error) {
ctx, st := timing.Start(ctx, "try_reuse_session")
defer st.End()
testing.ContextLog(ctx, "Trying to reuse existing chrome session")
agg := jslog.NewAggregator()
defer func() {
if retErr != nil {
agg.Close()
}
}()
sess, err := driver.NewSession(ctx, cdputil.DebuggingPortPath, cdputil.NoWaitPort, agg)
if err != nil {
return nil, err
}
defer func() {
if retErr != nil {
sess.Close(ctx)
}
}()
if err := compareExtensions(cfg); err != nil {
return nil, err
}
if err := compareConfig(ctx, sess, cfg); err != nil {
return nil, err
}
if err := compareUserLogin(ctx, sess, cfg.Creds().User); err != nil {
return nil, err
}
logFilename, err := CurrentLogFile()
if err != nil {
return nil, err
}
testing.ContextLogf(ctx, "Log file name: %s", logFilename)
// When reusing the session, lines already in the log file should be unrelated
// to the test itself. Thus use logsaver.NewMarker to exclude already existing
// log lines.
logMarker, err := logsaver.NewMarker(logFilename)
if err != nil {
return nil, err
}
return &Chrome{
cfg: *cfg,
agg: agg,
sess: sess,
logFilename: logFilename,
logMarker: logMarker,
loginPending: cfg.DeferLogin(),
}, nil
}
// compareExtensions checks if the new session extensions match the ones of the existing session.
func compareExtensions(cfg *config.Config) error {
// Get existing extension checksums.
checksums, err := extension.Checksums(extensionsDir)
if err != nil {
return errors.Wrap(err, "failed to get checksums of existing extensions")
}
// Prepare extensions for new session in a temporary dir.
tempDir, err := ioutil.TempDir("", "")
if err != nil {
return err
}
defer os.RemoveAll(tempDir)
guestModeLogin := extension.GuestModeDisabled
if cfg.LoginMode() == config.GuestLogin {
guestModeLogin = extension.GuestModeEnabled
}
// PrepareExtensions() expects a non-existent extension dir.
tempExtDir := filepath.Join(tempDir, "extensions")
_, err = extension.PrepareExtensions(tempExtDir, cfg, guestModeLogin)
if err != nil {
return err
}
newChecksums, err := extension.Checksums(tempExtDir)
if err != nil {
return errors.Wrap(err, "failed to get checksums of new extensions")
}
// Make sure all new extensions exist in existing extensions by comparing checksums.
contains := func(slc []string, ele string) bool {
// Check if a slice contains a given element.
for _, e := range slc {
if e == ele {
return true
}
}
return false
}
for _, ne := range newChecksums {
if !contains(checksums, ne) {
return errors.New("required extension doesn't exist in the existing session")
}
}
return nil
}
// compareUserLogin checks if the configured user has logged in.
func compareUserLogin(ctx context.Context, sess *driver.Session, email string) error {
conn, err := sess.TestAPIConn(ctx)
if err != nil {
return errors.Wrap(err, "failed to establish Chrome connection")
}
var status struct {
IsLoggedIn bool
IsScreenLocked bool
}
if err := conn.Eval(ctx, `tast.promisify(chrome.usersPrivate.getLoginStatus)()`, &status); err != nil {
return errors.Wrap(err, "failed to run javascript to get login status")
}
if !status.IsLoggedIn {
return errors.New("user has not logged in")
}
if status.IsScreenLocked {
return errors.New("screen is locked")
}
return nil
}
// compareConfig compares the configuration between new and existing Chrome sessions.
func compareConfig(ctx context.Context, sess *driver.Session, cfg *config.Config) error {
conn, err := sess.TestAPIConn(ctx)
if err != nil {
return errors.Wrap(err, "failed to establish Chrome connection")
}
var existingChromeOptions string
if err := conn.Eval(ctx, extension.TastChromeOptionsJSVar, &existingChromeOptions); err != nil {
return errors.Wrapf(err, "failed to evaluate %s", extension.TastChromeOptionsJSVar)
}
existingCfg, err := config.Unmarshal([]byte(existingChromeOptions))
if err != nil {
return errors.Wrap(err, "failed to unmarshal existing config data")
}
return existingCfg.VerifySessionReuse(cfg)
}