blob: b8dfb6e19bc405d5effc80c0ee4f819fee1775f1 [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 power
import (
"context"
"regexp"
"time"
"github.com/golang/protobuf/ptypes/empty"
"chromiumos/tast/common/servo"
"chromiumos/tast/ctxutil"
"chromiumos/tast/errors"
"chromiumos/tast/remote/firmware/fixture"
"chromiumos/tast/rpc"
"chromiumos/tast/services/cros/security"
"chromiumos/tast/testing"
"chromiumos/tast/testing/hwdep"
)
func init() {
testing.AddTest(&testing.Test{
Func: DUTBehaviorOnACInsertionTabletMode,
LacrosStatus: testing.LacrosVariantUnneeded,
Desc: "Verifies that system comes back from sleep after AC insertion in tabletmode",
Contacts: []string{"pathan.jilani@intel.com", "intel-chrome-system-automation-team@intel.com"},
ServiceDeps: []string{"tast.cros.security.BootLockboxService"},
SoftwareDeps: []string{"chrome", "reboot"},
Vars: []string{"servo"},
HardwareDeps: hwdep.D(hwdep.ChromeEC()),
Timeout: 10 * time.Minute,
Fixture: fixture.NormalMode,
})
}
func DUTBehaviorOnACInsertionTabletMode(ctx context.Context, s *testing.State) {
ctxForCleanUp := ctx
ctx, cancel := ctxutil.Shorten(ctx, 30*time.Second)
defer cancel()
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 create config: ", err)
}
const (
// cmdTimeout is a short duration used for sending commands.
cmdTimeout = 3 * time.Second
)
c10PackageRe := regexp.MustCompile(`C10 : ([A-Za-z0-9]+)`)
const (
slpS0File = "/sys/kernel/debug/pmc_core/slp_s0_residency_usec"
pkgCstateFile = "/sys/kernel/debug/pmc_core/package_cstate_show"
)
// Get the initial tablet_mode_angle settings to restore at the end of test.
re := regexp.MustCompile(`tablet_mode_angle=(\d+) hys=(\d+)`)
out, 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(out)
if len(m) != 3 {
s.Fatalf("Failed to get initial tablet_mode_angle settings: got submatches %+v", m)
}
initLidAngle := m[1]
initHys := m[2]
// 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)
}
getChargerPollOptions := testing.PollOptions{
Timeout: 10 * time.Second,
Interval: 250 * time.Millisecond,
}
s.Log("Stopping power supply")
if err := h.SetDUTPower(ctx, false); err != nil {
s.Fatal("Failed to remove charger: ", err)
}
if err := testing.Poll(ctx, func(ctx context.Context) error {
if attached, err := h.Servo.GetChargerAttached(ctx); err != nil {
return err
} else if attached {
return errors.New("charger is still attached - use Servo V4 Type-C or supply RPM vars")
}
return nil
}, &getChargerPollOptions); err != nil {
s.Fatal("Check for charger failed: ", err)
}
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(ctxForCleanUp)
client := security.NewBootLockboxServiceClient(cl.Conn)
if _, err := client.NewChromeLogin(ctx, &empty.Empty{}); err != nil {
s.Fatal("Failed to start Chrome: ", err)
}
defer func(ctx context.Context) {
s.Log("Performing cleanup")
if !dut.Connected(ctx) {
if err := h.Servo.KeypressWithDuration(ctx, servo.PowerKey, servo.DurPress); err != nil {
s.Fatal("Failed to power normal press: ", err)
}
if err := dut.WaitConnect(ctx); err != nil {
s.Fatal("Failed to wait connect DUT: ", err)
}
}
if err := h.SetDUTPower(ctx, true); err != nil {
s.Fatal("Failed to connect charger: ", err)
}
if err := testing.Poll(ctx, func(ctx context.Context) error {
if attached, err := h.Servo.GetChargerAttached(ctx); err != nil {
return err
} else if !attached {
return errors.New("charger is not attached")
}
return nil
}, &getChargerPollOptions); err != nil {
s.Fatal("Check for charger failed: ", 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)
}
}(ctxForCleanUp)
cmdOutput := func(cmdFile string) string {
out, err := dut.Conn().CommandContext(ctx, "cat", cmdFile).Output()
if err != nil {
s.Fatalf("Failed to execute 'cat %s' command: %v", cmdFile, err)
}
return string(out)
}
slpOpSetPre := cmdOutput(slpS0File)
pkgOpSetOutput := cmdOutput(pkgCstateFile)
matchSetPre := c10PackageRe.FindStringSubmatch(pkgOpSetOutput)
if matchSetPre == nil {
s.Fatal("Failed to match pre PkgCstate value: ", pkgOpSetOutput)
}
pkgOpSetPre := matchSetPre[1]
powerOffCtx, cancel := context.WithTimeout(ctx, cmdTimeout)
defer cancel()
if err := h.DUT.Conn().CommandContext(powerOffCtx, "powerd_dbus_suspend").Run(); err != nil && !errors.Is(err, context.DeadlineExceeded) {
s.Fatal("Failed to power off DUT: ", err)
}
sdCtx, cancel := context.WithTimeout(ctx, 30*time.Second)
defer cancel()
if err := dut.WaitUnreachable(sdCtx); err != nil {
s.Fatal("Failed to wait for unreachable: ", err)
}
s.Log("Attaching power supply")
if err := h.SetDUTPower(ctx, true); err != nil {
s.Fatal("Failed to attach charger: ", err)
}
if err := testing.Poll(ctx, func(ctx context.Context) error {
if attached, err := h.Servo.GetChargerAttached(ctx); err != nil {
return err
} else if !attached {
return errors.New("charger is not attached")
}
return nil
}, &getChargerPollOptions); err != nil {
s.Fatal("Failed to attach charger: ", err)
}
waitCtx, cancel := context.WithTimeout(ctx, 20*time.Second)
defer cancel()
if err := dut.WaitConnect(waitCtx); err != nil {
s.Fatal("Failed to wait connect DUT at suspend state after insertion AC charger: ", err)
}
slpOpSetPost := cmdOutput(slpS0File)
if slpOpSetPre == slpOpSetPost {
s.Fatalf("Failed SLP counter value %q must be different than the value %q noted earlier", slpOpSetPost, slpOpSetPre)
}
if slpOpSetPost == "0" {
s.Fatalf("Failed SLP counter value = got %v, want non-zero", slpOpSetPost)
}
pkgOpSetPostOutput := cmdOutput(pkgCstateFile)
matchSetPost := c10PackageRe.FindStringSubmatch(pkgOpSetPostOutput)
if matchSetPost == nil {
s.Fatal("Failed to match post PkgCstate value: ", pkgOpSetPostOutput)
}
pkgOpSetPost := matchSetPost[1]
if pkgOpSetPre == pkgOpSetPost {
s.Fatalf("Failed Package C10 value %q must be different than the value %q noted earlier", pkgOpSetPost, pkgOpSetPre)
}
if pkgOpSetPost == "0x0" || pkgOpSetPost == "0" {
s.Fatalf("Failed Package C10 value = got %v, want non-zero", pkgOpSetPost)
}
}