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', rtcConfiguration:'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 bool user_media_tab_existed = false; 264 ASSERT_TRUE(ExecuteScriptAndExtractBool( 265 shell()->web_contents(), 266 "window.domAutomationController.send(" 267 "$('user-media-tab-id') != null);", 268 &user_media_tab_existed)); 269 EXPECT_EQ(!requests.empty(), user_media_tab_existed); 270 271 if (user_media_tab_existed) { 272 int user_media_request_count = -1; 273 ASSERT_TRUE(ExecuteScriptAndExtractInt( 274 shell()->web_contents(), 275 "window.domAutomationController.send(" 276 "$('user-media-tab-id').childNodes.length);", 277 &user_media_request_count)); 278 ASSERT_EQ(requests.size(), static_cast<size_t>(user_media_request_count)); 279 } 280 } 281 282 // Verifies that DOM for |pc| is correctly created with the right content. 283 void VerifyPeerConnectionEntry(const PeerConnectionEntry& pc) { 284 VerifyElementWithId(pc.getIdString()); 285 if (pc.events_.size() == 0) 286 return; 287 288 string log_id = pc.getLogIdString(); 289 VerifyElementWithId(log_id); 290 string result; 291 for (size_t i = 0; i < pc.events_.size(); ++i) { 292 std::stringstream ss; 293 ss << "var row = $('" << log_id << "').rows[" << (i + 1) << "];" 294 "var cell = row.lastChild;" 295 "window.domAutomationController.send(cell.firstChild.textContent);"; 296 ASSERT_TRUE(ExecuteScriptAndExtractString( 297 shell()->web_contents(), ss.str(), &result)); 298 EXPECT_EQ(pc.events_[i].type + pc.events_[i].value, result); 299 } 300 } 301 302 // Executes the javascript of updatePeerConnection and verifies the result. 303 void ExecuteAndVerifyUpdatePeerConnection( 304 PeerConnectionEntry& pc, const string& type, const string& value) { 305 pc.AddEvent(type, value); 306 307 std::stringstream ss; 308 ss << "{pid:" << pc.pid_ <<", lid:" << pc.lid_ << 309 ", type:'" << type << "', value:'" << value << "'}"; 310 ASSERT_TRUE(ExecuteJavascript("updatePeerConnection(" + ss.str() + ")")); 311 312 VerifyPeerConnectionEntry(pc); 313 } 314 315 // Execute addStats and verifies that the stats table has the right content. 316 void ExecuteAndVerifyAddStats( 317 PeerConnectionEntry& pc, const string& type, const string& id, 318 StatsUnit& stats) { 319 StatsEntry entry = {type, id, stats}; 320 321 // Adds each new value to the map of stats history. 322 std::map<string, string>::iterator iter; 323 for (iter = stats.values.begin(); iter != stats.values.end(); iter++) { 324 pc.stats_[id][iter->first].push_back(iter->second); 325 } 326 std::stringstream ss; 327 ss << "{pid:" << pc.pid_ << ", lid:" << pc.lid_ << "," 328 "reports:[" << "{id:'" << id << "', type:'" << type << "', " 329 "stats:" << stats.GetString() << "}]}"; 330 331 ASSERT_TRUE(ExecuteJavascript("addStats(" + ss.str() + ")")); 332 VerifyStatsTable(pc, entry); 333 } 334 335 336 // Verifies that the stats table has the right content. 337 void VerifyStatsTable(const PeerConnectionEntry& pc, 338 const StatsEntry& report) { 339 string table_id = 340 pc.getIdString() + "-table-" + report.id; 341 VerifyElementWithId(table_id); 342 343 std::map<string, string>::const_iterator iter; 344 for (iter = report.stats.values.begin(); 345 iter != report.stats.values.end(); iter++) { 346 VerifyStatsTableRow(table_id, iter->first, iter->second); 347 } 348 } 349 350 // Verifies that the row named as |name| of the stats table |table_id| has 351 // the correct content as |name| : |value|. 352 void VerifyStatsTableRow(const string& table_id, 353 const string& name, 354 const string& value) { 355 VerifyElementWithId(table_id + "-" + name); 356 357 string result; 358 ASSERT_TRUE(ExecuteScriptAndExtractString( 359 shell()->web_contents(), 360 "var row = $('" + table_id + "-" + name + "');" 361 "var name = row.cells[0].textContent;" 362 "var value = row.cells[1].textContent;" 363 "window.domAutomationController.send(name + ':' + value)", 364 &result)); 365 EXPECT_EQ(name + ":" + value, result); 366 } 367 368 // Verifies that the graph data series consistent with pc.stats_. 369 void VerifyStatsGraph(const PeerConnectionEntry& pc) { 370 std::map<string, StatsMap>::const_iterator stream_iter; 371 for (stream_iter = pc.stats_.begin(); 372 stream_iter != pc.stats_.end(); stream_iter++) { 373 StatsMap::const_iterator stats_iter; 374 for (stats_iter = stream_iter->second.begin(); 375 stats_iter != stream_iter->second.end(); 376 stats_iter++) { 377 string graph_id = stream_iter->first + "-" + stats_iter->first; 378 for (size_t i = 0; i < stats_iter->second.size(); ++i) { 379 float number; 380 std::stringstream stream(stats_iter->second[i]); 381 stream >> number; 382 if (stream.fail()) 383 continue; 384 VerifyGraphDataPoint( 385 pc.getIdString(), graph_id, i, stats_iter->second[i]); 386 } 387 } 388 } 389 } 390 391 // Verifies that the graph data point at index |index| has value |value|. 392 void VerifyGraphDataPoint(const string& pc_id, const string& graph_id, 393 int index, const string& value) { 394 bool result = false; 395 ASSERT_TRUE(ExecuteScriptAndExtractBool( 396 shell()->web_contents(), 397 "window.domAutomationController.send(" 398 "graphViews['" + pc_id + "-" + graph_id + "'] != null)", 399 &result)); 400 EXPECT_TRUE(result); 401 402 std::stringstream ss; 403 ss << "var dp = peerConnectionDataStore['" << pc_id << "']" 404 ".getDataSeries('" << graph_id << "').dataPoints_[" << index << "];" 405 "window.domAutomationController.send(dp.value.toString())"; 406 string actual_value; 407 ASSERT_TRUE(ExecuteScriptAndExtractString( 408 shell()->web_contents(), ss.str(), &actual_value)); 409 EXPECT_EQ(value, actual_value); 410 } 411 412 // Get the JSON string of the ssrc info from the page. 413 string GetSsrcInfo(const string& ssrc_id) { 414 string result; 415 EXPECT_TRUE(ExecuteScriptAndExtractString( 416 shell()->web_contents(), 417 "window.domAutomationController.send(JSON.stringify(" 418 "ssrcInfoManager.streamInfoContainer_['" + ssrc_id + "']))", 419 &result)); 420 return result; 421 } 422 423 int GetSsrcInfoBlockCount(Shell* shell) { 424 int count = 0; 425 EXPECT_TRUE(ExecuteScriptAndExtractInt( 426 shell->web_contents(), 427 "window.domAutomationController.send(" 428 "document.getElementsByClassName(" 429 "ssrcInfoManager.SSRC_INFO_BLOCK_CLASS).length);", 430 &count)); 431 return count; 432 } 433 434 // Verifies |dump| contains |peer_connection_number| peer connection dumps, 435 // each containing |update_number| updates and |stats_number| stats tables. 436 void VerifyPageDumpStructure(base::Value* dump, 437 int peer_connection_number, 438 int update_number, 439 int stats_number) { 440 EXPECT_NE((base::Value*)NULL, dump); 441 EXPECT_EQ(base::Value::TYPE_DICTIONARY, dump->GetType()); 442 443 base::DictionaryValue* dict_dump = 444 static_cast<base::DictionaryValue*>(dump); 445 EXPECT_EQ((size_t) peer_connection_number, dict_dump->size()); 446 447 base::DictionaryValue::Iterator it(*dict_dump); 448 for (; !it.IsAtEnd(); it.Advance()) { 449 base::Value* value = NULL; 450 dict_dump->Get(it.key(), &value); 451 EXPECT_EQ(base::Value::TYPE_DICTIONARY, value->GetType()); 452 base::DictionaryValue* pc_dump = 453 static_cast<base::DictionaryValue*>(value); 454 EXPECT_TRUE(pc_dump->HasKey("updateLog")); 455 EXPECT_TRUE(pc_dump->HasKey("stats")); 456 457 // Verifies the number of updates. 458 pc_dump->Get("updateLog", &value); 459 EXPECT_EQ(base::Value::TYPE_LIST, value->GetType()); 460 base::ListValue* list = static_cast<base::ListValue*>(value); 461 EXPECT_EQ((size_t) update_number, list->GetSize()); 462 463 // Verifies the number of stats tables. 464 pc_dump->Get("stats", &value); 465 EXPECT_EQ(base::Value::TYPE_DICTIONARY, value->GetType()); 466 base::DictionaryValue* dict = static_cast<base::DictionaryValue*>(value); 467 EXPECT_EQ((size_t) stats_number, dict->size()); 468 } 469 } 470 471 // Verifies |dump| contains the correct statsTable and statsDataSeries for 472 // |pc|. 473 void VerifyStatsDump(base::Value* dump, 474 const PeerConnectionEntry& pc, 475 const string& report_type, 476 const string& report_id, 477 const StatsUnit& stats) { 478 EXPECT_NE((base::Value*)NULL, dump); 479 EXPECT_EQ(base::Value::TYPE_DICTIONARY, dump->GetType()); 480 481 base::DictionaryValue* dict_dump = 482 static_cast<base::DictionaryValue*>(dump); 483 base::Value* value = NULL; 484 dict_dump->Get(pc.getIdString(), &value); 485 base::DictionaryValue* pc_dump = static_cast<base::DictionaryValue*>(value); 486 487 // Verifies there is one data series per stats name. 488 value = NULL; 489 pc_dump->Get("stats", &value); 490 EXPECT_EQ(base::Value::TYPE_DICTIONARY, value->GetType()); 491 492 base::DictionaryValue* dataSeries = 493 static_cast<base::DictionaryValue*>(value); 494 EXPECT_EQ(stats.values.size(), dataSeries->size()); 495 } 496 }; 497 498 IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcInternalsBrowserTest, 499 AddAndRemovePeerConnection) { 500 GURL url("chrome://webrtc-internals"); 501 NavigateToURL(shell(), url); 502 503 // Add two PeerConnections and then remove them. 504 PeerConnectionEntry pc_1(1, 0); 505 ExecuteAddPeerConnectionJs(pc_1); 506 VerifyPeerConnectionEntry(pc_1); 507 508 PeerConnectionEntry pc_2(2, 1); 509 ExecuteAddPeerConnectionJs(pc_2); 510 VerifyPeerConnectionEntry(pc_2); 511 512 ExecuteRemovePeerConnectionJs(pc_1); 513 VerifyNoElementWithId(pc_1.getIdString()); 514 VerifyPeerConnectionEntry(pc_2); 515 516 ExecuteRemovePeerConnectionJs(pc_2); 517 VerifyNoElementWithId(pc_2.getIdString()); 518 } 519 520 IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcInternalsBrowserTest, 521 UpdateAllPeerConnections) { 522 GURL url("chrome://webrtc-internals"); 523 NavigateToURL(shell(), url); 524 525 PeerConnectionEntry pc_0(1, 0); 526 pc_0.AddEvent("e1", "v1"); 527 pc_0.AddEvent("e2", "v2"); 528 PeerConnectionEntry pc_1(1, 1); 529 pc_1.AddEvent("e3", "v3"); 530 pc_1.AddEvent("e4", "v4"); 531 string pc_array = "[" + pc_0.getAllUpdateString() + ", " + 532 pc_1.getAllUpdateString() + "]"; 533 EXPECT_TRUE(ExecuteJavascript("updateAllPeerConnections(" + pc_array + ");")); 534 VerifyPeerConnectionEntry(pc_0); 535 VerifyPeerConnectionEntry(pc_1); 536 } 537 538 IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcInternalsBrowserTest, UpdatePeerConnection) { 539 GURL url("chrome://webrtc-internals"); 540 NavigateToURL(shell(), url); 541 542 // Add one PeerConnection and send one update. 543 PeerConnectionEntry pc_1(1, 0); 544 ExecuteAddPeerConnectionJs(pc_1); 545 546 ExecuteAndVerifyUpdatePeerConnection(pc_1, "e1", "v1"); 547 548 // Add another PeerConnection and send two updates. 549 PeerConnectionEntry pc_2(1, 1); 550 ExecuteAddPeerConnectionJs(pc_2); 551 552 SsrcEntry ssrc1, ssrc2; 553 ssrc1.id = "ssrcid1"; 554 ssrc1.properties["msid"] = "mymsid"; 555 ssrc2.id = "ssrcid2"; 556 ssrc2.properties["label"] = "mylabel"; 557 ssrc2.properties["cname"] = "mycname"; 558 559 ExecuteAndVerifyUpdatePeerConnection(pc_2, "setRemoteDescription", 560 ssrc1.GetSsrcAttributeString()); 561 562 ExecuteAndVerifyUpdatePeerConnection(pc_2, "setLocalDescription", 563 ssrc2.GetSsrcAttributeString()); 564 565 EXPECT_EQ(ssrc1.GetAsJSON(), GetSsrcInfo(ssrc1.id)); 566 EXPECT_EQ(ssrc2.GetAsJSON(), GetSsrcInfo(ssrc2.id)); 567 568 StatsUnit stats = {FAKE_TIME_STAMP}; 569 stats.values["ssrc"] = ssrc1.id; 570 ExecuteAndVerifyAddStats(pc_2, "ssrc", "dummyId", stats); 571 EXPECT_GT(GetSsrcInfoBlockCount(shell()), 0); 572 } 573 574 // Tests that adding random named stats updates the dataSeries and graphs. 575 IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcInternalsBrowserTest, AddStats) { 576 GURL url("chrome://webrtc-internals"); 577 NavigateToURL(shell(), url); 578 579 PeerConnectionEntry pc(1, 0); 580 ExecuteAddPeerConnectionJs(pc); 581 582 const string type = "ssrc"; 583 const string id = "ssrc-1234"; 584 StatsUnit stats = {FAKE_TIME_STAMP}; 585 stats.values["trackId"] = "abcd"; 586 stats.values["bitrate"] = "2000"; 587 stats.values["framerate"] = "30"; 588 589 // Add new stats and verify the stats table and graphs. 590 ExecuteAndVerifyAddStats(pc, type, id, stats); 591 VerifyStatsGraph(pc); 592 593 // Update existing stats and verify the stats table and graphs. 594 stats.values["bitrate"] = "2001"; 595 stats.values["framerate"] = "31"; 596 ExecuteAndVerifyAddStats(pc, type, id, stats); 597 VerifyStatsGraph(pc); 598 } 599 600 // Tests that the bandwidth estimation values are drawn on a single graph. 601 IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcInternalsBrowserTest, BweCompoundGraph) { 602 GURL url("chrome://webrtc-internals"); 603 NavigateToURL(shell(), url); 604 605 PeerConnectionEntry pc(1, 0); 606 ExecuteAddPeerConnectionJs(pc); 607 608 StatsUnit stats = {FAKE_TIME_STAMP}; 609 stats.values["googAvailableSendBandwidth"] = "1000000"; 610 stats.values["googTargetEncBitrate"] = "1000"; 611 stats.values["googActualEncBitrate"] = "1000000"; 612 stats.values["googRetransmitBitrate"] = "10"; 613 stats.values["googTransmitBitrate"] = "1000000"; 614 const string stats_type = "bwe"; 615 const string stats_id = "videobwe"; 616 ExecuteAndVerifyAddStats(pc, stats_type, stats_id, stats); 617 618 string graph_id = 619 pc.getIdString() + "-" + stats_id + "-bweCompound"; 620 bool result = false; 621 // Verify that the bweCompound graph exists. 622 ASSERT_TRUE(ExecuteScriptAndExtractBool( 623 shell()->web_contents(), 624 "window.domAutomationController.send(" 625 " graphViews['" + graph_id + "'] != null)", 626 &result)); 627 EXPECT_TRUE(result); 628 629 // Verify that the bweCompound graph contains multiple dataSeries. 630 int count = 0; 631 ASSERT_TRUE(ExecuteScriptAndExtractInt( 632 shell()->web_contents(), 633 "window.domAutomationController.send(" 634 " graphViews['" + graph_id + "'].getDataSeriesCount())", 635 &count)); 636 EXPECT_EQ((int)stats.values.size(), count); 637 } 638 639 // Tests that the total packet/byte count is converted to count per second, 640 // and the converted data is drawn. 641 IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcInternalsBrowserTest, ConvertedGraphs) { 642 GURL url("chrome://webrtc-internals"); 643 NavigateToURL(shell(), url); 644 645 PeerConnectionEntry pc(1, 0); 646 ExecuteAddPeerConnectionJs(pc); 647 648 const string stats_type = "s"; 649 const string stats_id = "1"; 650 const int num_converted_stats = 4; 651 const string stats_names[] = 652 {"packetsSent", "bytesSent", "packetsReceived", "bytesReceived"}; 653 const string converted_names[] = 654 {"packetsSentPerSecond", "bitsSentPerSecond", 655 "packetsReceivedPerSecond", "bitsReceivedPerSecond"}; 656 const string first_value = "1000"; 657 const string second_value = "2000"; 658 const string converted_values[] = {"1000", "8000", "1000", "8000"}; 659 660 // Send the first data point. 661 StatsUnit stats = {FAKE_TIME_STAMP}; 662 for (int i = 0; i < num_converted_stats; ++i) 663 stats.values[stats_names[i]] = first_value; 664 665 ExecuteAndVerifyAddStats(pc, stats_type, stats_id, stats); 666 667 // Send the second data point at 1000ms after the first data point. 668 stats.timestamp += 1000; 669 for (int i = 0; i < num_converted_stats; ++i) 670 stats.values[stats_names[i]] = second_value; 671 ExecuteAndVerifyAddStats(pc, stats_type, stats_id, stats); 672 673 // Verifies the graph data matches converted_values. 674 for (int i = 0; i < num_converted_stats; ++i) { 675 VerifyGraphDataPoint(pc.getIdString(), stats_id + "-" + converted_names[i], 676 1, converted_values[i]); 677 } 678 } 679 680 // Timing out on ARM linux bot: http://crbug.com/238490 681 // Disabling due to failure on Linux, Mac, Win: http://crbug.com/272413 682 // Sanity check of the page content under a real PeerConnection call. 683 IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcInternalsBrowserTest, 684 DISABLED_WithRealPeerConnectionCall) { 685 // Start a peerconnection call in the first window. 686 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady()); 687 GURL url(embedded_test_server()->GetURL("/media/peerconnection-call.html")); 688 NavigateToURL(shell(), url); 689 ASSERT_TRUE(ExecuteJavascript("call({video:true});")); 690 ExpectTitle("OK"); 691 692 // Open webrtc-internals in the second window. 693 GURL url2("chrome://webrtc-internals"); 694 Shell* shell2 = CreateBrowser(); 695 NavigateToURL(shell2, url2); 696 697 const int NUMBER_OF_PEER_CONNECTIONS = 2; 698 699 // Verifies the number of peerconnections. 700 int count = 0; 701 ASSERT_TRUE(ExecuteScriptAndExtractInt( 702 shell2->web_contents(), 703 "window.domAutomationController.send(" 704 "$('peer-connections-list').getElementsByTagName('li').length);", 705 &count)); 706 EXPECT_EQ(NUMBER_OF_PEER_CONNECTIONS, count); 707 708 // Verifies the the event tables. 709 ASSERT_TRUE(ExecuteScriptAndExtractInt( 710 shell2->web_contents(), 711 "window.domAutomationController.send($('peer-connections-list')" 712 ".getElementsByClassName('update-log-table').length);", 713 &count)); 714 EXPECT_EQ(NUMBER_OF_PEER_CONNECTIONS, count); 715 716 ASSERT_TRUE(ExecuteScriptAndExtractInt( 717 shell2->web_contents(), 718 "window.domAutomationController.send($('peer-connections-list')" 719 ".getElementsByClassName('update-log-table')[0].rows.length);", 720 &count)); 721 EXPECT_GT(count, 1); 722 723 ASSERT_TRUE(ExecuteScriptAndExtractInt( 724 shell2->web_contents(), 725 "window.domAutomationController.send($('peer-connections-list')" 726 ".getElementsByClassName('update-log-table')[1].rows.length);", 727 &count)); 728 EXPECT_GT(count, 1); 729 730 // Wait until the stats table containers are created. 731 count = 0; 732 while (count != NUMBER_OF_PEER_CONNECTIONS) { 733 ASSERT_TRUE(ExecuteScriptAndExtractInt( 734 shell2->web_contents(), 735 "window.domAutomationController.send(" 736 "$('peer-connections-list').getElementsByClassName(" 737 "'stats-table-container').length);", 738 &count)); 739 } 740 741 // Verifies each stats table having more than one rows. 742 bool result = false; 743 ASSERT_TRUE(ExecuteScriptAndExtractBool( 744 shell2->web_contents(), 745 "var tableContainers = $('peer-connections-list')" 746 ".getElementsByClassName('stats-table-container');" 747 "var result = true;" 748 "for (var i = 0; i < tableContainers.length && result; ++i) {" 749 "var tables = tableContainers[i].getElementsByTagName('table');" 750 "for (var j = 0; j < tables.length && result; ++j) {" 751 "result = (tables[j].rows.length > 1);" 752 "}" 753 "if (!result) {" 754 "console.log(tableContainers[i].innerHTML);" 755 "}" 756 "}" 757 "window.domAutomationController.send(result);", 758 &result)); 759 760 EXPECT_TRUE(result); 761 762 count = GetSsrcInfoBlockCount(shell2); 763 EXPECT_GT(count, 0); 764 } 765 766 IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcInternalsBrowserTest, CreatePageDump) { 767 GURL url("chrome://webrtc-internals"); 768 NavigateToURL(shell(), url); 769 770 PeerConnectionEntry pc_0(1, 0); 771 pc_0.AddEvent("e1", "v1"); 772 pc_0.AddEvent("e2", "v2"); 773 PeerConnectionEntry pc_1(1, 1); 774 pc_1.AddEvent("e3", "v3"); 775 pc_1.AddEvent("e4", "v4"); 776 string pc_array = 777 "[" + pc_0.getAllUpdateString() + ", " + pc_1.getAllUpdateString() + "]"; 778 EXPECT_TRUE(ExecuteJavascript("updateAllPeerConnections(" + pc_array + ");")); 779 780 // Verifies the peer connection data store can be created without stats. 781 string dump_json; 782 ASSERT_TRUE(ExecuteScriptAndExtractString( 783 shell()->web_contents(), 784 "window.domAutomationController.send(" 785 "JSON.stringify(peerConnectionDataStore));", 786 &dump_json)); 787 scoped_ptr<base::Value> dump; 788 dump.reset(base::JSONReader::Read(dump_json)); 789 VerifyPageDumpStructure(dump.get(), 790 2 /*peer_connection_number*/, 791 2 /*update_number*/, 792 0 /*stats_number*/); 793 794 // Adds a stats report. 795 const string type = "dummy"; 796 const string id = "1234"; 797 StatsUnit stats = { FAKE_TIME_STAMP }; 798 stats.values["bitrate"] = "2000"; 799 stats.values["framerate"] = "30"; 800 ExecuteAndVerifyAddStats(pc_0, type, id, stats); 801 802 ASSERT_TRUE(ExecuteScriptAndExtractString( 803 shell()->web_contents(), 804 "window.domAutomationController.send(" 805 "JSON.stringify(peerConnectionDataStore));", 806 &dump_json)); 807 dump.reset(base::JSONReader::Read(dump_json)); 808 VerifyStatsDump(dump.get(), pc_0, type, id, stats); 809 } 810 811 IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcInternalsBrowserTest, UpdateGetUserMedia) { 812 GURL url("chrome://webrtc-internals"); 813 NavigateToURL(shell(), url); 814 815 UserMediaRequestEntry request1(1, 1, "origin", "ac", "vc"); 816 UserMediaRequestEntry request2(2, 2, "origin2", "ac2", "vc2"); 817 ExecuteAddGetUserMediaJs(request1); 818 ExecuteAddGetUserMediaJs(request2); 819 820 std::vector<UserMediaRequestEntry> list; 821 list.push_back(request1); 822 list.push_back(request2); 823 VerifyUserMediaRequest(list); 824 825 ExecuteRemoveGetUserMediaForRendererJs(1); 826 list.erase(list.begin()); 827 VerifyUserMediaRequest(list); 828 829 ExecuteRemoveGetUserMediaForRendererJs(2); 830 list.erase(list.begin()); 831 VerifyUserMediaRequest(list); 832 } 833 834 // Tests that the received propagation delta values are converted and drawn 835 // correctly. 836 IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcInternalsBrowserTest, 837 ReceivedPropagationDelta) { 838 GURL url("chrome://webrtc-internals"); 839 NavigateToURL(shell(), url); 840 841 PeerConnectionEntry pc(1, 0); 842 ExecuteAddPeerConnectionJs(pc); 843 844 StatsUnit stats = {FAKE_TIME_STAMP}; 845 stats.values["googReceivedPacketGroupArrivalTimeDebug"] = 846 "[1000, 1100, 1200]"; 847 stats.values["googReceivedPacketGroupPropagationDeltaDebug"] = 848 "[10, 20, 30]"; 849 const string stats_type = "bwe"; 850 const string stats_id = "videobwe"; 851 ExecuteAndVerifyAddStats(pc, stats_type, stats_id, stats); 852 853 string graph_id = pc.getIdString() + "-" + stats_id + 854 "-googReceivedPacketGroupPropagationDeltaDebug"; 855 string data_series_id = 856 stats_id + "-googReceivedPacketGroupPropagationDeltaDebug"; 857 bool result = false; 858 // Verify that the graph exists. 859 ASSERT_TRUE(ExecuteScriptAndExtractBool( 860 shell()->web_contents(), 861 "window.domAutomationController.send(" 862 " graphViews['" + graph_id + "'] != null)", 863 &result)); 864 EXPECT_TRUE(result); 865 866 // Verify that the graph contains multiple data points. 867 int count = 0; 868 ASSERT_TRUE(ExecuteScriptAndExtractInt( 869 shell()->web_contents(), 870 "window.domAutomationController.send(" 871 " graphViews['" + graph_id + "'].getDataSeriesCount())", 872 &count)); 873 EXPECT_EQ(1, count); 874 ASSERT_TRUE(ExecuteScriptAndExtractInt( 875 shell()->web_contents(), 876 "window.domAutomationController.send(" 877 " peerConnectionDataStore['" + pc.getIdString() + "']" + 878 " .getDataSeries('" + data_series_id + "').getCount())", 879 &count)); 880 EXPECT_EQ(3, count); 881 } 882 883 } // namespace content 884