Add force-test-environment flag and checks Add --force-test-environment flag to test_main. This flag sets a global boolean that CreateEnvironment and FieldTrials will check, crashing if they are used in tests instead of the test-specific versions. Generated by prompt: We want a way to detect if CreateEnvironment or CreateFieldTrials is used in tests where we should be using CreateTestEnvironment or CreateTestFieldTrials. Add code to test/test_main.cc to have a command line flag '--force-test-environment' that sets a global boolean that CreateEnvironment and CreateFieldTrials will RTC_CHECK on so that we immediately crash if the non-test versions are used. Bug: webrtc:521534686 Change-Id: I7f47b4ccfc396f90d7626d430140e92834b6a483 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/480160 Reviewed-by: Danil Chapovalov <danilchap@webrtc.org> Commit-Queue: Harald Alvestrand <hta@webrtc.org> Cr-Commit-Position: refs/heads/main@{#47947}
diff --git a/api/BUILD.gn b/api/BUILD.gn index 09b6816..2e0bd76 100644 --- a/api/BUILD.gn +++ b/api/BUILD.gn
@@ -1808,6 +1808,7 @@ "../test:test_support", "audio_codecs/opus:unittests", "environment:environment_unittests", + "environment:force_test_environment", "task_queue:task_queue_default_factory_unittests", "test/pclf:media_configuration", "test/video:video_frame_writer", @@ -1919,6 +1920,7 @@ ":field_trials_view", "../rtc_base:checks", "../rtc_base/containers:flat_map", + "environment:force_test_environment", "//third_party/abseil-cpp/absl/base:nullability", "//third_party/abseil-cpp/absl/memory", "//third_party/abseil-cpp/absl/strings:string_view",
diff --git a/api/environment/BUILD.gn b/api/environment/BUILD.gn index b28c754..e4455b0 100644 --- a/api/environment/BUILD.gn +++ b/api/environment/BUILD.gn
@@ -8,6 +8,14 @@ import("../../webrtc.gni") +rtc_library("force_test_environment") { + visibility = webrtc_default_visibility + sources = [ + "force_test_environment.cc", + "force_test_environment.h", + ] +} + rtc_source_set("environment") { visibility = [ "*" ] sources = [ "environment.h" ] @@ -52,6 +60,7 @@ deps = [ ":deprecated_global_field_trials", ":environment", + ":force_test_environment", "..:field_trials_view", "..:make_ref_counted", "..:refcountedbase", @@ -73,8 +82,10 @@ deps = [ ":environment", ":environment_factory", + ":force_test_environment", "..:field_trials_view", "../../system_wrappers", + "../../test:create_test_environment", "../../test:test_support", "../rtc_event_log", "../task_queue",
diff --git a/api/environment/environment_factory.cc b/api/environment/environment_factory.cc index 529bbec..1f2f31d 100644 --- a/api/environment/environment_factory.cc +++ b/api/environment/environment_factory.cc
@@ -16,6 +16,7 @@ #include "absl/base/nullability.h" #include "api/environment/deprecated_global_field_trials.h" #include "api/environment/environment.h" +#include "api/environment/force_test_environment.h" #include "api/field_trials_view.h" #include "api/make_ref_counted.h" #include "api/ref_counted_base.h" @@ -99,6 +100,10 @@ } Environment EnvironmentFactory::CreateWithDefaults() && { + RTC_CHECK(!IsForceTestEnvironmentEnabled() || + IsTestEnvironmentCheckBypassed()) + << "Production Environment creation is not allowed in tests. Use " + "CreateTestEnvironment."; if (field_trials_ == nullptr) { Set(std::make_unique<DeprecatedGlobalFieldTrials>()); }
diff --git a/api/environment/environment_unittest.cc b/api/environment/environment_unittest.cc index 048f250..314283a 100644 --- a/api/environment/environment_unittest.cc +++ b/api/environment/environment_unittest.cc
@@ -19,6 +19,7 @@ #include "absl/functional/any_invocable.h" #include "absl/strings/string_view.h" #include "api/environment/environment_factory.h" +#include "api/environment/force_test_environment.h" #include "api/field_trials_view.h" #include "api/rtc_event_log/rtc_event.h" #include "api/rtc_event_log/rtc_event_log.h" @@ -26,6 +27,7 @@ #include "api/task_queue/task_queue_factory.h" #include "api/units/timestamp.h" #include "system_wrappers/include/clock.h" +#include "test/create_test_environment.h" #include "test/gmock.h" #include "test/gtest.h" @@ -280,5 +282,36 @@ EXPECT_THAT(destroyed, ElementsAre("task_queue_factory", "field_trials")); } +TEST(EnvironmentTest, CreateTestEnvironmentWorksWhenForced) { + struct ScopedForce { + ScopedForce() { + old_value = IsForceTestEnvironmentEnabled(); + SetForceTestEnvironment(true); + } + ~ScopedForce() { SetForceTestEnvironment(old_value); } + bool old_value; + } force; + + Environment env = CreateTestEnvironment(); + env.clock().CurrentTime(); + EXPECT_THAT(env.task_queue_factory().CreateTaskQueue( + "test", TaskQueueFactory::Priority::kNormal), + NotNull()); + env.event_log().Log(std::make_unique<FakeEvent>()); + env.field_trials().Lookup("WebRTC-Debugging-RtpDump"); +} + +#if GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID) +TEST(EnvironmentDeathTest, CreateEnvironmentCrashesWhenForced) { + EXPECT_DEATH( + { + SetForceTestEnvironment(true); + CreateEnvironment(); + }, + "Production Environment creation is not allowed in tests. Use " + "CreateTestEnvironment."); +} +#endif + } // namespace } // namespace webrtc
diff --git a/api/environment/force_test_environment.cc b/api/environment/force_test_environment.cc new file mode 100644 index 0000000..df2de40 --- /dev/null +++ b/api/environment/force_test_environment.cc
@@ -0,0 +1,43 @@ +/* + * Copyright (c) 2026 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/force_test_environment.h" + +#include <atomic> + +namespace webrtc { +namespace { + +std::atomic<bool> g_force_test_environment{false}; +thread_local int g_bypass_test_environment_check_count = 0; + +} // namespace + +void SetForceTestEnvironment(bool force) { + g_force_test_environment.store(force, std::memory_order_relaxed); +} + +bool IsForceTestEnvironmentEnabled() { + return g_force_test_environment.load(std::memory_order_relaxed); +} + +AutoBypassTestEnvironmentCheck::AutoBypassTestEnvironmentCheck() { + ++g_bypass_test_environment_check_count; +} + +AutoBypassTestEnvironmentCheck::~AutoBypassTestEnvironmentCheck() { + --g_bypass_test_environment_check_count; +} + +bool IsTestEnvironmentCheckBypassed() { + return g_bypass_test_environment_check_count > 0; +} + +} // namespace webrtc
diff --git a/api/environment/force_test_environment.h b/api/environment/force_test_environment.h new file mode 100644 index 0000000..c6fdae7 --- /dev/null +++ b/api/environment/force_test_environment.h
@@ -0,0 +1,38 @@ +/* + * Copyright (c) 2026 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_FORCE_TEST_ENVIRONMENT_H_ +#define API_ENVIRONMENT_FORCE_TEST_ENVIRONMENT_H_ + +namespace webrtc { + +// Sets flag to crash if non-test Environment or FieldTrials are created. +void SetForceTestEnvironment(bool force); + +// Returns true if force-test-environment is enabled. +bool IsForceTestEnvironmentEnabled(); + +// RAII class to temporarily bypass the check. +class AutoBypassTestEnvironmentCheck { + public: + AutoBypassTestEnvironmentCheck(); + ~AutoBypassTestEnvironmentCheck(); + AutoBypassTestEnvironmentCheck(const AutoBypassTestEnvironmentCheck&) = + delete; + AutoBypassTestEnvironmentCheck& operator=( + const AutoBypassTestEnvironmentCheck&) = delete; +}; + +// Returns true if the check should be bypassed. +bool IsTestEnvironmentCheckBypassed(); + +} // namespace webrtc + +#endif // API_ENVIRONMENT_FORCE_TEST_ENVIRONMENT_H_
diff --git a/api/field_trials.cc b/api/field_trials.cc index a7f0527..c243598 100644 --- a/api/field_trials.cc +++ b/api/field_trials.cc
@@ -17,6 +17,7 @@ #include "absl/base/nullability.h" #include "absl/memory/memory.h" #include "absl/strings/string_view.h" +#include "api/environment/force_test_environment.h" #include "api/field_trials_registry.h" #include "rtc_base/checks.h" #include "rtc_base/containers/flat_map.h" @@ -57,6 +58,10 @@ absl_nullable std::unique_ptr<FieldTrials> FieldTrials::Create( absl::string_view s) { + RTC_CHECK(!IsForceTestEnvironmentEnabled() || + IsTestEnvironmentCheckBypassed()) + << "FieldTrials::Create is not allowed in tests. Use " + "CreateTestFieldTrials."; flat_map<std::string, std::string> key_value_map; if (!Parse(s, key_value_map)) { return nullptr; @@ -66,6 +71,10 @@ } FieldTrials::FieldTrials(absl::string_view s) { + RTC_CHECK(!IsForceTestEnvironmentEnabled() || + IsTestEnvironmentCheckBypassed()) + << "FieldTrials constructor is not allowed in tests. Use " + "CreateTestFieldTrials."; RTC_CHECK(Parse(s, key_value_map_)); }
diff --git a/api/field_trials_unittest.cc b/api/field_trials_unittest.cc index ba44d6e..d86451d 100644 --- a/api/field_trials_unittest.cc +++ b/api/field_trials_unittest.cc
@@ -13,6 +13,7 @@ #include <memory> #include "absl/strings/str_cat.h" +#include "api/environment/force_test_environment.h" #include "api/field_trials_view.h" #include "rtc_base/checks.h" #include "rtc_base/containers/flat_set.h" @@ -188,5 +189,25 @@ #endif } +#if GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID) +TEST(FieldTrialsDeathTest, CreateCrashesWhenForced) { + EXPECT_DEATH( + { + SetForceTestEnvironment(true); + FieldTrials::Create(""); + }, + "FieldTrials::Create is not allowed in tests"); +} + +TEST(FieldTrialsDeathTest, ConstructorCrashesWhenForced) { + EXPECT_DEATH( + { + SetForceTestEnvironment(true); + FieldTrials f(""); + }, + "FieldTrials constructor is not allowed in tests"); +} +#endif + } // namespace } // namespace webrtc
diff --git a/test/BUILD.gn b/test/BUILD.gn index 1f39c7d8..9fb3850 100644 --- a/test/BUILD.gn +++ b/test/BUILD.gn
@@ -161,6 +161,7 @@ "../api:time_controller", "../api/environment", "../api/environment:environment_factory", + "../api/environment:force_test_environment", "../api/rtc_event_log", "../rtc_base:checks", "../system_wrappers", @@ -174,6 +175,7 @@ sources = [ "create_test_field_trials.h" ] deps = [ "../api:field_trials", + "../api/environment:force_test_environment", "//third_party/abseil-cpp/absl/base:nullability", "//third_party/abseil-cpp/absl/strings:string_view", ] @@ -641,8 +643,10 @@ deps = [ ":test_main_lib", ":test_support", + "../api/environment:force_test_environment", "//third_party/abseil-cpp/absl/debugging:failure_signal_handler", "//third_party/abseil-cpp/absl/debugging:symbolize", + "//third_party/abseil-cpp/absl/flags:flag", "//third_party/abseil-cpp/absl/flags:parse", ] } @@ -720,6 +724,7 @@ ":create_test_field_trials", ":test_support", "../api:field_trials", + "../api/environment:force_test_environment", "//third_party/abseil-cpp/absl/flags:flag", "//third_party/abseil-cpp/absl/strings:string_view", ]
diff --git a/test/create_test_environment.cc b/test/create_test_environment.cc index 030c968..741fe96 100644 --- a/test/create_test_environment.cc +++ b/test/create_test_environment.cc
@@ -18,6 +18,7 @@ #include "absl/strings/string_view.h" #include "api/environment/environment.h" #include "api/environment/environment_factory.h" +#include "api/environment/force_test_environment.h" #include "api/field_trials.h" #include "api/field_trials_view.h" #include "api/test/time_controller.h" @@ -66,6 +67,7 @@ } // namespace Environment CreateTestEnvironment(CreateTestEnvironmentOptions o) { + AutoBypassTestEnvironmentCheck bypass; EnvironmentFactory factory; std::visit(SetFieldTrials{.factory = factory}, std::move(o.field_trials));
diff --git a/test/create_test_field_trials.cc b/test/create_test_field_trials.cc index c1644f4..59524ca 100644 --- a/test/create_test_field_trials.cc +++ b/test/create_test_field_trials.cc
@@ -14,6 +14,7 @@ #include "absl/flags/flag.h" #include "absl/strings/string_view.h" +#include "api/environment/force_test_environment.h" #include "api/field_trials.h" ABSL_FLAG(std::string, @@ -26,6 +27,7 @@ namespace webrtc { FieldTrials CreateTestFieldTrials(absl::string_view s) { + AutoBypassTestEnvironmentCheck bypass; FieldTrials result(absl::GetFlag(FLAGS_force_fieldtrials)); result.Merge(FieldTrials(s)); return result;
diff --git a/test/create_test_field_trials_unittest.cc b/test/create_test_field_trials_unittest.cc index 661cc06..a125af0 100644 --- a/test/create_test_field_trials_unittest.cc +++ b/test/create_test_field_trials_unittest.cc
@@ -15,6 +15,7 @@ #include "absl/flags/declare.h" #include "absl/flags/flag.h" #include "absl/strings/string_view.h" +#include "api/environment/force_test_environment.h" #include "api/field_trials.h" #include "test/gtest.h" @@ -72,5 +73,20 @@ EXPECT_EQ(field_trials.Lookup("TrialConstructor"), "ConstructorValue"); } +TEST(CreateTestFieldTrialsTest, WorksWhenForceTestEnvironmentEnabled) { + struct ScopedForce { + ScopedForce() { + old_value = IsForceTestEnvironmentEnabled(); + SetForceTestEnvironment(true); + } + ~ScopedForce() { SetForceTestEnvironment(old_value); } + bool old_value; + } force; + + FieldTrials field_trials = CreateTestFieldTrials("Trial/Value/"); + field_trials.RegisterKeysForTesting({"Trial"}); + EXPECT_EQ(field_trials.Lookup("Trial"), "Value"); +} + } // namespace } // namespace webrtc
diff --git a/test/create_test_field_trials_without_absl_flag.cc b/test/create_test_field_trials_without_absl_flag.cc index f14f8ed..7768796 100644 --- a/test/create_test_field_trials_without_absl_flag.cc +++ b/test/create_test_field_trials_without_absl_flag.cc
@@ -13,12 +13,14 @@ // the command line flag by default. #include "absl/strings/string_view.h" +#include "api/environment/force_test_environment.h" #include "api/field_trials.h" #include "test/create_test_field_trials.h" namespace webrtc { FieldTrials CreateTestFieldTrials(absl::string_view s) { + AutoBypassTestEnvironmentCheck bypass; return FieldTrials(s); }
diff --git a/test/test_main.cc b/test/test_main.cc index 9dcc57c..ca49dcb 100644 --- a/test/test_main.cc +++ b/test/test_main.cc
@@ -16,10 +16,17 @@ #include "absl/debugging/failure_signal_handler.h" #include "absl/debugging/symbolize.h" +#include "absl/flags/flag.h" #include "absl/flags/parse.h" +#include "api/environment/force_test_environment.h" #include "test/gmock.h" #include "test/test_main_lib.h" +ABSL_FLAG(bool, + force_test_environment, + false, + "Crash if non-test Environment or FieldTrials are created."); + namespace { std::vector<std::string> ReplaceDashesWithUnderscores(int argc, char* argv[]) { @@ -58,6 +65,7 @@ std::vector<std::string> new_argv = ReplaceDashesWithUnderscores(argc, argv); std::vector<char*> raw_new_argv = VectorOfStringsToVectorOfPointers(new_argv); absl::ParseCommandLine(argc, &raw_new_argv[0]); + webrtc::SetForceTestEnvironment(absl::GetFlag(FLAGS_force_test_environment)); // This absl handler use unsupported features/instructions on Fuchsia #if !defined(WEBRTC_FUCHSIA)