diff options
Diffstat (limited to 'components/policy/core/common/schema_registry_unittest.cc')
-rw-r--r-- | components/policy/core/common/schema_registry_unittest.cc | 329 |
1 files changed, 329 insertions, 0 deletions
diff --git a/components/policy/core/common/schema_registry_unittest.cc b/components/policy/core/common/schema_registry_unittest.cc new file mode 100644 index 0000000000..aea54d1cc3 --- /dev/null +++ b/components/policy/core/common/schema_registry_unittest.cc @@ -0,0 +1,329 @@ +// Copyright 2013 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 "components/policy/core/common/schema_registry.h" + +#include <memory> + +#include "components/policy/core/common/policy_namespace.h" +#include "components/policy/core/common/schema.h" +#include "extensions/buildflags/buildflags.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +using ::testing::Mock; +using ::testing::_; + +namespace policy { + +namespace { + +const char kTestSchema[] = + "{" + " \"type\": \"object\"," + " \"properties\": {" + " \"string\": { \"type\": \"string\" }," + " \"integer\": { \"type\": \"integer\" }," + " \"boolean\": { \"type\": \"boolean\" }," + " \"null\": { \"type\": \"null\" }," + " \"double\": { \"type\": \"number\" }," + " \"list\": {" + " \"type\": \"array\"," + " \"items\": { \"type\": \"string\" }" + " }," + " \"object\": {" + " \"type\": \"object\"," + " \"properties\": {" + " \"a\": { \"type\": \"string\" }," + " \"b\": { \"type\": \"integer\" }" + " }" + " }" + " }" + "}"; + +class MockSchemaRegistryObserver : public SchemaRegistry::Observer { + public: + MockSchemaRegistryObserver() {} + ~MockSchemaRegistryObserver() override {} + + MOCK_METHOD1(OnSchemaRegistryUpdated, void(bool)); + MOCK_METHOD0(OnSchemaRegistryReady, void()); +}; + +bool SchemaMapEquals(const scoped_refptr<SchemaMap>& schema_map1, + const scoped_refptr<SchemaMap>& schema_map2) { + PolicyNamespaceList added; + PolicyNamespaceList removed; + schema_map1->GetChanges(schema_map2, &removed, &added); + return added.empty() && removed.empty(); +} + +} // namespace + +TEST(SchemaRegistryTest, Notifications) { + std::string error; + Schema schema = Schema::Parse(kTestSchema, &error); + ASSERT_TRUE(schema.valid()) << error; + + MockSchemaRegistryObserver observer; + SchemaRegistry registry; + registry.AddObserver(&observer); + + ASSERT_TRUE(registry.schema_map().get()); + EXPECT_FALSE(registry.schema_map()->GetSchema( + PolicyNamespace(POLICY_DOMAIN_EXTENSIONS, "abc"))); + + EXPECT_CALL(observer, OnSchemaRegistryUpdated(true)); + registry.RegisterComponent(PolicyNamespace(POLICY_DOMAIN_EXTENSIONS, "abc"), + schema); + Mock::VerifyAndClearExpectations(&observer); + + // Re-register also triggers notifications, because the Schema might have + // changed. + EXPECT_CALL(observer, OnSchemaRegistryUpdated(true)); + registry.RegisterComponent(PolicyNamespace(POLICY_DOMAIN_EXTENSIONS, "abc"), + schema); + Mock::VerifyAndClearExpectations(&observer); + + EXPECT_TRUE(registry.schema_map()->GetSchema( + PolicyNamespace(POLICY_DOMAIN_EXTENSIONS, "abc"))); + + EXPECT_CALL(observer, OnSchemaRegistryUpdated(false)); + registry.UnregisterComponent( + PolicyNamespace(POLICY_DOMAIN_EXTENSIONS, "abc")); + Mock::VerifyAndClearExpectations(&observer); + + EXPECT_FALSE(registry.schema_map()->GetSchema( + PolicyNamespace(POLICY_DOMAIN_EXTENSIONS, "abc"))); + + // Registering multiple components at once issues only one notification. + ComponentMap components; + components["abc"] = schema; + components["def"] = schema; + components["xyz"] = schema; + EXPECT_CALL(observer, OnSchemaRegistryUpdated(true)); + registry.RegisterComponents(POLICY_DOMAIN_EXTENSIONS, components); + Mock::VerifyAndClearExpectations(&observer); + + registry.RemoveObserver(&observer); +} + +TEST(SchemaRegistryTest, IsReady) { + SchemaRegistry registry; + MockSchemaRegistryObserver observer; + registry.AddObserver(&observer); + + EXPECT_FALSE(registry.IsReady()); +#if BUILDFLAG(ENABLE_EXTENSIONS) + EXPECT_CALL(observer, OnSchemaRegistryReady()).Times(0); + registry.SetExtensionsDomainsReady(); + Mock::VerifyAndClearExpectations(&observer); + EXPECT_FALSE(registry.IsReady()); +#endif + EXPECT_CALL(observer, OnSchemaRegistryReady()); + registry.SetDomainReady(POLICY_DOMAIN_CHROME); + Mock::VerifyAndClearExpectations(&observer); + EXPECT_TRUE(registry.IsReady()); + EXPECT_CALL(observer, OnSchemaRegistryReady()).Times(0); + registry.SetDomainReady(POLICY_DOMAIN_CHROME); + Mock::VerifyAndClearExpectations(&observer); + EXPECT_TRUE(registry.IsReady()); + + CombinedSchemaRegistry combined; + EXPECT_TRUE(combined.IsReady()); + + registry.RemoveObserver(&observer); +} + +TEST(SchemaRegistryTest, Combined) { + std::string error; + Schema schema = Schema::Parse(kTestSchema, &error); + ASSERT_TRUE(schema.valid()) << error; + + MockSchemaRegistryObserver observer; + std::unique_ptr<SchemaRegistry> registry1(new SchemaRegistry); + std::unique_ptr<SchemaRegistry> registry2(new SchemaRegistry); + CombinedSchemaRegistry combined; + combined.AddObserver(&observer); + + EXPECT_CALL(observer, OnSchemaRegistryUpdated(_)).Times(0); + registry1->RegisterComponent(PolicyNamespace(POLICY_DOMAIN_EXTENSIONS, "abc"), + schema); + Mock::VerifyAndClearExpectations(&observer); + + // Starting to track a registry issues notifications when it comes with new + // schemas. + EXPECT_CALL(observer, OnSchemaRegistryUpdated(true)); + combined.Track(registry1.get()); + Mock::VerifyAndClearExpectations(&observer); + + // Adding a new empty registry does not trigger notifications. + EXPECT_CALL(observer, OnSchemaRegistryUpdated(_)).Times(0); + combined.Track(registry2.get()); + Mock::VerifyAndClearExpectations(&observer); + + // Adding the same component to the combined registry itself triggers + // notifications. + EXPECT_CALL(observer, OnSchemaRegistryUpdated(true)); + combined.RegisterComponent(PolicyNamespace(POLICY_DOMAIN_EXTENSIONS, "abc"), + schema); + Mock::VerifyAndClearExpectations(&observer); + + // Adding components to the sub-registries triggers notifications. + EXPECT_CALL(observer, OnSchemaRegistryUpdated(true)); + registry2->RegisterComponent(PolicyNamespace(POLICY_DOMAIN_EXTENSIONS, "def"), + schema); + Mock::VerifyAndClearExpectations(&observer); + + // If the same component is published in 2 sub-registries then the combined + // registry publishes one of them. + EXPECT_CALL(observer, OnSchemaRegistryUpdated(true)); + registry1->RegisterComponent(PolicyNamespace(POLICY_DOMAIN_EXTENSIONS, "def"), + schema); + Mock::VerifyAndClearExpectations(&observer); + + ASSERT_EQ(1u, combined.schema_map()->GetDomains().size()); + ASSERT_TRUE(combined.schema_map()->GetComponents(POLICY_DOMAIN_EXTENSIONS)); + ASSERT_EQ( + 2u, + combined.schema_map()->GetComponents(POLICY_DOMAIN_EXTENSIONS)->size()); + EXPECT_TRUE(combined.schema_map()->GetSchema( + PolicyNamespace(POLICY_DOMAIN_EXTENSIONS, "abc"))); + EXPECT_TRUE(combined.schema_map()->GetSchema( + PolicyNamespace(POLICY_DOMAIN_EXTENSIONS, "def"))); + EXPECT_FALSE(combined.schema_map()->GetSchema( + PolicyNamespace(POLICY_DOMAIN_EXTENSIONS, "xyz"))); + + EXPECT_CALL(observer, OnSchemaRegistryUpdated(false)); + registry1->UnregisterComponent( + PolicyNamespace(POLICY_DOMAIN_EXTENSIONS, "abc")); + Mock::VerifyAndClearExpectations(&observer); + // Still registered at the combined registry. + EXPECT_TRUE(combined.schema_map()->GetSchema( + PolicyNamespace(POLICY_DOMAIN_EXTENSIONS, "abc"))); + + EXPECT_CALL(observer, OnSchemaRegistryUpdated(false)); + combined.UnregisterComponent( + PolicyNamespace(POLICY_DOMAIN_EXTENSIONS, "abc")); + Mock::VerifyAndClearExpectations(&observer); + // Now it's gone. + EXPECT_FALSE(combined.schema_map()->GetSchema( + PolicyNamespace(POLICY_DOMAIN_EXTENSIONS, "abc"))); + + EXPECT_CALL(observer, OnSchemaRegistryUpdated(false)); + registry1->UnregisterComponent( + PolicyNamespace(POLICY_DOMAIN_EXTENSIONS, "def")); + Mock::VerifyAndClearExpectations(&observer); + // Still registered at registry2. + EXPECT_TRUE(combined.schema_map()->GetSchema( + PolicyNamespace(POLICY_DOMAIN_EXTENSIONS, "def"))); + + EXPECT_CALL(observer, OnSchemaRegistryUpdated(false)); + registry2->UnregisterComponent( + PolicyNamespace(POLICY_DOMAIN_EXTENSIONS, "def")); + Mock::VerifyAndClearExpectations(&observer); + // Now it's gone. + EXPECT_FALSE(combined.schema_map()->GetSchema( + PolicyNamespace(POLICY_DOMAIN_EXTENSIONS, "def"))); + + EXPECT_CALL(observer, OnSchemaRegistryUpdated(true)).Times(2); + registry1->RegisterComponent(PolicyNamespace(POLICY_DOMAIN_CHROME, ""), + schema); + registry2->RegisterComponent(PolicyNamespace(POLICY_DOMAIN_EXTENSIONS, "hij"), + schema); + Mock::VerifyAndClearExpectations(&observer); + + // Untracking |registry1| doesn't trigger an update notification, because it + // doesn't contain any components. + EXPECT_CALL(observer, OnSchemaRegistryUpdated(_)).Times(0); + registry1.reset(); + Mock::VerifyAndClearExpectations(&observer); + + EXPECT_CALL(observer, OnSchemaRegistryUpdated(false)); + registry2.reset(); + Mock::VerifyAndClearExpectations(&observer); + + combined.RemoveObserver(&observer); +} + +TEST(SchemaRegistryTest, ForwardingSchemaRegistry) { + std::unique_ptr<SchemaRegistry> registry(new SchemaRegistry); + ForwardingSchemaRegistry forwarding(registry.get()); + MockSchemaRegistryObserver observer; + forwarding.AddObserver(&observer); + + EXPECT_FALSE(registry->IsReady()); + EXPECT_FALSE(forwarding.IsReady()); + // They always have the same SchemaMap. + EXPECT_TRUE(SchemaMapEquals(registry->schema_map(), forwarding.schema_map())); + + EXPECT_CALL(observer, OnSchemaRegistryUpdated(true)); + registry->RegisterComponent(PolicyNamespace(POLICY_DOMAIN_EXTENSIONS, "abc"), + Schema()); + Mock::VerifyAndClearExpectations(&observer); + EXPECT_TRUE(SchemaMapEquals(registry->schema_map(), forwarding.schema_map())); + + EXPECT_CALL(observer, OnSchemaRegistryUpdated(false)); + registry->UnregisterComponent( + PolicyNamespace(POLICY_DOMAIN_EXTENSIONS, "abc")); + Mock::VerifyAndClearExpectations(&observer); + EXPECT_TRUE(SchemaMapEquals(registry->schema_map(), forwarding.schema_map())); + + // No notifications expected for these calls. + EXPECT_FALSE(registry->IsReady()); + EXPECT_FALSE(forwarding.IsReady()); + + registry->SetExtensionsDomainsReady(); + EXPECT_FALSE(registry->IsReady()); + EXPECT_FALSE(forwarding.IsReady()); + + EXPECT_CALL(observer, OnSchemaRegistryReady()); + registry->SetDomainReady(POLICY_DOMAIN_CHROME); + EXPECT_TRUE(registry->IsReady()); + EXPECT_TRUE(forwarding.IsReady()); + Mock::VerifyAndClearExpectations(&observer); + + EXPECT_TRUE(SchemaMapEquals(registry->schema_map(), forwarding.schema_map())); + Mock::VerifyAndClearExpectations(&observer); + + forwarding.SetExtensionsDomainsReady(); + forwarding.SetDomainReady(POLICY_DOMAIN_CHROME); + EXPECT_TRUE(forwarding.IsReady()); + + // Keep the same SchemaMap when the original registry is gone. + // No notifications are expected in this case either. + scoped_refptr<SchemaMap> schema_map = registry->schema_map(); + registry.reset(); + EXPECT_TRUE(SchemaMapEquals(schema_map, forwarding.schema_map())); + Mock::VerifyAndClearExpectations(&observer); + + forwarding.RemoveObserver(&observer); +} + +TEST(SchemaRegistryTest, ForwardingSchemaRegistryReadiness) { + std::unique_ptr<SchemaRegistry> registry(new SchemaRegistry); + + ForwardingSchemaRegistry forwarding_1(registry.get()); + EXPECT_FALSE(registry->IsReady()); + EXPECT_FALSE(forwarding_1.IsReady()); + + // Once the wrapped registry gets ready, the forwarding schema registry + // becomes ready too. + registry->SetAllDomainsReady(); + EXPECT_TRUE(registry->IsReady()); + EXPECT_TRUE(forwarding_1.IsReady()); + + // The wrapped registry was ready at the time when the forwarding registry was + // constructed, so the forwarding registry is immediately ready too. + ForwardingSchemaRegistry forwarding_2(registry.get()); + EXPECT_TRUE(forwarding_2.IsReady()); + + // Destruction of the wrapped registry doesn't change the readiness of the + // forwarding registry. + registry.reset(); + EXPECT_TRUE(forwarding_1.IsReady()); + EXPECT_TRUE(forwarding_2.IsReady()); +} + +} // namespace policy |