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