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