1/* 2 * Copyright 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.bluetooth.avrcp; 18 19import static org.mockito.Mockito.*; 20 21import android.media.MediaDescription; 22import android.media.MediaMetadata; 23import android.media.session.MediaSession; 24import android.media.session.PlaybackState; 25import android.os.HandlerThread; 26import android.os.TestLooperManager; 27import android.support.test.InstrumentationRegistry; 28import android.support.test.filters.SmallTest; 29import android.support.test.runner.AndroidJUnit4; 30import android.util.Log; 31 32import org.junit.Assert; 33import org.junit.Before; 34import org.junit.Test; 35import org.junit.runner.RunWith; 36import org.mockito.ArgumentCaptor; 37import org.mockito.Captor; 38import org.mockito.Mock; 39import org.mockito.MockitoAnnotations; 40 41import java.util.ArrayList; 42import java.util.Arrays; 43import java.util.Collections; 44import java.util.List; 45 46@SmallTest 47@RunWith(AndroidJUnit4.class) 48public class MediaPlayerWrapperTest { 49 private static final int MSG_TIMEOUT = 0; 50 51 private HandlerThread mThread; 52 private MediaMetadata.Builder mTestMetadata; 53 private ArrayList<MediaDescription.Builder> mTestQueue; 54 private PlaybackState.Builder mTestState; 55 56 @Captor ArgumentCaptor<MediaController.Callback> mControllerCbs; 57 @Captor ArgumentCaptor<MediaData> mMediaUpdateData; 58 @Mock Log.TerribleFailureHandler mFailHandler; 59 @Mock MediaController mMockController; 60 @Mock MediaPlayerWrapper.Callback mTestCbs; 61 62 List<MediaSession.QueueItem> getQueueFromDescriptions( 63 List<MediaDescription.Builder> descriptions) { 64 ArrayList<MediaSession.QueueItem> newList = new ArrayList<MediaSession.QueueItem>(); 65 66 for (MediaDescription.Builder bob : descriptions) { 67 newList.add( 68 new MediaSession.QueueItem( 69 bob.build(), Long.valueOf(bob.build().getMediaId()))); 70 } 71 72 return newList; 73 } 74 75 @Before 76 public void setUp() { 77 MockitoAnnotations.initMocks(this); 78 79 // Set failure handler to capture Log.wtf messages 80 Log.setWtfHandler(mFailHandler); 81 82 // Set up Looper thread for the timeout handler 83 mThread = new HandlerThread("MediaPlayerWrapperTestThread"); 84 mThread.start(); 85 86 // Set up new metadata that can be used in each test 87 mTestMetadata = 88 new MediaMetadata.Builder() 89 .putString(MediaMetadata.METADATA_KEY_TITLE, "BT Test Song") 90 .putString(MediaMetadata.METADATA_KEY_ARTIST, "BT Test Artist") 91 .putString(MediaMetadata.METADATA_KEY_ALBUM, "BT Test Album") 92 .putLong(MediaMetadata.METADATA_KEY_DURATION, 5000L); 93 94 mTestState = 95 new PlaybackState.Builder() 96 .setActiveQueueItemId(100) 97 .setState(PlaybackState.STATE_PAUSED, 0, 1.0f); 98 99 mTestQueue = new ArrayList<MediaDescription.Builder>(); 100 mTestQueue.add( 101 new MediaDescription.Builder() 102 .setTitle("BT Test Song") 103 .setSubtitle("BT Test Artist") 104 .setDescription("BT Test Album") 105 .setMediaId("100")); 106 mTestQueue.add( 107 new MediaDescription.Builder() 108 .setTitle("BT Test Song 2") 109 .setSubtitle("BT Test Artist 2") 110 .setDescription("BT Test Album 2") 111 .setMediaId("101")); 112 mTestQueue.add( 113 new MediaDescription.Builder() 114 .setTitle("BT Test Song 3") 115 .setSubtitle("BT Test Artist 3") 116 .setDescription("BT Test Album 3") 117 .setMediaId("102")); 118 119 when(mMockController.getPackageName()).thenReturn("mMockController"); 120 // NOTE: We use doReturn below because using the normal stubbing method 121 // doesn't immediately update the stub with the new return value and this 122 // can cause the old stub to be used. 123 124 // Stub default metadata for the Media Controller 125 doReturn(mTestMetadata.build()).when(mMockController).getMetadata(); 126 doReturn(mTestState.build()).when(mMockController).getPlaybackState(); 127 doReturn(getQueueFromDescriptions(mTestQueue)).when(mMockController).getQueue(); 128 129 // Enable testing flag which enables Log.wtf statements. Some tests test against improper 130 // behaviour and the TerribleFailureListener is a good way to ensure that the error occurred 131 MediaPlayerWrapper.sTesting = true; 132 } 133 134 /* 135 * Test to make sure that the wrapper fails to be built if passed invalid 136 * data. 137 */ 138 @Test 139 public void testNullControllerLooper() { 140 MediaPlayerWrapper wrapper = MediaPlayerWrapper.wrap(null, mThread.getLooper()); 141 Assert.assertNull(wrapper); 142 143 wrapper = MediaPlayerWrapper.wrap(mMockController, null); 144 Assert.assertNull(wrapper); 145 } 146 147 /* 148 * Test to make sure that isReady() returns false if there is no playback state, 149 * there is no metadata, or if the metadata has no title. 150 */ 151 @Test 152 public void testIsReady() { 153 MediaPlayerWrapper wrapper = MediaPlayerWrapper.wrap(mMockController, mThread.getLooper()); 154 Assert.assertTrue(wrapper.isReady()); 155 156 // Test isReady() is false when the playback state is null 157 doReturn(null).when(mMockController).getPlaybackState(); 158 Assert.assertFalse(wrapper.isReady()); 159 160 // Restore the old playback state 161 doReturn(mTestState.build()).when(mMockController).getPlaybackState(); 162 Assert.assertTrue(wrapper.isReady()); 163 164 // Test isReady() is false when the metadata is null 165 doReturn(null).when(mMockController).getMetadata(); 166 Assert.assertFalse(wrapper.isReady()); 167 168 // Restore the old metadata 169 doReturn(mTestMetadata.build()).when(mMockController).getMetadata(); 170 Assert.assertTrue(wrapper.isReady()); 171 } 172 173 /* 174 * Test to make sure that if a new controller is registered with different metadata than the 175 * previous controller, the new metadata is pulled upon registration. 176 */ 177 @Test 178 public void testControllerUpdate() { 179 // Create the wrapper object and register the looper with the timeout handler 180 MediaPlayerWrapper wrapper = MediaPlayerWrapper.wrap(mMockController, mThread.getLooper()); 181 Assert.assertTrue(wrapper.isReady()); 182 wrapper.registerCallback(mTestCbs); 183 184 // Create a new MediaController that has different metadata than the previous controller 185 MediaController mUpdatedController = mock(MediaController.class); 186 doReturn(mTestState.build()).when(mUpdatedController).getPlaybackState(); 187 mTestMetadata.putString(MediaMetadata.METADATA_KEY_TITLE, "New Title"); 188 doReturn(mTestMetadata.build()).when(mUpdatedController).getMetadata(); 189 doReturn(null).when(mMockController).getQueue(); 190 191 // Update the wrappers controller to the new controller 192 wrapper.updateMediaController(mUpdatedController); 193 194 // Send a metadata update with the same data that the controller had upon registering 195 verify(mUpdatedController).registerCallback(mControllerCbs.capture(), any()); 196 MediaController.Callback controllerCallbacks = mControllerCbs.getValue(); 197 controllerCallbacks.onMetadataChanged(mTestMetadata.build()); 198 199 // Verify that a callback was never called since no data was updated 200 verify(mTestCbs, never()).mediaUpdatedCallback(any()); 201 } 202 203 /* 204 * Test to make sure that a media player update gets sent whenever a Media metadata or playback 205 * state change occurs instead of waiting for all data to be synced if the player doesn't 206 * support queues. 207 */ 208 @Test 209 public void testNoQueueMediaUpdates() { 210 // Create the wrapper object and register the looper with the timeout handler 211 TestLooperManager looperManager = new TestLooperManager(mThread.getLooper()); 212 MediaPlayerWrapper wrapper = 213 MediaPlayerWrapper.wrap(mMockController, mThread.getLooper()); 214 wrapper.registerCallback(mTestCbs); 215 216 // Return null when getting the queue 217 doReturn(null).when(mMockController).getQueue(); 218 219 // Grab the callbacks the wrapper registered with the controller 220 verify(mMockController).registerCallback(mControllerCbs.capture(), any()); 221 MediaController.Callback controllerCallbacks = mControllerCbs.getValue(); 222 223 // Update Metdata returned by controller 224 mTestMetadata.putString(MediaMetadata.METADATA_KEY_TITLE, "New Title"); 225 doReturn(mTestMetadata.build()).when(mMockController).getMetadata(); 226 controllerCallbacks.onMetadataChanged(mTestMetadata.build()); 227 228 // Assert that the metadata was updated and playback state wasn't 229 verify(mTestCbs, times(1)).mediaUpdatedCallback(mMediaUpdateData.capture()); 230 MediaData data = mMediaUpdateData.getValue(); 231 Assert.assertEquals( 232 "Returned Metadata isn't equal to given Metadata", 233 data.metadata, 234 Util.toMetadata(mTestMetadata.build())); 235 Assert.assertEquals( 236 "Returned PlaybackState isn't equal to original PlaybackState", 237 data.state.toString(), 238 mTestState.build().toString()); 239 Assert.assertEquals("Returned Queue isn't empty", data.queue.size(), 0); 240 241 // Update PlaybackState returned by controller 242 mTestState.setActiveQueueItemId(103); 243 doReturn(mTestState.build()).when(mMockController).getPlaybackState(); 244 controllerCallbacks.onPlaybackStateChanged(mTestState.build()); 245 246 // Assert that the PlaybackState was changed but metadata stayed the same 247 verify(mTestCbs, times(2)).mediaUpdatedCallback(mMediaUpdateData.capture()); 248 data = mMediaUpdateData.getValue(); 249 Assert.assertEquals( 250 "Returned PlaybackState isn't equal to given PlaybackState", 251 data.state.toString(), 252 mTestState.build().toString()); 253 Assert.assertEquals( 254 "Returned Metadata isn't equal to given Metadata", 255 data.metadata, 256 Util.toMetadata(mTestMetadata.build())); 257 Assert.assertEquals("Returned Queue isn't empty", data.queue.size(), 0); 258 259 // Verify that there are no timeout messages pending and there were no timeouts 260 Assert.assertFalse(wrapper.getTimeoutHandler().hasMessages(MSG_TIMEOUT)); 261 verify(mFailHandler, never()).onTerribleFailure(any(), any(), anyBoolean()); 262 } 263 264 /* 265 * This test updates the metadata and playback state returned by the 266 * controller then sends an update. This is to make sure that all relevant 267 * information is sent with every update. In the case without a queue, 268 * metadata and playback state are updated. 269 */ 270 @Test 271 public void testDataOnUpdateNoQueue() { 272 // Create the wrapper object and register the looper with the timeout handler 273 TestLooperManager looperManager = new TestLooperManager(mThread.getLooper()); 274 MediaPlayerWrapper wrapper = 275 MediaPlayerWrapper.wrap(mMockController, mThread.getLooper()); 276 wrapper.registerCallback(mTestCbs); 277 278 // Return null when getting the queue 279 doReturn(null).when(mMockController).getQueue(); 280 281 // Grab the callbacks the wrapper registered with the controller 282 verify(mMockController).registerCallback(mControllerCbs.capture(), any()); 283 MediaController.Callback controllerCallbacks = mControllerCbs.getValue(); 284 285 // Update Metadata returned by controller 286 mTestMetadata.putString(MediaMetadata.METADATA_KEY_TITLE, "New Title"); 287 doReturn(mTestMetadata.build()).when(mMockController).getMetadata(); 288 289 // Update PlaybackState returned by controller 290 mTestState.setActiveQueueItemId(103); 291 doReturn(mTestState.build()).when(mMockController).getPlaybackState(); 292 293 // Call the callback 294 controllerCallbacks.onPlaybackStateChanged(mTestState.build()); 295 296 // Assert that both metadata and playback state are there. 297 verify(mTestCbs, times(1)).mediaUpdatedCallback(mMediaUpdateData.capture()); 298 MediaData data = mMediaUpdateData.getValue(); 299 Assert.assertEquals( 300 "Returned PlaybackState isn't equal to given PlaybackState", 301 data.state.toString(), 302 mTestState.build().toString()); 303 Assert.assertEquals( 304 "Returned Metadata isn't equal to given Metadata", 305 data.metadata, 306 Util.toMetadata(mTestMetadata.build())); 307 Assert.assertEquals("Returned Queue isn't empty", data.queue.size(), 0); 308 309 // Verify that there are no timeout messages pending and there were no timeouts 310 Assert.assertFalse(wrapper.getTimeoutHandler().hasMessages(MSG_TIMEOUT)); 311 verify(mFailHandler, never()).onTerribleFailure(any(), any(), anyBoolean()); 312 } 313 314 /* 315 * This test checks whether getCurrentMetadata() returns the corresponding item from 316 * the now playing list instead of the current metadata if there is a match. 317 */ 318 @Test 319 public void testCurrentSongFromQueue() { 320 // Create the wrapper object and register the looper with the timeout handler 321 TestLooperManager looperManager = new TestLooperManager(mThread.getLooper()); 322 323 mTestState.setActiveQueueItemId(101); 324 doReturn(mTestState.build()).when(mMockController).getPlaybackState(); 325 326 MediaPlayerWrapper wrapper = 327 MediaPlayerWrapper.wrap(mMockController, mThread.getLooper()); 328 wrapper.registerCallback(mTestCbs); 329 330 // The current metadata doesn't contain track number info so check that 331 // field to see if the correct data was used. 332 Assert.assertEquals(wrapper.getCurrentMetadata().trackNum, "2"); 333 Assert.assertEquals(wrapper.getCurrentMetadata().numTracks, "3"); 334 } 335 336 @Test 337 public void testNullMetadata() { 338 // Create the wrapper object and register the looper with the timeout handler 339 TestLooperManager looperManager = new TestLooperManager(mThread.getLooper()); 340 MediaPlayerWrapper wrapper = 341 MediaPlayerWrapper.wrap(mMockController, mThread.getLooper()); 342 wrapper.registerCallback(mTestCbs); 343 344 // Return null when getting the queue 345 doReturn(null).when(mMockController).getQueue(); 346 347 // Grab the callbacks the wrapper registered with the controller 348 verify(mMockController).registerCallback(mControllerCbs.capture(), any()); 349 MediaController.Callback controllerCallbacks = mControllerCbs.getValue(); 350 351 // Update Metadata returned by controller 352 mTestMetadata.putString(MediaMetadata.METADATA_KEY_TITLE, "New Title"); 353 doReturn(mTestMetadata.build()).when(mMockController).getMetadata(); 354 355 // Call the callback 356 controllerCallbacks.onMetadataChanged(null); 357 358 // Assert that the metadata returned by getMetadata() is used instead of null 359 verify(mTestCbs, times(1)).mediaUpdatedCallback(mMediaUpdateData.capture()); 360 MediaData data = mMediaUpdateData.getValue(); 361 Assert.assertEquals("Returned metadata is incorrect", data.metadata, 362 Util.toMetadata(mTestMetadata.build())); 363 } 364 365 @Test 366 public void testNullQueue() { 367 // Create the wrapper object and register the looper with the timeout handler 368 TestLooperManager looperManager = new TestLooperManager(mThread.getLooper()); 369 MediaPlayerWrapper wrapper = 370 MediaPlayerWrapper.wrap(mMockController, mThread.getLooper()); 371 wrapper.registerCallback(mTestCbs); 372 373 // Return null when getting the queue 374 doReturn(null).when(mMockController).getQueue(); 375 376 // Grab the callbacks the wrapper registered with the controller 377 verify(mMockController).registerCallback(mControllerCbs.capture(), any()); 378 MediaController.Callback controllerCallbacks = mControllerCbs.getValue(); 379 380 // Call the callback 381 controllerCallbacks.onQueueChanged(null); 382 383 // Assert that both metadata and playback state are there. 384 verify(mTestCbs, times(1)).mediaUpdatedCallback(mMediaUpdateData.capture()); 385 MediaData data = mMediaUpdateData.getValue(); 386 Assert.assertEquals("Returned Queue isn't null", data.queue.size(), 0); 387 } 388 389 /* 390 * This test checks to see if the now playing queue data is cached. 391 */ 392 @Test 393 public void testQueueCached() { 394 // Create the wrapper object and register the looper with the timeout handler 395 TestLooperManager looperManager = new TestLooperManager(mThread.getLooper()); 396 MediaPlayerWrapper wrapper = 397 MediaPlayerWrapper.wrap(mMockController, mThread.getLooper()); 398 wrapper.registerCallback(mTestCbs); 399 400 // Call getCurrentQueue() multiple times. 401 for (int i = 0; i < 3; i++) { 402 Assert.assertEquals(wrapper.getCurrentQueue(), 403 Util.toMetadataList(getQueueFromDescriptions(mTestQueue))); 404 } 405 406 // Verify that getQueue() was only called twice. Once on creation and once during 407 // registration 408 verify(mMockController, times(2)).getQueue(); 409 } 410 411 /* 412 * This test sends repeated Playback State updates that only have a short 413 * position update change to see if they get debounced. 414 */ 415 @Test 416 public void testPlaybackStateUpdateSpam() { 417 // Create the wrapper object and register the looper with the timeout handler 418 TestLooperManager looperManager = new TestLooperManager(mThread.getLooper()); 419 MediaPlayerWrapper wrapper = 420 MediaPlayerWrapper.wrap(mMockController, mThread.getLooper()); 421 wrapper.registerCallback(mTestCbs); 422 423 // Return null when getting the queue 424 doReturn(null).when(mMockController).getQueue(); 425 426 // Grab the callbacks the wrapper registered with the controller 427 verify(mMockController).registerCallback(mControllerCbs.capture(), any()); 428 MediaController.Callback controllerCallbacks = mControllerCbs.getValue(); 429 430 // Update PlaybackState returned by controller (Should trigger update) 431 mTestState.setState(PlaybackState.STATE_PLAYING, 1000, 1.0f); 432 doReturn(mTestState.build()).when(mMockController).getPlaybackState(); 433 controllerCallbacks.onPlaybackStateChanged(mTestState.build()); 434 435 // Assert that both metadata and only the first playback state is there. 436 verify(mTestCbs, times(1)).mediaUpdatedCallback(mMediaUpdateData.capture()); 437 MediaData data = mMediaUpdateData.getValue(); 438 Assert.assertEquals( 439 "Returned PlaybackState isn't equal to given PlaybackState", 440 data.state.toString(), 441 mTestState.build().toString()); 442 Assert.assertEquals( 443 "Returned Metadata isn't equal to given Metadata", 444 data.metadata, 445 Util.toMetadata(mTestMetadata.build())); 446 Assert.assertEquals("Returned Queue isn't empty", data.queue.size(), 0); 447 448 // Update PlaybackState returned by controller (Shouldn't trigger update) 449 mTestState.setState(PlaybackState.STATE_PLAYING, 1020, 1.0f); 450 doReturn(mTestState.build()).when(mMockController).getPlaybackState(); 451 controllerCallbacks.onPlaybackStateChanged(mTestState.build()); 452 453 // Update PlaybackState returned by controller (Shouldn't trigger update) 454 mTestState.setState(PlaybackState.STATE_PLAYING, 1040, 1.0f); 455 doReturn(mTestState.build()).when(mMockController).getPlaybackState(); 456 controllerCallbacks.onPlaybackStateChanged(mTestState.build()); 457 458 // Update PlaybackState returned by controller (Should trigger update) 459 mTestState.setState(PlaybackState.STATE_PLAYING, 3000, 1.0f); 460 doReturn(mTestState.build()).when(mMockController).getPlaybackState(); 461 controllerCallbacks.onPlaybackStateChanged(mTestState.build()); 462 463 464 // Verify that there are no timeout messages pending and there were no timeouts 465 Assert.assertFalse(wrapper.getTimeoutHandler().hasMessages(MSG_TIMEOUT)); 466 verify(mFailHandler, never()).onTerribleFailure(any(), any(), anyBoolean()); 467 } 468 469 /* 470 * Check to make sure that cleanup tears down the object properly 471 */ 472 @Test 473 public void testWrapperCleanup() { 474 // Create the wrapper object and register the looper with the timeout handler 475 TestLooperManager looperManager = new TestLooperManager(mThread.getLooper()); 476 MediaPlayerWrapper wrapper = 477 MediaPlayerWrapper.wrap(mMockController, mThread.getLooper()); 478 wrapper.registerCallback(mTestCbs); 479 480 // Cleanup the wrapper 481 wrapper.cleanup(); 482 483 // Ensure that everything was cleaned up 484 verify(mMockController).unregisterCallback(any()); 485 Assert.assertNull(wrapper.getTimeoutHandler()); 486 } 487 488 /* 489 * Test to check that a PlaybackState of none is being ignored as that usually means that the 490 * MediaController isn't ready. 491 */ 492 @Test 493 public void testIgnorePlaystateNone() { 494 // Create the wrapper object and register the looper with the timeout handler 495 TestLooperManager looperManager = new TestLooperManager(mThread.getLooper()); 496 MediaPlayerWrapper wrapper = 497 MediaPlayerWrapper.wrap(mMockController, mThread.getLooper()); 498 wrapper.registerCallback(mTestCbs); 499 500 // Grab the callbacks the wrapper registered with the controller 501 verify(mMockController).registerCallback(mControllerCbs.capture(), any()); 502 MediaController.Callback controllerCallbacks = mControllerCbs.getValue(); 503 504 // Update PlaybackState returned by controller 505 mTestState.setState(PlaybackState.STATE_NONE, 0, 1.0f); 506 doReturn(mTestState.build()).when(mMockController).getPlaybackState(); 507 controllerCallbacks.onPlaybackStateChanged(mTestState.build()); 508 509 // Verify that there was no update 510 verify(mTestCbs, never()).mediaUpdatedCallback(any()); 511 512 // Verify that there are no timeout messages pending and there were no timeouts 513 Assert.assertFalse(wrapper.getTimeoutHandler().hasMessages(MSG_TIMEOUT)); 514 verify(mFailHandler, never()).onTerribleFailure(any(), any(), anyBoolean()); 515 } 516 517 /* 518 * Test to make sure that on a controller that supports browsing, the 519 * Media Metadata, Queue, and Playback state all have to be in sync in 520 * order for a media update to be sent via registered callback. 521 */ 522 @Test 523 public void testMetadataSync() { 524 // Create the wrapper object and register the looper with the timeout handler 525 TestLooperManager looperManager = new TestLooperManager(mThread.getLooper()); 526 MediaPlayerWrapper wrapper = 527 MediaPlayerWrapper.wrap(mMockController, mThread.getLooper()); 528 wrapper.registerCallback(mTestCbs); 529 530 // Grab the callbacks the wrapper registered with the controller 531 verify(mMockController).registerCallback(mControllerCbs.capture(), any()); 532 MediaController.Callback controllerCallbacks = mControllerCbs.getValue(); 533 534 // Update Metadata returned by controller 535 mTestMetadata.putString(MediaMetadata.METADATA_KEY_TITLE, "New Title"); 536 doReturn(mTestMetadata.build()).when(mMockController).getMetadata(); 537 controllerCallbacks.onMetadataChanged(mTestMetadata.build()); 538 539 // Update PlaybackState returned by controller 540 mTestState.setActiveQueueItemId(103); 541 doReturn(mTestState.build()).when(mMockController).getPlaybackState(); 542 controllerCallbacks.onPlaybackStateChanged(mTestState.build()); 543 544 // Update Queue returned by controller 545 mTestQueue.add( 546 new MediaDescription.Builder() 547 .setTitle("New Title") 548 .setSubtitle("BT Test Artist") 549 .setDescription("BT Test Album") 550 .setMediaId("103")); 551 doReturn(getQueueFromDescriptions(mTestQueue)).when(mMockController).getQueue(); 552 controllerCallbacks.onQueueChanged(getQueueFromDescriptions(mTestQueue)); 553 554 // Assert that the callback was called with the updated data 555 verify(mTestCbs, times(1)).mediaUpdatedCallback(mMediaUpdateData.capture()); 556 verify(mFailHandler, never()).onTerribleFailure(any(), any(), anyBoolean()); 557 MediaData data = mMediaUpdateData.getValue(); 558 Assert.assertEquals( 559 "Returned Metadata isn't equal to given Metadata", 560 data.metadata, 561 Util.toMetadata(mTestMetadata.build())); 562 Assert.assertEquals( 563 "Returned PlaybackState isn't equal to given PlaybackState", 564 data.state.toString(), 565 mTestState.build().toString()); 566 Assert.assertEquals( 567 "Returned Queue isn't equal to given Queue", 568 data.queue, 569 Util.toMetadataList(getQueueFromDescriptions(mTestQueue))); 570 571 // Verify that there are no timeout messages pending and there were no timeouts 572 Assert.assertFalse(wrapper.getTimeoutHandler().hasMessages(MSG_TIMEOUT)); 573 verify(mFailHandler, never()).onTerribleFailure(any(), any(), anyBoolean()); 574 } 575 576 /* 577 * Test to make sure that an error occurs when the MediaController fails to 578 * update all its media data in a resonable amount of time. 579 */ 580 @Test 581 public void testMetadataSyncFail() { 582 // Create the wrapper object and register the looper with the timeout handler 583 TestLooperManager looperManager = 584 InstrumentationRegistry.getInstrumentation() 585 .acquireLooperManager(mThread.getLooper()); 586 MediaPlayerWrapper wrapper = 587 MediaPlayerWrapper.wrap(mMockController, mThread.getLooper()); 588 wrapper.registerCallback(mTestCbs); 589 590 // Grab the callbacks the wrapper registered with the controller 591 verify(mMockController).registerCallback(mControllerCbs.capture(), any()); 592 MediaController.Callback controllerCallbacks = mControllerCbs.getValue(); 593 594 // Update Metadata returned by controller 595 mTestMetadata.putString(MediaMetadata.METADATA_KEY_TITLE, "Mismatch Title"); 596 doReturn(mTestMetadata.build()).when(mMockController).getMetadata(); 597 controllerCallbacks.onMetadataChanged(mTestMetadata.build()); 598 599 // Force the timeout to execute immediately 600 looperManager.execute(looperManager.next()); 601 602 // Assert that there was a timeout 603 verify(mFailHandler).onTerribleFailure(any(), any(), anyBoolean()); 604 605 // Assert that the callback was called with the mismatch data 606 verify(mTestCbs, times(1)).mediaUpdatedCallback(mMediaUpdateData.capture()); 607 MediaData data = mMediaUpdateData.getValue(); 608 Assert.assertEquals( 609 "Returned Metadata isn't equal to given Metadata", 610 data.metadata, 611 Util.toMetadata(mTestMetadata.build())); 612 Assert.assertEquals( 613 "Returned PlaybackState isn't equal to given PlaybackState", 614 data.state.toString(), 615 mTestState.build().toString()); 616 Assert.assertEquals( 617 "Returned Queue isn't equal to given Queue", 618 data.queue, 619 Util.toMetadataList(getQueueFromDescriptions(mTestQueue))); 620 } 621 622 /* 623 * testMetadataSyncFuzz() tests for the same conditions as testMetadataSync() 624 * but randomizes the order in which the MediaController update callbacks are 625 * called. The test is repeated 100 times for completeness. 626 */ 627 @Test 628 public void testMetadataSyncFuzz() { 629 // The number of times the random order test is run 630 final int numTestLoops = 100; 631 632 // Create the wrapper object and register the looper with the timeout handler 633 TestLooperManager looperManager = 634 InstrumentationRegistry.getInstrumentation() 635 .acquireLooperManager(mThread.getLooper()); 636 MediaPlayerWrapper wrapper = 637 MediaPlayerWrapper.wrap(mMockController, mThread.getLooper()); 638 wrapper.registerCallback(mTestCbs); 639 640 // Grab the callbacks the wrapper registered with the controller 641 verify(mMockController).registerCallback(mControllerCbs.capture(), any()); 642 MediaController.Callback controllerCallbacks = mControllerCbs.getValue(); 643 644 MediaMetadata.Builder m = new MediaMetadata.Builder(); 645 PlaybackState.Builder s = new PlaybackState.Builder(); 646 s.setState(PlaybackState.STATE_PAUSED, 0, 1.0f); 647 MediaDescription.Builder d = new MediaDescription.Builder(); 648 for (int i = 1; i <= numTestLoops; i++) { 649 // Setup Media Info for current itteration 650 m.putString(MediaMetadata.METADATA_KEY_TITLE, "BT Fuzz Song " + i); 651 m.putString(MediaMetadata.METADATA_KEY_ARTIST, "BT Fuzz Artist " + i); 652 m.putString(MediaMetadata.METADATA_KEY_ALBUM, "BT Fuzz Album " + i); 653 m.putLong(MediaMetadata.METADATA_KEY_DURATION, 5000L); 654 s.setActiveQueueItemId(i); 655 d.setTitle("BT Fuzz Song " + i); 656 d.setSubtitle("BT Fuzz Artist " + i); 657 d.setDescription("BT Fuzz Album " + i); 658 d.setMediaId(Integer.toString(i)); 659 660 // Create a new Queue each time to prevent double counting caused by 661 // Playback State matching the updated Queue 662 ArrayList<MediaSession.QueueItem> q = new ArrayList<MediaSession.QueueItem>(); 663 q.add(new MediaSession.QueueItem(d.build(), i)); 664 665 // Call the MediaController callbacks in a random order 666 ArrayList<Integer> callbackOrder = new ArrayList<>(Arrays.asList(0, 1, 2)); 667 Collections.shuffle(callbackOrder); 668 for (int j = 0; j < 3; j++) { 669 switch (callbackOrder.get(j)) { 670 case 0: // Update Metadata 671 doReturn(m.build()).when(mMockController).getMetadata(); 672 controllerCallbacks.onMetadataChanged(m.build()); 673 break; 674 case 1: // Update PlaybackState 675 doReturn(s.build()).when(mMockController).getPlaybackState(); 676 controllerCallbacks.onPlaybackStateChanged(s.build()); 677 break; 678 case 2: // Update Queue 679 doReturn(q).when(mMockController).getQueue(); 680 controllerCallbacks.onQueueChanged(q); 681 break; 682 } 683 } 684 685 // Check that the callback was called a certain number of times and 686 // that all the Media info matches what was given 687 verify(mTestCbs, times(i)).mediaUpdatedCallback(mMediaUpdateData.capture()); 688 MediaData data = mMediaUpdateData.getValue(); 689 Assert.assertEquals( 690 "Returned Metadata isn't equal to given Metadata", 691 data.metadata, 692 Util.toMetadata(m.build())); 693 Assert.assertEquals( 694 "Returned PlaybackState isn't equal to given PlaybackState", 695 data.state.toString(), 696 s.build().toString()); 697 Assert.assertEquals("Returned Queue isn't equal to given Queue", 698 data.queue, 699 Util.toMetadataList(q)); 700 } 701 702 // Verify that there are no timeout messages pending and there were no timeouts 703 Assert.assertFalse(wrapper.getTimeoutHandler().hasMessages(MSG_TIMEOUT)); 704 verify(mFailHandler, never()).onTerribleFailure(any(), any(), anyBoolean()); 705 } 706} 707