1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361
|
// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ui/base/class_property.h"
#include <limits.h>
#include <string>
#include <utility>
#include <vector>
#include "base/compiler_specific.h"
#include "base/memory/raw_ptr.h"
#include "build/build_config.h"
#include "testing/gtest/include/gtest/gtest.h"
DEFINE_UI_CLASS_PROPERTY_TYPE(const char*)
DEFINE_UI_CLASS_PROPERTY_TYPE(int)
DEFINE_UI_CLASS_PROPERTY_TYPE(float)
namespace {
class TestProperty {
public:
TestProperty() = default;
TestProperty(const TestProperty&) = delete;
TestProperty& operator=(const TestProperty&) = delete;
~TestProperty() {
last_deleted_ = this;
}
static void* last_deleted() { return last_deleted_; }
private:
static void* last_deleted_;
};
class TestCascadingProperty {
public:
explicit TestCascadingProperty(ui::PropertyHandler* handler)
: handler_(handler) {}
~TestCascadingProperty() = default;
ui::PropertyHandler* handler() { return handler_; }
private:
raw_ptr<ui::PropertyHandler> handler_;
};
void* TestProperty::last_deleted_ = nullptr;
class AssignableTestProperty {
public:
AssignableTestProperty() {}
AssignableTestProperty(int value) : value_(value) {}
AssignableTestProperty(const AssignableTestProperty& other)
: value_(other.value_) {}
AssignableTestProperty(AssignableTestProperty&& other)
: value_(std::move(other.value_)), was_move_assigned_(true) {}
AssignableTestProperty& operator=(const AssignableTestProperty& other) {
value_ = other.value_;
was_move_assigned_ = false;
return *this;
}
AssignableTestProperty& operator=(AssignableTestProperty&& other) {
value_ = std::move(other.value_);
was_move_assigned_ = true;
return *this;
}
int value() const { return value_; }
bool was_move_assigned() const { return was_move_assigned_; }
private:
int value_ = 0;
bool was_move_assigned_ = false;
};
DEFINE_OWNED_UI_CLASS_PROPERTY_KEY(TestProperty, kOwnedKey, nullptr)
DEFINE_OWNED_UI_CLASS_PROPERTY_KEY(AssignableTestProperty,
kAssignableKey,
nullptr)
DEFINE_CASCADING_OWNED_UI_CLASS_PROPERTY_KEY(TestCascadingProperty,
kCascadingOwnedKey,
nullptr)
} // namespace
DEFINE_UI_CLASS_PROPERTY_TYPE(TestProperty*)
DEFINE_UI_CLASS_PROPERTY_TYPE(AssignableTestProperty*)
DEFINE_UI_CLASS_PROPERTY_TYPE(TestCascadingProperty*)
namespace ui {
namespace test {
namespace {
class TestPropertyHandler : public PropertyHandler {
public:
TestPropertyHandler() = default;
explicit TestPropertyHandler(TestPropertyHandler* parent) : parent_(parent) {}
~TestPropertyHandler() override = default;
int num_events() const { return num_events_; }
protected:
void AfterPropertyChange(const void* key, int64_t old_value) override {
++num_events_;
}
PropertyHandler* GetParentHandler() const override { return parent_; }
private:
int num_events_ = 0;
raw_ptr<TestPropertyHandler> parent_ = nullptr;
};
const int kDefaultIntValue = -2;
const char* kDefaultStringValue = "squeamish";
const char* kTestStringValue = "ossifrage";
const float kDefaultFloatValue = 3.0;
DEFINE_UI_CLASS_PROPERTY_KEY(int, kIntKey, kDefaultIntValue)
DEFINE_UI_CLASS_PROPERTY_KEY(const char*, kStringKey, kDefaultStringValue)
DEFINE_UI_CLASS_PROPERTY_KEY(float, kFloatKey, kDefaultFloatValue)
}
TEST(PropertyTest, Property) {
PropertyHandler h;
// Non-existent properties should return the default values.
EXPECT_EQ(kDefaultIntValue, h.GetProperty(kIntKey));
EXPECT_EQ(std::string(kDefaultStringValue), h.GetProperty(kStringKey));
EXPECT_EQ(kDefaultFloatValue, h.GetProperty(kFloatKey));
// A set property value should be returned again (even if it's the default
// value).
h.SetProperty(kIntKey, INT_MAX);
EXPECT_EQ(INT_MAX, h.GetProperty(kIntKey));
h.SetProperty(kIntKey, kDefaultIntValue);
EXPECT_EQ(kDefaultIntValue, h.GetProperty(kIntKey));
h.SetProperty(kIntKey, INT_MIN);
EXPECT_EQ(INT_MIN, h.GetProperty(kIntKey));
h.SetProperty<const char*>(kStringKey, nullptr);
EXPECT_EQ(NULL, h.GetProperty(kStringKey));
h.SetProperty(kStringKey, kDefaultStringValue);
EXPECT_EQ(std::string(kDefaultStringValue), h.GetProperty(kStringKey));
h.SetProperty(kStringKey, kTestStringValue);
EXPECT_EQ(std::string(kTestStringValue), h.GetProperty(kStringKey));
h.SetProperty(kFloatKey, 3.14f);
EXPECT_EQ(3.14f, h.GetProperty(kFloatKey));
h.SetProperty(kFloatKey, kDefaultFloatValue);
EXPECT_EQ(kDefaultFloatValue, h.GetProperty(kFloatKey));
h.SetProperty(kFloatKey, -234.5678f);
EXPECT_EQ(-234.5678f, h.GetProperty(kFloatKey));
// ClearProperty should restore the default value.
h.ClearProperty(kIntKey);
EXPECT_EQ(kDefaultIntValue, h.GetProperty(kIntKey));
h.ClearProperty(kStringKey);
EXPECT_EQ(std::string(kDefaultStringValue), h.GetProperty(kStringKey));
}
TEST(PropertyTest, OwnedProperty) {
TestProperty* p3;
{
PropertyHandler h;
EXPECT_EQ(nullptr, h.GetProperty(kOwnedKey));
void* last_deleted = TestProperty::last_deleted();
TestProperty* p1 =
h.SetProperty(kOwnedKey, std::make_unique<TestProperty>());
EXPECT_EQ(p1, h.GetProperty(kOwnedKey));
EXPECT_EQ(last_deleted, TestProperty::last_deleted());
TestProperty* p2 =
h.SetProperty(kOwnedKey, std::make_unique<TestProperty>());
EXPECT_EQ(p2, h.GetProperty(kOwnedKey));
EXPECT_EQ(p1, TestProperty::last_deleted());
h.ClearProperty(kOwnedKey);
EXPECT_EQ(nullptr, h.GetProperty(kOwnedKey));
EXPECT_EQ(p2, TestProperty::last_deleted());
p3 = h.SetProperty(kOwnedKey, std::make_unique<TestProperty>());
EXPECT_EQ(p3, h.GetProperty(kOwnedKey));
EXPECT_EQ(p2, TestProperty::last_deleted());
}
EXPECT_EQ(p3, TestProperty::last_deleted());
}
TEST(PropertyTest, AcquireAllPropertiesFrom) {
// Set some properties on src, including an owned property.
std::unique_ptr<PropertyHandler> src = std::make_unique<PropertyHandler>();
void* last_deleted = TestProperty::last_deleted();
EXPECT_FALSE(src->GetProperty(kOwnedKey));
TestProperty* p1 =
src->SetProperty(kOwnedKey, std::make_unique<TestProperty>());
src->SetProperty(kIntKey, INT_MAX);
// dest will take ownership of the owned property. Existing properties with
// similar keys will be overwritten. Existing properties with different keys
// will remain unchanged.
std::unique_ptr<PropertyHandler> dest = std::make_unique<PropertyHandler>();
dest->SetProperty(kIntKey, INT_MIN);
dest->SetProperty(kStringKey, kTestStringValue);
dest->AcquireAllPropertiesFrom(std::move(*src));
EXPECT_EQ(p1, dest->GetProperty(kOwnedKey)); // Ownership taken.
EXPECT_EQ(INT_MAX, dest->GetProperty(kIntKey)); // Overwritten.
// Remains unchanged.
EXPECT_EQ(std::string(kTestStringValue), dest->GetProperty(kStringKey));
// src no longer has properties.
EXPECT_TRUE(src->GetAllPropertyKeys().empty());
EXPECT_FALSE(src->GetProperty(kOwnedKey));
EXPECT_EQ(kDefaultIntValue, src->GetProperty(kIntKey));
// Destroy src. Owned property remains alive.
src.reset();
EXPECT_EQ(last_deleted, TestProperty::last_deleted());
// Destroy dest, now the owned property is deleted.
dest.reset();
EXPECT_EQ(p1, TestProperty::last_deleted());
}
TEST(PropertyTest, AssignableProperty) {
PropertyHandler h;
// Verify that assigning a property by value allocates a value.
EXPECT_EQ(nullptr, h.GetProperty(kAssignableKey));
const AssignableTestProperty kTestProperty{1};
h.SetProperty(kAssignableKey, kTestProperty);
AssignableTestProperty* p = h.GetProperty(kAssignableKey);
EXPECT_NE(nullptr, p);
EXPECT_EQ(1, p->value());
// Verify that assigning by value updates the existing value without
// additional allocation.
h.SetProperty(kAssignableKey, AssignableTestProperty{2});
EXPECT_EQ(p, h.GetProperty(kAssignableKey));
EXPECT_EQ(2, p->value());
// Same as the above case, but with a const reference instead of a move.
const AssignableTestProperty kTestProperty2{3};
h.SetProperty(kAssignableKey, kTestProperty2);
EXPECT_EQ(p, h.GetProperty(kAssignableKey));
EXPECT_EQ(3, p->value());
// Verify that clearing the property deallocates the value.
h.ClearProperty(kAssignableKey);
EXPECT_EQ(nullptr, h.GetProperty(kAssignableKey));
// Verify that setting by value after clearing allocates a new value.
h.SetProperty(kAssignableKey, AssignableTestProperty{4});
EXPECT_EQ(4, h.GetProperty(kAssignableKey)->value());
}
TEST(PropertyTest, SetProperty_ForwardsParametersCorrectly) {
PropertyHandler h;
// New property from a const ref.
const AssignableTestProperty kTestProperty{1};
h.SetProperty(kAssignableKey, kTestProperty);
EXPECT_FALSE(h.GetProperty(kAssignableKey)->was_move_assigned());
// Set property from inline rvalue ref.
h.SetProperty(kAssignableKey, AssignableTestProperty{2});
EXPECT_TRUE(h.GetProperty(kAssignableKey)->was_move_assigned());
// Set property from lvalue.
AssignableTestProperty test_property{3};
h.SetProperty(kAssignableKey, test_property);
EXPECT_FALSE(h.GetProperty(kAssignableKey)->was_move_assigned());
// Set property from lvalue ref.
AssignableTestProperty& ref = test_property;
h.SetProperty(kAssignableKey, ref);
EXPECT_FALSE(h.GetProperty(kAssignableKey)->was_move_assigned());
// Set property from moved rvalue ref.
h.SetProperty(kAssignableKey, std::move(test_property));
EXPECT_TRUE(h.GetProperty(kAssignableKey)->was_move_assigned());
// Set property from const ref.
const AssignableTestProperty& const_ref = kTestProperty;
h.SetProperty(kAssignableKey, const_ref);
EXPECT_FALSE(h.GetProperty(kAssignableKey)->was_move_assigned());
// New property from rvalue ref.
h.ClearProperty(kAssignableKey);
h.SetProperty(kAssignableKey, AssignableTestProperty{4});
EXPECT_TRUE(h.GetProperty(kAssignableKey)->was_move_assigned());
// New property from lvalue.
h.ClearProperty(kAssignableKey);
test_property = AssignableTestProperty{5};
h.SetProperty(kAssignableKey, test_property);
EXPECT_FALSE(h.GetProperty(kAssignableKey)->was_move_assigned());
}
TEST(PropertyTest, PropertyChangedEvent) {
TestPropertyHandler h;
// Verify that initially setting the value creates an event.
const AssignableTestProperty kTestProperty{1};
h.SetProperty(kAssignableKey, kTestProperty);
EXPECT_EQ(1, h.num_events());
// Verify that assigning by value sends an event.
h.SetProperty(kAssignableKey, AssignableTestProperty{2});
EXPECT_EQ(2, h.num_events());
// Same as the above case, but with a const reference instead of a move.
const AssignableTestProperty kTestProperty2{3};
h.SetProperty(kAssignableKey, kTestProperty2);
EXPECT_EQ(3, h.num_events());
// Verify that clearing the property creates an event.
h.ClearProperty(kAssignableKey);
EXPECT_EQ(4, h.num_events());
// Verify that setting a heap-allocated value also ticks the event counter.
h.SetProperty(kAssignableKey, std::make_unique<AssignableTestProperty>(4));
EXPECT_EQ(5, h.num_events());
// Verify that overwriting a heap-allocated value ticks the event counter.
h.SetProperty(kAssignableKey, std::make_unique<AssignableTestProperty>(5));
EXPECT_EQ(6, h.num_events());
}
TEST(PropertyTest, CascadingProperties) {
TestPropertyHandler h;
TestPropertyHandler h2(&h);
// Set the property on the parent handler.
h.SetProperty(kCascadingOwnedKey,
std::make_unique<TestCascadingProperty>(&h));
// Get the property value from the child handler.
auto* value = h2.GetProperty(kCascadingOwnedKey);
EXPECT_TRUE(value);
// The property value should have a reference to |h|.
EXPECT_EQ(&h, value->handler());
}
// TODO(kylixrd, pbos): Once all the call-sites are fixed to only use the
// unique_ptr version for owned properties, enable the following test to ensure
// that passing raw pointers for owned properties will, in fact, DCHECK.
TEST(PropertyTest, DISABLED_CheckedOwnedProperties) {
PropertyHandler h;
EXPECT_EQ(nullptr, h.GetProperty(kOwnedKey));
// The following SetProperty call should DCHECK if it's enabled. NOTE: This
// will leak the TestProperty!
EXPECT_DEATH_IF_SUPPORTED(h.SetProperty(kOwnedKey, new TestProperty()), "");
EXPECT_EQ(nullptr, h.GetProperty(kOwnedKey));
}
} // namespace test
} // namespace ui
|