OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2015 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 <string> | |
6 | |
7 #include "base/bind.h" | |
8 #include "base/bind_helpers.h" | |
9 #include "base/memory/scoped_ptr.h" | |
10 #include "base/run_loop.h" | |
11 #include "base/synchronization/waitable_event.h" | |
12 #include "chrome/browser/media/router/media_route.h" | |
13 #include "chrome/browser/media/router/media_router_mojo_test.h" | |
14 #include "chrome/browser/media/router/mock_media_router.h" | |
15 #include "chrome/browser/media/router/test_helper.h" | |
16 #include "chrome/test/base/chrome_render_view_host_test_harness.h" | |
17 #include "chrome/test/base/testing_profile.h" | |
18 #include "extensions/browser/extension_registry.h" | |
19 #include "extensions/browser/process_manager.h" | |
20 #include "extensions/browser/process_manager_factory.h" | |
21 #include "media/base/gmock_callback_support.h" | |
22 #include "testing/gmock/include/gmock/gmock.h" | |
23 #include "testing/gtest/include/gtest/gtest.h" | |
24 | |
25 using testing::_; | |
26 using testing::Eq; | |
27 using testing::Invoke; | |
28 using testing::Pointee; | |
29 using testing::Return; | |
30 using testing::ReturnRef; | |
31 using testing::SaveArg; | |
32 | |
33 const char kDescription[] = "description"; | |
34 const char kError[] = "error"; | |
35 const char kExtensionId[] = "extension1234"; | |
36 const char kMessage[] = "message"; | |
37 const char kSource[] = "source1"; | |
38 const char kSource2[] = "source2"; | |
39 const char kRouteId[] = "routeId"; | |
40 const char kRouteId2[] = "routeId2"; | |
41 const char kSink[] = "sink"; | |
42 const char kSink2[] = "sink2"; | |
43 const char kSinkName[] = "sinkName"; | |
xhwang
2015/05/28 06:53:21
nit: move these into the media_router namespace?
imcheng (use chromium acct)
2015/05/28 20:46:35
Done.
| |
44 | |
45 namespace media_router { | |
46 | |
47 // Adapts Invoke(), which takes a move-only scoped_ptr parameter (not mockable) | |
48 // to a variant that accepts raw pointers instead (mock friendly). | |
49 class RouteResponseCallbackHandler { | |
50 public: | |
51 void Invoke(scoped_ptr<MediaRoute> route, const std::string& error_text) { | |
52 InvokeObserver(route.get(), error_text); | |
53 } | |
54 | |
55 MOCK_METHOD2(InvokeObserver, | |
56 void(MediaRoute* route, const std::string& error_text)); | |
57 }; | |
58 | |
59 template <typename T> | |
60 void StoreAndRun(T* result, const base::Closure& closure, const T& result_val) { | |
61 *result = result_val; | |
62 closure.Run(); | |
63 } | |
64 | |
65 class MediaRouterMojoImplTest : public MediaRouterMojoTest { | |
66 public: | |
67 MediaRouterMojoImplTest() {} | |
68 ~MediaRouterMojoImplTest() override {} | |
69 }; | |
70 | |
71 // ProcessManager with a mocked method subset, for testing extension suspend | |
72 // handling. | |
73 class TestProcessManager : public extensions::ProcessManager { | |
74 public: | |
75 explicit TestProcessManager(content::BrowserContext* context) | |
76 : extensions::ProcessManager( | |
77 context, | |
78 context, | |
79 extensions::ExtensionRegistry::Get(context)) {} | |
80 ~TestProcessManager() override {} | |
81 | |
82 static KeyedService* Create(content::BrowserContext* context) { | |
83 return new TestProcessManager(context); | |
84 } | |
85 | |
86 MOCK_METHOD1(IsEventPageSuspended, bool(const std::string& ext_id)); | |
87 | |
88 MOCK_METHOD2(WakeEventPage, | |
89 bool(const std::string& extension_id, | |
90 const base::Callback<void(bool)>& callback)); | |
91 | |
92 private: | |
93 DISALLOW_COPY_AND_ASSIGN(TestProcessManager); | |
94 }; | |
95 | |
96 // Mockable class for awaiting ProvideMediaRouter callbacks. | |
97 class ProvideMediaRouterHandler { | |
98 public: | |
99 MOCK_METHOD1(Invoke, void(const std::string& instance_id)); | |
100 }; | |
101 | |
102 TEST_F(MediaRouterMojoImplTest, CreateRoute) { | |
103 MediaRoute expected_route(kRouteId, MediaSource(std::string(kSource)), | |
104 MediaSink(kSink, kSinkName), "", false); | |
105 interfaces::MediaRoutePtr route = interfaces::MediaRoute::New(); | |
106 route->media_source = kSource; | |
107 route->media_sink = interfaces::MediaSink::New(); | |
108 route->media_sink->sink_id = kSink; | |
109 route->media_sink->name = kSinkName; | |
110 route->media_route_id = kRouteId; | |
111 route->description = kDescription; | |
112 | |
113 // Use a lambda function as an invocation target here to work around | |
114 // a limitation with GMock::Invoke that prevents it from using move-only types | |
115 // in runnable parameter lists. | |
116 EXPECT_CALL(mock_mrpm_host_, | |
117 CreateRoute(mojo::String(kSource), mojo::String(kSink), _)) | |
118 .WillOnce(Invoke( | |
119 [&route](const mojo::String& source, const mojo::String& sink, | |
120 const interfaces::MediaRouter::CreateRouteCallback& cb) { | |
121 cb.Run(route.Pass(), mojo::String()); | |
122 })); | |
123 | |
124 RouteResponseCallbackHandler handler; | |
125 EXPECT_CALL(handler, InvokeObserver(Pointee(Equals(expected_route)), "")); | |
126 observer_impl_->CreateRoute(kSource, kSink, | |
127 base::Bind(&RouteResponseCallbackHandler::Invoke, | |
128 base::Unretained(&handler))); | |
129 ProcessEventLoop(); | |
130 } | |
131 | |
132 TEST_F(MediaRouterMojoImplTest, CreateRouteFails) { | |
133 EXPECT_CALL(mock_mrpm_host_, | |
134 CreateRoute(mojo::String(kSource), mojo::String(kSink), _)) | |
135 .WillOnce( | |
136 Invoke([](const mojo::String& source, const mojo::String& sink, | |
137 const interfaces::MediaRouter::CreateRouteCallback& cb) { | |
138 cb.Run(interfaces::MediaRoutePtr(), mojo::String(kError)); | |
139 })); | |
140 | |
141 RouteResponseCallbackHandler handler; | |
142 EXPECT_CALL(handler, InvokeObserver(nullptr, kError)); | |
143 observer_impl_->CreateRoute(kSource, kSink, | |
144 base::Bind(&RouteResponseCallbackHandler::Invoke, | |
145 base::Unretained(&handler))); | |
146 ProcessEventLoop(); | |
147 } | |
148 | |
149 TEST_F(MediaRouterMojoImplTest, CloseRoute) { | |
150 EXPECT_CALL(mock_mrpm_host_, CloseRoute(mojo::String(kRouteId))); | |
151 observer_impl_->CloseRoute(kRouteId); | |
152 ProcessEventLoop(); | |
153 } | |
154 | |
155 TEST_F(MediaRouterMojoImplTest, RegisterAndUnregisterMediaSinksObserver) { | |
156 MediaSource media_source(kSource); | |
157 | |
158 MockMediaRouter mock_router; | |
159 EXPECT_CALL(mock_mrpm_host_, StartObservingMediaSinks(mojo::String(kSource))) | |
160 .Times(2); | |
161 EXPECT_CALL(mock_mrpm_host_, | |
162 StartObservingMediaSinks(mojo::String(kSource2))); | |
163 | |
164 MediaSinksObserver* captured_observer; | |
165 EXPECT_CALL(mock_router, RegisterMediaSinksObserver(_)) | |
166 .Times(3) | |
167 .WillRepeatedly(SaveArg<0>(&captured_observer)); | |
168 | |
169 MockMediaSinksObserver sinks_observer(&mock_router, media_source); | |
170 EXPECT_EQ(&sinks_observer, captured_observer); | |
171 observer_impl_->RegisterMediaSinksObserver(&sinks_observer); | |
172 MockMediaSinksObserver extra_sinks_observer(&mock_router, media_source); | |
173 EXPECT_EQ(&extra_sinks_observer, captured_observer); | |
174 observer_impl_->RegisterMediaSinksObserver(&extra_sinks_observer); | |
175 MockMediaSinksObserver unrelated_sinks_observer(&mock_router, | |
176 MediaSource(kSource2)); | |
177 EXPECT_EQ(&unrelated_sinks_observer, captured_observer); | |
178 observer_impl_->RegisterMediaSinksObserver(&unrelated_sinks_observer); | |
179 | |
180 std::vector<MediaSink> expected_sinks; | |
181 expected_sinks.push_back(MediaSink(kSink, kSinkName)); | |
182 expected_sinks.push_back(MediaSink(kSink2, kSinkName)); | |
183 | |
184 mojo::Array<interfaces::MediaSinkPtr> mojo_sinks(2); | |
185 mojo_sinks[0] = interfaces::MediaSink::New(); | |
186 mojo_sinks[0]->sink_id = kSink; | |
187 mojo_sinks[0]->name = kSink; | |
188 mojo_sinks[1] = interfaces::MediaSink::New(); | |
189 mojo_sinks[1]->sink_id = kSink2; | |
190 mojo_sinks[1]->name = kSink2; | |
191 | |
192 EXPECT_CALL(sinks_observer, OnSinksReceived(SequenceEquals(expected_sinks))); | |
193 EXPECT_CALL(extra_sinks_observer, | |
194 OnSinksReceived(SequenceEquals(expected_sinks))); | |
195 observer_proxy_->OnSinksReceived(media_source.id(), mojo_sinks.Pass()); | |
196 ProcessEventLoop(); | |
197 | |
198 EXPECT_CALL(mock_router, UnregisterMediaSinksObserver(&sinks_observer)); | |
199 EXPECT_CALL(mock_router, UnregisterMediaSinksObserver(&extra_sinks_observer)); | |
200 EXPECT_CALL(mock_router, | |
201 UnregisterMediaSinksObserver(&unrelated_sinks_observer)); | |
202 EXPECT_CALL(mock_mrpm_host_, StopObservingMediaSinks(mojo::String(kSource))); | |
203 EXPECT_CALL(mock_mrpm_host_, StopObservingMediaSinks(mojo::String(kSource2))); | |
204 observer_impl_->UnregisterMediaSinksObserver(&sinks_observer); | |
205 observer_impl_->UnregisterMediaSinksObserver(&extra_sinks_observer); | |
206 observer_impl_->UnregisterMediaSinksObserver(&unrelated_sinks_observer); | |
207 ProcessEventLoop(); | |
208 } | |
209 | |
210 TEST_F(MediaRouterMojoImplTest, RegisterAndUnregisterMediaRoutesObserver) { | |
211 MockMediaRouter mock_router; | |
212 EXPECT_CALL(mock_mrpm_host_, StartObservingMediaRoutes()).Times(2); | |
213 | |
214 MediaRoutesObserver* observer_captured; | |
215 EXPECT_CALL(mock_router, RegisterMediaRoutesObserver(_)) | |
216 .Times(2) | |
217 .WillRepeatedly(SaveArg<0>(&observer_captured)); | |
218 MockMediaRoutesObserver routes_observer(&mock_router); | |
219 EXPECT_EQ(observer_captured, &routes_observer); | |
220 MockMediaRoutesObserver extra_routes_observer(&mock_router); | |
221 EXPECT_EQ(observer_captured, &extra_routes_observer); | |
222 observer_impl_->RegisterMediaRoutesObserver(&routes_observer); | |
223 observer_impl_->RegisterMediaRoutesObserver(&extra_routes_observer); | |
224 | |
225 std::vector<MediaRoute> expected_routes; | |
226 expected_routes.push_back(MediaRoute(kRouteId, MediaSource(kSource), | |
227 MediaSink(kSink, kSink), kDescription, | |
228 false)); | |
229 expected_routes.push_back(MediaRoute(kRouteId2, MediaSource(kSource), | |
230 MediaSink(kSink, kSink), kDescription, | |
231 false)); | |
232 | |
233 mojo::Array<interfaces::MediaRoutePtr> mojo_routes(2); | |
234 mojo_routes[0] = interfaces::MediaRoute::New(); | |
235 mojo_routes[0]->media_route_id = kRouteId; | |
236 mojo_routes[0]->media_source = kSource; | |
237 mojo_routes[0]->media_sink = interfaces::MediaSink::New(); | |
238 mojo_routes[0]->media_sink->sink_id = kSink; | |
239 mojo_routes[0]->media_sink->name = kSink; | |
240 mojo_routes[0]->description = kDescription; | |
241 mojo_routes[0]->is_local = false; | |
242 mojo_routes[1] = interfaces::MediaRoute::New(); | |
243 mojo_routes[1]->media_route_id = kRouteId2; | |
244 mojo_routes[1]->media_source = kSource; | |
245 mojo_routes[1]->media_sink = interfaces::MediaSink::New(); | |
246 mojo_routes[1]->media_sink->sink_id = kSink; | |
247 mojo_routes[1]->media_sink->name = kSink; | |
248 mojo_routes[1]->description = kDescription; | |
249 mojo_routes[1]->is_local = false; | |
250 | |
251 EXPECT_CALL(routes_observer, | |
252 OnRoutesUpdated(SequenceEquals(expected_routes))); | |
253 EXPECT_CALL(extra_routes_observer, | |
254 OnRoutesUpdated(SequenceEquals(expected_routes))); | |
255 observer_proxy_->OnRoutesUpdated(mojo_routes.Pass()); | |
256 ProcessEventLoop(); | |
257 | |
258 EXPECT_CALL(mock_router, UnregisterMediaRoutesObserver(&routes_observer)); | |
259 EXPECT_CALL(mock_router, | |
260 UnregisterMediaRoutesObserver(&extra_routes_observer)); | |
261 observer_impl_->UnregisterMediaRoutesObserver(&routes_observer); | |
262 observer_impl_->UnregisterMediaRoutesObserver(&extra_routes_observer); | |
263 EXPECT_CALL(mock_mrpm_host_, StopObservingMediaRoutes()); | |
264 ProcessEventLoop(); | |
265 } | |
266 | |
267 TEST_F(MediaRouterMojoImplTest, PostMessage) { | |
268 EXPECT_CALL(mock_mrpm_host_, | |
269 PostMessage(mojo::String(kRouteId), mojo::String(kMessage))); | |
270 observer_impl_->PostMessage(kRouteId, kMessage); | |
271 ProcessEventLoop(); | |
272 } | |
273 | |
274 TEST_F(MediaRouterMojoImplTest, QueuedWhileAsleep) { | |
275 EXPECT_CALL(mock_event_page_tracker_, IsEventPageSuspended(extension_id_)) | |
276 .Times(2) | |
277 .WillRepeatedly(Return(true)); | |
278 EXPECT_CALL(mock_event_page_tracker_, WakeEventPage(extension_id_, _)) | |
279 .Times(2) | |
280 .WillRepeatedly(Return(true)); | |
281 observer_impl_->CloseRoute(kRouteId); | |
282 observer_impl_->CloseRoute(kRouteId2); | |
283 ProcessEventLoop(); | |
284 EXPECT_CALL(mock_event_page_tracker_, IsEventPageSuspended(extension_id_)) | |
285 .Times(1) | |
286 .WillRepeatedly(Return(false)); | |
287 EXPECT_CALL(mock_mrpm_host_, CloseRoute(mojo::String(kRouteId))); | |
288 EXPECT_CALL(mock_mrpm_host_, CloseRoute(mojo::String(kRouteId2))); | |
289 ConnectProviderManagerService(); | |
290 ProcessEventLoop(); | |
291 } | |
292 | |
293 // Temporarily disabled until the issues with extension system teardown | |
294 // are addressed. | |
295 // TODO(kmarshall): Re-enable this test. (http://crbug.com/490468) | |
296 TEST(MediaRouterMojoExtensionTest, DISABLED_DeferredBindingAndSuspension) { | |
297 base::MessageLoop message_loop_(mojo::common::MessagePumpMojo::Create()); | |
298 | |
299 // Set up a mock ProcessManager instance. | |
300 TestingProfile profile; | |
301 extensions::ProcessManagerFactory::GetInstance()->SetTestingFactory( | |
302 &profile, &TestProcessManager::Create); | |
303 TestProcessManager* process_manager = static_cast<TestProcessManager*>( | |
304 extensions::ProcessManager::Get(&profile)); | |
305 | |
306 // Create MR and its proxy, so that it can be accessed through Mojo. | |
307 MediaRouterMojoImpl media_router; | |
308 interfaces::MediaRouterObserverPtr media_router_proxy; | |
xhwang
2015/05/28 06:53:21
Can we be consistent about naming mojo::InterfaceP
imcheng (use chromium acct)
2015/05/28 20:46:35
Done.
| |
309 | |
310 // Create a client object and its Mojo proxy. | |
311 testing::StrictMock<MockMojoMediaRouterService> mrpm; | |
xhwang
2015/05/28 06:53:21
throughout this CL, we have mrpm in many places. I
imcheng (use chromium acct)
2015/05/28 20:46:35
Done.
| |
312 interfaces::MediaRouterPtr mrpm_proxy; | |
xhwang
2015/05/28 06:53:21
ditto about mrpm and proxy.
imcheng (use chromium acct)
2015/05/28 20:46:35
Done.
| |
313 | |
314 // CloseRoute is called before *any* extension has connected. | |
315 // It should be queued. | |
316 media_router.CloseRoute(kRouteId); | |
317 | |
318 // MRPM connects. | |
319 scoped_ptr<mojo::Binding<interfaces::MediaRouter>> mrpm_binding( | |
320 new mojo::Binding<interfaces::MediaRouter>(&mrpm, | |
321 mojo::GetProxy(&mrpm_proxy))); | |
322 media_router.BindToMojoRequest(mojo::GetProxy(&media_router_proxy)); | |
323 media_router.InitExtensionInfo(kExtensionId, &profile); | |
324 | |
325 // The MRPM signals its readiness to the MR by registering itself via | |
326 // ProvideMediaRouter(). | |
327 // Now that the MR and MRPM are fully initialized, the queued | |
328 // CloseRoute() call should be executed. | |
329 ProvideMediaRouterHandler provide_handler; | |
330 EXPECT_CALL(provide_handler, Invoke(testing::Not(""))); | |
331 EXPECT_CALL(*process_manager, IsEventPageSuspended(kExtensionId)) | |
332 .WillOnce(Return(false)); | |
333 EXPECT_CALL(mrpm, CloseRoute(mojo::String(kRouteId))); | |
334 media_router_proxy->ProvideMediaRouter( | |
335 mrpm_proxy.Pass(), base::Bind(&ProvideMediaRouterHandler::Invoke, | |
336 base::Unretained(&provide_handler))); | |
337 message_loop_.RunUntilIdle(); | |
338 | |
339 // MRPM's extension is suspended and re-awoken. | |
340 mrpm_binding.reset(); | |
341 media_router.BindToMojoRequest(mojo::GetProxy(&media_router_proxy)); | |
342 media_router.InitExtensionInfo(kExtensionId, &profile); | |
343 EXPECT_CALL(*process_manager, IsEventPageSuspended(kExtensionId)) | |
344 .WillOnce(Return(true)); | |
345 EXPECT_CALL(*process_manager, WakeEventPage(kExtensionId, _)) | |
346 .WillOnce(testing::DoAll(media::RunCallback<1>(true), Return(true))); | |
347 media_router.CloseRoute(kRouteId2); | |
348 message_loop_.RunUntilIdle(); | |
349 | |
350 // ProvideMediaRouter() is called. | |
351 // The queued CloseRoute(kRouteId2) call should be executed. | |
352 EXPECT_CALL(provide_handler, Invoke(testing::Not(""))); | |
353 EXPECT_CALL(*process_manager, IsEventPageSuspended(kExtensionId)) | |
354 .WillOnce(Return(false)); | |
355 EXPECT_CALL(mrpm, CloseRoute(mojo::String(kRouteId2))); | |
356 mrpm_binding.reset(new mojo::Binding<interfaces::MediaRouter>( | |
357 &mrpm, mojo::GetProxy(&mrpm_proxy))); | |
358 media_router_proxy->ProvideMediaRouter( | |
359 mrpm_proxy.Pass(), base::Bind(&ProvideMediaRouterHandler::Invoke, | |
360 base::Unretained(&provide_handler))); | |
361 message_loop_.RunUntilIdle(); | |
362 } | |
363 | |
364 } // namespace media_router | |
OLD | NEW |