// Copyright (c) 2016-present, Facebook, Inc. All rights reserved. // This source code is licensed under both the GPLv2 (found in the // COPYING file in the root directory) and Apache 2.0 License // (found in the LICENSE.Apache file in the root directory). #ifndef ROCKSDB_LITE #include "rocksdb/utilities/object_registry.h" #include "rocksdb/convenience.h" #include "rocksdb/customizable.h" #include "test_util/testharness.h" namespace ROCKSDB_NAMESPACE { class ObjRegistryTest : public testing::Test { public: static int num_a, num_b; }; int ObjRegistryTest::num_a = 0; int ObjRegistryTest::num_b = 0; static FactoryFunc test_reg_a = ObjectLibrary::Default()->AddFactory( ObjectLibrary::PatternEntry("a", false).AddSeparator("://"), [](const std::string& /*uri*/, std::unique_ptr* /*env_guard*/, std::string* /* errmsg */) { ++ObjRegistryTest::num_a; return Env::Default(); }); class WrappedEnv : public EnvWrapper { private: std::string id_; public: WrappedEnv(Env* t, const std::string& id) : EnvWrapper(t), id_(id) {} const char* Name() const override { return id_.c_str(); } std::string GetId() const override { return id_; } }; static FactoryFunc test_reg_b = ObjectLibrary::Default()->AddFactory( ObjectLibrary::PatternEntry("b", false).AddSeparator("://"), [](const std::string& uri, std::unique_ptr* env_guard, std::string* /* errmsg */) { ++ObjRegistryTest::num_b; // Env::Default() is a singleton so we can't grant ownership directly to // the caller - we must wrap it first. env_guard->reset(new WrappedEnv(Env::Default(), uri)); return env_guard->get(); }); TEST_F(ObjRegistryTest, Basics) { std::string msg; std::unique_ptr guard; Env* a_env = nullptr; auto registry = ObjectRegistry::NewInstance(); ASSERT_NOK(registry->NewStaticObject("c://test", &a_env)); ASSERT_NOK(registry->NewUniqueObject("c://test", &guard)); ASSERT_EQ(a_env, nullptr); ASSERT_EQ(guard, nullptr); ASSERT_EQ(0, num_a); ASSERT_EQ(0, num_b); ASSERT_OK(registry->NewStaticObject("a://test", &a_env)); ASSERT_NE(a_env, nullptr); ASSERT_EQ(1, num_a); ASSERT_EQ(0, num_b); ASSERT_OK(registry->NewUniqueObject("b://test", &guard)); ASSERT_NE(guard, nullptr); ASSERT_EQ(1, num_a); ASSERT_EQ(1, num_b); Env* b_env = nullptr; ASSERT_NOK(registry->NewStaticObject("b://test", &b_env)); ASSERT_EQ(b_env, nullptr); ASSERT_EQ(1, num_a); ASSERT_EQ(2, num_b); // Created but rejected as not static b_env = a_env; ASSERT_NOK(registry->NewStaticObject("b://test", &b_env)); ASSERT_EQ(b_env, a_env); ASSERT_EQ(1, num_a); ASSERT_EQ(3, num_b); b_env = guard.get(); ASSERT_NOK(registry->NewUniqueObject("a://test", &guard)); ASSERT_EQ(guard.get(), b_env); // Unchanged ASSERT_EQ(2, num_a); // Created one but rejected it as not unique ASSERT_EQ(3, num_b); } TEST_F(ObjRegistryTest, LocalRegistry) { Env* env = nullptr; auto registry = ObjectRegistry::NewInstance(); std::shared_ptr library = std::make_shared("local"); registry->AddLibrary(library); library->AddFactory( "test-local", [](const std::string& /*uri*/, std::unique_ptr* /*guard */, std::string* /* errmsg */) { return Env::Default(); }); ObjectLibrary::Default()->AddFactory( "test-global", [](const std::string& /*uri*/, std::unique_ptr* /*guard */, std::string* /* errmsg */) { return Env::Default(); }); ASSERT_NOK( ObjectRegistry::NewInstance()->NewStaticObject("test-local", &env)); ASSERT_EQ(env, nullptr); ASSERT_OK( ObjectRegistry::NewInstance()->NewStaticObject("test-global", &env)); ASSERT_NE(env, nullptr); ASSERT_OK(registry->NewStaticObject("test-local", &env)); ASSERT_NE(env, nullptr); ASSERT_OK(registry->NewStaticObject("test-global", &env)); ASSERT_NE(env, nullptr); } static int RegisterTestUnguarded(ObjectLibrary& library, const std::string& /*arg*/) { library.AddFactory( "unguarded", [](const std::string& /*uri*/, std::unique_ptr* /*guard */, std::string* /* errmsg */) { return Env::Default(); }); library.AddFactory( "guarded", [](const std::string& uri, std::unique_ptr* guard, std::string* /* errmsg */) { guard->reset(new WrappedEnv(Env::Default(), uri)); return guard->get(); }); return 2; } TEST_F(ObjRegistryTest, CheckShared) { std::shared_ptr shared; std::shared_ptr registry = ObjectRegistry::NewInstance(); registry->AddLibrary("shared", RegisterTestUnguarded, ""); ASSERT_OK(registry->NewSharedObject("guarded", &shared)); ASSERT_NE(shared, nullptr); shared.reset(); ASSERT_NOK(registry->NewSharedObject("unguarded", &shared)); ASSERT_EQ(shared, nullptr); } TEST_F(ObjRegistryTest, CheckStatic) { Env* env = nullptr; std::shared_ptr registry = ObjectRegistry::NewInstance(); registry->AddLibrary("static", RegisterTestUnguarded, ""); ASSERT_NOK(registry->NewStaticObject("guarded", &env)); ASSERT_EQ(env, nullptr); env = nullptr; ASSERT_OK(registry->NewStaticObject("unguarded", &env)); ASSERT_NE(env, nullptr); } TEST_F(ObjRegistryTest, CheckUnique) { std::unique_ptr unique; std::shared_ptr registry = ObjectRegistry::NewInstance(); registry->AddLibrary("unique", RegisterTestUnguarded, ""); ASSERT_OK(registry->NewUniqueObject("guarded", &unique)); ASSERT_NE(unique, nullptr); unique.reset(); ASSERT_NOK(registry->NewUniqueObject("unguarded", &unique)); ASSERT_EQ(unique, nullptr); } TEST_F(ObjRegistryTest, FailingFactory) { std::shared_ptr registry = ObjectRegistry::NewInstance(); std::shared_ptr library = std::make_shared("failing"); registry->AddLibrary(library); library->AddFactory( "failing", [](const std::string& /*uri*/, std::unique_ptr* /*guard */, std::string* errmsg) { *errmsg = "Bad Factory"; return nullptr; }); std::unique_ptr unique; std::shared_ptr shared; Env* pointer = nullptr; Status s; s = registry->NewUniqueObject("failing", &unique); ASSERT_TRUE(s.IsInvalidArgument()); s = registry->NewSharedObject("failing", &shared); ASSERT_TRUE(s.IsInvalidArgument()); s = registry->NewStaticObject("failing", &pointer); ASSERT_TRUE(s.IsInvalidArgument()); s = registry->NewUniqueObject("missing", &unique); ASSERT_TRUE(s.IsNotSupported()); s = registry->NewSharedObject("missing", &shared); ASSERT_TRUE(s.IsNotSupported()); s = registry->NewStaticObject("missing", &pointer); ASSERT_TRUE(s.IsNotSupported()); } TEST_F(ObjRegistryTest, TestRegistryParents) { auto grand = ObjectRegistry::Default(); auto parent = ObjectRegistry::NewInstance(); // parent with a grandparent auto uncle = ObjectRegistry::NewInstance(grand); auto child = ObjectRegistry::NewInstance(parent); auto cousin = ObjectRegistry::NewInstance(uncle); auto library = parent->AddLibrary("parent"); library->AddFactory( "parent", [](const std::string& uri, std::unique_ptr* guard, std::string* /* errmsg */) { guard->reset(new WrappedEnv(Env::Default(), uri)); return guard->get(); }); library = cousin->AddLibrary("cousin"); library->AddFactory( "cousin", [](const std::string& uri, std::unique_ptr* guard, std::string* /* errmsg */) { guard->reset(new WrappedEnv(Env::Default(), uri)); return guard->get(); }); Env* env = nullptr; std::unique_ptr guard; std::string msg; // a:://* is registered in Default, so they should all work ASSERT_OK(parent->NewStaticObject("a://test", &env)); ASSERT_OK(child->NewStaticObject("a://test", &env)); ASSERT_OK(uncle->NewStaticObject("a://test", &env)); ASSERT_OK(cousin->NewStaticObject("a://test", &env)); // The parent env is only registered for parent, not uncle, // So parent and child should return success and uncle and cousin should fail ASSERT_OK(parent->NewUniqueObject("parent", &guard)); ASSERT_OK(child->NewUniqueObject("parent", &guard)); ASSERT_NOK(uncle->NewUniqueObject("parent", &guard)); ASSERT_NOK(cousin->NewUniqueObject("parent", &guard)); // The cousin is only registered in the cousin, so all of the others should // fail ASSERT_OK(cousin->NewUniqueObject("cousin", &guard)); ASSERT_NOK(parent->NewUniqueObject("cousin", &guard)); ASSERT_NOK(child->NewUniqueObject("cousin", &guard)); ASSERT_NOK(uncle->NewUniqueObject("cousin", &guard)); } class MyCustomizable : public Customizable { public: static const char* Type() { return "MyCustomizable"; } MyCustomizable(const char* prefix, const std::string& id) : id_(id) { name_ = id_.substr(0, strlen(prefix) - 1); } const char* Name() const override { return name_.c_str(); } std::string GetId() const override { return id_; } private: std::string id_; std::string name_; }; TEST_F(ObjRegistryTest, TestFactoryCount) { std::string msg; auto grand = ObjectRegistry::Default(); auto local = ObjectRegistry::NewInstance(); std::unordered_set grand_types, local_types; std::vector grand_names, local_names; // Check how many types we have on startup. // Grand should equal local grand->GetFactoryTypes(&grand_types); local->GetFactoryTypes(&local_types); ASSERT_EQ(grand_types, local_types); size_t grand_count = grand->GetFactoryCount(Env::Type()); size_t local_count = local->GetFactoryCount(Env::Type()); ASSERT_EQ(grand_count, local_count); grand->GetFactoryNames(Env::Type(), &grand_names); local->GetFactoryNames(Env::Type(), &local_names); ASSERT_EQ(grand_names.size(), grand_count); ASSERT_EQ(local_names.size(), local_count); ASSERT_EQ(grand_names, local_names); // Add an Env to the local registry. // This will add one factory. auto library = local->AddLibrary("local"); library->AddFactory( "A", [](const std::string& /*uri*/, std::unique_ptr* /*guard */, std::string* /* errmsg */) { return nullptr; }); ASSERT_EQ(local_count + 1, local->GetFactoryCount(Env::Type())); ASSERT_EQ(grand_count, grand->GetFactoryCount(Env::Type())); local->GetFactoryTypes(&local_types); local->GetFactoryNames(Env::Type(), &local_names); ASSERT_EQ(grand_names.size() + 1, local_names.size()); ASSERT_EQ(local_names.size(), local->GetFactoryCount(Env::Type())); if (grand_count == 0) { // There were no Env when we started. Should have one more type // than previously ASSERT_NE(grand_types, local_types); ASSERT_EQ(grand_types.size() + 1, local_types.size()); } else { // There was an Env type when we started. The types should match ASSERT_EQ(grand_types, local_types); } // Add a MyCustomizable to the registry. This should be a new type library->AddFactory( "MY", [](const std::string& /*uri*/, std::unique_ptr* /*guard */, std::string* /* errmsg */) { return nullptr; }); ASSERT_EQ(local_count + 1, local->GetFactoryCount(Env::Type())); ASSERT_EQ(grand_count, grand->GetFactoryCount(Env::Type())); ASSERT_EQ(0U, grand->GetFactoryCount(MyCustomizable::Type())); ASSERT_EQ(1U, local->GetFactoryCount(MyCustomizable::Type())); local->GetFactoryNames(MyCustomizable::Type(), &local_names); ASSERT_EQ(1U, local_names.size()); ASSERT_EQ(local_names[0], "MY"); local->GetFactoryTypes(&local_types); ASSERT_EQ(grand_count == 0 ? 2 : grand_types.size() + 1, local_types.size()); // Add the same name again. We should now have 2 factories. library->AddFactory( "MY", [](const std::string& /*uri*/, std::unique_ptr* /*guard */, std::string* /* errmsg */) { return nullptr; }); local->GetFactoryNames(MyCustomizable::Type(), &local_names); ASSERT_EQ(2U, local_names.size()); } TEST_F(ObjRegistryTest, TestManagedObjects) { auto registry = ObjectRegistry::NewInstance(); auto m_a1 = std::make_shared("", "A"); auto m_a2 = std::make_shared("", "A"); ASSERT_EQ(registry->GetManagedObject("A"), nullptr); ASSERT_OK(registry->SetManagedObject(m_a1)); ASSERT_EQ(registry->GetManagedObject("A"), m_a1); ASSERT_NOK(registry->SetManagedObject(m_a2)); ASSERT_OK(registry->SetManagedObject(m_a1)); m_a1.reset(); ASSERT_EQ(registry->GetManagedObject("A"), nullptr); ASSERT_OK(registry->SetManagedObject(m_a2)); ASSERT_EQ(registry->GetManagedObject("A"), m_a2); } TEST_F(ObjRegistryTest, TestTwoManagedObjects) { auto registry = ObjectRegistry::NewInstance(); auto m_a = std::make_shared("", "A"); auto m_b = std::make_shared("", "B"); std::vector> objects; ASSERT_EQ(registry->GetManagedObject("A"), nullptr); ASSERT_EQ(registry->GetManagedObject("B"), nullptr); ASSERT_OK(registry->ListManagedObjects(&objects)); ASSERT_EQ(objects.size(), 0U); ASSERT_OK(registry->SetManagedObject(m_a)); ASSERT_EQ(registry->GetManagedObject("B"), nullptr); ASSERT_EQ(registry->GetManagedObject("A"), m_a); ASSERT_OK(registry->ListManagedObjects(&objects)); ASSERT_EQ(objects.size(), 1U); ASSERT_EQ(objects.front(), m_a); ASSERT_OK(registry->SetManagedObject(m_b)); ASSERT_EQ(registry->GetManagedObject("A"), m_a); ASSERT_EQ(registry->GetManagedObject("B"), m_b); ASSERT_OK(registry->ListManagedObjects(&objects)); ASSERT_EQ(objects.size(), 2U); ASSERT_OK(registry->ListManagedObjects("A", &objects)); ASSERT_EQ(objects.size(), 1U); ASSERT_EQ(objects.front(), m_a); ASSERT_OK(registry->ListManagedObjects("B", &objects)); ASSERT_EQ(objects.size(), 1U); ASSERT_EQ(objects.front(), m_b); ASSERT_OK(registry->ListManagedObjects("C", &objects)); ASSERT_EQ(objects.size(), 0U); m_a.reset(); objects.clear(); ASSERT_EQ(registry->GetManagedObject("B"), m_b); ASSERT_EQ(registry->GetManagedObject("A"), nullptr); ASSERT_OK(registry->ListManagedObjects(&objects)); ASSERT_EQ(objects.size(), 1U); ASSERT_EQ(objects.front(), m_b); m_b.reset(); objects.clear(); ASSERT_EQ(registry->GetManagedObject("A"), nullptr); ASSERT_EQ(registry->GetManagedObject("B"), nullptr); } TEST_F(ObjRegistryTest, TestAlternateNames) { auto registry = ObjectRegistry::NewInstance(); auto m_a = std::make_shared("", "A"); auto m_b = std::make_shared("", "B"); std::vector> objects; // Test no objects exist ASSERT_EQ(registry->GetManagedObject("A"), nullptr); ASSERT_EQ(registry->GetManagedObject("B"), nullptr); ASSERT_EQ(registry->GetManagedObject("TheOne"), nullptr); ASSERT_OK(registry->ListManagedObjects(&objects)); ASSERT_EQ(objects.size(), 0U); // Mark "TheOne" to be A ASSERT_OK(registry->SetManagedObject("TheOne", m_a)); ASSERT_EQ(registry->GetManagedObject("B"), nullptr); ASSERT_EQ(registry->GetManagedObject("A"), nullptr); ASSERT_EQ(registry->GetManagedObject("TheOne"), m_a); ASSERT_OK(registry->ListManagedObjects(&objects)); ASSERT_EQ(objects.size(), 1U); ASSERT_EQ(objects.front(), m_a); // Try to mark "TheOne" again. ASSERT_NOK(registry->SetManagedObject("TheOne", m_b)); ASSERT_OK(registry->SetManagedObject("TheOne", m_a)); // Add "A" as a managed object. Registered 2x ASSERT_OK(registry->SetManagedObject(m_a)); ASSERT_EQ(registry->GetManagedObject("B"), nullptr); ASSERT_EQ(registry->GetManagedObject("A"), m_a); ASSERT_EQ(registry->GetManagedObject("TheOne"), m_a); ASSERT_OK(registry->ListManagedObjects(&objects)); ASSERT_EQ(objects.size(), 2U); // Delete "A". m_a.reset(); objects.clear(); ASSERT_EQ(registry->GetManagedObject("TheOne"), nullptr); ASSERT_OK(registry->SetManagedObject("TheOne", m_b)); ASSERT_EQ(registry->GetManagedObject("TheOne"), m_b); ASSERT_OK(registry->ListManagedObjects(&objects)); ASSERT_EQ(objects.size(), 1U); ASSERT_EQ(objects.front(), m_b); m_b.reset(); objects.clear(); ASSERT_EQ(registry->GetManagedObject("A"), nullptr); ASSERT_EQ(registry->GetManagedObject("A"), nullptr); ASSERT_EQ(registry->GetManagedObject("TheOne"), nullptr); ASSERT_OK(registry->ListManagedObjects(&objects)); ASSERT_EQ(objects.size(), 0U); } TEST_F(ObjRegistryTest, TestTwoManagedClasses) { class MyCustomizable2 : public MyCustomizable { public: static const char* Type() { return "MyCustomizable2"; } MyCustomizable2(const char* prefix, const std::string& id) : MyCustomizable(prefix, id) {} }; auto registry = ObjectRegistry::NewInstance(); auto m_a1 = std::make_shared("", "A"); auto m_a2 = std::make_shared("", "A"); std::vector> obj1s; std::vector> obj2s; ASSERT_EQ(registry->GetManagedObject("A"), nullptr); ASSERT_EQ(registry->GetManagedObject("A"), nullptr); ASSERT_OK(registry->SetManagedObject(m_a1)); ASSERT_EQ(registry->GetManagedObject("A"), m_a1); ASSERT_EQ(registry->GetManagedObject("A"), nullptr); ASSERT_OK(registry->SetManagedObject(m_a2)); ASSERT_EQ(registry->GetManagedObject("A"), m_a2); ASSERT_OK(registry->ListManagedObjects(&obj1s)); ASSERT_OK(registry->ListManagedObjects(&obj2s)); ASSERT_EQ(obj1s.size(), 1U); ASSERT_EQ(obj2s.size(), 1U); ASSERT_EQ(obj1s.front(), m_a1); ASSERT_EQ(obj2s.front(), m_a2); m_a1.reset(); obj1s.clear(); obj2s.clear(); ASSERT_EQ(registry->GetManagedObject("A"), nullptr); ASSERT_EQ(registry->GetManagedObject("A"), m_a2); m_a2.reset(); ASSERT_EQ(registry->GetManagedObject("A"), nullptr); ASSERT_EQ(registry->GetManagedObject("A"), nullptr); } TEST_F(ObjRegistryTest, TestManagedObjectsWithParent) { auto base = ObjectRegistry::NewInstance(); auto registry = ObjectRegistry::NewInstance(base); auto m_a = std::make_shared("", "A"); auto m_b = std::make_shared("", "A"); ASSERT_EQ(registry->GetManagedObject("A"), nullptr); ASSERT_OK(base->SetManagedObject(m_a)); ASSERT_EQ(registry->GetManagedObject("A"), m_a); ASSERT_NOK(registry->SetManagedObject(m_b)); ASSERT_OK(registry->SetManagedObject(m_a)); m_a.reset(); ASSERT_EQ(registry->GetManagedObject("A"), nullptr); ASSERT_OK(registry->SetManagedObject(m_b)); ASSERT_EQ(registry->GetManagedObject("A"), m_b); } TEST_F(ObjRegistryTest, TestGetOrCreateManagedObject) { auto registry = ObjectRegistry::NewInstance(); registry->AddLibrary("test")->AddFactory( ObjectLibrary::PatternEntry::AsIndividualId("MC"), [](const std::string& uri, std::unique_ptr* guard, std::string* /* errmsg */) { guard->reset(new MyCustomizable("MC", uri)); return guard->get(); }); std::shared_ptr m_a, m_b, obj; std::vector> objs; std::unordered_map opt_map; ASSERT_EQ(registry->GetManagedObject("MC@A#1"), nullptr); ASSERT_EQ(registry->GetManagedObject("MC@B#1"), nullptr); ASSERT_OK(registry->GetOrCreateManagedObject("MC@A#1", &m_a)); ASSERT_OK(registry->GetOrCreateManagedObject("MC@B#1", &m_b)); ASSERT_EQ(registry->GetManagedObject("MC@A#1"), m_a); ASSERT_OK(registry->GetOrCreateManagedObject("MC@A#1", &obj)); ASSERT_EQ(obj, m_a); ASSERT_OK(registry->GetOrCreateManagedObject("MC@B#1", &obj)); ASSERT_EQ(obj, m_b); ASSERT_OK(registry->ListManagedObjects(&objs)); ASSERT_EQ(objs.size(), 2U); objs.clear(); m_a.reset(); obj.reset(); ASSERT_OK(registry->GetOrCreateManagedObject("MC@A#1", &m_a)); ASSERT_EQ(1, m_a.use_count()); ASSERT_OK(registry->GetOrCreateManagedObject("MC@B#1", &obj)); ASSERT_EQ(2, obj.use_count()); } TEST_F(ObjRegistryTest, RegisterPlugin) { std::shared_ptr registry = ObjectRegistry::NewInstance(); std::unique_ptr guard; Env* env = nullptr; ASSERT_NOK(registry->NewObject("unguarded", &env, &guard)); ASSERT_EQ(registry->RegisterPlugin("Missing", nullptr), -1); ASSERT_EQ(registry->RegisterPlugin("", RegisterTestUnguarded), -1); ASSERT_GT(registry->RegisterPlugin("Valid", RegisterTestUnguarded), 0); ASSERT_OK(registry->NewObject("unguarded", &env, &guard)); ASSERT_NE(env, nullptr); } class PatternEntryTest : public testing::Test {}; TEST_F(PatternEntryTest, TestSimpleEntry) { ObjectLibrary::PatternEntry entry("ABC", true); ASSERT_TRUE(entry.Matches("ABC")); ASSERT_FALSE(entry.Matches("AABC")); ASSERT_FALSE(entry.Matches("ABCA")); ASSERT_FALSE(entry.Matches("AABCA")); ASSERT_FALSE(entry.Matches("AB")); ASSERT_FALSE(entry.Matches("BC")); ASSERT_FALSE(entry.Matches("ABD")); ASSERT_FALSE(entry.Matches("BCA")); } TEST_F(PatternEntryTest, TestPatternEntry) { // Matches A:+ ObjectLibrary::PatternEntry entry("A", false); entry.AddSeparator(":"); ASSERT_FALSE(entry.Matches("A")); ASSERT_FALSE(entry.Matches("AA")); ASSERT_FALSE(entry.Matches("AB")); ASSERT_FALSE(entry.Matches("B")); ASSERT_FALSE(entry.Matches("A:")); ASSERT_FALSE(entry.Matches("AA:")); ASSERT_FALSE(entry.Matches("AA:B")); ASSERT_FALSE(entry.Matches("AA:BB")); ASSERT_TRUE(entry.Matches("A:B")); ASSERT_TRUE(entry.Matches("A:BB")); entry.SetOptional(true); // Now matches "A" or "A:+" ASSERT_TRUE(entry.Matches("A")); ASSERT_FALSE(entry.Matches("AA")); ASSERT_FALSE(entry.Matches("AB")); ASSERT_FALSE(entry.Matches("B")); ASSERT_FALSE(entry.Matches("A:")); ASSERT_FALSE(entry.Matches("AA:")); ASSERT_FALSE(entry.Matches("AA:B")); ASSERT_FALSE(entry.Matches("AA:BB")); ASSERT_TRUE(entry.Matches("A:B")); ASSERT_TRUE(entry.Matches("A:BB")); } TEST_F(PatternEntryTest, MatchZeroOrMore) { // Matches A:* ObjectLibrary::PatternEntry entry("A", false); entry.AddSeparator(":", false); ASSERT_FALSE(entry.Matches("A")); ASSERT_FALSE(entry.Matches("AA")); ASSERT_FALSE(entry.Matches("AB")); ASSERT_FALSE(entry.Matches("B")); ASSERT_TRUE(entry.Matches("A:")); ASSERT_FALSE(entry.Matches("B:")); ASSERT_FALSE(entry.Matches("B:A")); ASSERT_FALSE(entry.Matches("AA:")); ASSERT_FALSE(entry.Matches("AA:B")); ASSERT_FALSE(entry.Matches("AA:BB")); ASSERT_TRUE(entry.Matches("A:B")); ASSERT_TRUE(entry.Matches("A:BB")); entry.SetOptional(true); // Now matches "A" or "A:*" ASSERT_TRUE(entry.Matches("A")); ASSERT_FALSE(entry.Matches("AA")); ASSERT_FALSE(entry.Matches("AB")); ASSERT_FALSE(entry.Matches("B")); ASSERT_TRUE(entry.Matches("A:")); ASSERT_FALSE(entry.Matches("B:")); ASSERT_FALSE(entry.Matches("B:A")); ASSERT_FALSE(entry.Matches("AA:")); ASSERT_FALSE(entry.Matches("AA:B")); ASSERT_FALSE(entry.Matches("AA:BB")); ASSERT_TRUE(entry.Matches("A:B")); ASSERT_TRUE(entry.Matches("A:BB")); } TEST_F(PatternEntryTest, TestSuffixEntry) { ObjectLibrary::PatternEntry entry("AA", true); entry.AddSuffix("BB"); ASSERT_TRUE(entry.Matches("AA")); ASSERT_TRUE(entry.Matches("AABB")); ASSERT_FALSE(entry.Matches("A")); ASSERT_FALSE(entry.Matches("AB")); ASSERT_FALSE(entry.Matches("B")); ASSERT_FALSE(entry.Matches("BB")); ASSERT_FALSE(entry.Matches("ABA")); ASSERT_FALSE(entry.Matches("BBAA")); ASSERT_FALSE(entry.Matches("AABBA")); ASSERT_FALSE(entry.Matches("AABBB")); } TEST_F(PatternEntryTest, TestNumericEntry) { ObjectLibrary::PatternEntry entry("A", false); entry.AddNumber(":"); ASSERT_FALSE(entry.Matches("A")); ASSERT_FALSE(entry.Matches("AA")); ASSERT_FALSE(entry.Matches("A:")); ASSERT_FALSE(entry.Matches("AA:")); ASSERT_TRUE(entry.Matches("A:1")); ASSERT_TRUE(entry.Matches("A:11")); ASSERT_FALSE(entry.Matches("AA:1")); ASSERT_FALSE(entry.Matches("AA:11")); ASSERT_FALSE(entry.Matches("A:B")); ASSERT_FALSE(entry.Matches("A:1B")); ASSERT_FALSE(entry.Matches("A:B1")); entry.AddSeparator(":", false); ASSERT_FALSE(entry.Matches("A")); ASSERT_FALSE(entry.Matches("AA")); ASSERT_FALSE(entry.Matches("A:")); ASSERT_FALSE(entry.Matches("AA:")); ASSERT_TRUE(entry.Matches("A:1:")); ASSERT_TRUE(entry.Matches("A:11:")); ASSERT_FALSE(entry.Matches("A:1")); ASSERT_FALSE(entry.Matches("A:B1:")); ASSERT_FALSE(entry.Matches("A:1B:")); ASSERT_FALSE(entry.Matches("A::")); } TEST_F(PatternEntryTest, TestDoubleEntry) { ObjectLibrary::PatternEntry entry("A", false); entry.AddNumber(":", false); ASSERT_FALSE(entry.Matches("A")); ASSERT_FALSE(entry.Matches("AA")); ASSERT_FALSE(entry.Matches("A:")); ASSERT_FALSE(entry.Matches("AA:")); ASSERT_FALSE(entry.Matches("AA:1")); ASSERT_FALSE(entry.Matches("AA:11")); ASSERT_FALSE(entry.Matches("A:B")); ASSERT_FALSE(entry.Matches("A:1B")); ASSERT_FALSE(entry.Matches("A:B1")); ASSERT_TRUE(entry.Matches("A:1")); ASSERT_TRUE(entry.Matches("A:11")); ASSERT_TRUE(entry.Matches("A:1.1")); ASSERT_TRUE(entry.Matches("A:11.11")); ASSERT_TRUE(entry.Matches("A:1.")); ASSERT_TRUE(entry.Matches("A:.1")); ASSERT_TRUE(entry.Matches("A:0.1")); ASSERT_TRUE(entry.Matches("A:1.0")); ASSERT_TRUE(entry.Matches("A:1.0")); ASSERT_FALSE(entry.Matches("A:1.0.")); ASSERT_FALSE(entry.Matches("A:1.0.2")); ASSERT_FALSE(entry.Matches("A:.1.0")); ASSERT_FALSE(entry.Matches("A:..10")); ASSERT_FALSE(entry.Matches("A:10..")); ASSERT_FALSE(entry.Matches("A:.")); entry.AddSeparator(":", false); ASSERT_FALSE(entry.Matches("A:1")); ASSERT_FALSE(entry.Matches("A:1.0")); ASSERT_TRUE(entry.Matches("A:11:")); ASSERT_TRUE(entry.Matches("A:1.1:")); ASSERT_TRUE(entry.Matches("A:11.11:")); ASSERT_TRUE(entry.Matches("A:1.:")); ASSERT_TRUE(entry.Matches("A:.1:")); ASSERT_TRUE(entry.Matches("A:0.1:")); ASSERT_TRUE(entry.Matches("A:1.0:")); ASSERT_TRUE(entry.Matches("A:1.0:")); ASSERT_FALSE(entry.Matches("A:1.0.:")); ASSERT_FALSE(entry.Matches("A:1.0.2:")); ASSERT_FALSE(entry.Matches("A:.1.0:")); ASSERT_FALSE(entry.Matches("A:..10:")); ASSERT_FALSE(entry.Matches("A:10..:")); ASSERT_FALSE(entry.Matches("A:.:")); ASSERT_FALSE(entry.Matches("A::")); } TEST_F(PatternEntryTest, TestIndividualIdEntry) { auto entry = ObjectLibrary::PatternEntry::AsIndividualId("AA"); ASSERT_TRUE(entry.Matches("AA")); ASSERT_TRUE(entry.Matches("AA@123#456")); ASSERT_TRUE(entry.Matches("AA@deadbeef#id")); ASSERT_FALSE(entry.Matches("A")); ASSERT_FALSE(entry.Matches("AAA")); ASSERT_FALSE(entry.Matches("AA@123")); ASSERT_FALSE(entry.Matches("AA@123#")); ASSERT_FALSE(entry.Matches("AA@#123")); } TEST_F(PatternEntryTest, TestTwoNameEntry) { ObjectLibrary::PatternEntry entry("A"); entry.AnotherName("B"); ASSERT_TRUE(entry.Matches("A")); ASSERT_TRUE(entry.Matches("B")); ASSERT_FALSE(entry.Matches("AA")); ASSERT_FALSE(entry.Matches("BB")); ASSERT_FALSE(entry.Matches("AA")); ASSERT_FALSE(entry.Matches("BA")); ASSERT_FALSE(entry.Matches("AB")); } TEST_F(PatternEntryTest, TestTwoPatternEntry) { ObjectLibrary::PatternEntry entry("AA", false); entry.AddSeparator(":"); entry.AddSeparator(":"); ASSERT_FALSE(entry.Matches("AA")); ASSERT_FALSE(entry.Matches("AA:")); ASSERT_FALSE(entry.Matches("AA::")); ASSERT_FALSE(entry.Matches("AA::12")); ASSERT_TRUE(entry.Matches("AA:1:2")); ASSERT_TRUE(entry.Matches("AA:1:2:")); ObjectLibrary::PatternEntry entry2("AA", false); entry2.AddSeparator("::"); entry2.AddSeparator("##"); ASSERT_FALSE(entry2.Matches("AA")); ASSERT_FALSE(entry2.Matches("AA:")); ASSERT_FALSE(entry2.Matches("AA::")); ASSERT_FALSE(entry2.Matches("AA::#")); ASSERT_FALSE(entry2.Matches("AA::##")); ASSERT_FALSE(entry2.Matches("AA##1::2")); ASSERT_FALSE(entry2.Matches("AA::123##")); ASSERT_TRUE(entry2.Matches("AA::1##2")); ASSERT_TRUE(entry2.Matches("AA::12##34:")); ASSERT_TRUE(entry2.Matches("AA::12::34##56")); ASSERT_TRUE(entry2.Matches("AA::12##34::56")); } TEST_F(PatternEntryTest, TestTwoNumbersEntry) { ObjectLibrary::PatternEntry entry("AA", false); entry.AddNumber(":"); entry.AddNumber(":"); ASSERT_FALSE(entry.Matches("AA")); ASSERT_FALSE(entry.Matches("AA:")); ASSERT_FALSE(entry.Matches("AA::")); ASSERT_FALSE(entry.Matches("AA::12")); ASSERT_FALSE(entry.Matches("AA:1:2:")); ASSERT_TRUE(entry.Matches("AA:1:2")); ASSERT_TRUE(entry.Matches("AA:12:23456")); ObjectLibrary::PatternEntry entry2("AA", false); entry2.AddNumber(":"); entry2.AddNumber("#"); ASSERT_FALSE(entry2.Matches("AA")); ASSERT_FALSE(entry2.Matches("AA:")); ASSERT_FALSE(entry2.Matches("AA:#")); ASSERT_FALSE(entry2.Matches("AA#:")); ASSERT_FALSE(entry2.Matches("AA:123#")); ASSERT_FALSE(entry2.Matches("AA:123#B")); ASSERT_FALSE(entry2.Matches("AA:B#123")); ASSERT_TRUE(entry2.Matches("AA:1#2")); ASSERT_FALSE(entry2.Matches("AA:123#23:")); ASSERT_FALSE(entry2.Matches("AA::12#234")); } TEST_F(PatternEntryTest, TestPatternAndSuffix) { ObjectLibrary::PatternEntry entry("AA", false); entry.AddSeparator("::"); entry.AddSuffix("##"); ASSERT_FALSE(entry.Matches("AA")); ASSERT_FALSE(entry.Matches("AA::")); ASSERT_FALSE(entry.Matches("AA::##")); ASSERT_FALSE(entry.Matches("AB::1##")); ASSERT_FALSE(entry.Matches("AB::1##2")); ASSERT_FALSE(entry.Matches("AA##1::")); ASSERT_TRUE(entry.Matches("AA::1##")); ASSERT_FALSE(entry.Matches("AA::1###")); ObjectLibrary::PatternEntry entry2("AA", false); entry2.AddSuffix("::"); entry2.AddSeparator("##"); ASSERT_FALSE(entry2.Matches("AA")); ASSERT_FALSE(entry2.Matches("AA::")); ASSERT_FALSE(entry2.Matches("AA::##")); ASSERT_FALSE(entry2.Matches("AB::1##")); ASSERT_FALSE(entry2.Matches("AB::1##2")); ASSERT_TRUE(entry2.Matches("AA::##12")); } TEST_F(PatternEntryTest, TestTwoNamesAndPattern) { ObjectLibrary::PatternEntry entry("AA", true); entry.AddSeparator("::"); entry.AnotherName("BBB"); ASSERT_TRUE(entry.Matches("AA")); ASSERT_TRUE(entry.Matches("AA::1")); ASSERT_TRUE(entry.Matches("BBB")); ASSERT_TRUE(entry.Matches("BBB::2")); ASSERT_FALSE(entry.Matches("AA::")); ASSERT_FALSE(entry.Matches("AAA::")); ASSERT_FALSE(entry.Matches("BBB::")); entry.SetOptional(false); ASSERT_FALSE(entry.Matches("AA")); ASSERT_FALSE(entry.Matches("BBB")); ASSERT_FALSE(entry.Matches("AA::")); ASSERT_FALSE(entry.Matches("AAA::")); ASSERT_FALSE(entry.Matches("BBB::")); ASSERT_TRUE(entry.Matches("AA::1")); ASSERT_TRUE(entry.Matches("BBB::2")); } } // namespace ROCKSDB_NAMESPACE int main(int argc, char** argv) { ROCKSDB_NAMESPACE::port::InstallStackTraceHandler(); ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } #else // ROCKSDB_LITE #include int main(int /*argc*/, char** /*argv*/) { fprintf(stderr, "SKIPPED as ObjRegistry is not supported in ROCKSDB_LITE\n"); return 0; } #endif // ROCKSDB_LITE