diff options
-rw-r--r-- | base/command_line.h | 10 | ||||
-rw-r--r-- | chrome/browser/browser_main.cc | 44 | ||||
-rw-r--r-- | chrome/browser/views/find_bar_win_uitest.cc | 52 | ||||
-rw-r--r-- | chrome/browser/views/find_bar_win_unittest.cc | 143 | ||||
-rw-r--r-- | chrome/common/main_function_params.h | 7 | ||||
-rw-r--r-- | chrome/test/in_process_browser_test.cc | 179 | ||||
-rw-r--r-- | chrome/test/in_process_browser_test.h | 125 | ||||
-rw-r--r-- | chrome/test/ui_test_utils.cc | 77 | ||||
-rw-r--r-- | chrome/test/ui_test_utils.h | 30 | ||||
-rw-r--r-- | chrome/test/unit/unittests.vcproj | 20 |
10 files changed, 621 insertions, 66 deletions
diff --git a/base/command_line.h b/base/command_line.h index f1a2005..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 @@ class CommandLine { 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..5d2b149 100644 --- a/chrome/browser/browser_main.cc +++ b/chrome/browser/browser_main.cc @@ -175,6 +175,14 @@ StringPiece NetResourceProvider(int key) { } #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 @@ int BrowserMain(const MainFunctionParams& parameters) { } #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 @@ int BrowserMain(const MainFunctionParams& parameters) { // 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 @@ int BrowserMain(const MainFunctionParams& parameters) { 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 @@ int BrowserMain(const MainFunctionParams& parameters) { 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..607aa14 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 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"; -// 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 d5d5e95..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> |