blob: bea2f07e5ad7228c43ab5b974889ad5609f0a158 [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 firmware
import (
"context"
"strconv"
"strings"
"time"
"github.com/golang/protobuf/ptypes/empty"
"chromiumos/tast/common/servo"
"chromiumos/tast/dut"
"chromiumos/tast/errors"
"chromiumos/tast/remote/firmware/fixture"
"chromiumos/tast/rpc"
"chromiumos/tast/services/cros/ui"
"chromiumos/tast/testing"
"chromiumos/tast/testing/hwdep"
)
type shutdownModeTestParams struct {
shutdownmode string
}
const (
powerButton string = "powerbutton"
powerOff string = "poweroff"
)
func init() {
testing.AddTest(&testing.Test{
Func: ShutdownMode,
Desc: "Verifies that system comes back after power button press and poweroff",
Contacts: []string{"pathan.jilani@intel.com", "intel-chrome-system-automation-team@intel.com"},
ServiceDeps: []string{"tast.cros.ui.ScreenLockService"},
SoftwareDeps: []string{"chrome", "reboot"},
Vars: []string{"servo"},
Attr: []string{"group:firmware", "firmware_unstable"},
HardwareDeps: hwdep.D(hwdep.ChromeEC()),
Fixture: fixture.NormalMode,
Params: []testing.Param{{
Name: "power_button",
Val: shutdownModeTestParams{shutdownmode: powerButton},
}, {
Name: "poweroff_command",
Val: shutdownModeTestParams{shutdownmode: powerOff},
},
},
})
}
func ShutdownMode(ctx context.Context, s *testing.State) {
dut := s.DUT()
h := s.FixtValue().(*fixture.Value).Helper
if err := h.RequireServo(ctx); err != nil {
s.Fatal("Failed to connect to servo: ", err)
}
if err := h.RequireConfig(ctx); err != nil {
s.Fatal("Failed to connect to servo: ", err)
}
testOpt := s.Param().(shutdownModeTestParams)
const (
cmdTimeout = 3 * time.Second // cmdTimeout is a short duration used for sending commands.
powerStateInterval = 1 * time.Second
powerStateTimeout = 30 * time.Second
expectedPrevSleepState = 5 // expectedPrevSleepState is the expected previous sleep state value for coldboot.
)
// Logging into chrome.
chromeLogin := func() {
s.Log("Login to Chrome")
cl, err := rpc.Dial(ctx, s.DUT(), s.RPCHint())
if err != nil {
s.Fatal("Failed to connect to the RPC service on the DUT: ", err)
}
defer cl.Close(ctx)
screenLockService := ui.NewScreenLockServiceClient(cl.Conn)
if _, err := screenLockService.NewChrome(ctx, &empty.Empty{}); err != nil {
s.Fatal("Failed to login chrome: ", err)
}
defer screenLockService.CloseChrome(ctx, &empty.Empty{})
}
chromeLogin()
pwrOnDUT := func() {
s.Log("Power Normal Pressing")
if err := h.Servo.KeypressWithDuration(ctx, servo.PowerKey, servo.DurPress); err != nil {
s.Fatal("Failed to power button press: ", err)
}
waitCtx, cancel := context.WithTimeout(ctx, 2*time.Minute)
defer cancel()
if err := dut.WaitConnect(waitCtx); err != nil {
s.Log("Failed to wake up DUT. Retrying")
if err := h.Servo.KeypressWithDuration(ctx, servo.PowerKey, servo.DurPress); err != nil {
s.Fatal("Failed to power button press: ", err)
}
if err := dut.WaitConnect(waitCtx); err != nil {
s.Fatal("Failed to wait connect DUT: ", err)
}
}
}
defer func(ctx context.Context) {
if !dut.Connected(ctx) {
pwrOnDUT()
}
}(ctx)
if testOpt.shutdownmode == "powerbutton" {
if err := h.Servo.KeypressWithDuration(ctx, servo.PowerKey, servo.Dur(h.Config.HoldPwrButtonPowerOff)); err != nil {
s.Fatal("Failed to power button press: ", err)
}
if err := h.WaitForPowerStates(ctx, powerStateInterval, powerStateTimeout, "G3"); err != nil {
s.Fatal("Failed to get G3 powerstate: ", err)
}
pwrOnDUT()
chromeLogin()
if err := validateCbmemPrevSleepState(ctx, dut, expectedPrevSleepState); err != nil {
s.Fatalf("Failed Previous Sleep state is not %d after powerbutton long pressing via servo: %v", expectedPrevSleepState, err)
}
}
if testOpt.shutdownmode == "poweroff" {
powerOffCtx, cancel := context.WithTimeout(ctx, cmdTimeout)
defer cancel()
if err := h.DUT.Conn().CommandContext(powerOffCtx, "poweroff").Run(); err != nil && !errors.Is(err, context.DeadlineExceeded) {
s.Fatal("Failed to power off DUT: ", err)
}
if err := h.WaitForPowerStates(ctx, powerStateInterval, powerStateTimeout, "G3"); err != nil {
s.Fatal("Failed to get G3 powerstate: ", err)
}
pwrOnDUT()
if err := validateCbmemPrevSleepState(ctx, dut, expectedPrevSleepState); err != nil {
s.Fatalf("Failed Previous Sleep state is not %d after executing poweroff command: %v", expectedPrevSleepState, err)
}
haltCtx, cancel := context.WithTimeout(ctx, cmdTimeout)
defer cancel()
if err := h.DUT.Conn().CommandContext(haltCtx, "halt").Run(); err != nil && !errors.Is(err, context.DeadlineExceeded) {
s.Fatal("Failed to halt DUT: ", err)
}
if err := h.WaitForPowerStates(ctx, powerStateInterval, powerStateTimeout, "G3"); err != nil {
s.Fatal("Failed to get G3 powerstate: ", err)
}
pwrOnDUT()
if err := validateCbmemPrevSleepState(ctx, dut, expectedPrevSleepState); err != nil {
s.Fatalf("Failed Previous Sleep state is not %d after executing halt command: %v", expectedPrevSleepState, err)
}
}
}
// validateCbmemPrevSleepState sleep state from cbmem command output.
func validateCbmemPrevSleepState(ctx context.Context, dut *dut.DUT, sleepStateValue int) error {
const (
// Command to check previous sleep state.
prevSleepStateCmd = "cbmem -c | grep 'prev_sleep_state' | tail -1"
)
out, err := dut.Conn().CommandContext(ctx, "sh", "-c", prevSleepStateCmd).Output()
if err != nil {
return err
}
// Extract prevsleep state from example output "prev_sleep_state 5".
if count, err := strconv.Atoi(strings.Split(strings.Replace(string(out), "\n", "", -1), " ")[1]); err != nil {
return err
} else if count != sleepStateValue {
return errors.Errorf("previous sleep state must be %d", sleepStateValue)
}
return nil
}