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/files/file_util.h"
7 #include "base/path_service.h"
8 #include "base/process/launch.h"
9 #include "base/rand_util.h"
10 #include "base/threading/thread_restrictions.h"
11 #include "build/build_config.h"
12 #include "chrome/browser/browser_process.h"
13 #include "chrome/browser/infobars/infobar_responder.h"
14 #include "chrome/browser/infobars/infobar_service.h"
15 #include "chrome/browser/media/webrtc/webrtc_browsertest_base.h"
16 #include "chrome/browser/media/webrtc/webrtc_browsertest_common.h"
17 #include "chrome/browser/ui/browser.h"
18 #include "chrome/browser/ui/browser_tabstrip.h"
19 #include "chrome/browser/ui/tabs/tab_strip_model.h"
20 #include "chrome/common/chrome_switches.h"
21 #include "chrome/test/base/ui_test_utils.h"
22 #include "components/permissions/permission_request_manager.h"
23 #include "content/public/common/content_switches.h"
24 #include "content/public/test/browser_test.h"
25 #include "content/public/test/browser_test_utils.h"
26 #include "media/base/media_switches.h"
27 #include "net/test/python_utils.h"
28 #include "ui/gl/gl_switches.h"
29
30 const char kTitlePageOfAppEngineAdminPage[] = "Instances";
31
32 const char kIsApprtcCallUpJavascript[] =
33 "var remoteVideo = document.querySelector('#remote-video');"
34 "var remoteVideoActive ="
35 " remoteVideo != null &&"
36 " remoteVideo.classList.contains('active');"
37 "window.domAutomationController.send(remoteVideoActive.toString());";
38
39 // WebRTC-AppRTC integration test. Requires a real webcam and microphone
40 // on the running system. This test is not meant to run in the main browser
41 // test suite since normal tester machines do not have webcams.
42 //
43 // This test will bring up a AppRTC instance on localhost and verify that the
44 // call gets up when connecting to the same room from two tabs in a browser.
45 class WebRtcApprtcBrowserTest : public WebRtcTestBase {
46 public:
WebRtcApprtcBrowserTest()47 WebRtcApprtcBrowserTest() {}
48
SetUpCommandLine(base::CommandLine * command_line)49 void SetUpCommandLine(base::CommandLine* command_line) override {
50 EXPECT_FALSE(command_line->HasSwitch(switches::kUseFakeUIForMediaStream));
51
52 // The video playback will not work without a GPU, so force its use here.
53 command_line->AppendSwitch(switches::kUseGpuInTests);
54 // This test fails on some Mac bots if no default devices are specified on
55 // the command line.
56 command_line->RemoveSwitch(switches::kUseFakeDeviceForMediaStream);
57 base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
58 switches::kUseFakeDeviceForMediaStream,
59 "audio-input-default-id=default,video-input-default-id=default");
60 }
61
TearDown()62 void TearDown() override {
63 // Kill any processes we may have brought up. Note: this isn't perfect,
64 // especially if the test hangs or if we're on Windows.
65 LOG(INFO) << "Entering TearDown";
66 if (dev_appserver_.IsValid())
67 dev_appserver_.Terminate(0, false);
68 if (collider_server_.IsValid())
69 collider_server_.Terminate(0, false);
70 LOG(INFO) << "Exiting TearDown";
71 }
72
73 protected:
LaunchApprtcInstanceOnLocalhost(const std::string & port)74 bool LaunchApprtcInstanceOnLocalhost(const std::string& port) {
75 base::FilePath appengine_dev_appserver = GetSourceDir().Append(
76 FILE_PATH_LITERAL("third_party/webrtc/rtc_tools/testing/browsertest/"
77 "apprtc/temp/google-cloud-sdk/bin/dev_appserver.py"));
78 if (!base::PathExists(appengine_dev_appserver)) {
79 LOG(ERROR) << "Missing appengine sdk at " <<
80 appengine_dev_appserver.value() << ".\n" <<
81 test::kAdviseOnGclientSolution;
82 return false;
83 }
84
85 base::FilePath apprtc_dir = GetSourceDir().Append(
86 FILE_PATH_LITERAL("third_party/webrtc/rtc_tools/testing/"
87 "browsertest/apprtc/out/app_engine"));
88 if (!base::PathExists(apprtc_dir)) {
89 LOG(ERROR) << "Missing AppRTC AppEngine app at " <<
90 apprtc_dir.value() << ".\n" << test::kAdviseOnGclientSolution;
91 return false;
92 }
93 if (!base::PathExists(apprtc_dir.Append(FILE_PATH_LITERAL("app.yaml")))) {
94 LOG(ERROR) << "The AppRTC AppEngine app at " << apprtc_dir.value()
95 << " appears to have not been built."
96 << "This should have been done by webrtc.DEPS scripts.";
97 return false;
98 }
99
100 base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
101 EXPECT_TRUE(GetPythonCommand(&command_line));
102
103 command_line.AppendArgPath(appengine_dev_appserver);
104 command_line.AppendArgPath(apprtc_dir);
105 command_line.AppendArg("--port=" + port);
106 command_line.AppendArg("--admin_port=9998");
107 command_line.AppendArg("--skip_sdk_update_check");
108 command_line.AppendArg("--clear_datastore=yes");
109
110 DVLOG(1) << "Running " << command_line.GetCommandLineString();
111 dev_appserver_ = base::LaunchProcess(command_line, base::LaunchOptions());
112 return dev_appserver_.IsValid();
113 }
114
LaunchColliderOnLocalHost(const std::string & apprtc_url,const std::string & collider_port)115 bool LaunchColliderOnLocalHost(const std::string& apprtc_url,
116 const std::string& collider_port) {
117 // The go workspace should be created, and collidermain built, at the
118 // runhooks stage when webrtc.DEPS/build_apprtc_collider.py runs.
119 #if defined(OS_WIN)
120 base::FilePath collider_server = GetSourceDir().Append(
121 FILE_PATH_LITERAL("third_party/webrtc/rtc_tools/testing/"
122 "browsertest/collider/collidermain.exe"));
123 #else
124 base::FilePath collider_server = GetSourceDir().Append(
125 FILE_PATH_LITERAL("third_party/webrtc/rtc_tools/testing/"
126 "browsertest/collider/collidermain"));
127 #endif
128 if (!base::PathExists(collider_server)) {
129 LOG(ERROR) << "Missing Collider server binary at " <<
130 collider_server.value() << ".\n" << test::kAdviseOnGclientSolution;
131 return false;
132 }
133
134 base::CommandLine command_line(collider_server);
135
136 command_line.AppendArg("-tls=false");
137 command_line.AppendArg("-port=" + collider_port);
138 command_line.AppendArg("-room-server=" + apprtc_url);
139
140 DVLOG(1) << "Running " << command_line.GetCommandLineString();
141 collider_server_ = base::LaunchProcess(command_line, base::LaunchOptions());
142 return collider_server_.IsValid();
143 }
144
LocalApprtcInstanceIsUp()145 bool LocalApprtcInstanceIsUp() {
146 // Load the admin page and see if we manage to load it right.
147 ui_test_utils::NavigateToURL(browser(), GURL("http://localhost:9998"));
148 content::WebContents* tab_contents =
149 browser()->tab_strip_model()->GetActiveWebContents();
150 std::string javascript =
151 "window.domAutomationController.send(document.title)";
152 std::string result;
153 if (!content::ExecuteScriptAndExtractString(tab_contents, javascript,
154 &result))
155 return false;
156
157 return result == kTitlePageOfAppEngineAdminPage;
158 }
159
WaitForCallToComeUp(content::WebContents * tab_contents)160 bool WaitForCallToComeUp(content::WebContents* tab_contents) {
161 return test::PollingWaitUntil(kIsApprtcCallUpJavascript, "true",
162 tab_contents);
163 }
164
EvalInJavascriptFile(content::WebContents * tab_contents,const base::FilePath & path)165 bool EvalInJavascriptFile(content::WebContents* tab_contents,
166 const base::FilePath& path) {
167 std::string javascript;
168 if (!ReadFileToString(path, &javascript)) {
169 LOG(ERROR) << "Missing javascript code at " << path.value() << ".";
170 return false;
171 }
172
173 if (!content::ExecuteScript(tab_contents, javascript)) {
174 LOG(ERROR) << "Failed to execute the following javascript: " <<
175 javascript;
176 return false;
177 }
178 return true;
179 }
180
DetectLocalVideoPlaying(content::WebContents * tab_contents)181 bool DetectLocalVideoPlaying(content::WebContents* tab_contents) {
182 // The remote video tag is called "local-video" in the AppRTC code.
183 return DetectVideoPlaying(tab_contents, "local-video");
184 }
185
DetectRemoteVideoPlaying(content::WebContents * tab_contents)186 bool DetectRemoteVideoPlaying(content::WebContents* tab_contents) {
187 // The remote video tag is called "remote-video" in the AppRTC code.
188 return DetectVideoPlaying(tab_contents, "remote-video");
189 }
190
DetectVideoPlaying(content::WebContents * tab_contents,const std::string & video_tag)191 bool DetectVideoPlaying(content::WebContents* tab_contents,
192 const std::string& video_tag) {
193 if (!EvalInJavascriptFile(tab_contents, GetSourceDir().Append(
194 FILE_PATH_LITERAL("chrome/test/data/webrtc/test_functions.js"))))
195 return false;
196 if (!EvalInJavascriptFile(tab_contents, GetSourceDir().Append(
197 FILE_PATH_LITERAL("chrome/test/data/webrtc/video_detector.js"))))
198 return false;
199
200 StartDetectingVideo(tab_contents, video_tag);
201 WaitForVideoToPlay(tab_contents);
202 return true;
203 }
204
GetSourceDir()205 base::FilePath GetSourceDir() {
206 base::FilePath source_dir;
207 base::PathService::Get(base::DIR_SOURCE_ROOT, &source_dir);
208 return source_dir;
209 }
210
211 private:
212 base::Process dev_appserver_;
213 base::Process collider_server_;
214 };
215
IN_PROC_BROWSER_TEST_F(WebRtcApprtcBrowserTest,MANUAL_WorksOnApprtc)216 IN_PROC_BROWSER_TEST_F(WebRtcApprtcBrowserTest, MANUAL_WorksOnApprtc) {
217 base::ScopedAllowBlockingForTesting allow_blocking;
218 DetectErrorsInJavaScript();
219 ASSERT_TRUE(LaunchApprtcInstanceOnLocalhost("9999"));
220 ASSERT_TRUE(LaunchColliderOnLocalHost("http://localhost:9999", "8089"));
221 while (!LocalApprtcInstanceIsUp())
222 DVLOG(1) << "Waiting for AppRTC to come up...";
223
224 GURL room_url = GURL("http://localhost:9999/r/some_room"
225 "?wshpp=localhost:8089&wstls=false");
226
227 // Set up the left tab.
228 chrome::AddTabAt(browser(), GURL(), -1, true);
229 content::WebContents* left_tab =
230 browser()->tab_strip_model()->GetActiveWebContents();
231 permissions::PermissionRequestManager::FromWebContents(left_tab)
232 ->set_auto_response_for_test(
233 permissions::PermissionRequestManager::ACCEPT_ALL);
234 InfoBarResponder left_infobar_responder(
235 InfoBarService::FromWebContents(left_tab), InfoBarResponder::ACCEPT);
236 ui_test_utils::NavigateToURL(browser(), room_url);
237
238 // Wait for the local video to start playing. This is needed, because opening
239 // a new tab too quickly, by sending the current tab to the background, can
240 // lead to the request for starting the video capture in the current tab to
241 // not get sent before it comes back to the foreground (which in this test
242 // case is never).
243 ASSERT_TRUE(DetectLocalVideoPlaying(left_tab));
244
245 // Set up the right tab.
246 chrome::AddTabAt(browser(), GURL(), -1, true);
247 content::WebContents* right_tab =
248 browser()->tab_strip_model()->GetActiveWebContents();
249 permissions::PermissionRequestManager::FromWebContents(right_tab)
250 ->set_auto_response_for_test(
251 permissions::PermissionRequestManager::ACCEPT_ALL);
252 InfoBarResponder right_infobar_responder(
253 InfoBarService::FromWebContents(right_tab), InfoBarResponder::ACCEPT);
254 ui_test_utils::NavigateToURL(browser(), room_url);
255
256 ASSERT_TRUE(WaitForCallToComeUp(left_tab));
257 ASSERT_TRUE(WaitForCallToComeUp(right_tab));
258
259 ASSERT_TRUE(DetectRemoteVideoPlaying(left_tab));
260 ASSERT_TRUE(DetectRemoteVideoPlaying(right_tab));
261
262 chrome::CloseWebContents(browser(), left_tab, false);
263 chrome::CloseWebContents(browser(), right_tab, false);
264 }
265