/* * Copyright (c) 2023 The WebRTC project authors. All Rights Reserved. * * Use of this source code is governed by a BSD-style license * that can be found in the LICENSE file in the root of the source * tree. An additional intellectual property rights grant can be found * in the file PATENTS. All contributing project authors may * be found in the AUTHORS file in the root of the source tree. */ #include "api/environment/environment.h" #include #include #include #include #include "absl/functional/any_invocable.h" #include "absl/types/optional.h" #include "api/environment/environment_factory.h" #include "api/field_trials_view.h" #include "api/rtc_event_log/rtc_event_log.h" #include "api/task_queue/task_queue_factory.h" #include "api/units/timestamp.h" #include "system_wrappers/include/clock.h" #include "test/gmock.h" #include "test/gtest.h" namespace webrtc { namespace { using ::testing::ElementsAre; using ::testing::IsEmpty; using ::testing::Not; using ::testing::NotNull; using ::testing::Ref; class FakeEvent : public RtcEvent { public: Type GetType() const override { return RtcEvent::Type::FakeEvent; } bool IsConfigEvent() const override { return false; } }; class FakeFieldTrials : public FieldTrialsView { public: explicit FakeFieldTrials(absl::AnyInvocable on_destroyed = nullptr) : on_destroyed_(std::move(on_destroyed)) {} ~FakeFieldTrials() override { if (on_destroyed_ != nullptr) { std::move(on_destroyed_)(); } } std::string Lookup(absl::string_view key) const override { return "fake"; } private: absl::AnyInvocable on_destroyed_; }; class FakeTaskQueueFactory : public TaskQueueFactory { public: explicit FakeTaskQueueFactory( absl::AnyInvocable on_destroyed = nullptr) : on_destroyed_(std::move(on_destroyed)) {} ~FakeTaskQueueFactory() override { if (on_destroyed_ != nullptr) { std::move(on_destroyed_)(); } } std::unique_ptr CreateTaskQueue( absl::string_view name, Priority priority) const override { return nullptr; } private: absl::AnyInvocable on_destroyed_; }; TEST(EnvironmentTest, DefaultEnvironmentHasAllUtilities) { Environment env = EnvironmentFactory().Create(); // Try to use each utility, expect no crashes. env.clock().CurrentTime(); EXPECT_THAT(env.task_queue_factory().CreateTaskQueue( "test", TaskQueueFactory::Priority::NORMAL), NotNull()); env.event_log().Log(std::make_unique()); env.field_trials().Lookup("WebRTC-Debugging-RtpDump"); } TEST(EnvironmentTest, UsesProvidedUtilitiesWithOwnership) { auto owned_field_trials = std::make_unique(); auto owned_task_queue_factory = std::make_unique(); auto owned_clock = std::make_unique(Timestamp::Zero()); auto owned_event_log = std::make_unique(); FieldTrialsView& field_trials = *owned_field_trials; TaskQueueFactory& task_queue_factory = *owned_task_queue_factory; Clock& clock = *owned_clock; RtcEventLog& event_log = *owned_event_log; Environment env = CreateEnvironment( std::move(owned_field_trials), std::move(owned_clock), std::move(owned_task_queue_factory), std::move(owned_event_log)); EXPECT_THAT(env.field_trials(), Ref(field_trials)); EXPECT_THAT(env.task_queue_factory(), Ref(task_queue_factory)); EXPECT_THAT(env.clock(), Ref(clock)); EXPECT_THAT(env.event_log(), Ref(event_log)); } TEST(EnvironmentTest, UsesProvidedUtilitiesWithoutOwnership) { FakeFieldTrials field_trials; FakeTaskQueueFactory task_queue_factory; SimulatedClock clock(Timestamp::Zero()); RtcEventLogNull event_log; Environment env = CreateEnvironment(&field_trials, &clock, &task_queue_factory, &event_log); EXPECT_THAT(env.field_trials(), Ref(field_trials)); EXPECT_THAT(env.task_queue_factory(), Ref(task_queue_factory)); EXPECT_THAT(env.clock(), Ref(clock)); EXPECT_THAT(env.event_log(), Ref(event_log)); } TEST(EnvironmentTest, UsesLastProvidedUtility) { auto owned_field_trials1 = std::make_unique(); auto owned_field_trials2 = std::make_unique(); FieldTrialsView& field_trials2 = *owned_field_trials2; Environment env = CreateEnvironment(std::move(owned_field_trials1), std::move(owned_field_trials2)); EXPECT_THAT(env.field_trials(), Ref(field_trials2)); } // Utilities can be provided from different sources, and when some source // choose not to provide an utility, it is usually expressed with nullptr. // When utility is not provided, it is natural to use previously set one. // E.g. Both PeerConnectionFactoryDependencies and PeerConnectionDependencies // provide field trials. When PeerConnectionDependencies::trials == nullptr, // then trials from the PeerConnectionFactoryDependencies should be used. // With nullptr accepted and ignored this can be expressed by // `Environemt env = CreateEnvironment(pcf_deps.trials, pc_deps.trials);` // That would use pc_deps.trials when not nullptr, pcf_deps.trials when // pc_deps.trials is nullptr, but pcf_deps.trials is not, and default field // trials when both are nullptr. TEST(EnvironmentTest, IgnoresProvidedNullptrUtility) { auto owned_field_trials = std::make_unique(); std::unique_ptr null_field_trials = nullptr; FieldTrialsView& field_trials = *owned_field_trials; Environment env = CreateEnvironment(std::move(owned_field_trials), std::move(null_field_trials)); EXPECT_THAT(env.field_trials(), Ref(field_trials)); } TEST(EnvironmentTest, KeepsUtilityAliveWhileEnvironmentIsAlive) { bool utility_destroyed = false; auto field_trials = std::make_unique( /*on_destroyed=*/[&] { utility_destroyed = true; }); // Wrap Environment into optional to have explicit control when it is deleted. absl::optional env = CreateEnvironment(std::move(field_trials)); EXPECT_FALSE(utility_destroyed); env = absl::nullopt; EXPECT_TRUE(utility_destroyed); } TEST(EnvironmentTest, KeepsUtilityAliveWhileCopyOfEnvironmentIsAlive) { bool utility_destroyed = false; auto field_trials = std::make_unique( /*on_destroyed=*/[&] { utility_destroyed = true; }); absl::optional env1 = CreateEnvironment(std::move(field_trials)); absl::optional env2 = env1; EXPECT_FALSE(utility_destroyed); env1 = absl::nullopt; EXPECT_FALSE(utility_destroyed); env2 = absl::nullopt; EXPECT_TRUE(utility_destroyed); } TEST(EnvironmentTest, FactoryCanBeReusedToCreateDifferentEnvironments) { auto owned_task_queue_factory = std::make_unique(); auto owned_field_trials1 = std::make_unique(); auto owned_field_trials2 = std::make_unique(); TaskQueueFactory& task_queue_factory = *owned_task_queue_factory; FieldTrialsView& field_trials1 = *owned_field_trials1; FieldTrialsView& field_trials2 = *owned_field_trials2; EnvironmentFactory factory; factory.Set(std::move(owned_task_queue_factory)); factory.Set(std::move(owned_field_trials1)); Environment env1 = factory.Create(); factory.Set(std::move(owned_field_trials2)); Environment env2 = factory.Create(); // Environments share the same custom task queue factory. EXPECT_THAT(env1.task_queue_factory(), Ref(task_queue_factory)); EXPECT_THAT(env2.task_queue_factory(), Ref(task_queue_factory)); // Environments have different field trials. EXPECT_THAT(env1.field_trials(), Ref(field_trials1)); EXPECT_THAT(env2.field_trials(), Ref(field_trials2)); } TEST(EnvironmentTest, FactoryCanCreateNewEnvironmentFromExistingOne) { Environment env1 = CreateEnvironment(std::make_unique()); EnvironmentFactory factory(env1); factory.Set(std::make_unique()); Environment env2 = factory.Create(); // Environments share the same default clock. EXPECT_THAT(env2.clock(), Ref(env1.clock())); // Environments share the same custom task queue factory. EXPECT_THAT(env2.task_queue_factory(), Ref(env1.task_queue_factory())); // Environments have different field trials. EXPECT_THAT(env2.field_trials(), Not(Ref(env1.field_trials()))); } TEST(EnvironmentTest, KeepsOwnershipsWhenCreateNewEnvironmentFromExistingOne) { bool utility1_destroyed = false; bool utility2_destroyed = false; absl::optional env1 = CreateEnvironment(std::make_unique( /*on_destroyed=*/[&] { utility1_destroyed = true; })); absl::optional factory = EnvironmentFactory(*env1); // Destroy env1, check utility1 it was using is still alive. env1 = absl::nullopt; EXPECT_FALSE(utility1_destroyed); factory->Set(std::make_unique( /*on_destroyed=*/[&] { utility2_destroyed = true; })); absl::optional env2 = factory->Create(); // Destroy the factory, check all utilities used by env2 are alive. factory = absl::nullopt; EXPECT_FALSE(utility1_destroyed); EXPECT_FALSE(utility2_destroyed); // Once last Environment object is deleted, utilties should be deleted too. env2 = absl::nullopt; EXPECT_TRUE(utility1_destroyed); EXPECT_TRUE(utility2_destroyed); } TEST(EnvironmentTest, DestroysUtilitiesInReverseProvidedOrder) { std::vector destroyed; auto field_trials = std::make_unique( /*on_destroyed=*/[&] { destroyed.push_back("field_trials"); }); auto task_queue_factory = std::make_unique( /*on_destroyed=*/[&] { destroyed.push_back("task_queue_factory"); }); absl::optional env = CreateEnvironment(std::move(field_trials), std::move(task_queue_factory)); ASSERT_THAT(destroyed, IsEmpty()); env = absl::nullopt; EXPECT_THAT(destroyed, ElementsAre("task_queue_factory", "field_trials")); } } // namespace } // namespace webrtc