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