Index: ash/wm/solo_window_tracker_unittest.cc |
diff --git a/ash/wm/solo_window_tracker_unittest.cc b/ash/wm/solo_window_tracker_unittest.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..d134c4089ab6d129d6ae0a96553458c7d8c0ea21 |
--- /dev/null |
+++ b/ash/wm/solo_window_tracker_unittest.cc |
@@ -0,0 +1,429 @@ |
+// Copyright 2013 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/wm/solo_window_tracker.h" |
+ |
+#include "ash/ash_constants.h" |
+#include "ash/ash_switches.h" |
+#include "ash/root_window_controller.h" |
+#include "ash/screen_ash.h" |
+#include "ash/shell.h" |
+#include "ash/shell_window_ids.h" |
+#include "ash/test/ash_test_base.h" |
+#include "ash/wm/window_resizer.h" |
+#include "ash/wm/window_state.h" |
+#include "base/memory/scoped_ptr.h" |
+#include "ui/aura/client/aura_constants.h" |
+#include "ui/aura/root_window.h" |
+#include "ui/aura/test/event_generator.h" |
+#include "ui/aura/window.h" |
+#include "ui/aura/window_observer.h" |
+#include "ui/base/hit_test.h" |
+#include "ui/gfx/screen.h" |
+ |
+namespace ash { |
+ |
+namespace { |
+ |
+class WindowRepaintChecker : public aura::WindowObserver { |
+ public: |
+ explicit WindowRepaintChecker(aura::Window* window) |
+ : window_(window), |
+ is_paint_scheduled_(false) { |
+ window_->AddObserver(this); |
+ } |
+ |
+ virtual ~WindowRepaintChecker() { |
+ if (window_) |
+ window_->RemoveObserver(this); |
+ } |
+ |
+ bool IsPaintScheduledAndReset() { |
+ bool result = is_paint_scheduled_; |
+ is_paint_scheduled_ = false; |
+ return result; |
+ } |
+ |
+ private: |
+ // aura::WindowObserver overrides: |
+ virtual void OnWindowPaintScheduled(aura::Window* window, |
+ const gfx::Rect& region) OVERRIDE { |
+ is_paint_scheduled_ = true; |
+ } |
+ virtual void OnWindowDestroyed(aura::Window* window) OVERRIDE { |
+ DCHECK_EQ(window_, window); |
+ window_ = NULL; |
+ } |
+ |
+ aura::Window* window_; |
+ bool is_paint_scheduled_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(WindowRepaintChecker); |
+}; |
+ |
+} // namespace |
+ |
+class SoloWindowTrackerTest : public test::AshTestBase { |
+ public: |
+ SoloWindowTrackerTest() { |
+ } |
+ virtual ~SoloWindowTrackerTest() { |
+ } |
+ |
+ // Helpers methods to create test windows in the primary root window. |
+ aura::Window* CreateWindowInPrimary() { |
+ aura::Window* window = new aura::Window(NULL); |
+ window->SetType(ui::wm::WINDOW_TYPE_NORMAL); |
+ window->Init(aura::WINDOW_LAYER_TEXTURED); |
+ window->SetBounds(gfx::Rect(100, 100)); |
+ ParentWindowInPrimaryRootWindow(window); |
+ return window; |
+ } |
+ aura::Window* CreateAlwaysOnTopWindowInPrimary() { |
+ aura::Window* window = new aura::Window(NULL); |
+ window->SetType(ui::wm::WINDOW_TYPE_NORMAL); |
+ window->Init(aura::WINDOW_LAYER_TEXTURED); |
+ window->SetBounds(gfx::Rect(100, 100)); |
+ window->SetProperty(aura::client::kAlwaysOnTopKey, true); |
+ ParentWindowInPrimaryRootWindow(window); |
+ return window; |
+ } |
+ aura::Window* CreatePanelWindowInPrimary() { |
+ aura::Window* window = new aura::Window(NULL); |
+ window->SetType(ui::wm::WINDOW_TYPE_PANEL); |
+ window->Init(aura::WINDOW_LAYER_TEXTURED); |
+ window->SetBounds(gfx::Rect(100, 100)); |
+ ParentWindowInPrimaryRootWindow(window); |
+ return window; |
+ } |
+ |
+ // Drag |window| to the dock. |
+ void DockWindow(aura::Window* window) { |
+ // Because the tests use windows without delegates, |
+ // aura::test::EventGenerator cannot be used. |
+ gfx::Point drag_to = |
+ ash::ScreenAsh::GetDisplayBoundsInParent(window).top_right(); |
+ scoped_ptr<WindowResizer> resizer(CreateWindowResizer( |
+ window, |
+ window->bounds().origin(), |
+ HTCAPTION, |
+ aura::client::WINDOW_MOVE_SOURCE_MOUSE)); |
+ resizer->Drag(drag_to, 0); |
+ resizer->CompleteDrag(); |
+ EXPECT_EQ(internal::kShellWindowId_DockedContainer, |
+ window->parent()->id()); |
+ } |
+ |
+ // Drag |window| out of the dock. |
+ void UndockWindow(aura::Window* window) { |
+ gfx::Point drag_to = |
+ ash::ScreenAsh::GetDisplayWorkAreaBoundsInParent(window).top_right() - |
+ gfx::Vector2d(10, 0); |
+ scoped_ptr<WindowResizer> resizer(CreateWindowResizer( |
+ window, |
+ window->bounds().origin(), |
+ HTCAPTION, |
+ aura::client::WINDOW_MOVE_SOURCE_MOUSE)); |
+ resizer->Drag(drag_to, 0); |
+ resizer->CompleteDrag(); |
+ EXPECT_NE(internal::kShellWindowId_DockedContainer, |
+ window->parent()->id()); |
+ } |
+ |
+ // Returns the primary display. |
+ gfx::Display GetPrimaryDisplay() { |
+ return ash::Shell::GetInstance()->GetScreen()->GetPrimaryDisplay(); |
+ } |
+ |
+ // Returns the secondary display. |
+ gfx::Display GetSecondaryDisplay() { |
+ return ScreenAsh::GetSecondaryDisplay(); |
+ } |
+ |
+ // Returns the window which uses the solo header, if any, on the primary |
+ // display. |
+ aura::Window* GetWindowWithSoloHeaderInPrimary() { |
+ return GetWindowWithSoloHeader(Shell::GetPrimaryRootWindow()); |
+ } |
+ |
+ // Returns the window which uses the solo header, if any, in |root|. |
+ aura::Window* GetWindowWithSoloHeader(aura::Window* root) { |
+ SoloWindowTracker* solo_window_tracker = |
+ internal::GetRootWindowController(root)->solo_window_tracker(); |
+ return solo_window_tracker ? |
+ solo_window_tracker->GetWindowWithSoloHeader() : NULL; |
+ } |
+ |
+ private: |
+ DISALLOW_COPY_AND_ASSIGN(SoloWindowTrackerTest); |
+}; |
+ |
+TEST_F(SoloWindowTrackerTest, Basic) { |
+ scoped_ptr<aura::Window> w1(CreateWindowInPrimary()); |
+ w1->Show(); |
+ |
+ // We only have one window, so it should use a solo header. |
+ EXPECT_EQ(w1.get(), GetWindowWithSoloHeaderInPrimary()); |
+ |
+ // Create a second window. |
+ scoped_ptr<aura::Window> w2(CreateWindowInPrimary()); |
+ w2->Show(); |
+ |
+ // Now there are two windows, so we should not use solo headers. |
+ EXPECT_EQ(NULL, GetWindowWithSoloHeaderInPrimary()); |
+ |
+ // Hide one window. Solo should be enabled. |
+ w2->Hide(); |
+ EXPECT_EQ(w1.get(), GetWindowWithSoloHeaderInPrimary()); |
+ |
+ // Show that window. Solo should be disabled. |
+ w2->Show(); |
+ EXPECT_EQ(NULL, GetWindowWithSoloHeaderInPrimary()); |
+ |
+ // Minimize the first window. Solo should be enabled. |
+ wm::GetWindowState(w1.get())->Minimize(); |
+ EXPECT_EQ(w2.get(), GetWindowWithSoloHeaderInPrimary()); |
+ |
+ // Close the minimized window. |
+ w1.reset(); |
+ EXPECT_EQ(w2.get(), GetWindowWithSoloHeaderInPrimary()); |
+ |
+ // Open an always-on-top window (which lives in a different container). |
+ scoped_ptr<aura::Window> w3(CreateAlwaysOnTopWindowInPrimary()); |
+ w3->Show(); |
+ EXPECT_EQ(NULL, GetWindowWithSoloHeaderInPrimary()); |
+ |
+ // Close the always-on-top window. |
+ w3.reset(); |
+ EXPECT_EQ(w2.get(), GetWindowWithSoloHeaderInPrimary()); |
+} |
+ |
+// Test that docked windows never use the solo header and that the presence of a |
+// docked window prevents all other windows from the using the solo window |
+// header. |
+TEST_F(SoloWindowTrackerTest, DockedWindow) { |
+ if (!switches::UseDockedWindows() || !SupportsHostWindowResize()) |
+ return; |
+ |
+ scoped_ptr<aura::Window> w1(CreateWindowInPrimary()); |
+ w1->Show(); |
+ EXPECT_EQ(w1.get(), GetWindowWithSoloHeaderInPrimary()); |
+ |
+ DockWindow(w1.get()); |
+ EXPECT_EQ(NULL, GetWindowWithSoloHeaderInPrimary()); |
+ |
+ UndockWindow(w1.get()); |
+ EXPECT_EQ(w1.get(), GetWindowWithSoloHeaderInPrimary()); |
+ |
+ scoped_ptr<aura::Window> w2(CreateWindowInPrimary()); |
+ w2->Show(); |
+ EXPECT_EQ(NULL, GetWindowWithSoloHeaderInPrimary()); |
+ |
+ DockWindow(w2.get()); |
+ EXPECT_EQ(NULL, GetWindowWithSoloHeaderInPrimary()); |
+ |
+ wm::GetWindowState(w2.get())->Minimize(); |
+ EXPECT_EQ(w1.get(), GetWindowWithSoloHeaderInPrimary()); |
+} |
+ |
+// Panels should not "count" for computing solo window headers, and the panel |
+// itself should never use the solo header. |
+TEST_F(SoloWindowTrackerTest, Panel) { |
+ scoped_ptr<aura::Window> w1(CreateWindowInPrimary()); |
+ w1->Show(); |
+ |
+ // We only have one window, so it should use a solo header. |
+ EXPECT_EQ(w1.get(), GetWindowWithSoloHeaderInPrimary()); |
+ |
+ // Create a panel window. |
+ scoped_ptr<aura::Window> w2(CreatePanelWindowInPrimary()); |
+ w2->Show(); |
+ |
+ // Despite two windows, the first window should still be considered "solo" |
+ // because panels aren't included in the computation. |
+ EXPECT_EQ(w1.get(), GetWindowWithSoloHeaderInPrimary()); |
+ |
+ // Even after closing the first window, the panel is still not considered |
+ // solo. |
+ w1.reset(); |
+ EXPECT_EQ(NULL, GetWindowWithSoloHeaderInPrimary()); |
+} |
+ |
+// Modal dialogs should not use solo headers. |
+TEST_F(SoloWindowTrackerTest, Modal) { |
+ scoped_ptr<aura::Window> w1(CreateWindowInPrimary()); |
+ w1->Show(); |
+ |
+ // We only have one window, so it should use a solo header. |
+ EXPECT_EQ(w1.get(), GetWindowWithSoloHeaderInPrimary()); |
+ |
+ // Create a fake modal window. |
+ scoped_ptr<aura::Window> w2(CreateWindowInPrimary()); |
+ w2->SetProperty(aura::client::kModalKey, ui::MODAL_TYPE_WINDOW); |
+ w2->Show(); |
+ |
+ // Despite two windows, the first window should still be considered "solo" |
+ // because modal windows aren't included in the computation. |
+ EXPECT_EQ(w1.get(), GetWindowWithSoloHeaderInPrimary()); |
+} |
+ |
+// Constrained windows should not use solo headers. |
+TEST_F(SoloWindowTrackerTest, Constrained) { |
+ scoped_ptr<aura::Window> w1(CreateWindowInPrimary()); |
+ w1->Show(); |
+ |
+ // We only have one window, so it should use a solo header. |
+ EXPECT_EQ(w1.get(), GetWindowWithSoloHeaderInPrimary()); |
+ |
+ // Create a fake constrained window. |
+ scoped_ptr<aura::Window> w2(CreateWindowInPrimary()); |
+ w2->SetProperty(aura::client::kConstrainedWindowKey, true); |
+ w2->Show(); |
+ |
+ // Despite two windows, the first window should still be considered "solo" |
+ // because constrained windows aren't included in the computation. |
+ EXPECT_EQ(w1.get(), GetWindowWithSoloHeaderInPrimary()); |
+} |
+ |
+// Non-drawing windows should not affect the solo computation. |
+TEST_F(SoloWindowTrackerTest, NotDrawn) { |
+ aura::Window* w = CreateWindowInPrimary(); |
+ w->Show(); |
+ |
+ // We only have one window, so it should use a solo header. |
+ EXPECT_EQ(w, GetWindowWithSoloHeaderInPrimary()); |
+ |
+ // Create non-drawing window similar to DragDropTracker. |
+ aura::Window* not_drawn = new aura::Window(NULL); |
+ not_drawn->SetType(ui::wm::WINDOW_TYPE_NORMAL); |
+ not_drawn->Init(aura::WINDOW_LAYER_NOT_DRAWN); |
+ ParentWindowInPrimaryRootWindow(not_drawn); |
+ not_drawn->Show(); |
+ |
+ // Despite two windows, the first window should still be considered "solo" |
+ // because non-drawing windows aren't included in the computation. |
+ EXPECT_EQ(w, GetWindowWithSoloHeaderInPrimary()); |
+} |
+ |
+TEST_F(SoloWindowTrackerTest, MultiDisplay) { |
+ if (!SupportsMultipleDisplays()) |
+ return; |
+ |
+ UpdateDisplay("1000x600,600x400"); |
+ |
+ scoped_ptr<aura::Window> w1(CreateWindowInPrimary()); |
+ w1->SetBoundsInScreen(gfx::Rect(0, 0, 100, 100), GetPrimaryDisplay()); |
+ w1->Show(); |
+ WindowRepaintChecker checker1(w1.get()); |
+ scoped_ptr<aura::Window> w2(CreateWindowInPrimary()); |
+ w2->SetBoundsInScreen(gfx::Rect(0, 0, 100, 100), GetPrimaryDisplay()); |
+ w2->Show(); |
+ WindowRepaintChecker checker2(w2.get()); |
+ |
+ // Now there are two windows in the same display, so we should not use solo |
+ // headers. |
+ EXPECT_EQ(NULL, GetWindowWithSoloHeaderInPrimary()); |
+ EXPECT_TRUE(checker1.IsPaintScheduledAndReset()); |
+ |
+ // Moves the second window to the secondary display. Both w1/w2 should be |
+ // solo. |
+ w2->SetBoundsInScreen(gfx::Rect(1200, 0, 100, 100), |
+ ScreenAsh::GetSecondaryDisplay()); |
+ EXPECT_EQ(w1.get(), GetWindowWithSoloHeaderInPrimary()); |
+ EXPECT_EQ(w2.get(), GetWindowWithSoloHeader(w2->GetRootWindow())); |
+ EXPECT_TRUE(checker1.IsPaintScheduledAndReset()); |
+ EXPECT_TRUE(checker2.IsPaintScheduledAndReset()); |
+ |
+ // Open two more windows in the primary display. |
+ scoped_ptr<aura::Window> w3(CreateWindowInPrimary()); |
+ w3->SetBoundsInScreen(gfx::Rect(0, 0, 100, 100), GetPrimaryDisplay()); |
+ w3->Show(); |
+ scoped_ptr<aura::Window> w4(CreateWindowInPrimary()); |
+ w4->SetBoundsInScreen(gfx::Rect(0, 0, 100, 100), GetPrimaryDisplay()); |
+ w4->Show(); |
+ |
+ // Because the primary display has three windows w1, w3, and w4, they |
+ // shouldn't be solo. w2 should be solo. |
+ EXPECT_EQ(NULL, GetWindowWithSoloHeaderInPrimary()); |
+ EXPECT_EQ(w2.get(), GetWindowWithSoloHeader(w2->GetRootWindow())); |
+ EXPECT_TRUE(checker1.IsPaintScheduledAndReset()); |
+ |
+ // Move w4 to the secondary display. Now w2 shouldn't be solo anymore. |
+ w4->SetBoundsInScreen(gfx::Rect(1200, 0, 100, 100), GetSecondaryDisplay()); |
+ EXPECT_EQ(NULL, GetWindowWithSoloHeaderInPrimary()); |
+ EXPECT_EQ(NULL, GetWindowWithSoloHeader(w2->GetRootWindow())); |
+ EXPECT_TRUE(checker2.IsPaintScheduledAndReset()); |
+ |
+ // Moves w3 to the secondary display too. Now w1 should be solo again. |
+ w3->SetBoundsInScreen(gfx::Rect(1200, 0, 100, 100), GetSecondaryDisplay()); |
+ EXPECT_EQ(w1.get(), GetWindowWithSoloHeaderInPrimary()); |
+ EXPECT_EQ(NULL, GetWindowWithSoloHeader(w2->GetRootWindow())); |
+ EXPECT_TRUE(checker1.IsPaintScheduledAndReset()); |
+ |
+ // Change w3's state to maximize. Doesn't affect w1. |
+ wm::GetWindowState(w3.get())->Maximize(); |
+ EXPECT_EQ(w1.get(), GetWindowWithSoloHeaderInPrimary()); |
+ EXPECT_EQ(NULL, GetWindowWithSoloHeader(w2->GetRootWindow())); |
+ |
+ // Close w3 and w4. |
+ w3.reset(); |
+ w4.reset(); |
+ EXPECT_EQ(w1.get(), GetWindowWithSoloHeaderInPrimary()); |
+ EXPECT_EQ(w2.get(), GetWindowWithSoloHeader(w2->GetRootWindow())); |
+ EXPECT_TRUE(checker2.IsPaintScheduledAndReset()); |
+ |
+ // Move w2 back to the primary display. |
+ w2->SetBoundsInScreen(gfx::Rect(0, 0, 100, 100), GetPrimaryDisplay()); |
+ EXPECT_EQ(w1->GetRootWindow(), w2->GetRootWindow()); |
+ EXPECT_EQ(NULL, GetWindowWithSoloHeaderInPrimary()); |
+ EXPECT_TRUE(checker1.IsPaintScheduledAndReset()); |
+ EXPECT_TRUE(checker2.IsPaintScheduledAndReset()); |
+ |
+ // Close w2. |
+ w2.reset(); |
+ EXPECT_EQ(w1.get(), GetWindowWithSoloHeaderInPrimary()); |
+ EXPECT_TRUE(checker1.IsPaintScheduledAndReset()); |
+} |
+ |
+TEST_F(SoloWindowTrackerTest, ChildWindowVisibility) { |
+ aura::Window* w = CreateWindowInPrimary(); |
+ w->Show(); |
+ |
+ // We only have one window, so it should use a solo header. |
+ EXPECT_EQ(w, GetWindowWithSoloHeaderInPrimary()); |
+ |
+ // Create a child window. This should not affect the solo-ness of |w1|. |
+ aura::Window* child = new aura::Window(NULL); |
+ child->SetType(ui::wm::WINDOW_TYPE_CONTROL); |
+ child->Init(aura::WINDOW_LAYER_TEXTURED); |
+ child->SetBounds(gfx::Rect(100, 100)); |
+ w->AddChild(child); |
+ child->Show(); |
+ EXPECT_EQ(w, GetWindowWithSoloHeaderInPrimary()); |
+ |
+ // Changing the visibility of |child| should not affect the solo-ness of |w1|. |
+ child->Hide(); |
+ EXPECT_EQ(w, GetWindowWithSoloHeaderInPrimary()); |
+} |
+ |
+TEST_F(SoloWindowTrackerTest, CreateAndDeleteSingleWindow) { |
+ // Ensure that creating/deleting a window works well and doesn't cause |
+ // crashes. See crbug.com/155634 |
+ scoped_ptr<aura::Window> w(CreateWindowInPrimary()); |
+ w->Show(); |
+ |
+ // We only have one window, so it should use a solo header. |
+ EXPECT_EQ(w.get(), GetWindowWithSoloHeaderInPrimary()); |
+ |
+ // Close the window. |
+ w.reset(); |
+ EXPECT_EQ(NULL, GetWindowWithSoloHeaderInPrimary()); |
+ |
+ // Recreate another window again. |
+ w.reset(CreateWindowInPrimary()); |
+ w->Show(); |
+ EXPECT_EQ(w.get(), GetWindowWithSoloHeaderInPrimary()); |
+} |
+ |
+} // namespace ash |