1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "base/command_line.h" 6 #include "base/json/json_reader.h" 7 #include "base/strings/utf_string_conversions.h" 8 #include "base/time/time.h" 9 #include "base/values.h" 10 #include "content/public/common/content_switches.h" 11 #include "content/public/test/browser_test_utils.h" 12 #include "content/public/test/content_browser_test.h" 13 #include "content/public/test/content_browser_test_utils.h" 14 #include "content/shell/browser/shell.h" 15 #include "media/base/media_switches.h" 16 #include "net/test/embedded_test_server/embedded_test_server.h" 17 18 using std::string; 19 namespace content { 20 21 struct SsrcEntry { 22 string GetSsrcAttributeString() const { 23 std::stringstream ss; 24 ss << "a=ssrc:" << id; 25 std::map<string, string>::const_iterator iter; 26 for (iter = properties.begin(); iter != properties.end(); ++iter) { 27 ss << " " << iter->first << ":" << iter->second; 28 } 29 return ss.str(); 30 } 31 32 string GetAsJSON() const { 33 std::stringstream ss; 34 ss << "{"; 35 std::map<string, string>::const_iterator iter; 36 for (iter = properties.begin(); iter != properties.end(); ++iter) { 37 if (iter != properties.begin()) 38 ss << ","; 39 ss << "\"" << iter->first << "\":\"" << iter->second << "\""; 40 } 41 ss << "}"; 42 return ss.str(); 43 } 44 45 string id; 46 std::map<string, string> properties; 47 }; 48 49 struct EventEntry { 50 string type; 51 string value; 52 }; 53 54 struct StatsUnit { 55 string GetString() const { 56 std::stringstream ss; 57 ss << "{timestamp:" << timestamp << ", values:["; 58 std::map<string, string>::const_iterator iter; 59 for (iter = values.begin(); iter != values.end(); ++iter) { 60 ss << "'" << iter->first << "','" << iter->second << "',"; 61 } 62 ss << "]}"; 63 return ss.str(); 64 } 65 66 int64 timestamp; 67 std::map<string, string> values; 68 }; 69 70 struct StatsEntry { 71 string type; 72 string id; 73 StatsUnit stats; 74 }; 75 76 typedef std::map<string, std::vector<string> > StatsMap; 77 78 class PeerConnectionEntry { 79 public: 80 PeerConnectionEntry(int pid, int lid) : pid_(pid), lid_(lid) {} 81 82 void AddEvent(const string& type, const string& value) { 83 EventEntry entry = {type, value}; 84 events_.push_back(entry); 85 } 86 87 string getIdString() const { 88 std::stringstream ss; 89 ss << pid_ << "-" << lid_; 90 return ss.str(); 91 } 92 93 string getLogIdString() const { 94 std::stringstream ss; 95 ss << pid_ << "-" << lid_ << "-update-log"; 96 return ss.str(); 97 } 98 99 string getAllUpdateString() const { 100 std::stringstream ss; 101 ss << "{pid:" << pid_ << ", lid:" << lid_ << ", log:["; 102 for (size_t i = 0; i < events_.size(); ++i) { 103 ss << "{type:'" << events_[i].type << 104 "', value:'" << events_[i].value << "'},"; 105 } 106 ss << "]}"; 107 return ss.str(); 108 } 109 110 int pid_; 111 int lid_; 112 std::vector<EventEntry> events_; 113 // This is a record of the history of stats value reported for each stats 114 // report id (e.g. ssrc-1234) for each stats name (e.g. framerate). 115 // It a 2-D map with each map entry is a vector of reported values. 116 // It is used to verify the graph data series. 117 std::map<string, StatsMap> stats_; 118 }; 119 120 class UserMediaRequestEntry { 121 public: 122 UserMediaRequestEntry(int pid, 123 int rid, 124 const std::string& origin, 125 const std::string& audio_constraints, 126 const std::string& video_constraints) 127 : pid(pid), 128 rid(rid), 129 origin(origin), 130 audio_constraints(audio_constraints), 131 video_constraints(video_constraints) {} 132 133 int pid; 134 int rid; 135 std::string origin; 136 std::string audio_constraints; 137 std::string video_constraints; 138 }; 139 140 static const int64 FAKE_TIME_STAMP = 3600000; 141 142 #if defined(OS_WIN) 143 // All tests are flaky on Windows: crbug.com/277322. 144 #define MAYBE_WebRtcInternalsBrowserTest DISABLED_WebRtcInternalsBrowserTest 145 #else 146 #define MAYBE_WebRtcInternalsBrowserTest WebRtcInternalsBrowserTest 147 #endif 148 149 class MAYBE_WebRtcInternalsBrowserTest: public ContentBrowserTest { 150 public: 151 MAYBE_WebRtcInternalsBrowserTest() {} 152 virtual ~MAYBE_WebRtcInternalsBrowserTest() {} 153 154 virtual void SetUpOnMainThread() OVERRIDE { 155 // We need fake devices in this test since we want to run on naked VMs. We 156 // assume these switches are set by default in content_browsertests. 157 ASSERT_TRUE(CommandLine::ForCurrentProcess()->HasSwitch( 158 switches::kUseFakeDeviceForMediaStream)); 159 ASSERT_TRUE(CommandLine::ForCurrentProcess()->HasSwitch( 160 switches::kUseFakeUIForMediaStream)); 161 } 162 163 protected: 164 bool ExecuteJavascript(const string& javascript) { 165 return ExecuteScript(shell()->web_contents(), javascript); 166 } 167 168 void ExpectTitle(const std::string& expected_title) const { 169 base::string16 expected_title16(base::ASCIIToUTF16(expected_title)); 170 TitleWatcher title_watcher(shell()->web_contents(), expected_title16); 171 EXPECT_EQ(expected_title16, title_watcher.WaitAndGetTitle()); 172 } 173 174 // Execute the javascript of addPeerConnection. 175 void ExecuteAddPeerConnectionJs(const PeerConnectionEntry& pc) { 176 std::stringstream ss; 177 ss << "{pid:" << pc.pid_ <<", lid:" << pc.lid_ << ", " << 178 "url:'u', servers:'s', constraints:'c'}"; 179 ASSERT_TRUE(ExecuteJavascript("addPeerConnection(" + ss.str() + ");")); 180 } 181 182 // Execute the javascript of removePeerConnection. 183 void ExecuteRemovePeerConnectionJs(const PeerConnectionEntry& pc) { 184 std::stringstream ss; 185 ss << "{pid:" << pc.pid_ <<", lid:" << pc.lid_ << "}"; 186 187 ASSERT_TRUE(ExecuteJavascript("removePeerConnection(" + ss.str() + ");")); 188 } 189 190 // Execute the javascript of addGetUserMedia. 191 void ExecuteAddGetUserMediaJs(const UserMediaRequestEntry& request) { 192 std::stringstream ss; 193 ss << "{pid:" << request.pid << ", rid:" << request.rid << ", origin:'" 194 << request.origin << "', audio:'" << request.audio_constraints 195 << "', video:'" << request.video_constraints << "'}"; 196 197 ASSERT_TRUE(ExecuteJavascript("addGetUserMedia(" + ss.str() + ");")); 198 } 199 200 // Execute the javascript of removeGetUserMediaForRenderer. 201 void ExecuteRemoveGetUserMediaForRendererJs(int rid) { 202 std::stringstream ss; 203 ss << "{rid:" << rid << "}"; 204 ASSERT_TRUE( 205 ExecuteJavascript("removeGetUserMediaForRenderer(" + ss.str() + ");")); 206 } 207 208 // Verifies that the DOM element with id |id| exists. 209 void VerifyElementWithId(const string& id) { 210 bool result = false; 211 ASSERT_TRUE(ExecuteScriptAndExtractBool( 212 shell()->web_contents(), 213 "window.domAutomationController.send($('" + id + "') != null);", 214 &result)); 215 EXPECT_TRUE(result); 216 } 217 218 // Verifies that the DOM element with id |id| does not exist. 219 void VerifyNoElementWithId(const string& id) { 220 bool result = false; 221 ASSERT_TRUE(ExecuteScriptAndExtractBool( 222 shell()->web_contents(), 223 "window.domAutomationController.send($('" + id + "') == null);", 224 &result)); 225 EXPECT_TRUE(result); 226 } 227 228 // Verifies the JS Array of userMediaRequests matches |requests|. 229 void VerifyUserMediaRequest( 230 const std::vector<UserMediaRequestEntry>& requests) { 231 string json_requests; 232 ASSERT_TRUE(ExecuteScriptAndExtractString( 233 shell()->web_contents(), 234 "window.domAutomationController.send(" 235 "JSON.stringify(userMediaRequests));", 236 &json_requests)); 237 scoped_ptr<base::Value> value_requests; 238 value_requests.reset(base::JSONReader::Read(json_requests)); 239 240 EXPECT_EQ(base::Value::TYPE_LIST, value_requests->GetType()); 241 242 base::ListValue* list_request = 243 static_cast<base::ListValue*>(value_requests.get()); 244 EXPECT_EQ(requests.size(), list_request->GetSize()); 245 246 for (size_t i = 0; i < requests.size(); ++i) { 247 base::DictionaryValue* dict = NULL; 248 ASSERT_TRUE(list_request->GetDictionary(i, &dict)); 249 int pid, rid; 250 std::string origin, audio, video; 251 ASSERT_TRUE(dict->GetInteger("pid", &pid)); 252 ASSERT_TRUE(dict->GetInteger("rid", &rid)); 253 ASSERT_TRUE(dict->GetString("origin", &origin)); 254 ASSERT_TRUE(dict->GetString("audio", &audio)); 255 ASSERT_TRUE(dict->GetString("video", &video)); 256 EXPECT_EQ(requests[i].pid, pid); 257 EXPECT_EQ(requests[i].rid, rid); 258 EXPECT_EQ(requests[i].origin, origin); 259 EXPECT_EQ(requests[i].audio_constraints, audio); 260 EXPECT_EQ(requests[i].video_constraints, video); 261 } 262 } 263 264 // Verifies that DOM for |pc| is correctly created with the right content. 265 void VerifyPeerConnectionEntry(const PeerConnectionEntry& pc) { 266 VerifyElementWithId(pc.getIdString()); 267 if (pc.events_.size() == 0) 268 return; 269 270 string log_id = pc.getLogIdString(); 271 VerifyElementWithId(log_id); 272 string result; 273 for (size_t i = 0; i < pc.events_.size(); ++i) { 274 std::stringstream ss; 275 ss << "var row = $('" << log_id << "').rows[" << (i + 1) << "];" 276 "var cell = row.lastChild;" 277 "window.domAutomationController.send(cell.firstChild.textContent);"; 278 ASSERT_TRUE(ExecuteScriptAndExtractString( 279 shell()->web_contents(), ss.str(), &result)); 280 EXPECT_EQ(pc.events_[i].type + pc.events_[i].value, result); 281 } 282 } 283 284 // Executes the javascript of updatePeerConnection and verifies the result. 285 void ExecuteAndVerifyUpdatePeerConnection( 286 PeerConnectionEntry& pc, const string& type, const string& value) { 287 pc.AddEvent(type, value); 288 289 std::stringstream ss; 290 ss << "{pid:" << pc.pid_ <<", lid:" << pc.lid_ << 291 ", type:'" << type << "', value:'" << value << "'}"; 292 ASSERT_TRUE(ExecuteJavascript("updatePeerConnection(" + ss.str() + ")")); 293 294 VerifyPeerConnectionEntry(pc); 295 } 296 297 // Execute addStats and verifies that the stats table has the right content. 298 void ExecuteAndVerifyAddStats( 299 PeerConnectionEntry& pc, const string& type, const string& id, 300 StatsUnit& stats) { 301 StatsEntry entry = {type, id, stats}; 302 303 // Adds each new value to the map of stats history. 304 std::map<string, string>::iterator iter; 305 for (iter = stats.values.begin(); iter != stats.values.end(); iter++) { 306 pc.stats_[id][iter->first].push_back(iter->second); 307 } 308 std::stringstream ss; 309 ss << "{pid:" << pc.pid_ << ", lid:" << pc.lid_ << "," 310 "reports:[" << "{id:'" << id << "', type:'" << type << "', " 311 "stats:" << stats.GetString() << "}]}"; 312 313 ASSERT_TRUE(ExecuteJavascript("addStats(" + ss.str() + ")")); 314 VerifyStatsTable(pc, entry); 315 } 316 317 318 // Verifies that the stats table has the right content. 319 void VerifyStatsTable(const PeerConnectionEntry& pc, 320 const StatsEntry& report) { 321 string table_id = 322 pc.getIdString() + "-table-" + report.id; 323 VerifyElementWithId(table_id); 324 325 std::map<string, string>::const_iterator iter; 326 for (iter = report.stats.values.begin(); 327 iter != report.stats.values.end(); iter++) { 328 VerifyStatsTableRow(table_id, iter->first, iter->second); 329 } 330 } 331 332 // Verifies that the row named as |name| of the stats table |table_id| has 333 // the correct content as |name| : |value|. 334 void VerifyStatsTableRow(const string& table_id, 335 const string& name, 336 const string& value) { 337 VerifyElementWithId(table_id + "-" + name); 338 339 string result; 340 ASSERT_TRUE(ExecuteScriptAndExtractString( 341 shell()->web_contents(), 342 "var row = $('" + table_id + "-" + name + "');" 343 "var name = row.cells[0].textContent;" 344 "var value = row.cells[1].textContent;" 345 "window.domAutomationController.send(name + ':' + value)", 346 &result)); 347 EXPECT_EQ(name + ":" + value, result); 348 } 349 350 // Verifies that the graph data series consistent with pc.stats_. 351 void VerifyStatsGraph(const PeerConnectionEntry& pc) { 352 std::map<string, StatsMap>::const_iterator stream_iter; 353 for (stream_iter = pc.stats_.begin(); 354 stream_iter != pc.stats_.end(); stream_iter++) { 355 StatsMap::const_iterator stats_iter; 356 for (stats_iter = stream_iter->second.begin(); 357 stats_iter != stream_iter->second.end(); 358 stats_iter++) { 359 string graph_id = stream_iter->first + "-" + stats_iter->first; 360 for (size_t i = 0; i < stats_iter->second.size(); ++i) { 361 float number; 362 std::stringstream stream(stats_iter->second[i]); 363 stream >> number; 364 if (stream.fail()) 365 continue; 366 VerifyGraphDataPoint( 367 pc.getIdString(), graph_id, i, stats_iter->second[i]); 368 } 369 } 370 } 371 } 372 373 // Verifies that the graph data point at index |index| has value |value|. 374 void VerifyGraphDataPoint(const string& pc_id, const string& graph_id, 375 int index, const string& value) { 376 bool result = false; 377 ASSERT_TRUE(ExecuteScriptAndExtractBool( 378 shell()->web_contents(), 379 "window.domAutomationController.send(" 380 "graphViews['" + pc_id + "-" + graph_id + "'] != null)", 381 &result)); 382 EXPECT_TRUE(result); 383 384 std::stringstream ss; 385 ss << "var dp = peerConnectionDataStore['" << pc_id << "']" 386 ".getDataSeries('" << graph_id << "').dataPoints_[" << index << "];" 387 "window.domAutomationController.send(dp.value.toString())"; 388 string actual_value; 389 ASSERT_TRUE(ExecuteScriptAndExtractString( 390 shell()->web_contents(), ss.str(), &actual_value)); 391 EXPECT_EQ(value, actual_value); 392 } 393 394 // Get the JSON string of the ssrc info from the page. 395 string GetSsrcInfo(const string& ssrc_id) { 396 string result; 397 EXPECT_TRUE(ExecuteScriptAndExtractString( 398 shell()->web_contents(), 399 "window.domAutomationController.send(JSON.stringify(" 400 "ssrcInfoManager.streamInfoContainer_['" + ssrc_id + "']))", 401 &result)); 402 return result; 403 } 404 405 int GetSsrcInfoBlockCount(Shell* shell) { 406 int count = 0; 407 EXPECT_TRUE(ExecuteScriptAndExtractInt( 408 shell->web_contents(), 409 "window.domAutomationController.send(" 410 "document.getElementsByClassName(" 411 "ssrcInfoManager.SSRC_INFO_BLOCK_CLASS).length);", 412 &count)); 413 return count; 414 } 415 416 // Verifies |dump| contains |peer_connection_number| peer connection dumps, 417 // each containing |update_number| updates and |stats_number| stats tables. 418 void VerifyPageDumpStructure(base::Value* dump, 419 int peer_connection_number, 420 int update_number, 421 int stats_number) { 422 EXPECT_NE((base::Value*)NULL, dump); 423 EXPECT_EQ(base::Value::TYPE_DICTIONARY, dump->GetType()); 424 425 base::DictionaryValue* dict_dump = 426 static_cast<base::DictionaryValue*>(dump); 427 EXPECT_EQ((size_t) peer_connection_number, dict_dump->size()); 428 429 base::DictionaryValue::Iterator it(*dict_dump); 430 for (; !it.IsAtEnd(); it.Advance()) { 431 base::Value* value = NULL; 432 dict_dump->Get(it.key(), &value); 433 EXPECT_EQ(base::Value::TYPE_DICTIONARY, value->GetType()); 434 base::DictionaryValue* pc_dump = 435 static_cast<base::DictionaryValue*>(value); 436 EXPECT_TRUE(pc_dump->HasKey("updateLog")); 437 EXPECT_TRUE(pc_dump->HasKey("stats")); 438 439 // Verifies the number of updates. 440 pc_dump->Get("updateLog", &value); 441 EXPECT_EQ(base::Value::TYPE_LIST, value->GetType()); 442 base::ListValue* list = static_cast<base::ListValue*>(value); 443 EXPECT_EQ((size_t) update_number, list->GetSize()); 444 445 // Verifies the number of stats tables. 446 pc_dump->Get("stats", &value); 447 EXPECT_EQ(base::Value::TYPE_DICTIONARY, value->GetType()); 448 base::DictionaryValue* dict = static_cast<base::DictionaryValue*>(value); 449 EXPECT_EQ((size_t) stats_number, dict->size()); 450 } 451 } 452 453 // Verifies |dump| contains the correct statsTable and statsDataSeries for 454 // |pc|. 455 void VerifyStatsDump(base::Value* dump, 456 const PeerConnectionEntry& pc, 457 const string& report_type, 458 const string& report_id, 459 const StatsUnit& stats) { 460 EXPECT_NE((base::Value*)NULL, dump); 461 EXPECT_EQ(base::Value::TYPE_DICTIONARY, dump->GetType()); 462 463 base::DictionaryValue* dict_dump = 464 static_cast<base::DictionaryValue*>(dump); 465 base::Value* value = NULL; 466 dict_dump->Get(pc.getIdString(), &value); 467 base::DictionaryValue* pc_dump = static_cast<base::DictionaryValue*>(value); 468 469 // Verifies there is one data series per stats name. 470 value = NULL; 471 pc_dump->Get("stats", &value); 472 EXPECT_EQ(base::Value::TYPE_DICTIONARY, value->GetType()); 473 474 base::DictionaryValue* dataSeries = 475 static_cast<base::DictionaryValue*>(value); 476 EXPECT_EQ(stats.values.size(), dataSeries->size()); 477 } 478 }; 479 480 IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcInternalsBrowserTest, 481 AddAndRemovePeerConnection) { 482 GURL url("chrome://webrtc-internals"); 483 NavigateToURL(shell(), url); 484 485 // Add two PeerConnections and then remove them. 486 PeerConnectionEntry pc_1(1, 0); 487 ExecuteAddPeerConnectionJs(pc_1); 488 VerifyPeerConnectionEntry(pc_1); 489 490 PeerConnectionEntry pc_2(2, 1); 491 ExecuteAddPeerConnectionJs(pc_2); 492 VerifyPeerConnectionEntry(pc_2); 493 494 ExecuteRemovePeerConnectionJs(pc_1); 495 VerifyNoElementWithId(pc_1.getIdString()); 496 VerifyPeerConnectionEntry(pc_2); 497 498 ExecuteRemovePeerConnectionJs(pc_2); 499 VerifyNoElementWithId(pc_2.getIdString()); 500 } 501 502 IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcInternalsBrowserTest, 503 UpdateAllPeerConnections) { 504 GURL url("chrome://webrtc-internals"); 505 NavigateToURL(shell(), url); 506 507 PeerConnectionEntry pc_0(1, 0); 508 pc_0.AddEvent("e1", "v1"); 509 pc_0.AddEvent("e2", "v2"); 510 PeerConnectionEntry pc_1(1, 1); 511 pc_1.AddEvent("e3", "v3"); 512 pc_1.AddEvent("e4", "v4"); 513 string pc_array = "[" + pc_0.getAllUpdateString() + ", " + 514 pc_1.getAllUpdateString() + "]"; 515 EXPECT_TRUE(ExecuteJavascript("updateAllPeerConnections(" + pc_array + ");")); 516 VerifyPeerConnectionEntry(pc_0); 517 VerifyPeerConnectionEntry(pc_1); 518 } 519 520 IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcInternalsBrowserTest, UpdatePeerConnection) { 521 GURL url("chrome://webrtc-internals"); 522 NavigateToURL(shell(), url); 523 524 // Add one PeerConnection and send one update. 525 PeerConnectionEntry pc_1(1, 0); 526 ExecuteAddPeerConnectionJs(pc_1); 527 528 ExecuteAndVerifyUpdatePeerConnection(pc_1, "e1", "v1"); 529 530 // Add another PeerConnection and send two updates. 531 PeerConnectionEntry pc_2(1, 1); 532 ExecuteAddPeerConnectionJs(pc_2); 533 534 SsrcEntry ssrc1, ssrc2; 535 ssrc1.id = "ssrcid1"; 536 ssrc1.properties["msid"] = "mymsid"; 537 ssrc2.id = "ssrcid2"; 538 ssrc2.properties["label"] = "mylabel"; 539 ssrc2.properties["cname"] = "mycname"; 540 541 ExecuteAndVerifyUpdatePeerConnection(pc_2, "setRemoteDescription", 542 ssrc1.GetSsrcAttributeString()); 543 544 ExecuteAndVerifyUpdatePeerConnection(pc_2, "setLocalDescription", 545 ssrc2.GetSsrcAttributeString()); 546 547 EXPECT_EQ(ssrc1.GetAsJSON(), GetSsrcInfo(ssrc1.id)); 548 EXPECT_EQ(ssrc2.GetAsJSON(), GetSsrcInfo(ssrc2.id)); 549 550 StatsUnit stats = {FAKE_TIME_STAMP}; 551 stats.values["ssrc"] = ssrc1.id; 552 ExecuteAndVerifyAddStats(pc_2, "ssrc", "dummyId", stats); 553 EXPECT_GT(GetSsrcInfoBlockCount(shell()), 0); 554 } 555 556 // Tests that adding random named stats updates the dataSeries and graphs. 557 IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcInternalsBrowserTest, AddStats) { 558 GURL url("chrome://webrtc-internals"); 559 NavigateToURL(shell(), url); 560 561 PeerConnectionEntry pc(1, 0); 562 ExecuteAddPeerConnectionJs(pc); 563 564 const string type = "ssrc"; 565 const string id = "ssrc-1234"; 566 StatsUnit stats = {FAKE_TIME_STAMP}; 567 stats.values["trackId"] = "abcd"; 568 stats.values["bitrate"] = "2000"; 569 stats.values["framerate"] = "30"; 570 571 // Add new stats and verify the stats table and graphs. 572 ExecuteAndVerifyAddStats(pc, type, id, stats); 573 VerifyStatsGraph(pc); 574 575 // Update existing stats and verify the stats table and graphs. 576 stats.values["bitrate"] = "2001"; 577 stats.values["framerate"] = "31"; 578 ExecuteAndVerifyAddStats(pc, type, id, stats); 579 VerifyStatsGraph(pc); 580 } 581 582 // Tests that the bandwidth estimation values are drawn on a single graph. 583 IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcInternalsBrowserTest, BweCompoundGraph) { 584 GURL url("chrome://webrtc-internals"); 585 NavigateToURL(shell(), url); 586 587 PeerConnectionEntry pc(1, 0); 588 ExecuteAddPeerConnectionJs(pc); 589 590 StatsUnit stats = {FAKE_TIME_STAMP}; 591 stats.values["googAvailableSendBandwidth"] = "1000000"; 592 stats.values["googTargetEncBitrate"] = "1000"; 593 stats.values["googActualEncBitrate"] = "1000000"; 594 stats.values["googRetransmitBitrate"] = "10"; 595 stats.values["googTransmitBitrate"] = "1000000"; 596 const string stats_type = "bwe"; 597 const string stats_id = "videobwe"; 598 ExecuteAndVerifyAddStats(pc, stats_type, stats_id, stats); 599 600 string graph_id = 601 pc.getIdString() + "-" + stats_id + "-bweCompound"; 602 bool result = false; 603 // Verify that the bweCompound graph exists. 604 ASSERT_TRUE(ExecuteScriptAndExtractBool( 605 shell()->web_contents(), 606 "window.domAutomationController.send(" 607 " graphViews['" + graph_id + "'] != null)", 608 &result)); 609 EXPECT_TRUE(result); 610 611 // Verify that the bweCompound graph contains multiple dataSeries. 612 int count = 0; 613 ASSERT_TRUE(ExecuteScriptAndExtractInt( 614 shell()->web_contents(), 615 "window.domAutomationController.send(" 616 " graphViews['" + graph_id + "'].getDataSeriesCount())", 617 &count)); 618 EXPECT_EQ((int)stats.values.size(), count); 619 } 620 621 // Tests that the total packet/byte count is converted to count per second, 622 // and the converted data is drawn. 623 IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcInternalsBrowserTest, ConvertedGraphs) { 624 GURL url("chrome://webrtc-internals"); 625 NavigateToURL(shell(), url); 626 627 PeerConnectionEntry pc(1, 0); 628 ExecuteAddPeerConnectionJs(pc); 629 630 const string stats_type = "s"; 631 const string stats_id = "1"; 632 const int num_converted_stats = 4; 633 const string stats_names[] = 634 {"packetsSent", "bytesSent", "packetsReceived", "bytesReceived"}; 635 const string converted_names[] = 636 {"packetsSentPerSecond", "bitsSentPerSecond", 637 "packetsReceivedPerSecond", "bitsReceivedPerSecond"}; 638 const string first_value = "1000"; 639 const string second_value = "2000"; 640 const string converted_values[] = {"1000", "8000", "1000", "8000"}; 641 642 // Send the first data point. 643 StatsUnit stats = {FAKE_TIME_STAMP}; 644 for (int i = 0; i < num_converted_stats; ++i) 645 stats.values[stats_names[i]] = first_value; 646 647 ExecuteAndVerifyAddStats(pc, stats_type, stats_id, stats); 648 649 // Send the second data point at 1000ms after the first data point. 650 stats.timestamp += 1000; 651 for (int i = 0; i < num_converted_stats; ++i) 652 stats.values[stats_names[i]] = second_value; 653 ExecuteAndVerifyAddStats(pc, stats_type, stats_id, stats); 654 655 // Verifies the graph data matches converted_values. 656 for (int i = 0; i < num_converted_stats; ++i) { 657 VerifyGraphDataPoint(pc.getIdString(), stats_id + "-" + converted_names[i], 658 1, converted_values[i]); 659 } 660 } 661 662 // Timing out on ARM linux bot: http://crbug.com/238490 663 // Disabling due to failure on Linux, Mac, Win: http://crbug.com/272413 664 // Sanity check of the page content under a real PeerConnection call. 665 IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcInternalsBrowserTest, 666 DISABLED_WithRealPeerConnectionCall) { 667 // Start a peerconnection call in the first window. 668 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady()); 669 GURL url(embedded_test_server()->GetURL("/media/peerconnection-call.html")); 670 NavigateToURL(shell(), url); 671 ASSERT_TRUE(ExecuteJavascript("call({video:true});")); 672 ExpectTitle("OK"); 673 674 // Open webrtc-internals in the second window. 675 GURL url2("chrome://webrtc-internals"); 676 Shell* shell2 = CreateBrowser(); 677 NavigateToURL(shell2, url2); 678 679 const int NUMBER_OF_PEER_CONNECTIONS = 2; 680 681 // Verifies the number of peerconnections. 682 int count = 0; 683 ASSERT_TRUE(ExecuteScriptAndExtractInt( 684 shell2->web_contents(), 685 "window.domAutomationController.send(" 686 "$('peer-connections-list').getElementsByTagName('li').length);", 687 &count)); 688 EXPECT_EQ(NUMBER_OF_PEER_CONNECTIONS, count); 689 690 // Verifies the the event tables. 691 ASSERT_TRUE(ExecuteScriptAndExtractInt( 692 shell2->web_contents(), 693 "window.domAutomationController.send($('peer-connections-list')" 694 ".getElementsByClassName('update-log-table').length);", 695 &count)); 696 EXPECT_EQ(NUMBER_OF_PEER_CONNECTIONS, count); 697 698 ASSERT_TRUE(ExecuteScriptAndExtractInt( 699 shell2->web_contents(), 700 "window.domAutomationController.send($('peer-connections-list')" 701 ".getElementsByClassName('update-log-table')[0].rows.length);", 702 &count)); 703 EXPECT_GT(count, 1); 704 705 ASSERT_TRUE(ExecuteScriptAndExtractInt( 706 shell2->web_contents(), 707 "window.domAutomationController.send($('peer-connections-list')" 708 ".getElementsByClassName('update-log-table')[1].rows.length);", 709 &count)); 710 EXPECT_GT(count, 1); 711 712 // Wait until the stats table containers are created. 713 count = 0; 714 while (count != NUMBER_OF_PEER_CONNECTIONS) { 715 ASSERT_TRUE(ExecuteScriptAndExtractInt( 716 shell2->web_contents(), 717 "window.domAutomationController.send(" 718 "$('peer-connections-list').getElementsByClassName(" 719 "'stats-table-container').length);", 720 &count)); 721 } 722 723 // Verifies each stats table having more than one rows. 724 bool result = false; 725 ASSERT_TRUE(ExecuteScriptAndExtractBool( 726 shell2->web_contents(), 727 "var tableContainers = $('peer-connections-list')" 728 ".getElementsByClassName('stats-table-container');" 729 "var result = true;" 730 "for (var i = 0; i < tableContainers.length && result; ++i) {" 731 "var tables = tableContainers[i].getElementsByTagName('table');" 732 "for (var j = 0; j < tables.length && result; ++j) {" 733 "result = (tables[j].rows.length > 1);" 734 "}" 735 "if (!result) {" 736 "console.log(tableContainers[i].innerHTML);" 737 "}" 738 "}" 739 "window.domAutomationController.send(result);", 740 &result)); 741 742 EXPECT_TRUE(result); 743 744 count = GetSsrcInfoBlockCount(shell2); 745 EXPECT_GT(count, 0); 746 } 747 748 IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcInternalsBrowserTest, CreatePageDump) { 749 GURL url("chrome://webrtc-internals"); 750 NavigateToURL(shell(), url); 751 752 PeerConnectionEntry pc_0(1, 0); 753 pc_0.AddEvent("e1", "v1"); 754 pc_0.AddEvent("e2", "v2"); 755 PeerConnectionEntry pc_1(1, 1); 756 pc_1.AddEvent("e3", "v3"); 757 pc_1.AddEvent("e4", "v4"); 758 string pc_array = 759 "[" + pc_0.getAllUpdateString() + ", " + pc_1.getAllUpdateString() + "]"; 760 EXPECT_TRUE(ExecuteJavascript("updateAllPeerConnections(" + pc_array + ");")); 761 762 // Verifies the peer connection data store can be created without stats. 763 string dump_json; 764 ASSERT_TRUE(ExecuteScriptAndExtractString( 765 shell()->web_contents(), 766 "window.domAutomationController.send(" 767 "JSON.stringify(peerConnectionDataStore));", 768 &dump_json)); 769 scoped_ptr<base::Value> dump; 770 dump.reset(base::JSONReader::Read(dump_json)); 771 VerifyPageDumpStructure(dump.get(), 772 2 /*peer_connection_number*/, 773 2 /*update_number*/, 774 0 /*stats_number*/); 775 776 // Adds a stats report. 777 const string type = "dummy"; 778 const string id = "1234"; 779 StatsUnit stats = { FAKE_TIME_STAMP }; 780 stats.values["bitrate"] = "2000"; 781 stats.values["framerate"] = "30"; 782 ExecuteAndVerifyAddStats(pc_0, type, id, stats); 783 784 ASSERT_TRUE(ExecuteScriptAndExtractString( 785 shell()->web_contents(), 786 "window.domAutomationController.send(" 787 "JSON.stringify(peerConnectionDataStore));", 788 &dump_json)); 789 dump.reset(base::JSONReader::Read(dump_json)); 790 VerifyStatsDump(dump.get(), pc_0, type, id, stats); 791 } 792 793 IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcInternalsBrowserTest, UpdateGetUserMedia) { 794 GURL url("chrome://webrtc-internals"); 795 NavigateToURL(shell(), url); 796 797 UserMediaRequestEntry request1(1, 1, "origin", "ac", "vc"); 798 UserMediaRequestEntry request2(2, 2, "origin2", "ac2", "vc2"); 799 ExecuteAddGetUserMediaJs(request1); 800 ExecuteAddGetUserMediaJs(request2); 801 802 std::vector<UserMediaRequestEntry> list; 803 list.push_back(request1); 804 list.push_back(request2); 805 VerifyUserMediaRequest(list); 806 807 ExecuteRemoveGetUserMediaForRendererJs(1); 808 list.erase(list.begin()); 809 VerifyUserMediaRequest(list); 810 811 ExecuteRemoveGetUserMediaForRendererJs(2); 812 list.erase(list.begin()); 813 VerifyUserMediaRequest(list); 814 } 815 816 // Tests that the received propagation delta values are converted and drawn 817 // correctly. 818 IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcInternalsBrowserTest, 819 ReceivedPropagationDelta) { 820 GURL url("chrome://webrtc-internals"); 821 NavigateToURL(shell(), url); 822 823 PeerConnectionEntry pc(1, 0); 824 ExecuteAddPeerConnectionJs(pc); 825 826 StatsUnit stats = {FAKE_TIME_STAMP}; 827 stats.values["googReceivedPacketGroupArrivalTimeDebug"] = 828 "[1000, 1100, 1200]"; 829 stats.values["googReceivedPacketGroupPropagationDeltaDebug"] = 830 "[10, 20, 30]"; 831 const string stats_type = "bwe"; 832 const string stats_id = "videobwe"; 833 ExecuteAndVerifyAddStats(pc, stats_type, stats_id, stats); 834 835 string graph_id = pc.getIdString() + "-" + stats_id + 836 "-googReceivedPacketGroupPropagationDeltaDebug"; 837 string data_series_id = 838 stats_id + "-googReceivedPacketGroupPropagationDeltaDebug"; 839 bool result = false; 840 // Verify that the graph exists. 841 ASSERT_TRUE(ExecuteScriptAndExtractBool( 842 shell()->web_contents(), 843 "window.domAutomationController.send(" 844 " graphViews['" + graph_id + "'] != null)", 845 &result)); 846 EXPECT_TRUE(result); 847 848 // Verify that the graph contains multiple data points. 849 int count = 0; 850 ASSERT_TRUE(ExecuteScriptAndExtractInt( 851 shell()->web_contents(), 852 "window.domAutomationController.send(" 853 " graphViews['" + graph_id + "'].getDataSeriesCount())", 854 &count)); 855 EXPECT_EQ(1, count); 856 ASSERT_TRUE(ExecuteScriptAndExtractInt( 857 shell()->web_contents(), 858 "window.domAutomationController.send(" 859 " peerConnectionDataStore['" + pc.getIdString() + "']" + 860 " .getDataSeries('" + data_series_id + "').getCount())", 861 &count)); 862 EXPECT_EQ(3, count); 863 } 864 865 } // namespace content 866