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