blob: 50db4e99d671bb3701790ceb9c153a6c4aa73d8e [file] [log] [blame]
// Copyright 2020 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 arc
import (
"context"
"math"
"time"
"chromiumos/tast/ctxutil"
"chromiumos/tast/errors"
"chromiumos/tast/local/android/ui"
"chromiumos/tast/local/arc"
"chromiumos/tast/local/bundles/cros/arc/wm"
"chromiumos/tast/local/chrome"
"chromiumos/tast/local/chrome/ash"
"chromiumos/tast/local/chrome/display"
"chromiumos/tast/local/coords"
"chromiumos/tast/testing"
)
func init() {
testing.AddTest(&testing.Test{
Func: WMNonresizableClamshell,
Desc: "Verifies that Window Manager non-resizable clamshell use-cases behave as described in go/arc-wm-r",
Contacts: []string{"armenk@google.com", "arc-framework+tast@google.com"},
Attr: []string{"group:mainline", "informational"},
SoftwareDeps: []string{"android_vm", "chrome"},
Pre: arc.Booted(),
Timeout: 8 * time.Minute,
})
}
func WMNonresizableClamshell(ctx context.Context, s *testing.State) {
wm.SetupAndRunTestCases(ctx, s, false, []wm.TestCase{
wm.TestCase{
// non-resizable/clamshell: default launch behavior
Name: "NC_default_launch_behavior",
Func: wmNC01,
},
wm.TestCase{
// non-resizable/clamshell: user immerse portrait app (pillarbox)
Name: "NC_user_immerse_portrait",
Func: wmNC04,
},
wm.TestCase{
// non-resizable/clamshell: user immerse non-portrait app
Name: "NC_user_immerse_non_portrait",
Func: wmNC05,
},
wm.TestCase{
// non-resizable/clamshell: immerse via API from maximized
Name: "NC_immerse_via_API_from_maximized",
Func: wmNC07,
},
wm.TestCase{
// non-resizable/clamshell: new activity follows root activity
Name: "NC_new_activity_follows_root_activity",
Func: wmNC09,
},
wm.TestCase{
// non-resizable/clamshell: new activity replaces root activity
Name: "NC_new_activity_replaces_root_activity",
Func: wmNC10,
},
wm.TestCase{
// non-resizable/clamshell: hide shelf when app maximized
Name: "NC_hide_shelf_app_max",
Func: wmNC12,
},
wm.TestCase{
// non-resizable/clamshell: display size change
Name: "NC_display_size_change",
Func: wmNC15,
},
wm.TestCase{
// non-resizable/clamshell: font size change
Name: "NC_font_size_change",
Func: wmNC17,
},
})
}
// wmNC01 covers non-resizable/clamshell default launch behavior.
// Expected behavior is defined in: go/arc-wm-r NC01: non-resizable/clamshell: default launch behavior.
func wmNC01(ctx context.Context, tconn *chrome.TestConn, a *arc.ARC, d *ui.Device) error {
for _, activityName := range []string{
wm.NonResizablePortraitActivity,
wm.NonResizableLandscapeActivity,
} {
if err := func() error {
act, err := arc.NewActivity(a, wm.Pkg24, activityName)
if err != nil {
return err
}
defer act.Close()
if err := act.Start(ctx, tconn); err != nil {
return err
}
defer act.Stop(ctx, tconn)
if err := wm.WaitUntilActivityIsReady(ctx, tconn, act, d); err != nil {
return err
}
return wm.CheckMaximizeNonResizable(ctx, tconn, act, d)
}(); err != nil {
return errors.Wrapf(err, "%q test failed", activityName)
}
}
return nil
}
// wmNC04 covers non-resizable/clamshell: user immerse portrait app (pillarbox) behavior.
// Expected behavior is defined in: go/arc-wm-r NC04: non-resizable/clamshell: user immerse portrait app (pillarbox).
func wmNC04(ctx context.Context, tconn *chrome.TestConn, a *arc.ARC, d *ui.Device) error {
return checkMaxActivityToFullscreen(ctx, tconn, a, d, wm.NonResizablePortraitActivity)
}
// wmNC05 covers non-resizable/clamshell: user immerse non-portrait app behavior.
// Expected behavior is defined in: go/arc-wm-r NC05: non-resizable/clamshell: user immerse non-portrait app.
func wmNC05(ctx context.Context, tconn *chrome.TestConn, a *arc.ARC, d *ui.Device) error {
for _, activityName := range []string{
wm.NonResizableLandscapeActivity,
wm.NonResizableUnspecifiedActivity,
} {
if err := checkMaxActivityToFullscreen(ctx, tconn, a, d, activityName); err != nil {
return err
}
}
return nil
}
// wmNC07 covers non-resizable/clamshell: immerse via API from maximized.
// Expected behavior is defined in: go/arc-wm-r NC07: non-resizable/clamshell: immerse via API from maximized.
func wmNC07(ctx context.Context, tconn *chrome.TestConn, a *arc.ARC, d *ui.Device) error {
// Start a new activity.
act, err := arc.NewActivity(a, wm.Pkg24, wm.NonResizablePortraitActivity)
if err != nil {
return err
}
defer act.Close()
if err := act.Start(ctx, tconn); err != nil {
return err
}
defer act.Stop(ctx, tconn)
if err := wm.WaitUntilActivityIsReady(ctx, tconn, act, d); err != nil {
return err
}
// Get window info before clicking on the immersive button.
windowInfoBefore, err := ash.GetARCAppWindowInfo(ctx, tconn, wm.Pkg24)
if err != nil {
return err
}
// Click on the immersive button.
if err := wm.UIClickImmersive(ctx, act, d); err != nil {
return err
}
if err := wm.WaitUntilActivityIsReady(ctx, tconn, act, d); err != nil {
return err
}
// Get window info after the immersive button is clicked.
windowInfoUIImmersive, err := ash.GetARCAppWindowInfo(ctx, tconn, wm.Pkg24)
if err != nil {
return err
}
if err := ash.WaitWindowFinishAnimating(ctx, tconn, windowInfoBefore.ID); err != nil {
return err
}
if err := wm.CheckMaximizeToFullscreenToggle(ctx, tconn, windowInfoBefore.TargetBounds, *windowInfoUIImmersive); err != nil {
return err
}
// Click on the normal button.
if err := wm.UIClickNormal(ctx, act, d); err != nil {
return err
}
if err := wm.WaitUntilActivityIsReady(ctx, tconn, act, d); err != nil {
return err
}
if err := ash.WaitWindowFinishAnimating(ctx, tconn, windowInfoBefore.ID); err != nil {
return err
}
windowInfoAfter, err := ash.GetARCAppWindowInfo(ctx, tconn, wm.Pkg24)
if err != nil {
return err
}
if windowInfoBefore.BoundsInRoot != windowInfoAfter.BoundsInRoot {
return errors.Errorf("invalid window bounds after click on the immersive button, got: %q, want: %q", windowInfoAfter.BoundsInRoot, windowInfoBefore.BoundsInRoot)
}
return nil
}
// wmNC09 covers non-resizable/clamshell: new activity follows root activity.
// Expected behavior is defined in: go/arc-wm-r NC09: resizable/clamshell: new activity follows root activity.
func wmNC09(ctx context.Context, tconn *chrome.TestConn, a *arc.ARC, d *ui.Device) error {
// Start the activity
act, err := arc.NewActivity(a, wm.Pkg24, wm.NonResizableUnspecifiedActivity)
if err != nil {
return err
}
defer act.Close()
if err := act.Start(ctx, tconn); err != nil {
return err
}
defer act.Stop(ctx, tconn)
if err := wm.WaitUntilActivityIsReady(ctx, tconn, act, d); err != nil {
return err
}
// Get the root window info so it could be compared with child window.
rootWindowInfo, err := ash.GetARCAppWindowInfo(ctx, tconn, wm.Pkg24)
if err != nil {
return err
}
if err := wm.UIClickLaunchActivity(ctx, act, d); err != nil {
return err
}
if err := wm.WaitUntilActivityIsReady(ctx, tconn, act, d); err != nil {
return err
}
if nrActivities, err := wm.UINumberActivities(ctx, act, d); err != nil {
return err
} else if nrActivities != 2 {
return errors.Errorf("invalid number of activities: got %d; want 2", nrActivities)
}
childWindowInfo, err := ash.GetARCAppWindowInfo(ctx, tconn, wm.Pkg24)
if err != nil {
return err
}
if rootWindowInfo.BoundsInRoot != childWindowInfo.BoundsInRoot {
return errors.Errorf("invalid child activity window bounds, got: %q, want: %q", childWindowInfo.BoundsInRoot, rootWindowInfo.BoundsInRoot)
}
return nil
}
// wmNC10 covers non-resizable/clamshell: new activity replaces root activity.
// Expected behavior is defined in: go/arc-wm-r NC10: non-resizable/clamshell: new activity replaces root activity.
func wmNC10(ctx context.Context, tconn *chrome.TestConn, a *arc.ARC, d *ui.Device) error {
// Start the activity.
act, err := arc.NewActivity(a, wm.Pkg24, wm.NonResizableUnspecifiedActivity)
if err != nil {
return err
}
defer act.Close()
if err := act.Start(ctx, tconn); err != nil {
return err
}
defer act.Stop(ctx, tconn)
if err := wm.WaitUntilActivityIsReady(ctx, tconn, act, d); err != nil {
return err
}
// Get the root window info so it could be compared with child window.
rootWindowInfo, err := ash.GetARCAppWindowInfo(ctx, tconn, wm.Pkg24)
if err != nil {
return err
}
if err := wm.UIClickRootActivity(ctx, act, d); err != nil {
return err
}
if err := wm.UIClickLaunchActivity(ctx, act, d); err != nil {
return err
}
if err := wm.WaitUntilActivityIsReady(ctx, tconn, act, d); err != nil {
return err
}
if nrActivities, err := wm.UINumberActivities(ctx, act, d); err != nil {
return err
} else if nrActivities != 1 {
return errors.Errorf("invalid number of activities: got %d; want 1", nrActivities)
}
childWindowInfo, err := ash.GetARCAppWindowInfo(ctx, tconn, wm.Pkg24)
if err != nil {
return err
}
if rootWindowInfo.BoundsInRoot != childWindowInfo.BoundsInRoot {
return errors.Errorf("invalid child activity window bounds, got: %q, want: %q", childWindowInfo.BoundsInRoot, rootWindowInfo.BoundsInRoot)
}
return nil
}
// wmNC12 covers non-resizable/clamshell: hide shelf when app maximized.
// Expected behavior is defined in: go/arc-wm-r NC12: non-resizable/clamshell: hide shelf when app maximized.
func wmNC12(ctx context.Context, tconn *chrome.TestConn, a *arc.ARC, d *ui.Device) error {
act, err := arc.NewActivity(a, wm.Pkg24, wm.NonResizableUnspecifiedActivity)
if err != nil {
return err
}
defer act.Close()
// Get primary display info to set shelf behavior.
primaryDisplayInfo, err := display.GetPrimaryInfo(ctx, tconn)
if err != nil {
return err
}
if primaryDisplayInfo == nil {
return errors.New("failed to find primary display info")
}
// Get initial shelf behavior.
initSB, err := ash.GetShelfBehavior(ctx, tconn, primaryDisplayInfo.ID)
if err != nil {
return err
}
if initSB != ash.ShelfBehaviorNeverAutoHide {
// Set shelf behavior to never auto hide for test's initial state.
if err := ash.SetShelfBehavior(ctx, tconn, primaryDisplayInfo.ID, ash.ShelfBehaviorNeverAutoHide); err != nil {
return err
}
}
// Start the activity.
if err := act.Start(ctx, tconn); err != nil {
return err
}
defer act.Stop(ctx, tconn)
if err := wm.WaitUntilActivityIsReady(ctx, tconn, act, d); err != nil {
return err
}
if err := wm.CheckMaximizeNonResizable(ctx, tconn, act, d); err != nil {
return err
}
// Store initial window info to compare with after hiding and showing the shelf.
winInfoInitialState, err := ash.GetARCAppWindowInfo(ctx, tconn, wm.Pkg24)
if err != nil {
return err
}
// Set shelf behavior to auto hide.
if err := ash.SetShelfBehavior(ctx, tconn, primaryDisplayInfo.ID, ash.ShelfBehaviorAlwaysAutoHide); err != nil {
return err
}
// Wait for shelf animation to complete.
if err := wm.WaitForShelfAnimationComplete(ctx, tconn); err != nil {
return errors.Wrap(err, "failed to wait for shelf animation to complete")
}
winInfoShelfHidden, err := ash.GetARCAppWindowInfo(ctx, tconn, wm.Pkg24)
if err != nil {
return err
}
// Compare window bounds before and after hiding the shelf. It should be larger when shelf is hidden.
if winInfoShelfHidden.BoundsInRoot.Height <= winInfoInitialState.BoundsInRoot.Height {
return errors.Errorf("invalid window bounds when shelf is shown, got: %s, want smaller than: %s", winInfoInitialState.BoundsInRoot, winInfoShelfHidden.BoundsInRoot)
}
// Show the shelf.
if err := ash.SetShelfBehavior(ctx, tconn, primaryDisplayInfo.ID, ash.ShelfBehaviorNeverAutoHide); err != nil {
return err
}
// Wait for shelf animation to complete.
if err := wm.WaitForShelfAnimationComplete(ctx, tconn); err != nil {
return errors.Wrap(err, "failed to wait for shelf animation to complete")
}
if err := wm.CheckMaximizeNonResizable(ctx, tconn, act, d); err != nil {
return err
}
winInfoShelfReShown, err := ash.GetARCAppWindowInfo(ctx, tconn, wm.Pkg24)
if err != nil {
return err
}
// Compare window bounds after showing the shelf with initial bounds. They should be equal.
if winInfoInitialState.BoundsInRoot != winInfoShelfReShown.BoundsInRoot {
return errors.Errorf("invalid window bounds after hiding and showing the shelf, got: %s, want: %s", winInfoShelfReShown.BoundsInRoot, winInfoInitialState.BoundsInRoot)
}
return nil
}
// wmNC15 covers non-resizable/clamshell: display size change.
// Expected behavior is defined in: go/arc-wm-r NC15: non-resizable/clamshell: display size change.
func wmNC15(ctx context.Context, tconn *chrome.TestConn, a *arc.ARC, d *ui.Device) error {
for _, actName := range []string{
wm.NonResizableLandscapeActivity,
wm.NonResizablePortraitActivity,
wm.NonResizableUnspecifiedActivity,
} {
if err := ncDisplaySizeChangeTestsHelper(ctx, tconn, a, d, actName); err != nil {
return err
}
}
return nil
}
// wmNC17 covers non-resizable/clamshell: font size change.
// Expected behavior is defined in: go/arc-wm-r NC17: non-resizable/clamshell: font size change.
func wmNC17(ctx context.Context, tconn *chrome.TestConn, a *arc.ARC, d *ui.Device) (retErr error) {
timeReservedForStop := 750 * time.Millisecond
timeReservedForCleanup := 750 * time.Millisecond
// Font scale, this const must not be 1.
const fsc = 1.2
// Save time for stopping the activity.
ctxForStopActivity := ctx
ctx, cancelForStopActivity := ctxutil.Shorten(ctx, timeReservedForStop)
defer cancelForStopActivity()
ctxForCleanup := ctx
ctx, cancelForCleanup := ctxutil.Shorten(ctx, timeReservedForCleanup)
defer cancelForCleanup()
// Start a new activity.
act, err := arc.NewActivity(a, wm.Pkg24, wm.NonResizableUnspecifiedActivity)
if err != nil {
return errors.Wrap(err, "unable to create new activity")
}
defer act.Close()
if err := act.Start(ctx, tconn); err != nil {
return errors.Wrap(err, "unable to start new activity")
}
defer func() {
if err := act.Stop(ctxForStopActivity, tconn); err != nil {
if retErr == nil {
retErr = errors.Wrap(err, "unable to stop activity")
} else {
testing.ContextLog(ctx, "Unable to stop activity")
}
}
}()
if err := wm.WaitUntilActivityIsReady(ctx, tconn, act, d); err != nil {
return errors.Wrap(err, "unable to wait until activity is ready")
}
// Store original window info.
owInfo, err := ash.GetARCAppWindowInfo(ctx, tconn, wm.Pkg24)
if err != nil {
return errors.Wrap(err, "unable to get arc app window info")
}
// Change the font scale.
if err := wm.EnsureARCFontScaleChanged(ctx, a, fsc); err != nil {
return errors.Wrap(err, "unable to set font scale")
}
defer func() {
if err := wm.EnsureARCFontScaleChanged(ctxForCleanup, a, 1); err != nil {
if retErr == nil {
retErr = errors.Wrap(err, "unable to clean up font scale changes")
} else {
testing.ContextLog(ctx, "Unlable to clean up font scale changes")
}
}
}()
// Get the font scale.
nfs, err := wm.GetARCFontScale(ctx, a)
if err != nil {
return errors.Wrap(err, "unable to get font scale")
}
// Get window info after font size change.
wInfo, err := ash.GetARCAppWindowInfo(ctx, tconn, wm.Pkg24)
if err != nil {
return errors.Wrap(err, "unable to get arc app window info")
}
if owInfo.TargetBounds != wInfo.TargetBounds {
return errors.Errorf("invalid window bounds after font scale is changed: got %q; want %q", wInfo.TargetBounds, owInfo.TargetBounds)
}
// Compare font scale before and after scale change.
if nfs != fsc {
return errors.Errorf("invalid font scale after font scale is changed: got %.1f; want %.1f", nfs, fsc)
}
return nil
}
// ncDisplaySizeChangeTestsHelper is used for non-resizable Tast-tests that are testing resolution change.
func ncDisplaySizeChangeTestsHelper(ctx context.Context, tconn *chrome.TestConn, a *arc.ARC, d *ui.Device, activityName string) error {
const roundingErrorDecimal = 0.01
act, err := arc.NewActivity(a, wm.Pkg24, activityName)
if err != nil {
return err
}
defer act.Close()
// Start the activity.
if err := act.Start(ctx, tconn); err != nil {
return err
}
defer act.Stop(ctx, tconn)
if err := wm.WaitUntilActivityIsReady(ctx, tconn, act, d); err != nil {
return err
}
// Get primary display info before zoom.
dispInfoBeforeZoom, err := display.GetPrimaryInfo(ctx, tconn)
if err != nil {
return err
}
if dispInfoBeforeZoom == nil {
return errors.New("failed to find primary display info")
}
displayID := dispInfoBeforeZoom.ID
originalZoom := dispInfoBeforeZoom.DisplayZoomFactor
// Get buttons info before zoom.
buttonBoundsBeforeZoom, err := wm.GetButtonBounds(ctx, d, act.PackageName())
if err != nil {
return err
}
newZoom := 0.
displayZoomFactors := dispInfoBeforeZoom.AvailableDisplayZoomFactors
for _, z := range displayZoomFactors {
if z > originalZoom {
newZoom = z
break
}
}
if newZoom == 0 {
return errors.Errorf("invalid AvailableDisplayZoomFactors: got an empty array; want array with at least one value greater than '%.2f'", originalZoom)
}
if err := wm.ChangeDisplayZoomFactor(ctx, tconn, displayID, newZoom); err != nil {
return err
}
defer wm.ChangeDisplayZoomFactor(ctx, tconn, displayID, dispInfoBeforeZoom.DisplayZoomFactor)
// Get buttons info after zoom.
buttonBoundsAfterZoom, err := wm.GetButtonBounds(ctx, d, act.PackageName())
if err != nil {
return err
}
// Get primary display info after zoom.
dispInfoAfterZoom, err := display.GetPrimaryInfo(ctx, tconn)
if err != nil {
return err
}
if dispInfoAfterZoom == nil {
return errors.New("failed to find primary display info")
}
// The window is maximized, so the content should be relayout (button bounds must be different).
if buttonBoundsBeforeZoom == buttonBoundsAfterZoom {
return errors.Errorf("invalid button bounds after resolution changed, got: %q, want different than: %q", buttonBoundsAfterZoom, buttonBoundsBeforeZoom)
}
if err := buttonBoundsCheckAfterZoom(buttonBoundsAfterZoom, buttonBoundsBeforeZoom, newZoom/originalZoom); err != nil {
return err
}
// DPI before zoom divided by DPI after zoom should be equal to zoom coefficient.
// Because of possible roundings, the difference is calculated that should be less than roundingErrorDecimal (0.01) to have up to 2 decimal points of precision.
if math.Abs(newZoom/originalZoom-dispInfoBeforeZoom.DPIX/dispInfoAfterZoom.DPIX) > roundingErrorDecimal {
return errors.Errorf("invalid DPIX ratio after resolution changed, got: %.3f, want: %.3f", dispInfoBeforeZoom.DPIX/dispInfoAfterZoom.DPIX, newZoom)
}
if math.Abs(newZoom/originalZoom-dispInfoBeforeZoom.DPIY/dispInfoAfterZoom.DPIY) > roundingErrorDecimal {
return errors.Errorf("invalid DPIY ratio after resolution changed, got: %.3f, want: %.3f", dispInfoBeforeZoom.DPIY/dispInfoAfterZoom.DPIY, newZoom)
}
return nil
}
// buttonBoundsCheckAfterZoom calculates old rect (before zoom) values based on the new rect and the ratio.
// If the difference between calculated values and old rect values is greater than roundingErrorInt (1), the function will return an error.
func buttonBoundsCheckAfterZoom(newRect, oldRect coords.Rect, ratio float64) error {
const roundingErrorInt = 1
oldTopCalc := (int)(math.Round((float64)(newRect.Top) * ratio))
oldLeftCalc := (int)(math.Round((float64)(newRect.Left) * ratio))
oldWidthCalc := (int)(math.Round((float64)(newRect.Width) * ratio))
oldHeightCalc := (int)(math.Round((float64)(newRect.Height) * ratio))
if intAbs(oldRect.Top-oldTopCalc) > roundingErrorInt {
return errors.Errorf("Calculated Top is incorrect, got: %d, want: %d", oldTopCalc, oldRect.Top)
}
if intAbs(oldRect.Left-oldLeftCalc) > roundingErrorInt {
return errors.Errorf("Calculated Left is incorrect, got: %d, want: %d", oldLeftCalc, oldRect.Left)
}
if intAbs(oldRect.Width-oldWidthCalc) > roundingErrorInt {
return errors.Errorf("Calculated Width is incorrect, got: %d, want: %d", oldWidthCalc, oldRect.Width)
}
if intAbs(oldRect.Height-oldHeightCalc) > roundingErrorInt {
return errors.Errorf("Calculated Height is incorrect, got: %d, want: %d", oldHeightCalc, oldRect.Height)
}
return nil
}
// intAbs returns absolute value of an integer input.
func intAbs(v int) int {
if v < 0 {
return -1 * v
}
return v
}
// checkMaxActivityToFullscreen creates a new activity, lunches it and toggles to fullscreen and checks for validity of window info.
func checkMaxActivityToFullscreen(ctx context.Context, tconn *chrome.TestConn, a *arc.ARC, d *ui.Device, activityName string) error {
// Start the activity
act, err := arc.NewActivity(a, wm.Pkg24, activityName)
if err != nil {
return err
}
defer act.Close()
if err := act.Start(ctx, tconn); err != nil {
return err
}
defer act.Stop(ctx, tconn)
if err := wm.WaitUntilActivityIsReady(ctx, tconn, act, d); err != nil {
return err
}
// Check the activity
if err := wm.CheckMaximizeNonResizable(ctx, tconn, act, d); err != nil {
return err
}
windowInfoBefore, err := ash.GetARCAppWindowInfo(ctx, tconn, wm.Pkg24)
if err != nil {
return err
}
// Toggle to fullscreen
if err := wm.ToggleFullscreen(ctx, tconn); err != nil {
return err
}
if err := ash.WaitForARCAppWindowState(ctx, tconn, wm.Pkg24, ash.WindowStateFullscreen); err != nil {
return err
}
if err := ash.WaitWindowFinishAnimating(ctx, tconn, windowInfoBefore.ID); err != nil {
return err
}
windowInfoFullscreen, err := ash.GetARCAppWindowInfo(ctx, tconn, wm.Pkg24)
if err != nil {
return err
}
if err := wm.CheckMaximizeToFullscreenToggle(ctx, tconn, windowInfoBefore.TargetBounds, *windowInfoFullscreen); err != nil {
return err
}
// Toggle back from fullscreen
if err := wm.ToggleFullscreen(ctx, tconn); err != nil {
return err
}
if err := ash.WaitForARCAppWindowState(ctx, tconn, wm.Pkg24, ash.WindowStateMaximized); err != nil {
return err
}
if err := ash.WaitWindowFinishAnimating(ctx, tconn, windowInfoBefore.ID); err != nil {
return err
}
windowInfoAfter, err := ash.GetARCAppWindowInfo(ctx, tconn, wm.Pkg24)
if err != nil {
return err
}
if windowInfoBefore.BoundsInRoot != windowInfoAfter.BoundsInRoot {
return errors.Errorf("invalid window bounds after switching from fullscreen, got: %q, want: %q", windowInfoAfter.BoundsInRoot, windowInfoBefore.BoundsInRoot)
}
return nil
}