blob: 0275df4d52d4a810fbb64194b1a917f4b08fd25c [file]
// 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 ui
import (
"context"
"time"
"github.com/golang/protobuf/ptypes/empty"
"google.golang.org/grpc"
"chromiumos/tast/errors"
"chromiumos/tast/local/chrome"
"chromiumos/tast/local/chrome/ash/ashproc"
"chromiumos/tast/local/chrome/uiauto"
"chromiumos/tast/local/chrome/uiauto/nodewith"
"chromiumos/tast/local/procutil"
pb "chromiumos/tast/services/cros/ui"
"chromiumos/tast/testing"
)
func init() {
testing.AddService(&testing.Service{
Register: func(srv *grpc.Server, s *testing.ServiceState) {
pb.RegisterPowerMenuServiceServer(srv, &PowerMenuService{s: s})
},
})
}
// PowerMenuService implements tast.cros.ui.PowerMenuService.
type PowerMenuService struct {
s *testing.ServiceState
cr *chrome.Chrome
loginRequested bool
tconn *chrome.TestConn
}
func (p *PowerMenuService) NewChrome(ctx context.Context, req *pb.NewChromeRequest) (*empty.Empty, error) {
if p.cr != nil {
return nil, errors.New("Chrome already available")
}
oldproc, err := ashproc.Root()
if err != nil {
return nil, errors.Wrap(err, "error getting Chrome root PID")
}
p.loginRequested = req.Login
if p.loginRequested {
p.cr, err = chrome.New(ctx, chrome.Region("us"))
} else {
p.cr, err = chrome.New(ctx, chrome.Region("us"), chrome.KeepState(), chrome.NoLogin(), chrome.LoadSigninProfileExtension(req.Key))
}
if err != nil {
return nil, err
}
// Make sure older chrome is gone.
if err := procutil.WaitForTerminated(ctx, oldproc, 30*time.Second); err != nil {
return nil, errors.Wrap(err, "chrome is not terminated")
}
// Then, wait for the new chrome processes.
if _, err := ashproc.WaitForRoot(ctx, 30*time.Second); err != nil {
return nil, errors.Wrap(err, "chrome is not restarted")
}
return &empty.Empty{}, nil
}
func (p *PowerMenuService) CloseChrome(ctx context.Context, req *empty.Empty) (*empty.Empty, error) {
if p.cr == nil {
return nil, errors.New("Chrome not available")
}
err := p.cr.Close(ctx)
p.cr = nil
return &empty.Empty{}, err
}
func (p *PowerMenuService) PowerMenuPresent(ctx context.Context, req *empty.Empty) (*pb.PowerMenuPresentResponse, error) {
if p.cr == nil {
return nil, errors.New("Chrome not available")
}
var err error
if p.loginRequested {
p.tconn, err = p.cr.TestAPIConn(ctx)
} else {
p.tconn, err = p.cr.SigninProfileTestAPIConn(ctx)
}
if err != nil {
return nil, err
}
// Check if the power menu is displayed
finder := nodewith.ClassName("PowerButtonMenuView").Onscreen().First()
exists, err := uiauto.New(p.tconn).IsNodeFound(ctx, finder)
if err != nil {
return nil, errors.Wrap(err, "failed to find power menu")
}
return &pb.PowerMenuPresentResponse{IsMenuPresent: exists}, nil
}
// PowerMenuItem reads from the power menu.
// Checking PowerMenuPresent is required prior to calling PowerMenuItem.
func (p *PowerMenuService) PowerMenuItem(ctx context.Context, req *empty.Empty) (*pb.PowerMenuItemResponse, error) {
if p.cr == nil {
return nil, errors.New("Chrome not available")
}
if p.tconn == nil {
return nil, errors.New("Test API connection not available")
}
ui := uiauto.New(p.tconn)
menu := nodewith.ClassName("PowerButtonMenuItemView")
menuItems, err := ui.NodesInfo(ctx, menu)
if err != nil {
return nil, err
}
var itemsName []string
for _, val := range menuItems {
itemsName = append(itemsName, val.Name)
}
return &pb.PowerMenuItemResponse{MenuItems: itemsName}, nil
}