diff options
-rw-r--r-- | chrome/browser/extensions/extension_uitest.cc | 409 | ||||
-rw-r--r-- | chrome/test/automation/automation_proxy_uitest.cc | 305 | ||||
-rw-r--r-- | chrome/test/automation/automation_proxy_uitest.h | 172 |
3 files changed, 275 insertions, 611 deletions
diff --git a/chrome/browser/extensions/extension_uitest.cc b/chrome/browser/extensions/extension_uitest.cc index 42e2459..83eff72 100644 --- a/chrome/browser/extensions/extension_uitest.cc +++ b/chrome/browser/extensions/extension_uitest.cc @@ -15,10 +15,23 @@ #include "chrome/test/automation/automation_proxy_uitest.h" #include "chrome/test/automation/tab_proxy.h" #include "chrome/test/ui/ui_test.h" +#include "chrome/test/ui_test_utils.h" #include "googleurl/src/gurl.h" +#define GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +#include "testing/gmock_mutant.h" namespace { +using testing::_; +using testing::CreateFunctor; +using testing::DoAll; +using testing::Invoke; +using testing::InvokeWithoutArgs; +using testing::SaveArg; +using testing::WithArgs; + +using ui_test_utils::TimedMessageLoopRunner; + static const char kTestDirectorySimpleApiCall[] = "extensions/uitest/simple_api_call"; static const char kTestDirectoryRoundtripApiCall[] = @@ -26,6 +39,9 @@ static const char kTestDirectoryRoundtripApiCall[] = static const char kTestDirectoryBrowserEvent[] = "extensions/uitest/event_sink"; +// TODO(port) Once external tab stuff is ported. +#if defined(OS_WIN) + // Base class to test extensions almost end-to-end by including browser // startup, manifest parsing, and the actual process model in the // equation. This would also let you write UITests that test individual @@ -37,10 +53,10 @@ static const char kTestDirectoryBrowserEvent[] = // By default, makes Chrome forward all Chrome Extension API function calls // via the automation interface. To override this, call set_functions_enabled() // with a list of function names that should be forwarded, -template <class ParentTestType> -class ExtensionUITest : public ParentTestType { +class ExtensionUITest : public ExternalTabUITest { public: - explicit ExtensionUITest(const std::string& extension_path) { + explicit ExtensionUITest(const std::string& extension_path) + : loop_(MessageLoop::current()) { FilePath filename(test_data_directory_); filename = filename.AppendASCII(extension_path); launch_arguments_.AppendSwitchWithValue(switches::kLoadExtension, @@ -54,76 +70,41 @@ class ExtensionUITest : public ParentTestType { } void SetUp() { - ParentTestType::SetUp(); - - AutomationProxyForExternalTab* proxy = - static_cast<AutomationProxyForExternalTab*>(automation()); - HWND external_tab_container = NULL; - HWND tab_wnd = NULL; - tab_ = proxy->CreateTabWithHostWindow(false, - GURL(), &external_tab_container, &tab_wnd); - + ExternalTabUITest::SetUp(); + tab_ = mock_->CreateTabWithUrl(GURL()); tab_->SetEnableExtensionAutomation(functions_enabled_); } void TearDown() { tab_->SetEnableExtensionAutomation(std::vector<std::string>()); - - AutomationProxyForExternalTab* proxy = - static_cast<AutomationProxyForExternalTab*>(automation()); - proxy->DestroyHostWindow(); - proxy->WaitForTabCleanup(tab_, action_max_timeout_ms()); - EXPECT_FALSE(tab_->is_valid()); - tab_.release(); - - ParentTestType::TearDown(); - } - - void TestWithURL(const GURL& url) { - EXPECT_TRUE(tab_->is_valid()); - if (tab_) { - AutomationProxyForExternalTab* proxy = - static_cast<AutomationProxyForExternalTab*>(automation()); - - // Enter a message loop to allow the tab to be created - proxy->WaitForNavigation(2000); - DoAdditionalPreNavigateSetup(tab_.get()); - - // We explicitly do not make this a toolstrip in the extension manifest, - // so that the test can control when it gets loaded, and so that we test - // the intended behavior that tabs should be able to show extension pages - // (useful for development etc.) - tab_->NavigateInExternalTab(url, GURL()); - EXPECT_TRUE(proxy->WaitForMessage(action_max_timeout_ms())); - } - } - - // Override if you need additional stuff before we navigate the page. - virtual void DoAdditionalPreNavigateSetup(TabProxy* tab) { + tab_ = NULL; + EXPECT_TRUE(mock_->HostWindowExists()) << + "You shouldn't DestroyHostWindow yourself, or extension automation " + "won't be correctly reset. Just exit your message loop."; + mock_->DestroyHostWindow(); + ExternalTabUITest::TearDown(); } protected: // Extension API functions that we want to take over. Defaults to all. std::vector<std::string> functions_enabled_; + + // Message loop for running the async bits of your test. + TimedMessageLoopRunner loop_; + + // The external tab. scoped_refptr<TabProxy> tab_; private: DISALLOW_COPY_AND_ASSIGN(ExtensionUITest); }; -// For tests that only need to check for a single postMessage -// being received from the tab in Chrome. These tests can send a message -// to the tab before receiving the new message, but there will not be -// a chance to respond by sending a message from the test to the tab after -// the postMessage is received. -typedef ExtensionUITest<ExternalTabTestType> SingleMessageExtensionUITest; - // A test that loads a basic extension that makes an API call that does // not require a response. -class SimpleApiCallExtensionTest : public SingleMessageExtensionUITest { +class ExtensionTestSimpleApiCall : public ExtensionUITest { public: - SimpleApiCallExtensionTest() - : SingleMessageExtensionUITest(kTestDirectorySimpleApiCall) { + ExtensionTestSimpleApiCall() + : ExtensionUITest(kTestDirectorySimpleApiCall) { } void SetUp() { @@ -132,30 +113,37 @@ class SimpleApiCallExtensionTest : public SingleMessageExtensionUITest { // universal forwarding. functions_enabled_.clear(); functions_enabled_.push_back("tabs.remove"); - SingleMessageExtensionUITest::SetUp(); + ExtensionUITest::SetUp(); } private: - DISALLOW_COPY_AND_ASSIGN(SimpleApiCallExtensionTest); + DISALLOW_COPY_AND_ASSIGN(ExtensionTestSimpleApiCall); }; -// TODO(port) Should become portable once ExternalTabMessageLoop is ported. -#if defined(OS_WIN) -TEST_F(SimpleApiCallExtensionTest, RunTest) { +TEST_F(ExtensionTestSimpleApiCall, RunTest) { namespace keys = extension_automation_constants; - TestWithURL(GURL( - "chrome-extension://pmgpglkggjdpkpghhdmbdhababjpcohk/test.html")); - AutomationProxyForExternalTab* proxy = - static_cast<AutomationProxyForExternalTab*>(automation()); - ASSERT_GT(proxy->messages_received(), 0); + ASSERT_THAT(mock_, testing::NotNull()); + EXPECT_CALL(*mock_, OnDidNavigate(_, _)).Times(1); + + std::string message_received; + EXPECT_CALL(*mock_, OnForwardMessageToExternalHost( + _, _, keys::kAutomationOrigin, keys::kAutomationRequestTarget)) + .WillOnce(DoAll( + SaveArg<1>(&message_received), + InvokeWithoutArgs( + CreateFunctor(&loop_, &TimedMessageLoopRunner::Quit)))); - // Using EXPECT_TRUE rather than EXPECT_EQ as the compiler (VC++) isn't - // finding the right match for EqHelper. - EXPECT_TRUE(proxy->origin() == keys::kAutomationOrigin); - EXPECT_TRUE(proxy->target() == keys::kAutomationRequestTarget); + EXPECT_CALL(*mock_, HandleClosed(_)); - scoped_ptr<Value> message_value(base::JSONReader::Read(proxy->message(), + ASSERT_EQ(AUTOMATION_MSG_NAVIGATION_SUCCESS, tab_->NavigateInExternalTab( + GURL("chrome-extension://pmgpglkggjdpkpghhdmbdhababjpcohk/test.html"), + GURL(""))); + + loop_.RunFor(2 * action_max_timeout_ms()); + ASSERT_FALSE(message_received.empty()); + + scoped_ptr<Value> message_value(base::JSONReader::Read(message_received, false)); ASSERT_TRUE(message_value->IsType(Value::TYPE_DICTIONARY)); DictionaryValue* message_dict = @@ -177,83 +165,46 @@ TEST_F(SimpleApiCallExtensionTest, RunTest) { &has_callback)); EXPECT_FALSE(has_callback); } -#endif // defined(OS_WIN) - -// A base class for an automation proxy that checks several messages in -// a row. -class MultiMessageAutomationProxy : public AutomationProxyForExternalTab { - public: - explicit MultiMessageAutomationProxy(int execution_timeout) - : AutomationProxyForExternalTab(execution_timeout) { - } - // Call when testing with the current tab is finished. - void Quit() { - PostQuitMessage(0); - } - - protected: - virtual void OnMessageReceived(const IPC::Message& msg) { - IPC_BEGIN_MESSAGE_MAP(MultiMessageAutomationProxy, msg) - IPC_MESSAGE_HANDLER(AutomationMsg_DidNavigate, - AutomationProxyForExternalTab::OnDidNavigate) - IPC_MESSAGE_HANDLER(AutomationMsg_ForwardMessageToExternalHost, - OnForwardMessageToExternalHost) - IPC_END_MESSAGE_MAP() - } - - void OnForwardMessageToExternalHost(int handle, - const std::string& message, - const std::string& origin, - const std::string& target) { - messages_received_++; - message_ = message; - origin_ = origin; - target_ = target; - HandleMessageFromChrome(); +// A test that loads a basic extension that makes an API call that does +// not require a response. +class ExtensionTestRoundtripApiCall : public ExtensionUITest { +public: + ExtensionTestRoundtripApiCall() + : ExtensionUITest(kTestDirectoryRoundtripApiCall), + messages_received_(0) { } - // Override to do your custom checking and initiate any custom actions - // needed in your particular unit test. - virtual void HandleMessageFromChrome() = 0; -}; - -// This proxy is specific to RoundtripApiCallExtensionTest. -class RoundtripAutomationProxy : public MultiMessageAutomationProxy { - public: - explicit RoundtripAutomationProxy(int execution_timeout) - : MultiMessageAutomationProxy(execution_timeout), - tab_(NULL) { + void SetUp() { + // Set just this one function explicitly to be forwarded, as a test of + // the selective forwarding. The next test will leave the default to test + // universal forwarding. + functions_enabled_.clear(); + functions_enabled_.push_back("tabs.getSelected"); + functions_enabled_.push_back("tabs.remove"); + ExtensionUITest::SetUp(); } - // Must set before initiating test. - TabProxy* tab_; - - protected: - virtual void HandleMessageFromChrome() { + void CheckAndSendResponse(const std::string& message) { namespace keys = extension_automation_constants; + ++messages_received_; ASSERT_TRUE(tab_ != NULL); ASSERT_TRUE(messages_received_ == 1 || messages_received_ == 2); - // Using EXPECT_TRUE rather than EXPECT_EQ as the compiler (VC++) isn't - // finding the right match for EqHelper. - EXPECT_TRUE(origin_ == keys::kAutomationOrigin); - EXPECT_TRUE(target_ == keys::kAutomationRequestTarget); - - scoped_ptr<Value> message_value(base::JSONReader::Read(message_, false)); + scoped_ptr<Value> message_value(base::JSONReader::Read(message, false)); ASSERT_TRUE(message_value->IsType(Value::TYPE_DICTIONARY)); DictionaryValue* request_dict = - static_cast<DictionaryValue*>(message_value.get()); + static_cast<DictionaryValue*>(message_value.get()); std::string function_name; ASSERT_TRUE(request_dict->GetString(keys::kAutomationNameKey, - &function_name)); + &function_name)); int request_id = -2; EXPECT_TRUE(request_dict->GetInteger(keys::kAutomationRequestIdKey, - &request_id)); + &request_id)); bool has_callback = false; EXPECT_TRUE(request_dict->GetBoolean(keys::kAutomationHasCallbackKey, - &has_callback)); + &has_callback)); if (messages_received_ == 1) { EXPECT_EQ(function_name, "tabs.getSelected"); @@ -268,7 +219,7 @@ class RoundtripAutomationProxy : public MultiMessageAutomationProxy { tab_dict.SetInteger(extension_tabs_module_constants::kWindowIdKey, 1); tab_dict.SetBoolean(extension_tabs_module_constants::kSelectedKey, true); tab_dict.SetString(extension_tabs_module_constants::kUrlKey, - "http://www.google.com"); + "http://www.google.com"); std::string tab_json; base::JSONWriter::Write(&tab_dict, false, &tab_json); @@ -279,9 +230,9 @@ class RoundtripAutomationProxy : public MultiMessageAutomationProxy { base::JSONWriter::Write(&response_dict, false, &response_json); tab_->HandleMessageFromExternalHost( - response_json, - keys::kAutomationOrigin, - keys::kAutomationResponseTarget); + response_json, + keys::kAutomationOrigin, + keys::kAutomationResponseTarget); } else if (messages_received_ == 2) { EXPECT_EQ(function_name, "tabs.remove"); EXPECT_FALSE(has_callback); @@ -289,76 +240,92 @@ class RoundtripAutomationProxy : public MultiMessageAutomationProxy { std::string args; EXPECT_TRUE(request_dict->GetString(keys::kAutomationArgsKey, &args)); EXPECT_NE(args.find("42"), -1); - - Quit(); + loop_.Quit(); } else { - Quit(); FAIL(); + loop_.Quit(); } } + +private: + int messages_received_; + DISALLOW_COPY_AND_ASSIGN(ExtensionTestRoundtripApiCall); }; -class RoundtripApiCallExtensionTest - : public ExtensionUITest< - CustomAutomationProxyTest<RoundtripAutomationProxy>> { +TEST_F(ExtensionTestRoundtripApiCall, RunTest) { + namespace keys = extension_automation_constants; + + ASSERT_THAT(mock_, testing::NotNull()); + EXPECT_CALL(*mock_, OnDidNavigate(_, _)).Times(1); + + EXPECT_CALL(*mock_, OnForwardMessageToExternalHost( + _, _, keys::kAutomationOrigin, keys::kAutomationRequestTarget)) + .Times(2) + .WillRepeatedly(WithArgs<1>(Invoke( + CreateFunctor(this, + &ExtensionTestRoundtripApiCall::CheckAndSendResponse)))); + + EXPECT_CALL(*mock_, HandleClosed(_)); + + ASSERT_EQ(AUTOMATION_MSG_NAVIGATION_SUCCESS, tab_->NavigateInExternalTab( + GURL("chrome-extension://ofoknjclcmghjfmbncljcnpjmfmldhno/test.html"), + GURL(""))); + + // CheckAndSendResponse (called by OnForwardMessageToExternalHost) + // will end the loop once it has received both of our expected messages. + loop_.RunFor(2 * action_max_timeout_ms()); +} + +class ExtensionTestBrowserEvents : public ExtensionUITest { public: - RoundtripApiCallExtensionTest() - : ExtensionUITest< - CustomAutomationProxyTest< - RoundtripAutomationProxy> >(kTestDirectoryRoundtripApiCall) { + ExtensionTestBrowserEvents() + : ExtensionUITest(kTestDirectoryBrowserEvent), + response_count_(0) { } - void DoAdditionalPreNavigateSetup(TabProxy* tab) { - RoundtripAutomationProxy* proxy = - static_cast<RoundtripAutomationProxy*>(automation()); - proxy->tab_ = tab; + void SetUp() { + // Set just this one function explicitly to be forwarded, as a test of + // the selective forwarding. The next test will leave the default to test + // universal forwarding. + functions_enabled_.clear(); + functions_enabled_.push_back("windows.getCurrent"); + ExtensionUITest::SetUp(); } - private: - DISALLOW_COPY_AND_ASSIGN(RoundtripApiCallExtensionTest); -}; + // Fire an event of the given name to the test extension. + void FireEvent(const char* event_name) { + namespace keys = extension_automation_constants; -// TODO(port) Should become portable once -// ExternalTabMessageLoop is ported. -#if defined(OS_WIN) -TEST_F(RoundtripApiCallExtensionTest, RunTest) { - TestWithURL(GURL( - "chrome-extension://ofoknjclcmghjfmbncljcnpjmfmldhno/test.html")); - RoundtripAutomationProxy* proxy = - static_cast<RoundtripAutomationProxy*>(automation()); - - // Validation is done in the RoundtripAutomationProxy, so we just check - // something basic here. - EXPECT_EQ(proxy->messages_received(), 2); -} -#endif // defined(OS_WIN) + ASSERT_TRUE(tab_ != NULL); -// This proxy is specific to BrowserEventExtensionTest. -class BrowserEventAutomationProxy : public MultiMessageAutomationProxy { - public: - explicit BrowserEventAutomationProxy(int execution_timeout) - : MultiMessageAutomationProxy(execution_timeout), - tab_(NULL) { + // Build the event message to send to the extension. The only important + // part is the name, as the payload is not used by the test extension. + std::string message; + message += event_name; + + tab_->HandleMessageFromExternalHost( + message, + keys::kAutomationOrigin, + keys::kAutomationBrowserEventRequestTarget); } - // Must set before initiating test. - TabProxy* tab_; + void HandleMessageFromChrome(const std::string& message, + const std::string& target); + protected: // Counts the number of times we got a given event. std::map<std::string, int> event_count_; // Array containing the names of the events to fire to the extension. static const char* events_[]; - protected: - // Process a message received from the test extension. - virtual void HandleMessageFromChrome(); + // Number of events extension has told us it received. + int response_count_; - // Fire an event of the given name to the test extension. - void FireEvent(const char* event_name); + DISALLOW_COPY_AND_ASSIGN(ExtensionTestBrowserEvents); }; -const char* BrowserEventAutomationProxy::events_[] = { +const char* ExtensionTestBrowserEvents::events_[] = { // Window events. "[\"windows.onCreated\", \"[{'id':42,'focused':true,'top':0,'left':0," "'width':100,'height':100}]\"]", @@ -399,16 +366,13 @@ const char* BrowserEventAutomationProxy::events_[] = { "{'childIds':['1', '2', '3']}]\"]" }; -void BrowserEventAutomationProxy::HandleMessageFromChrome() { +void ExtensionTestBrowserEvents::HandleMessageFromChrome( + const std::string& message, + const std::string& target) { namespace keys = extension_automation_constants; ASSERT_TRUE(tab_ != NULL); - std::string message(message()); - std::string origin(origin()); - std::string target(target()); - ASSERT_TRUE(message.length() > 0); - ASSERT_STREQ(keys::kAutomationOrigin, origin.c_str()); if (target == keys::kAutomationRequestTarget) { // This should be a request for the current window. We don't need to @@ -452,72 +416,59 @@ void BrowserEventAutomationProxy::HandleMessageFromChrome() { reinterpret_cast<DictionaryValue*>(message_value.get()); std::string event_name; - ASSERT_TRUE(message_dict->GetString(L"data", &event_name)); + message_dict->GetString(keys::kAutomationMessageDataKey, &event_name); if (event_name == "\"ACK\"") { ASSERT_EQ(0, event_count_.size()); + } else if (event_name.empty()) { + // This must be the post disconnect. + int request_id = -1; + message_dict->GetInteger(keys::kAutomationRequestIdKey, &request_id); + ASSERT_EQ(keys::CHANNEL_CLOSED, request_id); } else { ++event_count_[event_name]; } + + // Check if we're done. + if (event_count_.size() == arraysize(events_)) { + loop_.Quit(); + } } } -void BrowserEventAutomationProxy::FireEvent(const char* event) { +TEST_F(ExtensionTestBrowserEvents, RunTest) { + // This test loads an HTML file that tries to add listeners to a bunch of + // chrome.* events and upon adding a listener it posts the name of the event + // to the automation layer, which we'll count to make sure the events work. namespace keys = extension_automation_constants; - // Build the event message to send to the extension. The only important - // part is the name, as the payload is not used by the test extension. - std::string message; - message += event; + ASSERT_THAT(mock_, testing::NotNull()); + EXPECT_CALL(*mock_, OnDidNavigate(_, _)).Times(1); - tab_->HandleMessageFromExternalHost( - message, - keys::kAutomationOrigin, - keys::kAutomationBrowserEventRequestTarget); -} + EXPECT_CALL(*mock_, OnForwardMessageToExternalHost( + _, _, keys::kAutomationOrigin, _)) + .WillRepeatedly(WithArgs<1, 3>(Invoke( + CreateFunctor(this, + &ExtensionTestBrowserEvents::HandleMessageFromChrome)))); -class BrowserEventExtensionTest - : public ExtensionUITest< - CustomAutomationProxyTest<BrowserEventAutomationProxy>> { - public: - BrowserEventExtensionTest() - : ExtensionUITest< - CustomAutomationProxyTest< - BrowserEventAutomationProxy> >(kTestDirectoryBrowserEvent) { - } - - void DoAdditionalPreNavigateSetup(TabProxy* tab) { - BrowserEventAutomationProxy* proxy = - static_cast<BrowserEventAutomationProxy*>(automation()); - proxy->tab_ = tab; - } + EXPECT_CALL(*mock_, HandleClosed(_)); - private: + ASSERT_EQ(AUTOMATION_MSG_NAVIGATION_SUCCESS, tab_->NavigateInExternalTab( + GURL("chrome-extension://ofoknjclcmghjfmbncljcnpjmfmldhno/test.html"), + GURL(""))); - DISALLOW_COPY_AND_ASSIGN(BrowserEventExtensionTest); -}; - -// TODO(port) Should become portable once -// ExternalTabMessageLoop is ported. -#if defined(OS_WIN) -TEST_F(BrowserEventExtensionTest, RunTest) { - // This test loads an HTML file that tries to add listeners to a bunch of - // chrome.* events and upon adding a listener it posts the name of the event - // to the automation layer, which we'll count to make sure the events work. - TestWithURL(GURL( - "chrome-extension://ofoknjclcmghjfmbncljcnpjmfmldhno/test.html")); - BrowserEventAutomationProxy* proxy = - static_cast<BrowserEventAutomationProxy*>(automation()); + // HandleMessageFromChrome (called by OnForwardMessageToExternalHost) ends + // the loop when we've received the number of response messages we expect. + loop_.RunFor(2 * action_max_timeout_ms()); // If this assert hits and the actual size is 0 then you need to look at: // src\chrome\test\data\extensions\uitest\event_sink\test.html and see if // all the events we are attaching to are valid. Also compare the list against // the event_names_ string array above. - EXPECT_EQ(arraysize(BrowserEventAutomationProxy::events_), - proxy->event_count_.size()); - for (std::map<std::string, int>::iterator i = proxy->event_count_.begin(); - i != proxy->event_count_.end(); ++i) { + ASSERT_EQ(arraysize(events_), event_count_.size()); + for (std::map<std::string, int>::iterator i = event_count_.begin(); + i != event_count_.end(); ++i) { const std::pair<std::string, int>& value = *i; - ASSERT_EQ(1, value.second); + EXPECT_EQ(1, value.second); } } #endif // defined(OS_WIN) diff --git a/chrome/test/automation/automation_proxy_uitest.cc b/chrome/test/automation/automation_proxy_uitest.cc index 4a9dcdd..561173f 100644 --- a/chrome/test/automation/automation_proxy_uitest.cc +++ b/chrome/test/automation/automation_proxy_uitest.cc @@ -29,7 +29,6 @@ #include "chrome/test/ui/ui_test.h" #include "net/base/net_util.h" #include "net/url_request/url_request_unittest.h" -#include "testing/gmock/include/gmock/gmock.h" #define GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING #include "testing/gmock_mutant.h" #include "views/event.h" @@ -653,289 +652,53 @@ TEST_F(AutomationProxyTest, BlockedPopupTest) { // TODO(port): Remove HWND if possible #if defined(OS_WIN) -static const wchar_t class_name[] = L"External_Tab_UI_Test_Class"; -static const wchar_t window_title[] = L"External Tab Tester"; -AutomationProxyForExternalTab::AutomationProxyForExternalTab( - int execution_timeout) +const char simple_data_url[] = + "data:text/html,<html><head><title>External tab test</title></head>" + "<body>A simple page for testing a floating/invisible tab<br></div>" + "</body></html>"; + +ExternalTabUITestMockClient::ExternalTabUITestMockClient(int execution_timeout) : AutomationProxy(execution_timeout), - messages_received_(0), - navigate_complete_(false), - quit_after_(QUIT_INVALID), - host_window_class_(NULL), host_window_(NULL) { } -AutomationProxyForExternalTab::~AutomationProxyForExternalTab() { - DestroyHostWindow(); - UnregisterClassW(host_window_class_, NULL); -} - -gfx::NativeWindow AutomationProxyForExternalTab::CreateHostWindow() { - DCHECK(!IsWindow(host_window_)); - if (!host_window_class_) { - WNDCLASSEX wnd_class = {0}; - wnd_class.cbSize = sizeof(wnd_class); - wnd_class.style = CS_HREDRAW | CS_VREDRAW; - wnd_class.lpfnWndProc = DefWindowProc; - wnd_class.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); - wnd_class.lpszClassName = class_name; - host_window_class_ = reinterpret_cast<const wchar_t*>( - RegisterClassEx(&wnd_class)); - if (!host_window_class_) { - NOTREACHED() << "RegisterClassEx failed. Error: " << GetLastError(); - return false; - } - } - - unsigned long style = WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN; - host_window_ = CreateWindow(host_window_class_, window_title, style, - CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, - NULL, NULL, NULL); - if (!host_window_) { - NOTREACHED() << "CreateWindow failed. Error: " << GetLastError(); - return false; - } - - ShowWindow(host_window_, SW_SHOW); - return host_window_; +void ExternalTabUITestMockClient::ReplyStarted( + const IPC::AutomationURLResponse* response, + int tab_handle, int request_id) { + AutomationProxy::Send(new AutomationMsg_RequestStarted(0, tab_handle, + request_id, *response)); } -scoped_refptr<TabProxy> AutomationProxyForExternalTab::CreateTabWithHostWindow( - bool is_incognito, const GURL& initial_url, - gfx::NativeWindow* container_wnd, gfx::NativeWindow* tab_wnd) { - DCHECK(container_wnd); - DCHECK(tab_wnd); - - CreateHostWindow(); - EXPECT_NE(FALSE, ::IsWindow(host_window_)); - - RECT client_area = {0}; - GetClientRect(host_window_, &client_area); - - const IPC::ExternalTabSettings settings = { - host_window_, - gfx::Rect(client_area), - WS_CHILD | WS_VISIBLE, - is_incognito, - false, - false, - initial_url - }; - - scoped_refptr<TabProxy> tab(CreateExternalTab(settings, container_wnd, - tab_wnd)); - - EXPECT_TRUE(tab != NULL); - EXPECT_NE(FALSE, ::IsWindow(*container_wnd)); - EXPECT_NE(FALSE, ::IsWindow(*tab_wnd)); - return tab; -} - -void AutomationProxyForExternalTab::DestroyHostWindow() { - if (host_window_) { - DestroyWindow(host_window_); - host_window_ = NULL; - } -} - -bool AutomationProxyForExternalTab::WaitForNavigation(int timeout_ms) { - set_quit_after(AutomationProxyForExternalTab::QUIT_AFTER_NAVIGATION); - return RunMessageLoop(timeout_ms, NULL); -} - -bool AutomationProxyForExternalTab::WaitForMessage(int timeout_ms) { - set_quit_after(AutomationProxyForExternalTab::QUIT_AFTER_MESSAGE); - return RunMessageLoop(timeout_ms, NULL); -} - -bool AutomationProxyForExternalTab::WaitForTabCleanup(TabProxy* tab, - int timeout_ms) { - DCHECK(tab); - base::Time end_time = - base::Time::Now() + TimeDelta::FromMilliseconds(timeout_ms); - while (base::Time::Now() < end_time) { - const int kWaitInterval = 50; - DWORD wait_result = MsgWaitForMultipleObjects(0, NULL, FALSE, kWaitInterval, - QS_ALLINPUT); - if (!tab->is_valid()) - break; - if (WAIT_OBJECT_0 == wait_result) { - MSG msg = {0}; - while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { - TranslateMessage(&msg); - DispatchMessage(&msg); - } - } - } - - return !tab->is_valid(); +void ExternalTabUITestMockClient::ReplyData( + const std::string* data, int tab_handle, int request_id) { + AutomationProxy::Send(new AutomationMsg_RequestData(0, tab_handle, + request_id, *data)); } -bool AutomationProxyForExternalTab::RunMessageLoop( - int timeout_ms, - gfx::NativeWindow window_to_monitor) { - // If there's no host window then the abort or this loop will be stuck - // in GetMessage - if (!IsWindow(host_window_)) - return false; - - // Allow the renderers to connect. - const int kTimerIdQuit = 100; - const int kTimerIdProcessPendingMessages = 101; - - if (!window_to_monitor) - window_to_monitor = host_window_; - - UINT_PTR quit_timer = ::SetTimer(host_window_, kTimerIdQuit, - timeout_ms, NULL); - UINT_PTR pump_timer = ::SetTimer(host_window_, - kTimerIdProcessPendingMessages, 50, NULL); - - MSG msg; - bool quit = false; - do { - BOOL ok = ::GetMessage(&msg, NULL, 0, 0); - if (!ok || ok == -1) - break; - - if (msg.message == WM_TIMER && msg.hwnd == host_window_) { - switch (msg.wParam) { - case kTimerIdProcessPendingMessages: - MessageLoop::current()->RunAllPending(); - break; - case kTimerIdQuit: - quit = true; - break; - default: - NOTREACHED() << "invalid timer id"; - break; - } - } else if ((msg.message == WM_QUIT) || (msg.message == kQuitLoopMessage)) { - quit = true; - } else { - ::TranslateMessage(&msg); - ::DispatchMessage(&msg); - } - } while (!quit && ::IsWindow(window_to_monitor)); - - KillTimer(host_window_, quit_timer); - KillTimer(host_window_, pump_timer); - quit_after_ = QUIT_INVALID; - return true; +void ExternalTabUITestMockClient::ReplyEOF(int tab_handle, int request_id) { + AutomationProxy::Send(new AutomationMsg_RequestEnd(0, tab_handle, + request_id, + URLRequestStatus())); } -void AutomationProxyForExternalTab::OnMessageReceived(const IPC::Message& msg) { - IPC_BEGIN_MESSAGE_MAP(AutomationProxyForExternalTab, msg) - IPC_MESSAGE_HANDLER(AutomationMsg_DidNavigate, OnDidNavigate) - IPC_MESSAGE_HANDLER(AutomationMsg_ForwardMessageToExternalHost, - OnForwardMessageToExternalHost) - IPC_END_MESSAGE_MAP() +void ExternalTabUITestMockClient::Reply404(int tab_handle, int request_id) { + const IPC::AutomationURLResponse notfound = {"", "HTTP/1.1 404\r\n\r\n"}; + ReplyStarted(¬found, tab_handle, request_id); + ReplyEOF(tab_handle, request_id); } -void AutomationProxyForExternalTab::OnDidNavigate( - int tab_handle, - const IPC::NavigationInfo& nav_info) { - navigate_complete_ = true; - if (QUIT_AFTER_NAVIGATION == quit_after_) - QuitLoop(); -} +void ExternalTabUITestMockClient::InvalidateHandle( + const IPC::Message& message) { + void* iter = NULL; + int handle; + ASSERT_TRUE(message.ReadInt(&iter, &handle)); -void AutomationProxyForExternalTab::OnForwardMessageToExternalHost( - int handle, - const std::string& message, - const std::string& origin, - const std::string& target) { - messages_received_++; - message_ = message; - origin_ = origin; - target_ = target; - - if (QUIT_AFTER_MESSAGE == quit_after_) - QuitLoop(); + // Call base class + AutomationProxy::InvalidateHandle(message); + HandleClosed(handle); } -const char simple_data_url[] = - "data:text/html,<html><head><title>External tab test</title></head>" - "<body>A simple page for testing a floating/invisible tab<br></div>" - "</body></html>"; - -// We have to derive from AutomationProxy in order to hook up -// OnMessageReceived callbacks. -class ExternalTabUITestMockClient : public AutomationProxy { - public: - explicit ExternalTabUITestMockClient(int execution_timeout) - : AutomationProxy(execution_timeout), - host_window_(NULL) { - } - - MOCK_METHOD2(OnDidNavigate, void(int tab_handle, - const IPC::NavigationInfo& nav_info)); - MOCK_METHOD4(OnForwardMessageToExternalHost, void(int handle, - const std::string& message, const std::string& origin, - const std::string& target)); - MOCK_METHOD3(OnRequestStart, void(int tab_handle, int request_id, - const IPC::AutomationURLRequest& request)); - MOCK_METHOD3(OnRequestRead, void(int tab_handle, int request_id, - int bytes_to_read)); - MOCK_METHOD3(OnRequestEnd, void(int tab_handle, int request_id, - const URLRequestStatus& status)); - MOCK_METHOD3(OnSetCookieAsync, void(int tab_handle, const GURL& url, - const std::string& cookie)); - - - MOCK_METHOD1(HandleClosed, void(int handle)); - - - // Action helpers for OnRequest* incoming messages. Create the message and - // delegate sending to the base class. Apparently we do not have wrappers - // in AutomationProxy for these messages. - void ReplyStarted(const IPC::AutomationURLResponse* response, - int tab_handle, int request_id) { - AutomationProxy::Send(new AutomationMsg_RequestStarted(0, tab_handle, - request_id, *response)); - } - - void ReplyData(const std::string* data, int tab_handle, int request_id) { - AutomationProxy::Send(new AutomationMsg_RequestData(0, tab_handle, - request_id, *data)); - } - - void ReplyEOF(int tab_handle, int request_id) { - AutomationProxy::Send(new AutomationMsg_RequestEnd(0, tab_handle, - request_id, - URLRequestStatus())); - } - - void Reply404(int tab_handle, int request_id) { - const IPC::AutomationURLResponse notfound = {"", "HTTP/1.1 404\r\n\r\n"}; - ReplyStarted(¬found, tab_handle, request_id); - ReplyEOF(tab_handle, request_id); - } - - // Test setup helpers - scoped_refptr<TabProxy> CreateHostWindowAndTab( - const IPC::ExternalTabSettings& settings); - scoped_refptr<TabProxy> CreateTabWithUrl(const GURL& initial_url); - void DestroyHostWindow(); - - static const IPC::ExternalTabSettings default_settings; - protected: - HWND host_window_; - - // Simple dispatcher to above OnXXX methods. - virtual void OnMessageReceived(const IPC::Message& msg); - virtual void InvalidateHandle(const IPC::Message& message) { - void* iter = NULL; - int handle; - ASSERT_TRUE(message.ReadInt(&iter, &handle)); - - // Call base class - AutomationProxy::InvalidateHandle(message); - HandleClosed(handle); - } -}; - // Most of the time we need external tab with these settings. const IPC::ExternalTabSettings ExternalTabUITestMockClient::default_settings = { NULL, gfx::Rect(), // will be replaced by CreateHostWindowAndTab @@ -995,8 +758,14 @@ scoped_refptr<TabProxy> ExternalTabUITestMockClient::CreateTabWithUrl( void ExternalTabUITestMockClient::DestroyHostWindow() { ::DestroyWindow(host_window_); + host_window_ = NULL; } +bool ExternalTabUITestMockClient::HostWindowExists() { + return (host_window_ != NULL) && ::IsWindow(host_window_); +} + + // Handy macro #define QUIT_LOOP(loop) testing::InvokeWithoutArgs(\ CreateFunctor(loop, &TimedMessageLoopRunner::Quit)) diff --git a/chrome/test/automation/automation_proxy_uitest.h b/chrome/test/automation/automation_proxy_uitest.h index 7fa1a2b..0701c47 100644 --- a/chrome/test/automation/automation_proxy_uitest.h +++ b/chrome/test/automation/automation_proxy_uitest.h @@ -14,24 +14,11 @@ #include "chrome/test/automation/automation_proxy.h" #include "chrome/test/ui/ui_test.h" #include "googleurl/src/gurl.h" +#include "testing/gmock/include/gmock/gmock.h" class TabProxy; class ExternalTabUITestMockClient; -class ExternalTabUITest : public UITest { - public: - // Override UITest's CreateAutomationProxy to provide the unit test - // with our special implementation of AutomationProxy. - // This function is called from within UITest::LaunchBrowserAndServer. - virtual AutomationProxy* CreateAutomationProxy(int execution_timeout); - protected: - // Filtered Inet will override automation callbacks for network resources. - virtual bool ShouldFilterInet() { - return false; - } - ExternalTabUITestMockClient* mock_; -}; - // Base class for automation proxy testing. class AutomationProxyVisibleTest : public UITest { protected: @@ -40,114 +27,71 @@ class AutomationProxyVisibleTest : public UITest { } }; -// Automation proxy UITest that allows tests to override the automation -// proxy used by the UITest base class. -template <class AutomationProxyClass> -class CustomAutomationProxyTest : public AutomationProxyVisibleTest { - protected: - CustomAutomationProxyTest() { - } - - // Override UITest's CreateAutomationProxy to provide our the unit test - // with our special implementation of AutomationProxy. - // This function is called from within UITest::LaunchBrowserAndServer. - virtual AutomationProxy* CreateAutomationProxy(int execution_timeout) { - AutomationProxyClass* proxy = new AutomationProxyClass(execution_timeout); - return proxy; - } -}; - -// A single-use AutomationProxy implementation that's good -// for a single navigation and a single ForwardMessageToExternalHost -// message. Once the ForwardMessageToExternalHost message is received -// the class posts a quit message to the thread on which the message -// was received. -class AutomationProxyForExternalTab : public AutomationProxy { +// Used to implement external tab UI tests. +// +// We have to derive from AutomationProxy in order to hook up +// OnMessageReceived callbacks. +class ExternalTabUITestMockClient : public AutomationProxy { public: - // Allows us to reuse this mock for multiple tests. This is done - // by setting a state to trigger posting of Quit message to the - // wait loop. - enum QuitAfter { - QUIT_INVALID, - QUIT_AFTER_NAVIGATION, - QUIT_AFTER_MESSAGE, - }; - - explicit AutomationProxyForExternalTab(int execution_timeout); - ~AutomationProxyForExternalTab(); - - int messages_received() const { - return messages_received_; - } - - const std::string& message() const { - return message_; - } - - const std::string& origin() const { - return origin_; - } - - const std::string& target() const { - return target_; - } - - // Creates and sisplays a top-level window, that can be used as a parent - // to the external tab.window. - gfx::NativeWindow CreateHostWindow(); - scoped_refptr<TabProxy> CreateTabWithHostWindow(bool is_incognito, - const GURL& initial_url, gfx::NativeWindow* container_wnd, - gfx::NativeWindow* tab_wnd); + explicit ExternalTabUITestMockClient(int execution_timeout); + + MOCK_METHOD2(OnDidNavigate, void(int tab_handle, + const IPC::NavigationInfo& nav_info)); + MOCK_METHOD4(OnForwardMessageToExternalHost, void(int handle, + const std::string& message, const std::string& origin, + const std::string& target)); + MOCK_METHOD3(OnRequestStart, void(int tab_handle, int request_id, + const IPC::AutomationURLRequest& request)); + MOCK_METHOD3(OnRequestRead, void(int tab_handle, int request_id, + int bytes_to_read)); + MOCK_METHOD3(OnRequestEnd, void(int tab_handle, int request_id, + const URLRequestStatus& status)); + MOCK_METHOD3(OnSetCookieAsync, void(int tab_handle, const GURL& url, + const std::string& cookie)); + + MOCK_METHOD1(HandleClosed, void(int handle)); + + // Action helpers for OnRequest* incoming messages. Create the message and + // delegate sending to the base class. Apparently we do not have wrappers + // in AutomationProxy for these messages. + void ReplyStarted(const IPC::AutomationURLResponse* response, + int tab_handle, int request_id); + void ReplyData(const std::string* data, int tab_handle, int request_id); + void ReplyEOF(int tab_handle, int request_id); + void Reply404(int tab_handle, int request_id); + + // Test setup helpers + scoped_refptr<TabProxy> CreateHostWindowAndTab( + const IPC::ExternalTabSettings& settings); + scoped_refptr<TabProxy> CreateTabWithUrl(const GURL& initial_url); + + // Destroys the host window. void DestroyHostWindow(); + // Returns true if the host window exists. + bool HostWindowExists(); - // Wait for the event to happen or timeout - bool WaitForNavigation(int timeout_ms); - bool WaitForMessage(int timeout_ms); - bool WaitForTabCleanup(TabProxy* tab, int timeout_ms); - - // Enters a message loop that processes window messages as well - // as calling MessageLoop::current()->RunAllPending() to process any - // incoming IPC messages. The timeout_ms parameter is the maximum - // time the loop will run. To end the loop earlier, post a quit message to - // the thread. - bool RunMessageLoop(int timeout_ms, gfx::NativeWindow window_to_monitor); - + static const IPC::ExternalTabSettings default_settings; protected: -#if defined(OS_WIN) - static const int kQuitLoopMessage = WM_APP + 11; - // Quit the message loop - void QuitLoop() { - DCHECK(IsWindow(host_window_)); - // We could post WM_QUIT but lets keep it out of accidental usage - // by anyone else peeking it. - PostMessage(host_window_, kQuitLoopMessage, 0, 0); - } -#endif // defined(OS_WIN) - - // Internal state to flag posting of a quit message to the loop - void set_quit_after(QuitAfter q) { - quit_after_ = q; - } + gfx::NativeWindow host_window_; + // Simple dispatcher to above OnXXX methods. virtual void OnMessageReceived(const IPC::Message& msg); + virtual void InvalidateHandle(const IPC::Message& message); +}; - void OnDidNavigate(int tab_handle, const IPC::NavigationInfo& nav_info); - void OnForwardMessageToExternalHost(int handle, - const std::string& message, - const std::string& origin, - const std::string& target); - +// Base your external tab UI tests on this. +class ExternalTabUITest : public UITest { + public: + // Override UITest's CreateAutomationProxy to provide the unit test + // with our special implementation of AutomationProxy. + // This function is called from within UITest::LaunchBrowserAndServer. + virtual AutomationProxy* CreateAutomationProxy(int execution_timeout); protected: - bool navigate_complete_; - int messages_received_; - std::string message_, origin_, target_; - QuitAfter quit_after_; - const wchar_t* host_window_class_; - gfx::NativeWindow host_window_; + // Filtered Inet will override automation callbacks for network resources. + virtual bool ShouldFilterInet() { + return false; + } + ExternalTabUITestMockClient* mock_; }; -// A test harness for testing external tabs. -typedef CustomAutomationProxyTest<AutomationProxyForExternalTab> - ExternalTabTestType; - #endif // CHROME_TEST_AUTOMATION_AUTOMATION_PROXY_UITEST_H_ |