blob: 8dfc0fa6f72d3672e7c47a969d8b0d32e277073b [file] [log] [blame]
// Copyright 2013 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/command_line.h"
#include "base/file_util.h"
#include "base/path_service.h"
#include "base/process/launch.h"
#include "base/strings/string_split.h"
#include "base/strings/stringprintf.h"
#include "base/synchronization/waitable_event.h"
#include "base/test/test_timeouts.h"
#include "base/time/time.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/chrome_notification_types.h"
#include "chrome/browser/infobars/infobar.h"
#include "chrome/browser/infobars/infobar_service.h"
#include "chrome/browser/media/media_stream_infobar_delegate.h"
#include "chrome/browser/media/webrtc_browsertest_base.h"
#include "chrome/browser/media/webrtc_browsertest_common.h"
#include "chrome/browser/media/webrtc_log_uploader.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_tabstrip.h"
#include "chrome/browser/ui/tabs/tab_strip_model.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/browser_message_filter.h"
#include "content/public/browser/notification_service.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/test/browser_test_utils.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
static const char kMainWebrtcTestHtmlPage[] =
"/webrtc/webrtc_jsep01_test.html";
static const char kTestLoggingSessionId[] = "0123456789abcdef";
// 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 WebRtcTestBase {
public:
// See comment in test where this class is used for purpose.
class BrowserMessageFilter : public content::BrowserMessageFilter {
public:
explicit BrowserMessageFilter(
const base::Closure& on_channel_closing)
: on_channel_closing_(on_channel_closing) {}
virtual void OnChannelClosing() OVERRIDE {
// Posting on the file thread ensures that the callback is run after
// WebRtcLogUploader::UploadLog has finished. See also comment in
// MANUAL_RunsAudioVideoWebRTCCallInTwoTabsWithLogging test.
content::BrowserThread::PostTask(content::BrowserThread::FILE,
FROM_HERE, on_channel_closing_);
}
virtual bool OnMessageReceived(const IPC::Message& message,
bool* message_was_ok) OVERRIDE {
return false;
}
private:
virtual ~BrowserMessageFilter() {}
base::Closure on_channel_closing_;
};
virtual void SetUp() OVERRIDE {
// TODO(phoglund): Remove this when the bots enable real GPU with the
// command line. crbug.com/271504
UseRealGLBindings();
WebRtcTestBase::SetUp();
}
virtual void SetUpInProcessBrowserTestFixture() OVERRIDE {
PeerConnectionServerRunner::KillAllPeerConnectionServersOnCurrentSystem();
peerconnection_server_.Start();
}
virtual void TearDownInProcessBrowserTestFixture() OVERRIDE {
peerconnection_server_.Stop();
}
virtual 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));
EXPECT_FALSE(command_line->HasSwitch(
switches::kUseFakeUIForMediaStream));
}
// 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;
}
// 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,
bool enable_logging = false) {
std::string javascript = enable_logging ?
base::StringPrintf("preparePeerConnection(\"%s\")",
kTestLoggingSessionId) :
"preparePeerConnection(false)";
EXPECT_EQ("ok-peerconnection-created",
ExecuteJavascript(javascript, 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(PollingWaitUntil("getPeerConnectionReadyState()",
"active", from_tab));
EXPECT_TRUE(PollingWaitUntil("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 WaitForVideoToPlay(content::WebContents* tab_contents) {
EXPECT_TRUE(PollingWaitUntil("isVideoPlaying()", "video-playing",
tab_contents));
}
void WaitForVideoToStopPlaying(content::WebContents* tab_contents) {
EXPECT_TRUE(PollingWaitUntil("isVideoPlaying()", "video-not-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(PollingWaitUntil("getPeerConnectionReadyState()",
"no-peer-connection", tab_contents));
}
std::string ToggleLocalVideoTrack(content::WebContents* tab_contents) {
// Toggle the only video track in the page (e.g. video track 0).
return ExecuteJavascript("toggleLocalStream("
"function(local) { return local.getVideoTracks()[0]; }, "
"'video');", tab_contents);
}
std::string ToggleRemoteVideoTrack(content::WebContents* tab_contents) {
// Toggle the only video track in the page (e.g. video track 0).
return ExecuteJavascript("toggleRemoteStream("
"function(local) { return local.getVideoTracks()[0]; }, "
"'video');", tab_contents);
}
private:
PeerConnectionServerRunner peerconnection_server_;
};
IN_PROC_BROWSER_TEST_F(WebrtcBrowserTest,
MANUAL_RunsAudioVideoWebRTCCallInTwoTabs) {
EXPECT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
ui_test_utils::NavigateToURL(
browser(), embedded_test_server()->GetURL(kMainWebrtcTestHtmlPage));
content::WebContents* left_tab =
browser()->tab_strip_model()->GetActiveWebContents();
GetUserMediaAndAccept(left_tab);
chrome::AddBlankTabAt(browser(), -1, true);
content::WebContents* right_tab =
browser()->tab_strip_model()->GetActiveWebContents();
ui_test_utils::NavigateToURL(
browser(), embedded_test_server()->GetURL(kMainWebrtcTestHtmlPage));
GetUserMediaAndAccept(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");
WaitForVideoToPlay(left_tab);
WaitForVideoToPlay(right_tab);
HangUp(left_tab);
WaitUntilHangupVerified(left_tab);
WaitUntilHangupVerified(right_tab);
AssertNoAsynchronousErrors(left_tab);
AssertNoAsynchronousErrors(right_tab);
}
IN_PROC_BROWSER_TEST_F(WebrtcBrowserTest,
MANUAL_TestMediaStreamTrackEnableDisable) {
EXPECT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
ui_test_utils::NavigateToURL(
browser(), embedded_test_server()->GetURL(kMainWebrtcTestHtmlPage));
content::WebContents* left_tab =
browser()->tab_strip_model()->GetActiveWebContents();
GetUserMediaAndAccept(left_tab);
chrome::AddBlankTabAt(browser(), -1, true);
content::WebContents* right_tab =
browser()->tab_strip_model()->GetActiveWebContents();
ui_test_utils::NavigateToURL(
browser(), embedded_test_server()->GetURL(kMainWebrtcTestHtmlPage));
GetUserMediaAndAccept(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");
WaitForVideoToPlay(left_tab);
WaitForVideoToPlay(right_tab);
EXPECT_EQ("ok-video-toggled-to-false", ToggleLocalVideoTrack(left_tab));
WaitForVideoToStopPlaying(right_tab);
EXPECT_EQ("ok-video-toggled-to-true", ToggleLocalVideoTrack(left_tab));
WaitForVideoToPlay(right_tab);
HangUp(left_tab);
WaitUntilHangupVerified(left_tab);
WaitUntilHangupVerified(right_tab);
AssertNoAsynchronousErrors(left_tab);
AssertNoAsynchronousErrors(right_tab);
}
// Tests WebRTC diagnostic logging. Sets up the browser to save the multipart
// contents to a buffer instead of uploading it, then verifies it after a call.
// Example of multipart contents:
// ------**--yradnuoBgoLtrapitluMklaTelgooG--**----
// Content-Disposition: form-data; name="prod"
//
// Chrome_Linux
// ------**--yradnuoBgoLtrapitluMklaTelgooG--**----
// Content-Disposition: form-data; name="ver"
//
// 30.0.1554.0
// ------**--yradnuoBgoLtrapitluMklaTelgooG--**----
// Content-Disposition: form-data; name="guid"
//
// 0
// ------**--yradnuoBgoLtrapitluMklaTelgooG--**----
// Content-Disposition: form-data; name="type"
//
// webrtc_log
// ------**--yradnuoBgoLtrapitluMklaTelgooG--**----
// Content-Disposition: form-data; name="app_session_id"
//
// 0123456789abcdef
// ------**--yradnuoBgoLtrapitluMklaTelgooG--**----
// Content-Disposition: form-data; name="url"
//
// http://127.0.0.1:43213/webrtc/webrtc_jsep01_test.html
// ------**--yradnuoBgoLtrapitluMklaTelgooG--**----
// Content-Disposition: form-data; name="webrtc_log"; filename="webrtc_log.gz"
// Content-Type: application/gzip
//
// <compressed data (zip)>
// ------**--yradnuoBgoLtrapitluMklaTelgooG--**------
IN_PROC_BROWSER_TEST_F(WebrtcBrowserTest,
MANUAL_RunsAudioVideoWebRTCCallInTwoTabsWithLogging) {
EXPECT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
// Add command line switch that forces allowing log uploads.
CommandLine* command_line = CommandLine::ForCurrentProcess();
command_line->AppendSwitch(switches::kEnableMetricsReportingForTesting);
// Tell the uploader to save the multipart to a buffer instead of uploading.
std::string multipart;
g_browser_process->webrtc_log_uploader()->
OverrideUploadWithBufferForTesting(&multipart);
ui_test_utils::NavigateToURL(
browser(), embedded_test_server()->GetURL(kMainWebrtcTestHtmlPage));
content::WebContents* left_tab =
browser()->tab_strip_model()->GetActiveWebContents();
GetUserMediaAndAccept(left_tab);
chrome::AddBlankTabAt(browser(), -1, true);
content::WebContents* right_tab =
browser()->tab_strip_model()->GetActiveWebContents();
ui_test_utils::NavigateToURL(
browser(), embedded_test_server()->GetURL(kMainWebrtcTestHtmlPage));
GetUserMediaAndAccept(right_tab);
ConnectToPeerConnectionServer("peer 1", left_tab);
ConnectToPeerConnectionServer("peer 2", right_tab);
EstablishCall(left_tab, right_tab, true);
AssertNoAsynchronousErrors(left_tab);
AssertNoAsynchronousErrors(right_tab);
StartDetectingVideo(left_tab, "remote-view");
StartDetectingVideo(right_tab, "remote-view");
WaitForVideoToPlay(left_tab);
WaitForVideoToPlay(right_tab);
HangUp(left_tab);
WaitUntilHangupVerified(left_tab);
WaitUntilHangupVerified(right_tab);
AssertNoAsynchronousErrors(left_tab);
AssertNoAsynchronousErrors(right_tab);
// Uploading (or storing to our buffer in our test case) is triggered when
// the tab is closed. We must ensure that WebRtcLogUploader::UploadLog, which
// runs on the FILE thread, has finished after closing the tab before
// continuing.
// 1. Add a filter which just acts as a listener. It's added after
// WebRtcLoggingHandlerHost, which is the filter that posts a task for
// WebRtcLogUploader::UploadLog. So it's guarantueed to be removed after
// WebRtcLoggingHandlerHost.
// 2. When the filter goes away post a task on the file thread to signal the
// event.
base::WaitableEvent ipc_channel_closed(false, false);
left_tab->GetRenderProcessHost()->GetChannel()->AddFilter(
new BrowserMessageFilter(base::Bind(
&base::WaitableEvent::Signal,
base::Unretained(&ipc_channel_closed))));
chrome::CloseWebContents(browser(), left_tab, false);
ASSERT_TRUE(ipc_channel_closed.TimedWait(TestTimeouts::action_timeout()));
const char boundary[] = "------**--yradnuoBgoLtrapitluMklaTelgooG--**----";
// Remove the compressed data, it may contain "\r\n". Just verify that its
// size is > 0.
const char zip_content_type[] = "Content-Type: application/gzip";
size_t zip_pos = multipart.find(&zip_content_type[0]);
ASSERT_NE(std::string::npos, zip_pos);
// Move pos to where the zip begins. - 1 to remove '\0', + 4 for two "\r\n".
zip_pos += sizeof(zip_content_type) + 3;
size_t zip_length = multipart.find(boundary, zip_pos);
ASSERT_NE(std::string::npos, zip_length);
// Calculate length, adjust for a "\r\n".
zip_length -= zip_pos + 2;
ASSERT_GT(zip_length, 0u);
multipart.erase(zip_pos, zip_length);
// Check the multipart contents.
std::vector<std::string> multipart_lines;
base::SplitStringUsingSubstr(multipart, "\r\n", &multipart_lines);
ASSERT_EQ(31, static_cast<int>(multipart_lines.size()));
EXPECT_STREQ(&boundary[0], multipart_lines[0].c_str());
EXPECT_STREQ("Content-Disposition: form-data; name=\"prod\"",
multipart_lines[1].c_str());
EXPECT_TRUE(multipart_lines[2].empty());
EXPECT_NE(std::string::npos, multipart_lines[3].find("Chrome"));
EXPECT_STREQ(&boundary[0], multipart_lines[4].c_str());
EXPECT_STREQ("Content-Disposition: form-data; name=\"ver\"",
multipart_lines[5].c_str());
EXPECT_TRUE(multipart_lines[6].empty());
// Just check that the version contains a dot.
EXPECT_NE(std::string::npos, multipart_lines[7].find('.'));
EXPECT_STREQ(&boundary[0], multipart_lines[8].c_str());
EXPECT_STREQ("Content-Disposition: form-data; name=\"guid\"",
multipart_lines[9].c_str());
EXPECT_TRUE(multipart_lines[10].empty());
EXPECT_STREQ("0", multipart_lines[11].c_str());
EXPECT_STREQ(&boundary[0], multipart_lines[12].c_str());
EXPECT_STREQ("Content-Disposition: form-data; name=\"type\"",
multipart_lines[13].c_str());
EXPECT_TRUE(multipart_lines[14].empty());
EXPECT_STREQ("webrtc_log", multipart_lines[15].c_str());
EXPECT_STREQ(&boundary[0], multipart_lines[16].c_str());
EXPECT_STREQ("Content-Disposition: form-data; name=\"app_session_id\"",
multipart_lines[17].c_str());
EXPECT_TRUE(multipart_lines[18].empty());
EXPECT_STREQ(kTestLoggingSessionId, multipart_lines[19].c_str());
EXPECT_STREQ(&boundary[0], multipart_lines[20].c_str());
EXPECT_STREQ("Content-Disposition: form-data; name=\"url\"",
multipart_lines[21].c_str());
EXPECT_TRUE(multipart_lines[22].empty());
EXPECT_NE(std::string::npos,
multipart_lines[23].find(&kMainWebrtcTestHtmlPage[0]));
EXPECT_STREQ(&boundary[0], multipart_lines[24].c_str());
EXPECT_STREQ("Content-Disposition: form-data; name=\"webrtc_log\";"
" filename=\"webrtc_log.gz\"",
multipart_lines[25].c_str());
EXPECT_STREQ("Content-Type: application/gzip",
multipart_lines[26].c_str());
EXPECT_TRUE(multipart_lines[27].empty());
EXPECT_TRUE(multipart_lines[28].empty()); // The removed zip part.
std::string final_delimiter = boundary;
final_delimiter += "--";
EXPECT_STREQ(final_delimiter.c_str(), multipart_lines[29].c_str());
EXPECT_TRUE(multipart_lines[30].empty());
}