blob: 69b1f2ab0ebe3aeaf3bbf824866ff2ac98d788aa [file]
// 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 networkui
import (
"context"
"strings"
"time"
"go.chromium.org/tast-tests/cros/common/tbdep"
"google.golang.org/protobuf/types/known/emptypb"
"go.chromium.org/tast-tests/cros/common/network/protoutil"
"go.chromium.org/tast-tests/cros/common/shillconst"
"go.chromium.org/tast-tests/cros/remote/wificell"
"go.chromium.org/tast-tests/cros/services/cros/chrome/uiauto/ossettings"
"go.chromium.org/tast-tests/cros/services/cros/chrome/uiauto/quicksettings"
"go.chromium.org/tast-tests/cros/services/cros/ui"
"go.chromium.org/tast-tests/cros/services/cros/wifi"
"go.chromium.org/tast/core/ctxutil"
"go.chromium.org/tast/core/errors"
"go.chromium.org/tast/core/rpc"
"go.chromium.org/tast/core/testing"
)
func init() {
testing.AddTest(&testing.Test{
Func: EthernetIsPreferred,
LifeCycleStage: testing.LifeCycleOwnerMonitored,
LacrosStatus: testing.LacrosVariantUnneeded,
Desc: "Verify connection preference is for Ethernet when both wired and wireless networks are available",
Contacts: []string{
"cros-connectivity@google.com",
"chromeos-connectivity-engprod@google.com",
"shijinabraham@google.com",
"chadduffin@chromium.org",
},
BugComponent: "b:1318544", // ChromeOS > Software > System Services > Connectivity > General
Attr: []string{"group:wificell", "wificell_e2e"},
TestBedDeps: []string{tbdep.Wificell, tbdep.WifiStateNormal, tbdep.BluetoothStateNormal, tbdep.PeripheralWifiStateWorking},
ServiceDeps: []string{
wificell.WifiUIServiceName,
wificell.OsSettingsServiceName,
wificell.QuickSettingsServiceName,
wificell.AutomationServiceName,
wificell.BrowserChromeServiceName,
wificell.ShillServiceName,
},
SoftwareDeps: []string{"chrome"},
Fixture: wificell.FixtureID(wificell.TFFeaturesWithUI),
Timeout: 3 * time.Minute,
})
}
// EthernetIsPreferred verifies connection preference is for Ethernet when both
// wired and wireless networks are available.
func EthernetIsPreferred(ctx context.Context, s *testing.State) {
tf := s.FixtValue().(*wificell.TestFixture)
cleanupCtx := ctx
ctx, cancel := ctxutil.Shorten(ctx, 10*time.Second)
defer cancel()
rpcClient := tf.DUTRPC(wificell.DefaultDUT)
crSvc := ui.NewChromeServiceClient(rpcClient.Conn)
if _, err := crSvc.New(ctx, &ui.NewRequest{}); err != nil {
s.Fatal("Failed to start Chrome: ", err)
}
defer crSvc.Close(cleanupCtx, &emptypb.Empty{})
ap, err := tf.DefaultOpenNetworkAP(ctx)
if err != nil {
s.Fatal("Failed to configure the AP: ", err)
}
cleanupCtx = ctx
ctx, cancel = tf.ReserveForDeconfigAP(ctx, ap)
defer cancel()
defer tf.DeconfigAP(cleanupCtx, ap)
if _, err := tf.ConnectWifiAPFromDUT(ctx, wificell.DefaultDUT, ap); err != nil {
s.Fatal("Failed to connect to the AP: ", err)
}
if err := checkOSSettings(ctx, rpcClient, ap /* WiFi AP interface */); err != nil {
s.Fatal(`Failed to verify networks are shown as "Connected" in OS-Settings: `, err)
}
if err := checkQuickSettings(ctx, rpcClient, "Ethernet" /* network name */); err != nil {
s.Fatal(`Failed to verify networks are shown as "Connected" in Quick Settings: `, err)
}
if err := checkQuickSettings(ctx, rpcClient, ap.Config().SSID /* network name */); err != nil {
s.Fatal(`Failed to verify networks are shown as "Connected" in Quick Settings: `, err)
}
// Getting the shill properties for later use.
shillSvc := wifi.NewShillServiceClient(rpcClient.Conn)
resp, err := shillSvc.GetProperties(ctx, &wifi.GetPropertiesRequest{
PropertyNames: []string{
shillconst.ManagerPropertyDefaultService,
shillconst.ManagerPropertyDefaultTechnology,
},
})
if err != nil {
s.Fatal("Failed to get shill properties: ", err)
}
const expectedServiceName = "Ethernet"
const expectedDefaultTechnology = "ethernet"
for name, value := range resp.GetProps() {
val, err := protoutil.FromShillVal(value)
if err != nil {
s.Fatal("Failed to decode shill value: ", err)
}
propertyValue, ok := val.(string)
if !ok {
s.Fatal("Failed to decode shill value: unexpected property value")
}
switch name {
case shillconst.ManagerPropertyDefaultService:
servicePath := propertyValue
// Using the service path of the default service to retrieve and verify the name of this service.
serviceName, err := getServiceName(ctx, rpcClient, servicePath)
if err != nil {
s.Fatal("Failed to get the name of default service: ", err)
}
if serviceName != expectedServiceName {
s.Fatal("Failed to verify Ethernet is preferred: ", err)
}
case shillconst.ManagerPropertyDefaultTechnology:
// Verifying the default technology is the expected one.
if propertyValue != expectedDefaultTechnology {
s.Fatalf("Failed to verify Ethernet is preferred: unexpected default technology; got: %q, want: %q", propertyValue, expectedDefaultTechnology)
}
default:
s.Fatalf("Failed to verify Ethernet is preferred: unrecognized property %q", name)
}
}
}
// checkOSSettings performs UI verifications, checks Ethernet and the
// specified Wi-Fi network |ap| are shown as "Connected" in the OS-Settings.
func checkOSSettings(ctx context.Context, rpcClient *rpc.Client, ap *wificell.APIface) error {
cleanupCtx := ctx
ctx, cancel := ctxutil.Shorten(ctx, 10*time.Second)
defer cancel()
wifiSvc := wifi.NewWifiServiceClient(rpcClient.Conn)
if _, err := wifiSvc.WifiPageControl(ctx, &wifi.WifiPageControlRequest{
Ssid: ap.Config().SSID,
Control: wifi.WifiPageControlRequest_WaitUntilConnected,
}); err != nil {
return errors.Wrap(err, `failed to verify Wi-Fi is shown as "Connected" in the OS-Settings`)
}
settingsSvc := ossettings.NewOsSettingsServiceClient(rpcClient.Conn)
if _, err := settingsSvc.OpenNetworkDetailPage(ctx, &ossettings.OpenNetworkDetailPageRequest{
NetworkName: "Ethernet",
NetworkType: ossettings.OpenNetworkDetailPageRequest_ETHERNET,
}); err != nil {
return errors.Wrap(err, "failed to open the detail page of Ethernet")
}
defer settingsSvc.Close(cleanupCtx, &emptypb.Empty{})
// Using JS expression to narrow down the target node to under the
// OS-Settings without specifying the root UI node of the OS-Settings.
resp, err := settingsSvc.EvalJSWithShadowPiercer(ctx, &ossettings.EvalJSWithShadowPiercerRequest{
Expression: `var node = shadowPiercingQuery("#networkState");
if (node == undefined) {
throw new Error("network state node not found");
}
node.innerText;`,
})
if err != nil {
return errors.Wrap(err, "failed to acquire the connect state of Ethernet")
}
val := resp.GetStringValue()
if !strings.Contains(val, "Connected") || strings.Contains(val, "Not Connected") {
return errors.Wrapf(err, `failed to verify Ethernet is shown as "Connected"; got: %q, want: "Connected"`, val)
}
return nil
}
// checkQuickSettings performs UI verifications, checks the
// specified network is shown as "Connected" in the Quick Settings.
func checkQuickSettings(ctx context.Context, rpcClient *rpc.Client, networkName string) error {
cleanupCtx := ctx
ctx, cancel := ctxutil.Shorten(ctx, 10*time.Second)
defer cancel()
quicksettingsSvc := quicksettings.NewQuickSettingsServiceClient(rpcClient.Conn)
if _, err := quicksettingsSvc.NavigateToNetworkDetailedView(ctx, &emptypb.Empty{}); err != nil {
return errors.Wrap(err, "failed to launch Quick Settings at network detail view")
}
defer quicksettingsSvc.Hide(cleanupCtx, &emptypb.Empty{})
uiSvc := ui.NewAutomationServiceClient(rpcClient.Conn)
// The label indicates the connect state, could be "Connected" or "Connected, no internet".
connectedLabel := ui.Node().Role(ui.Role_ROLE_STATIC_TEXT).HasClass("UnfocusableLabel").NameContaining("Connected")
networkItem := ui.Node().Role(ui.Role_ROLE_BUTTON).HasClass("NetworkListNetworkItemView").NameContaining(networkName).Finder()
// Finding the node falls under the specified network.
target := connectedLabel.Ancestor(networkItem).Finder()
if _, err := uiSvc.WaitUntilExists(ctx, &ui.WaitUntilExistsRequest{Finder: target}); err != nil {
return errors.Wrap(err, `failed to verify Ethernet is shown as "Connected" in Quick Settings`)
}
return nil
}
// getServiceName gets the name of the specified service |servicePath|.
func getServiceName(ctx context.Context, rpcClient *rpc.Client, servicePath string) (string, error) {
shillSvc := wifi.NewShillServiceClient(rpcClient.Conn)
resp, err := shillSvc.GetProperties(ctx, &wifi.GetPropertiesRequest{
ServicePath: &servicePath,
PropertyNames: []string{shillconst.ServicePropertyName},
})
if err != nil {
return "", errors.Wrapf(err, "failed to get properties of service %q", servicePath)
}
val, err := protoutil.FromShillVal(resp.GetProps()[shillconst.ServicePropertyName])
if err != nil {
return "", errors.Wrap(err, "failed to decode shill value")
}
serviceName, ok := val.(string)
if !ok {
return "", errors.New("unexpected property value")
}
return serviceName, nil
}