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 |