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(&params);
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