diff options
Diffstat (limited to 'content/browser/media/webrtc/webrtc_internals_browsertest.cc')
-rw-r--r-- | content/browser/media/webrtc/webrtc_internals_browsertest.cc | 884 |
1 files changed, 884 insertions, 0 deletions
diff --git a/content/browser/media/webrtc/webrtc_internals_browsertest.cc b/content/browser/media/webrtc/webrtc_internals_browsertest.cc new file mode 100644 index 0000000..a266d03 --- /dev/null +++ b/content/browser/media/webrtc/webrtc_internals_browsertest.cc @@ -0,0 +1,884 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <stddef.h> +#include <stdint.h> + +#include "base/command_line.h" +#include "base/json/json_reader.h" +#include "base/strings/utf_string_conversions.h" +#include "base/time/time.h" +#include "base/values.h" +#include "build/build_config.h" +#include "content/public/common/content_switches.h" +#include "content/public/test/browser_test_utils.h" +#include "content/public/test/content_browser_test.h" +#include "content/public/test/content_browser_test_utils.h" +#include "content/shell/browser/shell.h" +#include "media/base/media_switches.h" +#include "net/test/embedded_test_server/embedded_test_server.h" + +using std::string; +namespace content { + +struct SsrcEntry { + string GetSsrcAttributeString() const { + std::stringstream ss; + ss << "a=ssrc:" << id; + std::map<string, string>::const_iterator iter; + for (iter = properties.begin(); iter != properties.end(); ++iter) { + ss << " " << iter->first << ":" << iter->second; + } + return ss.str(); + } + + string GetAsJSON() const { + std::stringstream ss; + ss << "{"; + std::map<string, string>::const_iterator iter; + for (iter = properties.begin(); iter != properties.end(); ++iter) { + if (iter != properties.begin()) + ss << ","; + ss << "\"" << iter->first << "\":\"" << iter->second << "\""; + } + ss << "}"; + return ss.str(); + } + + string id; + std::map<string, string> properties; +}; + +struct EventEntry { + string type; + string value; +}; + +struct StatsUnit { + string GetString() const { + std::stringstream ss; + ss << "{timestamp:" << timestamp << ", values:["; + std::map<string, string>::const_iterator iter; + for (iter = values.begin(); iter != values.end(); ++iter) { + ss << "'" << iter->first << "','" << iter->second << "',"; + } + ss << "]}"; + return ss.str(); + } + + int64_t timestamp; + std::map<string, string> values; +}; + +struct StatsEntry { + string type; + string id; + StatsUnit stats; +}; + +typedef std::map<string, std::vector<string> > StatsMap; + +class PeerConnectionEntry { + public: + PeerConnectionEntry(int pid, int lid) : pid_(pid), lid_(lid) {} + + void AddEvent(const string& type, const string& value) { + EventEntry entry = {type, value}; + events_.push_back(entry); + } + + string getIdString() const { + std::stringstream ss; + ss << pid_ << "-" << lid_; + return ss.str(); + } + + string getLogIdString() const { + std::stringstream ss; + ss << pid_ << "-" << lid_ << "-update-log"; + return ss.str(); + } + + string getAllUpdateString() const { + std::stringstream ss; + ss << "{pid:" << pid_ << ", lid:" << lid_ << ", log:["; + for (size_t i = 0; i < events_.size(); ++i) { + ss << "{type:'" << events_[i].type << + "', value:'" << events_[i].value << "'},"; + } + ss << "]}"; + return ss.str(); + } + + int pid_; + int lid_; + std::vector<EventEntry> events_; + // This is a record of the history of stats value reported for each stats + // report id (e.g. ssrc-1234) for each stats name (e.g. framerate). + // It a 2-D map with each map entry is a vector of reported values. + // It is used to verify the graph data series. + std::map<string, StatsMap> stats_; +}; + +class UserMediaRequestEntry { + public: + UserMediaRequestEntry(int pid, + int rid, + const std::string& origin, + const std::string& audio_constraints, + const std::string& video_constraints) + : pid(pid), + rid(rid), + origin(origin), + audio_constraints(audio_constraints), + video_constraints(video_constraints) {} + + int pid; + int rid; + std::string origin; + std::string audio_constraints; + std::string video_constraints; +}; + +static const int64_t FAKE_TIME_STAMP = 3600000; + +#if defined(OS_WIN) +// All tests are flaky on Windows: crbug.com/277322. +#define MAYBE_WebRtcInternalsBrowserTest DISABLED_WebRtcInternalsBrowserTest +#else +#define MAYBE_WebRtcInternalsBrowserTest WebRtcInternalsBrowserTest +#endif + +class MAYBE_WebRtcInternalsBrowserTest: public ContentBrowserTest { + public: + MAYBE_WebRtcInternalsBrowserTest() {} + ~MAYBE_WebRtcInternalsBrowserTest() override {} + + void SetUpOnMainThread() override { + base::CommandLine::ForCurrentProcess()->AppendSwitch( + switches::kUseFakeUIForMediaStream); + ASSERT_TRUE(base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kUseFakeDeviceForMediaStream)); + } + + protected: + bool ExecuteJavascript(const string& javascript) { + return ExecuteScript(shell()->web_contents(), javascript); + } + + void ExpectTitle(const std::string& expected_title) const { + base::string16 expected_title16(base::ASCIIToUTF16(expected_title)); + TitleWatcher title_watcher(shell()->web_contents(), expected_title16); + EXPECT_EQ(expected_title16, title_watcher.WaitAndGetTitle()); + } + + // Execute the javascript of addPeerConnection. + void ExecuteAddPeerConnectionJs(const PeerConnectionEntry& pc) { + std::stringstream ss; + ss << "{pid:" << pc.pid_ <<", lid:" << pc.lid_ << ", " << + "url:'u', rtcConfiguration:'s', constraints:'c'}"; + ASSERT_TRUE(ExecuteJavascript("addPeerConnection(" + ss.str() + ");")); + } + + // Execute the javascript of removePeerConnection. + void ExecuteRemovePeerConnectionJs(const PeerConnectionEntry& pc) { + std::stringstream ss; + ss << "{pid:" << pc.pid_ <<", lid:" << pc.lid_ << "}"; + + ASSERT_TRUE(ExecuteJavascript("removePeerConnection(" + ss.str() + ");")); + } + + // Execute the javascript of addGetUserMedia. + void ExecuteAddGetUserMediaJs(const UserMediaRequestEntry& request) { + std::stringstream ss; + ss << "{pid:" << request.pid << ", rid:" << request.rid << ", origin:'" + << request.origin << "', audio:'" << request.audio_constraints + << "', video:'" << request.video_constraints << "'}"; + + ASSERT_TRUE(ExecuteJavascript("addGetUserMedia(" + ss.str() + ");")); + } + + // Execute the javascript of removeGetUserMediaForRenderer. + void ExecuteRemoveGetUserMediaForRendererJs(int rid) { + std::stringstream ss; + ss << "{rid:" << rid << "}"; + ASSERT_TRUE( + ExecuteJavascript("removeGetUserMediaForRenderer(" + ss.str() + ");")); + } + + // Verifies that the DOM element with id |id| exists. + void VerifyElementWithId(const string& id) { + bool result = false; + ASSERT_TRUE(ExecuteScriptAndExtractBool( + shell()->web_contents(), + "window.domAutomationController.send($('" + id + "') != null);", + &result)); + EXPECT_TRUE(result); + } + + // Verifies that the DOM element with id |id| does not exist. + void VerifyNoElementWithId(const string& id) { + bool result = false; + ASSERT_TRUE(ExecuteScriptAndExtractBool( + shell()->web_contents(), + "window.domAutomationController.send($('" + id + "') == null);", + &result)); + EXPECT_TRUE(result); + } + + // Verifies the JS Array of userMediaRequests matches |requests|. + void VerifyUserMediaRequest( + const std::vector<UserMediaRequestEntry>& requests) { + string json_requests; + ASSERT_TRUE(ExecuteScriptAndExtractString( + shell()->web_contents(), + "window.domAutomationController.send(" + "JSON.stringify(userMediaRequests));", + &json_requests)); + scoped_ptr<base::Value> value_requests = + base::JSONReader::Read(json_requests); + + EXPECT_EQ(base::Value::TYPE_LIST, value_requests->GetType()); + + base::ListValue* list_request = + static_cast<base::ListValue*>(value_requests.get()); + EXPECT_EQ(requests.size(), list_request->GetSize()); + + for (size_t i = 0; i < requests.size(); ++i) { + base::DictionaryValue* dict = NULL; + ASSERT_TRUE(list_request->GetDictionary(i, &dict)); + int pid, rid; + std::string origin, audio, video; + ASSERT_TRUE(dict->GetInteger("pid", &pid)); + ASSERT_TRUE(dict->GetInteger("rid", &rid)); + ASSERT_TRUE(dict->GetString("origin", &origin)); + ASSERT_TRUE(dict->GetString("audio", &audio)); + ASSERT_TRUE(dict->GetString("video", &video)); + EXPECT_EQ(requests[i].pid, pid); + EXPECT_EQ(requests[i].rid, rid); + EXPECT_EQ(requests[i].origin, origin); + EXPECT_EQ(requests[i].audio_constraints, audio); + EXPECT_EQ(requests[i].video_constraints, video); + } + + bool user_media_tab_existed = false; + ASSERT_TRUE(ExecuteScriptAndExtractBool( + shell()->web_contents(), + "window.domAutomationController.send(" + "$('user-media-tab-id') != null);", + &user_media_tab_existed)); + EXPECT_EQ(!requests.empty(), user_media_tab_existed); + + if (user_media_tab_existed) { + int user_media_request_count = -1; + ASSERT_TRUE(ExecuteScriptAndExtractInt( + shell()->web_contents(), + "window.domAutomationController.send(" + "$('user-media-tab-id').childNodes.length);", + &user_media_request_count)); + ASSERT_EQ(requests.size(), static_cast<size_t>(user_media_request_count)); + } + } + + // Verifies that DOM for |pc| is correctly created with the right content. + void VerifyPeerConnectionEntry(const PeerConnectionEntry& pc) { + VerifyElementWithId(pc.getIdString()); + if (pc.events_.size() == 0) + return; + + string log_id = pc.getLogIdString(); + VerifyElementWithId(log_id); + string result; + for (size_t i = 0; i < pc.events_.size(); ++i) { + std::stringstream ss; + ss << "var row = $('" << log_id << "').rows[" << (i + 1) << "];" + "var cell = row.lastChild;" + "window.domAutomationController.send(cell.firstChild.textContent);"; + ASSERT_TRUE(ExecuteScriptAndExtractString( + shell()->web_contents(), ss.str(), &result)); + EXPECT_EQ(pc.events_[i].type + pc.events_[i].value, result); + } + } + + // Executes the javascript of updatePeerConnection and verifies the result. + void ExecuteAndVerifyUpdatePeerConnection( + PeerConnectionEntry& pc, const string& type, const string& value) { + pc.AddEvent(type, value); + + std::stringstream ss; + ss << "{pid:" << pc.pid_ <<", lid:" << pc.lid_ << + ", type:'" << type << "', value:'" << value << "'}"; + ASSERT_TRUE(ExecuteJavascript("updatePeerConnection(" + ss.str() + ")")); + + VerifyPeerConnectionEntry(pc); + } + + // Execute addStats and verifies that the stats table has the right content. + void ExecuteAndVerifyAddStats( + PeerConnectionEntry& pc, const string& type, const string& id, + StatsUnit& stats) { + StatsEntry entry = {type, id, stats}; + + // Adds each new value to the map of stats history. + std::map<string, string>::iterator iter; + for (iter = stats.values.begin(); iter != stats.values.end(); iter++) { + pc.stats_[id][iter->first].push_back(iter->second); + } + std::stringstream ss; + ss << "{pid:" << pc.pid_ << ", lid:" << pc.lid_ << "," + "reports:[" << "{id:'" << id << "', type:'" << type << "', " + "stats:" << stats.GetString() << "}]}"; + + ASSERT_TRUE(ExecuteJavascript("addStats(" + ss.str() + ")")); + VerifyStatsTable(pc, entry); + } + + + // Verifies that the stats table has the right content. + void VerifyStatsTable(const PeerConnectionEntry& pc, + const StatsEntry& report) { + string table_id = + pc.getIdString() + "-table-" + report.id; + VerifyElementWithId(table_id); + + std::map<string, string>::const_iterator iter; + for (iter = report.stats.values.begin(); + iter != report.stats.values.end(); iter++) { + VerifyStatsTableRow(table_id, iter->first, iter->second); + } + } + + // Verifies that the row named as |name| of the stats table |table_id| has + // the correct content as |name| : |value|. + void VerifyStatsTableRow(const string& table_id, + const string& name, + const string& value) { + VerifyElementWithId(table_id + "-" + name); + + string result; + ASSERT_TRUE(ExecuteScriptAndExtractString( + shell()->web_contents(), + "var row = $('" + table_id + "-" + name + "');" + "var name = row.cells[0].textContent;" + "var value = row.cells[1].textContent;" + "window.domAutomationController.send(name + ':' + value)", + &result)); + EXPECT_EQ(name + ":" + value, result); + } + + // Verifies that the graph data series consistent with pc.stats_. + void VerifyStatsGraph(const PeerConnectionEntry& pc) { + std::map<string, StatsMap>::const_iterator stream_iter; + for (stream_iter = pc.stats_.begin(); + stream_iter != pc.stats_.end(); stream_iter++) { + StatsMap::const_iterator stats_iter; + for (stats_iter = stream_iter->second.begin(); + stats_iter != stream_iter->second.end(); + stats_iter++) { + string graph_id = stream_iter->first + "-" + stats_iter->first; + for (size_t i = 0; i < stats_iter->second.size(); ++i) { + float number; + std::stringstream stream(stats_iter->second[i]); + stream >> number; + if (stream.fail()) + continue; + VerifyGraphDataPoint( + pc.getIdString(), graph_id, i, stats_iter->second[i]); + } + } + } + } + + // Verifies that the graph data point at index |index| has value |value|. + void VerifyGraphDataPoint(const string& pc_id, const string& graph_id, + int index, const string& value) { + bool result = false; + ASSERT_TRUE(ExecuteScriptAndExtractBool( + shell()->web_contents(), + "window.domAutomationController.send(" + "graphViews['" + pc_id + "-" + graph_id + "'] != null)", + &result)); + EXPECT_TRUE(result); + + std::stringstream ss; + ss << "var dp = peerConnectionDataStore['" << pc_id << "']" + ".getDataSeries('" << graph_id << "').dataPoints_[" << index << "];" + "window.domAutomationController.send(dp.value.toString())"; + string actual_value; + ASSERT_TRUE(ExecuteScriptAndExtractString( + shell()->web_contents(), ss.str(), &actual_value)); + EXPECT_EQ(value, actual_value); + } + + // Get the JSON string of the ssrc info from the page. + string GetSsrcInfo(const string& ssrc_id) { + string result; + EXPECT_TRUE(ExecuteScriptAndExtractString( + shell()->web_contents(), + "window.domAutomationController.send(JSON.stringify(" + "ssrcInfoManager.streamInfoContainer_['" + ssrc_id + "']))", + &result)); + return result; + } + + int GetSsrcInfoBlockCount(Shell* shell) { + int count = 0; + EXPECT_TRUE(ExecuteScriptAndExtractInt( + shell->web_contents(), + "window.domAutomationController.send(" + "document.getElementsByClassName(" + "ssrcInfoManager.SSRC_INFO_BLOCK_CLASS).length);", + &count)); + return count; + } + + // Verifies |dump| contains |peer_connection_number| peer connection dumps, + // each containing |update_number| updates and |stats_number| stats tables. + void VerifyPageDumpStructure(base::Value* dump, + int peer_connection_number, + int update_number, + int stats_number) { + EXPECT_NE((base::Value*)NULL, dump); + EXPECT_EQ(base::Value::TYPE_DICTIONARY, dump->GetType()); + + base::DictionaryValue* dict_dump = + static_cast<base::DictionaryValue*>(dump); + EXPECT_EQ((size_t) peer_connection_number, dict_dump->size()); + + base::DictionaryValue::Iterator it(*dict_dump); + for (; !it.IsAtEnd(); it.Advance()) { + base::Value* value = NULL; + dict_dump->Get(it.key(), &value); + EXPECT_EQ(base::Value::TYPE_DICTIONARY, value->GetType()); + base::DictionaryValue* pc_dump = + static_cast<base::DictionaryValue*>(value); + EXPECT_TRUE(pc_dump->HasKey("updateLog")); + EXPECT_TRUE(pc_dump->HasKey("stats")); + + // Verifies the number of updates. + pc_dump->Get("updateLog", &value); + EXPECT_EQ(base::Value::TYPE_LIST, value->GetType()); + base::ListValue* list = static_cast<base::ListValue*>(value); + EXPECT_EQ((size_t) update_number, list->GetSize()); + + // Verifies the number of stats tables. + pc_dump->Get("stats", &value); + EXPECT_EQ(base::Value::TYPE_DICTIONARY, value->GetType()); + base::DictionaryValue* dict = static_cast<base::DictionaryValue*>(value); + EXPECT_EQ((size_t) stats_number, dict->size()); + } + } + + // Verifies |dump| contains the correct statsTable and statsDataSeries for + // |pc|. + void VerifyStatsDump(base::Value* dump, + const PeerConnectionEntry& pc, + const string& report_type, + const string& report_id, + const StatsUnit& stats) { + EXPECT_NE((base::Value*)NULL, dump); + EXPECT_EQ(base::Value::TYPE_DICTIONARY, dump->GetType()); + + base::DictionaryValue* dict_dump = + static_cast<base::DictionaryValue*>(dump); + base::Value* value = NULL; + dict_dump->Get(pc.getIdString(), &value); + base::DictionaryValue* pc_dump = static_cast<base::DictionaryValue*>(value); + + // Verifies there is one data series per stats name. + value = NULL; + pc_dump->Get("stats", &value); + EXPECT_EQ(base::Value::TYPE_DICTIONARY, value->GetType()); + + base::DictionaryValue* dataSeries = + static_cast<base::DictionaryValue*>(value); + EXPECT_EQ(stats.values.size(), dataSeries->size()); + } +}; + +IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcInternalsBrowserTest, + AddAndRemovePeerConnection) { + GURL url("chrome://webrtc-internals"); + NavigateToURL(shell(), url); + + // Add two PeerConnections and then remove them. + PeerConnectionEntry pc_1(1, 0); + ExecuteAddPeerConnectionJs(pc_1); + VerifyPeerConnectionEntry(pc_1); + + PeerConnectionEntry pc_2(2, 1); + ExecuteAddPeerConnectionJs(pc_2); + VerifyPeerConnectionEntry(pc_2); + + ExecuteRemovePeerConnectionJs(pc_1); + VerifyNoElementWithId(pc_1.getIdString()); + VerifyPeerConnectionEntry(pc_2); + + ExecuteRemovePeerConnectionJs(pc_2); + VerifyNoElementWithId(pc_2.getIdString()); +} + +IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcInternalsBrowserTest, + UpdateAllPeerConnections) { + GURL url("chrome://webrtc-internals"); + NavigateToURL(shell(), url); + + PeerConnectionEntry pc_0(1, 0); + pc_0.AddEvent("e1", "v1"); + pc_0.AddEvent("e2", "v2"); + PeerConnectionEntry pc_1(1, 1); + pc_1.AddEvent("e3", "v3"); + pc_1.AddEvent("e4", "v4"); + string pc_array = "[" + pc_0.getAllUpdateString() + ", " + + pc_1.getAllUpdateString() + "]"; + EXPECT_TRUE(ExecuteJavascript("updateAllPeerConnections(" + pc_array + ");")); + VerifyPeerConnectionEntry(pc_0); + VerifyPeerConnectionEntry(pc_1); +} + +IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcInternalsBrowserTest, UpdatePeerConnection) { + GURL url("chrome://webrtc-internals"); + NavigateToURL(shell(), url); + + // Add one PeerConnection and send one update. + PeerConnectionEntry pc_1(1, 0); + ExecuteAddPeerConnectionJs(pc_1); + + ExecuteAndVerifyUpdatePeerConnection(pc_1, "e1", "v1"); + + // Add another PeerConnection and send two updates. + PeerConnectionEntry pc_2(1, 1); + ExecuteAddPeerConnectionJs(pc_2); + + SsrcEntry ssrc1, ssrc2; + ssrc1.id = "ssrcid1"; + ssrc1.properties["msid"] = "mymsid"; + ssrc2.id = "ssrcid2"; + ssrc2.properties["label"] = "mylabel"; + ssrc2.properties["cname"] = "mycname"; + + ExecuteAndVerifyUpdatePeerConnection(pc_2, "setRemoteDescription", + ssrc1.GetSsrcAttributeString()); + + ExecuteAndVerifyUpdatePeerConnection(pc_2, "setLocalDescription", + ssrc2.GetSsrcAttributeString()); + + EXPECT_EQ(ssrc1.GetAsJSON(), GetSsrcInfo(ssrc1.id)); + EXPECT_EQ(ssrc2.GetAsJSON(), GetSsrcInfo(ssrc2.id)); + + StatsUnit stats = {FAKE_TIME_STAMP}; + stats.values["ssrc"] = ssrc1.id; + ExecuteAndVerifyAddStats(pc_2, "ssrc", "dummyId", stats); + EXPECT_GT(GetSsrcInfoBlockCount(shell()), 0); +} + +// Tests that adding random named stats updates the dataSeries and graphs. +IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcInternalsBrowserTest, AddStats) { + GURL url("chrome://webrtc-internals"); + NavigateToURL(shell(), url); + + PeerConnectionEntry pc(1, 0); + ExecuteAddPeerConnectionJs(pc); + + const string type = "ssrc"; + const string id = "ssrc-1234"; + StatsUnit stats = {FAKE_TIME_STAMP}; + stats.values["trackId"] = "abcd"; + stats.values["bitrate"] = "2000"; + stats.values["framerate"] = "30"; + + // Add new stats and verify the stats table and graphs. + ExecuteAndVerifyAddStats(pc, type, id, stats); + VerifyStatsGraph(pc); + + // Update existing stats and verify the stats table and graphs. + stats.values["bitrate"] = "2001"; + stats.values["framerate"] = "31"; + ExecuteAndVerifyAddStats(pc, type, id, stats); + VerifyStatsGraph(pc); +} + +// Tests that the bandwidth estimation values are drawn on a single graph. +IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcInternalsBrowserTest, BweCompoundGraph) { + GURL url("chrome://webrtc-internals"); + NavigateToURL(shell(), url); + + PeerConnectionEntry pc(1, 0); + ExecuteAddPeerConnectionJs(pc); + + StatsUnit stats = {FAKE_TIME_STAMP}; + stats.values["googAvailableSendBandwidth"] = "1000000"; + stats.values["googTargetEncBitrate"] = "1000"; + stats.values["googActualEncBitrate"] = "1000000"; + stats.values["googRetransmitBitrate"] = "10"; + stats.values["googTransmitBitrate"] = "1000000"; + const string stats_type = "bwe"; + const string stats_id = "videobwe"; + ExecuteAndVerifyAddStats(pc, stats_type, stats_id, stats); + + string graph_id = + pc.getIdString() + "-" + stats_id + "-bweCompound"; + bool result = false; + // Verify that the bweCompound graph exists. + ASSERT_TRUE(ExecuteScriptAndExtractBool( + shell()->web_contents(), + "window.domAutomationController.send(" + " graphViews['" + graph_id + "'] != null)", + &result)); + EXPECT_TRUE(result); + + // Verify that the bweCompound graph contains multiple dataSeries. + int count = 0; + ASSERT_TRUE(ExecuteScriptAndExtractInt( + shell()->web_contents(), + "window.domAutomationController.send(" + " graphViews['" + graph_id + "'].getDataSeriesCount())", + &count)); + EXPECT_EQ((int)stats.values.size(), count); +} + +// Tests that the total packet/byte count is converted to count per second, +// and the converted data is drawn. +IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcInternalsBrowserTest, ConvertedGraphs) { + GURL url("chrome://webrtc-internals"); + NavigateToURL(shell(), url); + + PeerConnectionEntry pc(1, 0); + ExecuteAddPeerConnectionJs(pc); + + const string stats_type = "s"; + const string stats_id = "1"; + const int num_converted_stats = 4; + const string stats_names[] = + {"packetsSent", "bytesSent", "packetsReceived", "bytesReceived"}; + const string converted_names[] = + {"packetsSentPerSecond", "bitsSentPerSecond", + "packetsReceivedPerSecond", "bitsReceivedPerSecond"}; + const string first_value = "1000"; + const string second_value = "2000"; + const string converted_values[] = {"1000", "8000", "1000", "8000"}; + + // Send the first data point. + StatsUnit stats = {FAKE_TIME_STAMP}; + for (int i = 0; i < num_converted_stats; ++i) + stats.values[stats_names[i]] = first_value; + + ExecuteAndVerifyAddStats(pc, stats_type, stats_id, stats); + + // Send the second data point at 1000ms after the first data point. + stats.timestamp += 1000; + for (int i = 0; i < num_converted_stats; ++i) + stats.values[stats_names[i]] = second_value; + ExecuteAndVerifyAddStats(pc, stats_type, stats_id, stats); + + // Verifies the graph data matches converted_values. + for (int i = 0; i < num_converted_stats; ++i) { + VerifyGraphDataPoint(pc.getIdString(), stats_id + "-" + converted_names[i], + 1, converted_values[i]); + } +} + +// Timing out on ARM linux bot: http://crbug.com/238490 +// Disabling due to failure on Linux, Mac, Win: http://crbug.com/272413 +// Sanity check of the page content under a real PeerConnection call. +IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcInternalsBrowserTest, + DISABLED_WithRealPeerConnectionCall) { + // Start a peerconnection call in the first window. + ASSERT_TRUE(embedded_test_server()->Start()); + GURL url(embedded_test_server()->GetURL("/media/peerconnection-call.html")); + NavigateToURL(shell(), url); + ASSERT_TRUE(ExecuteJavascript("call({video:true});")); + ExpectTitle("OK"); + + // Open webrtc-internals in the second window. + GURL url2("chrome://webrtc-internals"); + Shell* shell2 = CreateBrowser(); + NavigateToURL(shell2, url2); + + const int NUMBER_OF_PEER_CONNECTIONS = 2; + + // Verifies the number of peerconnections. + int count = 0; + ASSERT_TRUE(ExecuteScriptAndExtractInt( + shell2->web_contents(), + "window.domAutomationController.send(" + "$('peer-connections-list').getElementsByTagName('li').length);", + &count)); + EXPECT_EQ(NUMBER_OF_PEER_CONNECTIONS, count); + + // Verifies the the event tables. + ASSERT_TRUE(ExecuteScriptAndExtractInt( + shell2->web_contents(), + "window.domAutomationController.send($('peer-connections-list')" + ".getElementsByClassName('update-log-table').length);", + &count)); + EXPECT_EQ(NUMBER_OF_PEER_CONNECTIONS, count); + + ASSERT_TRUE(ExecuteScriptAndExtractInt( + shell2->web_contents(), + "window.domAutomationController.send($('peer-connections-list')" + ".getElementsByClassName('update-log-table')[0].rows.length);", + &count)); + EXPECT_GT(count, 1); + + ASSERT_TRUE(ExecuteScriptAndExtractInt( + shell2->web_contents(), + "window.domAutomationController.send($('peer-connections-list')" + ".getElementsByClassName('update-log-table')[1].rows.length);", + &count)); + EXPECT_GT(count, 1); + + // Wait until the stats table containers are created. + count = 0; + while (count != NUMBER_OF_PEER_CONNECTIONS) { + ASSERT_TRUE(ExecuteScriptAndExtractInt( + shell2->web_contents(), + "window.domAutomationController.send(" + "$('peer-connections-list').getElementsByClassName(" + "'stats-table-container').length);", + &count)); + } + + // Verifies each stats table having more than one rows. + bool result = false; + ASSERT_TRUE(ExecuteScriptAndExtractBool( + shell2->web_contents(), + "var tableContainers = $('peer-connections-list')" + ".getElementsByClassName('stats-table-container');" + "var result = true;" + "for (var i = 0; i < tableContainers.length && result; ++i) {" + "var tables = tableContainers[i].getElementsByTagName('table');" + "for (var j = 0; j < tables.length && result; ++j) {" + "result = (tables[j].rows.length > 1);" + "}" + "if (!result) {" + "console.log(tableContainers[i].innerHTML);" + "}" + "}" + "window.domAutomationController.send(result);", + &result)); + + EXPECT_TRUE(result); + + count = GetSsrcInfoBlockCount(shell2); + EXPECT_GT(count, 0); +} + +IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcInternalsBrowserTest, CreatePageDump) { + GURL url("chrome://webrtc-internals"); + NavigateToURL(shell(), url); + + PeerConnectionEntry pc_0(1, 0); + pc_0.AddEvent("e1", "v1"); + pc_0.AddEvent("e2", "v2"); + PeerConnectionEntry pc_1(1, 1); + pc_1.AddEvent("e3", "v3"); + pc_1.AddEvent("e4", "v4"); + string pc_array = + "[" + pc_0.getAllUpdateString() + ", " + pc_1.getAllUpdateString() + "]"; + EXPECT_TRUE(ExecuteJavascript("updateAllPeerConnections(" + pc_array + ");")); + + // Verifies the peer connection data store can be created without stats. + string dump_json; + ASSERT_TRUE(ExecuteScriptAndExtractString( + shell()->web_contents(), + "window.domAutomationController.send(" + "JSON.stringify(peerConnectionDataStore));", + &dump_json)); + scoped_ptr<base::Value> dump = base::JSONReader::Read(dump_json); + VerifyPageDumpStructure(dump.get(), + 2 /*peer_connection_number*/, + 2 /*update_number*/, + 0 /*stats_number*/); + + // Adds a stats report. + const string type = "dummy"; + const string id = "1234"; + StatsUnit stats = { FAKE_TIME_STAMP }; + stats.values["bitrate"] = "2000"; + stats.values["framerate"] = "30"; + ExecuteAndVerifyAddStats(pc_0, type, id, stats); + + ASSERT_TRUE(ExecuteScriptAndExtractString( + shell()->web_contents(), + "window.domAutomationController.send(" + "JSON.stringify(peerConnectionDataStore));", + &dump_json)); + dump = base::JSONReader::Read(dump_json); + VerifyStatsDump(dump.get(), pc_0, type, id, stats); +} + +IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcInternalsBrowserTest, UpdateGetUserMedia) { + GURL url("chrome://webrtc-internals"); + NavigateToURL(shell(), url); + + UserMediaRequestEntry request1(1, 1, "origin", "ac", "vc"); + UserMediaRequestEntry request2(2, 2, "origin2", "ac2", "vc2"); + ExecuteAddGetUserMediaJs(request1); + ExecuteAddGetUserMediaJs(request2); + + std::vector<UserMediaRequestEntry> list; + list.push_back(request1); + list.push_back(request2); + VerifyUserMediaRequest(list); + + ExecuteRemoveGetUserMediaForRendererJs(1); + list.erase(list.begin()); + VerifyUserMediaRequest(list); + + ExecuteRemoveGetUserMediaForRendererJs(2); + list.erase(list.begin()); + VerifyUserMediaRequest(list); +} + +// Tests that the received propagation delta values are converted and drawn +// correctly. +IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcInternalsBrowserTest, + ReceivedPropagationDelta) { + GURL url("chrome://webrtc-internals"); + NavigateToURL(shell(), url); + + PeerConnectionEntry pc(1, 0); + ExecuteAddPeerConnectionJs(pc); + + StatsUnit stats = {FAKE_TIME_STAMP}; + stats.values["googReceivedPacketGroupArrivalTimeDebug"] = + "[1000, 1100, 1200]"; + stats.values["googReceivedPacketGroupPropagationDeltaDebug"] = + "[10, 20, 30]"; + const string stats_type = "bwe"; + const string stats_id = "videobwe"; + ExecuteAndVerifyAddStats(pc, stats_type, stats_id, stats); + + string graph_id = pc.getIdString() + "-" + stats_id + + "-googReceivedPacketGroupPropagationDeltaDebug"; + string data_series_id = + stats_id + "-googReceivedPacketGroupPropagationDeltaDebug"; + bool result = false; + // Verify that the graph exists. + ASSERT_TRUE(ExecuteScriptAndExtractBool( + shell()->web_contents(), + "window.domAutomationController.send(" + " graphViews['" + graph_id + "'] != null)", + &result)); + EXPECT_TRUE(result); + + // Verify that the graph contains multiple data points. + int count = 0; + ASSERT_TRUE(ExecuteScriptAndExtractInt( + shell()->web_contents(), + "window.domAutomationController.send(" + " graphViews['" + graph_id + "'].getDataSeriesCount())", + &count)); + EXPECT_EQ(1, count); + ASSERT_TRUE(ExecuteScriptAndExtractInt( + shell()->web_contents(), + "window.domAutomationController.send(" + " peerConnectionDataStore['" + pc.getIdString() + "']" + + " .getDataSeries('" + data_series_id + "').getCount())", + &count)); + EXPECT_EQ(3, count); +} + +} // namespace content |