Home | History | Annotate | Download | only in test
      1 // Copyright (c) 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/path_service.h"
      7 #include "chrome/browser/chrome_notification_types.h"
      8 #include "chrome/browser/extensions/extension_browsertest.h"
      9 #include "chrome/browser/extensions/extension_service.h"
     10 #include "chrome/browser/extensions/unpacked_installer.h"
     11 #include "chrome/browser/profiles/profile.h"
     12 #include "chrome/browser/ui/tabs/tab_strip_model.h"
     13 #include "chrome/common/chrome_paths.h"
     14 #include "chrome/common/chrome_switches.cc"
     15 #include "chrome/common/extensions/extension.h"
     16 #include "chrome/common/extensions/extension_file_util.h"
     17 #include "chrome/common/extensions/manifest.h"
     18 #include "chrome/common/pref_names.h"
     19 #include "chrome/test/base/in_process_browser_test.h"
     20 #include "chrome/test/base/ui_test_utils.h"
     21 #include "content/public/browser/navigation_controller.h"
     22 #include "content/public/browser/navigation_entry.h"
     23 #include "content/public/browser/notification_service.h"
     24 #include "content/public/browser/render_view_host.h"
     25 #include "content/public/browser/web_contents.h"
     26 #include "content/public/test/browser_test_utils.h"
     27 #include "content/public/test/test_utils.h"
     28 #include "net/dns/mock_host_resolver.h"
     29 
     30 using extensions::Extension;
     31 
     32 namespace {
     33 // Command line arguments specific to the chromoting browser tests.
     34 const char kOverrideUserDataDir[] = "override-user-data-dir";
     35 const char kNoCleanup[] = "no-cleanup";
     36 const char kNoInstall[] = "no-install";
     37 const char kWebAppCrx[] = "webapp-crx";
     38 const char kUsername[] = "username";
     39 const char kkPassword[] = "password";
     40 
     41 // ASSERT_TRUE can only be used in void returning functions.
     42 void _ASSERT_TRUE(bool condition) {
     43   ASSERT_TRUE(condition);
     44   return;
     45 }
     46 
     47 }
     48 
     49 namespace remoting {
     50 
     51 class RemoteDesktopBrowserTest : public ExtensionBrowserTest {
     52  public:
     53   virtual void SetUp() OVERRIDE {
     54     ParseCommandLine();
     55     ExtensionBrowserTest::SetUp();
     56   }
     57 
     58  protected:
     59   // Override InProcessBrowserTest. Change behavior of the default host
     60   // resolver to avoid DNS lookup errors, so we can make network calls.
     61   virtual void SetUpInProcessBrowserTestFixture() OVERRIDE {
     62     // The resolver object lifetime is managed by sync_test_setup, not here.
     63     EnableDNSLookupForThisTest(
     64         new net::RuleBasedHostResolverProc(host_resolver()));
     65   }
     66 
     67   // Override InProcessBrowserTest.
     68   virtual void TearDownInProcessBrowserTestFixture() OVERRIDE {
     69     DisableDNSLookupForThisTest();
     70   }
     71 
     72   // Install the chromoting extension from a crx file.
     73   void InstallChromotingApp();
     74 
     75   // Uninstall the chromoting extension.
     76   void UninstallChromotingApp();
     77 
     78   // Test whether the chromoting extension is installed.
     79   void VerifyChromotingLoaded(bool expected);
     80 
     81   // Launch the chromoting app.
     82   void LaunchChromotingApp();
     83 
     84   // Verify the test has access to the internet (specifically google.com)
     85   void VerifyInternetAccess();
     86 
     87   void Authorize();
     88 
     89   void Authenticate();
     90 
     91   // Whether to perform the cleanup tasks (uninstalling chromoting, etc).
     92   // This is useful for diagnostic purposes.
     93   bool NoCleanup() { return no_cleanup_; }
     94 
     95   // Whether to install the chromoting extension before running the test cases.
     96   // This is useful for diagnostic purposes.
     97   bool NoInstall() { return no_install_; }
     98 
     99  private:
    100   void ParseCommandLine();
    101 
    102   // Change behavior of the default host resolver to allow DNS lookup
    103   // to proceed instead of being blocked by the test infrastructure.
    104   void EnableDNSLookupForThisTest(
    105     net::RuleBasedHostResolverProc* host_resolver);
    106 
    107   // We need to reset the DNS lookup when we finish, or the test will fail.
    108   void DisableDNSLookupForThisTest();
    109 
    110   // Helper to get the path to the crx file of the webapp to be tested.
    111   base::FilePath WebAppCrxPath() { return webapp_crx_; }
    112 
    113   // Helper to get the extension ID of the installed chromoting webapp.
    114   std::string ChromotingID() { return chromoting_id_; }
    115 
    116   // Helper to retrieve the current URL of the active tab in the browser.
    117   GURL GetCurrentURL() {
    118     return browser()->tab_strip_model()->GetActiveWebContents()->GetURL();
    119   }
    120 
    121   // Helper to execute a javascript code snippet on the current page.
    122   void ExecuteScript(const std::string& script) {
    123     ASSERT_TRUE(content::ExecuteScript(
    124         browser()->tab_strip_model()->GetActiveWebContents(), script));
    125   }
    126 
    127   // Helper to execute a javascript code snippet on the current page and
    128   // wait for page load to complete.
    129   void ExecuteScriptAndWait(const std::string& script) {
    130     content::WindowedNotificationObserver observer(
    131         content::NOTIFICATION_LOAD_STOP,
    132         content::Source<content::NavigationController>(
    133             &browser()->tab_strip_model()->GetActiveWebContents()->
    134                 GetController()));
    135 
    136     ExecuteScript(script);
    137 
    138     observer.Wait();
    139   }
    140 
    141   // Helper to execute a javascript code snippet on the current page and
    142   // extract the boolean result.
    143   bool ExecuteScriptAndExtractBool(const std::string& script) {
    144     bool result;
    145     // Using a private assert function because ASSERT_TRUE can only be used in
    146     // void returning functions.
    147     _ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
    148         browser()->tab_strip_model()->GetActiveWebContents(),
    149         "window.domAutomationController.send(" + script + ");",
    150         &result));
    151 
    152     return result;
    153   }
    154 
    155   // Helper to check whether a html element with the given name exists on
    156   // the current page.
    157   bool HtmlElementExists(const std::string& name) {
    158     return ExecuteScriptAndExtractBool(
    159         "document.getElementById(\"" + name + "\") != null");
    160   }
    161 
    162   // Helper to navigate to a given url.
    163   void NavigateToURLAndWait(const GURL& url) {
    164     content::WindowedNotificationObserver observer(
    165         content::NOTIFICATION_LOAD_STOP,
    166         content::Source<content::NavigationController>(
    167             &browser()->tab_strip_model()->GetActiveWebContents()->
    168                 GetController()));
    169 
    170     ui_test_utils::NavigateToURL(browser(), url);
    171     observer.Wait();
    172   }
    173 
    174   // This test needs to make live DNS requests for access to
    175   // GAIA and sync server URLs under google.com. We use a scoped version
    176   // to override the default resolver while the test is active.
    177   scoped_ptr<net::ScopedDefaultHostResolverProc> mock_host_resolver_override_;
    178 
    179   bool no_cleanup_;
    180   bool no_install_;
    181   std::string chromoting_id_;
    182   base::FilePath webapp_crx_;
    183   std::string username_;
    184   std::string password_;
    185 };
    186 
    187 void RemoteDesktopBrowserTest::ParseCommandLine() {
    188   CommandLine* command_line = CommandLine::ForCurrentProcess();
    189 
    190   // The test framework overrides any command line user-data-dir
    191   // argument with a /tmp/.org.chromium.Chromium.XXXXXX directory.
    192   // That happens in the ChromeTestLauncherDelegate, and affects
    193   // all unit tests (no opt out available). It intentionally erases
    194   // any --user-data-dir switch if present and appends a new one.
    195   // Re-override the default data dir if override-user-data-dir
    196   // is specified.
    197   if (command_line->HasSwitch(kOverrideUserDataDir)) {
    198     const base::FilePath& override_user_data_dir =
    199         command_line->GetSwitchValuePath(kOverrideUserDataDir);
    200 
    201     ASSERT_FALSE(override_user_data_dir.empty());
    202 
    203     command_line->AppendSwitchPath(switches::kUserDataDir,
    204                                    override_user_data_dir);
    205   }
    206 
    207   username_ = command_line->GetSwitchValueASCII(kUsername);
    208   password_ = command_line->GetSwitchValueASCII(kkPassword);
    209 
    210   no_cleanup_ = command_line->HasSwitch(kNoCleanup);
    211   no_install_ = command_line->HasSwitch(kNoInstall);
    212 
    213   if (!no_install_) {
    214     webapp_crx_ = command_line->GetSwitchValuePath(kWebAppCrx);
    215     ASSERT_FALSE(webapp_crx_.empty());
    216   }
    217 }
    218 
    219 void RemoteDesktopBrowserTest::EnableDNSLookupForThisTest(
    220     net::RuleBasedHostResolverProc* host_resolver) {
    221   // mock_host_resolver_override_ takes ownership of the resolver.
    222   scoped_refptr<net::RuleBasedHostResolverProc> resolver =
    223       new net::RuleBasedHostResolverProc(host_resolver);
    224   resolver->AllowDirectLookup("*.google.com");
    225   // On Linux, we use Chromium's NSS implementation which uses the following
    226   // hosts for certificate verification. Without these overrides, running the
    227   // integration tests on Linux causes errors as we make external DNS lookups.
    228   resolver->AllowDirectLookup("*.thawte.com");
    229   resolver->AllowDirectLookup("*.geotrust.com");
    230   resolver->AllowDirectLookup("*.gstatic.com");
    231   resolver->AllowDirectLookup("*.googleapis.com");
    232   mock_host_resolver_override_.reset(
    233       new net::ScopedDefaultHostResolverProc(resolver.get()));
    234 }
    235 
    236 void RemoteDesktopBrowserTest::DisableDNSLookupForThisTest() {
    237   mock_host_resolver_override_.reset();
    238 }
    239 
    240 void RemoteDesktopBrowserTest::VerifyInternetAccess() {
    241   GURL google_url("http://www.google.com");
    242   NavigateToURLAndWait(google_url);
    243 
    244   EXPECT_EQ(GetCurrentURL().host(), "www.google.com");
    245 }
    246 
    247 void RemoteDesktopBrowserTest::InstallChromotingApp() {
    248   base::FilePath install_dir(WebAppCrxPath());
    249   scoped_refptr<const Extension> extension(InstallExtensionWithUIAutoConfirm(
    250       install_dir, 1, browser()));
    251 
    252   EXPECT_FALSE(extension.get() == NULL);
    253 }
    254 
    255 void RemoteDesktopBrowserTest::UninstallChromotingApp() {
    256   UninstallExtension(ChromotingID());
    257   chromoting_id_.clear();
    258 }
    259 
    260 void RemoteDesktopBrowserTest::LaunchChromotingApp() {
    261   ASSERT_FALSE(ChromotingID().empty());
    262 
    263   std::string url = "chrome-extension://" + ChromotingID() + "/main.html";
    264   const GURL chromoting_main(url);
    265   NavigateToURLAndWait(chromoting_main);
    266 
    267   EXPECT_EQ(GetCurrentURL(), chromoting_main);
    268 }
    269 
    270 void RemoteDesktopBrowserTest::VerifyChromotingLoaded(bool expected) {
    271   const ExtensionSet* extensions = extension_service()->extensions();
    272   scoped_refptr<const extensions::Extension> extension;
    273   ExtensionSet::const_iterator iter;
    274   bool installed = false;
    275 
    276   for (iter = extensions->begin(); iter != extensions->end(); ++iter) {
    277     extension = *iter;
    278     // Is there a better way to recognize the chromoting extension
    279     // than name comparison?
    280     if (extension->name() == "Chromoting" ||
    281         extension->name() == "Chrome Remote Desktop") {
    282       installed = true;
    283       break;
    284     }
    285   }
    286 
    287   if (installed) {
    288     chromoting_id_ = extension->id();
    289 
    290     EXPECT_EQ(extension->GetType(),
    291         extensions::Manifest::TYPE_LEGACY_PACKAGED_APP);
    292 
    293     EXPECT_TRUE(extension->ShouldDisplayInAppLauncher());
    294   }
    295 
    296   EXPECT_EQ(installed, expected);
    297 }
    298 
    299 void RemoteDesktopBrowserTest::Authorize() {
    300   // The chromoting extension should be installed.
    301   ASSERT_FALSE(ChromotingID().empty());
    302 
    303   // The chromoting main page should be loaded in the current tab
    304   // and isAuthenticated() should be false (auth dialog visible).
    305   std::string url = "chrome-extension://" + ChromotingID() + "/main.html";
    306   ASSERT_EQ(GetCurrentURL().spec(), url);
    307   ASSERT_FALSE(ExecuteScriptAndExtractBool(
    308       "remoting.OAuth2.prototype.isAuthenticated()"));
    309 
    310   ExecuteScriptAndWait("remoting.OAuth2.prototype.doAuthRedirect();");
    311 
    312   // Verify the active tab is at the "Google Accounts" login page.
    313   EXPECT_EQ(GetCurrentURL().host(), "accounts.google.com");
    314   EXPECT_TRUE(HtmlElementExists("Email"));
    315   EXPECT_TRUE(HtmlElementExists("Passwd"));
    316 }
    317 
    318 void RemoteDesktopBrowserTest::Authenticate() {
    319   // The chromoting extension should be installed.
    320   ASSERT_FALSE(ChromotingID().empty());
    321 
    322   // The active tab should have the "Google Accounts" login page loaded.
    323   ASSERT_EQ(GetCurrentURL().host(), "accounts.google.com");
    324   ASSERT_TRUE(HtmlElementExists("Email"));
    325   ASSERT_TRUE(HtmlElementExists("Passwd"));
    326 
    327   // Now log in using the username and password passed in from the command line.
    328   ExecuteScriptAndWait(
    329       "document.getElementById(\"Email\").value = \"" + username_ + "\";" +
    330       "document.getElementById(\"Passwd\").value = \"" + password_ +"\";" +
    331       "document.forms[\"gaia_loginform\"].submit();");
    332 
    333   EXPECT_EQ(GetCurrentURL().host(), "accounts.google.com");
    334 
    335   // TODO: Is there a better way to verify we are on the
    336   // "Request for Permission" page?
    337   EXPECT_TRUE(HtmlElementExists("submit_approve_access"));
    338 }
    339 
    340 IN_PROC_BROWSER_TEST_F(RemoteDesktopBrowserTest, MANUAL_Launch) {
    341   VerifyInternetAccess();
    342 
    343   if (!NoInstall()) {
    344     VerifyChromotingLoaded(false);
    345     InstallChromotingApp();
    346   }
    347 
    348   VerifyChromotingLoaded(true);
    349 
    350   LaunchChromotingApp();
    351 
    352   // TODO: Remove this hack by blocking on the appropriate notification.
    353   // The browser may still be loading images embedded in the webapp. If we
    354   // uinstall it now those load will fail. Navigating away to avoid the load
    355   // failures.
    356   ui_test_utils::NavigateToURL(browser(), GURL("about:blank"));
    357 
    358   if (!NoCleanup()) {
    359     UninstallChromotingApp();
    360     VerifyChromotingLoaded(false);
    361   }
    362 }
    363 
    364 IN_PROC_BROWSER_TEST_F(RemoteDesktopBrowserTest, MANUAL_Auth) {
    365   VerifyInternetAccess();
    366 
    367   if (!NoInstall()) {
    368     VerifyChromotingLoaded(false);
    369     InstallChromotingApp();
    370   }
    371 
    372   VerifyChromotingLoaded(true);
    373 
    374   LaunchChromotingApp();
    375 
    376   Authorize();
    377 
    378   Authenticate();
    379 
    380   if (!NoCleanup()) {
    381     UninstallChromotingApp();
    382     VerifyChromotingLoaded(false);
    383   }
    384 }
    385 
    386 }  // namespace remoting
    387