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
18using std::string;
19namespace content {
20
21struct 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
49struct EventEntry {
50  string type;
51  string value;
52};
53
54struct 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
70struct StatsEntry {
71  string type;
72  string id;
73  StatsUnit stats;
74};
75
76typedef std::map<string, std::vector<string> > StatsMap;
77
78class 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
120class 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
140static 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
149class 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
498IN_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
520IN_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
538IN_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.
575IN_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.
601IN_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.
641IN_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.
683IN_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
766IN_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
811IN_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.
836IN_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