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.
5 #include "chrome/test/base/in_process_browser_test.h"
7 #include "base/auto_reset.h"
8 #include "base/basictypes.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"
54 #if defined(OS_MACOSX)
55 #include "base/mac/scoped_nsautorelease_pool.h"
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"
66 #if defined(ENABLE_CAPTIVE_PORTAL_DETECTION)
67 #include "chrome/browser/captive_portal/captive_portal_service.h"
70 #if !defined(OS_ANDROID) && !defined(OS_IOS)
71 #include "chrome/browser/storage_monitor/test_storage_monitor.h"
76 // Passed as value of kTestType.
77 const char kBrowserTestType
[] = "browser";
79 // Used when running in single-process mode.
80 base::LazyInstance
<ChromeContentRendererClient
>::Leaky
81 g_chrome_content_renderer_client
= LAZY_INSTANCE_INITIALIZER
;
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
{
88 explicit SingleDesktopTestObserver(chrome::HostDesktopType allowed_desktop
);
89 virtual ~SingleDesktopTestObserver();
91 // chrome::BrowserListObserver:
92 virtual void OnBrowserAdded(Browser
* browser
) OVERRIDE
;
95 chrome::HostDesktopType allowed_desktop_
;
97 DISALLOW_COPY_AND_ASSIGN(SingleDesktopTestObserver
);
100 SingleDesktopTestObserver::SingleDesktopTestObserver(
101 chrome::HostDesktopType allowed_desktop
)
102 : allowed_desktop_(allowed_desktop
) {
103 BrowserList::AddObserver(this);
106 SingleDesktopTestObserver::~SingleDesktopTestObserver() {
107 BrowserList::RemoveObserver(this);
110 void SingleDesktopTestObserver::OnBrowserAdded(Browser
* browser
) {
111 CHECK(CalledOnValidThread());
112 CHECK_EQ(browser
->host_desktop_type(), allowed_desktop_
);
117 InProcessBrowserTest::InProcessBrowserTest()
119 exit_when_last_browser_closes_(true),
120 multi_desktop_test_(false)
121 #if defined(OS_MACOSX)
122 , autorelease_pool_(NULL
)
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"));
144 InProcessBrowserTest::~InProcessBrowserTest() {
147 void InProcessBrowserTest::SetUp() {
148 // Browser tests will create their own g_browser_process later.
149 DCHECK(!g_browser_process
);
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
);
157 // Create a temporary user data directory if required.
158 ASSERT_TRUE(CreateUserDataDirectory())
159 << "Could not create user data directory.";
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.";
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
.Pointer());
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)
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
;
190 #if defined(ENABLE_CAPTIVE_PORTAL_DETECTION)
191 captive_portal::CaptivePortalService::set_state_for_testing(
192 captive_portal::CaptivePortalService::DISABLED_FOR_TESTING
);
195 chrome_browser_net::NetErrorTabHelper::set_state_for_testing(
196 chrome_browser_net::NetErrorTabHelper::TESTING_FORCE_DISABLED
);
198 google_util::SetMockLinkDoctorBaseURLForTesting();
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());
209 BrowserTestBase::SetUp();
212 void InProcessBrowserTest::PrepareTestCommandLine(CommandLine
* command_line
) {
213 // Propagate commandline settings from test_launcher_utils.
214 test_launcher_utils::PrepareBrowserCommandLineForTests(command_line
);
216 // This is a Browser test.
217 command_line
->AppendSwitchASCII(switches::kTestType
, kBrowserTestType
);
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
);
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");
239 subprocess_path
.Append("Versions").Append(chrome::kChromeVersion
);
241 subprocess_path
.Append(chrome::kHelperProcessExecutablePath
);
242 command_line
->AppendSwitchPath(switches::kBrowserSubprocessPath
,
246 // TODO(pkotwicz): Investigate if we can remove this switch.
247 if (exit_when_last_browser_closes_
)
248 command_line
->AppendSwitch(switches::kDisableZeroBrowsersOpenForTests
);
250 if (command_line
->GetArgs().empty())
251 command_line
->AppendArg(content::kAboutBlankURL
);
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();
263 LOG(ERROR
) << "Could not create temporary user data directory \""
264 << temp_user_data_dir_
.path().value() << "\".";
268 return test_launcher_utils::OverrideUserDataDir(user_data_dir
);
271 void InProcessBrowserTest::TearDown() {
272 DCHECK(!g_browser_process
);
273 #if defined(OS_WIN) && defined(USE_AURA)
274 com_initializer_
.reset();
276 BrowserTestBase::TearDown();
279 void InProcessBrowserTest::AddTabAtIndexToBrowser(
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
);
289 content::WaitForLoadStop(params
.target_contents
);
292 void InProcessBrowserTest::AddTabAtIndex(
295 content::PageTransition transition
) {
296 AddTabAtIndexToBrowser(browser(), index
, url
, transition
);
299 bool InProcessBrowserTest::SetUpUserDataDirectory() {
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
);
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
);
321 Browser
* InProcessBrowserTest::CreateBrowserForPopup(Profile
* profile
) {
323 new Browser(Browser::CreateParams(Browser::TYPE_POPUP
, profile
,
324 chrome::GetActiveDesktop()));
325 AddBlankTabAndShow(browser
);
329 Browser
* InProcessBrowserTest::CreateBrowserForApp(
330 const std::string
& app_name
,
332 Browser
* browser
= new Browser(
333 Browser::CreateParams::CreateForApp(
334 Browser::TYPE_POPUP
, app_name
, gfx::Rect(), profile
,
335 chrome::GetActiveDesktop()));
336 AddBlankTabAndShow(browser
);
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
);
348 browser
->window()->Show();
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
);
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
);
365 for (CommandLine::SwitchMap::const_iterator iter
= switches
.begin();
366 iter
!= switches
.end(); ++iter
) {
367 new_command_line
.AppendSwitchNative((*iter
).first
, (*iter
).second
);
369 return new_command_line
;
373 void InProcessBrowserTest::RunTestOnMainThreadLoop() {
374 // Pump startup related events.
375 content::RunAllPendingInMessageLoop();
377 #if defined(OS_MACOSX)
378 autorelease_pool_
->Recycle();
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
));
389 const BrowserList
* active_browser_list
=
390 BrowserList::GetInstance(active_desktop
);
391 if (!active_browser_list
->empty()) {
392 browser_
= active_browser_list
->get(0);
394 // There are cases where windows get created maximized by default.
395 if (browser_
->window()->IsMaximized())
396 browser_
->window()->Restore();
398 content::WaitForLoadStop(
399 browser_
->tab_strip_model()->GetActiveWebContents());
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());
408 // Pump any pending events that were created as a result of creating a
410 content::RunAllPendingInMessageLoop();
413 #if defined(OS_MACOSX)
414 autorelease_pool_
->Recycle();
417 if (!HasFatalFailure())
418 RunTestOnMainThread();
419 #if defined(OS_MACOSX)
420 autorelease_pool_
->Recycle();
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();
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();
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
;
445 void InProcessBrowserTest::QuitBrowsers() {
446 if (chrome::GetTotalBrowserCount() == 0) {
447 chrome::NotifyAppTerminating();
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();
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.
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
468 content::RunAllPendingInMessageLoop();
469 delete autorelease_pool_
;
470 autorelease_pool_
= NULL
;