blob: 595c1f98402f8b4c007d9519044963bb3c8ba5e0 [file] [log] [blame]
// Copyright 2022 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package intel
import (
"context"
"regexp"
"time"
"go.chromium.org/tast-tests/cros/common/servo"
"go.chromium.org/tast-tests/cros/common/usbutils"
"go.chromium.org/tast-tests/cros/remote/powercontrol"
"go.chromium.org/tast/core/ctxutil"
"go.chromium.org/tast/core/dut"
"go.chromium.org/tast/core/errors"
"go.chromium.org/tast/core/testing"
"go.chromium.org/tast/core/testing/hwdep"
)
type extendedDisplayFunctionTestParams struct {
powerMode string
ecStateToCheck string
expectedPrevSleepState int
tabletmode bool
}
func init() {
testing.AddTest(&testing.Test{
Func: ExtendedDisplayFunctionality,
LacrosStatus: testing.LacrosVariantUnneeded,
Desc: "Verifies type-C extended display functionality before and after performing cold boot and warm boot",
Contacts: []string{"intel.chrome.automation.team@intel.com", "ambalavanan.m.m@intel.com"},
BugComponent: "b:157291", // ChromeOS > External > Intel
SoftwareDeps: []string{"chrome", "reboot"},
ServiceDeps: []string{"tast.cros.security.BootLockboxService"},
HardwareDeps: hwdep.D(hwdep.ChromeEC(), hwdep.InternalDisplay()),
Attr: []string{"group:intel-hdmi-type-c"},
Timeout: 5 * time.Minute,
Vars: []string{"servo"},
Params: []testing.Param{{
Name: "typec_hdmi_shutdown",
Val: extendedDisplayFunctionTestParams{
powerMode: "shutdown_command",
ecStateToCheck: "S5",
expectedPrevSleepState: 5,
tabletmode: false,
},
}, {
Name: "typec_hdmi_reboot_tablet",
Val: extendedDisplayFunctionTestParams{
powerMode: "reboot_command",
expectedPrevSleepState: 0,
tabletmode: true,
},
}, {
Name: "typec_hdmi_reboot_clamshell",
Val: extendedDisplayFunctionTestParams{
powerMode: "reboot_command",
expectedPrevSleepState: 0,
tabletmode: false,
},
}, {
Name: "typec_hdmi_powerbtn_tablet",
Val: extendedDisplayFunctionTestParams{
powerMode: "powerbtn_shutdown",
ecStateToCheck: "G3",
expectedPrevSleepState: 5,
tabletmode: true,
},
}, {
Name: "typec_hdmi_powerbtn_clamshell",
Val: extendedDisplayFunctionTestParams{
powerMode: "powerbtn_shutdown",
ecStateToCheck: "G3",
expectedPrevSleepState: 5,
tabletmode: false,
},
}},
})
}
func ExtendedDisplayFunctionality(ctx context.Context, s *testing.State) {
cleanupCtx := ctx
ctx, cancel := ctxutil.Shorten(ctx, 2*time.Minute)
defer cancel()
servoSpec, _ := s.Var("servo")
dut := s.DUT()
testOpt := s.Param().(extendedDisplayFunctionTestParams)
pxy, err := servo.NewProxy(ctx, servoSpec, dut.KeyFile(), dut.KeyDir())
if err != nil {
s.Fatal("Failed to connect to servo: ", err)
}
defer pxy.Close(cleanupCtx)
// Get the initial tablet_mode_angle settings to restore at the end of test.
re := regexp.MustCompile(`tablet_mode_angle=(\d+) hys=(\d+)`)
tabletOut, err := dut.Conn().CommandContext(ctx, "ectool", "motionsense", "tablet_mode_angle").Output()
if err != nil {
s.Fatal("Failed to retrieve tablet_mode_angle settings: ", err)
}
m := re.FindSubmatch(tabletOut)
if len(m) != 3 {
s.Fatalf("Failed to get initial tablet_mode_angle settings: got submatches %+v", m)
}
initLidAngle := m[1]
initHys := m[2]
if testOpt.tabletmode {
// Set tabletModeAngle to 0 to force the DUT into tablet mode.
testing.ContextLog(ctx, "Put DUT into tablet mode")
if err := dut.Conn().CommandContext(ctx, "ectool", "motionsense", "tablet_mode_angle", "0", "0").Run(); err != nil {
s.Fatal("Failed to set DUT into tablet mode: ", err)
}
}
defer func(ctx context.Context) {
testing.ContextLog(ctx, "Performing cleanup")
if !dut.Connected(ctx) {
if err := powercontrol.PowerOntoDUT(ctx, pxy, dut); err != nil {
s.Fatal("Failed to power on DUT at cleanup: ", err)
}
}
if err := dut.Conn().CommandContext(ctx, "ectool", "motionsense", "tablet_mode_angle", string(initLidAngle), string(initHys)).Run(); err != nil {
s.Fatal("Failed to restore tablet_mode_angle to the original settings: ", err)
}
}(cleanupCtx)
// Perform a Chrome login.
s.Log("Login to Chrome")
if err := powercontrol.ChromeOSLogin(ctx, dut, s.RPCHint()); err != nil {
s.Fatal("Failed to login to chrome: ", err)
}
// Verifying external display detection before shutdown/reboot.
numberOfDisplay := 1
spec := usbutils.DisplaySpec{
NumberOfDisplays: &numberOfDisplay,
DisplayType: usbutils.TypeCHDMI,
}
if err := usbutils.ExternalDisplayDetectionForRemote(ctx, dut, spec); err != nil {
s.Fatal("Failed to detect external HDMI display: ", err)
}
if testOpt.powerMode == "shutdown_command" {
testing.ContextLog(ctx, "Executing shutdown command")
if err := powercontrol.ShutdownAndWaitForPowerState(ctx, pxy, dut, testOpt.ecStateToCheck); err != nil {
s.Fatal("Failed to shutdown DUT and check EC state: ", err)
}
}
if testOpt.powerMode == "reboot_command" {
testing.ContextLog(ctx, "Rebooting the DUT")
if err := performReboot(ctx, dut); err != nil {
s.Fatal("Failed to perform DUT reboot: ", err)
}
}
if testOpt.powerMode == "powerbtn_shutdown" {
testing.ContextLog(ctx, "Shutdown DUT with power button long press via servo")
if err := shutdownWithPowerButtonViaServo(ctx, pxy, dut); err != nil {
s.Fatal("Failed to shutdown DUT with power long press via servo: ", err)
}
if err := verifyECPowerState(ctx, pxy, testOpt.ecStateToCheck); err != nil {
s.Fatal("Failed to check EC power state: ", err)
}
}
if testOpt.ecStateToCheck != "" {
// Performing power normal press to power ON DUT.
if err := powercontrol.PowerOntoDUT(ctx, pxy, dut); err != nil {
s.Fatal("Failed to power on DUT: ", err)
}
}
// Verifying external display detection after shutdown/reboot.
if err := usbutils.ExternalDisplayDetectionForRemote(ctx, dut, spec); err != nil {
s.Fatalf("Failed detecting external display after %q: %v", testOpt.powerMode, err)
}
// Performing prev_sleep_state check.
valid, err := powercontrol.IsPrevSleepStateAvailable(ctx, dut)
if err != nil {
s.Fatal("Failed to determine if prev_sleep_state is available: ", err)
}
if valid {
if err := powercontrol.ValidatePrevSleepState(ctx, dut, testOpt.expectedPrevSleepState); err != nil {
s.Fatal("Failed to validate previous sleep state: ", err)
}
}
}
// shutdownWithPowerButtonViaServo performs shutdown DUT with power button long press via servo.
func shutdownWithPowerButtonViaServo(ctx context.Context, pxy *servo.Proxy, dut *dut.DUT) error {
if err := pxy.Servo().KeypressWithDuration(ctx, servo.PowerKey, servo.DurLongPress); err != nil {
return errors.Wrap(err, "failed to power long press")
}
sdCtx, cancel := context.WithTimeout(ctx, 20*time.Second)
defer cancel()
if err := dut.WaitUnreachable(sdCtx); err != nil {
return errors.Wrap(err, "failed to wait DUT to be unreachable")
}
return nil
}
// verifyECPowerState verifies EC powerState of DUT.
func verifyECPowerState(ctx context.Context, pxy *servo.Proxy, powerState string) error {
return testing.Poll(ctx, func(ctx context.Context) error {
got, err := pxy.Servo().GetECSystemPowerState(ctx)
if err != nil {
return errors.Wrap(err, "failed to get EC power state")
}
if want := powerState; got != want {
return errors.Errorf("unexpected DUT EC power state = got %q, want %q", got, want)
}
return nil
}, &testing.PollOptions{Timeout: 20 * time.Second})
}
// performReboot performs DUT reboot.
func performReboot(ctx context.Context, dut *dut.DUT) error {
if err := dut.Reboot(ctx); err != nil {
return errors.Wrap(err, "failed to reboot the DUT")
}
waitCtx, cancel := context.WithTimeout(ctx, 20*time.Second)
defer cancel()
if err := dut.WaitConnect(waitCtx); err != nil {
return errors.Wrap(err, "failed to wait connect to DUT")
}
return nil
}