| OLD | NEW |
| 1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "base/command_line.h" | 5 #include "base/command_line.h" |
| 6 #include "base/gfx/rect.h" | 6 #include "base/gfx/rect.h" |
| 7 #include "base/json/json_reader.h" | 7 #include "base/json/json_reader.h" |
| 8 #include "base/json/json_writer.h" | 8 #include "base/json/json_writer.h" |
| 9 #include "base/values.h" | 9 #include "base/values.h" |
| 10 #include "chrome/browser/automation/extension_automation_constants.h" | 10 #include "chrome/browser/automation/extension_automation_constants.h" |
| 11 #include "chrome/browser/extensions/extension_tabs_module_constants.h" | 11 #include "chrome/browser/extensions/extension_tabs_module_constants.h" |
| 12 #include "chrome/common/chrome_switches.h" | 12 #include "chrome/common/chrome_switches.h" |
| 13 #include "chrome/common/extensions/extension.h" | 13 #include "chrome/common/extensions/extension.h" |
| 14 #include "chrome/test/automation/automation_messages.h" | 14 #include "chrome/test/automation/automation_messages.h" |
| 15 #include "chrome/test/automation/automation_proxy_uitest.h" | 15 #include "chrome/test/automation/automation_proxy_uitest.h" |
| 16 #include "chrome/test/automation/tab_proxy.h" | 16 #include "chrome/test/automation/tab_proxy.h" |
| 17 #include "chrome/test/ui/ui_test.h" | 17 #include "chrome/test/ui/ui_test.h" |
| 18 #include "chrome/test/ui_test_utils.h" |
| 18 #include "googleurl/src/gurl.h" | 19 #include "googleurl/src/gurl.h" |
| 20 #define GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING |
| 21 #include "testing/gmock_mutant.h" |
| 19 | 22 |
| 20 namespace { | 23 namespace { |
| 21 | 24 |
| 25 using testing::_; |
| 26 using testing::CreateFunctor; |
| 27 using testing::DoAll; |
| 28 using testing::Invoke; |
| 29 using testing::InvokeWithoutArgs; |
| 30 using testing::SaveArg; |
| 31 using testing::WithArgs; |
| 32 |
| 33 using ui_test_utils::TimedMessageLoopRunner; |
| 34 |
| 22 static const char kTestDirectorySimpleApiCall[] = | 35 static const char kTestDirectorySimpleApiCall[] = |
| 23 "extensions/uitest/simple_api_call"; | 36 "extensions/uitest/simple_api_call"; |
| 24 static const char kTestDirectoryRoundtripApiCall[] = | 37 static const char kTestDirectoryRoundtripApiCall[] = |
| 25 "extensions/uitest/roundtrip_api_call"; | 38 "extensions/uitest/roundtrip_api_call"; |
| 26 static const char kTestDirectoryBrowserEvent[] = | 39 static const char kTestDirectoryBrowserEvent[] = |
| 27 "extensions/uitest/event_sink"; | 40 "extensions/uitest/event_sink"; |
| 28 | 41 |
| 42 // TODO(port) Once external tab stuff is ported. |
| 43 #if defined(OS_WIN) |
| 44 |
| 29 // Base class to test extensions almost end-to-end by including browser | 45 // Base class to test extensions almost end-to-end by including browser |
| 30 // startup, manifest parsing, and the actual process model in the | 46 // startup, manifest parsing, and the actual process model in the |
| 31 // equation. This would also let you write UITests that test individual | 47 // equation. This would also let you write UITests that test individual |
| 32 // Chrome Extensions as running in Chrome. Takes over implementation of | 48 // Chrome Extensions as running in Chrome. Takes over implementation of |
| 33 // extension API calls so that behavior can be tested deterministically | 49 // extension API calls so that behavior can be tested deterministically |
| 34 // through code, instead of having to contort the browser into a state | 50 // through code, instead of having to contort the browser into a state |
| 35 // suitable for testing. | 51 // suitable for testing. |
| 36 // | 52 // |
| 37 // By default, makes Chrome forward all Chrome Extension API function calls | 53 // By default, makes Chrome forward all Chrome Extension API function calls |
| 38 // via the automation interface. To override this, call set_functions_enabled() | 54 // via the automation interface. To override this, call set_functions_enabled() |
| 39 // with a list of function names that should be forwarded, | 55 // with a list of function names that should be forwarded, |
| 40 template <class ParentTestType> | 56 class ExtensionUITest : public ExternalTabUITest { |
| 41 class ExtensionUITest : public ParentTestType { | |
| 42 public: | 57 public: |
| 43 explicit ExtensionUITest(const std::string& extension_path) { | 58 explicit ExtensionUITest(const std::string& extension_path) |
| 59 : loop_(MessageLoop::current()) { |
| 44 FilePath filename(test_data_directory_); | 60 FilePath filename(test_data_directory_); |
| 45 filename = filename.AppendASCII(extension_path); | 61 filename = filename.AppendASCII(extension_path); |
| 46 launch_arguments_.AppendSwitchWithValue(switches::kLoadExtension, | 62 launch_arguments_.AppendSwitchWithValue(switches::kLoadExtension, |
| 47 filename.value()); | 63 filename.value()); |
| 48 functions_enabled_.push_back("*"); | 64 functions_enabled_.push_back("*"); |
| 49 } | 65 } |
| 50 | 66 |
| 51 void set_functions_enabled( | 67 void set_functions_enabled( |
| 52 const std::vector<std::string>& functions_enabled) { | 68 const std::vector<std::string>& functions_enabled) { |
| 53 functions_enabled_ = functions_enabled; | 69 functions_enabled_ = functions_enabled; |
| 54 } | 70 } |
| 55 | 71 |
| 56 void SetUp() { | 72 void SetUp() { |
| 57 ParentTestType::SetUp(); | 73 ExternalTabUITest::SetUp(); |
| 58 | 74 tab_ = mock_->CreateTabWithUrl(GURL()); |
| 59 AutomationProxyForExternalTab* proxy = | |
| 60 static_cast<AutomationProxyForExternalTab*>(automation()); | |
| 61 HWND external_tab_container = NULL; | |
| 62 HWND tab_wnd = NULL; | |
| 63 tab_ = proxy->CreateTabWithHostWindow(false, | |
| 64 GURL(), &external_tab_container, &tab_wnd); | |
| 65 | |
| 66 tab_->SetEnableExtensionAutomation(functions_enabled_); | 75 tab_->SetEnableExtensionAutomation(functions_enabled_); |
| 67 } | 76 } |
| 68 | 77 |
| 69 void TearDown() { | 78 void TearDown() { |
| 70 tab_->SetEnableExtensionAutomation(std::vector<std::string>()); | 79 tab_->SetEnableExtensionAutomation(std::vector<std::string>()); |
| 71 | 80 tab_ = NULL; |
| 72 AutomationProxyForExternalTab* proxy = | 81 EXPECT_TRUE(mock_->HostWindowExists()) << |
| 73 static_cast<AutomationProxyForExternalTab*>(automation()); | 82 "You shouldn't DestroyHostWindow yourself, or extension automation " |
| 74 proxy->DestroyHostWindow(); | 83 "won't be correctly reset. Just exit your message loop."; |
| 75 proxy->WaitForTabCleanup(tab_, action_max_timeout_ms()); | 84 mock_->DestroyHostWindow(); |
| 76 EXPECT_FALSE(tab_->is_valid()); | 85 ExternalTabUITest::TearDown(); |
| 77 tab_.release(); | |
| 78 | |
| 79 ParentTestType::TearDown(); | |
| 80 } | |
| 81 | |
| 82 void TestWithURL(const GURL& url) { | |
| 83 EXPECT_TRUE(tab_->is_valid()); | |
| 84 if (tab_) { | |
| 85 AutomationProxyForExternalTab* proxy = | |
| 86 static_cast<AutomationProxyForExternalTab*>(automation()); | |
| 87 | |
| 88 // Enter a message loop to allow the tab to be created | |
| 89 proxy->WaitForNavigation(2000); | |
| 90 DoAdditionalPreNavigateSetup(tab_.get()); | |
| 91 | |
| 92 // We explicitly do not make this a toolstrip in the extension manifest, | |
| 93 // so that the test can control when it gets loaded, and so that we test | |
| 94 // the intended behavior that tabs should be able to show extension pages | |
| 95 // (useful for development etc.) | |
| 96 tab_->NavigateInExternalTab(url, GURL()); | |
| 97 EXPECT_TRUE(proxy->WaitForMessage(action_max_timeout_ms())); | |
| 98 } | |
| 99 } | |
| 100 | |
| 101 // Override if you need additional stuff before we navigate the page. | |
| 102 virtual void DoAdditionalPreNavigateSetup(TabProxy* tab) { | |
| 103 } | 86 } |
| 104 | 87 |
| 105 protected: | 88 protected: |
| 106 // Extension API functions that we want to take over. Defaults to all. | 89 // Extension API functions that we want to take over. Defaults to all. |
| 107 std::vector<std::string> functions_enabled_; | 90 std::vector<std::string> functions_enabled_; |
| 91 |
| 92 // Message loop for running the async bits of your test. |
| 93 TimedMessageLoopRunner loop_; |
| 94 |
| 95 // The external tab. |
| 108 scoped_refptr<TabProxy> tab_; | 96 scoped_refptr<TabProxy> tab_; |
| 109 | 97 |
| 110 private: | 98 private: |
| 111 DISALLOW_COPY_AND_ASSIGN(ExtensionUITest); | 99 DISALLOW_COPY_AND_ASSIGN(ExtensionUITest); |
| 112 }; | 100 }; |
| 113 | 101 |
| 114 // For tests that only need to check for a single postMessage | |
| 115 // being received from the tab in Chrome. These tests can send a message | |
| 116 // to the tab before receiving the new message, but there will not be | |
| 117 // a chance to respond by sending a message from the test to the tab after | |
| 118 // the postMessage is received. | |
| 119 typedef ExtensionUITest<ExternalTabTestType> SingleMessageExtensionUITest; | |
| 120 | |
| 121 // A test that loads a basic extension that makes an API call that does | 102 // A test that loads a basic extension that makes an API call that does |
| 122 // not require a response. | 103 // not require a response. |
| 123 class SimpleApiCallExtensionTest : public SingleMessageExtensionUITest { | 104 class ExtensionTestSimpleApiCall : public ExtensionUITest { |
| 124 public: | 105 public: |
| 125 SimpleApiCallExtensionTest() | 106 ExtensionTestSimpleApiCall() |
| 126 : SingleMessageExtensionUITest(kTestDirectorySimpleApiCall) { | 107 : ExtensionUITest(kTestDirectorySimpleApiCall) { |
| 127 } | 108 } |
| 128 | 109 |
| 129 void SetUp() { | 110 void SetUp() { |
| 130 // Set just this one function explicitly to be forwarded, as a test of | 111 // Set just this one function explicitly to be forwarded, as a test of |
| 131 // the selective forwarding. The next test will leave the default to test | 112 // the selective forwarding. The next test will leave the default to test |
| 132 // universal forwarding. | 113 // universal forwarding. |
| 133 functions_enabled_.clear(); | 114 functions_enabled_.clear(); |
| 134 functions_enabled_.push_back("tabs.remove"); | 115 functions_enabled_.push_back("tabs.remove"); |
| 135 SingleMessageExtensionUITest::SetUp(); | 116 ExtensionUITest::SetUp(); |
| 136 } | 117 } |
| 137 | 118 |
| 138 private: | 119 private: |
| 139 DISALLOW_COPY_AND_ASSIGN(SimpleApiCallExtensionTest); | 120 DISALLOW_COPY_AND_ASSIGN(ExtensionTestSimpleApiCall); |
| 140 }; | 121 }; |
| 141 | 122 |
| 142 // TODO(port) Should become portable once ExternalTabMessageLoop is ported. | 123 TEST_F(ExtensionTestSimpleApiCall, RunTest) { |
| 143 #if defined(OS_WIN) | |
| 144 TEST_F(SimpleApiCallExtensionTest, RunTest) { | |
| 145 namespace keys = extension_automation_constants; | 124 namespace keys = extension_automation_constants; |
| 146 | 125 |
| 147 TestWithURL(GURL( | 126 ASSERT_THAT(mock_, testing::NotNull()); |
| 148 "chrome-extension://pmgpglkggjdpkpghhdmbdhababjpcohk/test.html")); | 127 EXPECT_CALL(*mock_, OnDidNavigate(_, _)).Times(1); |
| 149 AutomationProxyForExternalTab* proxy = | |
| 150 static_cast<AutomationProxyForExternalTab*>(automation()); | |
| 151 ASSERT_GT(proxy->messages_received(), 0); | |
| 152 | 128 |
| 153 // Using EXPECT_TRUE rather than EXPECT_EQ as the compiler (VC++) isn't | 129 std::string message_received; |
| 154 // finding the right match for EqHelper. | 130 EXPECT_CALL(*mock_, OnForwardMessageToExternalHost( |
| 155 EXPECT_TRUE(proxy->origin() == keys::kAutomationOrigin); | 131 _, _, keys::kAutomationOrigin, keys::kAutomationRequestTarget)) |
| 156 EXPECT_TRUE(proxy->target() == keys::kAutomationRequestTarget); | 132 .WillOnce(DoAll( |
| 133 SaveArg<1>(&message_received), |
| 134 InvokeWithoutArgs( |
| 135 CreateFunctor(&loop_, &TimedMessageLoopRunner::Quit)))); |
| 157 | 136 |
| 158 scoped_ptr<Value> message_value(base::JSONReader::Read(proxy->message(), | 137 EXPECT_CALL(*mock_, HandleClosed(_)); |
| 138 |
| 139 ASSERT_EQ(AUTOMATION_MSG_NAVIGATION_SUCCESS, tab_->NavigateInExternalTab( |
| 140 GURL("chrome-extension://pmgpglkggjdpkpghhdmbdhababjpcohk/test.html"), |
| 141 GURL(""))); |
| 142 |
| 143 loop_.RunFor(2 * action_max_timeout_ms()); |
| 144 ASSERT_FALSE(message_received.empty()); |
| 145 |
| 146 scoped_ptr<Value> message_value(base::JSONReader::Read(message_received, |
| 159 false)); | 147 false)); |
| 160 ASSERT_TRUE(message_value->IsType(Value::TYPE_DICTIONARY)); | 148 ASSERT_TRUE(message_value->IsType(Value::TYPE_DICTIONARY)); |
| 161 DictionaryValue* message_dict = | 149 DictionaryValue* message_dict = |
| 162 reinterpret_cast<DictionaryValue*>(message_value.get()); | 150 reinterpret_cast<DictionaryValue*>(message_value.get()); |
| 163 std::string result; | 151 std::string result; |
| 164 ASSERT_TRUE(message_dict->GetString(keys::kAutomationNameKey, &result)); | 152 ASSERT_TRUE(message_dict->GetString(keys::kAutomationNameKey, &result)); |
| 165 EXPECT_EQ(result, "tabs.remove"); | 153 EXPECT_EQ(result, "tabs.remove"); |
| 166 | 154 |
| 167 result = ""; | 155 result = ""; |
| 168 ASSERT_TRUE(message_dict->GetString(keys::kAutomationArgsKey, &result)); | 156 ASSERT_TRUE(message_dict->GetString(keys::kAutomationArgsKey, &result)); |
| 169 EXPECT_NE(result, ""); | 157 EXPECT_NE(result, ""); |
| 170 | 158 |
| 171 int callback_id = 0xBAADF00D; | 159 int callback_id = 0xBAADF00D; |
| 172 message_dict->GetInteger(keys::kAutomationRequestIdKey, &callback_id); | 160 message_dict->GetInteger(keys::kAutomationRequestIdKey, &callback_id); |
| 173 EXPECT_NE(callback_id, 0xBAADF00D); | 161 EXPECT_NE(callback_id, 0xBAADF00D); |
| 174 | 162 |
| 175 bool has_callback = true; | 163 bool has_callback = true; |
| 176 EXPECT_TRUE(message_dict->GetBoolean(keys::kAutomationHasCallbackKey, | 164 EXPECT_TRUE(message_dict->GetBoolean(keys::kAutomationHasCallbackKey, |
| 177 &has_callback)); | 165 &has_callback)); |
| 178 EXPECT_FALSE(has_callback); | 166 EXPECT_FALSE(has_callback); |
| 179 } | 167 } |
| 180 #endif // defined(OS_WIN) | |
| 181 | 168 |
| 182 // A base class for an automation proxy that checks several messages in | 169 // A test that loads a basic extension that makes an API call that does |
| 183 // a row. | 170 // not require a response. |
| 184 class MultiMessageAutomationProxy : public AutomationProxyForExternalTab { | 171 class ExtensionTestRoundtripApiCall : public ExtensionUITest { |
| 185 public: | 172 public: |
| 186 explicit MultiMessageAutomationProxy(int execution_timeout) | 173 ExtensionTestRoundtripApiCall() |
| 187 : AutomationProxyForExternalTab(execution_timeout) { | 174 : ExtensionUITest(kTestDirectoryRoundtripApiCall), |
| 175 messages_received_(0) { |
| 188 } | 176 } |
| 189 | 177 |
| 190 // Call when testing with the current tab is finished. | 178 void SetUp() { |
| 191 void Quit() { | 179 // Set just this one function explicitly to be forwarded, as a test of |
| 192 PostQuitMessage(0); | 180 // the selective forwarding. The next test will leave the default to test |
| 181 // universal forwarding. |
| 182 functions_enabled_.clear(); |
| 183 functions_enabled_.push_back("tabs.getSelected"); |
| 184 functions_enabled_.push_back("tabs.remove"); |
| 185 ExtensionUITest::SetUp(); |
| 193 } | 186 } |
| 194 | 187 |
| 195 protected: | 188 void CheckAndSendResponse(const std::string& message) { |
| 196 virtual void OnMessageReceived(const IPC::Message& msg) { | |
| 197 IPC_BEGIN_MESSAGE_MAP(MultiMessageAutomationProxy, msg) | |
| 198 IPC_MESSAGE_HANDLER(AutomationMsg_DidNavigate, | |
| 199 AutomationProxyForExternalTab::OnDidNavigate) | |
| 200 IPC_MESSAGE_HANDLER(AutomationMsg_ForwardMessageToExternalHost, | |
| 201 OnForwardMessageToExternalHost) | |
| 202 IPC_END_MESSAGE_MAP() | |
| 203 } | |
| 204 | |
| 205 void OnForwardMessageToExternalHost(int handle, | |
| 206 const std::string& message, | |
| 207 const std::string& origin, | |
| 208 const std::string& target) { | |
| 209 messages_received_++; | |
| 210 message_ = message; | |
| 211 origin_ = origin; | |
| 212 target_ = target; | |
| 213 HandleMessageFromChrome(); | |
| 214 } | |
| 215 | |
| 216 // Override to do your custom checking and initiate any custom actions | |
| 217 // needed in your particular unit test. | |
| 218 virtual void HandleMessageFromChrome() = 0; | |
| 219 }; | |
| 220 | |
| 221 // This proxy is specific to RoundtripApiCallExtensionTest. | |
| 222 class RoundtripAutomationProxy : public MultiMessageAutomationProxy { | |
| 223 public: | |
| 224 explicit RoundtripAutomationProxy(int execution_timeout) | |
| 225 : MultiMessageAutomationProxy(execution_timeout), | |
| 226 tab_(NULL) { | |
| 227 } | |
| 228 | |
| 229 // Must set before initiating test. | |
| 230 TabProxy* tab_; | |
| 231 | |
| 232 protected: | |
| 233 virtual void HandleMessageFromChrome() { | |
| 234 namespace keys = extension_automation_constants; | 189 namespace keys = extension_automation_constants; |
| 190 ++messages_received_; |
| 235 | 191 |
| 236 ASSERT_TRUE(tab_ != NULL); | 192 ASSERT_TRUE(tab_ != NULL); |
| 237 ASSERT_TRUE(messages_received_ == 1 || messages_received_ == 2); | 193 ASSERT_TRUE(messages_received_ == 1 || messages_received_ == 2); |
| 238 | 194 |
| 239 // Using EXPECT_TRUE rather than EXPECT_EQ as the compiler (VC++) isn't | 195 scoped_ptr<Value> message_value(base::JSONReader::Read(message, false)); |
| 240 // finding the right match for EqHelper. | |
| 241 EXPECT_TRUE(origin_ == keys::kAutomationOrigin); | |
| 242 EXPECT_TRUE(target_ == keys::kAutomationRequestTarget); | |
| 243 | |
| 244 scoped_ptr<Value> message_value(base::JSONReader::Read(message_, false)); | |
| 245 ASSERT_TRUE(message_value->IsType(Value::TYPE_DICTIONARY)); | 196 ASSERT_TRUE(message_value->IsType(Value::TYPE_DICTIONARY)); |
| 246 DictionaryValue* request_dict = | 197 DictionaryValue* request_dict = |
| 247 static_cast<DictionaryValue*>(message_value.get()); | 198 static_cast<DictionaryValue*>(message_value.get()); |
| 248 std::string function_name; | 199 std::string function_name; |
| 249 ASSERT_TRUE(request_dict->GetString(keys::kAutomationNameKey, | 200 ASSERT_TRUE(request_dict->GetString(keys::kAutomationNameKey, |
| 250 &function_name)); | 201 &function_name)); |
| 251 int request_id = -2; | 202 int request_id = -2; |
| 252 EXPECT_TRUE(request_dict->GetInteger(keys::kAutomationRequestIdKey, | 203 EXPECT_TRUE(request_dict->GetInteger(keys::kAutomationRequestIdKey, |
| 253 &request_id)); | 204 &request_id)); |
| 254 bool has_callback = false; | 205 bool has_callback = false; |
| 255 EXPECT_TRUE(request_dict->GetBoolean(keys::kAutomationHasCallbackKey, | 206 EXPECT_TRUE(request_dict->GetBoolean(keys::kAutomationHasCallbackKey, |
| 256 &has_callback)); | 207 &has_callback)); |
| 257 | 208 |
| 258 if (messages_received_ == 1) { | 209 if (messages_received_ == 1) { |
| 259 EXPECT_EQ(function_name, "tabs.getSelected"); | 210 EXPECT_EQ(function_name, "tabs.getSelected"); |
| 260 EXPECT_GE(request_id, 0); | 211 EXPECT_GE(request_id, 0); |
| 261 EXPECT_TRUE(has_callback); | 212 EXPECT_TRUE(has_callback); |
| 262 | 213 |
| 263 DictionaryValue response_dict; | 214 DictionaryValue response_dict; |
| 264 response_dict.SetInteger(keys::kAutomationRequestIdKey, request_id); | 215 response_dict.SetInteger(keys::kAutomationRequestIdKey, request_id); |
| 265 DictionaryValue tab_dict; | 216 DictionaryValue tab_dict; |
| 266 tab_dict.SetInteger(extension_tabs_module_constants::kIdKey, 42); | 217 tab_dict.SetInteger(extension_tabs_module_constants::kIdKey, 42); |
| 267 tab_dict.SetInteger(extension_tabs_module_constants::kIndexKey, 1); | 218 tab_dict.SetInteger(extension_tabs_module_constants::kIndexKey, 1); |
| 268 tab_dict.SetInteger(extension_tabs_module_constants::kWindowIdKey, 1); | 219 tab_dict.SetInteger(extension_tabs_module_constants::kWindowIdKey, 1); |
| 269 tab_dict.SetBoolean(extension_tabs_module_constants::kSelectedKey, true); | 220 tab_dict.SetBoolean(extension_tabs_module_constants::kSelectedKey, true); |
| 270 tab_dict.SetString(extension_tabs_module_constants::kUrlKey, | 221 tab_dict.SetString(extension_tabs_module_constants::kUrlKey, |
| 271 "http://www.google.com"); | 222 "http://www.google.com"); |
| 272 | 223 |
| 273 std::string tab_json; | 224 std::string tab_json; |
| 274 base::JSONWriter::Write(&tab_dict, false, &tab_json); | 225 base::JSONWriter::Write(&tab_dict, false, &tab_json); |
| 275 | 226 |
| 276 response_dict.SetString(keys::kAutomationResponseKey, tab_json); | 227 response_dict.SetString(keys::kAutomationResponseKey, tab_json); |
| 277 | 228 |
| 278 std::string response_json; | 229 std::string response_json; |
| 279 base::JSONWriter::Write(&response_dict, false, &response_json); | 230 base::JSONWriter::Write(&response_dict, false, &response_json); |
| 280 | 231 |
| 281 tab_->HandleMessageFromExternalHost( | 232 tab_->HandleMessageFromExternalHost( |
| 282 response_json, | 233 response_json, |
| 283 keys::kAutomationOrigin, | 234 keys::kAutomationOrigin, |
| 284 keys::kAutomationResponseTarget); | 235 keys::kAutomationResponseTarget); |
| 285 } else if (messages_received_ == 2) { | 236 } else if (messages_received_ == 2) { |
| 286 EXPECT_EQ(function_name, "tabs.remove"); | 237 EXPECT_EQ(function_name, "tabs.remove"); |
| 287 EXPECT_FALSE(has_callback); | 238 EXPECT_FALSE(has_callback); |
| 288 | 239 |
| 289 std::string args; | 240 std::string args; |
| 290 EXPECT_TRUE(request_dict->GetString(keys::kAutomationArgsKey, &args)); | 241 EXPECT_TRUE(request_dict->GetString(keys::kAutomationArgsKey, &args)); |
| 291 EXPECT_NE(args.find("42"), -1); | 242 EXPECT_NE(args.find("42"), -1); |
| 292 | 243 loop_.Quit(); |
| 293 Quit(); | |
| 294 } else { | 244 } else { |
| 295 Quit(); | |
| 296 FAIL(); | 245 FAIL(); |
| 246 loop_.Quit(); |
| 297 } | 247 } |
| 298 } | 248 } |
| 249 |
| 250 private: |
| 251 int messages_received_; |
| 252 DISALLOW_COPY_AND_ASSIGN(ExtensionTestRoundtripApiCall); |
| 299 }; | 253 }; |
| 300 | 254 |
| 301 class RoundtripApiCallExtensionTest | 255 TEST_F(ExtensionTestRoundtripApiCall, RunTest) { |
| 302 : public ExtensionUITest< | 256 namespace keys = extension_automation_constants; |
| 303 CustomAutomationProxyTest<RoundtripAutomationProxy>> { | 257 |
| 258 ASSERT_THAT(mock_, testing::NotNull()); |
| 259 EXPECT_CALL(*mock_, OnDidNavigate(_, _)).Times(1); |
| 260 |
| 261 EXPECT_CALL(*mock_, OnForwardMessageToExternalHost( |
| 262 _, _, keys::kAutomationOrigin, keys::kAutomationRequestTarget)) |
| 263 .Times(2) |
| 264 .WillRepeatedly(WithArgs<1>(Invoke( |
| 265 CreateFunctor(this, |
| 266 &ExtensionTestRoundtripApiCall::CheckAndSendResponse)))); |
| 267 |
| 268 EXPECT_CALL(*mock_, HandleClosed(_)); |
| 269 |
| 270 ASSERT_EQ(AUTOMATION_MSG_NAVIGATION_SUCCESS, tab_->NavigateInExternalTab( |
| 271 GURL("chrome-extension://ofoknjclcmghjfmbncljcnpjmfmldhno/test.html"), |
| 272 GURL(""))); |
| 273 |
| 274 // CheckAndSendResponse (called by OnForwardMessageToExternalHost) |
| 275 // will end the loop once it has received both of our expected messages. |
| 276 loop_.RunFor(2 * action_max_timeout_ms()); |
| 277 } |
| 278 |
| 279 class ExtensionTestBrowserEvents : public ExtensionUITest { |
| 304 public: | 280 public: |
| 305 RoundtripApiCallExtensionTest() | 281 ExtensionTestBrowserEvents() |
| 306 : ExtensionUITest< | 282 : ExtensionUITest(kTestDirectoryBrowserEvent), |
| 307 CustomAutomationProxyTest< | 283 response_count_(0) { |
| 308 RoundtripAutomationProxy> >(kTestDirectoryRoundtripApiCall) { | |
| 309 } | 284 } |
| 310 | 285 |
| 311 void DoAdditionalPreNavigateSetup(TabProxy* tab) { | 286 void SetUp() { |
| 312 RoundtripAutomationProxy* proxy = | 287 // Set just this one function explicitly to be forwarded, as a test of |
| 313 static_cast<RoundtripAutomationProxy*>(automation()); | 288 // the selective forwarding. The next test will leave the default to test |
| 314 proxy->tab_ = tab; | 289 // universal forwarding. |
| 290 functions_enabled_.clear(); |
| 291 functions_enabled_.push_back("windows.getCurrent"); |
| 292 ExtensionUITest::SetUp(); |
| 315 } | 293 } |
| 316 | 294 |
| 317 private: | 295 // Fire an event of the given name to the test extension. |
| 318 DISALLOW_COPY_AND_ASSIGN(RoundtripApiCallExtensionTest); | 296 void FireEvent(const char* event_name) { |
| 319 }; | 297 namespace keys = extension_automation_constants; |
| 320 | 298 |
| 321 // TODO(port) Should become portable once | 299 ASSERT_TRUE(tab_ != NULL); |
| 322 // ExternalTabMessageLoop is ported. | |
| 323 #if defined(OS_WIN) | |
| 324 TEST_F(RoundtripApiCallExtensionTest, RunTest) { | |
| 325 TestWithURL(GURL( | |
| 326 "chrome-extension://ofoknjclcmghjfmbncljcnpjmfmldhno/test.html")); | |
| 327 RoundtripAutomationProxy* proxy = | |
| 328 static_cast<RoundtripAutomationProxy*>(automation()); | |
| 329 | 300 |
| 330 // Validation is done in the RoundtripAutomationProxy, so we just check | 301 // Build the event message to send to the extension. The only important |
| 331 // something basic here. | 302 // part is the name, as the payload is not used by the test extension. |
| 332 EXPECT_EQ(proxy->messages_received(), 2); | 303 std::string message; |
| 333 } | 304 message += event_name; |
| 334 #endif // defined(OS_WIN) | |
| 335 | 305 |
| 336 // This proxy is specific to BrowserEventExtensionTest. | 306 tab_->HandleMessageFromExternalHost( |
| 337 class BrowserEventAutomationProxy : public MultiMessageAutomationProxy { | 307 message, |
| 338 public: | 308 keys::kAutomationOrigin, |
| 339 explicit BrowserEventAutomationProxy(int execution_timeout) | 309 keys::kAutomationBrowserEventRequestTarget); |
| 340 : MultiMessageAutomationProxy(execution_timeout), | |
| 341 tab_(NULL) { | |
| 342 } | 310 } |
| 343 | 311 |
| 344 // Must set before initiating test. | 312 void HandleMessageFromChrome(const std::string& message, |
| 345 TabProxy* tab_; | 313 const std::string& target); |
| 346 | 314 |
| 315 protected: |
| 347 // Counts the number of times we got a given event. | 316 // Counts the number of times we got a given event. |
| 348 std::map<std::string, int> event_count_; | 317 std::map<std::string, int> event_count_; |
| 349 | 318 |
| 350 // Array containing the names of the events to fire to the extension. | 319 // Array containing the names of the events to fire to the extension. |
| 351 static const char* events_[]; | 320 static const char* events_[]; |
| 352 | 321 |
| 353 protected: | 322 // Number of events extension has told us it received. |
| 354 // Process a message received from the test extension. | 323 int response_count_; |
| 355 virtual void HandleMessageFromChrome(); | |
| 356 | 324 |
| 357 // Fire an event of the given name to the test extension. | 325 DISALLOW_COPY_AND_ASSIGN(ExtensionTestBrowserEvents); |
| 358 void FireEvent(const char* event_name); | |
| 359 }; | 326 }; |
| 360 | 327 |
| 361 const char* BrowserEventAutomationProxy::events_[] = { | 328 const char* ExtensionTestBrowserEvents::events_[] = { |
| 362 // Window events. | 329 // Window events. |
| 363 "[\"windows.onCreated\", \"[{'id':42,'focused':true,'top':0,'left':0," | 330 "[\"windows.onCreated\", \"[{'id':42,'focused':true,'top':0,'left':0," |
| 364 "'width':100,'height':100}]\"]", | 331 "'width':100,'height':100}]\"]", |
| 365 | 332 |
| 366 "[\"windows.onRemoved\", \"[42]\"]", | 333 "[\"windows.onRemoved\", \"[42]\"]", |
| 367 | 334 |
| 368 "[\"windows.onFocusChanged\", \"[42]\"]", | 335 "[\"windows.onFocusChanged\", \"[42]\"]", |
| 369 | 336 |
| 370 // Tab events. | 337 // Tab events. |
| 371 "[\"tabs.onCreated\", \"[{'id\':42,'index':1,'windowId':1," | 338 "[\"tabs.onCreated\", \"[{'id\':42,'index':1,'windowId':1," |
| (...skipping 20 matching lines...) Expand all Loading... |
| 392 | 359 |
| 393 "[\"bookmarks.onChanged\", \"['42', {'title':'foo'}]\"]", | 360 "[\"bookmarks.onChanged\", \"['42', {'title':'foo'}]\"]", |
| 394 | 361 |
| 395 "[\"bookmarks.onMoved\", \"['42', {'parentId':'2','index':1," | 362 "[\"bookmarks.onMoved\", \"['42', {'parentId':'2','index':1," |
| 396 "'oldParentId':'3','oldIndex':2}]\"]", | 363 "'oldParentId':'3','oldIndex':2}]\"]", |
| 397 | 364 |
| 398 "[\"bookmarks.onChildrenReordered\", \"['32', " | 365 "[\"bookmarks.onChildrenReordered\", \"['32', " |
| 399 "{'childIds':['1', '2', '3']}]\"]" | 366 "{'childIds':['1', '2', '3']}]\"]" |
| 400 }; | 367 }; |
| 401 | 368 |
| 402 void BrowserEventAutomationProxy::HandleMessageFromChrome() { | 369 void ExtensionTestBrowserEvents::HandleMessageFromChrome( |
| 370 const std::string& message, |
| 371 const std::string& target) { |
| 403 namespace keys = extension_automation_constants; | 372 namespace keys = extension_automation_constants; |
| 404 ASSERT_TRUE(tab_ != NULL); | 373 ASSERT_TRUE(tab_ != NULL); |
| 405 | 374 |
| 406 std::string message(message()); | |
| 407 std::string origin(origin()); | |
| 408 std::string target(target()); | |
| 409 | |
| 410 ASSERT_TRUE(message.length() > 0); | 375 ASSERT_TRUE(message.length() > 0); |
| 411 ASSERT_STREQ(keys::kAutomationOrigin, origin.c_str()); | |
| 412 | 376 |
| 413 if (target == keys::kAutomationRequestTarget) { | 377 if (target == keys::kAutomationRequestTarget) { |
| 414 // This should be a request for the current window. We don't need to | 378 // This should be a request for the current window. We don't need to |
| 415 // respond, as this is used only as an indication that the extension | 379 // respond, as this is used only as an indication that the extension |
| 416 // page is now loaded. | 380 // page is now loaded. |
| 417 scoped_ptr<Value> message_value(base::JSONReader::Read(message, false)); | 381 scoped_ptr<Value> message_value(base::JSONReader::Read(message, false)); |
| 418 ASSERT_TRUE(message_value->IsType(Value::TYPE_DICTIONARY)); | 382 ASSERT_TRUE(message_value->IsType(Value::TYPE_DICTIONARY)); |
| 419 DictionaryValue* message_dict = | 383 DictionaryValue* message_dict = |
| 420 reinterpret_cast<DictionaryValue*>(message_value.get()); | 384 reinterpret_cast<DictionaryValue*>(message_value.get()); |
| 421 | 385 |
| (...skipping 23 matching lines...) Expand all Loading... |
| 445 // | 409 // |
| 446 // There is a special message "ACK" which means that the extension | 410 // There is a special message "ACK" which means that the extension |
| 447 // received the port connection. This is not an event response and | 411 // received the port connection. This is not an event response and |
| 448 // should happen before all events. | 412 // should happen before all events. |
| 449 scoped_ptr<Value> message_value(base::JSONReader::Read(message, false)); | 413 scoped_ptr<Value> message_value(base::JSONReader::Read(message, false)); |
| 450 ASSERT_TRUE(message_value->IsType(Value::TYPE_DICTIONARY)); | 414 ASSERT_TRUE(message_value->IsType(Value::TYPE_DICTIONARY)); |
| 451 DictionaryValue* message_dict = | 415 DictionaryValue* message_dict = |
| 452 reinterpret_cast<DictionaryValue*>(message_value.get()); | 416 reinterpret_cast<DictionaryValue*>(message_value.get()); |
| 453 | 417 |
| 454 std::string event_name; | 418 std::string event_name; |
| 455 ASSERT_TRUE(message_dict->GetString(L"data", &event_name)); | 419 message_dict->GetString(keys::kAutomationMessageDataKey, &event_name); |
| 456 if (event_name == "\"ACK\"") { | 420 if (event_name == "\"ACK\"") { |
| 457 ASSERT_EQ(0, event_count_.size()); | 421 ASSERT_EQ(0, event_count_.size()); |
| 422 } else if (event_name.empty()) { |
| 423 // This must be the post disconnect. |
| 424 int request_id = -1; |
| 425 message_dict->GetInteger(keys::kAutomationRequestIdKey, &request_id); |
| 426 ASSERT_EQ(keys::CHANNEL_CLOSED, request_id); |
| 458 } else { | 427 } else { |
| 459 ++event_count_[event_name]; | 428 ++event_count_[event_name]; |
| 460 } | 429 } |
| 430 |
| 431 // Check if we're done. |
| 432 if (event_count_.size() == arraysize(events_)) { |
| 433 loop_.Quit(); |
| 434 } |
| 461 } | 435 } |
| 462 } | 436 } |
| 463 | 437 |
| 464 void BrowserEventAutomationProxy::FireEvent(const char* event) { | 438 TEST_F(ExtensionTestBrowserEvents, RunTest) { |
| 465 namespace keys = extension_automation_constants; | |
| 466 | |
| 467 // Build the event message to send to the extension. The only important | |
| 468 // part is the name, as the payload is not used by the test extension. | |
| 469 std::string message; | |
| 470 message += event; | |
| 471 | |
| 472 tab_->HandleMessageFromExternalHost( | |
| 473 message, | |
| 474 keys::kAutomationOrigin, | |
| 475 keys::kAutomationBrowserEventRequestTarget); | |
| 476 } | |
| 477 | |
| 478 class BrowserEventExtensionTest | |
| 479 : public ExtensionUITest< | |
| 480 CustomAutomationProxyTest<BrowserEventAutomationProxy>> { | |
| 481 public: | |
| 482 BrowserEventExtensionTest() | |
| 483 : ExtensionUITest< | |
| 484 CustomAutomationProxyTest< | |
| 485 BrowserEventAutomationProxy> >(kTestDirectoryBrowserEvent) { | |
| 486 } | |
| 487 | |
| 488 void DoAdditionalPreNavigateSetup(TabProxy* tab) { | |
| 489 BrowserEventAutomationProxy* proxy = | |
| 490 static_cast<BrowserEventAutomationProxy*>(automation()); | |
| 491 proxy->tab_ = tab; | |
| 492 } | |
| 493 | |
| 494 private: | |
| 495 | |
| 496 DISALLOW_COPY_AND_ASSIGN(BrowserEventExtensionTest); | |
| 497 }; | |
| 498 | |
| 499 // TODO(port) Should become portable once | |
| 500 // ExternalTabMessageLoop is ported. | |
| 501 #if defined(OS_WIN) | |
| 502 TEST_F(BrowserEventExtensionTest, RunTest) { | |
| 503 // This test loads an HTML file that tries to add listeners to a bunch of | 439 // This test loads an HTML file that tries to add listeners to a bunch of |
| 504 // chrome.* events and upon adding a listener it posts the name of the event | 440 // chrome.* events and upon adding a listener it posts the name of the event |
| 505 // to the automation layer, which we'll count to make sure the events work. | 441 // to the automation layer, which we'll count to make sure the events work. |
| 506 TestWithURL(GURL( | 442 namespace keys = extension_automation_constants; |
| 507 "chrome-extension://ofoknjclcmghjfmbncljcnpjmfmldhno/test.html")); | 443 |
| 508 BrowserEventAutomationProxy* proxy = | 444 ASSERT_THAT(mock_, testing::NotNull()); |
| 509 static_cast<BrowserEventAutomationProxy*>(automation()); | 445 EXPECT_CALL(*mock_, OnDidNavigate(_, _)).Times(1); |
| 446 |
| 447 EXPECT_CALL(*mock_, OnForwardMessageToExternalHost( |
| 448 _, _, keys::kAutomationOrigin, _)) |
| 449 .WillRepeatedly(WithArgs<1, 3>(Invoke( |
| 450 CreateFunctor(this, |
| 451 &ExtensionTestBrowserEvents::HandleMessageFromChrome)))); |
| 452 |
| 453 EXPECT_CALL(*mock_, HandleClosed(_)); |
| 454 |
| 455 ASSERT_EQ(AUTOMATION_MSG_NAVIGATION_SUCCESS, tab_->NavigateInExternalTab( |
| 456 GURL("chrome-extension://ofoknjclcmghjfmbncljcnpjmfmldhno/test.html"), |
| 457 GURL(""))); |
| 458 |
| 459 // HandleMessageFromChrome (called by OnForwardMessageToExternalHost) ends |
| 460 // the loop when we've received the number of response messages we expect. |
| 461 loop_.RunFor(2 * action_max_timeout_ms()); |
| 510 | 462 |
| 511 // If this assert hits and the actual size is 0 then you need to look at: | 463 // If this assert hits and the actual size is 0 then you need to look at: |
| 512 // src\chrome\test\data\extensions\uitest\event_sink\test.html and see if | 464 // src\chrome\test\data\extensions\uitest\event_sink\test.html and see if |
| 513 // all the events we are attaching to are valid. Also compare the list against | 465 // all the events we are attaching to are valid. Also compare the list against |
| 514 // the event_names_ string array above. | 466 // the event_names_ string array above. |
| 515 EXPECT_EQ(arraysize(BrowserEventAutomationProxy::events_), | 467 ASSERT_EQ(arraysize(events_), event_count_.size()); |
| 516 proxy->event_count_.size()); | 468 for (std::map<std::string, int>::iterator i = event_count_.begin(); |
| 517 for (std::map<std::string, int>::iterator i = proxy->event_count_.begin(); | 469 i != event_count_.end(); ++i) { |
| 518 i != proxy->event_count_.end(); ++i) { | |
| 519 const std::pair<std::string, int>& value = *i; | 470 const std::pair<std::string, int>& value = *i; |
| 520 ASSERT_EQ(1, value.second); | 471 EXPECT_EQ(1, value.second); |
| 521 } | 472 } |
| 522 } | 473 } |
| 523 #endif // defined(OS_WIN) | 474 #endif // defined(OS_WIN) |
| 524 | 475 |
| 525 } // namespace | 476 } // namespace |
| OLD | NEW |