blob: 825f4ca0f5db2320344ad047ef09134c6eeabf82 [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 crostini
import (
"context"
"fmt"
"time"
//"chromiumos/tast/errors"
"chromiumos/tast/local/chrome"
"chromiumos/tast/local/chrome/uiauto"
"chromiumos/tast/local/chrome/uiauto/nodewith"
"chromiumos/tast/local/chrome/uiauto/restriction"
"chromiumos/tast/local/chrome/uiauto/role"
"chromiumos/tast/local/crostini"
cui "chromiumos/tast/local/crostini/ui"
"chromiumos/tast/local/crostini/ui/settings"
"chromiumos/tast/local/crostini/ui/terminalapp"
"chromiumos/tast/local/input"
"chromiumos/tast/testing"
)
/*
The rules for valid users are:
-- Less than 32 characters, so if the default user name is more than 32 characters, it should be truncated.
For example, if the default username is abcdefghijklmnopqrstuvwxyzabcdefg, then it will be truncated as abcdefghijklmnopqrstuvwxyzabcdef.
-- Not one of the reserved usernames: refer to var reservedUsers.
Please also refer container default usernames to check whether there is update.
-- The username must start with a lowercase letter or underscore. some invalid examples: 9user, Tus.
-- The username must only contain lowercase letters, digits, underscores, and hyphens. Some invalid examples: uTfdsa, us9)x.
*/
const (
longUser = "somebodywithreaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaallyname"
errMsgInvalidStart = "Must start with a lowercase character or underscore"
errMsgInvalidLetters = "Lowercase characters, digits, underscores, or dashes only"
)
var (
reservedUsers = []string{"root", "daemon", "bin", "sys", "sync",
"games", "man", "lp", "mail", "news", "uucp",
"proxy", "www-data", "backup", "list", "irc",
"gnats", "nobody", "_apt", "systemd-timesync",
"systemd-network", "systemd-resolve",
"systemd-bus-proxy", "messagebus", "sshd", "rtkit",
"pulse", "android-root", "chronos-access", "android-everybody"}
usersWithInvalidStart = []string{"UCapitialized", "9number", "-dash", " space", "\\slash", "@"}
usersWithInvaidLetters = []string{"user@gmail.com", "space ", "space space", "upperO"}
truncatedUser = longUser[0:32]
validUser = "validuser"
)
func init() {
testing.AddTest(&testing.Test{
Func: CheckUsers,
Desc: "Test the installation could not proceed with invalid user",
Contacts: []string{"jinrongwu@google.com", "cros-containers-dev@google.com"},
Attr: []string{"group:mainline", "informational"},
SoftwareDeps: []string{"chrome", "vm_host"},
Pre: chrome.LoggedIn(),
Params: []testing.Param{
// Parameters generated by check_users_test.go. DO NOT EDIT.
{
Name: "stable",
ExtraData: []string{crostini.GetContainerMetadataArtifact("buster", false), crostini.GetContainerRootfsArtifact("buster", false)},
ExtraSoftwareDeps: []string{"dlc"},
ExtraHardwareDeps: crostini.CrostiniStable,
Timeout: 7 * time.Minute,
}, {
Name: "unstable",
ExtraAttr: []string{"informational"},
ExtraData: []string{crostini.GetContainerMetadataArtifact("buster", false), crostini.GetContainerRootfsArtifact("buster", false)},
ExtraSoftwareDeps: []string{"dlc"},
ExtraHardwareDeps: crostini.CrostiniUnstable,
Timeout: 7 * time.Minute,
},
},
})
}
func CheckUsers(ctx context.Context, s *testing.State) {
cr := s.PreValue().(*chrome.Chrome)
tconn, err := cr.TestAPIConn(ctx)
if err != nil {
s.Fatal("Failed to create Test API connection: ", err)
}
kb, err := input.Keyboard(ctx)
if err != nil {
s.Fatal("Failed to get keyboard: ", err)
}
defer kb.Close()
ui := uiauto.New(tconn)
// Cleanup.
defer func() {
crostini.RunCrostiniPostTest(ctx, crostini.PreData{Chrome: cr, TestAPIConn: tconn, Container: nil, Keyboard: kb})
// Open the Linux settings.
st, err := settings.OpenLinuxSettings(ctx, tconn, cr)
if err != nil {
s.Fatal("Failed to open Linux Settings: ", err)
}
defer st.Close(ctx)
if err := st.Remove()(ctx); err != nil {
s.Error("Failed to remove Linux: ", err)
}
}()
if err := settings.OpenInstaller(ctx, tconn, cr); err != nil {
s.Fatal("Failed to open Crostini installer: ", err)
}
userNameField := nodewith.Name("Username").Role(role.TextField).Ancestor(cui.InstallWindow)
installButton := nodewith.Name("Install").Role(role.Button)
// alertNode is the node for error message when the input user name is invalid.
// The node does not exist if the user name is valid.
alertNode := nodewith.Role(role.Alert).Ancestor(cui.InstallWindow)
clearUserField := func() uiauto.Action {
return uiauto.Combine("clear user field",
ui.LeftClick(userNameField),
kb.AccelAction("Ctrl+A"),
kb.AccelAction("Backspace"))
}
checkUser := func(invalidUser, errMsg string) {
typedUserName := nodewith.Name(invalidUser).Role(role.StaticText).Ancestor(userNameField)
if err := uiauto.Combine(fmt.Sprintf("check user %s", invalidUser),
clearUserField(),
kb.TypeAction(invalidUser),
ui.WaitUntilExists(typedUserName),
ui.CheckRestriction(installButton, restriction.Disabled),
)(ctx); err != nil {
s.Fatal("Failed to check invalid user: ", err)
}
errMsgNode := nodewith.Role(role.StaticText).Ancestor(alertNode)
errMsgNodeInfo, err := ui.Info(ctx, errMsgNode)
if err != nil {
s.Fatal("Failed to find the error alert: ", err)
}
if errMsgNodeInfo.Name != errMsg {
s.Fatalf("Failed to verify the error msg, got %s, want %s", errMsgNodeInfo.Name, errMsg)
}
}
for _, userName := range reservedUsers {
checkUser(userName, fmt.Sprintf("Username %q is not available", userName))
}
for _, userName := range usersWithInvalidStart {
checkUser(userName, errMsgInvalidStart)
}
for _, userName := range usersWithInvaidLetters {
checkUser(userName, errMsgInvalidLetters)
}
// Check empty user.
if err := uiauto.Combine(fmt.Sprintf("check empty user"),
clearUserField(),
ui.CheckRestriction(installButton, restriction.Disabled),
)(ctx); err != nil {
s.Fatal("Failed to check empty user: ", err)
}
// Check long user with more than 32 characters.
// Install Crostini with this user.
truncatedUserName := nodewith.Name(truncatedUser).Role(role.StaticText).Ancestor(userNameField)
if err := uiauto.Combine(fmt.Sprintf("check user %s", longUser),
clearUserField(),
kb.TypeAction(longUser),
ui.WaitUntilExists(truncatedUserName),
ui.Gone(alertNode),
ui.LeftClick(installButton),
ui.WithTimeout(8*time.Minute).WaitUntilGone(cui.InstallWindow),
)(ctx); err != nil {
s.Fatalf("Failed to check long user %s: %s", longUser, err)
}
// Find Terminal window.
terminalApp, err := terminalapp.Find(ctx, tconn)
if err != nil {
s.Fatal("Failed to find terminal after installing Crostini: ", err)
}
defer terminalApp.Close()(ctx)
// Check user name in the Terminal app.
terminalWindow := nodewith.Name(truncatedUser + "@penguin: ~").Role(role.RootWebArea)
if err := ui.WaitUntilExists(terminalWindow)(ctx); err != nil {
s.Fatalf("Failed to find username %s in terminal: %s", truncatedUser, err)
}
}