diff options
author | aa@chromium.org <aa@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-05-15 22:58:33 +0000 |
---|---|---|
committer | aa@chromium.org <aa@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-05-15 22:58:33 +0000 |
commit | b83e4600fc1c2f1c42598f8d89dbd36d9415309d (patch) | |
tree | 4b2ee497813a59a91105f91f6a5329224da18eac /chrome/browser/extensions/extension_uitest.cc | |
parent | a9a2668386addfa40ff262005d396468f54b99e2 (diff) | |
download | chromium_src-b83e4600fc1c2f1c42598f8d89dbd36d9415309d.zip chromium_src-b83e4600fc1c2f1c42598f8d89dbd36d9415309d.tar.gz chromium_src-b83e4600fc1c2f1c42598f8d89dbd36d9415309d.tar.bz2 |
First step to enable end-to-end testing of extensions through the
automation interface. This adds a method to turn on automation of
extension API functions, plumbing that redirects API requests
through the automation interface when appropriate, and a couple
of UITests that exercise the functionality.
See http://codereview.chromium.org/113277 for the original review.
Review URL: http://codereview.chromium.org/115427
Patch from Joi Sigurdsson <joi.sigurdsson@gmail.com>.
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@16207 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/extensions/extension_uitest.cc')
-rw-r--r-- | chrome/browser/extensions/extension_uitest.cc | 282 |
1 files changed, 282 insertions, 0 deletions
diff --git a/chrome/browser/extensions/extension_uitest.cc b/chrome/browser/extensions/extension_uitest.cc new file mode 100644 index 0000000..da075ca --- /dev/null +++ b/chrome/browser/extensions/extension_uitest.cc @@ -0,0 +1,282 @@ +// Copyright (c) 2009 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/command_line.h" +#include "base/gfx/rect.h" +#include "base/json_reader.h" +#include "base/json_writer.h" +#include "base/values.h" +#include "chrome/common/chrome_switches.h" +#include "chrome/test/automation/automation_proxy_uitest.h" +#include "chrome/test/automation/extension_automation_constants.h" +#include "chrome/test/automation/tab_proxy.h" +#include "chrome/test/ui/ui_test.h" +#include "googleurl/src/gurl.h" + +namespace { + +static const char kTestDirectorySimpleApiCall[] = + "extensions/uitest/simple_api_call"; +static const char kTestDirectoryRoundtripApiCall[] = + "extensions/uitest/roundtrip_api_call"; + +// 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 +// Chrome Extensions as running in Chrome. Takes over implementation of +// extension API calls so that behavior can be tested deterministically +// through code, instead of having to contort the browser into a state +// suitable for testing. +template <class ParentTestType> +class ExtensionUITest : public ParentTestType { + public: + explicit ExtensionUITest(const std::string& extension_path) { + launch_arguments_.AppendSwitch(switches::kEnableExtensions); + + FilePath filename(test_data_directory_); + filename = filename.AppendASCII(extension_path); + launch_arguments_.AppendSwitchWithValue(switches::kLoadExtension, + filename.value()); + } + + void SetUp() { + ParentTestType::SetUp(); + automation()->SetEnableExtensionAutomation(true); + } + + void TearDown() { + automation()->SetEnableExtensionAutomation(false); + ParentTestType::TearDown(); + } + + void TestWithURL(const GURL& url) { + HWND external_tab_container = NULL; + scoped_ptr<TabProxy> tab(automation()->CreateExternalTab(NULL, gfx::Rect(), + WS_POPUP, false, &external_tab_container)); + ASSERT_TRUE(tab != NULL); + ASSERT_NE(FALSE, ::IsWindow(external_tab_container)); + 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); + EXPECT_EQ(true, ExternalTabMessageLoop(external_tab_container, 5000)); + // Since the tab goes away lazily, wait a bit. + PlatformThread::Sleep(1000); + EXPECT_FALSE(tab->is_valid()); + } + + // Override if you need additional stuff before we navigate the page. + virtual void DoAdditionalPreNavigateSetup(TabProxy* tab) { + } + + private: + DISALLOW_EVIL_CONSTRUCTORS(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 { + public: + SimpleApiCallExtensionTest() + : SingleMessageExtensionUITest(kTestDirectorySimpleApiCall) { + } + + private: + DISALLOW_COPY_AND_ASSIGN(SimpleApiCallExtensionTest); +}; + +// TODO(port) Should become portable once ExternalTabMessageLoop is ported. +#if defined(OS_WIN) +TEST_F(SimpleApiCallExtensionTest, RunTest) { + namespace keys = extension_automation_constants; + + TestWithURL(GURL( + "chrome-extension://77774444789ABCDEF0123456789ABCDEF0123456/test.html")); + AutomationProxyForExternalTab* proxy = + static_cast<AutomationProxyForExternalTab*>(automation()); + ASSERT_GT(proxy->messages_received(), 0); + + // 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); + + scoped_ptr<Value> message_value(JSONReader::Read(proxy->message(), false)); + ASSERT_TRUE(message_value->IsType(Value::TYPE_DICTIONARY)); + DictionaryValue* message_dict = + reinterpret_cast<DictionaryValue*>(message_value.get()); + std::string result; + message_dict->GetString(keys::kAutomationNameKey, &result); + EXPECT_EQ(result, "RemoveTab"); + + result = ""; + message_dict->GetString(keys::kAutomationArgsKey, &result); + EXPECT_NE(result, ""); + + int callback_id = 0xBAADF00D; + message_dict->GetInteger(keys::kAutomationRequestIdKey, &callback_id); + EXPECT_NE(callback_id, 0xBAADF00D); + + bool has_callback = true; + EXPECT_TRUE(message_dict->GetBoolean(keys::kAutomationHasCallbackKey, + &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(); + } + + // 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) { + } + + // Must set before initiating test. + TabProxy* tab_; + + protected: + virtual void HandleMessageFromChrome() { + namespace keys = extension_automation_constants; + + 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(JSONReader::Read(message_, false)); + ASSERT_TRUE(message_value->IsType(Value::TYPE_DICTIONARY)); + DictionaryValue* request_dict = + static_cast<DictionaryValue*>(message_value.get()); + std::string function_name; + ASSERT_TRUE(request_dict->GetString(keys::kAutomationNameKey, + &function_name)); + int request_id = -2; + EXPECT_TRUE(request_dict->GetInteger(keys::kAutomationRequestIdKey, + &request_id)); + bool has_callback = false; + EXPECT_TRUE(request_dict->GetBoolean(keys::kAutomationHasCallbackKey, + &has_callback)); + + if (messages_received_ == 1) { + EXPECT_EQ(function_name, "GetLastFocusedWindow"); + EXPECT_GE(request_id, 0); + EXPECT_TRUE(has_callback); + + DictionaryValue response_dict; + EXPECT_TRUE(response_dict.SetInteger(keys::kAutomationRequestIdKey, + request_id)); + EXPECT_TRUE(response_dict.SetString(keys::kAutomationResponseKey, "42")); + + std::string response_json; + JSONWriter::Write(&response_dict, false, &response_json); + + tab_->HandleMessageFromExternalHost( + response_json, + keys::kAutomationOrigin, + keys::kAutomationResponseTarget); + } else if (messages_received_ == 2) { + EXPECT_EQ(function_name, "RemoveTab"); + EXPECT_FALSE(has_callback); + + std::string args; + EXPECT_TRUE(request_dict->GetString(keys::kAutomationArgsKey, &args)); + EXPECT_NE(args.find("42"), -1); + + Quit(); + } else { + Quit(); + FAIL(); + } + } +}; + +class RoundtripApiCallExtensionTest + : public ExtensionUITest< + CustomAutomationProxyTest<RoundtripAutomationProxy>> { + public: + RoundtripApiCallExtensionTest() + : ExtensionUITest< + CustomAutomationProxyTest< + RoundtripAutomationProxy> >(kTestDirectoryRoundtripApiCall) { + } + + void DoAdditionalPreNavigateSetup(TabProxy* tab) { + RoundtripAutomationProxy* proxy = + static_cast<RoundtripAutomationProxy*>(automation()); + proxy->tab_ = tab; + } + + private: + DISALLOW_COPY_AND_ASSIGN(RoundtripApiCallExtensionTest); +}; + +// TODO(port) Should become portable once +// ExternalTabMessageLoop is ported. +#if defined(OS_WIN) +TEST_F(RoundtripApiCallExtensionTest, RunTest) { + TestWithURL(GURL( + "chrome-extension://66664444789ABCDEF0123456789ABCDEF0123456/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) + +} // namespace |