/* * Copyright 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 "pc/scoped_operations_batcher.h" #include #include #include #include "absl/functional/any_invocable.h" #include "api/rtc_error.h" #include "rtc_base/thread.h" #include "test/gtest.h" namespace webrtc { namespace { TEST(ScopedOperationsBatcherTest, ExecutesTasksOnTargetThread) { auto target_thread = Thread::Create(); target_thread->Start(); bool task_executed = false; bool target_checked = false; { ScopedOperationsBatcher batcher(target_thread.get()); batcher.Add([&] { task_executed = true; target_checked = target_thread->IsCurrent(); }); } EXPECT_TRUE(task_executed); EXPECT_TRUE(target_checked); } TEST(ScopedOperationsBatcherTest, ExecutesReturnedTasksOnCallingThread) { // Use current thread as the calling (signaling) thread. This test runs // without an explicit RunLoop, because the returned tasks are executed // synchronously within the caller's context of `Run()` or // `~ScopedOperationsBatcher()`. auto signaling_thread = Thread::Current(); auto target_thread = Thread::Create(); target_thread->Start(); bool return_task_executed = false; Thread* return_task_thread = nullptr; bool task_executed = false; Thread* task_thread = nullptr; { ScopedOperationsBatcher batcher(target_thread.get()); ScopedOperationsBatcher::BatchTaskWithFinalizer task = [&]() -> RTCErrorOr { task_executed = true; task_thread = Thread::Current(); return ScopedOperationsBatcher::FinalizerTask([&]() { return_task_executed = true; return_task_thread = Thread::Current(); }); }; batcher.AddWithFinalizer(std::move(task)); } EXPECT_TRUE(task_executed); EXPECT_EQ(task_thread, target_thread.get()); EXPECT_TRUE(return_task_executed); EXPECT_EQ(return_task_thread, signaling_thread); } TEST(ScopedOperationsBatcherTest, YieldsToOtherTasks) { auto target_thread = Thread::Create(); target_thread->Start(); std::vector execution_order; { ScopedOperationsBatcher batcher(target_thread.get()); batcher.Add([&] { execution_order.push_back(1); }); batcher.Add([&] { execution_order.push_back(2); // Post a task that should interrupt the batch since we now yield to any // pending task. target_thread->PostTask([&] { execution_order.push_back(3); }); }); batcher.Add([&] { execution_order.push_back(4); }); batcher.Add([&] { execution_order.push_back(5); }); } // Expect the task (3) to execute immediately after the task // that posted it (2). The operations remaining in the batch (4, 5) // should resume after the thread has processed the new task. EXPECT_EQ(execution_order, std::vector({1, 2, 3, 4, 5})); } TEST(ScopedOperationsBatcherTest, StopsExecutionOnError) { auto target_thread = Thread::Create(); target_thread->Start(); bool task1_executed = false; bool task2_executed = false; bool task3_executed = false; // There's no finalizer for task2. bool finalizer1_executed = false; bool finalizer3_executed = false; RTCError error = RTCError::OK(); { ScopedOperationsBatcher batcher(target_thread.get()); // Task 1. batcher.AddWithFinalizer( [&]() -> RTCErrorOr { task1_executed = true; return ScopedOperationsBatcher::FinalizerTask( [&] { finalizer1_executed = true; }); }); // Task 2. batcher.AddWithFinalizer( [&]() -> RTCErrorOr { task2_executed = true; return RTCError(RTCErrorType::INVALID_STATE, "Failed"); }); // Task 3. batcher.AddWithFinalizer( [&]() -> RTCErrorOr { task3_executed = true; return ScopedOperationsBatcher::FinalizerTask( [&] { finalizer3_executed = true; }); }); error = batcher.Run(); } EXPECT_FALSE(error.ok()); EXPECT_EQ(error.type(), RTCErrorType::INVALID_STATE); EXPECT_STREQ(error.message(), "Failed"); EXPECT_TRUE(task1_executed); EXPECT_TRUE(finalizer1_executed); EXPECT_TRUE(task2_executed); EXPECT_FALSE(task3_executed); EXPECT_FALSE(finalizer3_executed); } } // namespace } // namespace webrtc