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