blob: 058103f71d9f15b69a9f4b95b6571cfc6adeee39 [file]
// 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 inputs
import (
"context"
"io/ioutil"
"os"
"strconv"
"strings"
"time"
"chromiumos/tast/common/policy"
"chromiumos/tast/common/policy/fakedms"
"chromiumos/tast/errors"
"chromiumos/tast/local/arc"
"chromiumos/tast/local/chrome"
"chromiumos/tast/local/chrome/ime"
"chromiumos/tast/local/chrome/uiauto/crd"
policyFixt "chromiumos/tast/local/policyutil/fixtures"
"chromiumos/tast/testing"
)
const helpStr = `
This test is designed to automatically setup test environment for manual verification with given conditions.
Supported variables:
userType: 'guest' or 'enterprise'. Login params will be ignored if userType is provided.
user: Username used for gaia login. Use fake user if user is not supplied.
pass: Password used for gaia login.
contact: Contact is required for dual auth on test account login.
imeID: Input method to be installed and activated for testing. Default IME is xkb:us::eng.
region: Region for device language. Default value is "us". Other region options: jp, fr.
vk: 'true' or 'false'. Whether virtual keyboard is force enabled.
arc: 'true' or 'false'. Whether install arc and input test app. It is disabled by default to optimize booting time.
crd: 'true' or 'false'. Whether install RDP extension and initializing remote desktop session.
reset: 'true' or 'false'. Existing home is cleaned every iteration by default unless reset is false.
chromeArgs: Extra chrome args used to setup the environment. e,g. "--enable-features=LanguageSettingsUpdate2 --disable-features=DefaultWebAppInstallation".
Example command:
* Login guest user with virtual keyboard enabled.
tast run -var=userType=guest -var=vk=true <dut ip> inputs.Manual
* To test a specific input method on an android app.
tast run -var=arc=true -var=imeID=nacl_mozc_us <dut ip> inputs.Manual
`
// envSettings represents the configurable parameters for the manual testing environment.
type envSettings struct {
// userType can be 'guest', 'enterprise'.
// Enterprise mode only support specific fake user.
// Login params will be ignored if userType is provided.
userType string
// user is the user name for gaia login. Use fake user if user is not supplied.
user string
// pass is the password used for gaia login.
pass string
// contact is required for dual auth on test account login.
contact string
// imeID represents the input method to be installed and activated for testing. Default IME is xkb:us::eng.
// More code refer to http://google3/i18n/input/javascript/chos/common/input_method_config.textproto.
imeID string
// region for device language. Default value is "us". Other region options: jp, fr.
region string
// vk indicates whether virtual keyboard is force enabled.
vk bool
// arc indicates whether install arc and input test app.
arc bool
// crd indicates whether install RDP extension and initializing remote desktop session.
// Note: this only works in English at the moment.
crd bool
// reset cleans existing home every iteration unless it is false.
reset bool
// chromeArgs is extra chrome args used to setup the environment.
chromeArgs []string
}
func init() {
// Example usage:
// $ tast run -var=user=<username> -var=pass=<password> <dut ip> inputs.Manual
// <username> and <password> are the credentials of the test GAIA account.
testing.AddTest(&testing.Test{
Func: Manual,
LacrosStatus: testing.LacrosVariantUnneeded,
Desc: "Login device and setup environment for manual testing purpose",
Contacts: []string{"shengjun@google.com", "essential-inputs-team@google.com"},
SoftwareDeps: []string{"chrome", "arc"},
Timeout: 10 * time.Minute,
Vars: []string{
"userType", "user", "pass", "contact", "imeID", "region", "vk",
"arc", "crd", "reset", "chromeArgs",
},
Params: []testing.Param{{
Name: "",
Val: false,
}, {
Name: "help",
Val: true,
}},
})
}
// getVars extracts the testing parameters from testing.State.
func getVars(s *testing.State) *envSettings {
settings := &envSettings{
userType: parseStringVar(s, "userType", ""),
user: parseStringVar(s, "user", ""),
pass: parseStringVar(s, "pass", ""),
contact: parseStringVar(s, "contact", ""),
imeID: parseStringVar(s, "imeID", ""),
region: parseStringVar(s, "region", ""),
vk: true,
arc: false,
crd: false,
reset: true,
chromeArgs: []string{},
}
vkStr := parseStringVar(s, "vk", "true")
vk, err := strconv.ParseBool(vkStr)
if err != nil {
s.Fatal("Failed to parse the variable `vk`: ", err)
}
settings.vk = vk
arcStr := parseStringVar(s, "arc", "false")
arc, err := strconv.ParseBool(arcStr)
if err != nil {
s.Fatal("Failed to parse the variable `arc`: ", err)
}
settings.arc = arc
crdStr := parseStringVar(s, "crd", "false")
crd, err := strconv.ParseBool(crdStr)
if err != nil {
s.Fatal("Failed to parse the variable `crd`: ", err)
}
settings.crd = crd
resetStr := parseStringVar(s, "reset", "true")
reset, err := strconv.ParseBool(resetStr)
if err != nil {
s.Fatal("Failed to parse the variable `reset`: ", err)
}
settings.reset = reset
chromeArgs, hasChromeArgs := s.Var("chromeArgs")
if hasChromeArgs {
settings.chromeArgs = strings.Fields(chromeArgs)
}
if err := validateSettings(settings); err != nil {
s.Fatal("Failed to validate variables: ", err)
}
return settings
}
// validateSettings verify setting conflicts.
// It terminate testing on unsupported / incompatible settings.
func validateSettings(settings *envSettings) error {
if settings.crd && (settings.user == "" || settings.userType == "guest") {
return errors.New("Remote Desktop is not supported in fake user / guest mode")
}
return nil
}
func Manual(ctx context.Context, s *testing.State) {
// Print out help content and end the test.
isHelp := s.Param().(bool)
if isHelp {
testing.ContextLog(ctx, helpStr)
return
}
defer func() {
if s.HasError() {
s.Log(`Please run "tast run <dut ip> inputs.Manual.help" for more insructions of this test`)
}
}()
settings := getVars(s)
testing.ContextLogf(ctx, "Start to set up test environment with settings: %+v", settings)
var opts []chrome.Option
switch settings.userType {
case "guest":
opts = append(opts, chrome.GuestLogin())
case "enterprise":
// Using fakedms and login.
// Start FakeDMS.
tmpdir, err := ioutil.TempDir("", "fdms-")
if err != nil {
s.Fatal("Failed to create fdms temp dir: ", err)
}
defer os.RemoveAll(tmpdir)
testing.ContextLogf(ctx, "FakeDMS starting in %s", tmpdir)
fdms, err := fakedms.New(ctx, tmpdir)
if err != nil {
s.Fatal("Failed to start FakeDMS: ", err)
}
defer fdms.Stop(ctx)
if err := fdms.WritePolicyBlob(policy.NewBlob()); err != nil {
s.Fatal("Failed to write policies to FakeDMS: ", err)
}
opts = append(opts,
chrome.FakeLogin(chrome.Creds{User: policyFixt.Username, Pass: policyFixt.Password}),
chrome.DMSPolicy(fdms.URL))
default:
if settings.user != "" {
opts = append(opts, chrome.GAIALogin(chrome.Creds{
User: settings.user,
Pass: settings.pass,
Contact: settings.contact,
}))
}
}
if settings.region != "" {
opts = append(opts, chrome.Region(settings.region))
}
if settings.vk {
opts = append(opts, chrome.VKEnabled())
}
if !settings.reset {
opts = append(opts, chrome.KeepState())
}
chromeARCOpt := chrome.ARCDisabled()
if settings.arc {
if arc.Supported() {
chromeARCOpt = chrome.ARCEnabled()
} else {
s.Fatal("The DUT board does not support ARC")
}
}
opts = append(opts, chromeARCOpt)
if len(settings.chromeArgs) > 0 {
opts = append(opts, chrome.ExtraArgs(settings.chromeArgs...))
}
cr, err := chrome.New(ctx, opts...)
if err != nil {
// In case of authentication error, provide a more informative message to the user.
if strings.Contains(err.Error(), "chrome.Auth") {
err = errors.Wrap(err, "please supply a password with -var=pass=<password>")
} else if strings.Contains(err.Error(), "chrome.Contact") {
err = errors.Wrap(err, "please supply a valid contact email with -var=contact=<contact>")
}
s.Fatal("Failed to start Chrome: ", err)
}
defer cr.Close(ctx)
var a *arc.ARC
if settings.arc {
a, err = arc.New(ctx, s.OutDir())
if err != nil {
s.Fatal("Failed to start ARC: ", err)
}
}
tconn, err := cr.TestAPIConn(ctx)
if err != nil {
s.Fatal("Failed to connect Test API: ", err)
}
// Launch e14s-test test page for non-arc environment.
const e14sTestPage = "https://sites.google.com/corp/view/e14s-test"
if !settings.arc {
_, err := cr.NewConn(ctx, e14sTestPage)
if err != nil {
s.Fatal("Failed to create renderer: ", err)
}
} else {
// Launch android app for arc testing.
const (
apk = "ArcKeyboardTest.apk"
pkg = "org.chromium.arc.testapp.keyboard"
fieldID = "org.chromium.arc.testapp.keyboard:id/text"
)
s.Log("Installing ArcKeyboardTest app")
if err := a.Install(ctx, arc.APKPath(apk)); err != nil {
s.Fatal("Failed installing app: ", err)
}
act, err := arc.NewActivity(a, pkg, ".MainActivity")
if err != nil {
s.Fatal("Failed to create new activity: ", err)
}
defer act.Close()
s.Log("Starting ArcKeyboardTest app")
if err := act.StartWithDefaultOptions(ctx, tconn); err != nil {
s.Fatal("Failed to start app: ", err)
}
s.Log("ArcKeyboardTest app started")
s.Log("Note: First input is free text and second one is visible password")
}
// Add testing input method.
if settings.imeID != "" {
if err := ime.AddAndSetInputMethod(ctx, tconn, ime.ChromeIMEPrefix+settings.imeID); err != nil {
s.Fatalf("Failed to set input method %q: %v", settings.imeID, err)
}
}
if settings.crd {
if err := crd.Launch(ctx, cr, tconn); err != nil {
s.Fatal("Failed to Launch remote desktop: ", err)
}
s.Log("Waiting connection")
if err := crd.WaitConnection(ctx, tconn); err != nil {
s.Fatal("No client connected: ", err)
}
}
}
//parseStringVar reads string variable. It returns default value if not exist.
func parseStringVar(s *testing.State, varName, defaultStr string) string {
value, hasValue := s.Var(varName)
if hasValue {
return value
}
return defaultStr
}