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