diff --git a/openfeature/BUILD b/openfeature/BUILD index ac0f8e4..e32039a 100644 --- a/openfeature/BUILD +++ b/openfeature/BUILD @@ -77,6 +77,20 @@ cc_library( ], ) +cc_library( + name = "provider_repository", + srcs = ["provider_repository.cpp"], + hdrs = ["provider_repository.h"], + deps = [ + "@abseil-cpp//absl/status", + ":evaluation_context", + ":feature_provider_status_manager", + ":provider", + ":provider_status", + ":noop_provider", + ], +) + cc_library( name = "provider_status", hdrs = ["provider_status.h"], diff --git a/openfeature/provider_repository.cpp b/openfeature/provider_repository.cpp new file mode 100644 index 0000000..9ae404b --- /dev/null +++ b/openfeature/provider_repository.cpp @@ -0,0 +1,222 @@ +#include "openfeature/provider_repository.h" + +#include + +#include "absl/status/statusor.h" +#include "openfeature/noop_provider.h" + +namespace openfeature { + +ProviderRepository::ProviderRepository() { + std::shared_ptr noop_provider = + std::make_shared(); + absl::StatusOr> status_manager = + FeatureProviderStatusManager::Create(noop_provider); + if (status_manager.ok()) { + std::unique_lock lock(repo_mutex_); + default_manager_ = std::move(status_manager.value()); + default_manager_->SetStatus(ProviderStatus::kReady); + } +} + +ProviderRepository::~ProviderRepository() { Shutdown(); } + +std::shared_ptr +ProviderRepository::GetFeatureProviderStatusManager( + std::string_view domain) const { + std::shared_lock lock(repo_mutex_); + + if (domain.empty()) { + return default_manager_; + } + + auto it = provider_manager_.find(std::string(domain)); + if (it != provider_manager_.end()) { + return it->second; + } + + return default_manager_; +} + +std::shared_ptr ProviderRepository::GetProvider( + std::string_view domain) const { + std::shared_ptr manager = + GetFeatureProviderStatusManager(domain); + + if (manager) { + return manager->GetProvider(); + } + return nullptr; +} + +void ProviderRepository::SetProvider(std::shared_ptr provider, + const EvaluationContext& ctx, + bool wait_for_init) { + if (!provider) { + std::cerr << "Provider cannot be null" << std::endl; + return; + } + PrepareAndInitializeProvider(std::nullopt, std::move(provider), ctx, + wait_for_init); +} + +void ProviderRepository::SetProvider(std::string_view domain, + std::shared_ptr provider, + const EvaluationContext& ctx, + bool wait_for_init) { + if (!provider) { + std::cerr << "Provider cannot be null" << std::endl; + return; + } + + if (domain.empty()) { + SetProvider(std::move(provider), ctx, wait_for_init); + return; + } + + PrepareAndInitializeProvider(std::string(domain), std::move(provider), ctx, + wait_for_init); +} + +ProviderStatus ProviderRepository::GetProviderStatus( + std::string_view domain) const { + std::shared_ptr manager = + GetFeatureProviderStatusManager(domain); + if (manager) { + return manager->GetStatus(); + } + return ProviderStatus::kNotReady; +} + +void ProviderRepository::Shutdown() { + { + std::lock_guard lock(threads_mutex_); + for (std::thread& thread : initialization_threads_) { + if (thread.joinable()) { + thread.join(); + } + } + initialization_threads_.clear(); + } + + std::unique_lock lock(repo_mutex_); + + if (default_manager_) { + default_manager_->Shutdown(); + default_manager_.reset(); + } + + for (std::pair>& pair : + provider_manager_) { + // Check if the provider is still active before shutting down. + if (pair.second->GetStatus() != ProviderStatus::kNotReady) { + pair.second->Shutdown(); + } + } + provider_manager_.clear(); +} + +void ProviderRepository::PrepareAndInitializeProvider( + const std::optional domain, + std::shared_ptr new_provider, const EvaluationContext& ctx, + bool wait_for_init) { + std::shared_ptr new_status_manager; + std::shared_ptr old_status_manager; + + { // Scoping for the unique_lock. + std::unique_lock lock(repo_mutex_); + + std::shared_ptr existing_manager = + GetExistingStatusManagerForProvider(new_provider); + if (!existing_manager) { + absl::StatusOr> manager = + FeatureProviderStatusManager::Create(new_provider); + if (!manager.ok()) { + std::cerr << "Failed to create FeatureProviderStatusManager: " + << manager.status() << std::endl; + return; + } + new_status_manager = std::move(manager.value()); + } else { + new_status_manager = existing_manager; + } + + if (domain) { + // Setting a named provider. + auto it = provider_manager_.find(domain.value()); + if (it != provider_manager_.end()) { + old_status_manager = it->second; + } + provider_manager_[domain.value()] = new_status_manager; + } else { + // Setting the default provider. + old_status_manager = default_manager_; + default_manager_ = new_status_manager; + } + } // Release the lock before running initialization logic. + + if (wait_for_init) { + InitializeProvider(new_status_manager, old_status_manager, ctx); + } else { + std::lock_guard lock(threads_mutex_); + initialization_threads_.emplace_back( + [this, new_status_manager, old_status_manager, ctx] { + InitializeProvider(new_status_manager, old_status_manager, ctx); + }); + } +} + +void ProviderRepository::InitializeProvider( + std::shared_ptr new_status_manager, + std::shared_ptr old_status_manager, + const EvaluationContext& ctx) { + if (new_status_manager->GetStatus() == ProviderStatus::kNotReady) { + new_status_manager->Init(ctx); + } + + if (new_status_manager->GetStatus() == ProviderStatus::kReady) { + ShutdownOldProvider(old_status_manager); + } +} + +void ProviderRepository::ShutdownOldProvider( + std::shared_ptr old_status_manager) { + if (old_status_manager) { + std::shared_lock lock(repo_mutex_); + + if (!IsStatusManagerRegistered(old_status_manager)) { + old_status_manager->Shutdown(); + } + } +} + +std::shared_ptr +ProviderRepository::GetExistingStatusManagerForProvider( + const std::shared_ptr& provider) { + if (default_manager_ && default_manager_->GetProvider() == provider) { + return default_manager_; + } + + for (const auto& pair : provider_manager_) { + if (pair.second && pair.second->GetProvider() == provider) { + return pair.second; + } + } + return nullptr; +} + +bool ProviderRepository::IsStatusManagerRegistered( + const std::shared_ptr& manager) { + if (default_manager_ == manager) { + return true; + } + for (const auto& pair : provider_manager_) { + if (pair.second == manager) { + return true; + } + } + return false; +} + +} // namespace openfeature diff --git a/openfeature/provider_repository.h b/openfeature/provider_repository.h new file mode 100644 index 0000000..6db23d4 --- /dev/null +++ b/openfeature/provider_repository.h @@ -0,0 +1,96 @@ +#ifndef CPP_SDK_INCLUDE_OPENFEATURE_PROVIDER_REPOSITORY_H_ +#define CPP_SDK_INCLUDE_OPENFEATURE_PROVIDER_REPOSITORY_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "absl/status/status.h" +#include "openfeature/evaluation_context.h" +#include "openfeature/feature_provider_status_manager.h" +#include "openfeature/provider.h" +#include "openfeature/provider_status.h" + +namespace openfeature { + +// ProviderRepository holds a default provider and allows for registering named +// providers associated with specific domains. The repository is responsible for +// the initialization and shutdown of providers and provides thread-safe access +// to them. +class ProviderRepository { + public: + ProviderRepository(); + ~ProviderRepository(); + + ProviderRepository(const ProviderRepository&) = delete; + ProviderRepository& operator=(const ProviderRepository&) = delete; + + // If the domain is empty, fetch status manager for the default provider. + // Otherwise, fetch the status manager for a specific provider. + std::shared_ptr GetFeatureProviderStatusManager( + std::string_view domain = "") const; + + // If the domain is empty then GetProvider returns the default + // FeatureProvider. Otherwise it returns the FeatureProvider for the + // domain. + std::shared_ptr GetProvider( + std::string_view domain = "") const; + + // Set the default provider. + void SetProvider(std::shared_ptr provider, + const EvaluationContext& ctx, bool waitForInit); + + // Add a provider for a domain. + void SetProvider(std::string_view domain, + std::shared_ptr provider, + const EvaluationContext& ctx, bool waitForInit); + + // Fetch the status of a provider for a domain. + // If the domain is not set, return the default provider status. + // If not found, return the default. + ProviderStatus GetProviderStatus(std::string_view domain = "") const; + + // Wait for any pending initialization threads to complete before shutting + // down providers. Afterwards, shuts down all registered providers and + // clears the repository. + void Shutdown(); + + private: + void PrepareAndInitializeProvider( + const std::optional domain, + std::shared_ptr new_provider, + const EvaluationContext& ctx, bool waitForInit); + + void InitializeProvider( + std::shared_ptr new_status_manager, + std::shared_ptr old_status_manager, + const EvaluationContext& ctx); + + void ShutdownOldProvider( + std::shared_ptr old_status_manager); + + std::shared_ptr + GetExistingStatusManagerForProvider( + const std::shared_ptr& provider); + + bool IsStatusManagerRegistered( + const std::shared_ptr& manager); + + std::unordered_map> + provider_manager_; + std::shared_ptr default_manager_; + mutable std::shared_mutex repo_mutex_; + + std::vector initialization_threads_; + std::mutex threads_mutex_; +}; + +} // namespace openfeature + +#endif // CPP_SDK_INCLUDE_OPENFEATURE_PROVIDER_REPOSITORY_H_ diff --git a/test/BUILD b/test/BUILD index 9f96a74..bddb116 100644 --- a/test/BUILD +++ b/test/BUILD @@ -36,3 +36,13 @@ cc_test( "@googletest//:gtest_main", ], ) + +cc_test( + name = "provider_repository_test", + srcs = ["provider_repository_test.cpp"], + deps = [ + ":mock_feature_provider", + "//openfeature:provider_repository", + "@googletest//:gtest_main", + ], +) diff --git a/test/provider_repository_test.cpp b/test/provider_repository_test.cpp new file mode 100644 index 0000000..4f960f4 --- /dev/null +++ b/test/provider_repository_test.cpp @@ -0,0 +1,367 @@ +#include "openfeature/provider_repository.h" + +#include +#include + +#include +#include + +#include "mocks/mock_feature_provider.h" +#include "openfeature/noop_provider.h" + +using namespace openfeature; +using ::testing::_; +using ::testing::InSequence; +using ::testing::Return; + +class ProviderRepositoryTest : public ::testing::Test { + protected: + ProviderRepository repo; + EvaluationContext ctx; +}; + +// Test to verify the constructor initializes with a NoopProvider. +TEST_F(ProviderRepositoryTest, ConstructorInitializesWithNoopProvider) { + std::shared_ptr provider = repo.GetProvider(); + ASSERT_NE(provider, nullptr); + EXPECT_NE(dynamic_cast(provider.get()), nullptr); + EXPECT_EQ(repo.GetProvider()->GetMetadata().name, "Noop Provider"); + EXPECT_EQ(repo.GetProviderStatus(), ProviderStatus::kReady); +} + +// Test to verify that GetFeatureProviderStatusManager returns the correct +// manager. +TEST_F(ProviderRepositoryTest, + GetFeatureProviderStatusManagerReturnsCorrectManager) { + // On initialization, it should return the default manager. + std::shared_ptr initial_default_manager = + repo.GetFeatureProviderStatusManager(); + ASSERT_NE(initial_default_manager, nullptr); + + // Asking for a non-existent domain should return the default manager. + std::shared_ptr non_existent_manager = + repo.GetFeatureProviderStatusManager("non-existent"); + EXPECT_EQ(non_existent_manager, initial_default_manager); + + // After setting a named provider, it should return the new manager. + std::shared_ptr mock_provider = + std::make_shared(); + std::string domain = "my-domain"; + EXPECT_CALL(*mock_provider, Init(_)).WillOnce(Return(absl::OkStatus())); + repo.SetProvider(domain, mock_provider, ctx, true); + + std::shared_ptr named_manager = + repo.GetFeatureProviderStatusManager(domain); + ASSERT_NE(named_manager, nullptr); + EXPECT_NE(named_manager, initial_default_manager); + EXPECT_EQ(named_manager->GetProvider(), mock_provider); + + // Getting the default manager should still return the original one. + std::shared_ptr current_default_manager = + repo.GetFeatureProviderStatusManager(); + EXPECT_EQ(current_default_manager, initial_default_manager); +} + +// Test to verify GetProvider returns the default when the domain is empty. +TEST_F(ProviderRepositoryTest, GetProviderReturnsDefaultWhenDomainIsEmpty) { + std::shared_ptr default_provider = repo.GetProvider(); + std::shared_ptr empty_domain_provider = repo.GetProvider(""); + EXPECT_EQ(default_provider, empty_domain_provider); +} + +// Test to verify GetProvider returns the default when the domain is not found. +TEST_F(ProviderRepositoryTest, GetProviderReturnsDefaultWhenDomainNotFound) { + std::shared_ptr default_provider = repo.GetProvider(); + std::shared_ptr not_found_provider = + repo.GetProvider("non-existent-domain"); + EXPECT_EQ(default_provider, not_found_provider); +} + +// Test to verify setting and retrieving a named provider. +TEST_F(ProviderRepositoryTest, SetAndGetNamedProvider) { + std::shared_ptr mock_provider = + std::make_shared(); + std::string domain = "my-domain"; + + EXPECT_CALL(*mock_provider, Init(_)).WillOnce(Return(absl::OkStatus())); + + repo.SetProvider(domain, mock_provider, ctx, true); + + EXPECT_EQ(repo.GetProvider(domain), mock_provider); + EXPECT_NE(dynamic_cast(repo.GetProvider().get()), nullptr); +} + +// Test to verify that setting the default provider replaces the NoopProvider. +TEST_F(ProviderRepositoryTest, SetDefaultProviderReplacesNoop) { + std::shared_ptr mock_provider = + std::make_shared(); + + EXPECT_CALL(*mock_provider, Init(_)).WillOnce(Return(absl::OkStatus())); + + repo.SetProvider(mock_provider, ctx, true); + + EXPECT_EQ(repo.GetProvider(), mock_provider); +} + +// Test that setting a null provider for the default does not change the state. +TEST_F(ProviderRepositoryTest, SetProviderWithNullProviderDoesNothing) { + std::shared_ptr initial_provider = repo.GetProvider(); + ProviderStatus initial_status = repo.GetProviderStatus(); + testing::internal::CaptureStderr(); + + repo.SetProvider(nullptr, ctx, true); + std::string output = testing::internal::GetCapturedStderr(); + + EXPECT_THAT(output, testing::HasSubstr("Provider cannot be null")); + EXPECT_EQ(repo.GetProvider(), initial_provider); + EXPECT_EQ(repo.GetProviderStatus(), initial_status); +} + +// Test that setting a null provider for a named domain does not change the +// state. +TEST_F(ProviderRepositoryTest, SetNamedProviderWithNullProviderDoesNothing) { + const std::string domain = "my-domain"; + std::shared_ptr initial_provider_for_domain = + repo.GetProvider(domain); + ProviderStatus initial_status = repo.GetProviderStatus(domain); + + testing::internal::CaptureStderr(); + + repo.SetProvider(domain, nullptr, ctx, true); + std::string output = testing::internal::GetCapturedStderr(); + + EXPECT_THAT(output, testing::HasSubstr("Provider cannot be null")); + EXPECT_EQ(repo.GetProvider(domain), initial_provider_for_domain); + EXPECT_EQ(repo.GetProviderStatus(domain), initial_status); +} + +// Test to verify that SetProvider waits for initialization to complete. +TEST_F(ProviderRepositoryTest, SetProviderWaitsForInitialization) { + std::shared_ptr mock_provider = + std::make_shared(); + + EXPECT_CALL(*mock_provider, Init(_)).WillOnce(Return(absl::OkStatus())); + + repo.SetProvider(mock_provider, ctx, true); + + EXPECT_EQ(repo.GetProviderStatus(), ProviderStatus::kReady); +} + +// Test to verify that SetProvider initializes asynchronously. +TEST_F(ProviderRepositoryTest, SetProviderDoesNotWaitForInitialization) { + std::shared_ptr mock_provider = + std::make_shared(); + + std::promise init_can_complete; + std::future init_future = init_can_complete.get_future(); + + EXPECT_CALL(*mock_provider, Init(_)).WillOnce([&init_future](const auto&) { + init_future.get(); + return absl::OkStatus(); + }); + + repo.SetProvider(mock_provider, ctx, false); + + EXPECT_EQ(repo.GetProviderStatus(), ProviderStatus::kNotReady); + + init_can_complete.set_value(); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + + EXPECT_EQ(repo.GetProviderStatus(), ProviderStatus::kReady); +} + +// Test to verify that a failed initialization sets the status to kError. +TEST_F(ProviderRepositoryTest, SetProviderWithFailedInitSetsErrorStatus) { + std::shared_ptr mock_provider = + std::make_shared(); + + EXPECT_CALL(*mock_provider, Init(_)) + .WillOnce(Return(absl::InternalError("Init failed"))); + + repo.SetProvider(mock_provider, ctx, true); + + EXPECT_EQ(repo.GetProviderStatus(), ProviderStatus::kError); +} + +// Test that getting status after shutdown does not crash and returns a safe +// value. +TEST_F(ProviderRepositoryTest, GetProviderStatusAfterShutdownReturnsNotReady) { + ASSERT_NE(repo.GetProvider(), nullptr); + ASSERT_EQ(repo.GetProviderStatus(), ProviderStatus::kReady); + + repo.Shutdown(); + + EXPECT_EQ(repo.GetProviderStatus(), ProviderStatus::kNotReady); +} + +// Test to verify the old provider is shutdown after a new one is ready. +TEST_F(ProviderRepositoryTest, OldProviderIsShutdownAfterNewOneIsReady) { + std::shared_ptr mock_provider1 = + std::make_shared(); + std::shared_ptr mock_provider2 = + std::make_shared(); + + EXPECT_CALL(*mock_provider1, Init(_)).WillOnce(Return(absl::OkStatus())); + repo.SetProvider(mock_provider1, ctx, true); + EXPECT_EQ(repo.GetProvider(), mock_provider1); + ASSERT_EQ(repo.GetProviderStatus(), ProviderStatus::kReady); + + { + InSequence seq; + EXPECT_CALL(*mock_provider2, Init(_)).WillOnce(Return(absl::OkStatus())); + EXPECT_CALL(*mock_provider1, Shutdown()).WillOnce(Return(absl::OkStatus())); + } + + repo.SetProvider(mock_provider2, ctx, true); + EXPECT_EQ(repo.GetProvider(), mock_provider2); + EXPECT_EQ(repo.GetProviderStatus(), ProviderStatus::kReady); +} + +// Test to verify the old provider is not shut down if the new one fails to +// init. +TEST_F(ProviderRepositoryTest, OldProviderIsNotShutdownIfNewOneFailsToInit) { + std::shared_ptr mock_provider1 = + std::make_shared(); + std::shared_ptr mock_provider2 = + std::make_shared(); + + EXPECT_CALL(*mock_provider1, Init(_)).WillOnce(Return(absl::OkStatus())); + repo.SetProvider(mock_provider1, ctx, true); + + EXPECT_CALL(*mock_provider2, Init(_)) + .WillOnce(Return(absl::InternalError("Init failed"))); + EXPECT_CALL(*mock_provider1, Shutdown()).Times(0); + + repo.SetProvider(mock_provider2, ctx, true); + + // The new provider should be set, but in an error state. + EXPECT_EQ(repo.GetProvider(), mock_provider2); + EXPECT_EQ(repo.GetProviderStatus(), ProviderStatus::kError); +} + +// Test to verify Shutdown calls Shutdown on all registered providers. +TEST_F(ProviderRepositoryTest, ShutdownAllProviders) { + std::shared_ptr mock_default = + std::make_shared(); + std::shared_ptr mock_named_1 = + std::make_shared(); + std::string domain_1 = "my-first-domain"; + std::shared_ptr mock_named_2 = + std::make_shared(); + std::string domain_2 = "my-second-domain"; + std::string domain_3 = "my-third-domain"; + + EXPECT_CALL(*mock_default, Init(_)).WillOnce(Return(absl::OkStatus())); + EXPECT_CALL(*mock_named_1, Init(_)).WillOnce(Return(absl::OkStatus())); + EXPECT_CALL(*mock_named_2, Init(_)).WillOnce(Return(absl::OkStatus())); + + repo.SetProvider(mock_default, ctx, true); + repo.SetProvider(domain_1, mock_named_1, ctx, true); + repo.SetProvider(domain_2, mock_named_2, ctx, true); + repo.SetProvider(domain_3, mock_named_2, ctx, true); + + EXPECT_CALL(*mock_default, Shutdown()).WillOnce(Return(absl::OkStatus())); + EXPECT_CALL(*mock_named_1, Shutdown()).WillOnce(Return(absl::OkStatus())); + EXPECT_CALL(*mock_named_2, Shutdown()).WillOnce(Return(absl::OkStatus())); + + repo.Shutdown(); + EXPECT_EQ(repo.GetFeatureProviderStatusManager(), nullptr); +} + +// Test to verify that Shutdown waits for asynchronous initialization to finish. +TEST_F(ProviderRepositoryTest, ShutdownWaitsForAsyncInitializationToComplete) { + std::shared_ptr mock_provider = + std::make_shared(); + + std::promise init_started_promise; + std::future init_started_future = init_started_promise.get_future(); + + std::promise init_can_complete_promise; + std::future init_can_complete_future = + init_can_complete_promise.get_future(); + + // Mock the Init() call. + EXPECT_CALL(*mock_provider, Init(_)).WillOnce([&](const auto&) { + init_started_promise.set_value(); + init_can_complete_future.get(); + return absl::OkStatus(); + }); + + EXPECT_CALL(*mock_provider, Shutdown()).WillOnce(Return(absl::OkStatus())); + + repo.SetProvider(mock_provider, ctx, false); + + // Wait until the background thread is confirmed to be inside the Init() + // method. + init_started_future.wait(); + + // Run Shutdown() to verify that it blocks until init is allowed to complete. + auto shutdown_future = + std::async(std::launch::async, [&]() { repo.Shutdown(); }); + + // We expect it to time out because the Init() is still blocked. + auto status = shutdown_future.wait_for(std::chrono::milliseconds(100)); + ASSERT_EQ(status, std::future_status::timeout) + << "Shutdown() did not wait for initialization to complete."; + + // Unblock Init(). + init_can_complete_promise.set_value(); + + // Shutdown should complete promptly. + shutdown_future.get(); + + SUCCEED() << "Shutdown() correctly waited for the async task."; +} + +// Test that setting an existing provider in a new location reuses the manager. +TEST_F(ProviderRepositoryTest, SetExistingProviderReusesManager) { + std::shared_ptr mock_provider = + std::make_shared(); + std::string domain = "my-domain"; + + EXPECT_CALL(*mock_provider, Init(_)).WillOnce(Return(absl::OkStatus())); + + repo.SetProvider(mock_provider, ctx, true); + std::shared_ptr default_manager = + repo.GetFeatureProviderStatusManager(); + + repo.SetProvider(domain, mock_provider, ctx, true); + std::shared_ptr named_manager = + repo.GetFeatureProviderStatusManager(domain); + + EXPECT_EQ(default_manager, named_manager); +} + +// Test that replacing a provider does not shut it down if it's still in use. +TEST_F(ProviderRepositoryTest, ReplacingProviderDoesNotShutdownIfStillInUse) { + std::shared_ptr mock_provider1 = + std::make_shared(); + std::shared_ptr mock_provider2 = + std::make_shared(); + std::string domain_a = "domain-A"; + + EXPECT_CALL(*mock_provider1, Init(_)).WillOnce(Return(absl::OkStatus())); + EXPECT_CALL(*mock_provider2, Init(_)).WillOnce(Return(absl::OkStatus())); + + repo.SetProvider(mock_provider1, ctx, true); + repo.SetProvider(domain_a, mock_provider1, ctx, true); + + std::shared_ptr manager_for_provider1 = + repo.GetFeatureProviderStatusManager(); + ASSERT_EQ(manager_for_provider1->GetProvider(), mock_provider1); + + EXPECT_CALL(*mock_provider1, Shutdown()).Times(0); + repo.SetProvider(mock_provider2, ctx, true); + + EXPECT_EQ(manager_for_provider1->GetStatus(), ProviderStatus::kReady); + + EXPECT_CALL(*mock_provider1, Shutdown()).WillOnce(Return(absl::OkStatus())); + repo.SetProvider(domain_a, mock_provider2, ctx, true); + + EXPECT_EQ(manager_for_provider1->GetStatus(), ProviderStatus::kNotReady); +} + +int main(int argc, char** argv) { + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +}