| OLD | NEW |
| (Empty) |
| 1 /* | |
| 2 * Copyright 2016 The WebRTC project authors. All Rights Reserved. | |
| 3 * | |
| 4 * Use of this source code is governed by a BSD-style license | |
| 5 * that can be found in the LICENSE file in the root of the source | |
| 6 * tree. An additional intellectual property rights grant can be found | |
| 7 * in the file PATENTS. All contributing project authors may | |
| 8 * be found in the AUTHORS file in the root of the source tree. | |
| 9 */ | |
| 10 | |
| 11 #include "webrtc/stats/rtcstatscollector.h" | |
| 12 | |
| 13 #include <memory> | |
| 14 #include <string> | |
| 15 #include <vector> | |
| 16 | |
| 17 #include "webrtc/api/jsepsessiondescription.h" | |
| 18 #include "webrtc/api/rtcstats_objects.h" | |
| 19 #include "webrtc/api/rtcstatsreport.h" | |
| 20 #include "webrtc/api/test/mock_datachannel.h" | |
| 21 #include "webrtc/api/test/mock_peerconnection.h" | |
| 22 #include "webrtc/api/test/mock_webrtcsession.h" | |
| 23 #include "webrtc/base/checks.h" | |
| 24 #include "webrtc/base/fakeclock.h" | |
| 25 #include "webrtc/base/gunit.h" | |
| 26 #include "webrtc/base/logging.h" | |
| 27 #include "webrtc/base/thread_checker.h" | |
| 28 #include "webrtc/base/timedelta.h" | |
| 29 #include "webrtc/base/timeutils.h" | |
| 30 #include "webrtc/media/base/fakemediaengine.h" | |
| 31 | |
| 32 using testing::Return; | |
| 33 using testing::ReturnRef; | |
| 34 | |
| 35 namespace webrtc { | |
| 36 | |
| 37 namespace { | |
| 38 | |
| 39 const int64_t kGetStatsReportTimeoutMs = 1000; | |
| 40 | |
| 41 class RTCStatsCollectorTestHelper : public SetSessionDescriptionObserver { | |
| 42 public: | |
| 43 RTCStatsCollectorTestHelper() | |
| 44 : worker_thread_(rtc::Thread::Current()), | |
| 45 network_thread_(rtc::Thread::Current()), | |
| 46 channel_manager_(new cricket::ChannelManager( | |
| 47 new cricket::FakeMediaEngine(), | |
| 48 worker_thread_, | |
| 49 network_thread_)), | |
| 50 media_controller_( | |
| 51 MediaControllerInterface::Create(cricket::MediaConfig(), | |
| 52 worker_thread_, | |
| 53 channel_manager_.get())), | |
| 54 session_(media_controller_.get()), | |
| 55 pc_() { | |
| 56 EXPECT_CALL(pc_, session()).WillRepeatedly(Return(&session_)); | |
| 57 EXPECT_CALL(pc_, sctp_data_channels()).WillRepeatedly( | |
| 58 ReturnRef(data_channels_)); | |
| 59 } | |
| 60 | |
| 61 rtc::ScopedFakeClock& fake_clock() { return fake_clock_; } | |
| 62 MockWebRtcSession& session() { return session_; } | |
| 63 MockPeerConnection& pc() { return pc_; } | |
| 64 std::vector<rtc::scoped_refptr<DataChannel>>& data_channels() { | |
| 65 return data_channels_; | |
| 66 } | |
| 67 | |
| 68 // SetSessionDescriptionObserver overrides. | |
| 69 void OnSuccess() override {} | |
| 70 void OnFailure(const std::string& error) override { | |
| 71 RTC_NOTREACHED() << error; | |
| 72 } | |
| 73 | |
| 74 private: | |
| 75 rtc::ScopedFakeClock fake_clock_; | |
| 76 rtc::Thread* const worker_thread_; | |
| 77 rtc::Thread* const network_thread_; | |
| 78 std::unique_ptr<cricket::ChannelManager> channel_manager_; | |
| 79 std::unique_ptr<webrtc::MediaControllerInterface> media_controller_; | |
| 80 MockWebRtcSession session_; | |
| 81 MockPeerConnection pc_; | |
| 82 | |
| 83 std::vector<rtc::scoped_refptr<DataChannel>> data_channels_; | |
| 84 }; | |
| 85 | |
| 86 class RTCTestStats : public RTCStats { | |
| 87 public: | |
| 88 RTCTestStats(const std::string& id, int64_t timestamp_us) | |
| 89 : RTCStats(id, timestamp_us), | |
| 90 dummy_stat("dummyStat") {} | |
| 91 | |
| 92 WEBRTC_RTCSTATS_IMPL(RTCStats, RTCTestStats, | |
| 93 &dummy_stat); | |
| 94 | |
| 95 RTCStatsMember<int32_t> dummy_stat; | |
| 96 }; | |
| 97 | |
| 98 const char RTCTestStats::kType[] = "test-stats"; | |
| 99 | |
| 100 // Overrides the stats collection to verify thread usage and that the resulting | |
| 101 // partial reports are merged. | |
| 102 class FakeRTCStatsCollector : public RTCStatsCollector, | |
| 103 public RTCStatsCollectorCallback { | |
| 104 public: | |
| 105 static rtc::scoped_refptr<FakeRTCStatsCollector> Create( | |
| 106 PeerConnection* pc, | |
| 107 int64_t cache_lifetime_us) { | |
| 108 return rtc::scoped_refptr<FakeRTCStatsCollector>( | |
| 109 new rtc::RefCountedObject<FakeRTCStatsCollector>( | |
| 110 pc, cache_lifetime_us)); | |
| 111 } | |
| 112 | |
| 113 // RTCStatsCollectorCallback implementation. | |
| 114 void OnStatsDelivered( | |
| 115 const rtc::scoped_refptr<const RTCStatsReport>& report) override { | |
| 116 EXPECT_TRUE(signaling_thread_->IsCurrent()); | |
| 117 rtc::CritScope cs(&lock_); | |
| 118 delivered_report_ = report; | |
| 119 } | |
| 120 | |
| 121 void VerifyThreadUsageAndResultsMerging() { | |
| 122 GetStatsReport(rtc::scoped_refptr<RTCStatsCollectorCallback>(this)); | |
| 123 EXPECT_TRUE_WAIT(HasVerifiedResults(), kGetStatsReportTimeoutMs); | |
| 124 } | |
| 125 | |
| 126 bool HasVerifiedResults() { | |
| 127 EXPECT_TRUE(signaling_thread_->IsCurrent()); | |
| 128 rtc::CritScope cs(&lock_); | |
| 129 if (!delivered_report_) | |
| 130 return false; | |
| 131 EXPECT_EQ(produced_on_signaling_thread_, 1); | |
| 132 EXPECT_EQ(produced_on_worker_thread_, 1); | |
| 133 EXPECT_EQ(produced_on_network_thread_, 1); | |
| 134 | |
| 135 EXPECT_TRUE(delivered_report_->Get("SignalingThreadStats")); | |
| 136 EXPECT_TRUE(delivered_report_->Get("WorkerThreadStats")); | |
| 137 EXPECT_TRUE(delivered_report_->Get("NetworkThreadStats")); | |
| 138 | |
| 139 produced_on_signaling_thread_ = 0; | |
| 140 produced_on_worker_thread_ = 0; | |
| 141 produced_on_network_thread_ = 0; | |
| 142 delivered_report_ = nullptr; | |
| 143 return true; | |
| 144 } | |
| 145 | |
| 146 protected: | |
| 147 FakeRTCStatsCollector( | |
| 148 PeerConnection* pc, | |
| 149 int64_t cache_lifetime) | |
| 150 : RTCStatsCollector(pc, cache_lifetime), | |
| 151 signaling_thread_(pc->session()->signaling_thread()), | |
| 152 worker_thread_(pc->session()->worker_thread()), | |
| 153 network_thread_(pc->session()->network_thread()) { | |
| 154 } | |
| 155 | |
| 156 void ProducePartialResultsOnSignalingThread(int64_t timestamp_us) override { | |
| 157 EXPECT_TRUE(signaling_thread_->IsCurrent()); | |
| 158 { | |
| 159 rtc::CritScope cs(&lock_); | |
| 160 EXPECT_FALSE(delivered_report_); | |
| 161 ++produced_on_signaling_thread_; | |
| 162 } | |
| 163 | |
| 164 rtc::scoped_refptr<RTCStatsReport> signaling_report = | |
| 165 RTCStatsReport::Create(); | |
| 166 signaling_report->AddStats(std::unique_ptr<const RTCStats>( | |
| 167 new RTCTestStats("SignalingThreadStats", timestamp_us))); | |
| 168 AddPartialResults(signaling_report); | |
| 169 } | |
| 170 void ProducePartialResultsOnWorkerThread(int64_t timestamp_us) override { | |
| 171 EXPECT_TRUE(worker_thread_->IsCurrent()); | |
| 172 { | |
| 173 rtc::CritScope cs(&lock_); | |
| 174 EXPECT_FALSE(delivered_report_); | |
| 175 ++produced_on_worker_thread_; | |
| 176 } | |
| 177 | |
| 178 rtc::scoped_refptr<RTCStatsReport> worker_report = RTCStatsReport::Create(); | |
| 179 worker_report->AddStats(std::unique_ptr<const RTCStats>( | |
| 180 new RTCTestStats("WorkerThreadStats", timestamp_us))); | |
| 181 AddPartialResults(worker_report); | |
| 182 } | |
| 183 void ProducePartialResultsOnNetworkThread(int64_t timestamp_us) override { | |
| 184 EXPECT_TRUE(network_thread_->IsCurrent()); | |
| 185 { | |
| 186 rtc::CritScope cs(&lock_); | |
| 187 EXPECT_FALSE(delivered_report_); | |
| 188 ++produced_on_network_thread_; | |
| 189 } | |
| 190 | |
| 191 rtc::scoped_refptr<RTCStatsReport> network_report = | |
| 192 RTCStatsReport::Create(); | |
| 193 network_report->AddStats(std::unique_ptr<const RTCStats>( | |
| 194 new RTCTestStats("NetworkThreadStats", timestamp_us))); | |
| 195 AddPartialResults(network_report); | |
| 196 } | |
| 197 | |
| 198 private: | |
| 199 rtc::Thread* const signaling_thread_; | |
| 200 rtc::Thread* const worker_thread_; | |
| 201 rtc::Thread* const network_thread_; | |
| 202 | |
| 203 rtc::CriticalSection lock_; | |
| 204 rtc::scoped_refptr<const RTCStatsReport> delivered_report_; | |
| 205 int produced_on_signaling_thread_ = 0; | |
| 206 int produced_on_worker_thread_ = 0; | |
| 207 int produced_on_network_thread_ = 0; | |
| 208 }; | |
| 209 | |
| 210 class StatsCallback : public RTCStatsCollectorCallback { | |
| 211 public: | |
| 212 static rtc::scoped_refptr<StatsCallback> Create( | |
| 213 rtc::scoped_refptr<const RTCStatsReport>* report_ptr = nullptr) { | |
| 214 return rtc::scoped_refptr<StatsCallback>( | |
| 215 new rtc::RefCountedObject<StatsCallback>(report_ptr)); | |
| 216 } | |
| 217 | |
| 218 void OnStatsDelivered( | |
| 219 const rtc::scoped_refptr<const RTCStatsReport>& report) override { | |
| 220 EXPECT_TRUE(thread_checker_.CalledOnValidThread()); | |
| 221 report_ = report; | |
| 222 if (report_ptr_) | |
| 223 *report_ptr_ = report_; | |
| 224 } | |
| 225 | |
| 226 rtc::scoped_refptr<const RTCStatsReport> report() const { | |
| 227 EXPECT_TRUE(thread_checker_.CalledOnValidThread()); | |
| 228 return report_; | |
| 229 } | |
| 230 | |
| 231 protected: | |
| 232 explicit StatsCallback(rtc::scoped_refptr<const RTCStatsReport>* report_ptr) | |
| 233 : report_ptr_(report_ptr) {} | |
| 234 | |
| 235 private: | |
| 236 rtc::ThreadChecker thread_checker_; | |
| 237 rtc::scoped_refptr<const RTCStatsReport> report_; | |
| 238 rtc::scoped_refptr<const RTCStatsReport>* report_ptr_; | |
| 239 }; | |
| 240 | |
| 241 class RTCStatsCollectorTest : public testing::Test { | |
| 242 public: | |
| 243 RTCStatsCollectorTest() | |
| 244 : test_(new rtc::RefCountedObject<RTCStatsCollectorTestHelper>()), | |
| 245 collector_(RTCStatsCollector::Create( | |
| 246 &test_->pc(), 50 * rtc::kNumMicrosecsPerMillisec)) { | |
| 247 } | |
| 248 | |
| 249 rtc::scoped_refptr<const RTCStatsReport> GetStatsReport() { | |
| 250 rtc::scoped_refptr<StatsCallback> callback = StatsCallback::Create(); | |
| 251 collector_->GetStatsReport(callback); | |
| 252 EXPECT_TRUE_WAIT(callback->report(), kGetStatsReportTimeoutMs); | |
| 253 return callback->report(); | |
| 254 } | |
| 255 | |
| 256 protected: | |
| 257 rtc::scoped_refptr<RTCStatsCollectorTestHelper> test_; | |
| 258 rtc::scoped_refptr<RTCStatsCollector> collector_; | |
| 259 }; | |
| 260 | |
| 261 TEST_F(RTCStatsCollectorTest, SingleCallback) { | |
| 262 rtc::scoped_refptr<const RTCStatsReport> result; | |
| 263 collector_->GetStatsReport(StatsCallback::Create(&result)); | |
| 264 EXPECT_TRUE_WAIT(result, kGetStatsReportTimeoutMs); | |
| 265 } | |
| 266 | |
| 267 TEST_F(RTCStatsCollectorTest, MultipleCallbacks) { | |
| 268 rtc::scoped_refptr<const RTCStatsReport> a; | |
| 269 rtc::scoped_refptr<const RTCStatsReport> b; | |
| 270 rtc::scoped_refptr<const RTCStatsReport> c; | |
| 271 collector_->GetStatsReport(StatsCallback::Create(&a)); | |
| 272 collector_->GetStatsReport(StatsCallback::Create(&b)); | |
| 273 collector_->GetStatsReport(StatsCallback::Create(&c)); | |
| 274 EXPECT_TRUE_WAIT(a, kGetStatsReportTimeoutMs); | |
| 275 EXPECT_TRUE_WAIT(b, kGetStatsReportTimeoutMs); | |
| 276 EXPECT_TRUE_WAIT(c, kGetStatsReportTimeoutMs); | |
| 277 EXPECT_EQ(a.get(), b.get()); | |
| 278 EXPECT_EQ(b.get(), c.get()); | |
| 279 } | |
| 280 | |
| 281 TEST_F(RTCStatsCollectorTest, CachedStatsReports) { | |
| 282 // Caching should ensure |a| and |b| are the same report. | |
| 283 rtc::scoped_refptr<const RTCStatsReport> a = GetStatsReport(); | |
| 284 rtc::scoped_refptr<const RTCStatsReport> b = GetStatsReport(); | |
| 285 EXPECT_EQ(a.get(), b.get()); | |
| 286 // Invalidate cache by clearing it. | |
| 287 collector_->ClearCachedStatsReport(); | |
| 288 rtc::scoped_refptr<const RTCStatsReport> c = GetStatsReport(); | |
| 289 EXPECT_NE(b.get(), c.get()); | |
| 290 // Invalidate cache by advancing time. | |
| 291 test_->fake_clock().AdvanceTime(rtc::TimeDelta::FromMilliseconds(51)); | |
| 292 rtc::scoped_refptr<const RTCStatsReport> d = GetStatsReport(); | |
| 293 EXPECT_TRUE(d); | |
| 294 EXPECT_NE(c.get(), d.get()); | |
| 295 } | |
| 296 | |
| 297 TEST_F(RTCStatsCollectorTest, MultipleCallbacksWithInvalidatedCacheInBetween) { | |
| 298 rtc::scoped_refptr<const RTCStatsReport> a; | |
| 299 rtc::scoped_refptr<const RTCStatsReport> b; | |
| 300 rtc::scoped_refptr<const RTCStatsReport> c; | |
| 301 collector_->GetStatsReport(StatsCallback::Create(&a)); | |
| 302 collector_->GetStatsReport(StatsCallback::Create(&b)); | |
| 303 // Cache is invalidated after 50 ms. | |
| 304 test_->fake_clock().AdvanceTime(rtc::TimeDelta::FromMilliseconds(51)); | |
| 305 collector_->GetStatsReport(StatsCallback::Create(&c)); | |
| 306 EXPECT_TRUE_WAIT(a, kGetStatsReportTimeoutMs); | |
| 307 EXPECT_TRUE_WAIT(b, kGetStatsReportTimeoutMs); | |
| 308 EXPECT_TRUE_WAIT(c, kGetStatsReportTimeoutMs); | |
| 309 EXPECT_EQ(a.get(), b.get()); | |
| 310 // The act of doing |AdvanceTime| processes all messages. If this was not the | |
| 311 // case we might not require |c| to be fresher than |b|. | |
| 312 EXPECT_NE(c.get(), b.get()); | |
| 313 } | |
| 314 | |
| 315 TEST_F(RTCStatsCollectorTest, CollectRTCPeerConnectionStats) { | |
| 316 int64_t before = rtc::TimeUTCMicros(); | |
| 317 rtc::scoped_refptr<const RTCStatsReport> report = GetStatsReport(); | |
| 318 int64_t after = rtc::TimeUTCMicros(); | |
| 319 EXPECT_EQ(report->GetStatsOfType<RTCPeerConnectionStats>().size(), | |
| 320 static_cast<size_t>(1)) << "Expecting 1 RTCPeerConnectionStats."; | |
| 321 const RTCStats* stats = report->Get("RTCPeerConnection"); | |
| 322 EXPECT_TRUE(stats); | |
| 323 EXPECT_LE(before, stats->timestamp_us()); | |
| 324 EXPECT_LE(stats->timestamp_us(), after); | |
| 325 { | |
| 326 // Expected stats with no data channels | |
| 327 const RTCPeerConnectionStats& pcstats = | |
| 328 stats->cast_to<RTCPeerConnectionStats>(); | |
| 329 EXPECT_EQ(*pcstats.data_channels_opened, static_cast<uint32_t>(0)); | |
| 330 EXPECT_EQ(*pcstats.data_channels_closed, static_cast<uint32_t>(0)); | |
| 331 } | |
| 332 | |
| 333 test_->data_channels().push_back( | |
| 334 new MockDataChannel(DataChannelInterface::kConnecting)); | |
| 335 test_->data_channels().push_back( | |
| 336 new MockDataChannel(DataChannelInterface::kOpen)); | |
| 337 test_->data_channels().push_back( | |
| 338 new MockDataChannel(DataChannelInterface::kClosing)); | |
| 339 test_->data_channels().push_back( | |
| 340 new MockDataChannel(DataChannelInterface::kClosed)); | |
| 341 | |
| 342 collector_->ClearCachedStatsReport(); | |
| 343 report = GetStatsReport(); | |
| 344 EXPECT_EQ(report->GetStatsOfType<RTCPeerConnectionStats>().size(), | |
| 345 static_cast<size_t>(1)) << "Expecting 1 RTCPeerConnectionStats."; | |
| 346 stats = report->Get("RTCPeerConnection"); | |
| 347 EXPECT_TRUE(stats); | |
| 348 { | |
| 349 // Expected stats with the above four data channels | |
| 350 // TODO(hbos): When the |RTCPeerConnectionStats| is the number of data | |
| 351 // channels that have been opened and closed, not the numbers currently | |
| 352 // open/closed, we would expect opened >= closed and (opened - closed) to be | |
| 353 // the number currently open. crbug.com/636818. | |
| 354 const RTCPeerConnectionStats& pcstats = | |
| 355 stats->cast_to<RTCPeerConnectionStats>(); | |
| 356 EXPECT_EQ(*pcstats.data_channels_opened, static_cast<uint32_t>(1)); | |
| 357 EXPECT_EQ(*pcstats.data_channels_closed, static_cast<uint32_t>(3)); | |
| 358 } | |
| 359 } | |
| 360 | |
| 361 class RTCStatsCollectorTestWithFakeCollector : public testing::Test { | |
| 362 public: | |
| 363 RTCStatsCollectorTestWithFakeCollector() | |
| 364 : test_(new rtc::RefCountedObject<RTCStatsCollectorTestHelper>()), | |
| 365 collector_(FakeRTCStatsCollector::Create( | |
| 366 &test_->pc(), 50 * rtc::kNumMicrosecsPerMillisec)) { | |
| 367 } | |
| 368 | |
| 369 protected: | |
| 370 rtc::scoped_refptr<RTCStatsCollectorTestHelper> test_; | |
| 371 rtc::scoped_refptr<FakeRTCStatsCollector> collector_; | |
| 372 }; | |
| 373 | |
| 374 TEST_F(RTCStatsCollectorTestWithFakeCollector, ThreadUsageAndResultsMerging) { | |
| 375 collector_->VerifyThreadUsageAndResultsMerging(); | |
| 376 } | |
| 377 | |
| 378 } // namespace | |
| 379 | |
| 380 } // namespace webrtc | |
| OLD | NEW |