[go: nahoru, domu]

Provides the ability to write a unit test that brings up a browser. As
a proof of concept I converted
FindInPageControllerTest.FindInPageFrames over to this.

See the description in InProcessBrowserTest for how it all works.

BUG=none
TEST=none

Review URL: http://codereview.chromium.org/19644

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@8934 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/base/command_line.h b/base/command_line.h
index f1a2005c..9a02546 100644
--- a/base/command_line.h
+++ b/base/command_line.h
@@ -27,6 +27,8 @@
 #include "base/logging.h"
 #include "base/scoped_ptr.h"
 
+class InProcessBrowserTest;
+
 class CommandLine {
  public:
 #if defined(OS_WIN)
@@ -113,8 +115,16 @@
                        bool include_program);
 
  private:
+  friend class InProcessBrowserTest;
+
   CommandLine() {}
 
+  // Used by InProcessBrowserTest.
+  static CommandLine* ForCurrentProcessMutable() {
+    DCHECK(current_process_commandline_);
+    return current_process_commandline_;
+  }
+
   // The singleton CommandLine instance representing the current process's
   // command line.
   static CommandLine* current_process_commandline_;
diff --git a/chrome/browser/browser_main.cc b/chrome/browser/browser_main.cc
index bc25e95..5d2b1496 100644
--- a/chrome/browser/browser_main.cc
+++ b/chrome/browser/browser_main.cc
@@ -175,6 +175,14 @@
 }
 #endif
 
+void RunUIMessageLoop(BrowserProcess* browser_process) {
+#if defined(OS_WIN)
+  MessageLoopForUI::current()->Run(browser_process->accelerator_handler());
+#elif defined(OS_POSIX)
+  MessageLoopForUI::current()->Run();
+#endif
+}
+
 }  // namespace
 
 // Main routine for running as the Browser process.
@@ -295,10 +303,14 @@
   }
 
 #if defined(OS_WIN)
-  ResourceBundle::InitSharedInstance(
-      local_state->GetString(prefs::kApplicationLocale));
-  // We only load the theme dll in the browser process.
-  ResourceBundle::GetSharedInstance().LoadThemeResources();
+  // If we're running tests (ui_task is non-null), then the ResourceBundle
+  // has already been initialized.
+  if (!parameters.ui_task) {
+    ResourceBundle::InitSharedInstance(
+        local_state->GetString(prefs::kApplicationLocale));
+    // We only load the theme dll in the browser process.
+    ResourceBundle::GetSharedInstance().LoadThemeResources();
+  }
 #endif
 
   if (!parsed_command_line.HasSwitch(switches::kNoErrorDialogs)) {
@@ -324,7 +336,12 @@
     // Flush the message loop which lets the UserDataDirDialog close.
     MessageLoop::current()->Run();
 
-    ResourceBundle::CleanupSharedInstance();
+    if (!parameters.ui_task && browser_shutdown::delete_resources_on_shutdown) {
+      // Only delete the resources if we're not running tests. If we're running
+      // tests the resources need to be reused as many places in the UI cache
+      // SkBitmaps from the ResourceBundle.
+      ResourceBundle::CleanupSharedInstance();
+    }
 
     if (!user_data_dir.empty()) {
       // Because of the way CommandLine parses, it's sufficient to append a new
@@ -455,7 +472,8 @@
 
   sandbox::BrokerServices* broker_services =
       parameters.sandbox_info_.BrokerServices();
-  browser_process->InitBrokerServices(broker_services);
+  if (broker_services)
+    browser_process->InitBrokerServices(broker_services);
 #endif
 
   // In unittest mode, this will do nothing.  In normal mode, this will create
@@ -526,13 +544,13 @@
   RecordBreakpadStatusUMA(metrics);
 
   int result_code = ResultCodes::NORMAL_EXIT;
-  if (BrowserInit::ProcessCommandLine(parsed_command_line, L"", local_state,
-                                      true, profile, &result_code)) {
-#if defined(OS_WIN)
-    MessageLoopForUI::current()->Run(browser_process->accelerator_handler());
-#elif defined(OS_POSIX)
-    MessageLoopForUI::current()->Run();
-#endif
+  if (parameters.ui_task) {
+    MessageLoopForUI::current()->PostTask(FROM_HERE, parameters.ui_task);
+    RunUIMessageLoop(browser_process.get());
+  } else if (BrowserInit::ProcessCommandLine(parsed_command_line,
+                                             std::wstring(), local_state, true,
+                                             profile, &result_code)) {
+    RunUIMessageLoop(browser_process.get());
   }
 
   Platform::WillTerminate();
diff --git a/chrome/browser/views/find_bar_win_uitest.cc b/chrome/browser/views/find_bar_win_uitest.cc
index 108e7ce..607aa149 100644
--- a/chrome/browser/views/find_bar_win_uitest.cc
+++ b/chrome/browser/views/find_bar_win_uitest.cc
@@ -23,58 +23,6 @@
 const std::wstring kCrashPage = L"files/find_in_page/crash_1341577.html";
 const std::wstring kTooFewMatchesPage = L"files/find_in_page/bug_1155639.html";
 
-// This test loads a page with frames and starts FindInPage requests
-TEST_F(FindInPageControllerTest, FindInPageFrames) {
-  scoped_refptr<HTTPTestServer> server =
-      HTTPTestServer::CreateServer(L"chrome/test/data", NULL);
-  ASSERT_TRUE(NULL != server.get());
-
-  // First we navigate to our frames page.
-  GURL url = server->TestServerPageW(kFramePage);
-  scoped_ptr<TabProxy> tab(GetActiveTab());
-  ASSERT_TRUE(tab->NavigateToURL(url));
-  WaitUntilTabCount(1);
-
-  // Try incremental search (mimicking user typing in).
-  EXPECT_EQ(18, tab->FindInPage(L"g",       FWD, IGNORE_CASE, false, NULL));
-  EXPECT_EQ(11, tab->FindInPage(L"go",      FWD, IGNORE_CASE, false, NULL));
-  EXPECT_EQ(04, tab->FindInPage(L"goo",     FWD, IGNORE_CASE, false, NULL));
-  EXPECT_EQ(03, tab->FindInPage(L"goog",    FWD, IGNORE_CASE, false, NULL));
-  EXPECT_EQ(02, tab->FindInPage(L"googl",   FWD, IGNORE_CASE, false, NULL));
-  EXPECT_EQ(01, tab->FindInPage(L"google",  FWD, IGNORE_CASE, false, NULL));
-  EXPECT_EQ(00, tab->FindInPage(L"google!", FWD, IGNORE_CASE, false, NULL));
-
-  // Negative test (no matches should be found).
-  EXPECT_EQ(0, tab->FindInPage(L"Non-existing string", FWD, IGNORE_CASE,
-                               false, NULL));
-
-  // 'horse' only exists in the three right frames.
-  EXPECT_EQ(3, tab->FindInPage(L"horse", FWD, IGNORE_CASE, false, NULL));
-
-  // 'cat' only exists in the first frame.
-  EXPECT_EQ(1, tab->FindInPage(L"cat", FWD, IGNORE_CASE, false, NULL));
-
-  // Try searching again, should still come up with 1 match.
-  EXPECT_EQ(1, tab->FindInPage(L"cat", FWD, IGNORE_CASE, false, NULL));
-
-  // Try searching backwards, ignoring case, should still come up with 1 match.
-  EXPECT_EQ(1, tab->FindInPage(L"CAT", BACK, IGNORE_CASE, false, NULL));
-
-  // Try case sensitive, should NOT find it.
-  EXPECT_EQ(0, tab->FindInPage(L"CAT", FWD, CASE_SENSITIVE, false, NULL));
-
-  // Try again case sensitive, but this time with right case.
-  EXPECT_EQ(1, tab->FindInPage(L"dog", FWD, CASE_SENSITIVE, false, NULL));
-
-  // Try non-Latin characters ('Hreggvidur' with 'eth' for 'd' in left frame).
-  EXPECT_EQ(1, tab->FindInPage(L"Hreggvi\u00F0ur", FWD, IGNORE_CASE,
-                               false, NULL));
-  EXPECT_EQ(1, tab->FindInPage(L"Hreggvi\u00F0ur", FWD, CASE_SENSITIVE,
-                               false, NULL));
-  EXPECT_EQ(0, tab->FindInPage(L"hreggvi\u00F0ur", FWD, CASE_SENSITIVE,
-                               false, NULL));
-}
-
 // This test loads a single-frame page and makes sure the ordinal returned makes
 // sense as we FindNext over all the items.
 TEST_F(FindInPageControllerTest, FindInPageOrdinal) {
diff --git a/chrome/browser/views/find_bar_win_unittest.cc b/chrome/browser/views/find_bar_win_unittest.cc
new file mode 100644
index 0000000..0112696
--- /dev/null
+++ b/chrome/browser/views/find_bar_win_unittest.cc
@@ -0,0 +1,143 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/message_loop.h"
+#include "chrome/browser/browser.h"
+#include "chrome/browser/find_notification_details.h"
+#include "chrome/browser/renderer_host/render_view_host.h"
+#include "chrome/browser/tab_contents/tab_contents.h"
+#include "chrome/browser/tab_contents/web_contents.h"
+#include "chrome/browser/tab_contents/web_contents_view.h"
+#include "chrome/browser/views/find_bar_win.h"
+#include "chrome/test/in_process_browser_test.h"
+#include "chrome/test/ui_test_utils.h"
+
+const std::wstring kFramePage = L"files/find_in_page/frames.html";
+const std::wstring kFrameData = L"files/find_in_page/framedata_general.html";
+const std::wstring kUserSelectPage = L"files/find_in_page/user-select.html";
+const std::wstring kCrashPage = L"files/find_in_page/crash_1341577.html";
+const std::wstring kTooFewMatchesPage = L"files/find_in_page/bug_1155639.html";
+
+class FindInPageNotificationObserver : public NotificationObserver {
+ public:
+  FindInPageNotificationObserver(TabContents* parent_tab)
+      : parent_tab_(parent_tab),
+        active_match_ordinal_(-1),
+        number_of_matches_(0) {
+    registrar_.Add(this, NOTIFY_FIND_RESULT_AVAILABLE,
+        Source<TabContents>(parent_tab_));
+    ui_test_utils::RunMessageLoop();
+  }
+
+  int active_match_ordinal() const { return active_match_ordinal_; }
+
+  int number_of_matches() const { return number_of_matches_; }
+
+  virtual void Observe(NotificationType type, const NotificationSource& source,
+                       const NotificationDetails& details) {
+    if (type == NOTIFY_FIND_RESULT_AVAILABLE) {
+      Details<FindNotificationDetails> find_details(details);
+      if (find_details->request_id() == kFindInPageRequestId) {
+        // We get multiple responses and one of those will contain the ordinal.
+        // This message comes to us before the final update is sent.
+        if (find_details->active_match_ordinal() > -1)
+          active_match_ordinal_ = find_details->active_match_ordinal();
+        if (find_details->final_update()) {
+          number_of_matches_ = find_details->number_of_matches();
+          MessageLoopForUI::current()->Quit();
+        } else {
+          DLOG(INFO) << "Ignoring, since we only care about the final message";
+        }
+      }
+    } else {
+      NOTREACHED();
+    }
+  }
+
+  // The Find mechanism is over asynchronous IPC, so a search is kicked off and
+  // we wait for notification to find out what the results are. As the user is
+  // typing, new search requests can be issued and the Request ID helps us make
+  // sense of whether this is the current request or an old one. The unit tests,
+  // however, which uses this constant issues only one search at a time, so we
+  // don't need a rolling id to identify each search. But, we still need to
+  // specify one, so we just use a fixed one - its value does not matter.
+  static const int kFindInPageRequestId;
+
+ private:
+  NotificationRegistrar registrar_;
+  TabContents* parent_tab_;
+  // We will at some point (before final update) be notified of the ordinal and
+  // we need to preserve it so we can send it later.
+  int active_match_ordinal_;
+  int number_of_matches_;
+};
+
+typedef enum FindInPageDirection { BACK = 0, FWD = 1 };
+typedef enum FindInPageCase { IGNORE_CASE = 0, CASE_SENSITIVE = 1 };
+
+class FindInPageControllerTest : public InProcessBrowserTest {
+ public:
+  FindInPageControllerTest() {}
+
+ protected:
+  int FindInPage(const std::wstring& search_string,
+                 FindInPageDirection forward,
+                 FindInPageCase match_case,
+                 bool find_next) {
+    WebContents* web_contents =
+        browser()->GetSelectedTabContents()->AsWebContents();
+    if (web_contents) {
+      web_contents->view()->FindInPage(*browser(), true, forward == FWD);
+      web_contents->render_view_host()->StartFinding(
+          FindInPageNotificationObserver::kFindInPageRequestId,
+          search_string, forward == FWD, match_case == CASE_SENSITIVE, find_next);
+      return FindInPageNotificationObserver(web_contents).number_of_matches();
+    }
+    return 0;
+  }
+};
+
+// This test loads a page with frames and starts FindInPage requests
+IN_PROC_BROWSER_TEST_F(FindInPageControllerTest, FindInPageFrames) {
+  HTTPTestServer* server = StartHTTPServer();
+
+  // First we navigate to our frames page.
+  GURL url = server->TestServerPageW(kFramePage);
+  ui_test_utils::NavigateToURL(browser(), url);
+
+  // Try incremental search (mimicking user typing in).
+  EXPECT_EQ(18, FindInPage(L"g",       FWD, IGNORE_CASE, false));
+  EXPECT_EQ(11, FindInPage(L"go",      FWD, IGNORE_CASE, false));
+  EXPECT_EQ(04, FindInPage(L"goo",     FWD, IGNORE_CASE, false));
+  EXPECT_EQ(03, FindInPage(L"goog",    FWD, IGNORE_CASE, false));
+  EXPECT_EQ(02, FindInPage(L"googl",   FWD, IGNORE_CASE, false));
+  EXPECT_EQ(01, FindInPage(L"google",  FWD, IGNORE_CASE, false));
+  EXPECT_EQ(00, FindInPage(L"google!", FWD, IGNORE_CASE, false));
+
+  // Negative test (no matches should be found).
+  EXPECT_EQ(0, FindInPage(L"Non-existing string", FWD, IGNORE_CASE, false));
+
+  // 'horse' only exists in the three right frames.
+  EXPECT_EQ(3, FindInPage(L"horse", FWD, IGNORE_CASE, false));
+
+  // 'cat' only exists in the first frame.
+  EXPECT_EQ(1, FindInPage(L"cat", FWD, IGNORE_CASE, false));
+
+  // Try searching again, should still come up with 1 match.
+  EXPECT_EQ(1, FindInPage(L"cat", FWD, IGNORE_CASE, false));
+
+  // Try searching backwards, ignoring case, should still come up with 1 match.
+  EXPECT_EQ(1, FindInPage(L"CAT", BACK, IGNORE_CASE, false));
+
+  // Try case sensitive, should NOT find it.
+  EXPECT_EQ(0, FindInPage(L"CAT", FWD, CASE_SENSITIVE, false));
+
+  // Try again case sensitive, but this time with right case.
+  EXPECT_EQ(1, FindInPage(L"dog", FWD, CASE_SENSITIVE, false));
+
+  // Try non-Latin characters ('Hreggvidur' with 'eth' for 'd' in left frame).
+  EXPECT_EQ(1, FindInPage(L"Hreggvi\u00F0ur", FWD, IGNORE_CASE, false));
+  EXPECT_EQ(1, FindInPage(L"Hreggvi\u00F0ur", FWD, CASE_SENSITIVE, false));
+  EXPECT_EQ(0, FindInPage(L"hreggvi\u00F0ur", FWD, CASE_SENSITIVE, false));
+}
diff --git a/chrome/common/main_function_params.h b/chrome/common/main_function_params.h
index e3a1c55..5537879 100644
--- a/chrome/common/main_function_params.h
+++ b/chrome/common/main_function_params.h
@@ -12,11 +12,16 @@
 #include "base/command_line.h"
 #include "chrome/common/sandbox_init_wrapper.h"
 
+class Task;
+
 struct MainFunctionParams {
   MainFunctionParams(const CommandLine& cl, const SandboxInitWrapper& sb)
-      : command_line_(cl), sandbox_info_(sb) { }
+      : command_line_(cl), sandbox_info_(sb), ui_task(NULL) { }
   const CommandLine& command_line_;
   const SandboxInitWrapper& sandbox_info_;
+  // Used by InProcessBrowserTest. If non-null BrowserMain schedules this
+  // task to run on the MessageLoop and BrowserInit is not invoked.
+  Task* ui_task;
 };
 
 #endif  // CHROME_COMMON_MAIN_FUNCTINON_PARAMS_H_
diff --git a/chrome/test/in_process_browser_test.cc b/chrome/test/in_process_browser_test.cc
new file mode 100644
index 0000000..581ddab
--- /dev/null
+++ b/chrome/test/in_process_browser_test.cc
@@ -0,0 +1,179 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/test/in_process_browser_test.h"
+
+#include "base/command_line.h"
+#include "base/file_util.h"
+#include "base/path_service.h"
+#include "chrome/browser/browser.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/browser_shutdown.h"
+#include "chrome/browser/profile.h"
+#include "chrome/browser/profile_manager.h"
+#include "chrome/browser/views/frame/browser_view.h"
+#include "chrome/common/chrome_constants.h"
+#include "chrome/common/chrome_paths.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/common/main_function_params.h"
+#include "chrome/test/testing_browser_process.h"
+#include "chrome/test/ui_test_utils.h"
+#include "sandbox/src/sandbox_factory.h"
+#include "sandbox/src/dep.h"
+
+extern int BrowserMain(const MainFunctionParams&);
+
+const wchar_t kUnitTestShowWindows[] = L"show-windows";
+
+namespace {
+
+bool DieFileDie(const std::wstring& file, bool recurse) {
+  if (!file_util::PathExists(file))
+    return true;
+
+  // Sometimes Delete fails, so try a few more times.
+  for (int i = 0; i < 10; ++i) {
+    if (file_util::Delete(file, recurse))
+      return true;
+    PlatformThread::Sleep(100);
+  }
+  return false;
+}
+
+}  // namespace
+
+InProcessBrowserTest::InProcessBrowserTest() : browser_(NULL) {
+}
+
+void InProcessBrowserTest::SetUp() {
+  // Cleanup the user data dir.
+  std::wstring user_data_dir;
+  PathService::Get(chrome::DIR_USER_DATA, &user_data_dir);
+  ASSERT_LT(10, static_cast<int>(user_data_dir.size())) <<
+      "The user data directory name passed into this test was too "
+      "short to delete safely.  Please check the user-data-dir "
+      "argument and try again.";
+  ASSERT_TRUE(DieFileDie(user_data_dir, true));
+
+  // The unit test suite creates a testingbrowser, but we want the real thing.
+  // Delete the current one. We'll install the testing one in TearDown.
+  delete g_browser_process;
+
+  // Don't delete the resources when BrowserMain returns. Many ui classes
+  // cache SkBitmaps in a static field so that if we delete the resource
+  // bundle we'll crash.
+  browser_shutdown::delete_resources_on_shutdown = false;
+
+  CommandLine* command_line = CommandLine::ForCurrentProcessMutable();
+
+  // Hide windows on show.
+  if (!command_line->HasSwitch(kUnitTestShowWindows))
+    BrowserView::SetShowState(SW_HIDE);
+
+  command_line->AppendSwitchWithValue(switches::kUserDataDir, user_data_dir);
+
+  // For some reason the sandbox wasn't happy running in test mode. These
+  // tests aren't intended to test the sandbox, so we turn it off.
+  command_line->AppendSwitch(switches::kNoSandbox);
+
+  // Explicitly set the path of the exe used for the renderer, otherwise it'll
+  // try to use unit_test.exe.
+  std::wstring renderer_path;
+  PathService::Get(base::FILE_EXE, &renderer_path);
+  file_util::TrimFilename(&renderer_path);
+  file_util::AppendToPath(&renderer_path,
+                          chrome::kBrowserProcessExecutableName);
+  command_line->AppendSwitchWithValue(switches::kRendererPath, renderer_path);
+
+  sandbox::SandboxInterfaceInfo sandbox_info = {0};
+  SandboxInitWrapper sandbox_wrapper;
+  MainFunctionParams params(*command_line, sandbox_wrapper);
+  params.ui_task =
+      NewRunnableMethod(this, &InProcessBrowserTest::RunTestOnMainThreadLoop);
+  BrowserMain(params);
+}
+
+void InProcessBrowserTest::TearDown() {
+  // Reinstall testing browser process.
+  delete g_browser_process;
+  g_browser_process = new TestingBrowserProcess();
+
+  browser_shutdown::delete_resources_on_shutdown = true;
+
+  BrowserView::SetShowState(-1);
+}
+
+void InProcessBrowserTest::Observe(NotificationType type,
+                                   const NotificationSource& source,
+                                   const NotificationDetails& details) {
+  if (type == NOTIFY_BROWSER_CLOSED) {
+    DCHECK(Source<Browser>(source).ptr() == browser_);
+    browser_ = NULL;
+  } else {
+    NOTREACHED();
+  }
+}
+
+HTTPTestServer* InProcessBrowserTest::StartHTTPServer() {
+  // The HTTPServer must run on the IO thread.
+  DCHECK(!http_server_.get());
+  http_server_ = HTTPTestServer::CreateServer(
+      L"chrome/test/data",
+      g_browser_process->io_thread()->message_loop());
+  return http_server_.get();
+}
+
+// Creates a browser with a single tab (about:blank), waits for the tab to
+// finish loading and shows the browser.
+Browser* InProcessBrowserTest::CreateBrowser(Profile* profile) {
+  Browser* browser = Browser::Create(profile);
+
+  browser->AddTabWithURL(
+      GURL("about:blank"), GURL(), PageTransition::START_PAGE, true, NULL);
+  
+  // Wait for the page to finish loading.
+  ui_test_utils::WaitForNavigation(
+      browser->GetSelectedTabContents()->controller());
+
+  browser->window()->Show();
+
+  return browser;
+}
+
+void InProcessBrowserTest::RunTestOnMainThreadLoop() {
+  // In the long term it would be great if we could use a TestingProfile
+  // here and only enable services you want tested, but that requires all
+  // consumers of Profile to handle NULL services.
+  FilePath user_data_dir;
+  PathService::Get(chrome::DIR_USER_DATA, &user_data_dir);
+  ProfileManager* profile_manager = g_browser_process->profile_manager();
+  Profile* profile = profile_manager->GetDefaultProfile(user_data_dir);
+  if (!profile) {
+    // We should only be able to get here if the profile already exists and
+    // has been created.
+    NOTREACHED();
+    MessageLoopForUI::current()->Quit();
+    return;
+  }
+
+  profile->InitExtensions();
+
+  browser_ = CreateBrowser(profile);
+
+  registrar_.Add(this, NOTIFY_BROWSER_CLOSED, Source<Browser>(browser_));
+
+  RunTestOnMainThread();
+
+  if (browser_)
+    browser_->CloseAllTabs();
+
+  // Remove all registered notifications, otherwise by the time the
+  // destructor is run the NotificationService is dead.
+  registrar_.RemoveAll();
+
+  // Stop the HTTP server.
+  http_server_ = NULL;
+
+  MessageLoopForUI::current()->Quit();
+}
diff --git a/chrome/test/in_process_browser_test.h b/chrome/test/in_process_browser_test.h
new file mode 100644
index 0000000..fb8bd9d
--- /dev/null
+++ b/chrome/test/in_process_browser_test.h
@@ -0,0 +1,125 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_TEST_IN_PROCESS_BROWSER_TEST_H_
+#define CHROME_TEST_IN_PROCESS_BROWSER_TEST_H_
+
+#include "chrome/app/scoped_ole_initializer.h"
+#include "chrome/common/notification_registrar.h"
+#include "chrome/common/notification_service.h"
+#include "net/url_request/url_request_unittest.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+class Browser;
+class Profile;
+
+// Base class for tests wanting to bring up a browser in the unit test process.
+// Writing tests with InProcessBrowserTest is slightly different than that of
+// other tests. This is necessitated by InProcessBrowserTest running a message
+// loop. To use InProcessBrowserTest do the following:
+// . Use the macro IN_PROC_BROWSER_TEST_F to define your test.
+// . Your test method is invoked on the ui thread. If you need to block until
+//   state changes you'll need to run the message loop from your test method.
+//   For example, if you need to wait till a find bar has completely been shown
+//   you'll need to invoke ui_test_utils::RunMessageLoop. When the message bar
+//   is shown, invoke MessageLoop::current()->Quit() to return control back to
+//   your test method.
+// . If you subclass and override SetUp, be sure and invoke
+//   InProcessBrowserTest::SetUp.
+//
+// By default InProcessBrowserTest creates a single Browser (as returned from
+// the CreateBrowser method). You can obviously create more as needed.
+
+// Browsers created while InProcessBrowserTest is running are shown hidden. Use
+// the command line switch --show-windows to make them visible when debugging.
+//
+// InProcessBrowserTest disables the sandbox when running.
+//
+// See ui_test_utils for a handful of methods designed for use with this class.
+class InProcessBrowserTest : public testing::Test, public NotificationObserver {
+ public:
+  InProcessBrowserTest();
+
+  // We do this so we can be used in a Task.
+  void AddRef() {}
+  void Release() {}
+
+  // Configures everything for an in process browser test, then invokes
+  // BrowserMain. BrowserMain ends up invoking RunTestOnMainThreadLoop.
+  virtual void SetUp();
+
+  // Restores state configured in SetUp.
+  virtual void TearDown();
+
+  // Used to track when the browser_ is destroyed. Resets the |browser_| field
+  // to NULL.
+  virtual void Observe(NotificationType type,
+                       const NotificationSource& source,
+                       const NotificationDetails& details);
+
+ protected:
+  // Returns the browser created by CreateBrowser.
+  Browser* browser() const { return browser_; }
+
+  // Override this rather than TestBody.
+  virtual void RunTestOnMainThread() = 0;
+
+  // Starts an HTTP server.
+  HTTPTestServer* StartHTTPServer();
+
+  // Creates a browser with a single tab (about:blank), waits for the tab to
+  // finish loading and shows the browser.
+  //
+  // This is invoked from Setup.
+  virtual Browser* CreateBrowser(Profile* profile);
+
+ private:
+  // Invokes CreateBrowser to create a browser, then RunTestOnMainThread, and
+  // destroys the browser.
+  void RunTestOnMainThreadLoop();
+
+  // Browser created from CreateBrowser.
+  Browser* browser_;
+
+  // Used to track when the browser is deleted.
+  NotificationRegistrar registrar_;
+
+  // HTTPServer, created when StartHTTPServer is invoked.
+  scoped_refptr<HTTPTestServer> http_server_;
+
+  ScopedOleInitializer ole_initializer_;
+
+  DISALLOW_COPY_AND_ASSIGN(InProcessBrowserTest);
+};
+
+#define IN_PROC_BROWSER_TEST_(test_case_name, test_name, parent_class,\
+                              parent_id)\
+class GTEST_TEST_CLASS_NAME_(test_case_name, test_name) : public parent_class {\
+ public:\
+  GTEST_TEST_CLASS_NAME_(test_case_name, test_name)() {}\
+ protected:\
+  virtual void RunTestOnMainThread();\
+ private:\
+  virtual void TestBody() {}\
+  static ::testing::TestInfo* const test_info_;\
+  GTEST_DISALLOW_COPY_AND_ASSIGN_(\
+      GTEST_TEST_CLASS_NAME_(test_case_name, test_name));\
+};\
+\
+::testing::TestInfo* const GTEST_TEST_CLASS_NAME_(test_case_name, test_name)\
+  ::test_info_ =\
+    ::testing::internal::MakeAndRegisterTestInfo(\
+        #test_case_name, #test_name, "", "", \
+        (parent_id), \
+        parent_class::SetUpTestCase, \
+        parent_class::TearDownTestCase, \
+        new ::testing::internal::TestFactoryImpl<\
+            GTEST_TEST_CLASS_NAME_(test_case_name, test_name)>);\
+void GTEST_TEST_CLASS_NAME_(test_case_name, test_name)::RunTestOnMainThread()
+
+#define IN_PROC_BROWSER_TEST_F(test_fixture, test_name)\
+  IN_PROC_BROWSER_TEST_(test_fixture, test_name, test_fixture,\
+                    ::testing::internal::GetTypeId<test_fixture>())
+
+#endif  // CHROME_TEST_IN_PROCESS_BROWSER_TEST_H_
diff --git a/chrome/test/ui_test_utils.cc b/chrome/test/ui_test_utils.cc
new file mode 100644
index 0000000..07016f7
--- /dev/null
+++ b/chrome/test/ui_test_utils.cc
@@ -0,0 +1,77 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/test/ui_test_utils.h"
+
+#include "base/message_loop.h"
+#include "chrome/browser/browser.h"
+#include "chrome/browser/tab_contents/navigation_controller.h"
+#include "chrome/browser/tab_contents/web_contents.h"
+#include "chrome/common/notification_registrar.h"
+#include "chrome/common/notification_service.h"
+#include "googleurl/src/gurl.h"
+
+namespace ui_test_utils {
+
+namespace {
+
+// Used to block until a navigation completes.
+class NavigationNotificationObserver : public NotificationObserver {
+ public:
+  explicit NavigationNotificationObserver(NavigationController* controller)
+      : navigation_started_(false) {
+    registrar_.Add(this, NOTIFY_NAV_ENTRY_COMMITTED,
+                   Source<NavigationController>(controller));
+    registrar_.Add(this, NOTIFY_LOAD_START,
+                   Source<NavigationController>(controller));
+    registrar_.Add(this, NOTIFY_LOAD_STOP,
+                   Source<NavigationController>(controller));
+    RunMessageLoop();
+  }
+
+  virtual void Observe(NotificationType type,
+                       const NotificationSource& source,
+                       const NotificationDetails& details) {
+    if (type == NOTIFY_NAV_ENTRY_COMMITTED || type == NOTIFY_LOAD_START) {
+      navigation_started_ = true;
+    } else if (type == NOTIFY_LOAD_STOP) {
+      if (navigation_started_) {
+        navigation_started_ = false;
+        MessageLoopForUI::current()->Quit();
+      }
+    }
+  }
+
+ private:
+  NotificationRegistrar registrar_;
+
+  // If true the navigation has started.
+  bool navigation_started_;
+
+  DISALLOW_COPY_AND_ASSIGN(NavigationNotificationObserver);
+};
+
+}  // namespace 
+
+void RunMessageLoop() {
+  MessageLoopForUI* loop = MessageLoopForUI::current();
+  bool did_allow_task_nesting = loop->NestableTasksAllowed();
+  loop->SetNestableTasksAllowed(true);
+  loop->Run(NULL);
+  loop->SetNestableTasksAllowed(did_allow_task_nesting);
+}
+
+void WaitForNavigation(NavigationController* controller) {
+  NavigationNotificationObserver observer(controller);
+}
+
+void NavigateToURL(Browser* browser, const GURL& url) {
+  NavigationController* controller =
+      browser->GetSelectedTabContents()->controller();
+  browser->OpenURLFromTab(browser->GetSelectedTabContents(), url, GURL(),
+                          CURRENT_TAB, PageTransition::TYPED);
+  WaitForNavigation(controller);
+}
+
+}  // namespace ui_test_utils
diff --git a/chrome/test/ui_test_utils.h b/chrome/test/ui_test_utils.h
new file mode 100644
index 0000000..f0433b0
--- /dev/null
+++ b/chrome/test/ui_test_utils.h
@@ -0,0 +1,30 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_TEST_UI_TEST_UTILS_H_
+#define CHROME_TEST_UI_TEST_UTILS_H_
+
+class Browser;
+class GURL;
+class NavigationController;
+
+// A collections of functions designed for use with InProcessBrowserTest.
+namespace ui_test_utils {
+
+// Turns on nestable tasks, runs the message loop, then resets nestable tasks
+// to what they were originally. Prefer this over MessageLoop::Run for in
+// process browser tests that need to block until a condition is met.
+void RunMessageLoop();
+
+// Waits for |controller| to complete a navigation. This blocks until
+// the navigation finishes.
+void WaitForNavigation(NavigationController* controller);
+
+// Navigates the selected tab of |browser| to |url|, blocking until the
+// navigation finishes.
+void NavigateToURL(Browser* browser, const GURL& url);
+
+}
+
+#endif  // CHROME_TEST_UI_TEST_UTILS_H_
diff --git a/chrome/test/unit/unittests.vcproj b/chrome/test/unit/unittests.vcproj
index d5d5e954..eafd8c4 100644
--- a/chrome/test/unit/unittests.vcproj
+++ b/chrome/test/unit/unittests.vcproj
@@ -159,6 +159,14 @@
 				>
 			</File>
 			<File
+				RelativePath="..\in_process_browser_test.cc"
+				>
+			</File>
+			<File
+				RelativePath="..\in_process_browser_test.h"
+				>
+			</File>
+			<File
 				RelativePath="..\..\common\ipc_test_sink.cc"
 				>
 			</File>
@@ -207,6 +215,14 @@
 				>
 			</File>
 			<File
+				RelativePath="..\ui_test_utils.cc"
+				>
+			</File>
+			<File
+				RelativePath="..\ui_test_utils.h"
+				>
+			</File>
+			<File
 				RelativePath="..\..\..\net\url_request\url_request_test_job.cc"
 				>
 			</File>
@@ -367,6 +383,10 @@
 			Name="browser"
 			>
 			<File
+				RelativePath="..\..\browser\views\find_bar_win_unittest.cc"
+				>
+			</File>
+			<File
 				RelativePath="..\..\browser\autocomplete\autocomplete_unittest.cc"
 				>
 			</File>