Home | History | Annotate | Download | only in base
      1 // Copyright (c) 2012 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/test/base/in_process_browser_test.h"
      6 
      7 #include "base/auto_reset.h"
      8 #include "base/basictypes.h"
      9 #include "base/bind.h"
     10 #include "base/command_line.h"
     11 #include "base/files/file_path.h"
     12 #include "base/files/file_util.h"
     13 #include "base/lazy_instance.h"
     14 #include "base/path_service.h"
     15 #include "base/strings/string_number_conversions.h"
     16 #include "base/test/test_file_util.h"
     17 #include "base/threading/non_thread_safe.h"
     18 #include "chrome/browser/browser_process.h"
     19 #include "chrome/browser/lifetime/application_lifetime.h"
     20 #include "chrome/browser/net/net_error_tab_helper.h"
     21 #include "chrome/browser/profiles/profile.h"
     22 #include "chrome/browser/profiles/profile_manager.h"
     23 #include "chrome/browser/ui/browser.h"
     24 #include "chrome/browser/ui/browser_finder.h"
     25 #include "chrome/browser/ui/browser_list.h"
     26 #include "chrome/browser/ui/browser_list_observer.h"
     27 #include "chrome/browser/ui/browser_navigator.h"
     28 #include "chrome/browser/ui/browser_tabstrip.h"
     29 #include "chrome/browser/ui/browser_window.h"
     30 #include "chrome/browser/ui/host_desktop.h"
     31 #include "chrome/browser/ui/tabs/tab_strip_model.h"
     32 #include "chrome/common/chrome_constants.h"
     33 #include "chrome/common/chrome_paths.h"
     34 #include "chrome/common/chrome_switches.h"
     35 #include "chrome/common/logging_chrome.h"
     36 #include "chrome/common/url_constants.h"
     37 #include "chrome/renderer/chrome_content_renderer_client.h"
     38 #include "chrome/test/base/chrome_test_suite.h"
     39 #include "chrome/test/base/test_launcher_utils.h"
     40 #include "chrome/test/base/test_switches.h"
     41 #include "chrome/test/base/testing_browser_process.h"
     42 #include "chrome/test/base/ui_test_utils.h"
     43 #include "components/google/core/browser/google_util.h"
     44 #include "components/os_crypt/os_crypt.h"
     45 #include "content/public/browser/notification_service.h"
     46 #include "content/public/browser/notification_types.h"
     47 #include "content/public/test/browser_test_utils.h"
     48 #include "content/public/test/test_launcher.h"
     49 #include "content/public/test/test_navigation_observer.h"
     50 #include "net/test/embedded_test_server/embedded_test_server.h"
     51 #include "net/test/spawned_test_server/spawned_test_server.h"
     52 
     53 #if defined(OS_MACOSX)
     54 #include "base/mac/scoped_nsautorelease_pool.h"
     55 #endif
     56 
     57 #if defined(OS_WIN)
     58 #include "base/win/scoped_com_initializer.h"
     59 #include "base/win/windows_version.h"
     60 #include "ui/base/win/atl_module.h"
     61 #include "win8/test/metro_registration_helper.h"
     62 #include "win8/test/test_registrar_constants.h"
     63 #endif
     64 
     65 #if defined(ENABLE_CAPTIVE_PORTAL_DETECTION)
     66 #include "chrome/browser/captive_portal/captive_portal_service.h"
     67 #endif
     68 
     69 #if !defined(OS_ANDROID) && !defined(OS_IOS)
     70 #include "components/storage_monitor/test_storage_monitor.h"
     71 #endif
     72 
     73 #if defined(OS_CHROMEOS)
     74 #include "chrome/browser/chromeos/input_method/input_method_configuration.h"
     75 #endif
     76 
     77 namespace {
     78 
     79 // Passed as value of kTestType.
     80 const char kBrowserTestType[] = "browser";
     81 
     82 // A BrowserListObserver that makes sure that all browsers created are on the
     83 // |allowed_desktop_|.
     84 class SingleDesktopTestObserver : public chrome::BrowserListObserver,
     85                                   public base::NonThreadSafe {
     86  public:
     87   explicit SingleDesktopTestObserver(chrome::HostDesktopType allowed_desktop);
     88   virtual ~SingleDesktopTestObserver();
     89 
     90   // chrome::BrowserListObserver:
     91   virtual void OnBrowserAdded(Browser* browser) OVERRIDE;
     92 
     93  private:
     94   chrome::HostDesktopType allowed_desktop_;
     95 
     96   DISALLOW_COPY_AND_ASSIGN(SingleDesktopTestObserver);
     97 };
     98 
     99 SingleDesktopTestObserver::SingleDesktopTestObserver(
    100     chrome::HostDesktopType allowed_desktop)
    101         : allowed_desktop_(allowed_desktop) {
    102   BrowserList::AddObserver(this);
    103 }
    104 
    105 SingleDesktopTestObserver::~SingleDesktopTestObserver() {
    106   BrowserList::RemoveObserver(this);
    107 }
    108 
    109 void SingleDesktopTestObserver::OnBrowserAdded(Browser* browser) {
    110   CHECK(CalledOnValidThread());
    111   CHECK_EQ(browser->host_desktop_type(), allowed_desktop_);
    112 }
    113 
    114 }  // namespace
    115 
    116 InProcessBrowserTest::InProcessBrowserTest()
    117     : browser_(NULL),
    118       exit_when_last_browser_closes_(true),
    119       open_about_blank_on_browser_launch_(true),
    120       multi_desktop_test_(false)
    121 #if defined(OS_MACOSX)
    122       , autorelease_pool_(NULL)
    123 #endif  // OS_MACOSX
    124     {
    125 #if defined(OS_MACOSX)
    126   // TODO(phajdan.jr): Make browser_tests self-contained on Mac, remove this.
    127   // Before we run the browser, we have to hack the path to the exe to match
    128   // what it would be if Chrome was running, because it is used to fork renderer
    129   // processes, on Linux at least (failure to do so will cause a browser_test to
    130   // be run instead of a renderer).
    131   base::FilePath chrome_path;
    132   CHECK(PathService::Get(base::FILE_EXE, &chrome_path));
    133   chrome_path = chrome_path.DirName();
    134   chrome_path = chrome_path.Append(chrome::kBrowserProcessExecutablePath);
    135   CHECK(PathService::Override(base::FILE_EXE, chrome_path));
    136 #endif  // defined(OS_MACOSX)
    137 
    138   CreateTestServer(base::FilePath(FILE_PATH_LITERAL("chrome/test/data")));
    139   base::FilePath src_dir;
    140   CHECK(PathService::Get(base::DIR_SOURCE_ROOT, &src_dir));
    141   base::FilePath test_data_dir = src_dir.AppendASCII("chrome/test/data");
    142   embedded_test_server()->ServeFilesFromDirectory(test_data_dir);
    143 
    144   // chrome::DIR_TEST_DATA isn't going to be setup until after we call
    145   // ContentMain. However that is after tests' constructors or SetUp methods,
    146   // which sometimes need it. So just override it.
    147   CHECK(PathService::Override(chrome::DIR_TEST_DATA, test_data_dir));
    148 }
    149 
    150 InProcessBrowserTest::~InProcessBrowserTest() {
    151 }
    152 
    153 void InProcessBrowserTest::SetUp() {
    154   // Browser tests will create their own g_browser_process later.
    155   DCHECK(!g_browser_process);
    156 
    157   CommandLine* command_line = CommandLine::ForCurrentProcess();
    158 
    159   // Auto-reload breaks many browser tests, which assume error pages won't be
    160   // reloaded out from under them. Tests that expect or desire this behavior can
    161   // append switches::kEnableOfflineAutoReload, which will override the disable
    162   // here.
    163   command_line->AppendSwitch(switches::kDisableOfflineAutoReload);
    164 
    165   // Allow subclasses to change the command line before running any tests.
    166   SetUpCommandLine(command_line);
    167   // Add command line arguments that are used by all InProcessBrowserTests.
    168   PrepareTestCommandLine(command_line);
    169 
    170   // Create a temporary user data directory if required.
    171   ASSERT_TRUE(CreateUserDataDirectory())
    172       << "Could not create user data directory.";
    173 
    174   // Allow subclasses the opportunity to make changes to the default user data
    175   // dir before running any tests.
    176   ASSERT_TRUE(SetUpUserDataDirectory())
    177       << "Could not set up user data directory.";
    178 
    179 #if defined(OS_CHROMEOS)
    180   // Make sure that the log directory exists.
    181   base::FilePath log_dir = logging::GetSessionLogFile(*command_line).DirName();
    182   base::CreateDirectory(log_dir);
    183   // Disable IME extension loading to avoid many browser tests failures.
    184   chromeos::input_method::DisableExtensionLoading();
    185 #endif  // defined(OS_CHROMEOS)
    186 
    187 #if defined(OS_MACOSX)
    188   // Always use the MockKeychain if OS encription is used (which is when
    189   // anything sensitive gets stored, including Cookies).  Without this,
    190   // many tests will hang waiting for a user to approve KeyChain access.
    191   OSCrypt::UseMockKeychain(true);
    192 #endif
    193 
    194 #if defined(ENABLE_CAPTIVE_PORTAL_DETECTION)
    195   CaptivePortalService::set_state_for_testing(
    196       CaptivePortalService::DISABLED_FOR_TESTING);
    197 #endif
    198 
    199   chrome_browser_net::NetErrorTabHelper::set_state_for_testing(
    200       chrome_browser_net::NetErrorTabHelper::TESTING_FORCE_DISABLED);
    201 
    202   google_util::SetMockLinkDoctorBaseURLForTesting();
    203 
    204 #if defined(OS_WIN)
    205   base::win::Version version = base::win::GetVersion();
    206   // Although Ash officially is only supported for users on Win7+, we still run
    207   // ash_unittests on Vista builders, so we still need to initialize COM.
    208   if (version >= base::win::VERSION_VISTA &&
    209       CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests)) {
    210     com_initializer_.reset(new base::win::ScopedCOMInitializer());
    211     ui::win::CreateATLModuleIfNeeded();
    212     if (version >= base::win::VERSION_WIN8)
    213       ASSERT_TRUE(win8::MakeTestDefaultBrowserSynchronously());
    214   }
    215 #endif
    216 
    217   BrowserTestBase::SetUp();
    218 }
    219 
    220 void InProcessBrowserTest::PrepareTestCommandLine(CommandLine* command_line) {
    221   // Propagate commandline settings from test_launcher_utils.
    222   test_launcher_utils::PrepareBrowserCommandLineForTests(command_line);
    223 
    224   // This is a Browser test.
    225   command_line->AppendSwitchASCII(switches::kTestType, kBrowserTestType);
    226 
    227 #if defined(OS_WIN)
    228   if (command_line->HasSwitch(switches::kAshBrowserTests)) {
    229     command_line->AppendSwitchNative(switches::kViewerLaunchViaAppId,
    230                                      win8::test::kDefaultTestAppUserModelId);
    231     // Ash already launches with a single browser opened, add kSilentLaunch to
    232     // make sure StartupBrowserCreator doesn't attempt to launch a browser on
    233     // the native desktop on startup.
    234     command_line->AppendSwitch(switches::kSilentLaunch);
    235   }
    236 #endif
    237 
    238 #if defined(OS_MACOSX)
    239   // Explicitly set the path of the binary used for child processes, otherwise
    240   // they'll try to use browser_tests which doesn't contain ChromeMain.
    241   base::FilePath subprocess_path;
    242   PathService::Get(base::FILE_EXE, &subprocess_path);
    243   // Recreate the real environment, run the helper within the app bundle.
    244   subprocess_path = subprocess_path.DirName().DirName();
    245   DCHECK_EQ(subprocess_path.BaseName().value(), "Contents");
    246   subprocess_path =
    247       subprocess_path.Append("Versions").Append(chrome::kChromeVersion);
    248   subprocess_path =
    249       subprocess_path.Append(chrome::kHelperProcessExecutablePath);
    250   command_line->AppendSwitchPath(switches::kBrowserSubprocessPath,
    251                                  subprocess_path);
    252 #endif
    253 
    254   // TODO(pkotwicz): Investigate if we can remove this switch.
    255   if (exit_when_last_browser_closes_)
    256     command_line->AppendSwitch(switches::kDisableZeroBrowsersOpenForTests);
    257 
    258   if (open_about_blank_on_browser_launch_ && command_line->GetArgs().empty())
    259     command_line->AppendArg(url::kAboutBlankURL);
    260 }
    261 
    262 bool InProcessBrowserTest::CreateUserDataDirectory() {
    263   CommandLine* command_line = CommandLine::ForCurrentProcess();
    264   base::FilePath user_data_dir =
    265       command_line->GetSwitchValuePath(switches::kUserDataDir);
    266   if (user_data_dir.empty()) {
    267     if (temp_user_data_dir_.CreateUniqueTempDir() &&
    268         temp_user_data_dir_.IsValid()) {
    269       user_data_dir = temp_user_data_dir_.path();
    270     } else {
    271       LOG(ERROR) << "Could not create temporary user data directory \""
    272                  << temp_user_data_dir_.path().value() << "\".";
    273       return false;
    274     }
    275   }
    276   return test_launcher_utils::OverrideUserDataDir(user_data_dir);
    277 }
    278 
    279 void InProcessBrowserTest::TearDown() {
    280   DCHECK(!g_browser_process);
    281 #if defined(OS_WIN)
    282   com_initializer_.reset();
    283 #endif
    284   BrowserTestBase::TearDown();
    285 }
    286 
    287 void InProcessBrowserTest::AddTabAtIndexToBrowser(
    288     Browser* browser,
    289     int index,
    290     const GURL& url,
    291     ui::PageTransition transition) {
    292   chrome::NavigateParams params(browser, url, transition);
    293   params.tabstrip_index = index;
    294   params.disposition = NEW_FOREGROUND_TAB;
    295   chrome::Navigate(&params);
    296 
    297   content::WaitForLoadStop(params.target_contents);
    298 }
    299 
    300 void InProcessBrowserTest::AddTabAtIndex(
    301     int index,
    302     const GURL& url,
    303     ui::PageTransition transition) {
    304   AddTabAtIndexToBrowser(browser(), index, url, transition);
    305 }
    306 
    307 bool InProcessBrowserTest::SetUpUserDataDirectory() {
    308   return true;
    309 }
    310 
    311 // Creates a browser with a single tab (about:blank), waits for the tab to
    312 // finish loading and shows the browser.
    313 Browser* InProcessBrowserTest::CreateBrowser(Profile* profile) {
    314   Browser* browser = new Browser(
    315       Browser::CreateParams(profile, chrome::GetActiveDesktop()));
    316   AddBlankTabAndShow(browser);
    317   return browser;
    318 }
    319 
    320 Browser* InProcessBrowserTest::CreateIncognitoBrowser() {
    321   // Create a new browser with using the incognito profile.
    322   Browser* incognito = new Browser(
    323       Browser::CreateParams(browser()->profile()->GetOffTheRecordProfile(),
    324                             chrome::GetActiveDesktop()));
    325   AddBlankTabAndShow(incognito);
    326   return incognito;
    327 }
    328 
    329 Browser* InProcessBrowserTest::CreateBrowserForPopup(Profile* profile) {
    330   Browser* browser =
    331       new Browser(Browser::CreateParams(Browser::TYPE_POPUP, profile,
    332                   chrome::GetActiveDesktop()));
    333   AddBlankTabAndShow(browser);
    334   return browser;
    335 }
    336 
    337 Browser* InProcessBrowserTest::CreateBrowserForApp(
    338     const std::string& app_name,
    339     Profile* profile) {
    340   Browser* browser = new Browser(
    341       Browser::CreateParams::CreateForApp(
    342           app_name, false /* trusted_source */, gfx::Rect(), profile,
    343           chrome::GetActiveDesktop()));
    344   AddBlankTabAndShow(browser);
    345   return browser;
    346 }
    347 
    348 void InProcessBrowserTest::AddBlankTabAndShow(Browser* browser) {
    349   content::WindowedNotificationObserver observer(
    350       content::NOTIFICATION_LOAD_STOP,
    351       content::NotificationService::AllSources());
    352   chrome::AddSelectedTabWithURL(browser,
    353                                 GURL(url::kAboutBlankURL),
    354                                 ui::PAGE_TRANSITION_AUTO_TOPLEVEL);
    355   observer.Wait();
    356 
    357   browser->window()->Show();
    358 }
    359 
    360 #if !defined(OS_MACOSX)
    361 CommandLine InProcessBrowserTest::GetCommandLineForRelaunch() {
    362   CommandLine new_command_line(CommandLine::ForCurrentProcess()->GetProgram());
    363   CommandLine::SwitchMap switches =
    364       CommandLine::ForCurrentProcess()->GetSwitches();
    365   switches.erase(switches::kUserDataDir);
    366   switches.erase(content::kSingleProcessTestsFlag);
    367   switches.erase(switches::kSingleProcess);
    368   new_command_line.AppendSwitch(content::kLaunchAsBrowser);
    369 
    370   base::FilePath user_data_dir;
    371   PathService::Get(chrome::DIR_USER_DATA, &user_data_dir);
    372   new_command_line.AppendSwitchPath(switches::kUserDataDir, user_data_dir);
    373 
    374   for (CommandLine::SwitchMap::const_iterator iter = switches.begin();
    375         iter != switches.end(); ++iter) {
    376     new_command_line.AppendSwitchNative((*iter).first, (*iter).second);
    377   }
    378   return new_command_line;
    379 }
    380 #endif
    381 
    382 void InProcessBrowserTest::RunTestOnMainThreadLoop() {
    383   // Pump startup related events.
    384   content::RunAllPendingInMessageLoop();
    385 
    386   chrome::HostDesktopType active_desktop = chrome::GetActiveDesktop();
    387   // Self-adds/removes itself from the BrowserList observers.
    388   scoped_ptr<SingleDesktopTestObserver> single_desktop_test_observer;
    389   if (!multi_desktop_test_) {
    390     single_desktop_test_observer.reset(
    391         new SingleDesktopTestObserver(active_desktop));
    392   }
    393 
    394   const BrowserList* active_browser_list =
    395       BrowserList::GetInstance(active_desktop);
    396   if (!active_browser_list->empty()) {
    397     browser_ = active_browser_list->get(0);
    398 #if defined(USE_ASH)
    399     // There are cases where windows get created maximized by default.
    400     if (browser_->window()->IsMaximized())
    401       browser_->window()->Restore();
    402 #endif
    403     content::WaitForLoadStop(
    404         browser_->tab_strip_model()->GetActiveWebContents());
    405   }
    406 
    407 #if !defined(OS_ANDROID) && !defined(OS_IOS)
    408   // Do not use the real StorageMonitor for tests, which introduces another
    409   // source of variability and potential slowness.
    410   ASSERT_TRUE(storage_monitor::TestStorageMonitor::CreateForBrowserTests());
    411 #endif
    412 
    413 #if defined(OS_MACOSX)
    414   // On Mac, without the following autorelease pool, code which is directly
    415   // executed (as opposed to executed inside a message loop) would autorelease
    416   // objects into a higher-level pool. This pool is not recycled in-sync with
    417   // the message loops' pools and causes problems with code relying on
    418   // deallocation via an autorelease pool (such as browser window closure and
    419   // browser shutdown). To avoid this, the following pool is recycled after each
    420   // time code is directly executed.
    421   autorelease_pool_ = new base::mac::ScopedNSAutoreleasePool;
    422 #endif
    423 
    424   // Pump any pending events that were created as a result of creating a
    425   // browser.
    426   content::RunAllPendingInMessageLoop();
    427 
    428   SetUpOnMainThread();
    429 #if defined(OS_MACOSX)
    430   autorelease_pool_->Recycle();
    431 #endif
    432 
    433   if (!HasFatalFailure())
    434     RunTestOnMainThread();
    435 #if defined(OS_MACOSX)
    436   autorelease_pool_->Recycle();
    437 #endif
    438 
    439   // Invoke cleanup and quit even if there are failures. This is similar to
    440   // gtest in that it invokes TearDown even if Setup fails.
    441   TearDownOnMainThread();
    442 #if defined(OS_MACOSX)
    443   autorelease_pool_->Recycle();
    444 #endif
    445 
    446   // Sometimes tests leave Quit tasks in the MessageLoop (for shame), so let's
    447   // run all pending messages here to avoid preempting the QuitBrowsers tasks.
    448   // TODO(jbates) Once crbug.com/134753 is fixed, this can be removed because it
    449   // will not be possible to post Quit tasks.
    450   content::RunAllPendingInMessageLoop();
    451 
    452   QuitBrowsers();
    453   // All BrowserLists should be empty at this point.
    454   for (chrome::HostDesktopType t = chrome::HOST_DESKTOP_TYPE_FIRST;
    455        t < chrome::HOST_DESKTOP_TYPE_COUNT;
    456        t = static_cast<chrome::HostDesktopType>(t + 1)) {
    457     CHECK(BrowserList::GetInstance(t)->empty()) << t;
    458   }
    459 }
    460 
    461 void InProcessBrowserTest::QuitBrowsers() {
    462   if (chrome::GetTotalBrowserCount() == 0) {
    463     chrome::NotifyAppTerminating();
    464     return;
    465   }
    466 
    467   // Invoke AttemptExit on a running message loop.
    468   // AttemptExit exits the message loop after everything has been
    469   // shut down properly.
    470   base::MessageLoopForUI::current()->PostTask(FROM_HERE,
    471                                               base::Bind(&chrome::AttemptExit));
    472   content::RunMessageLoop();
    473 
    474 #if defined(OS_MACOSX)
    475   // chrome::AttemptExit() will attempt to close all browsers by deleting
    476   // their tab contents. The last tab contents being removed triggers closing of
    477   // the browser window.
    478   //
    479   // On the Mac, this eventually reaches
    480   // -[BrowserWindowController windowWillClose:], which will post a deferred
    481   // -autorelease on itself to ultimately destroy the Browser object. The line
    482   // below is necessary to pump these pending messages to ensure all Browsers
    483   // get deleted.
    484   content::RunAllPendingInMessageLoop();
    485   delete autorelease_pool_;
    486   autorelease_pool_ = NULL;
    487 #endif
    488 }
    489