blob: 6d96604f7bb96cc05e0783b6ad59ad5dd4038bb5 [file] [log] [blame]
// Copyright 2015 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 "base/macros.h"
#include "base/strings/string_number_conversions.h"
#include "services/ui/ws/server_window.h"
#include "services/ui/ws/server_window_observer.h"
#include "services/ui/ws/test_server_window_delegate.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace ui {
namespace ws {
namespace {
class TestTransientWindowObserver : public ServerWindowObserver {
public:
TestTransientWindowObserver() : add_count_(0), remove_count_(0) {}
~TestTransientWindowObserver() override {}
int add_count() const { return add_count_; }
int remove_count() const { return remove_count_; }
// TransientWindowObserver overrides:
void OnTransientWindowAdded(ServerWindow* window,
ServerWindow* transient) override {
add_count_++;
}
void OnTransientWindowRemoved(ServerWindow* window,
ServerWindow* transient) override {
remove_count_++;
}
private:
int add_count_;
int remove_count_;
DISALLOW_COPY_AND_ASSIGN(TestTransientWindowObserver);
};
ServerWindow* CreateTestWindow(TestServerWindowDelegate* delegate,
const WindowId& window_id,
ServerWindow* parent) {
ServerWindow* window = new ServerWindow(delegate, window_id);
window->SetVisible(true);
if (parent)
parent->Add(window);
else
delegate->set_root_window(window);
return window;
}
std::string ChildWindowIDsAsString(ServerWindow* parent) {
std::string result;
for (auto i = parent->children().begin(); i != parent->children().end();
++i) {
if (!result.empty())
result += " ";
result += base::IntToString(WindowIdToTransportId((*i)->id()));
}
return result;
}
} // namespace
class TransientWindowsTest : public testing::Test {
public:
TransientWindowsTest() {}
~TransientWindowsTest() override {}
private:
DISALLOW_COPY_AND_ASSIGN(TransientWindowsTest);
};
TEST_F(TransientWindowsTest, TransientChildren) {
TestServerWindowDelegate server_window_delegate;
std::unique_ptr<ServerWindow> parent(
CreateTestWindow(&server_window_delegate, WindowId(), nullptr));
std::unique_ptr<ServerWindow> w1(
CreateTestWindow(&server_window_delegate, WindowId(1, 1), parent.get()));
std::unique_ptr<ServerWindow> w3(
CreateTestWindow(&server_window_delegate, WindowId(1, 2), parent.get()));
ServerWindow* w2 =
CreateTestWindow(&server_window_delegate, WindowId(1, 3), parent.get());
// w2 is now owned by w1.
w1->AddTransientWindow(w2);
// Stack w1 at the top (end), this should force w2 to be last (on top of w1).
parent->StackChildAtTop(w1.get());
ASSERT_EQ(3u, parent->children().size());
EXPECT_EQ(w2, parent->children().back());
// Destroy w1, which should also destroy w3 (since it's a transient child).
w1.reset();
w2 = nullptr;
ASSERT_EQ(1u, parent->children().size());
EXPECT_EQ(w3.get(), parent->children()[0]);
}
// Tests that transient children are stacked as a unit when using stack above.
TEST_F(TransientWindowsTest, TransientChildrenGroupAbove) {
TestServerWindowDelegate server_window_delegate;
std::unique_ptr<ServerWindow> parent(
CreateTestWindow(&server_window_delegate, WindowId(), nullptr));
std::unique_ptr<ServerWindow> w1(
CreateTestWindow(&server_window_delegate, WindowId(0, 1), parent.get()));
ServerWindow* w11 =
CreateTestWindow(&server_window_delegate, WindowId(0, 11), parent.get());
std::unique_ptr<ServerWindow> w2(
CreateTestWindow(&server_window_delegate, WindowId(0, 2), parent.get()));
ServerWindow* w21 =
CreateTestWindow(&server_window_delegate, WindowId(0, 21), parent.get());
ServerWindow* w211 =
CreateTestWindow(&server_window_delegate, WindowId(0, 211), parent.get());
ServerWindow* w212 =
CreateTestWindow(&server_window_delegate, WindowId(0, 212), parent.get());
ServerWindow* w213 =
CreateTestWindow(&server_window_delegate, WindowId(0, 213), parent.get());
ServerWindow* w22 =
CreateTestWindow(&server_window_delegate, WindowId(0, 22), parent.get());
ASSERT_EQ(8u, parent->children().size());
// w11 is now owned by w1.
w1->AddTransientWindow(w11);
// w21 is now owned by w2.
w2->AddTransientWindow(w21);
// w22 is now owned by w2.
w2->AddTransientWindow(w22);
// w211 is now owned by w21.
w21->AddTransientWindow(w211);
// w212 is now owned by w21.
w21->AddTransientWindow(w212);
// w213 is now owned by w21.
w21->AddTransientWindow(w213);
EXPECT_EQ("1 11 2 21 211 212 213 22", ChildWindowIDsAsString(parent.get()));
// Stack w1 at the top (end), this should force w11 to be last (on top of w1).
parent->StackChildAtTop(w1.get());
EXPECT_EQ(w11, parent->children().back());
EXPECT_EQ("2 21 211 212 213 22 1 11", ChildWindowIDsAsString(parent.get()));
// This tests that the order in children_ array rather than in
// transient_children_ array is used when reinserting transient children.
// If transient_children_ array was used '22' would be following '21'.
parent->StackChildAtTop(w2.get());
EXPECT_EQ(w22, parent->children().back());
EXPECT_EQ("1 11 2 21 211 212 213 22", ChildWindowIDsAsString(parent.get()));
w11->Reorder(w2.get(), mojom::OrderDirection::ABOVE);
EXPECT_EQ(w11, parent->children().back());
EXPECT_EQ("2 21 211 212 213 22 1 11", ChildWindowIDsAsString(parent.get()));
w21->Reorder(w1.get(), mojom::OrderDirection::ABOVE);
EXPECT_EQ(w22, parent->children().back());
EXPECT_EQ("1 11 2 21 211 212 213 22", ChildWindowIDsAsString(parent.get()));
w21->Reorder(w22, mojom::OrderDirection::ABOVE);
EXPECT_EQ(w213, parent->children().back());
EXPECT_EQ("1 11 2 22 21 211 212 213", ChildWindowIDsAsString(parent.get()));
w11->Reorder(w21, mojom::OrderDirection::ABOVE);
EXPECT_EQ(w11, parent->children().back());
EXPECT_EQ("2 22 21 211 212 213 1 11", ChildWindowIDsAsString(parent.get()));
w213->Reorder(w21, mojom::OrderDirection::ABOVE);
EXPECT_EQ(w11, parent->children().back());
EXPECT_EQ("2 22 21 213 211 212 1 11", ChildWindowIDsAsString(parent.get()));
// No change when stacking a transient parent above its transient child.
w21->Reorder(w211, mojom::OrderDirection::ABOVE);
EXPECT_EQ(w11, parent->children().back());
EXPECT_EQ("2 22 21 213 211 212 1 11", ChildWindowIDsAsString(parent.get()));
// This tests that the order in children_ array rather than in
// transient_children_ array is used when reinserting transient children.
// If transient_children_ array was used '22' would be following '21'.
w2->Reorder(w1.get(), mojom::OrderDirection::ABOVE);
EXPECT_EQ(w212, parent->children().back());
EXPECT_EQ("1 11 2 22 21 213 211 212", ChildWindowIDsAsString(parent.get()));
w11->Reorder(w213, mojom::OrderDirection::ABOVE);
EXPECT_EQ(w11, parent->children().back());
EXPECT_EQ("2 22 21 213 211 212 1 11", ChildWindowIDsAsString(parent.get()));
}
TEST_F(TransientWindowsTest, TransienChildGroupBelow) {
TestServerWindowDelegate server_window_delegate;
std::unique_ptr<ServerWindow> parent(
CreateTestWindow(&server_window_delegate, WindowId(), nullptr));
std::unique_ptr<ServerWindow> w1(
CreateTestWindow(&server_window_delegate, WindowId(0, 1), parent.get()));
ServerWindow* w11 =
CreateTestWindow(&server_window_delegate, WindowId(0, 11), parent.get());
std::unique_ptr<ServerWindow> w2(
CreateTestWindow(&server_window_delegate, WindowId(0, 2), parent.get()));
ServerWindow* w21 =
CreateTestWindow(&server_window_delegate, WindowId(0, 21), parent.get());
ServerWindow* w211 =
CreateTestWindow(&server_window_delegate, WindowId(0, 211), parent.get());
ServerWindow* w212 =
CreateTestWindow(&server_window_delegate, WindowId(0, 212), parent.get());
ServerWindow* w213 =
CreateTestWindow(&server_window_delegate, WindowId(0, 213), parent.get());
ServerWindow* w22 =
CreateTestWindow(&server_window_delegate, WindowId(0, 22), parent.get());
ASSERT_EQ(8u, parent->children().size());
// w11 is now owned by w1.
w1->AddTransientWindow(w11);
// w21 is now owned by w2.
w2->AddTransientWindow(w21);
// w22 is now owned by w2.
w2->AddTransientWindow(w22);
// w211 is now owned by w21.
w21->AddTransientWindow(w211);
// w212 is now owned by w21.
w21->AddTransientWindow(w212);
// w213 is now owned by w21.
w21->AddTransientWindow(w213);
EXPECT_EQ("1 11 2 21 211 212 213 22", ChildWindowIDsAsString(parent.get()));
// Stack w2 at the bottom, this should force w11 to be last (on top of w1).
// This also tests that the order in children_ array rather than in
// transient_children_ array is used when reinserting transient children.
// If transient_children_ array was used '22' would be following '21'.
parent->StackChildAtBottom(w2.get());
EXPECT_EQ(w11, parent->children().back());
EXPECT_EQ("2 21 211 212 213 22 1 11", ChildWindowIDsAsString(parent.get()));
parent->StackChildAtBottom(w1.get());
EXPECT_EQ(w22, parent->children().back());
EXPECT_EQ("1 11 2 21 211 212 213 22", ChildWindowIDsAsString(parent.get()));
w21->Reorder(w1.get(), mojom::OrderDirection::BELOW);
EXPECT_EQ(w11, parent->children().back());
EXPECT_EQ("2 21 211 212 213 22 1 11", ChildWindowIDsAsString(parent.get()));
w11->Reorder(w2.get(), mojom::OrderDirection::BELOW);
EXPECT_EQ(w22, parent->children().back());
EXPECT_EQ("1 11 2 21 211 212 213 22", ChildWindowIDsAsString(parent.get()));
w22->Reorder(w21, mojom::OrderDirection::BELOW);
EXPECT_EQ(w213, parent->children().back());
EXPECT_EQ("1 11 2 22 21 211 212 213", ChildWindowIDsAsString(parent.get()));
w21->Reorder(w11, mojom::OrderDirection::BELOW);
EXPECT_EQ(w11, parent->children().back());
EXPECT_EQ("2 22 21 211 212 213 1 11", ChildWindowIDsAsString(parent.get()));
w213->Reorder(w211, mojom::OrderDirection::BELOW);
EXPECT_EQ(w11, parent->children().back());
EXPECT_EQ("2 22 21 213 211 212 1 11", ChildWindowIDsAsString(parent.get()));
// No change when stacking a transient parent below its transient child.
w21->Reorder(w211, mojom::OrderDirection::BELOW);
EXPECT_EQ(w11, parent->children().back());
EXPECT_EQ("2 22 21 213 211 212 1 11", ChildWindowIDsAsString(parent.get()));
w1->Reorder(w2.get(), mojom::OrderDirection::BELOW);
EXPECT_EQ(w212, parent->children().back());
EXPECT_EQ("1 11 2 22 21 213 211 212", ChildWindowIDsAsString(parent.get()));
w213->Reorder(w11, mojom::OrderDirection::BELOW);
EXPECT_EQ(w11, parent->children().back());
EXPECT_EQ("2 22 21 213 211 212 1 11", ChildWindowIDsAsString(parent.get()));
}
// Tests that transient windows are stacked properly when created.
TEST_F(TransientWindowsTest, StackUponCreation) {
TestServerWindowDelegate delegate;
std::unique_ptr<ServerWindow> parent(
CreateTestWindow(&delegate, WindowId(), nullptr));
std::unique_ptr<ServerWindow> window0(
CreateTestWindow(&delegate, WindowId(0, 1), parent.get()));
std::unique_ptr<ServerWindow> window1(
CreateTestWindow(&delegate, WindowId(0, 2), parent.get()));
ServerWindow* window2 =
CreateTestWindow(&delegate, WindowId(0, 3), parent.get());
window0->AddTransientWindow(window2);
EXPECT_EQ("1 3 2", ChildWindowIDsAsString(parent.get()));
}
// Tests that windows are restacked properly after a call to
// AddTransientWindow() or RemoveTransientWindow().
TEST_F(TransientWindowsTest, RestackUponAddOrRemoveTransientWindow) {
TestServerWindowDelegate delegate;
std::unique_ptr<ServerWindow> parent(
CreateTestWindow(&delegate, WindowId(), nullptr));
std::unique_ptr<ServerWindow> windows[4];
for (int i = 0; i < 4; i++)
windows[i].reset(CreateTestWindow(&delegate, WindowId(0, i), parent.get()));
EXPECT_EQ("0 1 2 3", ChildWindowIDsAsString(parent.get()));
windows[0]->AddTransientWindow(windows[2].get());
EXPECT_EQ("0 2 1 3", ChildWindowIDsAsString(parent.get()));
windows[0]->AddTransientWindow(windows[3].get());
EXPECT_EQ("0 2 3 1", ChildWindowIDsAsString(parent.get()));
windows[0]->RemoveTransientWindow(windows[2].get());
EXPECT_EQ("0 3 2 1", ChildWindowIDsAsString(parent.get()));
windows[0]->RemoveTransientWindow(windows[3].get());
EXPECT_EQ("0 3 2 1", ChildWindowIDsAsString(parent.get()));
}
// Verifies TransientWindowObserver is notified appropriately.
TEST_F(TransientWindowsTest, TransientWindowObserverNotified) {
TestServerWindowDelegate delegate;
std::unique_ptr<ServerWindow> parent(
CreateTestWindow(&delegate, WindowId(), nullptr));
std::unique_ptr<ServerWindow> w1(
CreateTestWindow(&delegate, WindowId(0, 1), parent.get()));
TestTransientWindowObserver test_observer;
parent->AddObserver(&test_observer);
parent->AddTransientWindow(w1.get());
EXPECT_EQ(1, test_observer.add_count());
EXPECT_EQ(0, test_observer.remove_count());
parent->RemoveTransientWindow(w1.get());
EXPECT_EQ(1, test_observer.add_count());
EXPECT_EQ(1, test_observer.remove_count());
parent->RemoveObserver(&test_observer);
}
} // namespace ws
} // namespace ui