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