Chromium Code Reviews| 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 |