1// Copyright 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/file_util.h"
7#include "base/path_service.h"
8#include "base/process/launch.h"
9#include "base/strings/string_split.h"
10#include "base/strings/stringprintf.h"
11#include "base/synchronization/waitable_event.h"
12#include "base/test/test_timeouts.h"
13#include "base/time/time.h"
14#include "chrome/browser/browser_process.h"
15#include "chrome/browser/chrome_notification_types.h"
16#include "chrome/browser/infobars/infobar.h"
17#include "chrome/browser/infobars/infobar_service.h"
18#include "chrome/browser/media/media_stream_infobar_delegate.h"
19#include "chrome/browser/media/webrtc_browsertest_base.h"
20#include "chrome/browser/media/webrtc_browsertest_common.h"
21#include "chrome/browser/media/webrtc_log_uploader.h"
22#include "chrome/browser/profiles/profile.h"
23#include "chrome/browser/ui/browser.h"
24#include "chrome/browser/ui/browser_tabstrip.h"
25#include "chrome/browser/ui/tabs/tab_strip_model.h"
26#include "chrome/common/chrome_switches.h"
27#include "chrome/test/base/in_process_browser_test.h"
28#include "chrome/test/base/ui_test_utils.h"
29#include "chrome/test/ui/ui_test.h"
30#include "content/public/browser/browser_message_filter.h"
31#include "content/public/browser/notification_service.h"
32#include "content/public/browser/render_process_host.h"
33#include "content/public/test/browser_test_utils.h"
34#include "net/test/embedded_test_server/embedded_test_server.h"
35
36static const char kMainWebrtcTestHtmlPage[] =
37    "/webrtc/webrtc_jsep01_test.html";
38
39static const char kTestLoggingSessionId[] = "0123456789abcdef";
40
41// Top-level integration test for WebRTC. Requires a real webcam and microphone
42// on the running system. This test is not meant to run in the main browser
43// test suite since normal tester machines do not have webcams.
44class WebrtcBrowserTest : public WebRtcTestBase {
45 public:
46  // See comment in test where this class is used for purpose.
47  class BrowserMessageFilter : public content::BrowserMessageFilter {
48   public:
49    explicit BrowserMessageFilter(
50        const base::Closure& on_channel_closing)
51        : on_channel_closing_(on_channel_closing) {}
52
53    virtual void OnChannelClosing() OVERRIDE {
54      // Posting on the file thread ensures that the callback is run after
55      // WebRtcLogUploader::UploadLog has finished. See also comment in
56      // MANUAL_RunsAudioVideoWebRTCCallInTwoTabsWithLogging test.
57      content::BrowserThread::PostTask(content::BrowserThread::FILE,
58          FROM_HERE, on_channel_closing_);
59    }
60
61    virtual bool OnMessageReceived(const IPC::Message& message,
62                                   bool* message_was_ok) OVERRIDE {
63      return false;
64    }
65
66  private:
67    virtual ~BrowserMessageFilter() {}
68
69    base::Closure on_channel_closing_;
70  };
71
72  virtual void SetUp() OVERRIDE {
73    // TODO(phoglund): Remove this when the bots enable real GPU with the
74    // command line. crbug.com/271504
75    UseRealGLBindings();
76
77    WebRtcTestBase::SetUp();
78  }
79
80  virtual void SetUpInProcessBrowserTestFixture() OVERRIDE {
81    PeerConnectionServerRunner::KillAllPeerConnectionServersOnCurrentSystem();
82    peerconnection_server_.Start();
83  }
84
85  virtual void TearDownInProcessBrowserTestFixture() OVERRIDE {
86    peerconnection_server_.Stop();
87  }
88
89  virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
90    // TODO(phoglund): check that user actually has the requisite devices and
91    // print a nice message if not; otherwise the test just times out which can
92    // be confusing.
93    // This test expects real device handling and requires a real webcam / audio
94    // device; it will not work with fake devices.
95    EXPECT_FALSE(command_line->HasSwitch(
96        switches::kUseFakeDeviceForMediaStream));
97    EXPECT_FALSE(command_line->HasSwitch(
98        switches::kUseFakeUIForMediaStream));
99  }
100
101  // Convenience method which executes the provided javascript in the context
102  // of the provided web contents and returns what it evaluated to.
103  std::string ExecuteJavascript(const std::string& javascript,
104                                content::WebContents* tab_contents) {
105    std::string result;
106    EXPECT_TRUE(content::ExecuteScriptAndExtractString(
107        tab_contents, javascript, &result));
108    return result;
109  }
110
111  // Ensures we didn't get any errors asynchronously (e.g. while no javascript
112  // call from this test was outstanding).
113  // TODO(phoglund): this becomes obsolete when we switch to communicating with
114  // the DOM message queue.
115  void AssertNoAsynchronousErrors(content::WebContents* tab_contents) {
116    EXPECT_EQ("ok-no-errors",
117              ExecuteJavascript("getAnyTestFailures()", tab_contents));
118  }
119
120  // The peer connection server lets our two tabs find each other and talk to
121  // each other (e.g. it is the application-specific "signaling solution").
122  void ConnectToPeerConnectionServer(const std::string peer_name,
123                                     content::WebContents* tab_contents) {
124    std::string javascript = base::StringPrintf(
125        "connect('http://localhost:8888', '%s');", peer_name.c_str());
126    EXPECT_EQ("ok-connected", ExecuteJavascript(javascript, tab_contents));
127  }
128
129  void EstablishCall(content::WebContents* from_tab,
130                     content::WebContents* to_tab,
131                     bool enable_logging = false) {
132    std::string javascript = enable_logging ?
133        base::StringPrintf("preparePeerConnection(\"%s\")",
134            kTestLoggingSessionId) :
135        "preparePeerConnection(false)";
136    EXPECT_EQ("ok-peerconnection-created",
137              ExecuteJavascript(javascript, from_tab));
138    EXPECT_EQ("ok-added",
139              ExecuteJavascript("addLocalStream()", from_tab));
140    EXPECT_EQ("ok-negotiating",
141              ExecuteJavascript("negotiateCall()", from_tab));
142
143    // Ensure the call gets up on both sides.
144    EXPECT_TRUE(PollingWaitUntil("getPeerConnectionReadyState()",
145                                 "active", from_tab));
146    EXPECT_TRUE(PollingWaitUntil("getPeerConnectionReadyState()",
147                                 "active", to_tab));
148  }
149
150  void StartDetectingVideo(content::WebContents* tab_contents,
151                           const std::string& video_element) {
152    std::string javascript = base::StringPrintf(
153        "startDetection('%s', 'frame-buffer', 320, 240)",
154        video_element.c_str());
155    EXPECT_EQ("ok-started", ExecuteJavascript(javascript, tab_contents));
156  }
157
158  void WaitForVideoToPlay(content::WebContents* tab_contents) {
159    EXPECT_TRUE(PollingWaitUntil("isVideoPlaying()", "video-playing",
160                                 tab_contents));
161  }
162
163  void WaitForVideoToStopPlaying(content::WebContents* tab_contents) {
164    EXPECT_TRUE(PollingWaitUntil("isVideoPlaying()", "video-not-playing",
165                                 tab_contents));
166  }
167
168  void HangUp(content::WebContents* from_tab) {
169    EXPECT_EQ("ok-call-hung-up", ExecuteJavascript("hangUp()", from_tab));
170  }
171
172  void WaitUntilHangupVerified(content::WebContents* tab_contents) {
173    EXPECT_TRUE(PollingWaitUntil("getPeerConnectionReadyState()",
174                                 "no-peer-connection", tab_contents));
175  }
176
177  std::string ToggleLocalVideoTrack(content::WebContents* tab_contents) {
178    // Toggle the only video track in the page (e.g. video track 0).
179    return ExecuteJavascript("toggleLocalStream("
180        "function(local) { return local.getVideoTracks()[0]; }, "
181        "'video');", tab_contents);
182  }
183
184  std::string ToggleRemoteVideoTrack(content::WebContents* tab_contents) {
185    // Toggle the only video track in the page (e.g. video track 0).
186    return ExecuteJavascript("toggleRemoteStream("
187        "function(local) { return local.getVideoTracks()[0]; }, "
188        "'video');", tab_contents);
189  }
190
191 private:
192  PeerConnectionServerRunner peerconnection_server_;
193};
194
195IN_PROC_BROWSER_TEST_F(WebrtcBrowserTest,
196                       MANUAL_RunsAudioVideoWebRTCCallInTwoTabs) {
197  EXPECT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
198
199  ui_test_utils::NavigateToURL(
200      browser(), embedded_test_server()->GetURL(kMainWebrtcTestHtmlPage));
201  content::WebContents* left_tab =
202      browser()->tab_strip_model()->GetActiveWebContents();
203  GetUserMediaAndAccept(left_tab);
204
205  chrome::AddBlankTabAt(browser(), -1, true);
206  content::WebContents* right_tab =
207      browser()->tab_strip_model()->GetActiveWebContents();
208  ui_test_utils::NavigateToURL(
209        browser(), embedded_test_server()->GetURL(kMainWebrtcTestHtmlPage));
210  GetUserMediaAndAccept(right_tab);
211
212  ConnectToPeerConnectionServer("peer 1", left_tab);
213  ConnectToPeerConnectionServer("peer 2", right_tab);
214
215  EstablishCall(left_tab, right_tab);
216
217  AssertNoAsynchronousErrors(left_tab);
218  AssertNoAsynchronousErrors(right_tab);
219
220  StartDetectingVideo(left_tab, "remote-view");
221  StartDetectingVideo(right_tab, "remote-view");
222
223  WaitForVideoToPlay(left_tab);
224  WaitForVideoToPlay(right_tab);
225
226  HangUp(left_tab);
227  WaitUntilHangupVerified(left_tab);
228  WaitUntilHangupVerified(right_tab);
229
230  AssertNoAsynchronousErrors(left_tab);
231  AssertNoAsynchronousErrors(right_tab);
232}
233
234IN_PROC_BROWSER_TEST_F(WebrtcBrowserTest,
235                       MANUAL_TestMediaStreamTrackEnableDisable) {
236  EXPECT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
237
238  ui_test_utils::NavigateToURL(
239      browser(), embedded_test_server()->GetURL(kMainWebrtcTestHtmlPage));
240  content::WebContents* left_tab =
241      browser()->tab_strip_model()->GetActiveWebContents();
242  GetUserMediaAndAccept(left_tab);
243
244  chrome::AddBlankTabAt(browser(), -1, true);
245  content::WebContents* right_tab =
246      browser()->tab_strip_model()->GetActiveWebContents();
247  ui_test_utils::NavigateToURL(
248        browser(), embedded_test_server()->GetURL(kMainWebrtcTestHtmlPage));
249  GetUserMediaAndAccept(right_tab);
250
251  ConnectToPeerConnectionServer("peer 1", left_tab);
252  ConnectToPeerConnectionServer("peer 2", right_tab);
253
254  EstablishCall(left_tab, right_tab);
255
256  AssertNoAsynchronousErrors(left_tab);
257  AssertNoAsynchronousErrors(right_tab);
258
259  StartDetectingVideo(left_tab, "remote-view");
260  StartDetectingVideo(right_tab, "remote-view");
261
262  WaitForVideoToPlay(left_tab);
263  WaitForVideoToPlay(right_tab);
264
265  EXPECT_EQ("ok-video-toggled-to-false", ToggleLocalVideoTrack(left_tab));
266
267  WaitForVideoToStopPlaying(right_tab);
268
269  EXPECT_EQ("ok-video-toggled-to-true", ToggleLocalVideoTrack(left_tab));
270
271  WaitForVideoToPlay(right_tab);
272
273  HangUp(left_tab);
274  WaitUntilHangupVerified(left_tab);
275  WaitUntilHangupVerified(right_tab);
276
277  AssertNoAsynchronousErrors(left_tab);
278  AssertNoAsynchronousErrors(right_tab);
279}
280
281// Tests WebRTC diagnostic logging. Sets up the browser to save the multipart
282// contents to a buffer instead of uploading it, then verifies it after a call.
283// Example of multipart contents:
284// ------**--yradnuoBgoLtrapitluMklaTelgooG--**----
285// Content-Disposition: form-data; name="prod"
286//
287// Chrome_Linux
288// ------**--yradnuoBgoLtrapitluMklaTelgooG--**----
289// Content-Disposition: form-data; name="ver"
290//
291// 30.0.1554.0
292// ------**--yradnuoBgoLtrapitluMklaTelgooG--**----
293// Content-Disposition: form-data; name="guid"
294//
295// 0
296// ------**--yradnuoBgoLtrapitluMklaTelgooG--**----
297// Content-Disposition: form-data; name="type"
298//
299// webrtc_log
300// ------**--yradnuoBgoLtrapitluMklaTelgooG--**----
301// Content-Disposition: form-data; name="app_session_id"
302//
303// 0123456789abcdef
304// ------**--yradnuoBgoLtrapitluMklaTelgooG--**----
305// Content-Disposition: form-data; name="url"
306//
307// http://127.0.0.1:43213/webrtc/webrtc_jsep01_test.html
308// ------**--yradnuoBgoLtrapitluMklaTelgooG--**----
309// Content-Disposition: form-data; name="webrtc_log"; filename="webrtc_log.gz"
310// Content-Type: application/gzip
311//
312// <compressed data (zip)>
313// ------**--yradnuoBgoLtrapitluMklaTelgooG--**------
314IN_PROC_BROWSER_TEST_F(WebrtcBrowserTest,
315                       MANUAL_RunsAudioVideoWebRTCCallInTwoTabsWithLogging) {
316  EXPECT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
317
318  // Add command line switch that forces allowing log uploads.
319  CommandLine* command_line = CommandLine::ForCurrentProcess();
320  command_line->AppendSwitch(switches::kEnableMetricsReportingForTesting);
321
322  // Tell the uploader to save the multipart to a buffer instead of uploading.
323  std::string multipart;
324  g_browser_process->webrtc_log_uploader()->
325      OverrideUploadWithBufferForTesting(&multipart);
326  ui_test_utils::NavigateToURL(
327      browser(), embedded_test_server()->GetURL(kMainWebrtcTestHtmlPage));
328  content::WebContents* left_tab =
329      browser()->tab_strip_model()->GetActiveWebContents();
330  GetUserMediaAndAccept(left_tab);
331
332  chrome::AddBlankTabAt(browser(), -1, true);
333  content::WebContents* right_tab =
334      browser()->tab_strip_model()->GetActiveWebContents();
335  ui_test_utils::NavigateToURL(
336      browser(), embedded_test_server()->GetURL(kMainWebrtcTestHtmlPage));
337  GetUserMediaAndAccept(right_tab);
338
339  ConnectToPeerConnectionServer("peer 1", left_tab);
340  ConnectToPeerConnectionServer("peer 2", right_tab);
341
342  EstablishCall(left_tab, right_tab, true);
343
344  AssertNoAsynchronousErrors(left_tab);
345  AssertNoAsynchronousErrors(right_tab);
346
347  StartDetectingVideo(left_tab, "remote-view");
348  StartDetectingVideo(right_tab, "remote-view");
349
350  WaitForVideoToPlay(left_tab);
351  WaitForVideoToPlay(right_tab);
352
353  HangUp(left_tab);
354  WaitUntilHangupVerified(left_tab);
355  WaitUntilHangupVerified(right_tab);
356
357  AssertNoAsynchronousErrors(left_tab);
358  AssertNoAsynchronousErrors(right_tab);
359
360  // Uploading (or storing to our buffer in our test case) is triggered when
361  // the tab is closed. We must ensure that WebRtcLogUploader::UploadLog, which
362  // runs on the FILE thread, has finished after closing the tab before
363  // continuing.
364  // 1. Add a filter which just acts as a listener. It's added after
365  //    WebRtcLoggingHandlerHost, which is the filter that posts a task for
366  //    WebRtcLogUploader::UploadLog. So it's guarantueed to be removed after
367  //    WebRtcLoggingHandlerHost.
368  // 2. When the filter goes away post a task on the file thread to signal the
369  //    event.
370  base::WaitableEvent ipc_channel_closed(false, false);
371  left_tab->GetRenderProcessHost()->GetChannel()->AddFilter(
372      new BrowserMessageFilter(base::Bind(
373          &base::WaitableEvent::Signal,
374          base::Unretained(&ipc_channel_closed))));
375  chrome::CloseWebContents(browser(), left_tab, false);
376  ASSERT_TRUE(ipc_channel_closed.TimedWait(TestTimeouts::action_timeout()));
377
378  const char boundary[] = "------**--yradnuoBgoLtrapitluMklaTelgooG--**----";
379
380  // Remove the compressed data, it may contain "\r\n". Just verify that its
381  // size is > 0.
382  const char zip_content_type[] = "Content-Type: application/gzip";
383  size_t zip_pos = multipart.find(&zip_content_type[0]);
384  ASSERT_NE(std::string::npos, zip_pos);
385  // Move pos to where the zip begins. - 1 to remove '\0', + 4 for two "\r\n".
386  zip_pos += sizeof(zip_content_type) + 3;
387  size_t zip_length = multipart.find(boundary, zip_pos);
388  ASSERT_NE(std::string::npos, zip_length);
389  // Calculate length, adjust for a "\r\n".
390  zip_length -= zip_pos + 2;
391  ASSERT_GT(zip_length, 0u);
392  multipart.erase(zip_pos, zip_length);
393
394  // Check the multipart contents.
395  std::vector<std::string> multipart_lines;
396  base::SplitStringUsingSubstr(multipart, "\r\n", &multipart_lines);
397  ASSERT_EQ(31, static_cast<int>(multipart_lines.size()));
398
399  EXPECT_STREQ(&boundary[0], multipart_lines[0].c_str());
400  EXPECT_STREQ("Content-Disposition: form-data; name=\"prod\"",
401               multipart_lines[1].c_str());
402  EXPECT_TRUE(multipart_lines[2].empty());
403  EXPECT_NE(std::string::npos, multipart_lines[3].find("Chrome"));
404
405  EXPECT_STREQ(&boundary[0], multipart_lines[4].c_str());
406  EXPECT_STREQ("Content-Disposition: form-data; name=\"ver\"",
407               multipart_lines[5].c_str());
408  EXPECT_TRUE(multipart_lines[6].empty());
409  // Just check that the version contains a dot.
410  EXPECT_NE(std::string::npos, multipart_lines[7].find('.'));
411
412  EXPECT_STREQ(&boundary[0], multipart_lines[8].c_str());
413  EXPECT_STREQ("Content-Disposition: form-data; name=\"guid\"",
414               multipart_lines[9].c_str());
415  EXPECT_TRUE(multipart_lines[10].empty());
416  EXPECT_STREQ("0", multipart_lines[11].c_str());
417
418  EXPECT_STREQ(&boundary[0], multipart_lines[12].c_str());
419  EXPECT_STREQ("Content-Disposition: form-data; name=\"type\"",
420               multipart_lines[13].c_str());
421  EXPECT_TRUE(multipart_lines[14].empty());
422  EXPECT_STREQ("webrtc_log", multipart_lines[15].c_str());
423
424  EXPECT_STREQ(&boundary[0], multipart_lines[16].c_str());
425  EXPECT_STREQ("Content-Disposition: form-data; name=\"app_session_id\"",
426               multipart_lines[17].c_str());
427  EXPECT_TRUE(multipart_lines[18].empty());
428  EXPECT_STREQ(kTestLoggingSessionId, multipart_lines[19].c_str());
429
430  EXPECT_STREQ(&boundary[0], multipart_lines[20].c_str());
431  EXPECT_STREQ("Content-Disposition: form-data; name=\"url\"",
432               multipart_lines[21].c_str());
433  EXPECT_TRUE(multipart_lines[22].empty());
434  EXPECT_NE(std::string::npos,
435            multipart_lines[23].find(&kMainWebrtcTestHtmlPage[0]));
436
437  EXPECT_STREQ(&boundary[0], multipart_lines[24].c_str());
438  EXPECT_STREQ("Content-Disposition: form-data; name=\"webrtc_log\";"
439               " filename=\"webrtc_log.gz\"",
440               multipart_lines[25].c_str());
441  EXPECT_STREQ("Content-Type: application/gzip",
442               multipart_lines[26].c_str());
443  EXPECT_TRUE(multipart_lines[27].empty());
444  EXPECT_TRUE(multipart_lines[28].empty());  // The removed zip part.
445  std::string final_delimiter = boundary;
446  final_delimiter += "--";
447  EXPECT_STREQ(final_delimiter.c_str(), multipart_lines[29].c_str());
448  EXPECT_TRUE(multipart_lines[30].empty());
449}
450