root/ash/wm/panels/panel_layout_manager_unittest.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. SetUp
  2. CreateNormalWindow
  3. CreatePanelWindow
  4. GetPanelContainer
  5. GetCalloutWidgetForPanel
  6. PanelInScreen
  7. PanelsNotOverlapping
  8. IsPanelAboveLauncherIcon
  9. IsCalloutAboveLauncherIcon
  10. IsPanelCalloutVisible
  11. shelf_view_test
  12. ClickShelfItemForWindow
  13. SetAlignment
  14. GetAlignment
  15. SetShelfAutoHideBehavior
  16. SetShelfVisibilityState
  17. GetShelfView
  18. IsHorizontal
  19. SetUp
  20. TearDown
  21. TEST_P
  22. TEST_F
  23. TEST_F
  24. TEST_F
  25. TEST_F
  26. TEST_F
  27. TEST_F
  28. TEST_F
  29. TEST_F
  30. TEST_F
  31. TEST_F
  32. TEST_F
  33. TEST_F
  34. TEST_F
  35. TEST_F
  36. TEST_F
  37. TEST_F
  38. TEST_F
  39. TEST_F
  40. TEST_F
  41. TEST_F
  42. TEST_F
  43. TEST_F

// Copyright (c) 2012 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/panels/panel_layout_manager.h"

#include "ash/ash_switches.h"
#include "ash/root_window_controller.h"
#include "ash/screen_util.h"
#include "ash/shelf/shelf.h"
#include "ash/shelf/shelf_button.h"
#include "ash/shelf/shelf_layout_manager.h"
#include "ash/shelf/shelf_model.h"
#include "ash/shelf/shelf_types.h"
#include "ash/shelf/shelf_util.h"
#include "ash/shelf/shelf_view.h"
#include "ash/shelf/shelf_widget.h"
#include "ash/shell.h"
#include "ash/shell_window_ids.h"
#include "ash/test/ash_test_base.h"
#include "ash/test/shelf_test_api.h"
#include "ash/test/shelf_view_test_api.h"
#include "ash/test/shell_test_api.h"
#include "ash/test/test_shelf_delegate.h"
#include "ash/wm/mru_window_tracker.h"
#include "ash/wm/window_util.h"
#include "base/basictypes.h"
#include "base/command_line.h"
#include "base/compiler_specific.h"
#include "base/i18n/rtl.h"
#include "base/run_loop.h"
#include "ui/aura/client/aura_constants.h"
#include "ui/aura/test/event_generator.h"
#include "ui/aura/test/test_windows.h"
#include "ui/aura/window.h"
#include "ui/aura/window_event_dispatcher.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/views/widget/widget.h"

namespace ash {

using aura::test::WindowIsAbove;

class PanelLayoutManagerTest : public test::AshTestBase {
 public:
  PanelLayoutManagerTest() {}
  virtual ~PanelLayoutManagerTest() {}

  virtual void SetUp() OVERRIDE {
    test::AshTestBase::SetUp();
    ASSERT_TRUE(test::TestShelfDelegate::instance());

    shelf_view_test_.reset(new test::ShelfViewTestAPI(
        GetShelfView(Shelf::ForPrimaryDisplay())));
    shelf_view_test_->SetAnimationDuration(1);
  }

  aura::Window* CreateNormalWindow(const gfx::Rect& bounds) {
    return CreateTestWindowInShellWithBounds(bounds);
  }

  aura::Window* CreatePanelWindow(const gfx::Rect& bounds) {
    aura::Window* window = CreateTestWindowInShellWithDelegateAndType(
        NULL, ui::wm::WINDOW_TYPE_PANEL, 0, bounds);
    test::TestShelfDelegate* shelf_delegate =
        test::TestShelfDelegate::instance();
    shelf_delegate->AddShelfItem(window);
    PanelLayoutManager* manager = static_cast<PanelLayoutManager*>(
        GetPanelContainer(window)->layout_manager());
    manager->Relayout();
    shelf_view_test()->RunMessageLoopUntilAnimationsDone();
    return window;
  }

  aura::Window* GetPanelContainer(aura::Window* panel) {
    return Shell::GetContainer(panel->GetRootWindow(),
                               kShellWindowId_PanelContainer);
  }

  views::Widget* GetCalloutWidgetForPanel(aura::Window* panel) {
    PanelLayoutManager* manager =
        static_cast<PanelLayoutManager*>(GetPanelContainer(panel)->
                                         layout_manager());
    DCHECK(manager);
    PanelLayoutManager::PanelList::iterator found = std::find(
        manager->panel_windows_.begin(), manager->panel_windows_.end(),
        panel);
    DCHECK(found != manager->panel_windows_.end());
    DCHECK(found->callout_widget);
    return reinterpret_cast<views::Widget*>(found->callout_widget);
  }

  void PanelInScreen(aura::Window* panel) {
    gfx::Rect panel_bounds = panel->GetBoundsInRootWindow();
    gfx::Point root_point = gfx::Point(panel_bounds.x(), panel_bounds.y());
    gfx::Display display = ScreenUtil::FindDisplayContainingPoint(root_point);

    gfx::Rect panel_bounds_in_screen = panel->GetBoundsInScreen();
    gfx::Point screen_bottom_right = gfx::Point(
        panel_bounds_in_screen.right(),
        panel_bounds_in_screen.bottom());
    gfx::Rect display_bounds = display.bounds();
    EXPECT_TRUE(screen_bottom_right.x() < display_bounds.width() &&
                screen_bottom_right.y() < display_bounds.height());
  }

  void PanelsNotOverlapping(aura::Window* panel1, aura::Window* panel2) {
    // Waits until all shelf view animations are done.
    shelf_view_test()->RunMessageLoopUntilAnimationsDone();
    gfx::Rect window1_bounds = panel1->GetBoundsInRootWindow();
    gfx::Rect window2_bounds = panel2->GetBoundsInRootWindow();

    EXPECT_FALSE(window1_bounds.Intersects(window2_bounds));
  }

  // TODO(dcheng): This should be const, but GetScreenBoundsOfItemIconForWindow
  // takes a non-const Window. We can probably fix that.
  void IsPanelAboveLauncherIcon(aura::Window* panel) {
    // Waits until all shelf view animations are done.
    shelf_view_test()->RunMessageLoopUntilAnimationsDone();

    Shelf* shelf = RootWindowController::ForShelf(panel)->shelf()->shelf();
    gfx::Rect icon_bounds = shelf->GetScreenBoundsOfItemIconForWindow(panel);
    ASSERT_FALSE(icon_bounds.width() == 0 && icon_bounds.height() == 0);

    gfx::Rect window_bounds = panel->GetBoundsInScreen();
    ASSERT_LT(icon_bounds.width(), window_bounds.width());
    ASSERT_LT(icon_bounds.height(), window_bounds.height());
    gfx::Rect shelf_bounds = shelf->shelf_widget()->GetWindowBoundsInScreen();
    ShelfAlignment alignment = GetAlignment(panel->GetRootWindow());

    if (IsHorizontal(alignment)) {
      // The horizontal bounds of the panel window should contain the bounds of
      // the shelf icon.
      EXPECT_LE(window_bounds.x(), icon_bounds.x());
      EXPECT_GE(window_bounds.right(), icon_bounds.right());
    } else {
      // The vertical bounds of the panel window should contain the bounds of
      // the shelf icon.
      EXPECT_LE(window_bounds.y(), icon_bounds.y());
      EXPECT_GE(window_bounds.bottom(), icon_bounds.bottom());
    }

    switch (alignment) {
      case SHELF_ALIGNMENT_BOTTOM:
        EXPECT_EQ(shelf_bounds.y(), window_bounds.bottom());
        break;
      case SHELF_ALIGNMENT_LEFT:
        EXPECT_EQ(shelf_bounds.right(), window_bounds.x());
        break;
      case SHELF_ALIGNMENT_RIGHT:
        EXPECT_EQ(shelf_bounds.x(), window_bounds.right());
        break;
      case SHELF_ALIGNMENT_TOP:
        EXPECT_EQ(shelf_bounds.bottom(), window_bounds.y());
        break;
    }
  }

  void IsCalloutAboveLauncherIcon(aura::Window* panel) {
    // Flush the message loop, since callout updates use a delayed task.
    base::RunLoop().RunUntilIdle();
    views::Widget* widget = GetCalloutWidgetForPanel(panel);

    Shelf* shelf = RootWindowController::ForShelf(panel)->shelf()->shelf();
    gfx::Rect icon_bounds = shelf->GetScreenBoundsOfItemIconForWindow(panel);
    ASSERT_FALSE(icon_bounds.IsEmpty());

    gfx::Rect panel_bounds = panel->GetBoundsInScreen();
    gfx::Rect callout_bounds = widget->GetWindowBoundsInScreen();
    ASSERT_FALSE(icon_bounds.IsEmpty());

    EXPECT_TRUE(widget->IsVisible());

    ShelfAlignment alignment = GetAlignment(panel->GetRootWindow());
    switch (alignment) {
      case SHELF_ALIGNMENT_BOTTOM:
        EXPECT_EQ(panel_bounds.bottom(), callout_bounds.y());
        break;
      case SHELF_ALIGNMENT_LEFT:
        EXPECT_EQ(panel_bounds.x(), callout_bounds.right());
        break;
      case SHELF_ALIGNMENT_RIGHT:
        EXPECT_EQ(panel_bounds.right(), callout_bounds.x());
        break;
      case SHELF_ALIGNMENT_TOP:
        EXPECT_EQ(panel_bounds.y(), callout_bounds.bottom());
        break;
    }

    if (IsHorizontal(alignment)) {
      EXPECT_NEAR(icon_bounds.CenterPoint().x(),
                  widget->GetWindowBoundsInScreen().CenterPoint().x(),
                  1);
    } else {
      EXPECT_NEAR(icon_bounds.CenterPoint().y(),
                  widget->GetWindowBoundsInScreen().CenterPoint().y(),
                  1);
    }
  }

  bool IsPanelCalloutVisible(aura::Window* panel) {
    views::Widget* widget = GetCalloutWidgetForPanel(panel);
    return widget->IsVisible();
  }

  test::ShelfViewTestAPI* shelf_view_test() {
    return shelf_view_test_.get();
  }

  // Clicks the shelf items on |shelf_view| that is associated with given
  // |window|.
  void ClickShelfItemForWindow(ShelfView* shelf_view, aura::Window* window) {
    test::ShelfViewTestAPI test_api(shelf_view);
    test_api.SetAnimationDuration(1);
    test_api.RunMessageLoopUntilAnimationsDone();
    ShelfModel* model = test::ShellTestApi(Shell::GetInstance()).shelf_model();
    int index = model->ItemIndexByID(GetShelfIDForWindow(window));
    gfx::Rect bounds = test_api.GetButton(index)->GetBoundsInScreen();

    aura::test::EventGenerator& event_generator = GetEventGenerator();
    event_generator.MoveMouseTo(bounds.CenterPoint());
    event_generator.ClickLeftButton();

    test_api.RunMessageLoopUntilAnimationsDone();
  }

  void SetAlignment(aura::Window* root_window, ShelfAlignment alignment) {
    ash::Shell* shell = ash::Shell::GetInstance();
    shell->SetShelfAlignment(alignment, root_window);
  }

  ShelfAlignment GetAlignment(aura::Window* root_window) {
    ash::Shell* shell = ash::Shell::GetInstance();
    return shell->GetShelfAlignment(root_window);
  }

  void SetShelfAutoHideBehavior(aura::Window* window,
                                ShelfAutoHideBehavior behavior) {
    ShelfLayoutManager* shelf = RootWindowController::ForWindow(window)
                                    ->shelf()
                                    ->shelf_layout_manager();
    shelf->SetAutoHideBehavior(behavior);
    ShelfView* shelf_view = GetShelfView(Shelf::ForWindow(window));
    test::ShelfViewTestAPI test_api(shelf_view);
    test_api.RunMessageLoopUntilAnimationsDone();
  }

  void SetShelfVisibilityState(aura::Window* window,
                               ShelfVisibilityState visibility_state) {
    ShelfLayoutManager* shelf = RootWindowController::ForWindow(window)
                                    ->shelf()
                                    ->shelf_layout_manager();
    shelf->SetState(visibility_state);
  }

  ShelfView* GetShelfView(Shelf* shelf) {
    return test::ShelfTestAPI(shelf).shelf_view();
  }

 private:
  scoped_ptr<test::ShelfViewTestAPI> shelf_view_test_;

  bool IsHorizontal(ShelfAlignment alignment) {
    return alignment == SHELF_ALIGNMENT_BOTTOM ||
           alignment == SHELF_ALIGNMENT_TOP;
  }

  DISALLOW_COPY_AND_ASSIGN(PanelLayoutManagerTest);
};

class PanelLayoutManagerTextDirectionTest
    : public PanelLayoutManagerTest,
      public testing::WithParamInterface<bool> {
 public:
  PanelLayoutManagerTextDirectionTest() : is_rtl_(GetParam()) {}
  virtual ~PanelLayoutManagerTextDirectionTest() {}

  virtual void SetUp() OVERRIDE {
    original_locale = l10n_util::GetApplicationLocale(std::string());
    if (is_rtl_)
      base::i18n::SetICUDefaultLocale("he");
    PanelLayoutManagerTest::SetUp();
    ASSERT_EQ(is_rtl_, base::i18n::IsRTL());
  }

  virtual void TearDown() OVERRIDE {
    if (is_rtl_)
      base::i18n::SetICUDefaultLocale(original_locale);
    PanelLayoutManagerTest::TearDown();
  }

 private:
  bool is_rtl_;
  std::string original_locale;

  DISALLOW_COPY_AND_ASSIGN(PanelLayoutManagerTextDirectionTest);
};

// Tests that a created panel window is above the shelf icon in LTR and RTL.
TEST_P(PanelLayoutManagerTextDirectionTest, AddOnePanel) {
  gfx::Rect bounds(0, 0, 201, 201);
  scoped_ptr<aura::Window> window(CreatePanelWindow(bounds));
  EXPECT_EQ(GetPanelContainer(window.get()), window->parent());
  EXPECT_NO_FATAL_FAILURE(IsPanelAboveLauncherIcon(window.get()));
  EXPECT_NO_FATAL_FAILURE(IsCalloutAboveLauncherIcon(window.get()));
}

// Tests that a created panel window is successfully aligned over a hidden
// shelf icon.
TEST_F(PanelLayoutManagerTest, PanelAlignsToHiddenLauncherIcon) {
  gfx::Rect bounds(0, 0, 201, 201);
  SetShelfAutoHideBehavior(Shell::GetPrimaryRootWindow(),
                           SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS);
  scoped_ptr<aura::Window> normal_window(CreateNormalWindow(bounds));
  scoped_ptr<aura::Window> window(CreatePanelWindow(bounds));
  EXPECT_EQ(GetPanelContainer(window.get()), window->parent());
  EXPECT_NO_FATAL_FAILURE(IsPanelAboveLauncherIcon(window.get()));
}

TEST_F(PanelLayoutManagerTest, PanelAlignsToHiddenLauncherIconSecondDisplay) {
  if (!SupportsMultipleDisplays())
    return;

  // Keep the displays wide so that shelves have enough space for shelves
  // buttons.
  UpdateDisplay("400x400,600x400");
  aura::Window::Windows root_windows = Shell::GetAllRootWindows();

  scoped_ptr<aura::Window> normal_window(
      CreateNormalWindow(gfx::Rect(450, 0, 100, 100)));
  scoped_ptr<aura::Window> panel(CreatePanelWindow(gfx::Rect(400, 0, 50, 50)));
  EXPECT_EQ(root_windows[1], panel->GetRootWindow());
  EXPECT_NO_FATAL_FAILURE(IsPanelAboveLauncherIcon(panel.get()));
  gfx::Rect shelf_visible_position = panel->GetBoundsInScreen();

  SetShelfAutoHideBehavior(root_windows[1],
                           SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS);
  // Expect the panel X position to remain the same after the shelf is hidden
  // but the Y to move down.
  EXPECT_NO_FATAL_FAILURE(IsPanelAboveLauncherIcon(panel.get()));
  EXPECT_EQ(shelf_visible_position.x(), panel->GetBoundsInScreen().x());
  EXPECT_GT(panel->GetBoundsInScreen().y(), shelf_visible_position.y());
}

// Tests interactions between multiple panels
TEST_F(PanelLayoutManagerTest, MultiplePanelsAreAboveIcons) {
  gfx::Rect odd_bounds(0, 0, 201, 201);
  gfx::Rect even_bounds(0, 0, 200, 200);

  scoped_ptr<aura::Window> w1(CreatePanelWindow(odd_bounds));
  EXPECT_NO_FATAL_FAILURE(IsPanelAboveLauncherIcon(w1.get()));

  scoped_ptr<aura::Window> w2(CreatePanelWindow(even_bounds));
  EXPECT_NO_FATAL_FAILURE(IsPanelAboveLauncherIcon(w1.get()));
  EXPECT_NO_FATAL_FAILURE(IsPanelAboveLauncherIcon(w2.get()));

  scoped_ptr<aura::Window> w3(CreatePanelWindow(odd_bounds));
  EXPECT_NO_FATAL_FAILURE(IsPanelAboveLauncherIcon(w1.get()));
  EXPECT_NO_FATAL_FAILURE(IsPanelAboveLauncherIcon(w2.get()));
  EXPECT_NO_FATAL_FAILURE(IsPanelAboveLauncherIcon(w3.get()));
}

TEST_F(PanelLayoutManagerTest, MultiplePanelStacking) {
  gfx::Rect bounds(0, 0, 201, 201);
  scoped_ptr<aura::Window> w1(CreatePanelWindow(bounds));
  scoped_ptr<aura::Window> w2(CreatePanelWindow(bounds));
  scoped_ptr<aura::Window> w3(CreatePanelWindow(bounds));

  // Default stacking order.
  EXPECT_TRUE(WindowIsAbove(w3.get(), w2.get()));
  EXPECT_TRUE(WindowIsAbove(w2.get(), w1.get()));

  // Changing the active window should update the stacking order.
  wm::ActivateWindow(w1.get());
  shelf_view_test()->RunMessageLoopUntilAnimationsDone();
  EXPECT_TRUE(WindowIsAbove(w1.get(), w2.get()));
  EXPECT_TRUE(WindowIsAbove(w2.get(), w3.get()));

  wm::ActivateWindow(w2.get());
  shelf_view_test()->RunMessageLoopUntilAnimationsDone();
  EXPECT_TRUE(WindowIsAbove(w1.get(), w3.get()));
  EXPECT_TRUE(WindowIsAbove(w2.get(), w3.get()));
  EXPECT_TRUE(WindowIsAbove(w2.get(), w1.get()));

  wm::ActivateWindow(w3.get());
  EXPECT_TRUE(WindowIsAbove(w3.get(), w2.get()));
  EXPECT_TRUE(WindowIsAbove(w2.get(), w1.get()));
}

TEST_F(PanelLayoutManagerTest, MultiplePanelStackingVertical) {
  // Set shelf to be aligned on the right.
  SetAlignment(Shell::GetPrimaryRootWindow(), SHELF_ALIGNMENT_RIGHT);

  // Size panels in such a way that ordering them by X coordinate would cause
  // stacking order to be incorrect. Test that stacking order is based on Y.
  scoped_ptr<aura::Window> w1(CreatePanelWindow(gfx::Rect(0, 0, 210, 201)));
  scoped_ptr<aura::Window> w2(CreatePanelWindow(gfx::Rect(0, 0, 220, 201)));
  scoped_ptr<aura::Window> w3(CreatePanelWindow(gfx::Rect(0, 0, 200, 201)));

  // Default stacking order.
  EXPECT_TRUE(WindowIsAbove(w3.get(), w2.get()));
  EXPECT_TRUE(WindowIsAbove(w2.get(), w1.get()));

  // Changing the active window should update the stacking order.
  wm::ActivateWindow(w1.get());
  shelf_view_test()->RunMessageLoopUntilAnimationsDone();
  EXPECT_TRUE(WindowIsAbove(w1.get(), w2.get()));
  EXPECT_TRUE(WindowIsAbove(w2.get(), w3.get()));

  wm::ActivateWindow(w2.get());
  shelf_view_test()->RunMessageLoopUntilAnimationsDone();
  EXPECT_TRUE(WindowIsAbove(w1.get(), w3.get()));
  EXPECT_TRUE(WindowIsAbove(w2.get(), w3.get()));
  EXPECT_TRUE(WindowIsAbove(w2.get(), w1.get()));

  wm::ActivateWindow(w3.get());
  EXPECT_TRUE(WindowIsAbove(w3.get(), w2.get()));
  EXPECT_TRUE(WindowIsAbove(w2.get(), w1.get()));
}

TEST_F(PanelLayoutManagerTest, MultiplePanelCallout) {
  gfx::Rect bounds(0, 0, 200, 200);
  scoped_ptr<aura::Window> w1(CreatePanelWindow(bounds));
  scoped_ptr<aura::Window> w2(CreatePanelWindow(bounds));
  scoped_ptr<aura::Window> w3(CreatePanelWindow(bounds));
  scoped_ptr<aura::Window> w4(CreateNormalWindow(gfx::Rect()));
  shelf_view_test()->RunMessageLoopUntilAnimationsDone();
  EXPECT_TRUE(IsPanelCalloutVisible(w1.get()));
  EXPECT_TRUE(IsPanelCalloutVisible(w2.get()));
  EXPECT_TRUE(IsPanelCalloutVisible(w3.get()));
  wm::ActivateWindow(w1.get());
  EXPECT_NO_FATAL_FAILURE(IsCalloutAboveLauncherIcon(w1.get()));
  wm::ActivateWindow(w2.get());
  EXPECT_NO_FATAL_FAILURE(IsCalloutAboveLauncherIcon(w2.get()));
  wm::ActivateWindow(w3.get());
  EXPECT_NO_FATAL_FAILURE(IsCalloutAboveLauncherIcon(w3.get()));
  wm::ActivateWindow(w4.get());
  wm::ActivateWindow(w3.get());
  EXPECT_NO_FATAL_FAILURE(IsCalloutAboveLauncherIcon(w3.get()));
  w3.reset();
  EXPECT_NO_FATAL_FAILURE(IsCalloutAboveLauncherIcon(w2.get()));
}

// Tests removing panels.
TEST_F(PanelLayoutManagerTest, RemoveLeftPanel) {
  gfx::Rect bounds(0, 0, 201, 201);
  scoped_ptr<aura::Window> w1(CreatePanelWindow(bounds));
  scoped_ptr<aura::Window> w2(CreatePanelWindow(bounds));
  scoped_ptr<aura::Window> w3(CreatePanelWindow(bounds));

  // At this point, windows should be stacked with 1 < 2 < 3
  wm::ActivateWindow(w1.get());
  shelf_view_test()->RunMessageLoopUntilAnimationsDone();
  // Now, windows should be stacked 1 > 2 > 3
  w1.reset();
  EXPECT_NO_FATAL_FAILURE(IsPanelAboveLauncherIcon(w2.get()));
  EXPECT_NO_FATAL_FAILURE(IsPanelAboveLauncherIcon(w3.get()));
  EXPECT_TRUE(WindowIsAbove(w2.get(), w3.get()));
}

TEST_F(PanelLayoutManagerTest, RemoveMiddlePanel) {
  gfx::Rect bounds(0, 0, 201, 201);
  scoped_ptr<aura::Window> w1(CreatePanelWindow(bounds));
  scoped_ptr<aura::Window> w2(CreatePanelWindow(bounds));
  scoped_ptr<aura::Window> w3(CreatePanelWindow(bounds));

  // At this point, windows should be stacked with 1 < 2 < 3
  wm::ActivateWindow(w2.get());
  // Windows should be stacked 1 < 2 > 3
  w2.reset();
  EXPECT_NO_FATAL_FAILURE(IsPanelAboveLauncherIcon(w1.get()));
  EXPECT_NO_FATAL_FAILURE(IsPanelAboveLauncherIcon(w3.get()));
  EXPECT_TRUE(WindowIsAbove(w3.get(), w1.get()));
}

TEST_F(PanelLayoutManagerTest, RemoveRightPanel) {
  gfx::Rect bounds(0, 0, 201, 201);
  scoped_ptr<aura::Window> w1(CreatePanelWindow(bounds));
  scoped_ptr<aura::Window> w2(CreatePanelWindow(bounds));
  scoped_ptr<aura::Window> w3(CreatePanelWindow(bounds));

  // At this point, windows should be stacked with 1 < 2 < 3
  wm::ActivateWindow(w3.get());
  // Order shouldn't change.
  w3.reset();
  EXPECT_NO_FATAL_FAILURE(IsPanelAboveLauncherIcon(w1.get()));
  EXPECT_NO_FATAL_FAILURE(IsPanelAboveLauncherIcon(w2.get()));
  EXPECT_TRUE(WindowIsAbove(w2.get(), w1.get()));
}

TEST_F(PanelLayoutManagerTest, RemoveNonActivePanel) {
  gfx::Rect bounds(0, 0, 201, 201);
  scoped_ptr<aura::Window> w1(CreatePanelWindow(bounds));
  scoped_ptr<aura::Window> w2(CreatePanelWindow(bounds));
  scoped_ptr<aura::Window> w3(CreatePanelWindow(bounds));

  // At this point, windows should be stacked with 1 < 2 < 3
  wm::ActivateWindow(w2.get());
  // Windows should be stacked 1 < 2 > 3
  w1.reset();
  EXPECT_NO_FATAL_FAILURE(IsPanelAboveLauncherIcon(w2.get()));
  EXPECT_NO_FATAL_FAILURE(IsPanelAboveLauncherIcon(w3.get()));
  EXPECT_TRUE(WindowIsAbove(w2.get(), w3.get()));
}

TEST_F(PanelLayoutManagerTest, SplitView) {
  gfx::Rect bounds(0, 0, 90, 201);
  scoped_ptr<aura::Window> w1(CreatePanelWindow(bounds));
  scoped_ptr<aura::Window> w2(CreatePanelWindow(bounds));

  EXPECT_NO_FATAL_FAILURE(PanelsNotOverlapping(w1.get(), w2.get()));
}

#if defined(OS_WIN)
// RootWindow and Display can't resize on Windows Ash. http://crbug.com/165962
#define MAYBE_SplitViewOverlapWhenLarge DISABLED_SplitViewOverlapWhenLarge
#else
#define MAYBE_SplitViewOverlapWhenLarge SplitViewOverlapWhenLarge
#endif

TEST_F(PanelLayoutManagerTest, MAYBE_SplitViewOverlapWhenLarge) {
  gfx::Rect bounds(0, 0, 600, 201);
  scoped_ptr<aura::Window> w1(CreatePanelWindow(bounds));
  scoped_ptr<aura::Window> w2(CreatePanelWindow(bounds));

  EXPECT_NO_FATAL_FAILURE(PanelInScreen(w1.get()));
  EXPECT_NO_FATAL_FAILURE(PanelInScreen(w2.get()));
}

TEST_F(PanelLayoutManagerTest, FanWindows) {
  gfx::Rect bounds(0, 0, 201, 201);
  scoped_ptr<aura::Window> w1(CreatePanelWindow(bounds));
  scoped_ptr<aura::Window> w2(CreatePanelWindow(bounds));
  scoped_ptr<aura::Window> w3(CreatePanelWindow(bounds));

  shelf_view_test()->RunMessageLoopUntilAnimationsDone();
  int window_x1 = w1->GetBoundsInRootWindow().CenterPoint().x();
  int window_x2 = w2->GetBoundsInRootWindow().CenterPoint().x();
  int window_x3 = w3->GetBoundsInRootWindow().CenterPoint().x();
  Shelf* shelf = Shelf::ForPrimaryDisplay();
  int icon_x1 = shelf->GetScreenBoundsOfItemIconForWindow(w1.get()).x();
  int icon_x2 = shelf->GetScreenBoundsOfItemIconForWindow(w2.get()).x();
  EXPECT_EQ(window_x2 - window_x1, window_x3 - window_x2);
  int spacing = window_x2 - window_x1;
  EXPECT_GT(spacing, icon_x2 - icon_x1);
}

TEST_F(PanelLayoutManagerTest, FanLargeWindow) {
  gfx::Rect small_bounds(0, 0, 201, 201);
  gfx::Rect large_bounds(0, 0, 501, 201);
  scoped_ptr<aura::Window> w1(CreatePanelWindow(small_bounds));
  scoped_ptr<aura::Window> w2(CreatePanelWindow(large_bounds));
  scoped_ptr<aura::Window> w3(CreatePanelWindow(small_bounds));

  shelf_view_test()->RunMessageLoopUntilAnimationsDone();
  int window_x1 = w1->GetBoundsInRootWindow().CenterPoint().x();
  int window_x2 = w2->GetBoundsInRootWindow().CenterPoint().x();
  int window_x3 = w3->GetBoundsInRootWindow().CenterPoint().x();
  // The distances may not be equidistant with a large panel but the panels
  // should be in the correct order with respect to their midpoints.
  EXPECT_GT(window_x2, window_x1);
  EXPECT_GT(window_x3, window_x2);
}

TEST_F(PanelLayoutManagerTest, MinimizeRestorePanel) {
  gfx::Rect bounds(0, 0, 201, 201);
  scoped_ptr<aura::Window> window(CreatePanelWindow(bounds));
  // Activate the window, ensure callout is visible.
  wm::ActivateWindow(window.get());
  RunAllPendingInMessageLoop();
  EXPECT_TRUE(IsPanelCalloutVisible(window.get()));
  // Minimize the panel, callout should be hidden.
  window->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MINIMIZED);
  RunAllPendingInMessageLoop();
  EXPECT_FALSE(IsPanelCalloutVisible(window.get()));
  // Restore the pantel; panel should not be activated by default but callout
  // should be visible.
  window->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL);
  RunAllPendingInMessageLoop();
  EXPECT_TRUE(IsPanelCalloutVisible(window.get()));
  // Activate the window, ensure callout is visible.
  wm::ActivateWindow(window.get());
  RunAllPendingInMessageLoop();
  EXPECT_TRUE(IsPanelCalloutVisible(window.get()));
}

TEST_F(PanelLayoutManagerTest, PanelMoveBetweenMultipleDisplays) {
  if (!SupportsMultipleDisplays())
    return;

  // Keep the displays wide so that shelves have enough space for launcher
  // buttons.
  UpdateDisplay("600x400,600x400");
  aura::Window::Windows root_windows = Shell::GetAllRootWindows();

  scoped_ptr<aura::Window> p1_d1(CreatePanelWindow(gfx::Rect(0, 0, 50, 50)));
  scoped_ptr<aura::Window> p2_d1(CreatePanelWindow(gfx::Rect(0, 0, 50, 50)));
  scoped_ptr<aura::Window> p1_d2(CreatePanelWindow(gfx::Rect(600, 0, 50, 50)));
  scoped_ptr<aura::Window> p2_d2(CreatePanelWindow(gfx::Rect(600, 0, 50, 50)));

  ShelfView* shelf_view_1st = GetShelfView(Shelf::ForPrimaryDisplay());
  ShelfView* shelf_view_2nd =
      GetShelfView(Shelf::ForWindow(root_windows[1]));

  EXPECT_EQ(root_windows[0], p1_d1->GetRootWindow());
  EXPECT_EQ(root_windows[0], p2_d1->GetRootWindow());
  EXPECT_EQ(root_windows[1], p1_d2->GetRootWindow());
  EXPECT_EQ(root_windows[1], p2_d2->GetRootWindow());

  EXPECT_EQ(kShellWindowId_PanelContainer, p1_d1->parent()->id());
  EXPECT_EQ(kShellWindowId_PanelContainer, p2_d1->parent()->id());
  EXPECT_EQ(kShellWindowId_PanelContainer, p1_d2->parent()->id());
  EXPECT_EQ(kShellWindowId_PanelContainer, p2_d2->parent()->id());

  // Test a panel on 1st display.
  // Clicking on the same display has no effect.
  ClickShelfItemForWindow(shelf_view_1st, p1_d1.get());
  EXPECT_EQ(root_windows[0], p1_d1->GetRootWindow());
  EXPECT_EQ(root_windows[0], p2_d1->GetRootWindow());
  EXPECT_EQ(root_windows[1], p1_d2->GetRootWindow());
  EXPECT_EQ(root_windows[1], p1_d2->GetRootWindow());
  EXPECT_FALSE(root_windows[1]->GetBoundsInScreen().Contains(
      p1_d1->GetBoundsInScreen()));

  // Test if clicking on another display moves the panel to
  // that display.
  ClickShelfItemForWindow(shelf_view_2nd, p1_d1.get());
  EXPECT_EQ(root_windows[1], p1_d1->GetRootWindow());
  EXPECT_EQ(root_windows[0], p2_d1->GetRootWindow());
  EXPECT_EQ(root_windows[1], p1_d2->GetRootWindow());
  EXPECT_EQ(root_windows[1], p2_d2->GetRootWindow());
  EXPECT_TRUE(root_windows[1]->GetBoundsInScreen().Contains(
      p1_d1->GetBoundsInScreen()));

  // Test a panel on 2nd display.
  // Clicking on the same display has no effect.
  ClickShelfItemForWindow(shelf_view_2nd, p1_d2.get());
  EXPECT_EQ(root_windows[1], p1_d1->GetRootWindow());
  EXPECT_EQ(root_windows[0], p2_d1->GetRootWindow());
  EXPECT_EQ(root_windows[1], p1_d2->GetRootWindow());
  EXPECT_EQ(root_windows[1], p2_d2->GetRootWindow());
  EXPECT_TRUE(root_windows[1]->GetBoundsInScreen().Contains(
      p1_d2->GetBoundsInScreen()));

  // Test if clicking on another display moves the panel to
  // that display.
  ClickShelfItemForWindow(shelf_view_1st, p1_d2.get());
  EXPECT_EQ(root_windows[1], p1_d1->GetRootWindow());
  EXPECT_EQ(root_windows[0], p2_d1->GetRootWindow());
  EXPECT_EQ(root_windows[0], p1_d2->GetRootWindow());
  EXPECT_EQ(root_windows[1], p2_d2->GetRootWindow());
  EXPECT_TRUE(root_windows[0]->GetBoundsInScreen().Contains(
      p1_d2->GetBoundsInScreen()));

  // Test if clicking on a previously moved window moves the
  // panel back to the original display.
  ClickShelfItemForWindow(shelf_view_1st, p1_d1.get());
  EXPECT_EQ(root_windows[0], p1_d1->GetRootWindow());
  EXPECT_EQ(root_windows[0], p2_d1->GetRootWindow());
  EXPECT_EQ(root_windows[0], p1_d2->GetRootWindow());
  EXPECT_EQ(root_windows[1], p2_d2->GetRootWindow());
  EXPECT_TRUE(root_windows[0]->GetBoundsInScreen().Contains(
      p1_d1->GetBoundsInScreen()));
}

TEST_F(PanelLayoutManagerTest, PanelAttachPositionMultipleDisplays) {
  if (!SupportsMultipleDisplays())
    return;

  // Keep the displays wide so that shelves have enough space for shelf buttons.
  // Use differently sized displays so the shelf is in a different
  // position on second display.
  UpdateDisplay("600x400,600x600");
  aura::Window::Windows root_windows = Shell::GetAllRootWindows();

  scoped_ptr<aura::Window> p1_d1(CreatePanelWindow(gfx::Rect(0, 0, 50, 50)));
  scoped_ptr<aura::Window> p1_d2(CreatePanelWindow(gfx::Rect(600, 0, 50, 50)));

  EXPECT_EQ(root_windows[0], p1_d1->GetRootWindow());
  EXPECT_EQ(root_windows[1], p1_d2->GetRootWindow());

  IsPanelAboveLauncherIcon(p1_d1.get());
  IsCalloutAboveLauncherIcon(p1_d1.get());
  IsPanelAboveLauncherIcon(p1_d2.get());
  IsCalloutAboveLauncherIcon(p1_d2.get());
}

TEST_F(PanelLayoutManagerTest, PanelAlignmentSecondDisplay) {
  if (!SupportsMultipleDisplays())
    return;

  UpdateDisplay("600x400,600x400");
  aura::Window::Windows root_windows = Shell::GetAllRootWindows();

  scoped_ptr<aura::Window> p1_d2(CreatePanelWindow(gfx::Rect(600, 0, 50, 50)));
  EXPECT_EQ(root_windows[1], p1_d2->GetRootWindow());

  IsPanelAboveLauncherIcon(p1_d2.get());
  IsCalloutAboveLauncherIcon(p1_d2.get());

  SetAlignment(root_windows[1], SHELF_ALIGNMENT_RIGHT);
  IsPanelAboveLauncherIcon(p1_d2.get());
  IsCalloutAboveLauncherIcon(p1_d2.get());
  SetAlignment(root_windows[1], SHELF_ALIGNMENT_LEFT);
  IsPanelAboveLauncherIcon(p1_d2.get());
  IsCalloutAboveLauncherIcon(p1_d2.get());
  SetAlignment(root_windows[1], SHELF_ALIGNMENT_TOP);
  IsPanelAboveLauncherIcon(p1_d2.get());
  IsCalloutAboveLauncherIcon(p1_d2.get());
}

TEST_F(PanelLayoutManagerTest, AlignmentLeft) {
  gfx::Rect bounds(0, 0, 201, 201);
  scoped_ptr<aura::Window> w(CreatePanelWindow(bounds));
  SetAlignment(Shell::GetPrimaryRootWindow(), SHELF_ALIGNMENT_LEFT);
  IsPanelAboveLauncherIcon(w.get());
  IsCalloutAboveLauncherIcon(w.get());
}

TEST_F(PanelLayoutManagerTest, AlignmentRight) {
  gfx::Rect bounds(0, 0, 201, 201);
  scoped_ptr<aura::Window> w(CreatePanelWindow(bounds));
  SetAlignment(Shell::GetPrimaryRootWindow(), SHELF_ALIGNMENT_RIGHT);
  IsPanelAboveLauncherIcon(w.get());
  IsCalloutAboveLauncherIcon(w.get());
}

TEST_F(PanelLayoutManagerTest, AlignmentTop) {
  gfx::Rect bounds(0, 0, 201, 201);
  scoped_ptr<aura::Window> w(CreatePanelWindow(bounds));
  SetAlignment(Shell::GetPrimaryRootWindow(), SHELF_ALIGNMENT_TOP);
  IsPanelAboveLauncherIcon(w.get());
  IsCalloutAboveLauncherIcon(w.get());
}

// Tests that panels will hide and restore their state with the shelf visibility
// state. This ensures that entering full-screen mode will hide your panels
// until you leave it.
TEST_F(PanelLayoutManagerTest, PanelsHideAndRestoreWithShelf) {
  gfx::Rect bounds(0, 0, 201, 201);

  scoped_ptr<aura::Window> w1(CreatePanelWindow(bounds));
  scoped_ptr<aura::Window> w2(CreatePanelWindow(bounds));
  scoped_ptr<aura::Window> w3;
  // Minimize w2.
  w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MINIMIZED);
  RunAllPendingInMessageLoop();
  EXPECT_TRUE(w1->IsVisible());
  EXPECT_FALSE(w2->IsVisible());

  SetShelfVisibilityState(Shell::GetPrimaryRootWindow(), SHELF_HIDDEN);
  RunAllPendingInMessageLoop();

  // w3 is created while in full-screen mode, should only become visible when
  // we exit fullscreen mode.
  w3.reset(CreatePanelWindow(bounds));

  EXPECT_FALSE(w1->IsVisible());
  EXPECT_FALSE(w2->IsVisible());
  EXPECT_FALSE(w3->IsVisible());

  // While in full-screen mode, the panel windows should still be in the
  // switchable window list - http://crbug.com/313919.
  MruWindowTracker::WindowList switchable_window_list =
      Shell::GetInstance()->mru_window_tracker()->BuildMruWindowList();
  EXPECT_EQ(3u, switchable_window_list.size());
  EXPECT_NE(switchable_window_list.end(),
      std::find(switchable_window_list.begin(), switchable_window_list.end(),
          w1.get()));
  EXPECT_NE(switchable_window_list.end(),
      std::find(switchable_window_list.begin(), switchable_window_list.end(),
          w2.get()));
  EXPECT_NE(switchable_window_list.end(),
      std::find(switchable_window_list.begin(), switchable_window_list.end(),
          w3.get()));

  SetShelfVisibilityState(Shell::GetPrimaryRootWindow(), SHELF_VISIBLE);
  RunAllPendingInMessageLoop();

  // Windows should be restored to their prior state.
  EXPECT_TRUE(w1->IsVisible());
  EXPECT_FALSE(w2->IsVisible());
  EXPECT_TRUE(w3->IsVisible());
}

INSTANTIATE_TEST_CASE_P(LtrRtl, PanelLayoutManagerTextDirectionTest,
                        testing::Bool());

}  // namespace ash

/* [<][>][^][v][top][bottom][index][help] */