blob: cce1645bcab2dafff685e28ed41a0c0eeb236390 [file] [log] [blame]
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001/*
2 * libjingle
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are met:
6 *
7 * 1. Redistributions of source code must retain the above copyright notice,
8 * this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright notice,
10 * this list of conditions and the following disclaimer in the documentation
11 * and/or other materials provided with the distribution.
12 * 3. The name of the author may not be used to endorse or promote products
13 * derived from this software without specific prior written permission.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
16 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
17 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
18 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
19 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
21 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
22 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
23 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
24 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27#include <stdio.h>
28
29#include "talk/app/webrtc/statscollector.h"
30
31#include "talk/app/webrtc/mediastream.h"
32#include "talk/app/webrtc/videotrack.h"
33#include "talk/base/gunit.h"
34#include "talk/media/base/fakemediaengine.h"
35#include "talk/media/devices/fakedevicemanager.h"
36#include "talk/p2p/base/fakesession.h"
37#include "talk/session/media/channelmanager.h"
38#include "testing/base/public/gmock.h"
39
40using testing::_;
41using testing::DoAll;
42using testing::Return;
43using testing::ReturnNull;
44using testing::SetArgPointee;
45
46namespace cricket {
47
48class ChannelManager;
49class FakeDeviceManager;
50
51} // namespace cricket
52
53namespace {
54
55// Error return values
56const char kNotFound[] = "NOT FOUND";
57const char kNoReports[] = "NO REPORTS";
58
59class MockWebRtcSession : public webrtc::WebRtcSession {
60 public:
61 explicit MockWebRtcSession(cricket::ChannelManager* channel_manager)
62 : WebRtcSession(channel_manager, talk_base::Thread::Current(),
63 NULL, NULL, NULL) {
64 }
65 MOCK_METHOD0(video_channel, cricket::VideoChannel*());
66 MOCK_METHOD2(GetTrackIdBySsrc, bool(uint32, std::string*));
67 MOCK_METHOD1(GetStats, bool(cricket::SessionStats*));
68};
69
70class MockVideoMediaChannel : public cricket::FakeVideoMediaChannel {
71 public:
72 MockVideoMediaChannel()
73 : cricket::FakeVideoMediaChannel(NULL) {
74 }
75 // MOCK_METHOD0(transport_channel, cricket::TransportChannel*());
76 MOCK_METHOD1(GetStats, bool(cricket::VideoMediaInfo*));
77};
78
79std::string ExtractStatsValue(const std::string& type,
80 webrtc::StatsReports reports,
81 const std::string name) {
82 if (reports.empty()) {
83 return kNoReports;
84 }
85 for (size_t i = 0; i < reports.size(); ++i) {
86 if (reports[i].type != type)
87 continue;
88 webrtc::StatsReport::Values::const_iterator it =
89 reports[i].values.begin();
90 for (; it != reports[i].values.end(); ++it) {
91 if (it->name == name) {
92 return it->value;
93 }
94 }
95 }
96
97 return kNotFound;
98}
99
100// Finds the |n|-th report of type |type| in |reports|.
101// |n| starts from 1 for finding the first report.
102const webrtc::StatsReport* FindNthReportByType(webrtc::StatsReports reports,
103 const std::string& type,
104 int n) {
105 for (size_t i = 0; i < reports.size(); ++i) {
106 if (reports[i].type == type) {
107 n--;
108 if (n == 0)
109 return &reports[i];
110 }
111 }
112 return NULL;
113}
114
115const webrtc::StatsReport* FindReportById(webrtc::StatsReports reports,
116 const std::string& id) {
117 for (size_t i = 0; i < reports.size(); ++i) {
118 if (reports[i].id == id) {
119 return &reports[i];
120 }
121 }
122 return NULL;
123}
124
125std::string ExtractSsrcStatsValue(webrtc::StatsReports reports,
126 const std::string& name) {
127 return ExtractStatsValue(
128 webrtc::StatsReport::kStatsReportTypeSsrc, reports, name);
129}
130
131std::string ExtractBweStatsValue(webrtc::StatsReports reports,
132 const std::string& name) {
133 return ExtractStatsValue(
134 webrtc::StatsReport::kStatsReportTypeBwe, reports, name);
135}
136
137class StatsCollectorTest : public testing::Test {
138 protected:
139 StatsCollectorTest()
140 : media_engine_(new cricket::FakeMediaEngine),
141 channel_manager_(
142 new cricket::ChannelManager(media_engine_,
143 new cricket::FakeDeviceManager(),
144 talk_base::Thread::Current())),
145 session_(channel_manager_.get()) {
146 // By default, we ignore session GetStats calls.
147 EXPECT_CALL(session_, GetStats(_)).WillRepeatedly(Return(false));
148 }
149
150 cricket::FakeMediaEngine* media_engine_;
151 talk_base::scoped_ptr<cricket::ChannelManager> channel_manager_;
152 MockWebRtcSession session_;
153};
154
155// This test verifies that 64-bit counters are passed successfully.
156TEST_F(StatsCollectorTest, BytesCounterHandles64Bits) {
157 webrtc::StatsCollector stats; // Implementation under test.
158 MockVideoMediaChannel* media_channel = new MockVideoMediaChannel;
159 cricket::VideoChannel video_channel(talk_base::Thread::Current(),
160 media_engine_, media_channel, &session_, "", false, NULL);
161 webrtc::StatsReports reports; // returned values.
162 cricket::VideoSenderInfo video_sender_info;
163 cricket::VideoMediaInfo stats_read;
164 const uint32 kSsrcOfTrack = 1234;
165 const std::string kNameOfTrack("somename");
166 // The number of bytes must be larger than 0xFFFFFFFF for this test.
167 const int64 kBytesSent = 12345678901234LL;
168 const std::string kBytesSentString("12345678901234");
169
170 stats.set_session(&session_);
171 talk_base::scoped_refptr<webrtc::MediaStream> stream(
172 webrtc::MediaStream::Create("streamlabel"));
173 stream->AddTrack(webrtc::VideoTrack::Create(kNameOfTrack, NULL));
174 stats.AddStream(stream);
175
176 // Construct a stats value to read.
177 video_sender_info.ssrcs.push_back(1234);
178 video_sender_info.bytes_sent = kBytesSent;
179 stats_read.senders.push_back(video_sender_info);
180
181 EXPECT_CALL(session_, video_channel())
182 .WillRepeatedly(Return(&video_channel));
183 EXPECT_CALL(*media_channel, GetStats(_))
184 .WillOnce(DoAll(SetArgPointee<0>(stats_read),
185 Return(true)));
186 EXPECT_CALL(session_, GetTrackIdBySsrc(kSsrcOfTrack, _))
187 .WillOnce(DoAll(SetArgPointee<1>(kNameOfTrack),
188 Return(true)));
189 stats.UpdateStats();
190 stats.GetStats(NULL, &reports);
191 std::string result = ExtractSsrcStatsValue(reports, "bytesSent");
192 EXPECT_EQ(kBytesSentString, result);
193}
194
195// Test that BWE information is reported via stats.
196TEST_F(StatsCollectorTest, BandwidthEstimationInfoIsReported) {
197 webrtc::StatsCollector stats; // Implementation under test.
198 MockVideoMediaChannel* media_channel = new MockVideoMediaChannel;
199 cricket::VideoChannel video_channel(talk_base::Thread::Current(),
200 media_engine_, media_channel, &session_, "", false, NULL);
201 webrtc::StatsReports reports; // returned values.
202 cricket::VideoSenderInfo video_sender_info;
203 cricket::VideoMediaInfo stats_read;
204 // Set up an SSRC just to test that we get both kinds of stats back: SSRC and
205 // BWE.
206 const uint32 kSsrcOfTrack = 1234;
207 const std::string kNameOfTrack("somename");
208 const int64 kBytesSent = 12345678901234LL;
209 const std::string kBytesSentString("12345678901234");
210
211 stats.set_session(&session_);
212 talk_base::scoped_refptr<webrtc::MediaStream> stream(
213 webrtc::MediaStream::Create("streamlabel"));
214 stream->AddTrack(webrtc::VideoTrack::Create(kNameOfTrack, NULL));
215 stats.AddStream(stream);
216
217 // Construct a stats value to read.
218 video_sender_info.ssrcs.push_back(1234);
219 video_sender_info.bytes_sent = kBytesSent;
220 stats_read.senders.push_back(video_sender_info);
221 cricket::BandwidthEstimationInfo bwe;
222 const int kTargetEncBitrate = 123456;
223 const std::string kTargetEncBitrateString("123456");
224 bwe.target_enc_bitrate = kTargetEncBitrate;
225 stats_read.bw_estimations.push_back(bwe);
226
227 EXPECT_CALL(session_, video_channel())
228 .WillRepeatedly(Return(&video_channel));
229 EXPECT_CALL(*media_channel, GetStats(_))
230 .WillOnce(DoAll(SetArgPointee<0>(stats_read),
231 Return(true)));
232 EXPECT_CALL(session_, GetTrackIdBySsrc(kSsrcOfTrack, _))
233 .WillOnce(DoAll(SetArgPointee<1>(kNameOfTrack),
234 Return(true)));
235 stats.UpdateStats();
236 stats.GetStats(NULL, &reports);
237 std::string result = ExtractSsrcStatsValue(reports, "bytesSent");
238 EXPECT_EQ(kBytesSentString, result);
239 result = ExtractBweStatsValue(reports, "googTargetEncBitrate");
240 EXPECT_EQ(kTargetEncBitrateString, result);
241}
242
243// This test verifies that an object of type "googSession" always
244// exists in the returned stats.
245TEST_F(StatsCollectorTest, SessionObjectExists) {
246 webrtc::StatsCollector stats; // Implementation under test.
247 webrtc::StatsReports reports; // returned values.
248 stats.set_session(&session_);
249 EXPECT_CALL(session_, video_channel())
250 .WillRepeatedly(ReturnNull());
251 stats.UpdateStats();
252 stats.GetStats(NULL, &reports);
253 const webrtc::StatsReport* session_report = FindNthReportByType(
254 reports, webrtc::StatsReport::kStatsReportTypeSession, 1);
255 EXPECT_FALSE(session_report == NULL);
256}
257
258// This test verifies that only one object of type "googSession" exists
259// in the returned stats.
260TEST_F(StatsCollectorTest, OnlyOneSessionObjectExists) {
261 webrtc::StatsCollector stats; // Implementation under test.
262 webrtc::StatsReports reports; // returned values.
263 stats.set_session(&session_);
264 EXPECT_CALL(session_, video_channel())
265 .WillRepeatedly(ReturnNull());
266 stats.UpdateStats();
267 stats.UpdateStats();
268 stats.GetStats(NULL, &reports);
269 const webrtc::StatsReport* session_report = FindNthReportByType(
270 reports, webrtc::StatsReport::kStatsReportTypeSession, 1);
271 EXPECT_FALSE(session_report == NULL);
272 session_report = FindNthReportByType(
273 reports, webrtc::StatsReport::kStatsReportTypeSession, 2);
274 EXPECT_EQ(NULL, session_report);
275}
276
277// This test verifies that the empty track report exists in the returned stats
278// without calling StatsCollector::UpdateStats.
279TEST_F(StatsCollectorTest, TrackObjectExistsWithoutUpdateStats) {
280 webrtc::StatsCollector stats; // Implementation under test.
281 MockVideoMediaChannel* media_channel = new MockVideoMediaChannel;
282 cricket::VideoChannel video_channel(talk_base::Thread::Current(),
283 media_engine_, media_channel, &session_, "", false, NULL);
284 const std::string kTrackId("somename");
285 talk_base::scoped_refptr<webrtc::MediaStream> stream(
286 webrtc::MediaStream::Create("streamlabel"));
287 talk_base::scoped_refptr<webrtc::VideoTrack> track =
288 webrtc::VideoTrack::Create(kTrackId, NULL);
289 stream->AddTrack(track);
290 stats.AddStream(stream);
291
292 stats.set_session(&session_);
293
294 webrtc::StatsReports reports;
295
296 // Verfies the existence of the track report.
297 stats.GetStats(NULL, &reports);
298 EXPECT_EQ((size_t)1, reports.size());
299 EXPECT_EQ(std::string(webrtc::StatsReport::kStatsReportTypeTrack),
300 reports[0].type);
301
302 std::string trackValue =
303 ExtractStatsValue(webrtc::StatsReport::kStatsReportTypeTrack,
304 reports,
305 webrtc::StatsReport::kStatsValueNameTrackId);
306 EXPECT_EQ(kTrackId, trackValue);
307}
308
309// This test verifies that the empty track report exists in the returned stats
310// when StatsCollector::UpdateStats is called with ssrc stats.
311TEST_F(StatsCollectorTest, TrackAndSsrcObjectExistAfterUpdateSsrcStats) {
312 webrtc::StatsCollector stats; // Implementation under test.
313 MockVideoMediaChannel* media_channel = new MockVideoMediaChannel;
314 cricket::VideoChannel video_channel(talk_base::Thread::Current(),
315 media_engine_, media_channel, &session_, "", false, NULL);
316 const std::string kTrackId("somename");
317 talk_base::scoped_refptr<webrtc::MediaStream> stream(
318 webrtc::MediaStream::Create("streamlabel"));
319 talk_base::scoped_refptr<webrtc::VideoTrack> track =
320 webrtc::VideoTrack::Create(kTrackId, NULL);
321 stream->AddTrack(track);
322 stats.AddStream(stream);
323
324 stats.set_session(&session_);
325
326 webrtc::StatsReports reports;
327
328 // Constructs an ssrc stats update.
329 cricket::VideoSenderInfo video_sender_info;
330 cricket::VideoMediaInfo stats_read;
331 const uint32 kSsrcOfTrack = 1234;
332 const int64 kBytesSent = 12345678901234LL;
333
334 // Construct a stats value to read.
335 video_sender_info.ssrcs.push_back(1234);
336 video_sender_info.bytes_sent = kBytesSent;
337 stats_read.senders.push_back(video_sender_info);
338
339 EXPECT_CALL(session_, video_channel())
340 .WillRepeatedly(Return(&video_channel));
341 EXPECT_CALL(*media_channel, GetStats(_))
342 .WillOnce(DoAll(SetArgPointee<0>(stats_read),
343 Return(true)));
344 EXPECT_CALL(session_, GetTrackIdBySsrc(kSsrcOfTrack, _))
345 .WillOnce(DoAll(SetArgPointee<1>(kTrackId),
346 Return(true)));
347
348 stats.UpdateStats();
349 stats.GetStats(NULL, &reports);
350 // |reports| should contain one session report, one track report, and one ssrc
351 // report.
352 EXPECT_EQ((size_t)3, reports.size());
353 const webrtc::StatsReport* track_report = FindNthReportByType(
354 reports, webrtc::StatsReport::kStatsReportTypeTrack, 1);
355 EXPECT_FALSE(track_report == NULL);
356
357 stats.GetStats(track, &reports);
358 // |reports| should contain one session report, one track report, and one ssrc
359 // report.
360 EXPECT_EQ((size_t)3, reports.size());
361 track_report = FindNthReportByType(
362 reports, webrtc::StatsReport::kStatsReportTypeTrack, 1);
363 EXPECT_FALSE(track_report == NULL);
364
365 std::string ssrc_id = ExtractSsrcStatsValue(
366 reports, webrtc::StatsReport::kStatsValueNameSsrc);
367 EXPECT_EQ(talk_base::ToString<uint32>(kSsrcOfTrack), ssrc_id);
368
369 std::string track_id = ExtractSsrcStatsValue(
370 reports, webrtc::StatsReport::kStatsValueNameTrackId);
371 EXPECT_EQ(kTrackId, track_id);
372}
373
374// This test verifies that an SSRC object has the identifier of a Transport
375// stats object, and that this transport stats object exists in stats.
376TEST_F(StatsCollectorTest, TransportObjectLinkedFromSsrcObject) {
377 webrtc::StatsCollector stats; // Implementation under test.
378 MockVideoMediaChannel* media_channel = new MockVideoMediaChannel;
379 // The content_name known by the video channel.
380 const std::string kVcName("vcname");
381 cricket::VideoChannel video_channel(talk_base::Thread::Current(),
382 media_engine_, media_channel, &session_, kVcName, false, NULL);
383 const std::string kTrackId("somename");
384 talk_base::scoped_refptr<webrtc::MediaStream> stream(
385 webrtc::MediaStream::Create("streamlabel"));
386 talk_base::scoped_refptr<webrtc::VideoTrack> track =
387 webrtc::VideoTrack::Create(kTrackId, NULL);
388 stream->AddTrack(track);
389 stats.AddStream(stream);
390
391 stats.set_session(&session_);
392
393 webrtc::StatsReports reports;
394
395 // Constructs an ssrc stats update.
396 cricket::VideoSenderInfo video_sender_info;
397 cricket::VideoMediaInfo stats_read;
398 const uint32 kSsrcOfTrack = 1234;
399 const int64 kBytesSent = 12345678901234LL;
400
401 // Construct a stats value to read.
402 video_sender_info.ssrcs.push_back(1234);
403 video_sender_info.bytes_sent = kBytesSent;
404 stats_read.senders.push_back(video_sender_info);
405
406 EXPECT_CALL(session_, video_channel())
407 .WillRepeatedly(Return(&video_channel));
408 EXPECT_CALL(*media_channel, GetStats(_))
409 .WillRepeatedly(DoAll(SetArgPointee<0>(stats_read),
410 Return(true)));
411 EXPECT_CALL(session_, GetTrackIdBySsrc(kSsrcOfTrack, _))
412 .WillOnce(DoAll(SetArgPointee<1>(kTrackId),
413 Return(true)));
414
415 // Instruct the session to return stats containing the transport channel.
416 const std::string kTransportName("trspname");
417 cricket::SessionStats session_stats;
418 cricket::TransportStats transport_stats;
419 cricket::TransportChannelStats channel_stats;
420 channel_stats.component = 1;
421 transport_stats.content_name = kTransportName;
422 transport_stats.channel_stats.push_back(channel_stats);
423
424 session_stats.transport_stats[kTransportName] = transport_stats;
425 session_stats.proxy_to_transport[kVcName] = kTransportName;
426 EXPECT_CALL(session_, GetStats(_))
427 .WillRepeatedly(DoAll(SetArgPointee<0>(session_stats),
428 Return(true)));
429
430 stats.UpdateStats();
431 stats.GetStats(NULL, &reports);
432 std::string transport_id = ExtractStatsValue(
433 webrtc::StatsReport::kStatsReportTypeSsrc,
434 reports,
435 webrtc::StatsReport::kStatsValueNameTransportId);
436 ASSERT_NE(kNotFound, transport_id);
437 const webrtc::StatsReport* transport_report = FindReportById(reports,
438 transport_id);
439 ASSERT_FALSE(transport_report == NULL);
440}
441
442} // namespace