Home | History | Annotate | Download | only in media
      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 "chrome/browser/media/webrtc_browsertest_base.h"
      6 
      7 #include "base/lazy_instance.h"
      8 #include "base/strings/string_util.h"
      9 #include "base/strings/stringprintf.h"
     10 #include "chrome/browser/chrome_notification_types.h"
     11 #include "chrome/browser/infobars/infobar_service.h"
     12 #include "chrome/browser/media/media_stream_infobar_delegate.h"
     13 #include "chrome/browser/media/webrtc_browsertest_common.h"
     14 #include "chrome/browser/ui/browser.h"
     15 #include "chrome/browser/ui/browser_tabstrip.h"
     16 #include "chrome/browser/ui/tabs/tab_strip_model.h"
     17 #include "chrome/test/base/ui_test_utils.h"
     18 #include "components/infobars/core/infobar.h"
     19 #include "content/public/browser/notification_service.h"
     20 #include "content/public/test/browser_test_utils.h"
     21 #include "net/test/embedded_test_server/embedded_test_server.h"
     22 
     23 #if defined(OS_WIN)
     24 // For fine-grained suppression.
     25 #include "base/win/windows_version.h"
     26 #endif
     27 
     28 const char WebRtcTestBase::kAudioVideoCallConstraints[] =
     29     "{audio: true, video: true}";
     30 const char WebRtcTestBase::kAudioVideoCallConstraintsQVGA[] =
     31    "{audio: true, video: {mandatory: {minWidth: 320, maxWidth: 320, "
     32    " minHeight: 240, maxHeight: 240}}}";
     33 const char WebRtcTestBase::kAudioVideoCallConstraints360p[] =
     34    "{audio: true, video: {mandatory: {minWidth: 640, maxWidth: 640, "
     35    " minHeight: 360, maxHeight: 360}}}";
     36 const char WebRtcTestBase::kAudioVideoCallConstraintsVGA[] =
     37    "{audio: true, video: {mandatory: {minWidth: 640, maxWidth: 640, "
     38    " minHeight: 480, maxHeight: 480}}}";
     39 const char WebRtcTestBase::kAudioVideoCallConstraints720p[] =
     40    "{audio: true, video: {mandatory: {minWidth: 1280, maxWidth: 1280, "
     41    " minHeight: 720, maxHeight: 720}}}";
     42 const char WebRtcTestBase::kAudioVideoCallConstraints1080p[] =
     43     "{audio: true, video: {mandatory: {minWidth: 1920, maxWidth: 1920, "
     44     " minHeight: 1080, maxHeight: 1080}}}";
     45 const char WebRtcTestBase::kAudioOnlyCallConstraints[] = "{audio: true}";
     46 const char WebRtcTestBase::kVideoOnlyCallConstraints[] = "{video: true}";
     47 const char WebRtcTestBase::kFailedWithPermissionDeniedError[] =
     48     "failed-with-error-PermissionDeniedError";
     49 const char WebRtcTestBase::kFailedWithPermissionDismissedError[] =
     50     "failed-with-error-PermissionDismissedError";
     51 
     52 namespace {
     53 
     54 base::LazyInstance<bool> hit_javascript_errors_ =
     55       LAZY_INSTANCE_INITIALIZER;
     56 
     57 // Intercepts all log messages. We always attach this handler but only look at
     58 // the results if the test requests so. Note that this will only work if the
     59 // WebrtcTestBase-inheriting test cases do not run in parallel (if they did they
     60 // would race to look at the log, which is global to all tests).
     61 bool JavascriptErrorDetectingLogHandler(int severity,
     62                                         const char* file,
     63                                         int line,
     64                                         size_t message_start,
     65                                         const std::string& str) {
     66   if (file == NULL || std::string("CONSOLE") != file)
     67     return false;
     68 
     69   bool contains_uncaught = str.find("\"Uncaught ") != std::string::npos;
     70   if (severity == logging::LOG_ERROR ||
     71       (severity == logging::LOG_INFO && contains_uncaught)) {
     72     hit_javascript_errors_.Get() = true;
     73   }
     74 
     75   return false;
     76 }
     77 
     78 }  // namespace
     79 
     80 WebRtcTestBase::WebRtcTestBase(): detect_errors_in_javascript_(false) {
     81   // The handler gets set for each test method, but that's fine since this
     82   // set operation is idempotent.
     83   logging::SetLogMessageHandler(&JavascriptErrorDetectingLogHandler);
     84   hit_javascript_errors_.Get() = false;
     85 
     86   EnablePixelOutput();
     87 }
     88 
     89 WebRtcTestBase::~WebRtcTestBase() {
     90   if (detect_errors_in_javascript_) {
     91     EXPECT_FALSE(hit_javascript_errors_.Get())
     92         << "Encountered javascript errors during test execution (Search "
     93         << "for Uncaught or ERROR:CONSOLE in the test output).";
     94   }
     95 }
     96 
     97 void WebRtcTestBase::GetUserMediaAndAccept(
     98     content::WebContents* tab_contents) const {
     99   GetUserMediaWithSpecificConstraintsAndAccept(tab_contents,
    100                                                kAudioVideoCallConstraints);
    101 }
    102 
    103 void WebRtcTestBase::GetUserMediaWithSpecificConstraintsAndAccept(
    104     content::WebContents* tab_contents,
    105     const std::string& constraints) const {
    106   infobars::InfoBar* infobar =
    107       GetUserMediaAndWaitForInfoBar(tab_contents, constraints);
    108   infobar->delegate()->AsConfirmInfoBarDelegate()->Accept();
    109   CloseInfoBarInTab(tab_contents, infobar);
    110 
    111   // Wait for WebRTC to call the success callback.
    112   const char kOkGotStream[] = "ok-got-stream";
    113   EXPECT_TRUE(test::PollingWaitUntil("obtainGetUserMediaResult()", kOkGotStream,
    114                                      tab_contents));
    115 }
    116 
    117 void WebRtcTestBase::GetUserMediaAndDeny(content::WebContents* tab_contents) {
    118   return GetUserMediaWithSpecificConstraintsAndDeny(tab_contents,
    119                                                     kAudioVideoCallConstraints);
    120 }
    121 
    122 void WebRtcTestBase::GetUserMediaWithSpecificConstraintsAndDeny(
    123     content::WebContents* tab_contents,
    124     const std::string& constraints) const {
    125   infobars::InfoBar* infobar =
    126       GetUserMediaAndWaitForInfoBar(tab_contents, constraints);
    127   infobar->delegate()->AsConfirmInfoBarDelegate()->Cancel();
    128   CloseInfoBarInTab(tab_contents, infobar);
    129 
    130   // Wait for WebRTC to call the fail callback.
    131   EXPECT_TRUE(test::PollingWaitUntil("obtainGetUserMediaResult()",
    132                                      kFailedWithPermissionDeniedError,
    133                                      tab_contents));
    134 }
    135 
    136 void WebRtcTestBase::GetUserMediaAndDismiss(
    137     content::WebContents* tab_contents) const {
    138   infobars::InfoBar* infobar =
    139       GetUserMediaAndWaitForInfoBar(tab_contents, kAudioVideoCallConstraints);
    140   infobar->delegate()->InfoBarDismissed();
    141   CloseInfoBarInTab(tab_contents, infobar);
    142 
    143   // A dismiss should be treated like a deny.
    144   EXPECT_TRUE(test::PollingWaitUntil("obtainGetUserMediaResult()",
    145                                      kFailedWithPermissionDismissedError,
    146                                      tab_contents));
    147 }
    148 
    149 void WebRtcTestBase::GetUserMedia(content::WebContents* tab_contents,
    150                                   const std::string& constraints) const {
    151   // Request user media: this will launch the media stream info bar.
    152   std::string result;
    153   EXPECT_TRUE(content::ExecuteScriptAndExtractString(
    154       tab_contents, "doGetUserMedia(" + constraints + ");", &result));
    155   EXPECT_EQ("ok-requested", result);
    156 }
    157 
    158 infobars::InfoBar* WebRtcTestBase::GetUserMediaAndWaitForInfoBar(
    159     content::WebContents* tab_contents,
    160     const std::string& constraints) const {
    161   content::WindowedNotificationObserver infobar_added(
    162       chrome::NOTIFICATION_TAB_CONTENTS_INFOBAR_ADDED,
    163       content::NotificationService::AllSources());
    164 
    165   // Request user media: this will launch the media stream info bar.
    166   GetUserMedia(tab_contents, constraints);
    167 
    168   // Wait for the bar to pop up, then return it.
    169   infobar_added.Wait();
    170   content::Details<infobars::InfoBar::AddedDetails> details(
    171       infobar_added.details());
    172   EXPECT_TRUE(details->delegate()->AsMediaStreamInfoBarDelegate());
    173   return details.ptr();
    174 }
    175 
    176 content::WebContents* WebRtcTestBase::OpenPageAndGetUserMediaInNewTab(
    177     const GURL& url) const {
    178   return OpenPageAndGetUserMediaInNewTabWithConstraints(
    179       url, kAudioVideoCallConstraints);
    180 }
    181 
    182 content::WebContents*
    183 WebRtcTestBase::OpenPageAndGetUserMediaInNewTabWithConstraints(
    184     const GURL& url,
    185     const std::string& constraints) const {
    186   chrome::AddTabAt(browser(), GURL(), -1, true);
    187   ui_test_utils::NavigateToURL(browser(), url);
    188 #if defined (OS_LINUX)
    189   // Load the page again on Linux to work around crbug.com/281268.
    190   ui_test_utils::NavigateToURL(browser(), url);
    191 #endif
    192   content::WebContents* new_tab =
    193       browser()->tab_strip_model()->GetActiveWebContents();
    194   GetUserMediaWithSpecificConstraintsAndAccept(new_tab, constraints);
    195   return new_tab;
    196 }
    197 
    198 content::WebContents* WebRtcTestBase::OpenTestPageAndGetUserMediaInNewTab(
    199     const std::string& test_page) const {
    200   return OpenPageAndGetUserMediaInNewTab(
    201       embedded_test_server()->GetURL(test_page));
    202 }
    203 
    204 content::WebContents* WebRtcTestBase::OpenPageAndAcceptUserMedia(
    205     const GURL& url) const {
    206   content::WindowedNotificationObserver infobar_added(
    207       chrome::NOTIFICATION_TAB_CONTENTS_INFOBAR_ADDED,
    208       content::NotificationService::AllSources());
    209 
    210   ui_test_utils::NavigateToURL(browser(), url);
    211 
    212   infobar_added.Wait();
    213 
    214   content::WebContents* tab_contents =
    215       browser()->tab_strip_model()->GetActiveWebContents();
    216   content::Details<infobars::InfoBar::AddedDetails> details(
    217       infobar_added.details());
    218   infobars::InfoBar* infobar = details.ptr();
    219   EXPECT_TRUE(infobar);
    220   infobar->delegate()->AsMediaStreamInfoBarDelegate()->Accept();
    221 
    222   CloseInfoBarInTab(tab_contents, infobar);
    223   return tab_contents;
    224 }
    225 
    226 void WebRtcTestBase::CloseInfoBarInTab(content::WebContents* tab_contents,
    227                                        infobars::InfoBar* infobar) const {
    228   content::WindowedNotificationObserver infobar_removed(
    229       chrome::NOTIFICATION_TAB_CONTENTS_INFOBAR_REMOVED,
    230       content::NotificationService::AllSources());
    231 
    232   InfoBarService* infobar_service =
    233       InfoBarService::FromWebContents(tab_contents);
    234   infobar_service->RemoveInfoBar(infobar);
    235 
    236   infobar_removed.Wait();
    237 }
    238 
    239 void WebRtcTestBase::CloseLastLocalStream(
    240     content::WebContents* tab_contents) const {
    241   EXPECT_EQ("ok-stopped",
    242             ExecuteJavascript("stopLocalStream();", tab_contents));
    243 }
    244 
    245 // Convenience method which executes the provided javascript in the context
    246 // of the provided web contents and returns what it evaluated to.
    247 std::string WebRtcTestBase::ExecuteJavascript(
    248     const std::string& javascript,
    249     content::WebContents* tab_contents) const {
    250   std::string result;
    251   EXPECT_TRUE(content::ExecuteScriptAndExtractString(
    252       tab_contents, javascript, &result));
    253   return result;
    254 }
    255 
    256 void WebRtcTestBase::SetupPeerconnectionWithLocalStream(
    257     content::WebContents* tab) const {
    258   EXPECT_EQ("ok-peerconnection-created",
    259             ExecuteJavascript("preparePeerConnection()", tab));
    260   EXPECT_EQ("ok-added", ExecuteJavascript("addLocalStream()", tab));
    261 }
    262 
    263 std::string WebRtcTestBase::CreateLocalOffer(
    264       content::WebContents* from_tab) const {
    265   std::string response = ExecuteJavascript("createLocalOffer({})", from_tab);
    266   EXPECT_EQ("ok-", response.substr(0, 3)) << "Failed to create local offer: "
    267       << response;
    268 
    269   std::string local_offer = response.substr(3);
    270   return local_offer;
    271 }
    272 
    273 std::string WebRtcTestBase::CreateAnswer(std::string local_offer,
    274                                          content::WebContents* to_tab) const {
    275   std::string javascript =
    276       base::StringPrintf("receiveOfferFromPeer('%s', {})", local_offer.c_str());
    277   std::string response = ExecuteJavascript(javascript, to_tab);
    278   EXPECT_EQ("ok-", response.substr(0, 3))
    279       << "Receiving peer failed to receive offer and create answer: "
    280       << response;
    281 
    282   std::string answer = response.substr(3);
    283   return answer;
    284 }
    285 
    286 void WebRtcTestBase::ReceiveAnswer(std::string answer,
    287                                    content::WebContents* from_tab) const {
    288   ASSERT_EQ(
    289       "ok-accepted-answer",
    290       ExecuteJavascript(
    291           base::StringPrintf("receiveAnswerFromPeer('%s')", answer.c_str()),
    292           from_tab));
    293 }
    294 
    295 void WebRtcTestBase::GatherAndSendIceCandidates(
    296     content::WebContents* from_tab,
    297     content::WebContents* to_tab) const {
    298   std::string ice_candidates =
    299       ExecuteJavascript("getAllIceCandidates()", from_tab);
    300 
    301   EXPECT_EQ("ok-received-candidates", ExecuteJavascript(
    302       base::StringPrintf("receiveIceCandidates('%s')", ice_candidates.c_str()),
    303       to_tab));
    304 }
    305 
    306 void WebRtcTestBase::NegotiateCall(content::WebContents* from_tab,
    307                                    content::WebContents* to_tab) const {
    308   std::string local_offer = CreateLocalOffer(from_tab);
    309   std::string answer = CreateAnswer(local_offer, to_tab);
    310   ReceiveAnswer(answer, from_tab);
    311 
    312   // Send all ICE candidates (wait for gathering to finish if necessary).
    313   GatherAndSendIceCandidates(to_tab, from_tab);
    314   GatherAndSendIceCandidates(from_tab, to_tab);
    315 }
    316 
    317 void WebRtcTestBase::HangUp(content::WebContents* from_tab) const {
    318   EXPECT_EQ("ok-call-hung-up", ExecuteJavascript("hangUp()", from_tab));
    319 }
    320 
    321 void WebRtcTestBase::DetectErrorsInJavaScript() {
    322   detect_errors_in_javascript_ = true;
    323 }
    324 
    325 void WebRtcTestBase::StartDetectingVideo(
    326     content::WebContents* tab_contents,
    327     const std::string& video_element) const {
    328   std::string javascript = base::StringPrintf(
    329       "startDetection('%s', 320, 240)", video_element.c_str());
    330   EXPECT_EQ("ok-started", ExecuteJavascript(javascript, tab_contents));
    331 }
    332 
    333 void WebRtcTestBase::WaitForVideoToPlay(
    334     content::WebContents* tab_contents) const {
    335   EXPECT_TRUE(test::PollingWaitUntil("isVideoPlaying()", "video-playing",
    336                                      tab_contents));
    337 }
    338 
    339 std::string WebRtcTestBase::GetStreamSize(
    340     content::WebContents* tab_contents,
    341     const std::string& video_element) const {
    342   std::string javascript =
    343       base::StringPrintf("getStreamSize('%s')", video_element.c_str());
    344   std::string result = ExecuteJavascript(javascript, tab_contents);
    345   EXPECT_TRUE(StartsWithASCII(result, "ok-", true));
    346   return result.substr(3);
    347 }
    348 
    349 bool WebRtcTestBase::HasWebcamAvailableOnSystem(
    350     content::WebContents* tab_contents) const {
    351   std::string result =
    352       ExecuteJavascript("hasVideoInputDeviceOnSystem();", tab_contents);
    353   return result == "has-video-input-device";
    354 }
    355 
    356 bool WebRtcTestBase::OnWinXp() const {
    357 #if defined(OS_WIN)
    358   return base::win::GetVersion() <= base::win::VERSION_XP;
    359 #else
    360   return false;
    361 #endif
    362 }
    363 
    364 bool WebRtcTestBase::OnWin8() const {
    365 #if defined(OS_WIN)
    366   return base::win::GetVersion() > base::win::VERSION_WIN7;
    367 #else
    368   return false;
    369 #endif
    370 }
    371