Danil Chapovalov | e7b48a1 | 2023-11-22 15:01:34 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (c) 2023 The WebRTC project authors. All Rights Reserved. |
| 3 | * |
| 4 | * Use of this source code is governed by a BSD-style license |
| 5 | * that can be found in the LICENSE file in the root of the source |
| 6 | * tree. An additional intellectual property rights grant can be found |
| 7 | * in the file PATENTS. All contributing project authors may |
| 8 | * be found in the AUTHORS file in the root of the source tree. |
| 9 | */ |
| 10 | |
| 11 | #include "api/environment/environment.h" |
| 12 | |
| 13 | #include <memory> |
| 14 | #include <string> |
| 15 | #include <utility> |
| 16 | #include <vector> |
| 17 | |
| 18 | #include "absl/functional/any_invocable.h" |
| 19 | #include "absl/types/optional.h" |
| 20 | #include "api/environment/environment_factory.h" |
| 21 | #include "api/field_trials_view.h" |
| 22 | #include "api/rtc_event_log/rtc_event_log.h" |
| 23 | #include "api/task_queue/task_queue_factory.h" |
| 24 | #include "api/units/timestamp.h" |
| 25 | #include "system_wrappers/include/clock.h" |
| 26 | #include "test/gmock.h" |
| 27 | #include "test/gtest.h" |
| 28 | |
| 29 | namespace webrtc { |
| 30 | namespace { |
| 31 | |
| 32 | using ::testing::ElementsAre; |
| 33 | using ::testing::IsEmpty; |
| 34 | using ::testing::Not; |
| 35 | using ::testing::NotNull; |
| 36 | using ::testing::Ref; |
| 37 | |
| 38 | class FakeEvent : public RtcEvent { |
| 39 | public: |
| 40 | Type GetType() const override { return RtcEvent::Type::FakeEvent; } |
| 41 | bool IsConfigEvent() const override { return false; } |
| 42 | }; |
| 43 | |
| 44 | class FakeFieldTrials : public FieldTrialsView { |
| 45 | public: |
| 46 | explicit FakeFieldTrials(absl::AnyInvocable<void() &&> on_destroyed = nullptr) |
| 47 | : on_destroyed_(std::move(on_destroyed)) {} |
| 48 | ~FakeFieldTrials() override { |
| 49 | if (on_destroyed_ != nullptr) { |
| 50 | std::move(on_destroyed_)(); |
| 51 | } |
| 52 | } |
| 53 | |
| 54 | std::string Lookup(absl::string_view key) const override { return "fake"; } |
| 55 | |
| 56 | private: |
| 57 | absl::AnyInvocable<void() &&> on_destroyed_; |
| 58 | }; |
| 59 | |
| 60 | class FakeTaskQueueFactory : public TaskQueueFactory { |
| 61 | public: |
| 62 | explicit FakeTaskQueueFactory( |
| 63 | absl::AnyInvocable<void() &&> on_destroyed = nullptr) |
| 64 | : on_destroyed_(std::move(on_destroyed)) {} |
| 65 | ~FakeTaskQueueFactory() override { |
| 66 | if (on_destroyed_ != nullptr) { |
| 67 | std::move(on_destroyed_)(); |
| 68 | } |
| 69 | } |
| 70 | |
| 71 | std::unique_ptr<TaskQueueBase, TaskQueueDeleter> CreateTaskQueue( |
| 72 | absl::string_view name, |
| 73 | Priority priority) const override { |
| 74 | return nullptr; |
| 75 | } |
| 76 | |
| 77 | private: |
| 78 | absl::AnyInvocable<void() &&> on_destroyed_; |
| 79 | }; |
| 80 | |
| 81 | TEST(EnvironmentTest, DefaultEnvironmentHasAllUtilities) { |
| 82 | Environment env = EnvironmentFactory().Create(); |
| 83 | |
| 84 | // Try to use each utility, expect no crashes. |
| 85 | env.clock().CurrentTime(); |
| 86 | EXPECT_THAT(env.task_queue_factory().CreateTaskQueue( |
| 87 | "test", TaskQueueFactory::Priority::NORMAL), |
| 88 | NotNull()); |
| 89 | env.event_log().Log(std::make_unique<FakeEvent>()); |
| 90 | env.field_trials().Lookup("WebRTC-Debugging-RtpDump"); |
| 91 | } |
| 92 | |
| 93 | TEST(EnvironmentTest, UsesProvidedUtilitiesWithOwnership) { |
| 94 | auto owned_field_trials = std::make_unique<FakeFieldTrials>(); |
| 95 | auto owned_task_queue_factory = std::make_unique<FakeTaskQueueFactory>(); |
| 96 | auto owned_clock = std::make_unique<SimulatedClock>(Timestamp::Zero()); |
| 97 | auto owned_event_log = std::make_unique<RtcEventLogNull>(); |
| 98 | |
| 99 | FieldTrialsView& field_trials = *owned_field_trials; |
| 100 | TaskQueueFactory& task_queue_factory = *owned_task_queue_factory; |
| 101 | Clock& clock = *owned_clock; |
| 102 | RtcEventLog& event_log = *owned_event_log; |
| 103 | |
| 104 | Environment env = CreateEnvironment( |
| 105 | std::move(owned_field_trials), std::move(owned_clock), |
| 106 | std::move(owned_task_queue_factory), std::move(owned_event_log)); |
| 107 | |
| 108 | EXPECT_THAT(env.field_trials(), Ref(field_trials)); |
| 109 | EXPECT_THAT(env.task_queue_factory(), Ref(task_queue_factory)); |
| 110 | EXPECT_THAT(env.clock(), Ref(clock)); |
| 111 | EXPECT_THAT(env.event_log(), Ref(event_log)); |
| 112 | } |
| 113 | |
| 114 | TEST(EnvironmentTest, UsesProvidedUtilitiesWithoutOwnership) { |
| 115 | FakeFieldTrials field_trials; |
| 116 | FakeTaskQueueFactory task_queue_factory; |
| 117 | SimulatedClock clock(Timestamp::Zero()); |
| 118 | RtcEventLogNull event_log; |
| 119 | |
| 120 | Environment env = |
| 121 | CreateEnvironment(&field_trials, &clock, &task_queue_factory, &event_log); |
| 122 | |
| 123 | EXPECT_THAT(env.field_trials(), Ref(field_trials)); |
| 124 | EXPECT_THAT(env.task_queue_factory(), Ref(task_queue_factory)); |
| 125 | EXPECT_THAT(env.clock(), Ref(clock)); |
| 126 | EXPECT_THAT(env.event_log(), Ref(event_log)); |
| 127 | } |
| 128 | |
| 129 | TEST(EnvironmentTest, UsesLastProvidedUtility) { |
| 130 | auto owned_field_trials1 = std::make_unique<FakeFieldTrials>(); |
| 131 | auto owned_field_trials2 = std::make_unique<FakeFieldTrials>(); |
| 132 | FieldTrialsView& field_trials2 = *owned_field_trials2; |
| 133 | |
| 134 | Environment env = CreateEnvironment(std::move(owned_field_trials1), |
| 135 | std::move(owned_field_trials2)); |
| 136 | |
| 137 | EXPECT_THAT(env.field_trials(), Ref(field_trials2)); |
| 138 | } |
| 139 | |
| 140 | // Utilities can be provided from different sources, and when some source |
| 141 | // choose not to provide an utility, it is usually expressed with nullptr. |
| 142 | // When utility is not provided, it is natural to use previously set one. |
| 143 | // E.g. Both PeerConnectionFactoryDependencies and PeerConnectionDependencies |
| 144 | // provide field trials. When PeerConnectionDependencies::trials == nullptr, |
| 145 | // then trials from the PeerConnectionFactoryDependencies should be used. |
| 146 | // With nullptr accepted and ignored this can be expressed by |
| 147 | // `Environemt env = CreateEnvironment(pcf_deps.trials, pc_deps.trials);` |
| 148 | // That would use pc_deps.trials when not nullptr, pcf_deps.trials when |
| 149 | // pc_deps.trials is nullptr, but pcf_deps.trials is not, and default field |
| 150 | // trials when both are nullptr. |
| 151 | TEST(EnvironmentTest, IgnoresProvidedNullptrUtility) { |
| 152 | auto owned_field_trials = std::make_unique<FakeFieldTrials>(); |
| 153 | std::unique_ptr<FieldTrialsView> null_field_trials = nullptr; |
| 154 | FieldTrialsView& field_trials = *owned_field_trials; |
| 155 | |
| 156 | Environment env = CreateEnvironment(std::move(owned_field_trials), |
| 157 | std::move(null_field_trials)); |
| 158 | |
| 159 | EXPECT_THAT(env.field_trials(), Ref(field_trials)); |
| 160 | } |
| 161 | |
| 162 | TEST(EnvironmentTest, KeepsUtilityAliveWhileEnvironmentIsAlive) { |
| 163 | bool utility_destroyed = false; |
| 164 | auto field_trials = std::make_unique<FakeFieldTrials>( |
| 165 | /*on_destroyed=*/[&] { utility_destroyed = true; }); |
| 166 | |
| 167 | // Wrap Environment into optional to have explicit control when it is deleted. |
| 168 | absl::optional<Environment> env = CreateEnvironment(std::move(field_trials)); |
| 169 | |
| 170 | EXPECT_FALSE(utility_destroyed); |
| 171 | env = absl::nullopt; |
| 172 | EXPECT_TRUE(utility_destroyed); |
| 173 | } |
| 174 | |
| 175 | TEST(EnvironmentTest, KeepsUtilityAliveWhileCopyOfEnvironmentIsAlive) { |
| 176 | bool utility_destroyed = false; |
| 177 | auto field_trials = std::make_unique<FakeFieldTrials>( |
| 178 | /*on_destroyed=*/[&] { utility_destroyed = true; }); |
| 179 | |
| 180 | absl::optional<Environment> env1 = CreateEnvironment(std::move(field_trials)); |
| 181 | absl::optional<Environment> env2 = env1; |
| 182 | |
| 183 | EXPECT_FALSE(utility_destroyed); |
| 184 | env1 = absl::nullopt; |
| 185 | EXPECT_FALSE(utility_destroyed); |
| 186 | env2 = absl::nullopt; |
| 187 | EXPECT_TRUE(utility_destroyed); |
| 188 | } |
| 189 | |
| 190 | TEST(EnvironmentTest, FactoryCanBeReusedToCreateDifferentEnvironments) { |
| 191 | auto owned_task_queue_factory = std::make_unique<FakeTaskQueueFactory>(); |
| 192 | auto owned_field_trials1 = std::make_unique<FakeFieldTrials>(); |
| 193 | auto owned_field_trials2 = std::make_unique<FakeFieldTrials>(); |
| 194 | TaskQueueFactory& task_queue_factory = *owned_task_queue_factory; |
| 195 | FieldTrialsView& field_trials1 = *owned_field_trials1; |
| 196 | FieldTrialsView& field_trials2 = *owned_field_trials2; |
| 197 | |
| 198 | EnvironmentFactory factory; |
| 199 | factory.Set(std::move(owned_task_queue_factory)); |
| 200 | factory.Set(std::move(owned_field_trials1)); |
| 201 | Environment env1 = factory.Create(); |
| 202 | factory.Set(std::move(owned_field_trials2)); |
| 203 | Environment env2 = factory.Create(); |
| 204 | |
| 205 | // Environments share the same custom task queue factory. |
| 206 | EXPECT_THAT(env1.task_queue_factory(), Ref(task_queue_factory)); |
| 207 | EXPECT_THAT(env2.task_queue_factory(), Ref(task_queue_factory)); |
| 208 | |
| 209 | // Environments have different field trials. |
| 210 | EXPECT_THAT(env1.field_trials(), Ref(field_trials1)); |
| 211 | EXPECT_THAT(env2.field_trials(), Ref(field_trials2)); |
| 212 | } |
| 213 | |
| 214 | TEST(EnvironmentTest, FactoryCanCreateNewEnvironmentFromExistingOne) { |
| 215 | Environment env1 = |
| 216 | CreateEnvironment(std::make_unique<FakeTaskQueueFactory>()); |
| 217 | EnvironmentFactory factory(env1); |
| 218 | factory.Set(std::make_unique<FakeFieldTrials>()); |
| 219 | Environment env2 = factory.Create(); |
| 220 | |
| 221 | // Environments share the same default clock. |
| 222 | EXPECT_THAT(env2.clock(), Ref(env1.clock())); |
| 223 | |
| 224 | // Environments share the same custom task queue factory. |
| 225 | EXPECT_THAT(env2.task_queue_factory(), Ref(env1.task_queue_factory())); |
| 226 | |
| 227 | // Environments have different field trials. |
| 228 | EXPECT_THAT(env2.field_trials(), Not(Ref(env1.field_trials()))); |
| 229 | } |
| 230 | |
| 231 | TEST(EnvironmentTest, KeepsOwnershipsWhenCreateNewEnvironmentFromExistingOne) { |
| 232 | bool utility1_destroyed = false; |
| 233 | bool utility2_destroyed = false; |
| 234 | absl::optional<Environment> env1 = |
| 235 | CreateEnvironment(std::make_unique<FakeTaskQueueFactory>( |
| 236 | /*on_destroyed=*/[&] { utility1_destroyed = true; })); |
| 237 | |
| 238 | absl::optional<EnvironmentFactory> factory = EnvironmentFactory(*env1); |
| 239 | |
| 240 | // Destroy env1, check utility1 it was using is still alive. |
| 241 | env1 = absl::nullopt; |
| 242 | EXPECT_FALSE(utility1_destroyed); |
| 243 | |
| 244 | factory->Set(std::make_unique<FakeFieldTrials>( |
| 245 | /*on_destroyed=*/[&] { utility2_destroyed = true; })); |
| 246 | absl::optional<Environment> env2 = factory->Create(); |
| 247 | |
| 248 | // Destroy the factory, check all utilities used by env2 are alive. |
| 249 | factory = absl::nullopt; |
| 250 | EXPECT_FALSE(utility1_destroyed); |
| 251 | EXPECT_FALSE(utility2_destroyed); |
| 252 | |
| 253 | // Once last Environment object is deleted, utilties should be deleted too. |
| 254 | env2 = absl::nullopt; |
| 255 | EXPECT_TRUE(utility1_destroyed); |
| 256 | EXPECT_TRUE(utility2_destroyed); |
| 257 | } |
| 258 | |
| 259 | TEST(EnvironmentTest, DestroysUtilitiesInReverseProvidedOrder) { |
| 260 | std::vector<std::string> destroyed; |
| 261 | auto field_trials = std::make_unique<FakeFieldTrials>( |
| 262 | /*on_destroyed=*/[&] { destroyed.push_back("field_trials"); }); |
| 263 | auto task_queue_factory = std::make_unique<FakeTaskQueueFactory>( |
| 264 | /*on_destroyed=*/[&] { destroyed.push_back("task_queue_factory"); }); |
| 265 | |
| 266 | absl::optional<Environment> env = |
| 267 | CreateEnvironment(std::move(field_trials), std::move(task_queue_factory)); |
| 268 | |
| 269 | ASSERT_THAT(destroyed, IsEmpty()); |
| 270 | env = absl::nullopt; |
| 271 | EXPECT_THAT(destroyed, ElementsAre("task_queue_factory", "field_trials")); |
| 272 | } |
| 273 | |
| 274 | } // namespace |
| 275 | } // namespace webrtc |