Commit a9fdeeb0 authored by Mitsuru Oshima's avatar Mitsuru Oshima Committed by Commit Bot

[CrOS PerfTest] Launcher Dragging

This measures the presentation time of following actions:

* gesture-drag to show the launcher from shelf to fullscreen state.
* gesture-drag to hide the fullscreen launcher.

I also moved the perf test utility classes into
separate file so that they can be used in other tests.

Bug: 948324
Change-Id: Iac4d5ca7a39ab44c0404ab927df12608cb7529de
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1565235Reviewed-by: default avatarNico Weber <thakis@chromium.org>
Reviewed-by: default avatarXiyuan Xia <xiyuan@chromium.org>
Reviewed-by: default avatarAlex Newcomer <newcomer@chromium.org>
Commit-Queue: Mitsuru Oshima <oshima@chromium.org>
Cr-Commit-Position: refs/heads/master@{#652675}
parent ab787412
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ash/public/cpp/app_list/app_list_config.h"
#include "ash/public/interfaces/app_list_view.mojom.h"
#include "base/macros.h"
#include "base/run_loop.h"
#include "base/strings/stringprintf.h"
#include "base/task/post_task.h"
#include "chrome/browser/ui/ash/ash_test_util.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/views/frame/browser_view.h"
#include "chrome/test/base/perf/drag_event_generator.h"
#include "chrome/test/base/perf/performance_test.h"
#include "ui/base/test/ui_controls.h"
#include "ui/display/display.h"
#include "ui/display/screen.h"
// Test launcher drag performance in clamshell mode.
// TODO(oshima): Add test for tablet mode.
class LauncherDragTest : public UIPerformanceTest {
public:
LauncherDragTest() = default;
~LauncherDragTest() override = default;
// UIPerformanceTest:
void SetUpOnMainThread() override {
UIPerformanceTest::SetUpOnMainThread();
// Ash may not be ready to receive events right away.
int warmup_seconds = base::SysInfo::IsRunningOnChromeOS() ? 5 : 1;
base::RunLoop run_loop;
base::PostDelayedTask(FROM_HERE, run_loop.QuitClosure(),
base::TimeDelta::FromSeconds(warmup_seconds));
run_loop.Run();
}
// UIPerformanceTest:
std::vector<std::string> GetUMAHistogramNames() const override {
return {
"Apps.StateTransition.Drag.PresentationTime.ClamshellMode",
};
}
static gfx::Rect GetDisplayBounds(aura::Window* window) {
return display::Screen::GetScreen()
->GetDisplayNearestWindow(window)
.bounds();
}
private:
DISALLOW_COPY_AND_ASSIGN(LauncherDragTest);
};
// Drag to open the launcher from shelf.
IN_PROC_BROWSER_TEST_F(LauncherDragTest, Open) {
BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser());
aura::Window* browser_window = browser_view->GetWidget()->GetNativeWindow();
ash::mojom::ShellTestApiPtr shell_test_api = test::GetShellTestApi();
base::RunLoop waiter;
shell_test_api->WaitForLauncherAnimationState(
ash::mojom::AppListViewState::kFullscreenAllApps, waiter.QuitClosure());
gfx::Rect display_bounds = GetDisplayBounds(browser_window);
// TODO(oshima): Use shelf constants.
gfx::Point start_point =
gfx::Point(display_bounds.width() / 4, display_bounds.bottom() - 28);
gfx::Point end_point(start_point);
end_point.set_y(10);
ui_test_utils::DragEventGenerator generator(
std::make_unique<ui_test_utils::InterporateProducer>(
start_point, end_point, base::TimeDelta::FromMilliseconds(1000)),
/*touch=*/true);
generator.Wait();
waiter.Run();
}
// Drag to close the launcher.
IN_PROC_BROWSER_TEST_F(LauncherDragTest, Close) {
BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser());
aura::Window* browser_window = browser_view->GetWidget()->GetNativeWindow();
ash::mojom::ShellTestApiPtr shell_test_api = test::GetShellTestApi();
{
base::RunLoop waiter;
shell_test_api->WaitForLauncherAnimationState(
ash::mojom::AppListViewState::kFullscreenAllApps, waiter.QuitClosure());
ui_controls::SendKeyPress(browser_window, ui::VKEY_BROWSER_SEARCH,
/*control=*/false,
/*shift=*/true,
/*alt=*/false,
/*command=*/false);
waiter.Run();
}
base::RunLoop waiter;
shell_test_api->WaitForLauncherAnimationState(
ash::mojom::AppListViewState::kClosed, waiter.QuitClosure());
gfx::Rect display_bounds = GetDisplayBounds(browser_window);
gfx::Point start_point = gfx::Point(display_bounds.width() / 4, 10);
gfx::Point end_point(start_point);
// TODO(oshima): Use shelf_constants.
end_point.set_y(display_bounds.bottom() - 56);
ui_test_utils::DragEventGenerator generator(
std::make_unique<ui_test_utils::InterporateProducer>(
start_point, end_point, base::TimeDelta::FromMilliseconds(1000)),
/*touch=*/true);
generator.Wait();
waiter.Run();
}
......@@ -14,6 +14,7 @@
#include "chrome/browser/ui/browser_finder.h"
#include "chrome/browser/ui/views/frame/browser_view.h"
#include "chrome/test/base/interactive_test_utils.h"
#include "chrome/test/base/perf/drag_event_generator.h"
#include "chrome/test/base/perf/performance_test.h"
#include "content/public/browser/notification_service.h"
#include "ui/aura/window.h"
......@@ -26,148 +27,6 @@
namespace {
// The frame duration for 60fps.
constexpr base::TimeDelta kFrameDuration =
base::TimeDelta::FromMicroseconds(16666);
// TODO(oshima): Move this to utility lib.
// Producer produces the point for given progress value.
class PointProducer {
public:
virtual ~PointProducer() = default;
virtual gfx::Point GetPosition(float progress) = 0;
};
// InterporateProducer produces the interpolated location between two points
// based on tween type.
class InterporateProducer : public PointProducer {
public:
InterporateProducer(const gfx::Point& start,
const gfx::Point& end,
gfx::Tween::Type type = gfx::Tween::LINEAR)
: start_(start), end_(end), type_(type) {}
~InterporateProducer() override = default;
// PointProducer:
gfx::Point GetPosition(float progress) override {
float value = gfx::Tween::CalculateValue(type_, progress);
return gfx::Point(
gfx::Tween::LinearIntValueBetween(value, start_.x(), end_.x()),
gfx::Tween::LinearIntValueBetween(value, start_.y(), end_.y()));
}
private:
gfx::Point start_, end_;
gfx::Tween::Type type_;
DISALLOW_COPY_AND_ASSIGN(InterporateProducer);
};
// A utility class that generates drag events using |producer| logic.
class DragEventGenerator {
public:
DragEventGenerator(std::unique_ptr<PointProducer> producer,
const base::TimeDelta duration,
bool touch = true)
: producer_(std::move(producer)),
start_(base::TimeTicks::Now()),
expected_next_time_(start_ + kFrameDuration),
duration_(duration),
touch_(touch) {
gfx::Point initial_position = producer_->GetPosition(0.f);
if (touch_) {
ui_controls::SendTouchEvents(ui_controls::PRESS, 0, initial_position.x(),
initial_position.y());
} else {
ui_controls::SendMouseMove(initial_position.x(), initial_position.y());
ui_controls::SendMouseEvents(ui_controls::LEFT, ui_controls::DOWN);
}
base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
FROM_HERE,
base::BindOnce(&DragEventGenerator::GenerateNext,
base::Unretained(this)),
kFrameDuration);
}
~DragEventGenerator() {
VLOG(1) << "Effective Event per seconds="
<< (count_ * 1000) / duration_.InMilliseconds();
}
void GenerateNext() {
auto now = base::TimeTicks::Now();
auto elapsed = now - start_;
expected_next_time_ += kFrameDuration;
count_++;
if (elapsed >= duration_) {
gfx::Point position = producer_->GetPosition(1.0f);
if (touch_) {
ui_controls::SendTouchEventsNotifyWhenDone(
ui_controls::MOVE, 0, position.x(), position.y(),
base::BindOnce(&DragEventGenerator::Done, base::Unretained(this),
position));
} else {
ui_controls::SendMouseMoveNotifyWhenDone(
position.x(), position.y(),
base::BindOnce(&DragEventGenerator::Done, base::Unretained(this),
position));
}
return;
}
float progress = static_cast<float>(elapsed.InMilliseconds()) /
duration_.InMilliseconds();
gfx::Point position = producer_->GetPosition(progress);
if (touch_) {
ui_controls::SendTouchEvents(ui_controls::MOVE, 0, position.x(),
position.y());
} else {
ui_controls::SendMouseMove(position.x(), position.y());
}
auto delta = expected_next_time_ - now;
if (delta.InMilliseconds() > 0) {
base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
FROM_HERE,
base::BindOnce(&DragEventGenerator::GenerateNext,
base::Unretained(this)),
delta);
} else {
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(&DragEventGenerator::GenerateNext,
base::Unretained(this)));
}
}
void Done(const gfx::Point position) {
if (touch_) {
ui_controls::SendTouchEventsNotifyWhenDone(ui_controls::RELEASE, 0,
position.x(), position.y(),
run_loop_.QuitClosure());
} else {
ui_controls::SendMouseEventsNotifyWhenDone(
ui_controls::LEFT, ui_controls::UP, run_loop_.QuitClosure());
}
}
void Wait() { run_loop_.Run(); }
private:
std::unique_ptr<PointProducer> producer_;
int count_ = 0;
base::TimeTicks start_;
base::TimeTicks expected_next_time_;
base::TimeDelta duration_;
bool touch_;
base::RunLoop run_loop_;
DISALLOW_COPY_AND_ASSIGN(DragEventGenerator);
};
// Wait until the window's state changed to left snapped.
// The window should stay alive, so no need to observer destroying.
class LeftSnapWaiter : public aura::WindowObserver {
......@@ -279,9 +138,10 @@ IN_PROC_BROWSER_TEST_P(OverviewWindowDragTest, NormalDrag) {
gfx::Point start_point = GetStartLocation(display_size);
gfx::Point end_point(start_point);
end_point.set_x(end_point.x() + display_size.width() / 2);
DragEventGenerator generator(
std::make_unique<InterporateProducer>(start_point, end_point),
base::TimeDelta::FromMilliseconds(1000));
ui_test_utils::DragEventGenerator generator(
std::make_unique<ui_test_utils::InterporateProducer>(
start_point, end_point, base::TimeDelta::FromMilliseconds(1000)),
/*touch=*/true);
generator.Wait();
}
......@@ -306,9 +166,11 @@ IN_PROC_BROWSER_TEST_P(OverviewWindowDragTest, DISABLED_DragToClose) {
gfx::Point end_point(start_point);
end_point.set_y(0);
end_point.set_x(end_point.x() + 10);
DragEventGenerator generator(
std::make_unique<InterporateProducer>(start_point, end_point),
base::TimeDelta::FromMilliseconds(500), gfx::Tween::EASE_IN_2);
ui_test_utils::DragEventGenerator generator(
std::make_unique<ui_test_utils::InterporateProducer>(
start_point, end_point, base::TimeDelta::FromMilliseconds(500),
gfx::Tween::EASE_IN_2),
/*touch=*/true);
generator.Wait();
// Wait for the window to close.
......@@ -329,9 +191,10 @@ IN_PROC_BROWSER_TEST_P(OverviewWindowDragTest, DragToSnap) {
gfx::Point start_point = GetStartLocation(GetDisplaySize(browser_window));
gfx::Point end_point(start_point);
end_point.set_x(0);
DragEventGenerator generator(
std::make_unique<InterporateProducer>(start_point, end_point),
base::TimeDelta::FromMilliseconds(1000));
ui_test_utils::DragEventGenerator generator(
std::make_unique<ui_test_utils::InterporateProducer>(
start_point, end_point, base::TimeDelta::FromMilliseconds(1000)),
/*touch=*/true);
generator.Wait();
Browser* active = chrome::FindLastActive();
......
......@@ -5239,9 +5239,12 @@ if (!is_android) {
sources += [
"../browser/ui/ash/drag_to_overview_interactive_uitest.cc",
"../browser/ui/ash/launcher_animations_interactive_uitest.cc",
"../browser/ui/ash/launcher_drag_interactive_uitest.cc",
"../browser/ui/ash/overview_animations_interactive_uitest.cc",
"../browser/ui/ash/overview_window_drag_interactive_uitest.cc",
"../browser/ui/ash/split_view_interactive_uitest.cc",
"base/perf/drag_event_generator.cc",
"base/perf/drag_event_generator.h",
]
} else { # ! is_chromeos
# Non-ChromeOS notifications tests.
......
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/test/base/perf/drag_event_generator.h"
#include "base/bind.h"
#include "base/callback.h"
#include "base/macros.h"
#include "base/task/post_task.h"
#include "base/threading/thread_task_runner_handle.h"
#include "ui/base/test/ui_controls.h"
namespace ui_test_utils {
namespace {
// The frame duration for 60fps.
constexpr base::TimeDelta kFrameDuration =
base::TimeDelta::FromMicroseconds(16666);
} // namespace
////////////////////////////////////////////////////////////////////////////////
// DragEventGenerator
DragEventGenerator::DragEventGenerator(std::unique_ptr<PointProducer> producer,
bool touch)
: producer_(std::move(producer)),
start_(base::TimeTicks::Now()),
expected_next_time_(start_ + kFrameDuration),
touch_(touch) {
gfx::Point initial_position = producer_->GetPosition(0.f);
if (touch_) {
ui_controls::SendTouchEvents(ui_controls::PRESS, 0, initial_position.x(),
initial_position.y());
} else {
ui_controls::SendMouseMove(initial_position.x(), initial_position.y());
ui_controls::SendMouseEvents(ui_controls::LEFT, ui_controls::DOWN);
}
base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
FROM_HERE,
base::BindOnce(&DragEventGenerator::GenerateNext, base::Unretained(this)),
kFrameDuration);
}
DragEventGenerator::~DragEventGenerator() {
VLOG(1) << "Effective Event per seconds="
<< (count_ * 1000) / producer_->GetDuration().InMilliseconds();
}
void DragEventGenerator::Wait() {
run_loop_.Run();
}
void DragEventGenerator::GenerateNext() {
auto now = base::TimeTicks::Now();
auto elapsed = now - start_;
expected_next_time_ += kFrameDuration;
count_++;
const base::TimeDelta duration = producer_->GetDuration();
if (elapsed >= duration) {
gfx::Point position = producer_->GetPosition(1.0f);
if (touch_) {
ui_controls::SendTouchEventsNotifyWhenDone(
ui_controls::MOVE, 0, position.x(), position.y(),
base::BindOnce(&DragEventGenerator::Done, base::Unretained(this),
position));
} else {
ui_controls::SendMouseMoveNotifyWhenDone(
position.x(), position.y(),
base::BindOnce(&DragEventGenerator::Done, base::Unretained(this),
position));
}
return;
}
float progress =
static_cast<float>(elapsed.InMilliseconds()) / duration.InMilliseconds();
gfx::Point position = producer_->GetPosition(progress);
if (touch_) {
ui_controls::SendTouchEvents(ui_controls::MOVE, 0, position.x(),
position.y());
} else {
ui_controls::SendMouseMove(position.x(), position.y());
}
auto delta = expected_next_time_ - now;
if (delta.InMilliseconds() > 0) {
base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
FROM_HERE,
base::BindOnce(&DragEventGenerator::GenerateNext,
base::Unretained(this)),
delta);
} else {
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(&DragEventGenerator::GenerateNext,
base::Unretained(this)));
}
}
void DragEventGenerator::Done(const gfx::Point position) {
if (touch_) {
ui_controls::SendTouchEventsNotifyWhenDone(ui_controls::RELEASE, 0,
position.x(), position.y(),
run_loop_.QuitClosure());
} else {
ui_controls::SendMouseEventsNotifyWhenDone(
ui_controls::LEFT, ui_controls::UP, run_loop_.QuitClosure());
}
}
////////////////////////////////////////////////////////////////////////////////
// DragEventGenerator::PointProducer
DragEventGenerator::PointProducer::~PointProducer() = default;
////////////////////////////////////////////////////////////////////////////////
// InterporateProducer:
InterporateProducer::InterporateProducer(const gfx::Point& start,
const gfx::Point& end,
const base::TimeDelta duration,
gfx::Tween::Type type)
: start_(start), end_(end), duration_(duration), type_(type) {}
InterporateProducer::~InterporateProducer() = default;
gfx::Point InterporateProducer::GetPosition(float progress) {
float value = gfx::Tween::CalculateValue(type_, progress);
return gfx::Point(
gfx::Tween::LinearIntValueBetween(value, start_.x(), end_.x()),
gfx::Tween::LinearIntValueBetween(value, start_.y(), end_.y()));
}
const base::TimeDelta InterporateProducer::GetDuration() const {
return duration_;
}
} // namespace ui_test_utils
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_TEST_BASE_PERF_DRAG_EVENT_GENERATOR_H_
#define CHROME_TEST_BASE_PERF_DRAG_EVENT_GENERATOR_H_
#include "base/run_loop.h"
#include "base/time/time.h"
#include "ui/gfx/animation/tween.h"
#include "ui/gfx/geometry/point.h"
namespace ui_test_utils {
// A utility class that generates drag events using |producer| logic
// at a rate of 60 events per seconds.
class DragEventGenerator {
public:
// Producer produces the point for given progress value. The range of
// the progress is between 0.f (start) to 1.f (end).
class PointProducer {
public:
virtual ~PointProducer();
// Returns the position at |progression|.
virtual gfx::Point GetPosition(float progress) = 0;
// Returns the duration this produce should be used.
virtual const base::TimeDelta GetDuration() const = 0;
};
DragEventGenerator(std::unique_ptr<PointProducer> producer,
bool touch = false);
~DragEventGenerator();
void Wait();
private:
void Done(const gfx::Point position);
void GenerateNext();
std::unique_ptr<PointProducer> producer_;
int count_ = 0;
const base::TimeTicks start_;
base::TimeTicks expected_next_time_;
const bool touch_;
base::RunLoop run_loop_;
DISALLOW_COPY_AND_ASSIGN(DragEventGenerator);
};
// InterporateProducer produces the interpolated location between two points
// based on tween type.
class InterporateProducer : public DragEventGenerator::PointProducer {
public:
InterporateProducer(const gfx::Point& start,
const gfx::Point& end,
const base::TimeDelta duration,
gfx::Tween::Type type = gfx::Tween::LINEAR);
~InterporateProducer() override;
// PointProducer:
gfx::Point GetPosition(float progress) override;
const base::TimeDelta GetDuration() const override;
private:
gfx::Point start_, end_;
base::TimeDelta duration_;
gfx::Tween::Type type_;
DISALLOW_COPY_AND_ASSIGN(InterporateProducer);
};
} // namespace ui_test_utils
#endif // CHROME_TEST_BASE_PERF_DRAG_EVENT_GENERATOR_H_
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment