#include "ash/wm/overview/overview_grid.h"
#include "ash/screen_util.h"
#include "ash/shell.h"
#include "ash/test/ash_test_base.h"
#include "ash/wm/desks/window_occlusion_calculator.h"
#include "ash/wm/overview/overview_item.h"
#include "ash/wm/overview/overview_metrics.h"
#include "ash/wm/overview/overview_test_base.h"
#include "ash/wm/overview/overview_test_util.h"
#include "ash/wm/splitview/split_view_controller.h"
#include "ash/wm/tablet_mode/tablet_mode_controller.h"
#include "ash/wm/window_state.h"
#include "ash/wm/workspace/backdrop_controller.h"
#include "ash/wm/workspace/workspace_layout_manager.h"
#include "ash/wm/workspace_controller.h"
#include "base/memory/raw_ptr.h"
#include "base/strings/string_number_conversions.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "chromeos/constants/chromeos_features.h"
#include "ui/aura/client/aura_constants.h"
#include "ui/aura/window.h"
#include "ui/aura/window_tree_host.h"
#include "ui/compositor/compositor.h"
#include "ui/compositor/test/test_utils.h"
#include "ui/display/manager/display_manager.h"
#include "ui/display/screen.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/scoped_animation_duration_scale_mode.h"
#include "ui/wm/core/window_util.h"
namespace ash {
class OverviewGridTest : public OverviewTestBase {
public:
OverviewGridTest() = default;
OverviewGridTest(const OverviewGridTest&) = delete;
OverviewGridTest& operator=(const OverviewGridTest&) = delete;
~OverviewGridTest() override = default;
void CalculateShouldAnimateWhenExiting(
OverviewItemBase* selected_item = nullptr) {
ASSERT_EQ(1u, Shell::GetAllRootWindows().size());
OverviewGrid* grid = GetOverviewGridForRoot(Shell::GetPrimaryRootWindow());
ASSERT_TRUE(grid);
grid->CalculateWindowListAnimationStates(selected_item,
OverviewTransition::kExit,
{});
}
void VerifyAnimationStates(
const std::vector<bool>& expected_enter_animations,
const std::vector<bool>& expected_exit_animations) {
ASSERT_EQ(1u, Shell::GetAllRootWindows().size());
OverviewGrid* grid = GetOverviewGridForRoot(Shell::GetPrimaryRootWindow());
ASSERT_TRUE(grid);
const std::vector<std::unique_ptr<OverviewItemBase>>& overview_items =
grid->item_list();
if (!expected_enter_animations.empty()) {
ASSERT_EQ(overview_items.size(), expected_enter_animations.size());
for (size_t i = 0; i < overview_items.size(); ++i) {
EXPECT_EQ(expected_enter_animations[i],
overview_items[i]->should_animate_when_entering());
}
}
if (!expected_exit_animations.empty()) {
ASSERT_EQ(overview_items.size(), expected_exit_animations.size());
for (size_t i = 0; i < overview_items.size(); ++i) {
EXPECT_EQ(expected_exit_animations[i],
overview_items[i]->should_animate_when_exiting());
}
}
}
};
TEST_F(OverviewGridTest, AnimateWithSingleWindow) {
auto window = CreateAppWindow(gfx::Rect(100, 100));
ToggleOverview();
VerifyAnimationStates({true}, {});
CalculateShouldAnimateWhenExiting();
VerifyAnimationStates({}, {true});
}
TEST_F(OverviewGridTest, SourceHiddenDestinationShown) {
auto window1 = CreateAppWindow(gfx::Rect(100, 100));
auto window2 = CreateAppWindow(gfx::Rect(200, 200));
ToggleOverview();
VerifyAnimationStates({true, true}, {});
CalculateShouldAnimateWhenExiting();
VerifyAnimationStates({}, {true, true});
}
TEST_F(OverviewGridTest, SourceShownDestinationHidden) {
auto window1 = CreateAppWindow(gfx::Rect(100, 100));
WindowState::Get(window1.get())->Maximize();
auto window2 = CreateAppWindow(gfx::Rect(400, 400));
ToggleOverview();
VerifyAnimationStates({true, true}, {});
CalculateShouldAnimateWhenExiting();
VerifyAnimationStates({}, {true, true});
}
TEST_F(OverviewGridTest, SourceShownButInTheUnionOfTwoOtherWindows) {
auto window3 = CreateAppWindow(gfx::Rect(50, 200));
auto window2 = CreateAppWindow(gfx::Rect(50, 50, 150, 150));
auto window1 = CreateAppWindow(gfx::Rect(100, 100));
ToggleOverview();
VerifyAnimationStates({true, true, true}, {});
CalculateShouldAnimateWhenExiting();
VerifyAnimationStates({}, {true, true, true});
}
TEST_F(OverviewGridTest, AlwaysOnTopWindow) {
UpdateDisplay("800x600");
auto window2 = CreateAppWindow(gfx::Rect(100, 100));
window2->SetProperty(aura::client::kZOrderingKey,
ui::ZOrderLevel::kFloatingWindow);
auto window1 = CreateAppWindow(gfx::Rect(100, 100));
WindowState::Get(window1.get())->Maximize();
ToggleOverview();
VerifyAnimationStates({true, true}, {});
CalculateShouldAnimateWhenExiting();
VerifyAnimationStates({}, {true, true});
}
TEST_F(OverviewGridTest, MinimizedWindows) {
UpdateDisplay("800x600");
auto window3 = CreateAppWindow(gfx::Rect(800, 600));
WindowState::Get(window3.get())->Minimize();
auto window2 = CreateAppWindow(gfx::Rect(800, 600));
WindowState::Get(window2.get())->Minimize();
auto window1 = CreateAppWindow(gfx::Rect(10, 10, 780, 580));
ToggleOverview();
VerifyAnimationStates({true, false, false}, {});
CalculateShouldAnimateWhenExiting();
VerifyAnimationStates({}, {true, false, false});
}
TEST_F(OverviewGridTest, SelectedWindow) {
auto window3 = CreateAppWindow(gfx::Rect(400, 400));
WindowState::Get(window3.get())->Maximize();
auto window2 = CreateAppWindow(gfx::Rect(400, 400));
auto window1 = CreateAppWindow(gfx::Rect(100, 100));
ToggleOverview();
VerifyAnimationStates({true, true, true}, {});
OverviewItemBase* selected_item = GetOverviewItemForWindow(window3.get());
CalculateShouldAnimateWhenExiting(selected_item);
VerifyAnimationStates({}, {false, false, true});
}
TEST_F(OverviewGridTest, WindowWithBackdrop) {
auto window1 = CreateTestWindow(gfx::Rect(100, 100));
auto window2 = CreateTestWindow(gfx::Rect(400, 400));
window1->SetProperty(aura::client::kResizeBehaviorKey,
aura::client::kResizeBehaviorNone);
wm::ActivateWindow(window1.get());
Shell::Get()->tablet_mode_controller()->SetEnabledForTest(true);
BackdropController* backdrop_controller =
GetWorkspaceControllerForContext(window1.get())
->layout_manager()
->backdrop_controller();
EXPECT_EQ(window1.get(), backdrop_controller->GetTopmostWindowWithBackdrop());
EXPECT_TRUE(backdrop_controller->backdrop_window());
EXPECT_TRUE(WindowState::Get(window2.get())->IsMaximized());
ToggleOverview();
VerifyAnimationStates({true, false}, {});
CalculateShouldAnimateWhenExiting();
VerifyAnimationStates({}, {true, true});
}
TEST_F(OverviewGridTest, SourcePartiallyOffscreenWindow) {
UpdateDisplay("500x400");
auto window2 = CreateAppWindow(gfx::Rect(450, 100, 100, 100));
auto window1 = CreateAppWindow(gfx::Rect(100, 100));
ToggleOverview();
VerifyAnimationStates({true, true}, {});
CalculateShouldAnimateWhenExiting();
VerifyAnimationStates({}, {true, true});
ToggleOverview();
WindowState::Get(window1.get())->Maximize();
ToggleOverview();
VerifyAnimationStates({true, false}, {});
}
TEST_F(OverviewGridTest, PartialAndFullOffscreenWindow) {
UpdateDisplay("800x600");
Shell::Get()->tablet_mode_controller()->SetEnabledForTest(true);
std::vector<std::unique_ptr<aura::Window>> windows;
for (int i = 0; i < 12; ++i) {
windows.push_back(CreateAppWindow(gfx::Rect(100, 100)));
}
ToggleOverview();
OverviewItemBase* partially_offscreen_item =
GetOverviewItemForWindow(windows[2].get());
OverviewItemBase* fully_offscreen_item =
GetOverviewItemForWindow(windows[0].get());
ASSERT_FALSE(gfx::RectF(800.f, 600.f)
.Contains(partially_offscreen_item->target_bounds()));
ASSERT_TRUE(gfx::RectF(800.f, 600.f)
.Intersects(partially_offscreen_item->target_bounds()));
ASSERT_FALSE(
gfx::RectF(800.f, 600.f).Contains(fully_offscreen_item->target_bounds()));
ASSERT_FALSE(gfx::RectF(800.f, 600.f)
.Intersects(fully_offscreen_item->target_bounds()));
EXPECT_FALSE(partially_offscreen_item->should_animate_when_entering());
EXPECT_FALSE(fully_offscreen_item->should_animate_when_entering());
CalculateShouldAnimateWhenExiting();
EXPECT_FALSE(partially_offscreen_item->should_animate_when_exiting());
EXPECT_FALSE(fully_offscreen_item->should_animate_when_exiting());
}
TEST_F(OverviewGridTest, SnappedWindow) {
auto window3 = CreateAppWindow(gfx::Rect(100, 100));
auto window2 = CreateAppWindow(gfx::Rect(100, 100));
auto window1 = CreateAppWindow(gfx::Rect(100, 100));
Shell::Get()->tablet_mode_controller()->SetEnabledForTest(true);
auto* split_view_controller =
SplitViewController::Get(Shell::GetPrimaryRootWindow());
split_view_controller->SnapWindow(window1.get(), SnapPosition::kPrimary);
split_view_controller->SnapWindow(window2.get(), SnapPosition::kSecondary);
EXPECT_TRUE(WindowState::Get(window3.get())->IsMaximized());
ToggleOverview();
VerifyAnimationStates({true, false}, {});
}
TEST_F(OverviewGridTest, RecordsDelayedDeskBarPresentationMetric) {
gfx::ScopedAnimationDurationScaleMode animation_scale(
gfx::ScopedAnimationDurationScaleMode::SLOW_DURATION);
std::unique_ptr<aura::Window> window1(CreateTestWindow());
std::unique_ptr<aura::Window> window2(CreateTestWindow());
ui::Compositor* const compositor = window1->GetHost()->compositor();
base::HistogramTester histogram_tester;
ToggleOverview();
ASSERT_TRUE(ui::WaitForNextFrameToBePresented(compositor));
histogram_tester.ExpectTotalCount(
kOverviewDelayedDeskBarPresentationHistogram, 0);
WaitForOverviewEnterAnimation();
ASSERT_TRUE(ui::WaitForNextFrameToBePresented(compositor));
histogram_tester.ExpectTotalCount(
kOverviewDelayedDeskBarPresentationHistogram, 1);
}
TEST_F(OverviewGridTest, DoesNotRecordDelayedDeskBarPresentationMetric) {
gfx::ScopedAnimationDurationScaleMode animation_scale(
gfx::ScopedAnimationDurationScaleMode::FAST_DURATION);
std::unique_ptr<aura::Window> window1(CreateTestWindow());
std::unique_ptr<aura::Window> window2(CreateTestWindow());
WindowState::Get(window1.get())->Maximize();
WindowState::Get(window2.get())->Maximize();
ui::Compositor* const compositor = window1->GetHost()->compositor();
base::HistogramTester histogram_tester;
ToggleOverview();
ASSERT_TRUE(ui::WaitForNextFrameToBePresented(compositor));
WaitForOverviewEnterAnimation();
ASSERT_TRUE(ui::WaitForNextFrameToBePresented(compositor));
histogram_tester.ExpectTotalCount(
kOverviewDelayedDeskBarPresentationHistogram, 0);
}
}