Commit c2eb607b authored by Sammie Quon's avatar Sammie Quon Committed by Commit Bot

desks: Add autotest api for activating multiple adjacent desks.

This will used for a perf test. Adds an api and a supporting animation
observing class which waits until the animation has been scheduled,
which means the desk ending screenshot has been taken. The observing
class then activates the adjacent desk. This repeats until we have
reached the target desk, then we wait for the whole animation to finish
and run the given callback.

Test: added test
Bug: 1111445
Change-Id: I9f859a2ae31da958c0d539f7e00a1a9944c3bb3d
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2437151
Commit-Queue: Sammie Quon <sammiequon@chromium.org>
Reviewed-by: default avatarAhmed Fakhry <afakhry@chromium.org>
Cr-Commit-Position: refs/heads/master@{#815409}
parent 6740cd70
...@@ -15,10 +15,9 @@ namespace ash { ...@@ -15,10 +15,9 @@ namespace ash {
class ASH_EXPORT AutotestDesksApi { class ASH_EXPORT AutotestDesksApi {
public: public:
AutotestDesksApi(); AutotestDesksApi();
~AutotestDesksApi();
AutotestDesksApi(const AutotestDesksApi& other) = delete; AutotestDesksApi(const AutotestDesksApi& other) = delete;
AutotestDesksApi& operator=(const AutotestDesksApi& rhs) = delete; AutotestDesksApi& operator=(const AutotestDesksApi& rhs) = delete;
~AutotestDesksApi();
// Creates a new desk if the maximum number of desks has not been reached, and // Creates a new desk if the maximum number of desks has not been reached, and
// returns true if succeeded, false otherwise. // returns true if succeeded, false otherwise.
...@@ -34,6 +33,15 @@ class ASH_EXPORT AutotestDesksApi { ...@@ -34,6 +33,15 @@ class ASH_EXPORT AutotestDesksApi {
// Returns false if the active desk is the last available desk which cannot be // Returns false if the active desk is the last available desk which cannot be
// removed; true otherwise. // removed; true otherwise.
bool RemoveActiveDesk(base::OnceClosure on_complete); bool RemoveActiveDesk(base::OnceClosure on_complete);
// Activates the desk at the given |index| by activating all the desks between
// the current desk and the desk at |index| in succession. This mimics
// pressing the activate adjacent desk accelerator rapidly. |on_complete| will
// be invoked when the the final animation to |index| completes. Returns false
// if |index| is invalid, or the desk at |index| is already the active desk;
// true otherwise.
bool ActivateAdjacentDesksToTargetIndex(int index,
base::OnceClosure on_complete);
}; };
} // namespace ash } // namespace ash
......
...@@ -4,10 +4,19 @@ ...@@ -4,10 +4,19 @@
#include "ash/public/cpp/autotest_desks_api.h" #include "ash/public/cpp/autotest_desks_api.h"
#include "ash/shell.h"
#include "ash/wm/desks/desk.h"
#include "ash/wm/desks/desk_animation_base.h"
#include "ash/wm/desks/desks_controller.h" #include "ash/wm/desks/desks_controller.h"
#include "ash/wm/desks/desks_histogram_enums.h" #include "ash/wm/desks/desks_histogram_enums.h"
#include "ash/wm/desks/root_window_desk_switch_animator.h"
#include "base/callback.h" #include "base/callback.h"
#include "base/callback_forward.h"
#include "base/check.h" #include "base/check.h"
#include "ui/compositor/layer.h"
#include "ui/compositor/layer_animation_observer.h"
#include "ui/compositor/layer_animation_sequence.h"
#include "ui/compositor/layer_animator.h"
namespace ash { namespace ash {
...@@ -42,6 +51,84 @@ class DeskAnimationObserver : public DesksController::Observer { ...@@ -42,6 +51,84 @@ class DeskAnimationObserver : public DesksController::Observer {
base::OnceClosure on_desk_animation_complete_; base::OnceClosure on_desk_animation_complete_;
}; };
// Self deleting desk animation observer which takes a target index and then
// waits until the animation layer has scheduled a new animation (ending
// screenshot will have been taken at this point). If the next desk is not the
// target desk, activate the adjacent desk (replacing the current animation). If
// the next desk is the target desk, wait until the animation is finished, then
// run the given callback.
class ChainedDeskAnimationObserver : public ui::LayerAnimationObserver,
public DesksController::Observer {
public:
ChainedDeskAnimationObserver(bool going_left,
int target_index,
base::OnceClosure on_desk_animation_complete)
: going_left_(going_left),
target_index_(target_index),
on_desk_animation_complete_(std::move(on_desk_animation_complete)) {
DesksController::Get()->AddObserver(this);
}
ChainedDeskAnimationObserver(const ChainedDeskAnimationObserver& other) =
delete;
ChainedDeskAnimationObserver& operator=(
const ChainedDeskAnimationObserver& rhs) = delete;
~ChainedDeskAnimationObserver() override {
DesksController::Get()->RemoveObserver(this);
}
// ui::LayerAnimationObserver:
void OnLayerAnimationEnded(ui::LayerAnimationSequence* sequence) override {}
void OnLayerAnimationAborted(ui::LayerAnimationSequence* sequence) override {}
void OnLayerAnimationScheduled(
ui::LayerAnimationSequence* sequence) override {
if ((sequence->properties() & ui::LayerAnimationElement::TRANSFORM) == 0)
return;
auto* controller = DesksController::Get();
const bool activated = controller->ActivateAdjacentDesk(
going_left_, DesksSwitchSource::kDeskSwitchShortcut);
DCHECK(activated);
// If the animation goes to the last expected desk, remove the observer so
// that the next scheduled animation does not try activating another desk.
// We will then wait for the entire desk switch animation to finish and then
// run |on_desk_animation_complete_|.
if (controller->GetDeskIndex(controller->GetTargetActiveDesk()) ==
target_index_) {
animation_layer_->GetAnimator()->RemoveObserver(this);
}
}
// DesksController::Observer:
void OnDeskAdded(const Desk* desk) override {}
void OnDeskRemoved(const Desk* desk) override {}
void OnDeskActivationChanged(const Desk* activated,
const Desk* deactivated) override {
// The first activation changed happens when the initial ending screenshot
// is being taken. This is the first point when the animation layer we want
// to observe is guaranteed to be set as a child to the root window layer.
if (animation_layer_)
return;
animation_layer_ = DesksController::Get()
->GetAnimationForTesting()
->GetFirstDeskSwitchAnimatorForTesting()
->GetAnimationLayerForTesting();
animation_layer_->GetAnimator()->AddObserver(this);
}
void OnDeskSwitchAnimationLaunching() override {}
void OnDeskSwitchAnimationFinished() override {
std::move(on_desk_animation_complete_).Run();
delete this;
}
private:
const bool going_left_;
const int target_index_;
base::OnceClosure on_desk_animation_complete_;
ui::Layer* animation_layer_ = nullptr;
};
} // namespace } // namespace
AutotestDesksApi::AutotestDesksApi() = default; AutotestDesksApi::AutotestDesksApi() = default;
...@@ -89,4 +176,29 @@ bool AutotestDesksApi::RemoveActiveDesk(base::OnceClosure on_complete) { ...@@ -89,4 +176,29 @@ bool AutotestDesksApi::RemoveActiveDesk(base::OnceClosure on_complete) {
return true; return true;
} }
bool AutotestDesksApi::ActivateAdjacentDesksToTargetIndex(
int index,
base::OnceClosure on_complete) {
DCHECK(!on_complete.is_null());
if (index < 0)
return false;
auto* controller = DesksController::Get();
if (index >= int{controller->desks().size()})
return false;
const Desk* target_desk = controller->desks()[index].get();
if (target_desk == controller->active_desk())
return false;
int active_index = controller->GetDeskIndex(controller->active_desk());
const bool going_left = index < active_index;
new ChainedDeskAnimationObserver(going_left, index, std::move(on_complete));
const bool activated = controller->ActivateAdjacentDesk(
going_left, DesksSwitchSource::kDeskSwitchShortcut);
DCHECK(activated);
return true;
}
} // namespace ash } // namespace ash
...@@ -10,9 +10,41 @@ ...@@ -10,9 +10,41 @@
#include "base/bind_helpers.h" #include "base/bind_helpers.h"
#include "base/run_loop.h" #include "base/run_loop.h"
#include "base/test/scoped_feature_list.h" #include "base/test/scoped_feature_list.h"
#include "ui/compositor/scoped_animation_duration_scale_mode.h"
namespace ash { namespace ash {
namespace {
class TestDesksActivationObserver : public DesksController::Observer {
public:
TestDesksActivationObserver() { DesksController::Get()->AddObserver(this); }
TestDesksActivationObserver(const TestDesksActivationObserver&) = delete;
TestDesksActivationObserver& operator=(const TestDesksActivationObserver&) =
delete;
~TestDesksActivationObserver() override {
DesksController::Get()->RemoveObserver(this);
}
int activation_changes() const { return activation_changes_; }
void set_activation_changes(int val) { activation_changes_ = val; }
// DesksController::Observer:
void OnDeskAdded(const Desk* desk) override {}
void OnDeskRemoved(const Desk* desk) override {}
void OnDeskActivationChanged(const Desk* activated,
const Desk* deactivated) override {
++activation_changes_;
}
void OnDeskSwitchAnimationLaunching() override {}
void OnDeskSwitchAnimationFinished() override {}
private:
int activation_changes_ = 0;
};
} // namespace
using AutotestDesksApiTest = AshTestBase; using AutotestDesksApiTest = AshTestBase;
TEST_F(AutotestDesksApiTest, CreateNewDesk) { TEST_F(AutotestDesksApiTest, CreateNewDesk) {
...@@ -74,4 +106,72 @@ TEST_F(AutotestDesksApiTest, RemoveActiveDesk) { ...@@ -74,4 +106,72 @@ TEST_F(AutotestDesksApiTest, RemoveActiveDesk) {
EXPECT_FALSE(test_api.RemoveActiveDesk(base::DoNothing())); EXPECT_FALSE(test_api.RemoveActiveDesk(base::DoNothing()));
} }
class EnhancedDeskAnimationsAutotestDesksApiTest : public AutotestDesksApiTest {
public:
EnhancedDeskAnimationsAutotestDesksApiTest() = default;
EnhancedDeskAnimationsAutotestDesksApiTest(
const EnhancedDeskAnimationsAutotestDesksApiTest&) = delete;
EnhancedDeskAnimationsAutotestDesksApiTest& operator=(
const EnhancedDeskAnimationsAutotestDesksApiTest&) = delete;
~EnhancedDeskAnimationsAutotestDesksApiTest() override = default;
// AutotestDesksApiTest:
void SetUp() override {
features_.InitAndEnableFeature(features::kEnhancedDeskAnimations);
AutotestDesksApiTest::SetUp();
}
private:
base::test::ScopedFeatureList features_;
};
TEST_F(EnhancedDeskAnimationsAutotestDesksApiTest,
ActivateAdjacentDesksToTargetIndex) {
// Create all desks possible.
AutotestDesksApi test_api;
auto* controller = DesksController::Get();
while (controller->CanCreateDesks())
EXPECT_TRUE(test_api.CreateNewDesk());
EXPECT_FALSE(
test_api.ActivateAdjacentDesksToTargetIndex(-1, base::DoNothing()));
EXPECT_FALSE(
test_api.ActivateAdjacentDesksToTargetIndex(4, base::DoNothing()));
// Activating already active desk does nothing.
EXPECT_FALSE(
test_api.ActivateAdjacentDesksToTargetIndex(0, base::DoNothing()));
EXPECT_EQ(4u, controller->desks().size());
// Replacing needs to be done while a current animation is underway, otherwise
// it will have no effect.
ui::ScopedAnimationDurationScaleMode non_zero(
ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION);
// Activate the rightmost desk. Test that we end on that desk and that we
// observed 3 activation changes.
TestDesksActivationObserver desk_activation_observer;
{
base::RunLoop run_loop;
EXPECT_TRUE(
test_api.ActivateAdjacentDesksToTargetIndex(3, run_loop.QuitClosure()));
run_loop.Run();
}
EXPECT_EQ(controller->active_desk(), controller->desks().back().get());
EXPECT_EQ(3, desk_activation_observer.activation_changes());
// Activate the leftmost desk. Test that we end on that desk and that we
// observed 3 activation changes.
desk_activation_observer.set_activation_changes(0);
{
base::RunLoop run_loop;
EXPECT_TRUE(
test_api.ActivateAdjacentDesksToTargetIndex(0, run_loop.QuitClosure()));
run_loop.Run();
}
EXPECT_EQ(controller->active_desk(), controller->desks().front().get());
EXPECT_EQ(3, desk_activation_observer.activation_changes());
}
} // namespace ash } // namespace ash
...@@ -132,4 +132,10 @@ void DeskAnimationBase::OnDeskSwitchAnimationFinished() { ...@@ -132,4 +132,10 @@ void DeskAnimationBase::OnDeskSwitchAnimationFinished() {
// `this` is now deleted. // `this` is now deleted.
} }
RootWindowDeskSwitchAnimator*
DeskAnimationBase::GetFirstDeskSwitchAnimatorForTesting() const {
DCHECK(!desk_switch_animators_.empty());
return desk_switch_animators_.front().get();
}
} // namespace ash } // namespace ash
...@@ -54,6 +54,8 @@ class DeskAnimationBase : public RootWindowDeskSwitchAnimator::Delegate { ...@@ -54,6 +54,8 @@ class DeskAnimationBase : public RootWindowDeskSwitchAnimator::Delegate {
void OnEndingDeskScreenshotTaken() override; void OnEndingDeskScreenshotTaken() override;
void OnDeskSwitchAnimationFinished() override; void OnDeskSwitchAnimationFinished() override;
RootWindowDeskSwitchAnimator* GetFirstDeskSwitchAnimatorForTesting() const;
protected: protected:
// Abstract functions that can be overridden by child classes to do different // Abstract functions that can be overridden by child classes to do different
// things when phase (1), and phase (3) completes. Note that // things when phase (1), and phase (3) completes. Note that
......
...@@ -432,6 +432,16 @@ void DesksController::OnRootWindowClosing(aura::Window* root_window) { ...@@ -432,6 +432,16 @@ void DesksController::OnRootWindowClosing(aura::Window* root_window) {
desk->OnRootWindowClosing(root_window); desk->OnRootWindowClosing(root_window);
} }
int DesksController::GetDeskIndex(const Desk* desk) const {
for (size_t i = 0; i < desks_.size(); ++i) {
if (desk == desks_[i].get())
return i;
}
NOTREACHED();
return -1;
}
bool DesksController::BelongsToActiveDesk(aura::Window* window) { bool DesksController::BelongsToActiveDesk(aura::Window* window) {
return desks_util::BelongsToActiveDesk(window); return desks_util::BelongsToActiveDesk(window);
} }
...@@ -493,6 +503,11 @@ void DesksController::OnFirstSessionStarted() { ...@@ -493,6 +503,11 @@ void DesksController::OnFirstSessionStarted() {
desks_restore_util::RestorePrimaryUserDesks(); desks_restore_util::RestorePrimaryUserDesks();
} }
DeskAnimationBase* DesksController::GetAnimationForTesting() const {
DCHECK(animation_);
return animation_.get();
}
void DesksController::OnAnimationFinished(DeskAnimationBase* animation) { void DesksController::OnAnimationFinished(DeskAnimationBase* animation) {
DCHECK_EQ(animation_.get(), animation); DCHECK_EQ(animation_.get(), animation);
animation_.reset(); animation_.reset();
...@@ -505,16 +520,6 @@ bool DesksController::HasDesk(const Desk* desk) const { ...@@ -505,16 +520,6 @@ bool DesksController::HasDesk(const Desk* desk) const {
return iter != desks_.end(); return iter != desks_.end();
} }
int DesksController::GetDeskIndex(const Desk* desk) const {
for (size_t i = 0; i < desks_.size(); ++i) {
if (desk == desks_[i].get())
return i;
}
NOTREACHED();
return -1;
}
void DesksController::ActivateDeskInternal(const Desk* desk, void DesksController::ActivateDeskInternal(const Desk* desk,
bool update_window_activation) { bool update_window_activation) {
DCHECK(HasDesk(desk)); DCHECK(HasDesk(desk));
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#include <vector> #include <vector>
#include "ash/ash_export.h" #include "ash/ash_export.h"
#include "ash/public/cpp/autotest_desks_api.h"
#include "ash/public/cpp/desks_helper.h" #include "ash/public/cpp/desks_helper.h"
#include "ash/public/cpp/session/session_observer.h" #include "ash/public/cpp/session/session_observer.h"
#include "ash/wm/desks/desks_histogram_enums.h" #include "ash/wm/desks/desks_histogram_enums.h"
...@@ -160,6 +161,8 @@ class ASH_EXPORT DesksController : public DesksHelper, ...@@ -160,6 +161,8 @@ class ASH_EXPORT DesksController : public DesksHelper,
void OnRootWindowAdded(aura::Window* root_window); void OnRootWindowAdded(aura::Window* root_window);
void OnRootWindowClosing(aura::Window* root_window); void OnRootWindowClosing(aura::Window* root_window);
int GetDeskIndex(const Desk* desk) const;
// DesksHelper: // DesksHelper:
bool BelongsToActiveDesk(aura::Window* window) override; bool BelongsToActiveDesk(aura::Window* window) override;
...@@ -175,6 +178,8 @@ class ASH_EXPORT DesksController : public DesksHelper, ...@@ -175,6 +178,8 @@ class ASH_EXPORT DesksController : public DesksHelper,
void OnActiveUserSessionChanged(const AccountId& account_id) override; void OnActiveUserSessionChanged(const AccountId& account_id) override;
void OnFirstSessionStarted() override; void OnFirstSessionStarted() override;
DeskAnimationBase* GetAnimationForTesting() const;
private: private:
friend class DeskAnimationBase; friend class DeskAnimationBase;
friend class DeskActivationAnimation; friend class DeskActivationAnimation;
...@@ -184,8 +189,6 @@ class ASH_EXPORT DesksController : public DesksHelper, ...@@ -184,8 +189,6 @@ class ASH_EXPORT DesksController : public DesksHelper,
bool HasDesk(const Desk* desk) const; bool HasDesk(const Desk* desk) const;
int GetDeskIndex(const Desk* desk) const;
// Activates the given |desk| and deactivates the currently active one. |desk| // Activates the given |desk| and deactivates the currently active one. |desk|
// has to be an existing desk. If |update_window_activation| is true, // has to be an existing desk. If |update_window_activation| is true,
// the active desk on the deactivated desk will be deactivated, and the most- // the active desk on the deactivated desk will be deactivated, and the most-
......
...@@ -339,6 +339,10 @@ void RootWindowDeskSwitchAnimator::OnImplicitAnimationsCompleted() { ...@@ -339,6 +339,10 @@ void RootWindowDeskSwitchAnimator::OnImplicitAnimationsCompleted() {
delegate_->OnDeskSwitchAnimationFinished(); delegate_->OnDeskSwitchAnimationFinished();
} }
ui::Layer* RootWindowDeskSwitchAnimator::GetAnimationLayerForTesting() const {
return animation_layer_owner_->root();
}
void RootWindowDeskSwitchAnimator::CompleteAnimationPhase1WithLayer( void RootWindowDeskSwitchAnimator::CompleteAnimationPhase1WithLayer(
std::unique_ptr<ui::Layer> layer) { std::unique_ptr<ui::Layer> layer) {
DCHECK(layer); DCHECK(layer);
......
...@@ -249,6 +249,8 @@ class ASH_EXPORT RootWindowDeskSwitchAnimator ...@@ -249,6 +249,8 @@ class ASH_EXPORT RootWindowDeskSwitchAnimator
// ui::ImplicitAnimationObserver: // ui::ImplicitAnimationObserver:
void OnImplicitAnimationsCompleted() override; void OnImplicitAnimationsCompleted() override;
ui::Layer* GetAnimationLayerForTesting() const;
private: private:
friend class RootWindowDeskSwitchAnimatorTestApi; friend class RootWindowDeskSwitchAnimatorTestApi;
......
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