Home | History | Annotate | Download | only in media
      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