blob: a65d8593b4af368187e097f938d730f7a8f17743 [file] [log] [blame]
// Copyright 2019 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"
"fmt"
"net/http"
"net/http/httptest"
"time"
"chromiumos/tast/common/perf"
"chromiumos/tast/errors"
"chromiumos/tast/local/bundles/cros/ui/perfutil"
"chromiumos/tast/local/chrome"
"chromiumos/tast/local/chrome/ash"
"chromiumos/tast/local/input"
"chromiumos/tast/local/lacros"
lacroslauncher "chromiumos/tast/local/lacros/launcher"
"chromiumos/tast/local/power"
"chromiumos/tast/testing"
"chromiumos/tast/testing/hwdep"
)
func init() {
testing.AddTest(&testing.Test{
Func: LauncherAnimationPerf,
Desc: "Measures animation smoothness of lancher animations",
Contacts: []string{
"newcomer@chromium.org", "tbarzic@chromium.org", "cros-launcher-prod-notifications@google.com",
"mukai@chromium.org", // original test author
"cros-system-ui-eng@google.com",
},
Attr: []string{"group:crosbolt", "crosbolt_perbuild"},
SoftwareDeps: []string{"chrome"},
HardwareDeps: hwdep.D(hwdep.InternalDisplay()),
Timeout: 3 * time.Minute,
Params: []testing.Param{{
Val: lacros.ChromeTypeChromeOS,
Fixture: "chromeLoggedInWith100FakeApps",
}, {
Name: "skia_renderer",
Val: lacros.ChromeTypeChromeOS,
Fixture: "chromeLoggedInWith100FakeAppsSkiaRenderer",
}, {
Name: "lacros",
Val: lacros.ChromeTypeLacros,
Fixture: "lacrosStartedByDataWith100FakeApps",
ExtraData: []string{lacroslauncher.DataArtifact},
ExtraSoftwareDeps: []string{"lacros"},
}},
Data: []string{"animation.html", "animation.js"},
})
}
// launcherAnimationType specifies the type of the animation of opening
// launcher.
type launcherAnimationType int
const (
animationTypePeeking launcherAnimationType = iota
animationTypeFullscreenAllApps
animationTypeFullscreenSearch
animationTypeHalf
)
func runLauncherAnimation(ctx context.Context, tconn *chrome.TestConn, kb *input.KeyboardEventWriter, at launcherAnimationType) error {
trigger := ash.AccelSearch
firstState := ash.Peeking
if at == animationTypeFullscreenAllApps {
trigger = ash.AccelShiftSearch
firstState = ash.FullscreenAllApps
}
if err := ash.TriggerLauncherStateChange(ctx, tconn, trigger); err != nil {
return errors.Wrap(err, "failed to open launcher")
}
if err := ash.WaitForLauncherState(ctx, tconn, firstState); err != nil {
return errors.Wrap(err, "failed to wait for state")
}
if at == animationTypeHalf || at == animationTypeFullscreenSearch {
if err := kb.Type(ctx, "a"); err != nil {
return errors.Wrap(err, "failed to type 'a'")
}
if err := ash.WaitForLauncherState(ctx, tconn, ash.Half); err != nil {
return errors.Wrap(err, "failed to switch the state to 'Half'")
}
}
if at == animationTypeFullscreenSearch {
if err := ash.TriggerLauncherStateChange(ctx, tconn, ash.AccelShiftSearch); err != nil {
return errors.Wrap(err, "failed to switch to fullscreen")
}
if err := ash.WaitForLauncherState(ctx, tconn, ash.FullscreenSearch); err != nil {
return errors.Wrap(err, "failed to switch the state to 'FullscreenSearch'")
}
}
// Close
if err := ash.TriggerLauncherStateChange(ctx, tconn, ash.AccelSearch); err != nil {
return errors.Wrap(err, "failed to close launcher")
}
if err := ash.WaitForLauncherState(ctx, tconn, ash.Closed); err != nil {
return errors.Wrap(err, "failed to switch the state to 'Closed'")
}
return nil
}
func LauncherAnimationPerf(ctx context.Context, s *testing.State) {
// Ensure display on to record ui performance correctly.
if err := power.TurnOnDisplay(ctx); err != nil {
s.Fatal("Failed to turn on display: ", err)
}
f := s.FixtValue()
cr, err := lacros.GetChrome(ctx, f)
if err != nil {
s.Fatal("Failed to initialize test: ", err)
}
tconn, err := cr.TestAPIConn(ctx)
if err != nil {
s.Fatal("Failed to connect to test API: ", err)
}
kb, err := input.Keyboard(ctx)
if err != nil {
s.Fatal("Failed to find keyboard: ", err)
}
defer kb.Close()
cleanup, err := ash.EnsureTabletModeEnabled(ctx, tconn, false)
if err != nil {
s.Fatal("Failed to ensure in clamshell mode: ", err)
}
defer cleanup(ctx)
// Run an http server to serve the test contents for accessing from the chrome browsers.
server := httptest.NewServer(http.FileServer(s.DataFileSystem()))
defer server.Close()
url := server.URL + "/animation.html"
// TODO(oshima|mukai): run animation once to force creating a
// launcher widget once we have a utility to initialize the
// prevHists with current data. (crbug.com/1024071)
runner := perfutil.NewRunner(cr)
currentWindows := 0
// Run the launcher open/close flow for various situations.
// - change the number of browser windows, 0 or 2.
// - peeking->close, peeking->half, peeking->half->fullscreen->close, fullscreen->close.
for _, windows := range []int{0, 2} {
func() {
// TODO(crbug.com/1127165): Remove the artifactPath argument when we can use Data in fixtures.
var artifactPath string
if s.Param().(lacros.ChromeType) == lacros.ChromeTypeLacros {
artifactPath = s.DataPath(lacroslauncher.DataArtifact)
}
_, l, cs, err := lacros.Setup(ctx, f, artifactPath, s.Param().(lacros.ChromeType))
if err != nil {
s.Fatal("Failed to setup lacrostest: ", err)
}
defer lacros.CloseLacrosChrome(ctx, l)
if err := ash.CreateWindows(ctx, tconn, cs, url, windows-currentWindows); err != nil {
s.Fatal("Failed to create browser windows: ", err)
}
// Maximize all windows to ensure a consistent state.
if err := ash.ForEachWindow(ctx, tconn, func(w *ash.Window) error {
return ash.SetWindowStateAndWait(ctx, tconn, w.ID, ash.WindowStateMaximized)
}); err != nil {
s.Fatal("Failed to maximize windows: ", err)
}
if s.Param().(lacros.ChromeType) == lacros.ChromeTypeLacros {
if err := lacros.CloseAboutBlank(ctx, tconn, l.Devsess, 1); err != nil {
s.Fatal("Failed to close about:blank: ", err)
}
}
currentWindows = windows
for _, at := range []launcherAnimationType{animationTypePeeking, animationTypeHalf, animationTypeFullscreenSearch, animationTypeFullscreenAllApps} {
// Wait for 1 seconds to stabilize the result. Note that this doesn't
// have to be cpu.WaitUntilIdle(). It may wait too much.
// TODO(mukai): find the way to wait more properly on the idleness of Ash.
// https://crbug.com/1001314.
if err := testing.Sleep(ctx, 1*time.Second); err != nil {
s.Fatal("Failed to wait: ", err)
}
var suffix string
switch at {
case animationTypePeeking:
suffix = "Peeking.ClamshellMode"
case animationTypeFullscreenAllApps:
suffix = "FullscreenAllApps.ClamshellMode"
case animationTypeFullscreenSearch:
suffix = "FullscreenSearch.ClamshellMode"
case animationTypeHalf:
suffix = "Half.ClamshellMode"
}
histograms := []string{
"Apps.StateTransition.AnimationSmoothness." + suffix,
"Apps.StateTransition.AnimationSmoothness.Close.ClamshellMode",
}
runner.RunMultiple(ctx, s, fmt.Sprintf("%s.%dwindows", suffix, currentWindows), perfutil.RunAndWaitAll(tconn, func(ctx context.Context) error {
return runLauncherAnimation(ctx, tconn, kb, at)
}, histograms...),
perfutil.StoreAll(perf.BiggerIsBetter, "percent", fmt.Sprintf("%dwindows", currentWindows)))
}
}()
}
if err := runner.Values().Save(ctx, s.OutDir()); err != nil {
s.Error("Failed saving perf data: ", err)
}
}