tast-tests: Add Move/Resize function test for ChromeOS Companion Lib

BUG=b:143514720
BUG=b:141978622
TEST=tast -verbose run my_crbook arc.CompanionLibrary

Change-Id: Ie13b86886bbbc6cac9d3aefdd080e100dc50d6e9
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/tast-tests/+/1890292
Tested-by: Shengsong Tan <sstan@google.com>
Commit-Queue: Shengsong Tan <sstan@google.com>
Reviewed-by: Hidehiko Abe <hidehiko@chromium.org>
Reviewed-by: Stefan Kuhne <skuhne@chromium.org>
diff --git a/src/chromiumos/tast/local/bundles/cros/arc/companion_library.go b/src/chromiumos/tast/local/bundles/cros/arc/companion_library.go
index ea39845..fb77840 100644
--- a/src/chromiumos/tast/local/bundles/cros/arc/companion_library.go
+++ b/src/chromiumos/tast/local/bundles/cros/arc/companion_library.go
@@ -18,6 +18,7 @@
 	"chromiumos/tast/local/chrome"
 	"chromiumos/tast/local/chrome/ash"
 	"chromiumos/tast/local/chrome/display"
+	"chromiumos/tast/local/input"
 	"chromiumos/tast/testing"
 )
 
@@ -58,7 +59,8 @@
 	const (
 		apk = "ArcCompanionLibDemo.apk"
 
-		mainActivity = ".MainActivity"
+		mainActivity     = ".MainActivity"
+		resizeActivityID = ".MoveResizeActivity"
 	)
 
 	cr := s.PreValue().(arc.PreData).Chrome
@@ -105,6 +107,7 @@
 		s.Fatal("Failed to wait for activity to resume: ", err)
 	}
 
+	// All of tests in this block running on MainActivity.
 	type testFunc func(context.Context, *chrome.Conn, *arc.Activity, *ui.Device, *testing.State) error
 	for _, test := range []struct {
 		name string
@@ -131,6 +134,25 @@
 		}
 	}
 
+	resizeAct, err := arc.NewActivity(a, pkg, resizeActivityID)
+	if err != nil {
+		s.Fatal("Could not create ResizeActivity: ", err)
+	}
+	if err := resizeAct.Start(ctx); err != nil {
+		s.Fatal("Could not start ResizeActivity: ", err)
+	}
+	defer func() {
+		if err := resizeAct.Stop(ctx); err != nil {
+			s.Fatal("Could not stop resize activity: ", err)
+		}
+	}()
+	if err := setWindowStateSync(ctx, resizeAct, arc.WindowStateNormal); err != nil {
+		s.Fatal("Could not set window normal state: ", err)
+	}
+	if err := testResizeWindow(ctx, tconn, resizeAct, d, s); err != nil {
+		s.Error("Move & Resize Window test failed: ", err)
+	}
+	// End of function, i.e. no resizeAct.Stop() call here, because it is called in defer.
 }
 
 // testCaptionHeight verifies that the caption height length getting from ChromeOS companion library is correct.
@@ -169,7 +191,7 @@
 		return errors.Errorf("unexpected JSON message format: no CaptionHeightMsg; got %v", msg)
 	}
 
-	appWindow, err := getArcAppWindowInfo(ctx, tconn, pkg)
+	appWindow, err := ash.GetARCAppWindowInfo(ctx, tconn, pkg)
 	if err != nil {
 		return errors.Wrap(err, "failed to get arc app window")
 	}
@@ -182,6 +204,96 @@
 
 }
 
+// testResizeWindow verifies that the resize function in ChromeOS companion library works as expected.
+// ARC companion library demo provide a activity for resize test, there are four draggable hit-boxes in four sides.
+// The test maximizing the window by drag from four side inner hit-boxes. The events will be handled by Companion Library, not Chrome.
+func testResizeWindow(ctx context.Context, tconn *chrome.Conn, act *arc.Activity, d *ui.Device, s *testing.State) error {
+	dispMode, err := ash.InternalDisplayMode(ctx, tconn)
+	if err != nil {
+		return errors.Wrap(err, "failed to get display mode")
+	}
+	dispInfo, err := display.GetInternalInfo(ctx, tconn)
+	if err != nil {
+		return errors.Wrap(err, "failed to get internal display info")
+	}
+	appWindow, err := ash.GetARCAppWindowInfo(ctx, tconn, pkg)
+	if err != nil {
+		return errors.Wrap(err, "failed to get arc window info")
+	}
+
+	tsw, err := input.Touchscreen(ctx)
+	if err != nil {
+		return errors.Wrap(err, "failed to open touchscreen device")
+	}
+	defer tsw.Close()
+
+	stw, err := tsw.NewSingleTouchWriter()
+	if err != nil {
+		return errors.Wrap(err, "could not create TouchEventWriter")
+	}
+	defer stw.Close()
+
+	// Calculate Pixel (screen display) / Tuxel (touch device) ratio.
+	dispW := dispMode.WidthInNativePixels
+	dispH := dispMode.HeightInNativePixels
+	pixelToTuxelX := float64(tsw.Width()) / float64(dispW)
+	pixelToTuxelY := float64(tsw.Height()) / float64(dispH)
+
+	captionHeight := int(math.Round(float64(appWindow.CaptionHeight) * dispMode.DeviceScaleFactor))
+	bounds := ash.ConvertBoundsFromDpToPx(appWindow.BoundsInRoot, dispMode.DeviceScaleFactor)
+	testing.ContextLogf(ctx, "The original window bound is %v, try to maximize it by drag inner hit-boxes", bounds)
+
+	// Waiting for hit-boxes UI ready.
+	if err := d.WaitForIdle(ctx, 10*time.Second); err != nil {
+		return errors.Wrap(err, "failed to wait for idle")
+	}
+
+	innerMargin := 5
+	middleX := bounds.Left + bounds.Width/2
+	middleY := bounds.Top + bounds.Height/2
+	for _, test := range []struct {
+		startX, startY, endX, endY int
+	}{
+		{startX: bounds.Left + innerMargin, startY: middleY, endX: 0, endY: middleY},                        //left
+		{startX: bounds.Left + bounds.Width - innerMargin, startY: middleY, endX: dispW - 1, endY: middleY}, //right
+		{startX: middleX, startY: bounds.Top + innerMargin + captionHeight, endX: middleX, endY: 0},         //top
+		{startX: middleX, startY: bounds.Top + bounds.Height - innerMargin, endX: middleX, endY: dispH - 1}, //bottom
+	} {
+		// Wait for application's UI ready.
+		x0 := input.TouchCoord(float64(test.startX) * pixelToTuxelX)
+		y0 := input.TouchCoord(float64(test.startY) * pixelToTuxelY)
+
+		x1 := input.TouchCoord(float64(test.endX) * pixelToTuxelX)
+		y1 := input.TouchCoord(float64(test.endY) * pixelToTuxelY)
+
+		testing.ContextLogf(ctx, "Running the swipe gesture from {%d,%d} to {%d,%d} to ensure to start drag move", x0, y0, x1, y1)
+		if err := stw.Swipe(ctx, x0, y0, x1, y1, 2*time.Second); err != nil {
+			return errors.Wrap(err, "failed to execute a swipe gesture")
+		}
+		if err := stw.End(); err != nil {
+			return errors.Wrap(err, "failed to finish the swipe gesture")
+		}
+		// Resize by companion library will take long time waiting for application's UI ready.
+		if _, err := d.WaitForWindowUpdate(ctx, pkg, 10*time.Second); err != nil {
+			return errors.Wrap(err, "failed to wait window updated after swipe resize")
+		}
+	}
+	if err := testing.Poll(ctx, func(ctx context.Context) error {
+		appWindow, err = ash.GetARCAppWindowInfo(ctx, tconn, pkg)
+		if err != nil {
+			return testing.PollBreak(errors.Wrap(err, "failed to get arc window info"))
+		}
+		if appWindow.BoundsInRoot != ash.Rect(*dispInfo.WorkArea) {
+			return errors.Errorf("resize window doesn't have the expected bounds yet; got %v, want %v", appWindow.BoundsInRoot, dispInfo.WorkArea)
+		}
+		return nil
+	}, &testing.PollOptions{Timeout: 5 * time.Second}); err != nil {
+		return err
+	}
+	return nil
+}
+
+// testWorkspaceInsets verifies that the workspace insets info from ChromeOS companion library is correct.
 func testWorkspaceInsets(ctx context.Context, tconn *chrome.Conn, act *arc.Activity, d *ui.Device, s *testing.State) error {
 	const getWorkspaceInsetsButtonID = pkg + ":id/get_workspace_insets"
 
@@ -292,6 +404,7 @@
 	return nil
 }
 
+// testCaptionButton verifies that hidden caption button API works as expected.
 func testCaptionButton(ctx context.Context, tconn *chrome.Conn, act *arc.Activity, d *ui.Device, s *testing.State) error {
 	const (
 		setCaptionButtonID                      = pkg + ":id/set_caption_buttons_visibility"
@@ -354,23 +467,18 @@
 			return errors.New("Error while changing hidden caption button")
 		}
 
-		if err := testing.Poll(ctx, func(ctx context.Context) error {
-			window, err := getArcAppWindowInfo(ctx, tconn, pkg)
-			if err != nil {
-				return errors.Wrap(err, "error while get ARC window")
-			}
-			if window.CaptionButtonVisibleStatus&int(test.buttonVisibleStatusMask) != 0 {
-				return errors.Errorf("Caption Button %v still visible", test.buttonCheckboxID)
-			}
-			return nil
-		}, &testing.PollOptions{Timeout: 5 * time.Second}); err != nil {
-			return errors.Wrap(err, "failed to waiting caption button changed")
+		window, err := ash.GetARCAppWindowInfo(ctx, tconn, pkg)
+		if err != nil {
+			return errors.Wrap(err, "error while get ARC window")
 		}
-
+		if window.CaptionButtonVisibleStatus&int(test.buttonVisibleStatusMask) != 0 {
+			return errors.Errorf("Caption Button %v still visible", test.buttonCheckboxID)
+		}
 	}
 	return nil
 }
 
+// testDeviceMode verifies that the device mode info from ChromeOS companion library is correct.
 func testDeviceMode(ctx context.Context, tconn *chrome.Conn, act *arc.Activity, d *ui.Device, s *testing.State) error {
 	const getDeviceModeButtonID = pkg + ":id/get_device_mode_button"
 
@@ -421,6 +529,7 @@
 	return nil
 }
 
+// testWindowState verifies that change window state by ChromeOS companion library works as expected.
 func testWindowState(ctx context.Context, tconn *chrome.Conn, act *arc.Activity, d *ui.Device, s *testing.State) error {
 	const (
 		setWindowStateButtonID = pkg + ":id/set_task_window_state_button"
@@ -483,22 +592,21 @@
 	return nil
 }
 
-// getArcAppWindowInfo returns corresponding arc window infomation.
-func getArcAppWindowInfo(ctx context.Context, tconn *chrome.Conn, pkgName string) (*ash.Window, error) {
-	var appWindow *ash.Window
-	if windows, err := ash.GetAllWindows(ctx, tconn); err != nil {
-		return nil, errors.Wrap(err, "get all windows info")
-	} else if len(windows) > 0 {
-		for _, w := range windows {
-			if w.WindowType == ash.WindowTypeArc && w.ARCPackageName == pkgName {
-				appWindow = w
-			}
+func setWindowStateSync(ctx context.Context, act *arc.Activity, state arc.WindowState) error {
+	if err := act.SetWindowState(ctx, state); err != nil {
+		return errors.Wrap(err, "could not set window state to normal")
+	}
+	if err := testing.Poll(ctx, func(ctx context.Context) error {
+		if currentState, err := act.GetWindowState(ctx); err != nil {
+			return testing.PollBreak(errors.Wrap(err, "could not get the window state"))
+		} else if currentState != state {
+			return errors.Errorf("window state has not changed yet: got %s; want %s", currentState, state)
 		}
+		return nil
+	}, &testing.PollOptions{Timeout: 4 * time.Second}); err != nil {
+		return errors.Wrap(err, "failed to waiting for change to normal window state")
 	}
-	if appWindow == nil {
-		return nil, errors.New("can not find corresponding ARC window")
-	}
-	return appWindow, nil
+	return nil
 }
 
 // getTextViewContent returns all text in status textview.