blob: 2afaeb636fe90112ad8150a041689aa2589e97e5 [file] [log] [blame]
// 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 <vector>
#include "ash/public/cpp/ash_features.h"
#include "ash/shell.h"
#include "ash/test/ash_test_base.h"
#include "ash/wm/desks/close_desk_button.h"
#include "ash/wm/desks/desk.h"
#include "ash/wm/desks/desk_mini_view.h"
#include "ash/wm/desks/desks_bar_view.h"
#include "ash/wm/desks/desks_controller.h"
#include "ash/wm/desks/desks_util.h"
#include "ash/wm/desks/new_desk_button.h"
#include "ash/wm/overview/overview_controller.h"
#include "ash/wm/overview/overview_grid.h"
#include "ash/wm/overview/overview_session.h"
#include "ash/wm/window_util.h"
#include "base/stl_util.h"
#include "base/test/scoped_feature_list.h"
#include "ui/events/test/event_generator.h"
#include "ui/wm/core/window_util.h"
namespace ash {
namespace {
bool DoesActiveDeskContainWindow(aura::Window* window) {
return DesksController::Get()->active_desk()->windows().contains(window);
}
// Defines an observer to test DesksController notifications.
class TestObserver : public DesksController::Observer {
public:
TestObserver() = default;
~TestObserver() override = default;
const std::vector<const Desk*>& desks() const { return desks_; }
// DesksController::Observer:
void OnDeskAdded(const Desk* desk) override { desks_.emplace_back(desk); }
void OnDeskRemoved(const Desk* desk) override { base::Erase(desks_, desk); }
private:
std::vector<const Desk*> desks_;
DISALLOW_COPY_AND_ASSIGN(TestObserver);
};
class DesksTest : public AshTestBase {
public:
DesksTest() = default;
~DesksTest() override = default;
// AshTestBase:
void SetUp() override {
scoped_feature_list_.InitAndEnableFeature(features::kVirtualDesks);
AshTestBase::SetUp();
}
private:
base::test::ScopedFeatureList scoped_feature_list_;
DISALLOW_COPY_AND_ASSIGN(DesksTest);
};
TEST_F(DesksTest, DesksCreationAndRemoval) {
TestObserver observer;
auto* controller = DesksController::Get();
controller->AddObserver(&observer);
// There's always a default pre-existing desk that cannot be removed.
EXPECT_EQ(1u, controller->desks().size());
EXPECT_FALSE(controller->CanRemoveDesks());
EXPECT_TRUE(controller->CanCreateDesks());
// Add desks until no longer possible.
while (controller->CanCreateDesks())
controller->NewDesk();
// Expect we've reached the max number of desks, and we've been notified only
// with the newly created desks.
EXPECT_EQ(desks_util::kMaxNumberOfDesks, controller->desks().size());
EXPECT_EQ(desks_util::kMaxNumberOfDesks - 1, observer.desks().size());
EXPECT_TRUE(controller->CanRemoveDesks());
// Remove all desks until no longer possible, and expect that there's always
// one default desk remaining.
while (controller->CanRemoveDesks())
controller->RemoveDesk(observer.desks().back());
EXPECT_EQ(1u, controller->desks().size());
EXPECT_FALSE(controller->CanRemoveDesks());
EXPECT_TRUE(controller->CanCreateDesks());
EXPECT_TRUE(observer.desks().empty());
controller->RemoveObserver(&observer);
}
TEST_F(DesksTest, DesksBarViewDeskCreation) {
TestObserver observer;
auto* controller = DesksController::Get();
auto* overview_controller = Shell::Get()->overview_controller();
overview_controller->ToggleOverview();
EXPECT_TRUE(overview_controller->IsSelecting());
const auto* overview_grid =
overview_controller->overview_session()->GetGridWithRootWindow(
Shell::GetPrimaryRootWindow());
// Initially the grid is not offset down when there are no desk mini_views
// once animations are added.
EXPECT_FALSE(overview_grid->IsDesksBarViewActive());
const auto* desks_bar_view = overview_grid->GetDesksBarViewForTesting();
// Since we have a single default desk, there should be no mini_views, and the
// new desk button is enabled.
DCHECK(desks_bar_view);
EXPECT_TRUE(desks_bar_view->mini_views().empty());
EXPECT_TRUE(desks_bar_view->new_desk_button()->enabled());
// Click many times on the new desk button and expect only the max number of
// desks will be created, and the button is no longer enabled.
const gfx::Point button_center =
desks_bar_view->new_desk_button()->GetBoundsInScreen().CenterPoint();
auto* event_generator = GetEventGenerator();
event_generator->MoveMouseTo(button_center);
for (size_t i = 0; i < desks_util::kMaxNumberOfDesks + 2; ++i)
event_generator->ClickLeftButton();
EXPECT_TRUE(overview_grid->IsDesksBarViewActive());
EXPECT_EQ(desks_util::kMaxNumberOfDesks, controller->desks().size());
EXPECT_EQ(controller->desks().size(), desks_bar_view->mini_views().size());
EXPECT_FALSE(controller->CanCreateDesks());
EXPECT_TRUE(controller->CanRemoveDesks());
EXPECT_FALSE(desks_bar_view->new_desk_button()->enabled());
// Hover over one of the mini_views, and expect that the close button becomes
// visible.
const auto* mini_view = desks_bar_view->mini_views().back().get();
EXPECT_FALSE(mini_view->close_desk_button()->visible());
const gfx::Point mini_view_center =
mini_view->GetBoundsInScreen().CenterPoint();
event_generator->MoveMouseTo(mini_view_center);
EXPECT_TRUE(mini_view->close_desk_button()->visible());
// Use the close button to close the desk.
event_generator->MoveMouseTo(
mini_view->close_desk_button()->GetBoundsInScreen().CenterPoint());
event_generator->ClickLeftButton();
// The new desk button is now enabled again.
EXPECT_EQ(desks_util::kMaxNumberOfDesks - 1, controller->desks().size());
EXPECT_EQ(controller->desks().size(), desks_bar_view->mini_views().size());
EXPECT_TRUE(controller->CanCreateDesks());
EXPECT_TRUE(desks_bar_view->new_desk_button()->enabled());
// Exit overview mode and re-enter. Since we have more than one pre-existing
// desks, their mini_views should be created upon construction of the desks
// bar.
overview_controller->ToggleOverview();
EXPECT_FALSE(overview_controller->IsSelecting());
overview_controller->ToggleOverview();
EXPECT_TRUE(overview_controller->IsSelecting());
// Get the new grid and the new desk_bar_view.
overview_grid =
overview_controller->overview_session()->GetGridWithRootWindow(
Shell::GetPrimaryRootWindow());
EXPECT_TRUE(overview_grid->IsDesksBarViewActive());
desks_bar_view = overview_grid->GetDesksBarViewForTesting();
DCHECK(desks_bar_view);
EXPECT_EQ(controller->desks().size(), desks_bar_view->mini_views().size());
EXPECT_TRUE(desks_bar_view->new_desk_button()->enabled());
}
TEST_F(DesksTest, DeskActivation) {
auto* controller = DesksController::Get();
ASSERT_EQ(1u, controller->desks().size());
const Desk* desk_1 = controller->desks()[0].get();
EXPECT_EQ(desk_1, controller->active_desk());
EXPECT_TRUE(desk_1->is_active());
auto* root = Shell::GetPrimaryRootWindow();
EXPECT_TRUE(desk_1->GetDeskContainerForRoot(root)->IsVisible());
EXPECT_EQ(desks_util::GetActiveDeskContainerForRoot(root),
desk_1->GetDeskContainerForRoot(root));
// Create three new desks, and activate the one in the middle.
controller->NewDesk();
controller->NewDesk();
controller->NewDesk();
ASSERT_EQ(4u, controller->desks().size());
const Desk* desk_2 = controller->desks()[1].get();
const Desk* desk_3 = controller->desks()[2].get();
const Desk* desk_4 = controller->desks()[3].get();
controller->ActivateDesk(desk_2);
EXPECT_EQ(desk_2, controller->active_desk());
EXPECT_FALSE(desk_1->is_active());
EXPECT_TRUE(desk_2->is_active());
EXPECT_FALSE(desk_3->is_active());
EXPECT_FALSE(desk_4->is_active());
EXPECT_FALSE(desk_1->GetDeskContainerForRoot(root)->IsVisible());
EXPECT_TRUE(desk_2->GetDeskContainerForRoot(root)->IsVisible());
EXPECT_FALSE(desk_3->GetDeskContainerForRoot(root)->IsVisible());
EXPECT_FALSE(desk_4->GetDeskContainerForRoot(root)->IsVisible());
// Remove the active desk, which is in the middle, activation should move to
// the left, so desk 1 should be activated.
controller->RemoveDesk(desk_2);
ASSERT_EQ(3u, controller->desks().size());
EXPECT_EQ(desk_1, controller->active_desk());
EXPECT_TRUE(desk_1->is_active());
EXPECT_FALSE(desk_3->is_active());
EXPECT_FALSE(desk_4->is_active());
EXPECT_TRUE(desk_1->GetDeskContainerForRoot(root)->IsVisible());
EXPECT_FALSE(desk_3->GetDeskContainerForRoot(root)->IsVisible());
EXPECT_FALSE(desk_4->GetDeskContainerForRoot(root)->IsVisible());
// Remove the active desk, it's the first one on the left, so desk_3 (on the
// right) will be activated.
controller->RemoveDesk(desk_1);
ASSERT_EQ(2u, controller->desks().size());
EXPECT_EQ(desk_3, controller->active_desk());
EXPECT_TRUE(desk_3->is_active());
EXPECT_FALSE(desk_4->is_active());
EXPECT_TRUE(desk_3->GetDeskContainerForRoot(root)->IsVisible());
EXPECT_FALSE(desk_4->GetDeskContainerForRoot(root)->IsVisible());
}
TEST_F(DesksTest, TransientWindows) {
auto* controller = DesksController::Get();
ASSERT_EQ(1u, controller->desks().size());
const Desk* desk_1 = controller->desks()[0].get();
EXPECT_EQ(desk_1, controller->active_desk());
EXPECT_TRUE(desk_1->is_active());
// Create two windows, one is a transient child of the other.
auto win0 = CreateTestWindow(gfx::Rect(0, 0, 250, 100));
auto win1 = CreateTestWindow(gfx::Rect(100, 100, 100, 100),
aura::client::WINDOW_TYPE_POPUP);
::wm::AddTransientChild(win0.get(), win1.get());
EXPECT_EQ(2u, desk_1->windows().size());
EXPECT_TRUE(DoesActiveDeskContainWindow(win0.get()));
EXPECT_TRUE(DoesActiveDeskContainWindow(win1.get()));
auto* root = Shell::GetPrimaryRootWindow();
EXPECT_EQ(desks_util::GetActiveDeskContainerForRoot(root),
desks_util::GetDeskContainerForContext(win0.get()));
EXPECT_EQ(desks_util::GetActiveDeskContainerForRoot(root),
desks_util::GetDeskContainerForContext(win1.get()));
// Create a new desk and activate it.
controller->NewDesk();
const Desk* desk_2 = controller->desks()[1].get();
EXPECT_TRUE(desk_2->windows().empty());
controller->ActivateDesk(desk_2);
EXPECT_FALSE(desk_1->is_active());
EXPECT_TRUE(desk_2->is_active());
// Remove the inactive desk 1, and expect that its windows, including
// transient will move to desk 2.
controller->RemoveDesk(desk_1);
EXPECT_EQ(1u, controller->desks().size());
EXPECT_EQ(desk_2, controller->active_desk());
EXPECT_EQ(2u, desk_2->windows().size());
EXPECT_TRUE(DoesActiveDeskContainWindow(win0.get()));
EXPECT_TRUE(DoesActiveDeskContainWindow(win1.get()));
}
TEST_F(DesksTest, WindowActivation) {
// Create three windows.
auto win0 = CreateTestWindow(gfx::Rect(0, 0, 250, 100));
auto win1 = CreateTestWindow(gfx::Rect(50, 50, 200, 200));
auto win2 = CreateTestWindow(gfx::Rect(100, 100, 100, 100));
EXPECT_TRUE(DoesActiveDeskContainWindow(win0.get()));
EXPECT_TRUE(DoesActiveDeskContainWindow(win1.get()));
EXPECT_TRUE(DoesActiveDeskContainWindow(win2.get()));
// Activate win0 and expects that it remains activated until we switch desks.
wm::ActivateWindow(win0.get());
// Create a new desk and activate it. Expect it's not tracking any windows
// yet.
auto* controller = DesksController::Get();
controller->NewDesk();
ASSERT_EQ(2u, controller->desks().size());
const Desk* desk_1 = controller->desks()[0].get();
const Desk* desk_2 = controller->desks()[1].get();
EXPECT_EQ(desk_1, controller->active_desk());
EXPECT_EQ(3u, desk_1->windows().size());
EXPECT_TRUE(desk_2->windows().empty());
EXPECT_EQ(win0.get(), wm::GetActiveWindow());
// Activate the newly-added desk. Expect that the tracked windows per each
// desk will remain the same.
controller->ActivateDesk(desk_2);
EXPECT_EQ(desk_2, controller->active_desk());
EXPECT_EQ(3u, desk_1->windows().size());
EXPECT_TRUE(desk_2->windows().empty());
// `desk_2` has no windows, so now no window should be active, and windows on
// `desk_1` cannot be activated.
EXPECT_EQ(nullptr, wm::GetActiveWindow());
EXPECT_FALSE(wm::CanActivateWindow(win0.get()));
EXPECT_FALSE(wm::CanActivateWindow(win1.get()));
EXPECT_FALSE(wm::CanActivateWindow(win2.get()));
// Create two new windows, they should now go to desk_2.
auto win3 = CreateTestWindow(gfx::Rect(0, 0, 300, 200));
auto win4 = CreateTestWindow(gfx::Rect(10, 30, 400, 200));
wm::ActivateWindow(win3.get());
EXPECT_EQ(2u, desk_2->windows().size());
EXPECT_TRUE(DoesActiveDeskContainWindow(win3.get()));
EXPECT_TRUE(DoesActiveDeskContainWindow(win4.get()));
EXPECT_FALSE(DoesActiveDeskContainWindow(win0.get()));
EXPECT_FALSE(DoesActiveDeskContainWindow(win1.get()));
EXPECT_FALSE(DoesActiveDeskContainWindow(win2.get()));
EXPECT_EQ(win3.get(), wm::GetActiveWindow());
// Delete `win0` and expect that `desk_1`'s windows will be updated.
win0.reset();
EXPECT_EQ(2u, desk_1->windows().size());
EXPECT_EQ(2u, desk_2->windows().size());
// No change in the activation.
EXPECT_EQ(win3.get(), wm::GetActiveWindow());
// Switch back to `desk_1`. Now we can activate its windows.
controller->ActivateDesk(desk_1);
EXPECT_EQ(desk_1, controller->active_desk());
EXPECT_TRUE(wm::CanActivateWindow(win1.get()));
EXPECT_TRUE(wm::CanActivateWindow(win2.get()));
EXPECT_FALSE(wm::CanActivateWindow(win3.get()));
EXPECT_FALSE(wm::CanActivateWindow(win4.get()));
// After `win0` has been deleted, `win2` is next on the MRU list.
EXPECT_EQ(win2.get(), wm::GetActiveWindow());
// Remove `desk_2` and expect that its windows will be moved to the active
// desk.
controller->RemoveDesk(desk_2);
EXPECT_EQ(1u, controller->desks().size());
EXPECT_EQ(desk_1, controller->active_desk());
EXPECT_EQ(4u, desk_1->windows().size());
EXPECT_TRUE(DoesActiveDeskContainWindow(win3.get()));
EXPECT_TRUE(DoesActiveDeskContainWindow(win4.get()));
// `desk_2`'s windows moved to `desk_1`, but that should not change the
// already active window.
EXPECT_EQ(win2.get(), wm::GetActiveWindow());
// Moved windows can now be activated.
EXPECT_TRUE(wm::CanActivateWindow(win3.get()));
EXPECT_TRUE(wm::CanActivateWindow(win4.get()));
}
// TODO(afakhry): Add more tests:
// - Multi displays.
// - Always on top windows are not tracked by any desk.
// - Reusing containers when desks are removed and created.
} // namespace
} // namespace ash