blob: 9153001ed8d6d1b0be6577833d0c92d3c13db5c2 [file] [log] [blame]
// Copyright 2023 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package ui
import (
"context"
"time"
"github.com/golang/protobuf/ptypes/empty"
"google.golang.org/grpc"
"go.chromium.org/tast-tests/cros/remote/crosserverutil"
pb "go.chromium.org/tast-tests/cros/services/cros/ui"
"go.chromium.org/tast/core/errors"
"go.chromium.org/tast/core/testing"
)
func init() {
testing.AddTest(&testing.Test{
Func: ConnServiceNewConnForTargetAfterSuspendGRPC,
Desc: "Test re-establishing chrome connection through services after power suspension",
Contacts: []string{"chromeos-sw-engprod@google.com", "jonfan@google.com"},
BugComponent: "b:1034649",
Attr: []string{"group:mainline", "group:hw_agnostic", "informational"},
SoftwareDeps: []string{"chrome"},
LacrosStatus: testing.LacrosVariantUnneeded,
})
}
// ConnServiceNewConnForTargetAfterSuspendGRPC tests ConnService's ability to re-establish
// chrome connection after power suspension.
func ConnServiceNewConnForTargetAfterSuspendGRPC(ctx context.Context, s *testing.State) {
targetIds := createNewConns(ctx, s)
runPowerSuspend(ctx, s)
verifyNewConnForTarget(ctx, s, targetIds)
}
// createNewConns creates new Chrome connections and return connection targetIds.
func createNewConns(ctx context.Context, s *testing.State) (targetIds []string) {
numConns := 3
url := "about:blank"
targetIds = make([]string, numConns)
cl, err := crosserverutil.GetGRPCClient(ctx, s.DUT())
if err != nil {
s.Fatal("Failed to connect to the GRPC server on the DUT: ", err)
}
defer cl.Close(ctx)
// Start Chrome on the DUT.
cs := pb.NewChromeServiceClient(cl.Conn)
loginReq := &pb.NewRequest{}
if _, err := cs.New(ctx, loginReq, grpc.WaitForReady(true)); err != nil {
s.Fatal("Failed to start Chrome: ", err)
}
// Create new Chrome connections pointing to the same url.
svc := pb.NewConnServiceClient(cl.Conn)
for i := 0; i < numConns; i++ {
res, err := svc.NewConn(ctx, &pb.NewConnRequest{Url: url})
if err != nil {
s.Fatalf("Failed to open page %v: %v", url, err)
}
targetIds[i] = res.TargetId
}
return targetIds
}
// runPowerSuspend executes power suspension and ensures that the DUT can be reached after DUT wakes up.
func runPowerSuspend(ctx context.Context, s *testing.State) {
testing.ContextLog(ctx, "Before power suspension")
cmd := s.DUT().Conn().CommandContext(ctx, "powerd_dbus_suspend", "--delay=0", "--suspend_for_sec=3")
// Power suspension breaks the SSH connection which sometimes causes error for
// powerd_dbus_suspend executed over SSH if it is executed as a single blocking call.
// So we only check if the command is successfully triggered but doesn't
// check the error code for completion.
if err := cmd.Start(); err != nil {
s.Fatal("Failed to suspend: ", err)
}
cmd.Wait()
testing.ContextLog(ctx, "After power suspension")
// Re-establish SSH connection after power suspension.
if err := testing.Poll(ctx, func(ctx context.Context) error {
if s.DUT().Connected(ctx) {
testing.ContextLog(ctx, "DUT is connected through SSH")
return nil
}
if err := s.DUT().Connect(ctx); err != nil {
return errors.New("failed to create SSH connection")
}
return errors.New("succeeded in creating SSH connection")
}, &testing.PollOptions{Timeout: 30 * time.Second}); err != nil {
s.Fatal("Failed to re-establish SSH connection after power suspension: ", err)
}
}
// verifyNewConnForTarget ensures that NewConnForTarget can access existing chrome connections through targetIds.
func verifyNewConnForTarget(ctx context.Context, s *testing.State, targetIds []string) {
cl, err := crosserverutil.GetGRPCClient(ctx, s.DUT())
if err != nil {
s.Fatal("Failed to connect to the GRPC server on the DUT: ", err)
}
defer cl.Close(ctx)
cs := pb.NewChromeServiceClient(cl.Conn)
svc := pb.NewConnServiceClient(cl.Conn)
testing.ContextLog(ctx, "Reconnecting to Chrome")
loginReq := &pb.NewRequest{TryReuseSession: true, KeepState: true}
if _, err := cs.New(ctx, loginReq, grpc.WaitForReady(true)); err != nil {
s.Fatal("Failed to start Chrome: ", err)
}
if _, err := cs.Reconnect(ctx, &empty.Empty{}, grpc.WaitForReady(true)); err != nil {
s.Fatal("Failed to reconnect to Chrome after power suspension: ", err)
}
for _, targetID := range targetIds {
newConn, err := svc.NewConnForTarget(ctx, &pb.NewConnForTargetRequest{TargetId: targetID})
if err != nil {
s.Fatalf("Failed when calling NewConnForTargetRequest for %v: %v", targetID, err)
}
activateTargetRequest := &pb.ActivateTargetRequest{
Id: newConn.Id,
}
_, err = svc.ActivateTarget(ctx, activateTargetRequest)
if err != nil {
s.Fatalf("Failed when calling ActivateTarget for %v: %v", activateTargetRequest, err)
}
}
}