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