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