mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
875 lines
25 KiB
C++
875 lines
25 KiB
C++
// Copyright 2014 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 "mojo/services/view_manager/cpp/view.h"
|
|
|
|
#include "base/logging.h"
|
|
#include "base/strings/stringprintf.h"
|
|
#include "mojo/services/view_manager/cpp/lib/view_private.h"
|
|
#include "mojo/services/view_manager/cpp/util.h"
|
|
#include "mojo/services/view_manager/cpp/view_observer.h"
|
|
#include "mojo/services/view_manager/cpp/view_property.h"
|
|
#include "testing/gtest/include/gtest/gtest.h"
|
|
|
|
namespace mojo {
|
|
|
|
// View ------------------------------------------------------------------------
|
|
|
|
typedef testing::Test ViewTest;
|
|
|
|
// Subclass with public ctor/dtor.
|
|
class TestView : public View {
|
|
public:
|
|
TestView() { ViewPrivate(this).set_id(1); }
|
|
~TestView() {}
|
|
|
|
private:
|
|
MOJO_DISALLOW_COPY_AND_ASSIGN(TestView);
|
|
};
|
|
|
|
TEST_F(ViewTest, AddChild) {
|
|
TestView v1;
|
|
TestView v11;
|
|
v1.AddChild(&v11);
|
|
EXPECT_EQ(1U, v1.children().size());
|
|
}
|
|
|
|
TEST_F(ViewTest, RemoveChild) {
|
|
TestView v1;
|
|
TestView v11;
|
|
v1.AddChild(&v11);
|
|
EXPECT_EQ(1U, v1.children().size());
|
|
v1.RemoveChild(&v11);
|
|
EXPECT_EQ(0U, v1.children().size());
|
|
}
|
|
|
|
TEST_F(ViewTest, Reparent) {
|
|
TestView v1;
|
|
TestView v2;
|
|
TestView v11;
|
|
v1.AddChild(&v11);
|
|
EXPECT_EQ(1U, v1.children().size());
|
|
v2.AddChild(&v11);
|
|
EXPECT_EQ(1U, v2.children().size());
|
|
EXPECT_EQ(0U, v1.children().size());
|
|
}
|
|
|
|
TEST_F(ViewTest, Contains) {
|
|
TestView v1;
|
|
|
|
// Direct descendant.
|
|
TestView v11;
|
|
v1.AddChild(&v11);
|
|
EXPECT_TRUE(v1.Contains(&v11));
|
|
|
|
// Indirect descendant.
|
|
TestView v111;
|
|
v11.AddChild(&v111);
|
|
EXPECT_TRUE(v1.Contains(&v111));
|
|
}
|
|
|
|
TEST_F(ViewTest, GetChildById) {
|
|
TestView v1;
|
|
ViewPrivate(&v1).set_id(1);
|
|
TestView v11;
|
|
ViewPrivate(&v11).set_id(11);
|
|
v1.AddChild(&v11);
|
|
TestView v111;
|
|
ViewPrivate(&v111).set_id(111);
|
|
v11.AddChild(&v111);
|
|
|
|
// Find direct & indirect descendents.
|
|
EXPECT_EQ(&v11, v1.GetChildById(v11.id()));
|
|
EXPECT_EQ(&v111, v1.GetChildById(v111.id()));
|
|
}
|
|
|
|
TEST_F(ViewTest, DrawnAndVisible) {
|
|
TestView v1;
|
|
EXPECT_TRUE(v1.visible());
|
|
EXPECT_FALSE(v1.IsDrawn());
|
|
|
|
ViewPrivate(&v1).set_drawn(true);
|
|
|
|
TestView v11;
|
|
v1.AddChild(&v11);
|
|
EXPECT_TRUE(v11.visible());
|
|
EXPECT_TRUE(v11.IsDrawn());
|
|
|
|
v1.RemoveChild(&v11);
|
|
EXPECT_TRUE(v11.visible());
|
|
EXPECT_FALSE(v11.IsDrawn());
|
|
}
|
|
|
|
namespace {
|
|
DEFINE_VIEW_PROPERTY_KEY(int, kIntKey, -2);
|
|
DEFINE_VIEW_PROPERTY_KEY(const char*, kStringKey, "squeamish");
|
|
}
|
|
|
|
TEST_F(ViewTest, Property) {
|
|
TestView v;
|
|
|
|
// Non-existent properties should return the default values.
|
|
EXPECT_EQ(-2, v.GetLocalProperty(kIntKey));
|
|
EXPECT_EQ(std::string("squeamish"), v.GetLocalProperty(kStringKey));
|
|
|
|
// A set property value should be returned again (even if it's the default
|
|
// value).
|
|
v.SetLocalProperty(kIntKey, INT_MAX);
|
|
EXPECT_EQ(INT_MAX, v.GetLocalProperty(kIntKey));
|
|
v.SetLocalProperty(kIntKey, -2);
|
|
EXPECT_EQ(-2, v.GetLocalProperty(kIntKey));
|
|
v.SetLocalProperty(kIntKey, INT_MIN);
|
|
EXPECT_EQ(INT_MIN, v.GetLocalProperty(kIntKey));
|
|
|
|
v.SetLocalProperty(kStringKey, static_cast<const char*>(NULL));
|
|
EXPECT_EQ(NULL, v.GetLocalProperty(kStringKey));
|
|
v.SetLocalProperty(kStringKey, "squeamish");
|
|
EXPECT_EQ(std::string("squeamish"), v.GetLocalProperty(kStringKey));
|
|
v.SetLocalProperty(kStringKey, "ossifrage");
|
|
EXPECT_EQ(std::string("ossifrage"), v.GetLocalProperty(kStringKey));
|
|
|
|
// ClearProperty should restore the default value.
|
|
v.ClearLocalProperty(kIntKey);
|
|
EXPECT_EQ(-2, v.GetLocalProperty(kIntKey));
|
|
v.ClearLocalProperty(kStringKey);
|
|
EXPECT_EQ(std::string("squeamish"), v.GetLocalProperty(kStringKey));
|
|
}
|
|
|
|
namespace {
|
|
|
|
class TestProperty {
|
|
public:
|
|
TestProperty() {}
|
|
virtual ~TestProperty() { last_deleted_ = this; }
|
|
static TestProperty* last_deleted() { return last_deleted_; }
|
|
|
|
private:
|
|
static TestProperty* last_deleted_;
|
|
MOJO_DISALLOW_COPY_AND_ASSIGN(TestProperty);
|
|
};
|
|
|
|
TestProperty* TestProperty::last_deleted_ = NULL;
|
|
|
|
DEFINE_OWNED_VIEW_PROPERTY_KEY(TestProperty, kOwnedKey, NULL);
|
|
|
|
} // namespace
|
|
|
|
TEST_F(ViewTest, OwnedProperty) {
|
|
TestProperty* p3 = NULL;
|
|
{
|
|
TestView v;
|
|
EXPECT_EQ(NULL, v.GetLocalProperty(kOwnedKey));
|
|
TestProperty* p1 = new TestProperty();
|
|
v.SetLocalProperty(kOwnedKey, p1);
|
|
EXPECT_EQ(p1, v.GetLocalProperty(kOwnedKey));
|
|
EXPECT_EQ(NULL, TestProperty::last_deleted());
|
|
|
|
TestProperty* p2 = new TestProperty();
|
|
v.SetLocalProperty(kOwnedKey, p2);
|
|
EXPECT_EQ(p2, v.GetLocalProperty(kOwnedKey));
|
|
EXPECT_EQ(p1, TestProperty::last_deleted());
|
|
|
|
v.ClearLocalProperty(kOwnedKey);
|
|
EXPECT_EQ(NULL, v.GetLocalProperty(kOwnedKey));
|
|
EXPECT_EQ(p2, TestProperty::last_deleted());
|
|
|
|
p3 = new TestProperty();
|
|
v.SetLocalProperty(kOwnedKey, p3);
|
|
EXPECT_EQ(p3, v.GetLocalProperty(kOwnedKey));
|
|
EXPECT_EQ(p2, TestProperty::last_deleted());
|
|
}
|
|
|
|
EXPECT_EQ(p3, TestProperty::last_deleted());
|
|
}
|
|
|
|
// ViewObserver --------------------------------------------------------
|
|
|
|
typedef testing::Test ViewObserverTest;
|
|
|
|
bool TreeChangeParamsMatch(const ViewObserver::TreeChangeParams& lhs,
|
|
const ViewObserver::TreeChangeParams& rhs) {
|
|
return lhs.target == rhs.target && lhs.old_parent == rhs.old_parent &&
|
|
lhs.new_parent == rhs.new_parent && lhs.receiver == rhs.receiver;
|
|
}
|
|
|
|
class TreeChangeObserver : public ViewObserver {
|
|
public:
|
|
explicit TreeChangeObserver(View* observee) : observee_(observee) {
|
|
observee_->AddObserver(this);
|
|
}
|
|
~TreeChangeObserver() override { observee_->RemoveObserver(this); }
|
|
|
|
void Reset() { received_params_.clear(); }
|
|
|
|
const std::vector<TreeChangeParams>& received_params() {
|
|
return received_params_;
|
|
}
|
|
|
|
private:
|
|
// Overridden from ViewObserver:
|
|
void OnTreeChanging(const TreeChangeParams& params) override {
|
|
received_params_.push_back(params);
|
|
}
|
|
void OnTreeChanged(const TreeChangeParams& params) override {
|
|
received_params_.push_back(params);
|
|
}
|
|
|
|
View* observee_;
|
|
std::vector<TreeChangeParams> received_params_;
|
|
|
|
MOJO_DISALLOW_COPY_AND_ASSIGN(TreeChangeObserver);
|
|
};
|
|
|
|
// Adds/Removes v11 to v1.
|
|
TEST_F(ViewObserverTest, TreeChange_SimpleAddRemove) {
|
|
TestView v1;
|
|
TreeChangeObserver o1(&v1);
|
|
EXPECT_TRUE(o1.received_params().empty());
|
|
|
|
TestView v11;
|
|
TreeChangeObserver o11(&v11);
|
|
EXPECT_TRUE(o11.received_params().empty());
|
|
|
|
// Add.
|
|
|
|
v1.AddChild(&v11);
|
|
|
|
EXPECT_EQ(2U, o1.received_params().size());
|
|
ViewObserver::TreeChangeParams p1;
|
|
p1.target = &v11;
|
|
p1.receiver = &v1;
|
|
p1.old_parent = NULL;
|
|
p1.new_parent = &v1;
|
|
EXPECT_TRUE(TreeChangeParamsMatch(p1, o1.received_params().back()));
|
|
|
|
EXPECT_EQ(2U, o11.received_params().size());
|
|
ViewObserver::TreeChangeParams p11 = p1;
|
|
p11.receiver = &v11;
|
|
EXPECT_TRUE(TreeChangeParamsMatch(p11, o11.received_params().front()));
|
|
EXPECT_TRUE(TreeChangeParamsMatch(p11, o11.received_params().back()));
|
|
|
|
o1.Reset();
|
|
o11.Reset();
|
|
EXPECT_TRUE(o1.received_params().empty());
|
|
EXPECT_TRUE(o11.received_params().empty());
|
|
|
|
// Remove.
|
|
|
|
v1.RemoveChild(&v11);
|
|
|
|
EXPECT_EQ(2U, o1.received_params().size());
|
|
p1.target = &v11;
|
|
p1.receiver = &v1;
|
|
p1.old_parent = &v1;
|
|
p1.new_parent = NULL;
|
|
EXPECT_TRUE(TreeChangeParamsMatch(p1, o1.received_params().front()));
|
|
|
|
EXPECT_EQ(2U, o11.received_params().size());
|
|
p11 = p1;
|
|
p11.receiver = &v11;
|
|
EXPECT_TRUE(TreeChangeParamsMatch(p11, o11.received_params().front()));
|
|
EXPECT_TRUE(TreeChangeParamsMatch(p11, o11.received_params().back()));
|
|
}
|
|
|
|
// Creates these two trees:
|
|
// v1
|
|
// +- v11
|
|
// v111
|
|
// +- v1111
|
|
// +- v1112
|
|
// Then adds/removes v111 from v11.
|
|
TEST_F(ViewObserverTest, TreeChange_NestedAddRemove) {
|
|
TestView v1, v11, v111, v1111, v1112;
|
|
|
|
// Root tree.
|
|
v1.AddChild(&v11);
|
|
|
|
// Tree to be attached.
|
|
v111.AddChild(&v1111);
|
|
v111.AddChild(&v1112);
|
|
|
|
TreeChangeObserver o1(&v1), o11(&v11), o111(&v111), o1111(&v1111),
|
|
o1112(&v1112);
|
|
ViewObserver::TreeChangeParams p1, p11, p111, p1111, p1112;
|
|
|
|
// Add.
|
|
|
|
v11.AddChild(&v111);
|
|
|
|
EXPECT_EQ(2U, o1.received_params().size());
|
|
p1.target = &v111;
|
|
p1.receiver = &v1;
|
|
p1.old_parent = NULL;
|
|
p1.new_parent = &v11;
|
|
EXPECT_TRUE(TreeChangeParamsMatch(p1, o1.received_params().back()));
|
|
|
|
EXPECT_EQ(2U, o11.received_params().size());
|
|
p11 = p1;
|
|
p11.receiver = &v11;
|
|
EXPECT_TRUE(TreeChangeParamsMatch(p11, o11.received_params().back()));
|
|
|
|
EXPECT_EQ(2U, o111.received_params().size());
|
|
p111 = p11;
|
|
p111.receiver = &v111;
|
|
EXPECT_TRUE(TreeChangeParamsMatch(p111, o111.received_params().front()));
|
|
EXPECT_TRUE(TreeChangeParamsMatch(p111, o111.received_params().back()));
|
|
|
|
EXPECT_EQ(2U, o1111.received_params().size());
|
|
p1111 = p111;
|
|
p1111.receiver = &v1111;
|
|
EXPECT_TRUE(TreeChangeParamsMatch(p1111, o1111.received_params().front()));
|
|
EXPECT_TRUE(TreeChangeParamsMatch(p1111, o1111.received_params().back()));
|
|
|
|
EXPECT_EQ(2U, o1112.received_params().size());
|
|
p1112 = p111;
|
|
p1112.receiver = &v1112;
|
|
EXPECT_TRUE(TreeChangeParamsMatch(p1112, o1112.received_params().front()));
|
|
EXPECT_TRUE(TreeChangeParamsMatch(p1112, o1112.received_params().back()));
|
|
|
|
// Remove.
|
|
o1.Reset();
|
|
o11.Reset();
|
|
o111.Reset();
|
|
o1111.Reset();
|
|
o1112.Reset();
|
|
EXPECT_TRUE(o1.received_params().empty());
|
|
EXPECT_TRUE(o11.received_params().empty());
|
|
EXPECT_TRUE(o111.received_params().empty());
|
|
EXPECT_TRUE(o1111.received_params().empty());
|
|
EXPECT_TRUE(o1112.received_params().empty());
|
|
|
|
v11.RemoveChild(&v111);
|
|
|
|
EXPECT_EQ(2U, o1.received_params().size());
|
|
p1.target = &v111;
|
|
p1.receiver = &v1;
|
|
p1.old_parent = &v11;
|
|
p1.new_parent = NULL;
|
|
EXPECT_TRUE(TreeChangeParamsMatch(p1, o1.received_params().front()));
|
|
|
|
EXPECT_EQ(2U, o11.received_params().size());
|
|
p11 = p1;
|
|
p11.receiver = &v11;
|
|
EXPECT_TRUE(TreeChangeParamsMatch(p11, o11.received_params().front()));
|
|
|
|
EXPECT_EQ(2U, o111.received_params().size());
|
|
p111 = p11;
|
|
p111.receiver = &v111;
|
|
EXPECT_TRUE(TreeChangeParamsMatch(p111, o111.received_params().front()));
|
|
EXPECT_TRUE(TreeChangeParamsMatch(p111, o111.received_params().back()));
|
|
|
|
EXPECT_EQ(2U, o1111.received_params().size());
|
|
p1111 = p111;
|
|
p1111.receiver = &v1111;
|
|
EXPECT_TRUE(TreeChangeParamsMatch(p1111, o1111.received_params().front()));
|
|
EXPECT_TRUE(TreeChangeParamsMatch(p1111, o1111.received_params().back()));
|
|
|
|
EXPECT_EQ(2U, o1112.received_params().size());
|
|
p1112 = p111;
|
|
p1112.receiver = &v1112;
|
|
EXPECT_TRUE(TreeChangeParamsMatch(p1112, o1112.received_params().front()));
|
|
EXPECT_TRUE(TreeChangeParamsMatch(p1112, o1112.received_params().back()));
|
|
}
|
|
|
|
TEST_F(ViewObserverTest, TreeChange_Reparent) {
|
|
TestView v1, v11, v12, v111;
|
|
v1.AddChild(&v11);
|
|
v1.AddChild(&v12);
|
|
v11.AddChild(&v111);
|
|
|
|
TreeChangeObserver o1(&v1), o11(&v11), o12(&v12), o111(&v111);
|
|
|
|
// Reparent.
|
|
v12.AddChild(&v111);
|
|
|
|
// v1 (root) should see both changing and changed notifications.
|
|
EXPECT_EQ(4U, o1.received_params().size());
|
|
ViewObserver::TreeChangeParams p1;
|
|
p1.target = &v111;
|
|
p1.receiver = &v1;
|
|
p1.old_parent = &v11;
|
|
p1.new_parent = &v12;
|
|
EXPECT_TRUE(TreeChangeParamsMatch(p1, o1.received_params().front()));
|
|
EXPECT_TRUE(TreeChangeParamsMatch(p1, o1.received_params().back()));
|
|
|
|
// v11 should see changing notifications.
|
|
EXPECT_EQ(2U, o11.received_params().size());
|
|
ViewObserver::TreeChangeParams p11;
|
|
p11 = p1;
|
|
p11.receiver = &v11;
|
|
EXPECT_TRUE(TreeChangeParamsMatch(p11, o11.received_params().front()));
|
|
|
|
// v12 should see changed notifications.
|
|
EXPECT_EQ(2U, o12.received_params().size());
|
|
ViewObserver::TreeChangeParams p12;
|
|
p12 = p1;
|
|
p12.receiver = &v12;
|
|
EXPECT_TRUE(TreeChangeParamsMatch(p12, o12.received_params().back()));
|
|
|
|
// v111 should see both changing and changed notifications.
|
|
EXPECT_EQ(2U, o111.received_params().size());
|
|
ViewObserver::TreeChangeParams p111;
|
|
p111 = p1;
|
|
p111.receiver = &v111;
|
|
EXPECT_TRUE(TreeChangeParamsMatch(p111, o111.received_params().front()));
|
|
EXPECT_TRUE(TreeChangeParamsMatch(p111, o111.received_params().back()));
|
|
}
|
|
|
|
namespace {
|
|
|
|
class OrderChangeObserver : public ViewObserver {
|
|
public:
|
|
struct Change {
|
|
View* view;
|
|
View* relative_view;
|
|
OrderDirection direction;
|
|
};
|
|
typedef std::vector<Change> Changes;
|
|
|
|
explicit OrderChangeObserver(View* observee) : observee_(observee) {
|
|
observee_->AddObserver(this);
|
|
}
|
|
~OrderChangeObserver() override { observee_->RemoveObserver(this); }
|
|
|
|
Changes GetAndClearChanges() {
|
|
Changes changes;
|
|
changes_.swap(changes);
|
|
return changes;
|
|
}
|
|
|
|
private:
|
|
// Overridden from ViewObserver:
|
|
void OnViewReordering(View* view,
|
|
View* relative_view,
|
|
OrderDirection direction) override {
|
|
OnViewReordered(view, relative_view, direction);
|
|
}
|
|
|
|
void OnViewReordered(View* view,
|
|
View* relative_view,
|
|
OrderDirection direction) override {
|
|
Change change;
|
|
change.view = view;
|
|
change.relative_view = relative_view;
|
|
change.direction = direction;
|
|
changes_.push_back(change);
|
|
}
|
|
|
|
View* observee_;
|
|
Changes changes_;
|
|
|
|
MOJO_DISALLOW_COPY_AND_ASSIGN(OrderChangeObserver);
|
|
};
|
|
|
|
} // namespace
|
|
|
|
TEST_F(ViewObserverTest, Order) {
|
|
TestView v1, v11, v12, v13;
|
|
v1.AddChild(&v11);
|
|
v1.AddChild(&v12);
|
|
v1.AddChild(&v13);
|
|
|
|
// Order: v11, v12, v13
|
|
EXPECT_EQ(3U, v1.children().size());
|
|
EXPECT_EQ(&v11, v1.children().front());
|
|
EXPECT_EQ(&v13, v1.children().back());
|
|
|
|
{
|
|
OrderChangeObserver observer(&v11);
|
|
|
|
// Move v11 to front.
|
|
// Resulting order: v12, v13, v11
|
|
v11.MoveToFront();
|
|
EXPECT_EQ(&v12, v1.children().front());
|
|
EXPECT_EQ(&v11, v1.children().back());
|
|
|
|
OrderChangeObserver::Changes changes = observer.GetAndClearChanges();
|
|
ASSERT_EQ(2U, changes.size());
|
|
EXPECT_EQ(&v11, changes[0].view);
|
|
EXPECT_EQ(&v13, changes[0].relative_view);
|
|
EXPECT_EQ(OrderDirection::ABOVE, changes[0].direction);
|
|
|
|
EXPECT_EQ(&v11, changes[1].view);
|
|
EXPECT_EQ(&v13, changes[1].relative_view);
|
|
EXPECT_EQ(OrderDirection::ABOVE, changes[1].direction);
|
|
}
|
|
|
|
{
|
|
OrderChangeObserver observer(&v11);
|
|
|
|
// Move v11 to back.
|
|
// Resulting order: v11, v12, v13
|
|
v11.MoveToBack();
|
|
EXPECT_EQ(&v11, v1.children().front());
|
|
EXPECT_EQ(&v13, v1.children().back());
|
|
|
|
OrderChangeObserver::Changes changes = observer.GetAndClearChanges();
|
|
ASSERT_EQ(2U, changes.size());
|
|
EXPECT_EQ(&v11, changes[0].view);
|
|
EXPECT_EQ(&v12, changes[0].relative_view);
|
|
EXPECT_EQ(OrderDirection::BELOW, changes[0].direction);
|
|
|
|
EXPECT_EQ(&v11, changes[1].view);
|
|
EXPECT_EQ(&v12, changes[1].relative_view);
|
|
EXPECT_EQ(OrderDirection::BELOW, changes[1].direction);
|
|
}
|
|
|
|
{
|
|
OrderChangeObserver observer(&v11);
|
|
|
|
// Move v11 above v12.
|
|
// Resulting order: v12. v11, v13
|
|
v11.Reorder(&v12, OrderDirection::ABOVE);
|
|
EXPECT_EQ(&v12, v1.children().front());
|
|
EXPECT_EQ(&v13, v1.children().back());
|
|
|
|
OrderChangeObserver::Changes changes = observer.GetAndClearChanges();
|
|
ASSERT_EQ(2U, changes.size());
|
|
EXPECT_EQ(&v11, changes[0].view);
|
|
EXPECT_EQ(&v12, changes[0].relative_view);
|
|
EXPECT_EQ(OrderDirection::ABOVE, changes[0].direction);
|
|
|
|
EXPECT_EQ(&v11, changes[1].view);
|
|
EXPECT_EQ(&v12, changes[1].relative_view);
|
|
EXPECT_EQ(OrderDirection::ABOVE, changes[1].direction);
|
|
}
|
|
|
|
{
|
|
OrderChangeObserver observer(&v11);
|
|
|
|
// Move v11 below v12.
|
|
// Resulting order: v11, v12, v13
|
|
v11.Reorder(&v12, OrderDirection::BELOW);
|
|
EXPECT_EQ(&v11, v1.children().front());
|
|
EXPECT_EQ(&v13, v1.children().back());
|
|
|
|
OrderChangeObserver::Changes changes = observer.GetAndClearChanges();
|
|
ASSERT_EQ(2U, changes.size());
|
|
EXPECT_EQ(&v11, changes[0].view);
|
|
EXPECT_EQ(&v12, changes[0].relative_view);
|
|
EXPECT_EQ(OrderDirection::BELOW, changes[0].direction);
|
|
|
|
EXPECT_EQ(&v11, changes[1].view);
|
|
EXPECT_EQ(&v12, changes[1].relative_view);
|
|
EXPECT_EQ(OrderDirection::BELOW, changes[1].direction);
|
|
}
|
|
}
|
|
|
|
namespace {
|
|
|
|
typedef std::vector<std::string> Changes;
|
|
|
|
std::string ViewIdToString(Id id) {
|
|
return (id == 0) ? "null"
|
|
: base::StringPrintf("%d,%d", HiWord(id), LoWord(id));
|
|
}
|
|
|
|
std::string RectToString(const Rect& rect) {
|
|
return base::StringPrintf("%d,%d %dx%d", rect.x, rect.y, rect.width,
|
|
rect.height);
|
|
}
|
|
|
|
class BoundsChangeObserver : public ViewObserver {
|
|
public:
|
|
explicit BoundsChangeObserver(View* view) : view_(view) {
|
|
view_->AddObserver(this);
|
|
}
|
|
~BoundsChangeObserver() override { view_->RemoveObserver(this); }
|
|
|
|
Changes GetAndClearChanges() {
|
|
Changes changes;
|
|
changes.swap(changes_);
|
|
return changes;
|
|
}
|
|
|
|
private:
|
|
// Overridden from ViewObserver:
|
|
void OnViewBoundsChanging(View* view,
|
|
const Rect& old_bounds,
|
|
const Rect& new_bounds) override {
|
|
changes_.push_back(base::StringPrintf(
|
|
"view=%s old_bounds=%s new_bounds=%s phase=changing",
|
|
ViewIdToString(view->id()).c_str(), RectToString(old_bounds).c_str(),
|
|
RectToString(new_bounds).c_str()));
|
|
}
|
|
void OnViewBoundsChanged(View* view,
|
|
const Rect& old_bounds,
|
|
const Rect& new_bounds) override {
|
|
changes_.push_back(base::StringPrintf(
|
|
"view=%s old_bounds=%s new_bounds=%s phase=changed",
|
|
ViewIdToString(view->id()).c_str(), RectToString(old_bounds).c_str(),
|
|
RectToString(new_bounds).c_str()));
|
|
}
|
|
|
|
View* view_;
|
|
Changes changes_;
|
|
|
|
MOJO_DISALLOW_COPY_AND_ASSIGN(BoundsChangeObserver);
|
|
};
|
|
|
|
} // namespace
|
|
|
|
TEST_F(ViewObserverTest, SetBounds) {
|
|
TestView v1;
|
|
{
|
|
BoundsChangeObserver observer(&v1);
|
|
Rect rect;
|
|
rect.width = rect.height = 100;
|
|
v1.SetBounds(rect);
|
|
|
|
Changes changes = observer.GetAndClearChanges();
|
|
ASSERT_EQ(2U, changes.size());
|
|
EXPECT_EQ(
|
|
"view=0,1 old_bounds=0,0 0x0 new_bounds=0,0 100x100 phase=changing",
|
|
changes[0]);
|
|
EXPECT_EQ(
|
|
"view=0,1 old_bounds=0,0 0x0 new_bounds=0,0 100x100 phase=changed",
|
|
changes[1]);
|
|
}
|
|
}
|
|
|
|
namespace {
|
|
|
|
class VisibilityChangeObserver : public ViewObserver {
|
|
public:
|
|
explicit VisibilityChangeObserver(View* view) : view_(view) {
|
|
view_->AddObserver(this);
|
|
}
|
|
~VisibilityChangeObserver() override { view_->RemoveObserver(this); }
|
|
|
|
Changes GetAndClearChanges() {
|
|
Changes changes;
|
|
changes.swap(changes_);
|
|
return changes;
|
|
}
|
|
|
|
private:
|
|
// Overridden from ViewObserver:
|
|
void OnViewVisibilityChanging(View* view) override {
|
|
changes_.push_back(
|
|
base::StringPrintf("view=%s phase=changing visibility=%s",
|
|
ViewIdToString(view->id()).c_str(),
|
|
view->visible() ? "true" : "false"));
|
|
}
|
|
void OnViewVisibilityChanged(View* view) override {
|
|
changes_.push_back(base::StringPrintf("view=%s phase=changed visibility=%s",
|
|
ViewIdToString(view->id()).c_str(),
|
|
view->visible() ? "true" : "false"));
|
|
}
|
|
|
|
View* view_;
|
|
Changes changes_;
|
|
|
|
MOJO_DISALLOW_COPY_AND_ASSIGN(VisibilityChangeObserver);
|
|
};
|
|
|
|
} // namespace
|
|
|
|
TEST_F(ViewObserverTest, SetVisible) {
|
|
TestView v1;
|
|
EXPECT_TRUE(v1.visible());
|
|
{
|
|
// Change visibility from true to false and make sure we get notifications.
|
|
VisibilityChangeObserver observer(&v1);
|
|
v1.SetVisible(false);
|
|
|
|
Changes changes = observer.GetAndClearChanges();
|
|
ASSERT_EQ(2U, changes.size());
|
|
EXPECT_EQ("view=0,1 phase=changing visibility=true", changes[0]);
|
|
EXPECT_EQ("view=0,1 phase=changed visibility=false", changes[1]);
|
|
}
|
|
{
|
|
// Set visible to existing value and verify no notifications.
|
|
VisibilityChangeObserver observer(&v1);
|
|
v1.SetVisible(false);
|
|
EXPECT_TRUE(observer.GetAndClearChanges().empty());
|
|
}
|
|
}
|
|
|
|
TEST_F(ViewObserverTest, SetVisibleParent) {
|
|
TestView parent;
|
|
ViewPrivate(&parent).set_id(1);
|
|
TestView child;
|
|
ViewPrivate(&child).set_id(2);
|
|
parent.AddChild(&child);
|
|
EXPECT_TRUE(parent.visible());
|
|
EXPECT_TRUE(child.visible());
|
|
{
|
|
// Change visibility from true to false and make sure we get notifications
|
|
// on the parent.
|
|
VisibilityChangeObserver observer(&parent);
|
|
child.SetVisible(false);
|
|
|
|
Changes changes = observer.GetAndClearChanges();
|
|
ASSERT_EQ(1U, changes.size());
|
|
EXPECT_EQ("view=0,2 phase=changed visibility=false", changes[0]);
|
|
}
|
|
}
|
|
|
|
TEST_F(ViewObserverTest, SetVisibleChild) {
|
|
TestView parent;
|
|
ViewPrivate(&parent).set_id(1);
|
|
TestView child;
|
|
ViewPrivate(&child).set_id(2);
|
|
parent.AddChild(&child);
|
|
EXPECT_TRUE(parent.visible());
|
|
EXPECT_TRUE(child.visible());
|
|
{
|
|
// Change visibility from true to false and make sure we get notifications
|
|
// on the child.
|
|
VisibilityChangeObserver observer(&child);
|
|
parent.SetVisible(false);
|
|
|
|
Changes changes = observer.GetAndClearChanges();
|
|
ASSERT_EQ(1U, changes.size());
|
|
EXPECT_EQ("view=0,1 phase=changed visibility=false", changes[0]);
|
|
}
|
|
}
|
|
|
|
namespace {
|
|
|
|
class SharedPropertyChangeObserver : public ViewObserver {
|
|
public:
|
|
explicit SharedPropertyChangeObserver(View* view) : view_(view) {
|
|
view_->AddObserver(this);
|
|
}
|
|
~SharedPropertyChangeObserver() override { view_->RemoveObserver(this); }
|
|
|
|
Changes GetAndClearChanges() {
|
|
Changes changes;
|
|
changes.swap(changes_);
|
|
return changes;
|
|
}
|
|
|
|
private:
|
|
// Overridden from ViewObserver:
|
|
void OnViewSharedPropertyChanged(
|
|
View* view,
|
|
const std::string& name,
|
|
const std::vector<uint8_t>* old_data,
|
|
const std::vector<uint8_t>* new_data) override {
|
|
changes_.push_back(base::StringPrintf(
|
|
"view=%s shared property changed key=%s old_value=%s new_value=%s",
|
|
ViewIdToString(view->id()).c_str(), name.c_str(),
|
|
VectorToString(old_data).c_str(), VectorToString(new_data).c_str()));
|
|
}
|
|
|
|
std::string VectorToString(const std::vector<uint8_t>* data) {
|
|
if (!data)
|
|
return "NULL";
|
|
std::string s;
|
|
for (char c : *data)
|
|
s += c;
|
|
return s;
|
|
}
|
|
|
|
View* view_;
|
|
Changes changes_;
|
|
|
|
MOJO_DISALLOW_COPY_AND_ASSIGN(SharedPropertyChangeObserver);
|
|
};
|
|
|
|
} // namespace
|
|
|
|
TEST_F(ViewObserverTest, SetLocalProperty) {
|
|
TestView v1;
|
|
std::vector<uint8_t> one(1, '1');
|
|
|
|
{
|
|
// Change visibility from true to false and make sure we get notifications.
|
|
SharedPropertyChangeObserver observer(&v1);
|
|
v1.SetSharedProperty("one", &one);
|
|
Changes changes = observer.GetAndClearChanges();
|
|
ASSERT_EQ(1U, changes.size());
|
|
EXPECT_EQ(
|
|
"view=0,1 shared property changed key=one old_value=NULL new_value=1",
|
|
changes[0]);
|
|
EXPECT_EQ(1U, v1.shared_properties().size());
|
|
}
|
|
{
|
|
// Set visible to existing value and verify no notifications.
|
|
SharedPropertyChangeObserver observer(&v1);
|
|
v1.SetSharedProperty("one", &one);
|
|
EXPECT_TRUE(observer.GetAndClearChanges().empty());
|
|
EXPECT_EQ(1U, v1.shared_properties().size());
|
|
}
|
|
{
|
|
// Set the value to NULL to delete it.
|
|
// Change visibility from true to false and make sure we get notifications.
|
|
SharedPropertyChangeObserver observer(&v1);
|
|
v1.SetSharedProperty("one", NULL);
|
|
Changes changes = observer.GetAndClearChanges();
|
|
ASSERT_EQ(1U, changes.size());
|
|
EXPECT_EQ(
|
|
"view=0,1 shared property changed key=one old_value=1 new_value=NULL",
|
|
changes[0]);
|
|
EXPECT_EQ(0U, v1.shared_properties().size());
|
|
}
|
|
{
|
|
// Setting a null property to null shouldn't update us.
|
|
SharedPropertyChangeObserver observer(&v1);
|
|
v1.SetSharedProperty("one", NULL);
|
|
EXPECT_TRUE(observer.GetAndClearChanges().empty());
|
|
EXPECT_EQ(0U, v1.shared_properties().size());
|
|
}
|
|
}
|
|
|
|
namespace {
|
|
|
|
typedef std::pair<const void*, intptr_t> PropertyChangeInfo;
|
|
|
|
class LocalPropertyChangeObserver : public ViewObserver {
|
|
public:
|
|
explicit LocalPropertyChangeObserver(View* view)
|
|
: view_(view), property_key_(nullptr), old_property_value_(-1) {
|
|
view_->AddObserver(this);
|
|
}
|
|
~LocalPropertyChangeObserver() override { view_->RemoveObserver(this); }
|
|
|
|
PropertyChangeInfo PropertyChangeInfoAndClear() {
|
|
PropertyChangeInfo result(property_key_, old_property_value_);
|
|
property_key_ = NULL;
|
|
old_property_value_ = -3;
|
|
return result;
|
|
}
|
|
|
|
private:
|
|
void OnViewLocalPropertyChanged(View* window,
|
|
const void* key,
|
|
intptr_t old) override {
|
|
property_key_ = key;
|
|
old_property_value_ = old;
|
|
}
|
|
|
|
View* view_;
|
|
const void* property_key_;
|
|
intptr_t old_property_value_;
|
|
|
|
MOJO_DISALLOW_COPY_AND_ASSIGN(LocalPropertyChangeObserver);
|
|
};
|
|
|
|
} // namespace
|
|
|
|
TEST_F(ViewObserverTest, LocalPropertyChanged) {
|
|
TestView v1;
|
|
LocalPropertyChangeObserver o(&v1);
|
|
|
|
static const ViewProperty<int> prop = {-2};
|
|
|
|
v1.SetLocalProperty(&prop, 1);
|
|
EXPECT_EQ(PropertyChangeInfo(&prop, -2), o.PropertyChangeInfoAndClear());
|
|
v1.SetLocalProperty(&prop, -2);
|
|
EXPECT_EQ(PropertyChangeInfo(&prop, 1), o.PropertyChangeInfoAndClear());
|
|
v1.SetLocalProperty(&prop, 3);
|
|
EXPECT_EQ(PropertyChangeInfo(&prop, -2), o.PropertyChangeInfoAndClear());
|
|
v1.ClearLocalProperty(&prop);
|
|
EXPECT_EQ(PropertyChangeInfo(&prop, 3), o.PropertyChangeInfoAndClear());
|
|
|
|
// Sanity check to see if |PropertyChangeInfoAndClear| really clears.
|
|
EXPECT_EQ(PropertyChangeInfo(reinterpret_cast<const void*>(NULL), -3),
|
|
o.PropertyChangeInfoAndClear());
|
|
}
|
|
|
|
} // namespace mojo
|