// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "base/file_util.h" #include "base/path_service.h" #include "base/process_util.h" #include "base/stringprintf.h" #include "base/time.h" #include "chrome/browser/api/infobars/infobar_service.h" #include "chrome/browser/infobars/infobar.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_tabstrip.h" #include "chrome/browser/ui/media_stream_infobar_delegate.h" #include "chrome/browser/ui/tabs/tab_strip_model.h" #include "chrome/common/chrome_notification_types.h" #include "chrome/common/chrome_switches.h" #include "chrome/test/base/in_process_browser_test.h" #include "chrome/test/base/ui_test_utils.h" #include "chrome/test/ui/ui_test.h" #include "content/public/browser/notification_service.h" #include "content/public/test/browser_test_utils.h" #include "net/test/test_server.h" static const FilePath::CharType kPeerConnectionServer[] = #if defined(OS_WIN) FILE_PATH_LITERAL("peerconnection_server.exe"); #else FILE_PATH_LITERAL("peerconnection_server"); #endif static const char kMainWebrtcTestHtmlPage[] = "files/webrtc/webrtc_jsep01_test.html"; // Top-level integration test for WebRTC. Requires a real webcam and microphone // on the running system. This test is not meant to run in the main browser // test suite since normal tester machines do not have webcams. class WebrtcBrowserTest : public InProcessBrowserTest { public: WebrtcBrowserTest() : peerconnection_server_(0) {} void SetUp() OVERRIDE { RunPeerConnectionServer(); InProcessBrowserTest::SetUp(); } void TearDown() OVERRIDE { ShutdownPeerConnectionServer(); InProcessBrowserTest::TearDown(); } void SetUpCommandLine(CommandLine* command_line) OVERRIDE { // TODO(phoglund): check that user actually has the requisite devices and // print a nice message if not; otherwise the test just times out which can // be confusing. // This test expects real device handling and requires a real webcam / audio // device; it will not work with fake devices. EXPECT_FALSE(command_line->HasSwitch( switches::kUseFakeDeviceForMediaStream)); } // TODO(phoglund): This ugly poll method is only here while we transition // the test javascript to just post events when things happen. Right now they // don't because the webrtc_call.py and other tests use this polling way of // communicating when we are waiting from an asynchronous event in the // javascript. This method is meant to emulate WaitUntil in the PyAuto // framework. bool UglyPollingWaitUntil(const std::string& javascript, const std::string& evaluates_to, content::WebContents* tab_contents) { base::Time start_time = base::Time::Now(); base::TimeDelta timeout = base::TimeDelta::FromSeconds(20); std::string result; while (base::Time::Now() - start_time < timeout) { result = ExecuteJavascript(javascript, tab_contents); if (evaluates_to == result) return true; } LOG(ERROR) << "Timed out while waiting for " << javascript << " to evaluate to " << evaluates_to << "; last result was '" << result << "'"; return false; } // Convenience method which executes the provided javascript in the context // of the provided web contents and returns what it evaluated to. std::string ExecuteJavascript(const std::string& javascript, content::WebContents* tab_contents) { std::string result; EXPECT_TRUE(content::ExecuteScriptAndExtractString( tab_contents, javascript, &result)); return result; } void GetUserMedia(content::WebContents* tab_contents) { content::WindowedNotificationObserver infobar_added( chrome::NOTIFICATION_TAB_CONTENTS_INFOBAR_ADDED, content::NotificationService::AllSources()); // Request user media: this will launch the media stream info bar. EXPECT_EQ("ok-requested", ExecuteJavascript( "getUserMedia('{video: true, audio: true}');", tab_contents)); // Wait for the bar to pop up, then accept. infobar_added.Wait(); content::Details details(infobar_added.details()); MediaStreamInfoBarDelegate* media_infobar = details.ptr()->AsMediaStreamInfoBarDelegate(); media_infobar->Accept(); // Wait for WebRTC to call the success callback. EXPECT_TRUE(UglyPollingWaitUntil("obtainGetUserMediaResult();", "ok-got-stream", tab_contents)); } // Ensures we didn't get any errors asynchronously (e.g. while no javascript // call from this test was outstanding). // TODO(phoglund): this becomes obsolete when we switch to communicating with // the DOM message queue. void AssertNoAsynchronousErrors(content::WebContents* tab_contents) { EXPECT_EQ("ok-no-errors", ExecuteJavascript("getAnyTestFailures()", tab_contents)); } // The peer connection server lets our two tabs find each other and talk to // each other (e.g. it is the application-specific "signaling solution"). void ConnectToPeerConnectionServer(const std::string peer_name, content::WebContents* tab_contents) { std::string javascript = base::StringPrintf( "connect('http://localhost:8888', '%s');", peer_name.c_str()); EXPECT_EQ("ok-connected", ExecuteJavascript(javascript, tab_contents)); } void EstablishCall(content::WebContents* from_tab, content::WebContents* to_tab) { EXPECT_EQ("ok-peerconnection-created", ExecuteJavascript("preparePeerConnection()", from_tab)); EXPECT_EQ("ok-added", ExecuteJavascript("addLocalStream()", from_tab)); EXPECT_EQ("ok-negotiating", ExecuteJavascript("negotiateCall()", from_tab)); // Ensure the call gets up on both sides. EXPECT_TRUE(UglyPollingWaitUntil("getPeerConnectionReadyState()", "active", from_tab)); EXPECT_TRUE(UglyPollingWaitUntil("getPeerConnectionReadyState()", "active", to_tab)); } void StartDetectingVideo(content::WebContents* tab_contents, const std::string& video_element) { std::string javascript = base::StringPrintf( "startDetection('%s', 'frame-buffer', 320, 240)", video_element.c_str()); EXPECT_EQ("ok-started", ExecuteJavascript(javascript, tab_contents)); } void WaitForVideo(content::WebContents* tab_contents) { EXPECT_TRUE(UglyPollingWaitUntil("isVideoPlaying()", "video-playing", tab_contents)); } void HangUp(content::WebContents* from_tab) { EXPECT_EQ("ok-call-hung-up", ExecuteJavascript("hangUp()", from_tab)); } void WaitUntilHangupVerified(content::WebContents* tab_contents) { EXPECT_TRUE(UglyPollingWaitUntil("getPeerConnectionReadyState()", "no-peer-connection", tab_contents)); } private: void RunPeerConnectionServer() { FilePath peerconnection_server; EXPECT_TRUE(PathService::Get(base::DIR_EXE, &peerconnection_server)); peerconnection_server = peerconnection_server.Append(kPeerConnectionServer); EXPECT_TRUE(file_util::PathExists(peerconnection_server)) << "Missing peerconnection_server. You must build " "it so it ends up next to the browser test binary."; EXPECT_TRUE(base::LaunchProcess( CommandLine(peerconnection_server), base::LaunchOptions(), &peerconnection_server_)) << "Failed to launch peerconnection_server."; } void ShutdownPeerConnectionServer() { EXPECT_TRUE(base::KillProcess(peerconnection_server_, 0, false)) << "Failed to shut down peerconnection_server!"; } base::ProcessHandle peerconnection_server_; }; IN_PROC_BROWSER_TEST_F(WebrtcBrowserTest, MANUAL_RunsAudioVideoWebRTCCallInTwoTabs) { EXPECT_TRUE(test_server()->Start()); ui_test_utils::NavigateToURL( browser(), test_server()->GetURL(kMainWebrtcTestHtmlPage)); content::WebContents* left_tab = browser()->tab_strip_model()->GetActiveWebContents(); GetUserMedia(left_tab); chrome::AddBlankTabAt(browser(), -1, true); content::WebContents* right_tab = browser()->tab_strip_model()->GetActiveWebContents(); ui_test_utils::NavigateToURL( browser(), test_server()->GetURL(kMainWebrtcTestHtmlPage)); GetUserMedia(right_tab); ConnectToPeerConnectionServer("peer 1", left_tab); ConnectToPeerConnectionServer("peer 2", right_tab); EstablishCall(left_tab, right_tab); AssertNoAsynchronousErrors(left_tab); AssertNoAsynchronousErrors(right_tab); StartDetectingVideo(left_tab, "remote-view"); StartDetectingVideo(right_tab, "remote-view"); WaitForVideo(left_tab); WaitForVideo(right_tab); HangUp(left_tab); WaitUntilHangupVerified(left_tab); WaitUntilHangupVerified(right_tab); AssertNoAsynchronousErrors(left_tab); AssertNoAsynchronousErrors(right_tab); }