Implement means to create an Environment
implement EnvironmentFactory class
and CreateEnvironment function.
Bug: webrtc:15656
Change-Id: Ib5ec0e35896be08e6125bcabc6abf2341e613909
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/328060
Commit-Queue: Danil Chapovalov <danilchap@webrtc.org>
Reviewed-by: Harald Alvestrand <hta@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#41220}
diff --git a/api/BUILD.gn b/api/BUILD.gn
index e9e6887..e57092e 100644
--- a/api/BUILD.gn
+++ b/api/BUILD.gn
@@ -1484,6 +1484,7 @@
"../test:fileutils",
"../test:rtc_expect_death",
"../test:test_support",
+ "environment:environment_unittests",
"task_queue:task_queue_default_factory_unittests",
"test/pclf:media_configuration",
"test/video:video_frame_writer",
diff --git a/api/environment/BUILD.gn b/api/environment/BUILD.gn
index b4d583f..19d52be 100644
--- a/api/environment/BUILD.gn
+++ b/api/environment/BUILD.gn
@@ -18,3 +18,49 @@
]
absl_deps = [ "//third_party/abseil-cpp/absl/base:nullability" ]
}
+
+rtc_library("environment_factory") {
+ visibility = [ "*" ]
+
+ # TODO: bugs.webrtc.org/15656 - introduce new poison for environment
+ # construction and consume default_task_queue poison with it.
+ allow_poison = [ "default_task_queue" ]
+ sources = [
+ "environment_factory.cc",
+ "environment_factory.h",
+ ]
+ deps = [
+ ":environment",
+ "..:make_ref_counted",
+ "..:refcountedbase",
+ "..:scoped_refptr",
+ "../../rtc_base:checks",
+ "../../rtc_base/system:rtc_export",
+ "../../system_wrappers",
+ "../rtc_event_log",
+ "../task_queue:default_task_queue_factory",
+ "../transport:field_trial_based_config",
+ ]
+ absl_deps = [ "//third_party/abseil-cpp/absl/base:nullability" ]
+}
+
+if (rtc_include_tests) {
+ rtc_library("environment_unittests") {
+ testonly = true
+ sources = [ "environment_unittest.cc" ]
+ deps = [
+ ":environment",
+ ":environment_factory",
+ "..:field_trials_view",
+ "../../system_wrappers",
+ "../../test:test_support",
+ "../rtc_event_log",
+ "../task_queue",
+ "../units:timestamp",
+ ]
+ absl_deps = [
+ "//third_party/abseil-cpp/absl/functional:any_invocable",
+ "//third_party/abseil-cpp/absl/types:optional",
+ ]
+ }
+}
diff --git a/api/environment/environment.h b/api/environment/environment.h
index a742323..0849b0a 100644
--- a/api/environment/environment.h
+++ b/api/environment/environment.h
@@ -63,7 +63,7 @@
public:
// Default constructor is deleted in favor of creating this object using
// `EnvironmentFactory`. To create the default environment use
- // `EnvironmentFactory().Create()`.
+ // `EnvironmentFactory().Create()` or `CreateEnvironment()`.
Environment() = delete;
Environment(const Environment&) = default;
diff --git a/api/environment/environment_factory.cc b/api/environment/environment_factory.cc
new file mode 100644
index 0000000..c0b681a
--- /dev/null
+++ b/api/environment/environment_factory.cc
@@ -0,0 +1,123 @@
+/*
+ * Copyright 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_factory.h"
+
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include "api/make_ref_counted.h"
+#include "api/rtc_event_log/rtc_event_log.h"
+#include "api/task_queue/default_task_queue_factory.h"
+#include "api/transport/field_trial_based_config.h"
+#include "rtc_base/checks.h"
+#include "system_wrappers/include/clock.h"
+
+namespace webrtc {
+namespace {
+
+template <typename T>
+void Store(absl::Nonnull<std::unique_ptr<T>> value,
+ scoped_refptr<const rtc::RefCountedBase>& leaf) {
+ class StorageNode : public rtc::RefCountedBase {
+ public:
+ StorageNode(scoped_refptr<const rtc::RefCountedBase> parent,
+ absl::Nonnull<std::unique_ptr<T>> value)
+ : parent_(std::move(parent)), value_(std::move(value)) {}
+
+ StorageNode(const StorageNode&) = delete;
+ StorageNode& operator=(const StorageNode&) = delete;
+
+ ~StorageNode() override = default;
+
+ private:
+ scoped_refptr<const rtc::RefCountedBase> parent_;
+ absl::Nonnull<std::unique_ptr<T>> value_;
+ };
+
+ // Utilities provided with ownership form a tree:
+ // Root is nullptr, each node keeps an ownership of one utility.
+ // Each child node has a link to the parent, but parent is unaware of its
+ // children. Each `EnvironmentFactory` and `Environment` keep a reference to a
+ // 'leaf_' - node with the last provided utility. This way `Environment` keeps
+ // ownership of a single branch of the storage tree with each used utiltity
+ // owned by one of the nodes on that branch.
+ leaf = rtc::make_ref_counted<StorageNode>(std::move(leaf), std::move(value));
+}
+
+} // namespace
+
+EnvironmentFactory::EnvironmentFactory(const Environment& env)
+ : leaf_(env.storage_),
+ field_trials_(env.field_trials_),
+ clock_(env.clock_),
+ task_queue_factory_(env.task_queue_factory_),
+ event_log_(env.event_log_) {}
+
+void EnvironmentFactory::Set(
+ absl::Nullable<std::unique_ptr<const FieldTrialsView>> utility) {
+ if (utility != nullptr) {
+ field_trials_ = utility.get();
+ Store(std::move(utility), leaf_);
+ }
+}
+
+void EnvironmentFactory::Set(absl::Nullable<std::unique_ptr<Clock>> utility) {
+ if (utility != nullptr) {
+ clock_ = utility.get();
+ Store(std::move(utility), leaf_);
+ }
+}
+
+void EnvironmentFactory::Set(
+ absl::Nullable<std::unique_ptr<TaskQueueFactory>> utility) {
+ if (utility != nullptr) {
+ task_queue_factory_ = utility.get();
+ Store(std::move(utility), leaf_);
+ }
+}
+
+void EnvironmentFactory::Set(
+ absl::Nullable<std::unique_ptr<RtcEventLog>> utility) {
+ if (utility != nullptr) {
+ event_log_ = utility.get();
+ Store(std::move(utility), leaf_);
+ }
+}
+
+Environment EnvironmentFactory::CreateWithDefaults() && {
+ if (field_trials_ == nullptr) {
+ Set(std::make_unique<FieldTrialBasedConfig>());
+ }
+ if (clock_ == nullptr) {
+ Set(Clock::GetRealTimeClock());
+ }
+ if (task_queue_factory_ == nullptr) {
+ Set(CreateDefaultTaskQueueFactory(field_trials_));
+ }
+ if (event_log_ == nullptr) {
+ Set(std::make_unique<RtcEventLogNull>());
+ }
+
+ RTC_DCHECK(field_trials_ != nullptr);
+ RTC_DCHECK(clock_ != nullptr);
+ RTC_DCHECK(task_queue_factory_ != nullptr);
+ RTC_DCHECK(event_log_ != nullptr);
+ return Environment(std::move(leaf_), //
+ field_trials_, clock_, task_queue_factory_, event_log_);
+}
+
+Environment EnvironmentFactory::Create() const {
+ // Create a temporary copy to avoid mutating `this` with default utilities.
+ return EnvironmentFactory(*this).CreateWithDefaults();
+}
+
+} // namespace webrtc
diff --git a/api/environment/environment_factory.h b/api/environment/environment_factory.h
new file mode 100644
index 0000000..a0fc3ef
--- /dev/null
+++ b/api/environment/environment_factory.h
@@ -0,0 +1,148 @@
+/*
+ * Copyright 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.
+ */
+
+#ifndef API_ENVIRONMENT_ENVIRONMENT_FACTORY_H_
+#define API_ENVIRONMENT_ENVIRONMENT_FACTORY_H_
+
+#include <memory>
+#include <utility>
+
+#include "absl/base/nullability.h"
+#include "api/environment/environment.h"
+#include "api/ref_counted_base.h"
+#include "api/scoped_refptr.h"
+#include "rtc_base/system/rtc_export.h"
+
+namespace webrtc {
+
+// These classes are forward declared to reduce amount of headers exposed
+// through api header.
+class Clock;
+class TaskQueueFactory;
+class FieldTrialsView;
+class RtcEventLog;
+
+// Constructs `Environment`.
+// Individual utilities are provided using one of the `Set` functions.
+// `Set` functions do nothing when nullptr value is passed.
+// Creates default implementations for utilities that are not provided.
+//
+// Examples:
+// Environment default_env = EnvironmentFactory().Create();
+//
+// EnvironmentFactory factory;
+// factory.Set(std::make_unique<CustomTaskQueueFactory>());
+// factory.Set(std::make_unique<CustomFieldTrials>());
+// Environment custom_env = factory.Create();
+//
+class RTC_EXPORT EnvironmentFactory final {
+ public:
+ EnvironmentFactory() = default;
+ explicit EnvironmentFactory(const Environment& env);
+
+ EnvironmentFactory(const EnvironmentFactory&) = default;
+ EnvironmentFactory(EnvironmentFactory&&) = default;
+ EnvironmentFactory& operator=(const EnvironmentFactory&) = default;
+ EnvironmentFactory& operator=(EnvironmentFactory&&) = default;
+
+ ~EnvironmentFactory() = default;
+
+ void Set(absl::Nullable<std::unique_ptr<const FieldTrialsView>> utility);
+ void Set(absl::Nullable<std::unique_ptr<Clock>> utility);
+ void Set(absl::Nullable<std::unique_ptr<TaskQueueFactory>> utility);
+ void Set(absl::Nullable<std::unique_ptr<RtcEventLog>> utility);
+
+ void Set(absl::Nullable<const FieldTrialsView*> utility);
+ void Set(absl::Nullable<Clock*> utility);
+ void Set(absl::Nullable<TaskQueueFactory*> utility);
+ void Set(absl::Nullable<RtcEventLog*> utility);
+
+ Environment Create() const;
+
+ private:
+ Environment CreateWithDefaults() &&;
+
+ scoped_refptr<const rtc::RefCountedBase> leaf_;
+
+ absl::Nullable<const FieldTrialsView*> field_trials_ = nullptr;
+ absl::Nullable<Clock*> clock_ = nullptr;
+ absl::Nullable<TaskQueueFactory*> task_queue_factory_ = nullptr;
+ absl::Nullable<RtcEventLog*> event_log_ = nullptr;
+};
+
+// Helper for concise way to create an environment.
+// `Environment env = CreateEnvironment(utility1, utility2)` is a shortcut to
+// `EnvironmentFactory factory;
+// factory.Set(utility1);
+// factory.Set(utility2);
+// Environment env = factory.Create();`
+//
+// Examples:
+// Environment default_env = CreateEnvironment();
+// Environment custom_env =
+// CreateEnvironment(std::make_unique<CustomTaskQueueFactory>(),
+// std::make_unique<CustomFieldTrials>());
+template <typename... Utilities>
+Environment CreateEnvironment(Utilities&&... utilities);
+
+//------------------------------------------------------------------------------
+// Implementation details follow
+//------------------------------------------------------------------------------
+
+inline void EnvironmentFactory::Set(
+ absl::Nullable<const FieldTrialsView*> utility) {
+ if (utility != nullptr) {
+ field_trials_ = utility;
+ }
+}
+
+inline void EnvironmentFactory::Set(absl::Nullable<Clock*> utility) {
+ if (utility != nullptr) {
+ clock_ = utility;
+ }
+}
+
+inline void EnvironmentFactory::Set(absl::Nullable<TaskQueueFactory*> utility) {
+ if (utility != nullptr) {
+ task_queue_factory_ = utility;
+ }
+}
+
+inline void EnvironmentFactory::Set(absl::Nullable<RtcEventLog*> utility) {
+ if (utility != nullptr) {
+ event_log_ = utility;
+ }
+}
+
+namespace webrtc_create_environment_internal {
+
+inline void Set(EnvironmentFactory& factory) {}
+
+template <typename FirstUtility, typename... Utilities>
+void Set(EnvironmentFactory& factory,
+ FirstUtility&& first,
+ Utilities&&... utilities) {
+ factory.Set(std::forward<FirstUtility>(first));
+ Set(factory, std::forward<Utilities>(utilities)...);
+}
+
+} // namespace webrtc_create_environment_internal
+
+template <typename... Utilities>
+Environment CreateEnvironment(Utilities&&... utilities) {
+ EnvironmentFactory factory;
+ webrtc_create_environment_internal::Set(
+ factory, std::forward<Utilities>(utilities)...);
+ return factory.Create();
+}
+
+} // namespace webrtc
+
+#endif // API_ENVIRONMENT_ENVIRONMENT_FACTORY_H_
diff --git a/api/environment/environment_unittest.cc b/api/environment/environment_unittest.cc
new file mode 100644
index 0000000..07bd879
--- /dev/null
+++ b/api/environment/environment_unittest.cc
@@ -0,0 +1,275 @@
+/*
+ * 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 <string>
+#include <utility>
+#include <vector>
+
+#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<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.
+ absl::optional<Environment> 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<FakeFieldTrials>(
+ /*on_destroyed=*/[&] { utility_destroyed = true; });
+
+ absl::optional<Environment> env1 = CreateEnvironment(std::move(field_trials));
+ absl::optional<Environment> 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<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;
+ absl::optional<Environment> env1 =
+ CreateEnvironment(std::make_unique<FakeTaskQueueFactory>(
+ /*on_destroyed=*/[&] { utility1_destroyed = true; }));
+
+ absl::optional<EnvironmentFactory> 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<FakeFieldTrials>(
+ /*on_destroyed=*/[&] { utility2_destroyed = true; }));
+ absl::optional<Environment> 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<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"); });
+
+ absl::optional<Environment> 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