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', 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