Add unit tests for ChannelReceiveFrameTransformerDelegate

Verify cross-thread StartShortCircuiting and receive-thread Reset
in ChannelReceiveFrameTransformerDelegate, and guard callback invocation
when short-circuiting in Transform().

Bug: webrtc:11993
Change-Id: Ib79c07cced700353d5c2dcb1ecc9e46d15bc53a5
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/495020
Reviewed-by: Jakob Ivarsson‎ <jakobi@webrtc.org>
Commit-Queue: Tomas Gunnarsson <tommi@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#48628}
diff --git a/audio/channel_receive_frame_transformer_delegate.cc b/audio/channel_receive_frame_transformer_delegate.cc
index 8416115..40372bc 100644
--- a/audio/channel_receive_frame_transformer_delegate.cc
+++ b/audio/channel_receive_frame_transformer_delegate.cc
@@ -146,9 +146,11 @@
     ReceiveFrameCallback receive_frame_callback,
     scoped_refptr<FrameTransformerInterface> frame_transformer,
     TaskQueueBase* absl_nonnull channel_receive_thread)
-    : receive_frame_callback_(receive_frame_callback),
+    : receive_frame_callback_(std::move(receive_frame_callback)),
       frame_transformer_(std::move(frame_transformer)),
-      channel_receive_thread_(channel_receive_thread) {}
+      channel_receive_thread_(channel_receive_thread) {
+  RTC_DCHECK(receive_frame_callback_);
+}
 
 void ChannelReceiveFrameTransformerDelegate::Init() {
   RTC_DCHECK_RUN_ON(&sequence_checker_);
@@ -171,7 +173,9 @@
     Timestamp receive_time) {
   RTC_DCHECK_RUN_ON(&sequence_checker_);
   if (short_circuit_) {
-    receive_frame_callback_(packet, header, receive_time);
+    if (receive_frame_callback_) {
+      receive_frame_callback_(packet, header, receive_time);
+    }
   } else {
     frame_transformer_->Transform(
         std::make_unique<TransformableIncomingAudioFrame>(
diff --git a/audio/channel_receive_frame_transformer_delegate_unittest.cc b/audio/channel_receive_frame_transformer_delegate_unittest.cc
index 67a46a0..c601226 100644
--- a/audio/channel_receive_frame_transformer_delegate_unittest.cc
+++ b/audio/channel_receive_frame_transformer_delegate_unittest.cc
@@ -25,6 +25,7 @@
 #include "api/test/mock_transformable_audio_frame.h"
 #include "api/units/time_delta.h"
 #include "api/units/timestamp.h"
+#include "rtc_base/task_queue_for_test.h"
 #include "rtc_base/thread.h"
 #include "system_wrappers/include/ntp_time.h"
 #include "test/gmock.h"
@@ -66,8 +67,7 @@
       make_ref_counted<MockFrameTransformer>();
   scoped_refptr<ChannelReceiveFrameTransformerDelegate> delegate =
       make_ref_counted<ChannelReceiveFrameTransformerDelegate>(
-          ChannelReceiveFrameTransformerDelegate::ReceiveFrameCallback(),
-          mock_frame_transformer, main_thread.task_queue());
+          [](auto...) {}, mock_frame_transformer, main_thread.task_queue());
   EXPECT_CALL(*mock_frame_transformer, RegisterTransformedFrameCallback);
   delegate->Init();
 }
@@ -81,8 +81,7 @@
       make_ref_counted<MockFrameTransformer>();
   scoped_refptr<ChannelReceiveFrameTransformerDelegate> delegate =
       make_ref_counted<ChannelReceiveFrameTransformerDelegate>(
-          ChannelReceiveFrameTransformerDelegate::ReceiveFrameCallback(),
-          mock_frame_transformer, main_thread.task_queue());
+          [](auto...) {}, mock_frame_transformer, main_thread.task_queue());
   EXPECT_CALL(*mock_frame_transformer, UnregisterTransformedFrameCallback);
   delegate->Reset();
 }
@@ -206,7 +205,7 @@
       make_ref_counted<NiceMock<MockFrameTransformer>>();
   scoped_refptr<ChannelReceiveFrameTransformerDelegate> delegate =
       make_ref_counted<ChannelReceiveFrameTransformerDelegate>(
-          /*receive_frame_callback=*/nullptr, mock_frame_transformer,
+          /*receive_frame_callback=*/[](auto...) {}, mock_frame_transformer,
           main_thread.task_queue());
   scoped_refptr<TransformedFrameCallback> callback;
   EXPECT_CALL(*mock_frame_transformer, RegisterTransformedFrameCallback)
@@ -243,7 +242,7 @@
       make_ref_counted<NiceMock<MockFrameTransformer>>();
   scoped_refptr<ChannelReceiveFrameTransformerDelegate> delegate =
       make_ref_counted<ChannelReceiveFrameTransformerDelegate>(
-          /*receive_frame_callback=*/nullptr, mock_frame_transformer,
+          /*receive_frame_callback=*/[](auto...) {}, mock_frame_transformer,
           main_thread.task_queue());
   scoped_refptr<TransformedFrameCallback> callback;
   EXPECT_CALL(*mock_frame_transformer, RegisterTransformedFrameCallback)
@@ -281,7 +280,7 @@
       make_ref_counted<NiceMock<MockFrameTransformer>>();
   scoped_refptr<ChannelReceiveFrameTransformerDelegate> delegate =
       make_ref_counted<ChannelReceiveFrameTransformerDelegate>(
-          /*receive_frame_callback=*/nullptr, mock_frame_transformer,
+          /*receive_frame_callback=*/[](auto...) {}, mock_frame_transformer,
           main_thread.task_queue());
   scoped_refptr<TransformedFrameCallback> callback;
   EXPECT_CALL(*mock_frame_transformer, RegisterTransformedFrameCallback)
@@ -324,7 +323,7 @@
       make_ref_counted<NiceMock<MockFrameTransformer>>();
   scoped_refptr<ChannelReceiveFrameTransformerDelegate> delegate =
       make_ref_counted<ChannelReceiveFrameTransformerDelegate>(
-          /*receive_frame_callback=*/nullptr, mock_frame_transformer,
+          /*receive_frame_callback=*/[](auto...) {}, mock_frame_transformer,
           main_thread.task_queue());
   delegate->Init();
   const uint8_t data[] = {1, 2, 3, 4};
@@ -358,7 +357,7 @@
       make_ref_counted<NiceMock<MockFrameTransformer>>();
   scoped_refptr<ChannelReceiveFrameTransformerDelegate> delegate =
       make_ref_counted<ChannelReceiveFrameTransformerDelegate>(
-          /*receive_frame_callback=*/nullptr, mock_frame_transformer,
+          /*receive_frame_callback=*/[](auto...) {}, mock_frame_transformer,
           main_thread.task_queue());
   delegate->Init();
 
@@ -422,5 +421,47 @@
   EXPECT_EQ(header.extension.audio_level()->level(), 111);
 }
 
+TEST(ChannelReceiveFrameTransformerDelegateTest,
+     StartShortCircuitingFromAnotherThreadPostsToReceiveThread) {
+  test::RunLoop main_thread;
+  scoped_refptr<MockFrameTransformer> mock_frame_transformer =
+      make_ref_counted<NiceMock<MockFrameTransformer>>();
+  MockChannelReceive mock_channel;
+  scoped_refptr<ChannelReceiveFrameTransformerDelegate> delegate =
+      make_ref_counted<ChannelReceiveFrameTransformerDelegate>(
+          mock_channel.callback(), mock_frame_transformer,
+          main_thread.task_queue());
+  const uint8_t data[] = {1, 2, 3, 4};
+  std::span<const uint8_t> packet(data, sizeof(data));
+  RTPHeader header;
+
+  TaskQueueForTest off_thread("ShortCircuitQueue");
+  off_thread.SendTask([&]() { delegate->StartShortCircuiting(); });
+  main_thread.Flush();
+
+  EXPECT_CALL(*mock_frame_transformer, Transform).Times(0);
+  EXPECT_CALL(mock_channel, ReceiveFrame);
+  delegate->Transform(packet, header, /*ssrc=*/1111,
+                      /*codec_mime_type=*/"audio/opus", kFakeReceiveTimestamp);
+}
+
+TEST(ChannelReceiveFrameTransformerDelegateTest,
+     ResetSafelyClearsCallbackOnReceiveThread) {
+  test::RunLoop main_thread;
+  scoped_refptr<MockFrameTransformer> mock_frame_transformer =
+      make_ref_counted<NiceMock<MockFrameTransformer>>();
+  MockChannelReceive mock_channel;
+  scoped_refptr<ChannelReceiveFrameTransformerDelegate> delegate =
+      make_ref_counted<ChannelReceiveFrameTransformerDelegate>(
+          mock_channel.callback(), mock_frame_transformer,
+          main_thread.task_queue());
+
+  delegate->Reset();
+
+  EXPECT_CALL(mock_channel, ReceiveFrame).Times(0);
+  delegate->OnTransformedFrame(std::make_unique<MockTransformableAudioFrame>());
+  main_thread.Flush();
+}
+
 }  // namespace
 }  // namespace webrtc