| OLD | NEW |
| (Empty) |
| 1 // Copyright 2013 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "base/bind.h" | |
| 6 #include "base/command_line.h" | |
| 7 #include "base/files/file_path.h" | |
| 8 #include "chrome/browser/extensions/api/cast_channel/cast_channel_api.h" | |
| 9 #include "chrome/browser/extensions/api/cast_channel/cast_socket.h" | |
| 10 #include "chrome/browser/extensions/extension_apitest.h" | |
| 11 #include "chrome/browser/extensions/extension_function_test_utils.h" | |
| 12 #include "chrome/browser/extensions/extension_service.h" | |
| 13 #include "chrome/browser/ui/browser.h" | |
| 14 #include "chrome/common/extensions/api/cast_channel.h" | |
| 15 #include "content/public/browser/browser_thread.h" | |
| 16 #include "extensions/common/switches.h" | |
| 17 #include "net/base/capturing_net_log.h" | |
| 18 #include "net/base/completion_callback.h" | |
| 19 #include "net/base/net_errors.h" | |
| 20 #include "testing/gmock/include/gmock/gmock.h" | |
| 21 #include "testing/gmock_mutant.h" | |
| 22 | |
| 23 namespace cast_channel = extensions::api::cast_channel; | |
| 24 using cast_channel::CastSocket; | |
| 25 using cast_channel::ChannelError; | |
| 26 using cast_channel::MessageInfo; | |
| 27 using cast_channel::ReadyState; | |
| 28 using extensions::Extension; | |
| 29 | |
| 30 namespace utils = extension_function_test_utils; | |
| 31 | |
| 32 using ::testing::_; | |
| 33 using ::testing::A; | |
| 34 using ::testing::DoAll; | |
| 35 using ::testing::Invoke; | |
| 36 using ::testing::InSequence; | |
| 37 using ::testing::Return; | |
| 38 | |
| 39 namespace { | |
| 40 | |
| 41 const char kTestExtensionId[] = "ddchlicdkolnonkihahngkmmmjnjlkkf"; | |
| 42 const int64 kTimeoutMs = 10000; | |
| 43 | |
| 44 static void FillMessageInfo(MessageInfo* message_info, | |
| 45 const std::string& message) { | |
| 46 message_info->namespace_ = "foo"; | |
| 47 message_info->source_id = "src"; | |
| 48 message_info->destination_id = "dest"; | |
| 49 message_info->data.reset(new base::StringValue(message)); | |
| 50 } | |
| 51 | |
| 52 ACTION_TEMPLATE(InvokeCompletionCallback, | |
| 53 HAS_1_TEMPLATE_PARAMS(int, k), | |
| 54 AND_1_VALUE_PARAMS(result)) { | |
| 55 ::std::tr1::get<k>(args).Run(result); | |
| 56 } | |
| 57 | |
| 58 ACTION_P2(InvokeDelegateOnError, api_test, api) { | |
| 59 api_test->CallOnError(api); | |
| 60 } | |
| 61 | |
| 62 class MockCastSocket : public CastSocket { | |
| 63 public: | |
| 64 explicit MockCastSocket(CastSocket::Delegate* delegate, | |
| 65 net::IPEndPoint ip_endpoint, | |
| 66 net::NetLog* net_log) | |
| 67 : CastSocket(kTestExtensionId, ip_endpoint, | |
| 68 cast_channel::CHANNEL_AUTH_TYPE_SSL, delegate, net_log, | |
| 69 base::TimeDelta::FromMilliseconds(kTimeoutMs)) {} | |
| 70 virtual ~MockCastSocket() {} | |
| 71 | |
| 72 virtual bool CalledOnValidThread() const OVERRIDE { | |
| 73 // Always return true in testing. | |
| 74 return true; | |
| 75 } | |
| 76 | |
| 77 MOCK_METHOD1(Connect, void(const net::CompletionCallback& callback)); | |
| 78 MOCK_METHOD2(SendMessage, void(const MessageInfo& message, | |
| 79 const net::CompletionCallback& callback)); | |
| 80 MOCK_METHOD1(Close, void(const net::CompletionCallback& callback)); | |
| 81 MOCK_CONST_METHOD0(ready_state, cast_channel::ReadyState()); | |
| 82 MOCK_CONST_METHOD0(error_state, cast_channel::ChannelError()); | |
| 83 }; | |
| 84 | |
| 85 } // namespace | |
| 86 | |
| 87 class CastChannelAPITest : public ExtensionApiTest { | |
| 88 public: | |
| 89 CastChannelAPITest() {} | |
| 90 | |
| 91 virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE { | |
| 92 ExtensionApiTest::SetUpCommandLine(command_line); | |
| 93 command_line->AppendSwitchASCII( | |
| 94 extensions::switches::kWhitelistedExtensionID, | |
| 95 kTestExtensionId); | |
| 96 } | |
| 97 | |
| 98 void SetUpMockCastSocket() { | |
| 99 extensions::CastChannelAPI* api = GetApi(); | |
| 100 net::IPAddressNumber ip_number; | |
| 101 net::ParseIPLiteralToNumber("192.168.1.1", &ip_number); | |
| 102 net::IPEndPoint ip_endpoint(ip_number, 8009); | |
| 103 mock_cast_socket_ = new MockCastSocket(api, ip_endpoint, | |
| 104 &capturing_net_log_); | |
| 105 // Transfers ownership of the socket. | |
| 106 api->SetSocketForTest( | |
| 107 make_scoped_ptr<CastSocket>(mock_cast_socket_).Pass()); | |
| 108 | |
| 109 // Set expectations on error_state(). | |
| 110 EXPECT_CALL(*mock_cast_socket_, error_state()) | |
| 111 .WillRepeatedly(Return(cast_channel::CHANNEL_ERROR_NONE)); | |
| 112 } | |
| 113 | |
| 114 extensions::CastChannelAPI* GetApi() { | |
| 115 return extensions::CastChannelAPI::Get(profile()); | |
| 116 } | |
| 117 | |
| 118 void CallOnError(extensions::CastChannelAPI* api) { | |
| 119 api->OnError(mock_cast_socket_, | |
| 120 cast_channel::CHANNEL_ERROR_CONNECT_ERROR); | |
| 121 } | |
| 122 | |
| 123 protected: | |
| 124 void CallOnMessage(const std::string& message) { | |
| 125 content::BrowserThread::PostTask( | |
| 126 content::BrowserThread::IO, | |
| 127 FROM_HERE, | |
| 128 base::Bind(&CastChannelAPITest::DoCallOnMessage, this, | |
| 129 GetApi(), mock_cast_socket_, message)); | |
| 130 } | |
| 131 | |
| 132 void DoCallOnMessage(extensions::CastChannelAPI* api, | |
| 133 MockCastSocket* cast_socket, | |
| 134 const std::string& message) { | |
| 135 MessageInfo message_info; | |
| 136 FillMessageInfo(&message_info, message); | |
| 137 api->OnMessage(cast_socket, message_info); | |
| 138 } | |
| 139 | |
| 140 extensions::CastChannelOpenFunction* CreateOpenFunction( | |
| 141 scoped_refptr<Extension> extension) { | |
| 142 extensions::CastChannelOpenFunction* cast_channel_open_function = | |
| 143 new extensions::CastChannelOpenFunction; | |
| 144 cast_channel_open_function->set_extension(extension.get()); | |
| 145 return cast_channel_open_function; | |
| 146 } | |
| 147 | |
| 148 extensions::CastChannelSendFunction* CreateSendFunction( | |
| 149 scoped_refptr<Extension> extension) { | |
| 150 extensions::CastChannelSendFunction* cast_channel_send_function = | |
| 151 new extensions::CastChannelSendFunction; | |
| 152 cast_channel_send_function->set_extension(extension.get()); | |
| 153 return cast_channel_send_function; | |
| 154 } | |
| 155 | |
| 156 MockCastSocket* mock_cast_socket_; | |
| 157 net::CapturingNetLog capturing_net_log_; | |
| 158 }; | |
| 159 | |
| 160 // TODO(munjal): Win Dbg has a workaround that makes RunExtensionSubtest | |
| 161 // always return true without actually running the test. Remove when fixed. | |
| 162 #if defined(OS_WIN) && !defined(NDEBUG) | |
| 163 #define MAYBE_TestOpenSendClose DISABLED_TestOpenSendClose | |
| 164 #else | |
| 165 #define MAYBE_TestOpenSendClose TestOpenSendClose | |
| 166 #endif | |
| 167 // Test loading extension, opening a channel with ConnectInfo, adding a | |
| 168 // listener, writing, reading, and closing. | |
| 169 IN_PROC_BROWSER_TEST_F(CastChannelAPITest, MAYBE_TestOpenSendClose) { | |
| 170 SetUpMockCastSocket(); | |
| 171 | |
| 172 { | |
| 173 InSequence dummy; | |
| 174 EXPECT_CALL(*mock_cast_socket_, Connect(_)) | |
| 175 .WillOnce(InvokeCompletionCallback<0>(net::OK)); | |
| 176 EXPECT_CALL(*mock_cast_socket_, ready_state()) | |
| 177 .WillOnce(Return(cast_channel::READY_STATE_OPEN)); | |
| 178 EXPECT_CALL(*mock_cast_socket_, SendMessage(A<const MessageInfo&>(), _)) | |
| 179 .WillOnce(InvokeCompletionCallback<1>(net::OK)); | |
| 180 EXPECT_CALL(*mock_cast_socket_, ready_state()) | |
| 181 .WillOnce(Return(cast_channel::READY_STATE_OPEN)); | |
| 182 EXPECT_CALL(*mock_cast_socket_, Close(_)) | |
| 183 .WillOnce(InvokeCompletionCallback<0>(net::OK)); | |
| 184 EXPECT_CALL(*mock_cast_socket_, ready_state()) | |
| 185 .WillOnce(Return(cast_channel::READY_STATE_CLOSED)); | |
| 186 } | |
| 187 | |
| 188 EXPECT_TRUE(RunExtensionSubtest("cast_channel/api", | |
| 189 "test_open_send_close.html")); | |
| 190 } | |
| 191 | |
| 192 // TODO(munjal): Win Dbg has a workaround that makes RunExtensionSubtest | |
| 193 // always return true without actually running the test. Remove when fixed. | |
| 194 #if defined(OS_WIN) && !defined(NDEBUG) | |
| 195 #define MAYBE_TestOpenSendCloseWithUrl DISABLED_TestOpenSendCloseWithUrl | |
| 196 #else | |
| 197 #define MAYBE_TestOpenSendCloseWithUrl TestOpenSendCloseWithUrl | |
| 198 #endif | |
| 199 // Test loading extension, opening a channel with a URL, adding a listener, | |
| 200 // writing, reading, and closing. | |
| 201 IN_PROC_BROWSER_TEST_F(CastChannelAPITest, MAYBE_TestOpenSendCloseWithUrl) { | |
| 202 SetUpMockCastSocket(); | |
| 203 | |
| 204 { | |
| 205 InSequence dummy; | |
| 206 EXPECT_CALL(*mock_cast_socket_, Connect(_)) | |
| 207 .WillOnce(InvokeCompletionCallback<0>(net::OK)); | |
| 208 EXPECT_CALL(*mock_cast_socket_, ready_state()) | |
| 209 .WillOnce(Return(cast_channel::READY_STATE_OPEN)); | |
| 210 EXPECT_CALL(*mock_cast_socket_, SendMessage(A<const MessageInfo&>(), _)) | |
| 211 .WillOnce(InvokeCompletionCallback<1>(net::OK)); | |
| 212 EXPECT_CALL(*mock_cast_socket_, ready_state()) | |
| 213 .WillOnce(Return(cast_channel::READY_STATE_OPEN)); | |
| 214 EXPECT_CALL(*mock_cast_socket_, Close(_)) | |
| 215 .WillOnce(InvokeCompletionCallback<0>(net::OK)); | |
| 216 EXPECT_CALL(*mock_cast_socket_, ready_state()) | |
| 217 .WillOnce(Return(cast_channel::READY_STATE_CLOSED)); | |
| 218 } | |
| 219 | |
| 220 EXPECT_TRUE(RunExtensionSubtest("cast_channel/api", | |
| 221 "test_open_send_close_url.html")); | |
| 222 } | |
| 223 | |
| 224 // TODO(munjal): Win Dbg has a workaround that makes RunExtensionSubtest | |
| 225 // always return true without actually running the test. Remove when fixed. | |
| 226 #if defined(OS_WIN) && !defined(NDEBUG) | |
| 227 #define MAYBE_TestOpenReceiveClose DISABLED_TestOpenReceiveClose | |
| 228 #else | |
| 229 #define MAYBE_TestOpenReceiveClose TestOpenReceiveClose | |
| 230 #endif | |
| 231 // Test loading extension, opening a channel, adding a listener, | |
| 232 // writing, reading, and closing. | |
| 233 IN_PROC_BROWSER_TEST_F(CastChannelAPITest, MAYBE_TestOpenReceiveClose) { | |
| 234 SetUpMockCastSocket(); | |
| 235 | |
| 236 { | |
| 237 InSequence dummy; | |
| 238 EXPECT_CALL(*mock_cast_socket_, Connect(_)) | |
| 239 .WillOnce(InvokeCompletionCallback<0>(net::OK)); | |
| 240 EXPECT_CALL(*mock_cast_socket_, ready_state()) | |
| 241 .Times(3) | |
| 242 .WillRepeatedly(Return(cast_channel::READY_STATE_OPEN)); | |
| 243 EXPECT_CALL(*mock_cast_socket_, Close(_)) | |
| 244 .WillOnce(InvokeCompletionCallback<0>(net::OK)); | |
| 245 EXPECT_CALL(*mock_cast_socket_, ready_state()) | |
| 246 .WillOnce(Return(cast_channel::READY_STATE_CLOSED)); | |
| 247 } | |
| 248 | |
| 249 EXPECT_TRUE(RunExtensionSubtest("cast_channel/api", | |
| 250 "test_open_receive_close.html")); | |
| 251 | |
| 252 ResultCatcher catcher; | |
| 253 CallOnMessage("some-message"); | |
| 254 CallOnMessage("some-message"); | |
| 255 EXPECT_TRUE(catcher.GetNextResult()) << catcher.message(); | |
| 256 } | |
| 257 | |
| 258 // TODO(munjal): Win Dbg has a workaround that makes RunExtensionSubtest | |
| 259 // always return true without actually running the test. Remove when fixed. | |
| 260 // Flaky on mac: crbug.com/393969 | |
| 261 #if (defined(OS_WIN) && !defined(NDEBUG)) || defined(OS_MACOSX) | |
| 262 #define MAYBE_TestOpenError DISABLED_TestOpenError | |
| 263 #else | |
| 264 #define MAYBE_TestOpenError TestOpenError | |
| 265 #endif | |
| 266 // Test the case when socket open results in an error. | |
| 267 IN_PROC_BROWSER_TEST_F(CastChannelAPITest, MAYBE_TestOpenError) { | |
| 268 SetUpMockCastSocket(); | |
| 269 | |
| 270 EXPECT_CALL(*mock_cast_socket_, Connect(_)) | |
| 271 .WillOnce(DoAll( | |
| 272 InvokeDelegateOnError(this, GetApi()), | |
| 273 InvokeCompletionCallback<0>(net::ERR_FAILED))); | |
| 274 EXPECT_CALL(*mock_cast_socket_, ready_state()) | |
| 275 .WillRepeatedly(Return(cast_channel::READY_STATE_CLOSED)); | |
| 276 EXPECT_CALL(*mock_cast_socket_, Close(_)); | |
| 277 | |
| 278 EXPECT_TRUE(RunExtensionSubtest("cast_channel/api", | |
| 279 "test_open_error.html")); | |
| 280 | |
| 281 ResultCatcher catcher; | |
| 282 EXPECT_TRUE(catcher.GetNextResult()) << catcher.message(); | |
| 283 } | |
| 284 | |
| 285 IN_PROC_BROWSER_TEST_F(CastChannelAPITest, TestOpenInvalidConnectInfo) { | |
| 286 scoped_refptr<Extension> empty_extension(utils::CreateEmptyExtension()); | |
| 287 scoped_refptr<extensions::CastChannelOpenFunction> cast_channel_open_function; | |
| 288 | |
| 289 // Invalid URL | |
| 290 // TODO(mfoltz): Remove this test case when fixing crbug.com/331905 | |
| 291 cast_channel_open_function = CreateOpenFunction(empty_extension); | |
| 292 std::string error(utils::RunFunctionAndReturnError( | |
| 293 cast_channel_open_function.get(), "[\"blargh\"]", browser())); | |
| 294 EXPECT_EQ(error, "Invalid connect_info (invalid Cast URL blargh)"); | |
| 295 | |
| 296 // Wrong type | |
| 297 // TODO(mfoltz): Remove this test case when fixing crbug.com/331905 | |
| 298 cast_channel_open_function = CreateOpenFunction(empty_extension); | |
| 299 error = utils::RunFunctionAndReturnError( | |
| 300 cast_channel_open_function.get(), | |
| 301 "[123]", browser()); | |
| 302 EXPECT_EQ(error, "Invalid connect_info (unknown type)"); | |
| 303 | |
| 304 // Invalid IP address | |
| 305 cast_channel_open_function = CreateOpenFunction(empty_extension); | |
| 306 error = utils::RunFunctionAndReturnError( | |
| 307 cast_channel_open_function.get(), | |
| 308 "[{\"ipAddress\": \"invalid_ip\", \"port\": 8009, \"auth\": \"ssl\"}]", | |
| 309 browser()); | |
| 310 EXPECT_EQ(error, "Invalid connect_info (invalid IP address)"); | |
| 311 | |
| 312 // Invalid port | |
| 313 cast_channel_open_function = CreateOpenFunction(empty_extension); | |
| 314 error = utils::RunFunctionAndReturnError( | |
| 315 cast_channel_open_function.get(), | |
| 316 "[{\"ipAddress\": \"127.0.0.1\", \"port\": -200, \"auth\": \"ssl\"}]", | |
| 317 browser()); | |
| 318 EXPECT_EQ(error, "Invalid connect_info (invalid port)"); | |
| 319 | |
| 320 // Auth not set | |
| 321 cast_channel_open_function = CreateOpenFunction(empty_extension); | |
| 322 error = utils::RunFunctionAndReturnError( | |
| 323 cast_channel_open_function.get(), | |
| 324 "[{\"ipAddress\": \"127.0.0.1\", \"port\": 8009}]", | |
| 325 browser()); | |
| 326 EXPECT_EQ(error, "connect_info.auth is required"); | |
| 327 } | |
| 328 | |
| 329 IN_PROC_BROWSER_TEST_F(CastChannelAPITest, TestSendInvalidMessageInfo) { | |
| 330 scoped_refptr<Extension> empty_extension(utils::CreateEmptyExtension()); | |
| 331 scoped_refptr<extensions::CastChannelSendFunction> cast_channel_send_function; | |
| 332 | |
| 333 // Numbers are not supported | |
| 334 cast_channel_send_function = CreateSendFunction(empty_extension); | |
| 335 std::string error(utils::RunFunctionAndReturnError( | |
| 336 cast_channel_send_function.get(), | |
| 337 "[{\"channelId\": 1, \"url\": \"cast://127.0.0.1:8009\", " | |
| 338 "\"connectInfo\": " | |
| 339 "{\"ipAddress\": \"127.0.0.1\", \"port\": 8009, " | |
| 340 "\"auth\": \"ssl\"}, \"readyState\": \"open\"}, " | |
| 341 "{\"namespace_\": \"foo\", \"sourceId\": \"src\", " | |
| 342 "\"destinationId\": \"dest\", \"data\": 1235}]", | |
| 343 browser())); | |
| 344 EXPECT_EQ(error, "Invalid type of message_info.data"); | |
| 345 | |
| 346 // Missing namespace_ | |
| 347 cast_channel_send_function = CreateSendFunction(empty_extension); | |
| 348 error = utils::RunFunctionAndReturnError( | |
| 349 cast_channel_send_function.get(), | |
| 350 "[{\"channelId\": 1, \"url\": \"cast://127.0.0.1:8009\", " | |
| 351 "\"connectInfo\": " | |
| 352 "{\"ipAddress\": \"127.0.0.1\", \"port\": 8009, " | |
| 353 "\"auth\": \"ssl\"}, \"readyState\": \"open\"}, " | |
| 354 "{\"namespace_\": \"\", \"sourceId\": \"src\", " | |
| 355 "\"destinationId\": \"dest\", \"data\": \"data\"}]", | |
| 356 browser()); | |
| 357 EXPECT_EQ(error, "message_info.namespace_ is required"); | |
| 358 | |
| 359 // Missing source_id | |
| 360 cast_channel_send_function = CreateSendFunction(empty_extension); | |
| 361 error = utils::RunFunctionAndReturnError( | |
| 362 cast_channel_send_function.get(), | |
| 363 "[{\"channelId\": 1, \"url\": \"cast://127.0.0.1:8009\", " | |
| 364 "\"connectInfo\": " | |
| 365 "{\"ipAddress\": \"127.0.0.1\", \"port\": 8009, " | |
| 366 "\"auth\": \"ssl\"}, \"readyState\": \"open\"}, " | |
| 367 "{\"namespace_\": \"foo\", \"sourceId\": \"\", " | |
| 368 "\"destinationId\": \"dest\", \"data\": \"data\"}]", | |
| 369 browser()); | |
| 370 EXPECT_EQ(error, "message_info.source_id is required"); | |
| 371 | |
| 372 // Missing destination_id | |
| 373 cast_channel_send_function = CreateSendFunction(empty_extension); | |
| 374 error = utils::RunFunctionAndReturnError( | |
| 375 cast_channel_send_function.get(), | |
| 376 "[{\"channelId\": 1, \"url\": \"cast://127.0.0.1:8009\", " | |
| 377 "\"connectInfo\": " | |
| 378 "{\"ipAddress\": \"127.0.0.1\", \"port\": 8009, " | |
| 379 "\"auth\": \"ssl\"}, \"readyState\": \"open\"}, " | |
| 380 "{\"namespace_\": \"foo\", \"sourceId\": \"src\", " | |
| 381 "\"destinationId\": \"\", \"data\": \"data\"}]", | |
| 382 browser()); | |
| 383 EXPECT_EQ(error, "message_info.destination_id is required"); | |
| 384 } | |
| OLD | NEW |