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(¶ms); 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