/* * Copyright (c) Meta Platforms, Inc. and affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include THRIFT_FLAG_DEFINE_bool(test_flag_bool, true); THRIFT_FLAG_DEFINE_int64(test_flag_int, 42); THRIFT_FLAG_DEFINE_string(test_flag_string, "foo"); THRIFT_FLAG_DECLARE_bool(test_flag_bool_external); THRIFT_FLAG_DECLARE_int64(test_flag_int_external); THRIFT_FLAG_DECLARE_string(test_flag_string_external); class TestFlagsBackend : public apache::thrift::detail::FlagsBackend { public: folly::observer::Observer> getFlagObserverBool( std::string_view name) override { return getFlagObservableBool(name).getObserver(); } folly::observer::Observer> getFlagObserverInt64( std::string_view name) override { return getFlagObservableInt64(name).getObserver(); } folly::observer::Observer> getFlagObserverString( std::string_view name) override { return getFlagObservableString(name).getObserver(); } folly::observer::SimpleObservable>& getFlagObservableBool( std::string_view name) { return getFlagObservable(boolObservables_, name); } folly::observer::SimpleObservable>& getFlagObservableInt64(std::string_view name) { return getFlagObservable(int64Observables_, name); } folly::observer::SimpleObservable>& getFlagObservableString(std::string_view name) { return getFlagObservable(stringObservables_, name); } private: template using ObservablesMap = std::unordered_map< std::string, std::unique_ptr>>>; ObservablesMap boolObservables_; ObservablesMap int64Observables_; ObservablesMap stringObservables_; template static folly::observer::SimpleObservable>& getFlagObservable( ObservablesMap& observables, std::string_view name) { auto nameStr = std::string{name}; if (auto observablePtr = folly::get_ptr(observables, nameStr)) { return **observablePtr; } return *( observables[nameStr] = std::make_unique< folly::observer::SimpleObservable>>( std::optional{})); } }; namespace { TestFlagsBackend* testBackendPtr; bool useDummyBackend{false}; } // namespace namespace apache::thrift::detail { THRIFT_PLUGGABLE_FUNC_SET( std::unique_ptr, createFlagsBackend) { if (useDummyBackend) { return {}; } auto testBackend = std::make_unique(); testBackendPtr = testBackend.get(); return testBackend; } } // namespace apache::thrift::detail TEST(Flags, Get) { EXPECT_EQ(true, THRIFT_FLAG(test_flag_bool)); EXPECT_EQ(42, THRIFT_FLAG(test_flag_int)); EXPECT_EQ("foo", THRIFT_FLAG(test_flag_string)); EXPECT_EQ(true, THRIFT_FLAG(test_flag_bool_external)); EXPECT_EQ(42, THRIFT_FLAG(test_flag_int_external)); EXPECT_EQ("foo", THRIFT_FLAG(test_flag_string_external)); testBackendPtr->getFlagObservableBool("test_flag_bool").setValue(false); testBackendPtr->getFlagObservableInt64("test_flag_int").setValue(41); testBackendPtr->getFlagObservableString("test_flag_string") .setValue(std::string{"bar"}); testBackendPtr->getFlagObservableBool("test_flag_bool_external") .setValue(false); testBackendPtr->getFlagObservableInt64("test_flag_int_external").setValue(41); testBackendPtr->getFlagObservableString("test_flag_string_external") .setValue(std::string{"bar"}); folly::observer_detail::ObserverManager::waitForAllUpdates(); EXPECT_EQ(false, THRIFT_FLAG(test_flag_bool)); EXPECT_EQ(41, THRIFT_FLAG(test_flag_int)); EXPECT_EQ("bar", THRIFT_FLAG(test_flag_string)); EXPECT_EQ(false, THRIFT_FLAG(test_flag_bool_external)); EXPECT_EQ(41, THRIFT_FLAG(test_flag_int_external)); EXPECT_EQ("bar", THRIFT_FLAG(test_flag_string_external)); } TEST(Flags, Observe) { auto test_flag_bool_observer = THRIFT_FLAG_OBSERVE(test_flag_bool); auto test_flag_int_observer = THRIFT_FLAG_OBSERVE(test_flag_int); auto test_flag_string_observer = THRIFT_FLAG_OBSERVE(test_flag_string); auto test_flag_bool_external_observer = THRIFT_FLAG_OBSERVE(test_flag_bool_external); auto test_flag_int_extenal_observer = THRIFT_FLAG_OBSERVE(test_flag_int_external); auto test_flag_string_external_observer = THRIFT_FLAG_OBSERVE(test_flag_string_external); EXPECT_EQ(true, **test_flag_bool_observer); EXPECT_EQ(42, **test_flag_int_observer); EXPECT_EQ("foo", **test_flag_string_observer); EXPECT_EQ(true, **test_flag_bool_external_observer); EXPECT_EQ(42, **test_flag_int_extenal_observer); EXPECT_EQ("foo", **test_flag_string_external_observer); testBackendPtr->getFlagObservableBool("test_flag_bool").setValue(false); testBackendPtr->getFlagObservableInt64("test_flag_int").setValue(41); testBackendPtr->getFlagObservableString("test_flag_string") .setValue(std::string{"bar"}); testBackendPtr->getFlagObservableBool("test_flag_bool_external") .setValue(false); testBackendPtr->getFlagObservableInt64("test_flag_int_external").setValue(41); testBackendPtr->getFlagObservableString("test_flag_string_external") .setValue(std::string{"bar"}); folly::observer_detail::ObserverManager::waitForAllUpdates(); EXPECT_EQ(false, **test_flag_bool_observer); EXPECT_EQ(41, **test_flag_int_observer); EXPECT_EQ("bar", **test_flag_string_observer); EXPECT_EQ(false, **test_flag_bool_external_observer); EXPECT_EQ(41, **test_flag_int_extenal_observer); EXPECT_EQ("bar", **test_flag_string_external_observer); } TEST(Flags, NoBackend) { useDummyBackend = true; EXPECT_EQ(true, THRIFT_FLAG(test_flag_bool)); EXPECT_EQ(42, THRIFT_FLAG(test_flag_int)); EXPECT_EQ("foo", THRIFT_FLAG(test_flag_string)); EXPECT_EQ(true, THRIFT_FLAG(test_flag_bool_external)); EXPECT_EQ(42, THRIFT_FLAG(test_flag_int_external)); EXPECT_EQ("foo", THRIFT_FLAG(test_flag_string_external)); } TEST(Flags, MockGet) { EXPECT_EQ(true, THRIFT_FLAG(test_flag_bool)); EXPECT_EQ(42, THRIFT_FLAG(test_flag_int)); EXPECT_EQ("foo", THRIFT_FLAG(test_flag_string)); EXPECT_EQ(true, THRIFT_FLAG(test_flag_bool_external)); EXPECT_EQ(42, THRIFT_FLAG(test_flag_int_external)); EXPECT_EQ("foo", THRIFT_FLAG(test_flag_string_external)); THRIFT_FLAG_SET_MOCK(test_flag_bool, false); THRIFT_FLAG_SET_MOCK(test_flag_int, 41); THRIFT_FLAG_SET_MOCK(test_flag_string, std::string{"bar"}); THRIFT_FLAG_SET_MOCK(test_flag_bool_external, false); THRIFT_FLAG_SET_MOCK(test_flag_int_external, 41); THRIFT_FLAG_SET_MOCK(test_flag_string_external, std::string{"bar"}); folly::observer_detail::ObserverManager::waitForAllUpdates(); EXPECT_EQ(false, THRIFT_FLAG(test_flag_bool)); EXPECT_EQ(41, THRIFT_FLAG(test_flag_int)); EXPECT_EQ("bar", THRIFT_FLAG(test_flag_string)); EXPECT_EQ(false, THRIFT_FLAG(test_flag_bool_external)); EXPECT_EQ(41, THRIFT_FLAG(test_flag_int_external)); EXPECT_EQ("bar", THRIFT_FLAG(test_flag_string_external)); THRIFT_FLAG_SET_MOCK(test_flag_bool, true); THRIFT_FLAG_SET_MOCK(test_flag_int, 9); THRIFT_FLAG_SET_MOCK(test_flag_string, std::string{"baz"}); THRIFT_FLAG_SET_MOCK(test_flag_bool_external, true); THRIFT_FLAG_SET_MOCK(test_flag_int_external, 61); THRIFT_FLAG_SET_MOCK(test_flag_string_external, std::string{"baz"}); folly::observer_detail::ObserverManager::waitForAllUpdates(); EXPECT_EQ(true, THRIFT_FLAG(test_flag_bool)); EXPECT_EQ(9, THRIFT_FLAG(test_flag_int)); EXPECT_EQ("baz", THRIFT_FLAG(test_flag_string)); EXPECT_EQ(true, THRIFT_FLAG(test_flag_bool_external)); EXPECT_EQ(61, THRIFT_FLAG(test_flag_int_external)); EXPECT_EQ("baz", THRIFT_FLAG(test_flag_string_external)); } TEST(Flags, MockObserve) { EXPECT_EQ(true, THRIFT_FLAG(test_flag_bool)); EXPECT_EQ(42, THRIFT_FLAG(test_flag_int)); EXPECT_EQ("foo", THRIFT_FLAG(test_flag_string)); EXPECT_EQ(true, THRIFT_FLAG(test_flag_bool_external)); EXPECT_EQ(42, THRIFT_FLAG(test_flag_int_external)); EXPECT_EQ("foo", THRIFT_FLAG(test_flag_string_external)); auto test_flag_bool_observer = THRIFT_FLAG_OBSERVE(test_flag_bool); auto test_flag_int_observer = THRIFT_FLAG_OBSERVE(test_flag_int); auto test_flag_string_observer = THRIFT_FLAG_OBSERVE(test_flag_string); auto test_flag_bool_external_observer = THRIFT_FLAG_OBSERVE(test_flag_bool_external); auto test_flag_int_extenal_observer = THRIFT_FLAG_OBSERVE(test_flag_int_external); auto test_flag_string_external_observer = THRIFT_FLAG_OBSERVE(test_flag_string_external); THRIFT_FLAG_SET_MOCK(test_flag_bool, false); THRIFT_FLAG_SET_MOCK(test_flag_int, 41); THRIFT_FLAG_SET_MOCK(test_flag_string, std::string{"bar"}); THRIFT_FLAG_SET_MOCK(test_flag_bool_external, false); THRIFT_FLAG_SET_MOCK(test_flag_int_external, 41); THRIFT_FLAG_SET_MOCK(test_flag_string_external, std::string{"bar"}); folly::observer_detail::ObserverManager::waitForAllUpdates(); EXPECT_EQ(false, **test_flag_bool_observer); EXPECT_EQ(41, **test_flag_int_observer); EXPECT_EQ("bar", **test_flag_string_observer); EXPECT_EQ(false, **test_flag_bool_external_observer); EXPECT_EQ(41, **test_flag_int_extenal_observer); EXPECT_EQ("bar", **test_flag_string_external_observer); THRIFT_FLAG_SET_MOCK(test_flag_bool, true); THRIFT_FLAG_SET_MOCK(test_flag_int, 9); THRIFT_FLAG_SET_MOCK(test_flag_string, std::string{"baz"}); THRIFT_FLAG_SET_MOCK(test_flag_bool_external, true); THRIFT_FLAG_SET_MOCK(test_flag_int_external, 61); THRIFT_FLAG_SET_MOCK(test_flag_string_external, std::string{"baz"}); folly::observer_detail::ObserverManager::waitForAllUpdates(); EXPECT_EQ(true, **test_flag_bool_observer); EXPECT_EQ(9, **test_flag_int_observer); EXPECT_EQ("baz", **test_flag_string_observer); EXPECT_EQ(true, **test_flag_bool_external_observer); EXPECT_EQ(61, **test_flag_int_extenal_observer); EXPECT_EQ("baz", **test_flag_string_external_observer); } TEST(Flags, MockValuePreferred) { EXPECT_EQ(true, THRIFT_FLAG(test_flag_bool)); EXPECT_EQ(42, THRIFT_FLAG(test_flag_int)); EXPECT_EQ("foo", THRIFT_FLAG(test_flag_string)); EXPECT_EQ(true, THRIFT_FLAG(test_flag_bool_external)); EXPECT_EQ(42, THRIFT_FLAG(test_flag_int_external)); EXPECT_EQ("foo", THRIFT_FLAG(test_flag_string_external)); THRIFT_FLAG_SET_MOCK(test_flag_bool, true); THRIFT_FLAG_SET_MOCK(test_flag_int, 73); THRIFT_FLAG_SET_MOCK(test_flag_string, std::string{"mock"}); THRIFT_FLAG_SET_MOCK(test_flag_bool_external, true); THRIFT_FLAG_SET_MOCK(test_flag_int_external, 49); THRIFT_FLAG_SET_MOCK(test_flag_string_external, std::string{"mock"}); folly::observer_detail::ObserverManager::waitForAllUpdates(); testBackendPtr->getFlagObservableBool("test_flag_bool").setValue(false); testBackendPtr->getFlagObservableInt64("test_flag_int").setValue(41); testBackendPtr->getFlagObservableString("test_flag_string") .setValue(std::string{"bar"}); testBackendPtr->getFlagObservableBool("test_flag_bool_external") .setValue(false); testBackendPtr->getFlagObservableInt64("test_flag_int_external").setValue(41); testBackendPtr->getFlagObservableString("test_flag_string_external") .setValue(std::string{"bar"}); folly::observer_detail::ObserverManager::waitForAllUpdates(); EXPECT_EQ(true, THRIFT_FLAG(test_flag_bool)); EXPECT_EQ(73, THRIFT_FLAG(test_flag_int)); EXPECT_EQ("mock", THRIFT_FLAG(test_flag_string)); EXPECT_EQ(true, THRIFT_FLAG(test_flag_bool_external)); EXPECT_EQ(49, THRIFT_FLAG(test_flag_int_external)); EXPECT_EQ("mock", THRIFT_FLAG(test_flag_string_external)); } void assertFlagValue( std::vector allFlags, const std::string& name, const std::string& expectedValue) { bool flagFound = false; for (const auto& flag : allFlags) { if (flag.name == name) { EXPECT_EQ(expectedValue, flag.currentValue); flagFound = true; } } EXPECT_TRUE(flagFound); } TEST(Flags, getAllThriftFlags) { // Arrange THRIFT_FLAG_SET_MOCK(test_flag_bool, false); THRIFT_FLAG_SET_MOCK(test_flag_int, 88); THRIFT_FLAG_SET_MOCK(test_flag_string, std::string{"test string"}); // Act auto allFlags = apache::thrift::getAllThriftFlags(); // Assert assertFlagValue(allFlags, "test_flag_bool", "false"); assertFlagValue(allFlags, "test_flag_int", "88"); assertFlagValue(allFlags, "test_flag_string", "test string"); }