Source code
Revision control
Copy as Markdown
Other Tools
/*
* 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