1 // Copyright 2019 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "base/bind.h"
6 #include "base/strings/string16.h"
7 #include "base/strings/string_number_conversions.h"
8 #include "testing/gtest/include/gtest/gtest.h"
9 #include "testing/platform_test.h"
10 #include "ui/views/metadata/metadata_header_macros.h"
11 #include "ui/views/metadata/metadata_impl_macros.h"
12 #include "ui/views/metadata/metadata_types.h"
13 #include "ui/views/view.h"
14 
15 namespace VM = views::metadata;
16 
17 class MetadataTest : public PlatformTest {
18  public:
19   MetadataTest() = default;
20   ~MetadataTest() override = default;
21 
float_property_changed() const22   bool float_property_changed() const { return float_property_changed_; }
OnFloatPropertyChanged()23   void OnFloatPropertyChanged() { float_property_changed_ = true; }
24 
25  protected:
26   template <typename T>
GetMemberMetaData(T * obj,const std::string & member_name)27   VM::MemberMetaDataBase* GetMemberMetaData(T* obj,
28                                             const std::string& member_name) {
29     VM::ClassMetaData* meta_data = obj->GetClassMetaData();
30     if (meta_data == nullptr)
31       return nullptr;
32 
33     VM::MemberMetaDataBase* member_data =
34         meta_data->FindMemberData(member_name);
35     return member_data;
36   }
37 
38  private:
39   bool float_property_changed_ = false;
40 };
41 
42 // Base view in which a simple hierarchy is created for testing metadata
43 // iteration across class types.
44 class MetadataTestBaseView : public views::View {
45  public:
46   MetadataTestBaseView() = default;
47   ~MetadataTestBaseView() override = default;
48 
49   METADATA_HEADER(MetadataTestBaseView);
50 
SetIntProperty(int new_value)51   void SetIntProperty(int new_value) {
52     if (new_value == int_property_)
53       return;
54     int_property_ = new_value;
55     OnPropertyChanged(&int_property_, views::kPropertyEffectsNone);
56   }
GetIntProperty() const57   int GetIntProperty() const { return int_property_; }
AddIntPropertyChangedCallback(views::PropertyChangedCallback callback)58   views::PropertyChangedSubscription AddIntPropertyChangedCallback(
59       views::PropertyChangedCallback callback) WARN_UNUSED_RESULT {
60     return AddPropertyChangedCallback(&int_property_, std::move(callback));
61   }
62 
63  private:
64   int int_property_ = 0;
65 };
66 
67 BEGIN_METADATA(MetadataTestBaseView, views::View)
68 ADD_PROPERTY_METADATA(int, IntProperty)
69 END_METADATA
70 
71 // Descendent view in the simple hierarchy. The inherited properties are visible
72 // within the metadata.
73 class MetadataTestView : public MetadataTestBaseView {
74  public:
75   MetadataTestView() = default;
76   ~MetadataTestView() override = default;
77 
78   METADATA_HEADER(MetadataTestView);
79 
SetFloatProperty(float new_value)80   void SetFloatProperty(float new_value) {
81     if (float_property_ == new_value)
82       return;
83     float_property_ = new_value;
84     OnPropertyChanged(&float_property_, views::kPropertyEffectsNone);
85   }
GetFloatProperty() const86   float GetFloatProperty() const { return float_property_; }
AddFloatPropertyChangedCallback(views::PropertyChangedCallback callback)87   views::PropertyChangedSubscription AddFloatPropertyChangedCallback(
88       views::PropertyChangedCallback callback) WARN_UNUSED_RESULT {
89     return AddPropertyChangedCallback(&float_property_, std::move(callback));
90   }
91 
92  private:
93   float float_property_ = 0.f;
94 };
95 
BEGIN_METADATA(MetadataTestView,MetadataTestBaseView)96 BEGIN_METADATA(MetadataTestView, MetadataTestBaseView)
97 ADD_PROPERTY_METADATA(float, FloatProperty)
98 END_METADATA
99 
100 TEST_F(MetadataTest, TestFloatMetadataPropertyAccess) {
101   const float start_value = 12.34f;
102 
103   MetadataTestView test_obj;
104   test_obj.SetFloatProperty(start_value);
105 
106   VM::MemberMetaDataBase* member_data =
107       GetMemberMetaData(&test_obj, "FloatProperty");
108 
109   ASSERT_TRUE(member_data);
110   base::string16 member_value = member_data->GetValueAsString(&test_obj);
111   CHECK_EQ(member_value, base::NumberToString16(start_value));
112 }
113 
TEST_F(MetadataTest,TestFloatPropertyChangedCallback)114 TEST_F(MetadataTest, TestFloatPropertyChangedCallback) {
115   const float start_value = 12.34f;
116 
117   MetadataTestView test_obj;
118   views::PropertyChangedSubscription callback =
119       test_obj.AddFloatPropertyChangedCallback(base::BindRepeating(
120           &MetadataTest::OnFloatPropertyChanged, base::Unretained(this)));
121 
122   VM::MemberMetaDataBase* member_data =
123       GetMemberMetaData(&test_obj, "FloatProperty");
124 
125   ASSERT_TRUE(member_data);
126 
127   member_data->SetValueAsString(&test_obj, base::NumberToString16(start_value));
128 
129   CHECK(float_property_changed());
130 
131   base::string16 member_value = member_data->GetValueAsString(&test_obj);
132   CHECK_EQ(member_value, base::NumberToString16(start_value));
133 }
134 
TEST_F(MetadataTest,TestMetaDataParentClassTracking)135 TEST_F(MetadataTest, TestMetaDataParentClassTracking) {
136   VM::ClassMetaData* base_class_meta_data = MetadataTestBaseView::MetaData();
137   VM::ClassMetaData* derived_class_meta_data = MetadataTestView::MetaData();
138 
139   CHECK_EQ(base_class_meta_data,
140            derived_class_meta_data->parent_class_meta_data());
141 }
142 
TEST_F(MetadataTest,TestMetaDataFindParentClassMember)143 TEST_F(MetadataTest, TestMetaDataFindParentClassMember) {
144   VM::ClassMetaData* derived_class_meta_data = MetadataTestView::MetaData();
145 
146   VM::MemberMetaDataBase* member_data =
147       derived_class_meta_data->FindMemberData("IntProperty");
148 
149   CHECK_NE(member_data, nullptr);
150 }
151 
TEST_F(MetadataTest,TestMetaDataMemberIterator)152 TEST_F(MetadataTest, TestMetaDataMemberIterator) {
153   VM::ClassMetaData* derived_class_meta_data = MetadataTestView::MetaData();
154 
155   std::string derived_class_member_name = "IntProperty";
156   bool found_derived_class_member = false;
157 
158   std::string base_class_member_name = "IntProperty";
159   bool found_base_class_member = false;
160 
161   for (VM::MemberMetaDataBase* member_data : *derived_class_meta_data) {
162     if (member_data->member_name() == derived_class_member_name)
163       found_derived_class_member = true;
164 
165     if (member_data->member_name() == base_class_member_name)
166       found_base_class_member = true;
167   }
168 
169   CHECK(found_derived_class_member);
170   CHECK(found_base_class_member);
171 }
172 
TEST_F(MetadataTest,TestTypeCacheContainsTestClass)173 TEST_F(MetadataTest, TestTypeCacheContainsTestClass) {
174   VM::MetaDataCache* cache = VM::MetaDataCache::GetInstance();
175   ASSERT_TRUE(cache != nullptr);
176 
177   VM::ClassMetaData* test_class_meta = MetadataTestView::MetaData();
178 
179   const auto& cache_meta = cache->GetCachedTypes();
180   CHECK(std::find(cache_meta.begin(), cache_meta.end(), test_class_meta) !=
181         cache_meta.end());
182 }
183 
TEST_F(MetadataTest,TestMetaDataFile)184 TEST_F(MetadataTest, TestMetaDataFile) {
185   VM::ClassMetaData* metadata = MetadataTestBaseView::MetaData();
186 
187   CHECK_EQ(metadata->file(), "ui/views/metadata/metadata_unittest.cc");
188 }
189