| /* |
| * 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 <memory> |
| #include <optional> |
| #include <string> |
| #include <utility> |
| #include <vector> |
| |
| #include "absl/functional/any_invocable.h" |
| #include "absl/strings/string_view.h" |
| #include "api/environment/environment_factory.h" |
| #include "api/field_trials_view.h" |
| #include "api/rtc_event_log/rtc_event.h" |
| #include "api/rtc_event_log/rtc_event_log.h" |
| #include "api/task_queue/task_queue_base.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<void() &&> 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<void() &&> on_destroyed_; |
| }; |
| |
| class FakeTaskQueueFactory : public TaskQueueFactory { |
| public: |
| explicit FakeTaskQueueFactory( |
| absl::AnyInvocable<void() &&> on_destroyed = nullptr) |
| : on_destroyed_(std::move(on_destroyed)) {} |
| ~FakeTaskQueueFactory() override { |
| if (on_destroyed_ != nullptr) { |
| std::move(on_destroyed_)(); |
| } |
| } |
| |
| std::unique_ptr<TaskQueueBase, TaskQueueDeleter> CreateTaskQueue( |
| absl::string_view name, |
| Priority priority) const override { |
| return nullptr; |
| } |
| |
| private: |
| absl::AnyInvocable<void() &&> 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<FakeEvent>()); |
| env.field_trials().Lookup("WebRTC-Debugging-RtpDump"); |
| } |
| |
| TEST(EnvironmentTest, UsesProvidedUtilitiesWithOwnership) { |
| auto owned_field_trials = std::make_unique<FakeFieldTrials>(); |
| auto owned_task_queue_factory = std::make_unique<FakeTaskQueueFactory>(); |
| auto owned_clock = std::make_unique<SimulatedClock>(Timestamp::Zero()); |
| auto owned_event_log = std::make_unique<RtcEventLogNull>(); |
| |
| 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<FakeFieldTrials>(); |
| auto owned_field_trials2 = std::make_unique<FakeFieldTrials>(); |
| 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<FakeFieldTrials>(); |
| std::unique_ptr<FieldTrialsView> 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<FakeFieldTrials>( |
| /*on_destroyed=*/[&] { utility_destroyed = true; }); |
| |
| // Wrap Environment into optional to have explicit control when it is deleted. |
| std::optional<Environment> env = CreateEnvironment(std::move(field_trials)); |
| |
| EXPECT_FALSE(utility_destroyed); |
| env = std::nullopt; |
| EXPECT_TRUE(utility_destroyed); |
| } |
| |
| TEST(EnvironmentTest, KeepsUtilityAliveWhileCopyOfEnvironmentIsAlive) { |
| bool utility_destroyed = false; |
| auto field_trials = std::make_unique<FakeFieldTrials>( |
| /*on_destroyed=*/[&] { utility_destroyed = true; }); |
| |
| std::optional<Environment> env1 = CreateEnvironment(std::move(field_trials)); |
| std::optional<Environment> env2 = env1; |
| |
| EXPECT_FALSE(utility_destroyed); |
| env1 = std::nullopt; |
| EXPECT_FALSE(utility_destroyed); |
| env2 = std::nullopt; |
| EXPECT_TRUE(utility_destroyed); |
| } |
| |
| TEST(EnvironmentTest, FactoryCanBeReusedToCreateDifferentEnvironments) { |
| auto owned_task_queue_factory = std::make_unique<FakeTaskQueueFactory>(); |
| auto owned_field_trials1 = std::make_unique<FakeFieldTrials>(); |
| auto owned_field_trials2 = std::make_unique<FakeFieldTrials>(); |
| 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<FakeTaskQueueFactory>()); |
| EnvironmentFactory factory(env1); |
| factory.Set(std::make_unique<FakeFieldTrials>()); |
| 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; |
| std::optional<Environment> env1 = |
| CreateEnvironment(std::make_unique<FakeTaskQueueFactory>( |
| /*on_destroyed=*/[&] { utility1_destroyed = true; })); |
| |
| std::optional<EnvironmentFactory> factory = EnvironmentFactory(*env1); |
| |
| // Destroy env1, check utility1 it was using is still alive. |
| env1 = std::nullopt; |
| EXPECT_FALSE(utility1_destroyed); |
| |
| factory->Set(std::make_unique<FakeFieldTrials>( |
| /*on_destroyed=*/[&] { utility2_destroyed = true; })); |
| std::optional<Environment> env2 = factory->Create(); |
| |
| // Destroy the factory, check all utilities used by env2 are alive. |
| factory = std::nullopt; |
| EXPECT_FALSE(utility1_destroyed); |
| EXPECT_FALSE(utility2_destroyed); |
| |
| // Once last Environment object is deleted, utilties should be deleted too. |
| env2 = std::nullopt; |
| EXPECT_TRUE(utility1_destroyed); |
| EXPECT_TRUE(utility2_destroyed); |
| } |
| |
| TEST(EnvironmentTest, DestroysUtilitiesInReverseProvidedOrder) { |
| std::vector<std::string> destroyed; |
| auto field_trials = std::make_unique<FakeFieldTrials>( |
| /*on_destroyed=*/[&] { destroyed.push_back("field_trials"); }); |
| auto task_queue_factory = std::make_unique<FakeTaskQueueFactory>( |
| /*on_destroyed=*/[&] { destroyed.push_back("task_queue_factory"); }); |
| |
| std::optional<Environment> env = |
| CreateEnvironment(std::move(field_trials), std::move(task_queue_factory)); |
| |
| ASSERT_THAT(destroyed, IsEmpty()); |
| env = std::nullopt; |
| EXPECT_THAT(destroyed, ElementsAre("task_queue_factory", "field_trials")); |
| } |
| |
| } // namespace |
| } // namespace webrtc |