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