[PCLF] Make it possible to unregister participant for stats poller

Bug: b/231397778
Change-Id: I54c95543cbcf7d6ec9ae0bd121a07fd4e2a1fd4c
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/265408
Reviewed-by: Mirko Bonadei <mbonadei@webrtc.org>
Commit-Queue: Artem Titov <titovartem@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#37169}
diff --git a/test/pc/e2e/BUILD.gn b/test/pc/e2e/BUILD.gn
index 3e7bc15..3832bba 100644
--- a/test/pc/e2e/BUILD.gn
+++ b/test/pc/e2e/BUILD.gn
@@ -39,6 +39,7 @@
         ":multi_head_queue_test",
         ":peer_connection_e2e_smoke_test",
         ":single_process_encoded_image_data_injector_unittest",
+        ":stats_poller_test",
         ":video_frame_tracking_id_injector_unittest",
       ]
     }
@@ -226,6 +227,7 @@
       deps = [
         ":peer_configurer",
         ":peer_connection_quality_test_params",
+        ":stats_provider",
         "../../../api:frame_generator_api",
         "../../../api:function_view",
         "../../../api:libjingle_peerconnection_api",
@@ -500,6 +502,13 @@
       }
     }
 
+    rtc_library("stats_provider") {
+      visibility = [ "*" ]
+      testonly = true
+      sources = [ "stats_provider.h" ]
+      deps = [ "../../../api:rtc_stats_api" ]
+    }
+
     rtc_library("stats_poller") {
       visibility = [ "*" ]
       testonly = true
@@ -508,6 +517,7 @@
         "stats_poller.h",
       ]
       deps = [
+        ":stats_provider",
         ":test_peer",
         "../../../api:libjingle_peerconnection_api",
         "../../../api:rtc_stats_api",
@@ -518,6 +528,16 @@
       ]
     }
 
+    rtc_library("stats_poller_test") {
+      testonly = true
+      sources = [ "stats_poller_test.cc" ]
+      deps = [
+        ":stats_poller",
+        "../..:test_support",
+        "../../../api:rtc_stats_api",
+      ]
+    }
+
     rtc_library("default_video_quality_analyzer_test") {
       testonly = true
       sources = [ "analyzer/video/default_video_quality_analyzer_test.cc" ]
diff --git a/test/pc/e2e/peer_connection_quality_test.cc b/test/pc/e2e/peer_connection_quality_test.cc
index 13a0880..232442f 100644
--- a/test/pc/e2e/peer_connection_quality_test.cc
+++ b/test/pc/e2e/peer_connection_quality_test.cc
@@ -329,8 +329,10 @@
   for (auto& reporter : quality_metrics_reporters_) {
     observers.push_back(reporter.get());
   }
-  StatsPoller stats_poller(observers, {{*alice_->params().name, alice_.get()},
-                                       {*bob_->params().name, bob_.get()}});
+  StatsPoller stats_poller(observers,
+                           std::map<std::string, StatsProvider*>{
+                               {*alice_->params().name, alice_.get()},
+                               {*bob_->params().name, bob_.get()}});
   executor_->ScheduleActivity(TimeDelta::Zero(), kStatsUpdateInterval,
                               [&stats_poller](TimeDelta) {
                                 stats_poller.PollStatsAndNotifyObservers();
diff --git a/test/pc/e2e/stats_poller.cc b/test/pc/e2e/stats_poller.cc
index ecf640b..c04805f 100644
--- a/test/pc/e2e/stats_poller.cc
+++ b/test/pc/e2e/stats_poller.cc
@@ -19,7 +19,7 @@
 namespace webrtc_pc_e2e {
 
 void InternalStatsObserver::PollStats() {
-  peer_->pc()->GetStats(this);
+  peer_->GetStats(this);
 }
 
 void InternalStatsObserver::OnStatsDelivered(
@@ -30,8 +30,18 @@
 }
 
 StatsPoller::StatsPoller(std::vector<StatsObserverInterface*> observers,
+                         std::map<std::string, StatsProvider*> peers)
+    : observers_(std::move(observers)) {
+  webrtc::MutexLock lock(&mutex_);
+  for (auto& peer : peers) {
+    pollers_.push_back(rtc::make_ref_counted<InternalStatsObserver>(
+        peer.first, peer.second, observers_));
+  }
+}
+
+StatsPoller::StatsPoller(std::vector<StatsObserverInterface*> observers,
                          std::map<std::string, TestPeer*> peers)
-    : observers_(observers) {
+    : observers_(std::move(observers)) {
   webrtc::MutexLock lock(&mutex_);
   for (auto& peer : peers) {
     pollers_.push_back(rtc::make_ref_counted<InternalStatsObserver>(
@@ -47,11 +57,22 @@
 }
 
 void StatsPoller::RegisterParticipantInCall(absl::string_view peer_name,
-                                            TestPeer* peer) {
+                                            StatsProvider* peer) {
   webrtc::MutexLock lock(&mutex_);
   pollers_.push_back(rtc::make_ref_counted<InternalStatsObserver>(
       peer_name, peer, observers_));
 }
 
+bool StatsPoller::UnregisterParticipantInCall(absl::string_view peer_name) {
+  webrtc::MutexLock lock(&mutex_);
+  for (auto it = pollers_.begin(); it != pollers_.end(); ++it) {
+    if ((*it)->pc_label() == peer_name) {
+      pollers_.erase(it);
+      return true;
+    }
+  }
+  return false;
+}
+
 }  // namespace webrtc_pc_e2e
 }  // namespace webrtc
diff --git a/test/pc/e2e/stats_poller.h b/test/pc/e2e/stats_poller.h
index 2b3f52c..3576f1b 100644
--- a/test/pc/e2e/stats_poller.h
+++ b/test/pc/e2e/stats_poller.h
@@ -21,6 +21,7 @@
 #include "api/test/stats_observer_interface.h"
 #include "rtc_base/synchronization/mutex.h"
 #include "rtc_base/thread_annotations.h"
+#include "test/pc/e2e/stats_provider.h"
 #include "test/pc/e2e/test_peer.h"
 
 namespace webrtc {
@@ -31,10 +32,12 @@
 class InternalStatsObserver : public RTCStatsCollectorCallback {
  public:
   InternalStatsObserver(absl::string_view pc_label,
-                        TestPeer* peer,
+                        StatsProvider* peer,
                         std::vector<StatsObserverInterface*> observers)
       : pc_label_(pc_label), peer_(peer), observers_(std::move(observers)) {}
 
+  std::string pc_label() const { return pc_label_; }
+
   void PollStats();
 
   void OnStatsDelivered(
@@ -42,7 +45,7 @@
 
  private:
   std::string pc_label_;
-  TestPeer* peer_;
+  StatsProvider* peer_;
   std::vector<StatsObserverInterface*> observers_;
 };
 
@@ -52,11 +55,17 @@
 class StatsPoller {
  public:
   StatsPoller(std::vector<StatsObserverInterface*> observers,
+              std::map<std::string, StatsProvider*> peers_to_observe);
+  StatsPoller(std::vector<StatsObserverInterface*> observers,
               std::map<std::string, TestPeer*> peers_to_observe);
 
   void PollStatsAndNotifyObservers();
 
-  void RegisterParticipantInCall(absl::string_view peer_name, TestPeer* peer);
+  void RegisterParticipantInCall(absl::string_view peer_name,
+                                 StatsProvider* peer);
+  // Unregister participant from stats poller. Returns true if participant was
+  // removed and false if participant wasn't found.
+  bool UnregisterParticipantInCall(absl::string_view peer_name);
 
  private:
   const std::vector<StatsObserverInterface*> observers_;
diff --git a/test/pc/e2e/stats_poller_test.cc b/test/pc/e2e/stats_poller_test.cc
new file mode 100644
index 0000000..02a3231
--- /dev/null
+++ b/test/pc/e2e/stats_poller_test.cc
@@ -0,0 +1,90 @@
+/*
+ *  Copyright (c) 2022 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 "test/pc/e2e/stats_poller.h"
+
+#include "api/stats/rtc_stats_collector_callback.h"
+#include "test/gmock.h"
+#include "test/gtest.h"
+
+namespace webrtc {
+namespace webrtc_pc_e2e {
+namespace {
+
+using ::testing::Eq;
+
+class TestStatsProvider : public StatsProvider {
+ public:
+  ~TestStatsProvider() override = default;
+
+  void GetStats(RTCStatsCollectorCallback* callback) override {
+    stats_collections_count_++;
+  }
+
+  int stats_collections_count() const { return stats_collections_count_; }
+
+ private:
+  int stats_collections_count_ = 0;
+};
+
+class MockStatsObserver : public StatsObserverInterface {
+ public:
+  ~MockStatsObserver() override = default;
+
+  MOCK_METHOD(void,
+              OnStatsReports,
+              (absl::string_view pc_label,
+               const rtc::scoped_refptr<const RTCStatsReport>& report));
+};
+
+TEST(StatsPollerTest, UnregisterParticipantAddedInCtor) {
+  TestStatsProvider alice;
+  TestStatsProvider bob;
+
+  MockStatsObserver stats_observer;
+
+  StatsPoller poller(/*observers=*/{&stats_observer},
+                     /*peers_to_observe=*/{{"alice", &alice}, {"bob", &bob}});
+  poller.PollStatsAndNotifyObservers();
+
+  EXPECT_THAT(alice.stats_collections_count(), Eq(1));
+  EXPECT_THAT(bob.stats_collections_count(), Eq(1));
+
+  poller.UnregisterParticipantInCall("bob");
+  poller.PollStatsAndNotifyObservers();
+
+  EXPECT_THAT(alice.stats_collections_count(), Eq(2));
+  EXPECT_THAT(bob.stats_collections_count(), Eq(1));
+}
+
+TEST(StatsPollerTest, UnregisterParticipantRegisteredInCall) {
+  TestStatsProvider alice;
+  TestStatsProvider bob;
+
+  MockStatsObserver stats_observer;
+
+  StatsPoller poller(/*observers=*/{&stats_observer},
+                     /*peers_to_observe=*/{{"alice", &alice}});
+  poller.RegisterParticipantInCall("bob", &bob);
+  poller.PollStatsAndNotifyObservers();
+
+  EXPECT_THAT(alice.stats_collections_count(), Eq(1));
+  EXPECT_THAT(bob.stats_collections_count(), Eq(1));
+
+  poller.UnregisterParticipantInCall("bob");
+  poller.PollStatsAndNotifyObservers();
+
+  EXPECT_THAT(alice.stats_collections_count(), Eq(2));
+  EXPECT_THAT(bob.stats_collections_count(), Eq(1));
+}
+
+}  // namespace
+}  // namespace webrtc_pc_e2e
+}  // namespace webrtc
diff --git a/test/pc/e2e/stats_provider.h b/test/pc/e2e/stats_provider.h
new file mode 100644
index 0000000..eef62d7
--- /dev/null
+++ b/test/pc/e2e/stats_provider.h
@@ -0,0 +1,29 @@
+/*
+ *  Copyright (c) 2022 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 TEST_PC_E2E_STATS_PROVIDER_H_
+#define TEST_PC_E2E_STATS_PROVIDER_H_
+
+#include "api/stats/rtc_stats_collector_callback.h"
+
+namespace webrtc {
+namespace webrtc_pc_e2e {
+
+class StatsProvider {
+ public:
+  virtual ~StatsProvider() = default;
+
+  virtual void GetStats(RTCStatsCollectorCallback* callback) = 0;
+};
+
+}  // namespace webrtc_pc_e2e
+}  // namespace webrtc
+
+#endif  // TEST_PC_E2E_STATS_PROVIDER_H_
diff --git a/test/pc/e2e/test_peer.h b/test/pc/e2e/test_peer.h
index e21c054..216b9d9 100644
--- a/test/pc/e2e/test_peer.h
+++ b/test/pc/e2e/test_peer.h
@@ -28,13 +28,16 @@
 #include "rtc_base/synchronization/mutex.h"
 #include "test/pc/e2e/peer_configurer.h"
 #include "test/pc/e2e/peer_connection_quality_test_params.h"
+#include "test/pc/e2e/stats_provider.h"
 
 namespace webrtc {
 namespace webrtc_pc_e2e {
 
 // Describes a single participant in the call.
-class TestPeer final {
+class TestPeer final : public StatsProvider {
  public:
+  ~TestPeer() override = default;
+
   const Params& params() const { return params_; }
 
   ConfigurableParams configurable_params() const;
@@ -45,6 +48,10 @@
   void SetVideoSubscription(
       PeerConnectionE2EQualityTestFixture::VideoSubscription subscription);
 
+  void GetStats(RTCStatsCollectorCallback* callback) override {
+    pc()->GetStats(callback);
+  }
+
   PeerConfigurerImpl::VideoSource ReleaseVideoSource(size_t i) {
     RTC_CHECK(wrapper_) << "TestPeer is already closed";
     return std::move(video_sources_[i]);