1// Copyright 2013 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 "components/policy/core/common/schema_registry.h"
6
7#include "base/memory/scoped_ptr.h"
8#include "components/policy/core/common/policy_namespace.h"
9#include "components/policy/core/common/schema.h"
10#include "testing/gmock/include/gmock/gmock.h"
11#include "testing/gtest/include/gtest/gtest.h"
12
13using ::testing::Mock;
14using ::testing::_;
15
16namespace policy {
17
18namespace {
19
20const char kTestSchema[] =
21    "{"
22    "  \"type\": \"object\","
23    "  \"properties\": {"
24    "    \"string\": { \"type\": \"string\" },"
25    "    \"integer\": { \"type\": \"integer\" },"
26    "    \"boolean\": { \"type\": \"boolean\" },"
27    "    \"null\": { \"type\": \"null\" },"
28    "    \"double\": { \"type\": \"number\" },"
29    "    \"list\": {"
30    "      \"type\": \"array\","
31    "      \"items\": { \"type\": \"string\" }"
32    "    },"
33    "    \"object\": {"
34    "      \"type\": \"object\","
35    "      \"properties\": {"
36    "        \"a\": { \"type\": \"string\" },"
37    "        \"b\": { \"type\": \"integer\" }"
38    "      }"
39    "    }"
40    "  }"
41    "}";
42
43class MockSchemaRegistryObserver : public SchemaRegistry::Observer {
44 public:
45  MockSchemaRegistryObserver() {}
46  virtual ~MockSchemaRegistryObserver() {}
47
48  MOCK_METHOD1(OnSchemaRegistryUpdated, void(bool));
49  MOCK_METHOD0(OnSchemaRegistryReady, void());
50};
51
52bool SchemaMapEquals(const scoped_refptr<SchemaMap>& schema_map1,
53                     const scoped_refptr<SchemaMap>& schema_map2) {
54  PolicyNamespaceList added;
55  PolicyNamespaceList removed;
56  schema_map1->GetChanges(schema_map2, &removed, &added);
57  return added.empty() && removed.empty();
58}
59
60}  // namespace
61
62TEST(SchemaRegistryTest, Notifications) {
63  std::string error;
64  Schema schema = Schema::Parse(kTestSchema, &error);
65  ASSERT_TRUE(schema.valid()) << error;
66
67  MockSchemaRegistryObserver observer;
68  SchemaRegistry registry;
69  registry.AddObserver(&observer);
70
71  ASSERT_TRUE(registry.schema_map().get());
72  EXPECT_FALSE(registry.schema_map()->GetSchema(
73      PolicyNamespace(POLICY_DOMAIN_EXTENSIONS, "abc")));
74
75  EXPECT_CALL(observer, OnSchemaRegistryUpdated(true));
76  registry.RegisterComponent(PolicyNamespace(POLICY_DOMAIN_EXTENSIONS, "abc"),
77                             schema);
78  Mock::VerifyAndClearExpectations(&observer);
79
80  // Re-register also triggers notifications, because the Schema might have
81  // changed.
82  EXPECT_CALL(observer, OnSchemaRegistryUpdated(true));
83  registry.RegisterComponent(PolicyNamespace(POLICY_DOMAIN_EXTENSIONS, "abc"),
84                             schema);
85  Mock::VerifyAndClearExpectations(&observer);
86
87  EXPECT_TRUE(registry.schema_map()->GetSchema(
88      PolicyNamespace(POLICY_DOMAIN_EXTENSIONS, "abc")));
89
90  EXPECT_CALL(observer, OnSchemaRegistryUpdated(false));
91  registry.UnregisterComponent(
92      PolicyNamespace(POLICY_DOMAIN_EXTENSIONS, "abc"));
93  Mock::VerifyAndClearExpectations(&observer);
94
95  EXPECT_FALSE(registry.schema_map()->GetSchema(
96      PolicyNamespace(POLICY_DOMAIN_EXTENSIONS, "abc")));
97
98  // Registering multiple components at once issues only one notification.
99  ComponentMap components;
100  components["abc"] = schema;
101  components["def"] = schema;
102  components["xyz"] = schema;
103  EXPECT_CALL(observer, OnSchemaRegistryUpdated(true));
104  registry.RegisterComponents(POLICY_DOMAIN_EXTENSIONS, components);
105  Mock::VerifyAndClearExpectations(&observer);
106
107  registry.RemoveObserver(&observer);
108}
109
110TEST(SchemaRegistryTest, IsReady) {
111  SchemaRegistry registry;
112  MockSchemaRegistryObserver observer;
113  registry.AddObserver(&observer);
114
115  EXPECT_FALSE(registry.IsReady());
116#if defined(ENABLE_EXTENSIONS)
117  EXPECT_CALL(observer, OnSchemaRegistryReady()).Times(0);
118  registry.SetReady(POLICY_DOMAIN_EXTENSIONS);
119  Mock::VerifyAndClearExpectations(&observer);
120  EXPECT_FALSE(registry.IsReady());
121#endif
122  EXPECT_CALL(observer, OnSchemaRegistryReady());
123  registry.SetReady(POLICY_DOMAIN_CHROME);
124  Mock::VerifyAndClearExpectations(&observer);
125  EXPECT_TRUE(registry.IsReady());
126  EXPECT_CALL(observer, OnSchemaRegistryReady()).Times(0);
127  registry.SetReady(POLICY_DOMAIN_CHROME);
128  Mock::VerifyAndClearExpectations(&observer);
129  EXPECT_TRUE(registry.IsReady());
130
131  CombinedSchemaRegistry combined;
132  EXPECT_TRUE(combined.IsReady());
133
134  registry.RemoveObserver(&observer);
135}
136
137TEST(SchemaRegistryTest, Combined) {
138  std::string error;
139  Schema schema = Schema::Parse(kTestSchema, &error);
140  ASSERT_TRUE(schema.valid()) << error;
141
142  MockSchemaRegistryObserver observer;
143  scoped_ptr<SchemaRegistry> registry1(new SchemaRegistry);
144  scoped_ptr<SchemaRegistry> registry2(new SchemaRegistry);
145  CombinedSchemaRegistry combined;
146  combined.AddObserver(&observer);
147
148  EXPECT_CALL(observer, OnSchemaRegistryUpdated(_)).Times(0);
149  registry1->RegisterComponent(PolicyNamespace(POLICY_DOMAIN_EXTENSIONS, "abc"),
150                               schema);
151  Mock::VerifyAndClearExpectations(&observer);
152
153  // Starting to track a registry issues notifications when it comes with new
154  // schemas.
155  EXPECT_CALL(observer, OnSchemaRegistryUpdated(true));
156  combined.Track(registry1.get());
157  Mock::VerifyAndClearExpectations(&observer);
158
159  // Adding a new empty registry does not trigger notifications.
160  EXPECT_CALL(observer, OnSchemaRegistryUpdated(_)).Times(0);
161  combined.Track(registry2.get());
162  Mock::VerifyAndClearExpectations(&observer);
163
164  // Adding the same component to the combined registry itself triggers
165  // notifications.
166  EXPECT_CALL(observer, OnSchemaRegistryUpdated(true));
167  combined.RegisterComponent(PolicyNamespace(POLICY_DOMAIN_EXTENSIONS, "abc"),
168                             schema);
169  Mock::VerifyAndClearExpectations(&observer);
170
171  // Adding components to the sub-registries triggers notifications.
172  EXPECT_CALL(observer, OnSchemaRegistryUpdated(true));
173  registry2->RegisterComponent(PolicyNamespace(POLICY_DOMAIN_EXTENSIONS, "def"),
174                               schema);
175  Mock::VerifyAndClearExpectations(&observer);
176
177  // If the same component is published in 2 sub-registries then the combined
178  // registry publishes one of them.
179  EXPECT_CALL(observer, OnSchemaRegistryUpdated(true));
180  registry1->RegisterComponent(PolicyNamespace(POLICY_DOMAIN_EXTENSIONS, "def"),
181                               schema);
182  Mock::VerifyAndClearExpectations(&observer);
183
184  ASSERT_EQ(1u, combined.schema_map()->GetDomains().size());
185  ASSERT_TRUE(combined.schema_map()->GetComponents(POLICY_DOMAIN_EXTENSIONS));
186  ASSERT_EQ(
187      2u,
188      combined.schema_map()->GetComponents(POLICY_DOMAIN_EXTENSIONS)->size());
189  EXPECT_TRUE(combined.schema_map()->GetSchema(
190      PolicyNamespace(POLICY_DOMAIN_EXTENSIONS, "abc")));
191  EXPECT_TRUE(combined.schema_map()->GetSchema(
192      PolicyNamespace(POLICY_DOMAIN_EXTENSIONS, "def")));
193  EXPECT_FALSE(combined.schema_map()->GetSchema(
194      PolicyNamespace(POLICY_DOMAIN_EXTENSIONS, "xyz")));
195
196  EXPECT_CALL(observer, OnSchemaRegistryUpdated(false));
197  registry1->UnregisterComponent(
198      PolicyNamespace(POLICY_DOMAIN_EXTENSIONS, "abc"));
199  Mock::VerifyAndClearExpectations(&observer);
200  // Still registered at the combined registry.
201  EXPECT_TRUE(combined.schema_map()->GetSchema(
202      PolicyNamespace(POLICY_DOMAIN_EXTENSIONS, "abc")));
203
204  EXPECT_CALL(observer, OnSchemaRegistryUpdated(false));
205  combined.UnregisterComponent(
206      PolicyNamespace(POLICY_DOMAIN_EXTENSIONS, "abc"));
207  Mock::VerifyAndClearExpectations(&observer);
208  // Now it's gone.
209  EXPECT_FALSE(combined.schema_map()->GetSchema(
210      PolicyNamespace(POLICY_DOMAIN_EXTENSIONS, "abc")));
211
212  EXPECT_CALL(observer, OnSchemaRegistryUpdated(false));
213  registry1->UnregisterComponent(
214      PolicyNamespace(POLICY_DOMAIN_EXTENSIONS, "def"));
215  Mock::VerifyAndClearExpectations(&observer);
216  // Still registered at registry2.
217  EXPECT_TRUE(combined.schema_map()->GetSchema(
218      PolicyNamespace(POLICY_DOMAIN_EXTENSIONS, "def")));
219
220  EXPECT_CALL(observer, OnSchemaRegistryUpdated(false));
221  registry2->UnregisterComponent(
222      PolicyNamespace(POLICY_DOMAIN_EXTENSIONS, "def"));
223  Mock::VerifyAndClearExpectations(&observer);
224  // Now it's gone.
225  EXPECT_FALSE(combined.schema_map()->GetSchema(
226      PolicyNamespace(POLICY_DOMAIN_EXTENSIONS, "def")));
227
228  EXPECT_CALL(observer, OnSchemaRegistryUpdated(true)).Times(2);
229  registry1->RegisterComponent(PolicyNamespace(POLICY_DOMAIN_CHROME, ""),
230                               schema);
231  registry2->RegisterComponent(PolicyNamespace(POLICY_DOMAIN_EXTENSIONS, "hij"),
232                               schema);
233  Mock::VerifyAndClearExpectations(&observer);
234
235  // Untracking |registry1| doesn't trigger an update notification, because it
236  // doesn't contain any components.
237  EXPECT_CALL(observer, OnSchemaRegistryUpdated(_)).Times(0);
238  registry1.reset();
239  Mock::VerifyAndClearExpectations(&observer);
240
241  EXPECT_CALL(observer, OnSchemaRegistryUpdated(false));
242  registry2.reset();
243  Mock::VerifyAndClearExpectations(&observer);
244
245  combined.RemoveObserver(&observer);
246}
247
248TEST(SchemaRegistryTest, ForwardingSchemaRegistry) {
249  scoped_ptr<SchemaRegistry> registry(new SchemaRegistry);
250  ForwardingSchemaRegistry forwarding(registry.get());
251  MockSchemaRegistryObserver observer;
252  forwarding.AddObserver(&observer);
253
254  EXPECT_FALSE(registry->IsReady());
255  EXPECT_FALSE(forwarding.IsReady());
256  // They always have the same SchemaMap.
257  EXPECT_TRUE(SchemaMapEquals(registry->schema_map(), forwarding.schema_map()));
258
259  EXPECT_CALL(observer, OnSchemaRegistryUpdated(true));
260  registry->RegisterComponent(PolicyNamespace(POLICY_DOMAIN_EXTENSIONS, "abc"),
261                              Schema());
262  Mock::VerifyAndClearExpectations(&observer);
263  EXPECT_TRUE(SchemaMapEquals(registry->schema_map(), forwarding.schema_map()));
264
265  EXPECT_CALL(observer, OnSchemaRegistryUpdated(false));
266  registry->UnregisterComponent(
267      PolicyNamespace(POLICY_DOMAIN_EXTENSIONS, "abc"));
268  Mock::VerifyAndClearExpectations(&observer);
269  EXPECT_TRUE(SchemaMapEquals(registry->schema_map(), forwarding.schema_map()));
270
271  // No notifications expected for these calls.
272  EXPECT_FALSE(registry->IsReady());
273  EXPECT_FALSE(forwarding.IsReady());
274
275  registry->SetReady(POLICY_DOMAIN_EXTENSIONS);
276  EXPECT_FALSE(registry->IsReady());
277  EXPECT_FALSE(forwarding.IsReady());
278
279  registry->SetReady(POLICY_DOMAIN_CHROME);
280  EXPECT_TRUE(registry->IsReady());
281  // The ForwardingSchemaRegistry becomes ready independently of the wrapped
282  // registry.
283  EXPECT_FALSE(forwarding.IsReady());
284
285  EXPECT_TRUE(SchemaMapEquals(registry->schema_map(), forwarding.schema_map()));
286  Mock::VerifyAndClearExpectations(&observer);
287
288  forwarding.SetReady(POLICY_DOMAIN_EXTENSIONS);
289  EXPECT_FALSE(forwarding.IsReady());
290  Mock::VerifyAndClearExpectations(&observer);
291
292  EXPECT_CALL(observer, OnSchemaRegistryReady());
293  forwarding.SetReady(POLICY_DOMAIN_CHROME);
294  EXPECT_TRUE(forwarding.IsReady());
295  Mock::VerifyAndClearExpectations(&observer);
296
297  // Keep the same SchemaMap when the original registry is gone.
298  // No notifications are expected in this case either.
299  scoped_refptr<SchemaMap> schema_map = registry->schema_map();
300  registry.reset();
301  EXPECT_TRUE(SchemaMapEquals(schema_map, forwarding.schema_map()));
302  Mock::VerifyAndClearExpectations(&observer);
303
304  forwarding.RemoveObserver(&observer);
305}
306
307}  // namespace policy
308