1028 lines
35 KiB
JavaScript
1028 lines
35 KiB
JavaScript
/**
|
|
* Copyright (C) 2018 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*
|
|
* @fileoverview Unit tests for receiver.
|
|
*/
|
|
|
|
goog.module('exoplayer.cast.test.receiver');
|
|
goog.setTestOnly();
|
|
|
|
const ConfigurationFactory = goog.require('exoplayer.cast.ConfigurationFactory');
|
|
const MessageDispatcher = goog.require('exoplayer.cast.MessageDispatcher');
|
|
const Player = goog.require('exoplayer.cast.Player');
|
|
const Receiver = goog.require('exoplayer.cast.Receiver');
|
|
const mocks = goog.require('exoplayer.cast.test.mocks');
|
|
const testSuite = goog.require('goog.testing.testSuite');
|
|
const util = goog.require('exoplayer.cast.test.util');
|
|
|
|
/** @type {?Player|undefined} */
|
|
let player;
|
|
/** @type {!Array<!MediaItem>} */
|
|
let queue = [];
|
|
let shakaFake;
|
|
let castContextMock;
|
|
|
|
/**
|
|
* Sends a message to the receiver under test.
|
|
*
|
|
* @param {!Object} message The message to send as json.
|
|
*/
|
|
const sendMessage = function(message) {
|
|
mocks.state().customMessageListener({
|
|
data: message,
|
|
senderId: 'sender0',
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Creates a valid media item with the suffix appended to each field.
|
|
*
|
|
* @param {string} suffix The suffix to append to the fields value.
|
|
* @return {!Object} The media item.
|
|
*/
|
|
const createMediaItem = function(suffix) {
|
|
return {
|
|
uuid: 'uuid' + suffix,
|
|
media: {uri: 'uri' + suffix},
|
|
mimeType: 'application/dash+xml',
|
|
};
|
|
};
|
|
|
|
let messageSequence = 0;
|
|
|
|
/**
|
|
* Creates a message in the format sent bey the sender app.
|
|
*
|
|
* @param {string} method The name of the method.
|
|
* @param {?Object} args The arguments.
|
|
* @return {!Object} The message.
|
|
*/
|
|
const createMessage = function (method, args) {
|
|
return {
|
|
method: method,
|
|
args: args,
|
|
sequenceNumber: ++messageSequence,
|
|
};
|
|
};
|
|
|
|
/**
|
|
* Asserts the `playerState` is in the same state as just after creation of the
|
|
* player.
|
|
*
|
|
* @param {!PlayerState} playerState The player state to assert.
|
|
* @param {string} playbackState The expected playback state.
|
|
*/
|
|
const assertInitialState = function(playerState, playbackState) {
|
|
assertEquals(playbackState, playerState.playbackState);
|
|
// Assert the state is in initial state.
|
|
assertArrayEquals([], queue);
|
|
assertEquals(0, playerState.windowCount);
|
|
assertEquals(0, playerState.windowIndex);
|
|
assertUndefined(playerState.playbackError);
|
|
assertNull(playerState.playbackPosition);
|
|
// Assert player properties.
|
|
assertEquals(0, player.getDurationMs());
|
|
assertArrayEquals([], Object.entries(player.mediaItemInfoMap_));
|
|
assertEquals(0, player.windowPeriodIndex_);
|
|
assertEquals(999, player.playbackType_);
|
|
assertEquals(0, player.getCurrentWindowIndex());
|
|
assertEquals(Player.DUMMY_MEDIA_ITEM_INFO, player.windowMediaItemInfo_);
|
|
};
|
|
|
|
|
|
testSuite({
|
|
setUp() {
|
|
mocks.setUp();
|
|
shakaFake = mocks.createShakaFake();
|
|
castContextMock = mocks.createCastReceiverContextFake();
|
|
player = new Player(shakaFake, new ConfigurationFactory());
|
|
player.addPlayerListener((playerState) => {
|
|
queue = playerState.mediaQueue;
|
|
});
|
|
const messageDispatcher = new MessageDispatcher(
|
|
'urn:x-cast:com.google.exoplayer.cast', castContextMock);
|
|
new Receiver(player, castContextMock, messageDispatcher);
|
|
},
|
|
|
|
tearDown() {
|
|
queue = [];
|
|
},
|
|
|
|
/** Tests whether a status was sent to the sender on connect. */
|
|
testNotifyClientConnected() {
|
|
assertUndefined(mocks.state().outputMessages['sender0']);
|
|
|
|
sendMessage(createMessage('player.onClientConnected', {}));
|
|
const message = mocks.state().outputMessages['sender0'][0];
|
|
assertEquals(messageSequence, message.sequenceNumber);
|
|
},
|
|
|
|
/**
|
|
* Tests whether a custom message listener has been registered after
|
|
* construction.
|
|
*/
|
|
testCustomMessageListener() {
|
|
assertTrue(goog.isFunction(mocks.state().customMessageListener));
|
|
},
|
|
|
|
/** Tests set playWhenReady. */
|
|
testSetPlayWhenReady() {
|
|
let playWhenReady;
|
|
player.addPlayerListener((playerState) => {
|
|
playWhenReady = playerState.playWhenReady;
|
|
});
|
|
|
|
sendMessage(createMessage(
|
|
'player.setPlayWhenReady',
|
|
{ playWhenReady: true }
|
|
));
|
|
assertTrue(playWhenReady);
|
|
sendMessage(createMessage(
|
|
'player.setPlayWhenReady',
|
|
{ playWhenReady: false }
|
|
));
|
|
assertFalse(playWhenReady);
|
|
},
|
|
|
|
/** Tests setting repeat modes. */
|
|
testSetRepeatMode() {
|
|
let repeatMode;
|
|
player.addQueueItems(0, util.queue.slice(0, 3));
|
|
player.prepare();
|
|
player.addPlayerListener((playerState) => {
|
|
repeatMode = playerState.repeatMode;
|
|
});
|
|
|
|
sendMessage(createMessage(
|
|
'player.setRepeatMode',
|
|
{ repeatMode: Player.RepeatMode.ONE }
|
|
));
|
|
assertEquals(Player.RepeatMode.ONE, repeatMode);
|
|
assertEquals(0, player.getNextWindowIndex());
|
|
assertEquals(0, player.getPreviousWindowIndex());
|
|
|
|
sendMessage(createMessage(
|
|
'player.setRepeatMode',
|
|
{ repeatMode: Player.RepeatMode.ALL }
|
|
));
|
|
assertEquals(Player.RepeatMode.ALL, repeatMode);
|
|
assertEquals(1, player.getNextWindowIndex());
|
|
assertEquals(2, player.getPreviousWindowIndex());
|
|
|
|
sendMessage(createMessage(
|
|
'player.setRepeatMode',
|
|
{ repeatMode: Player.RepeatMode.OFF }
|
|
));
|
|
assertEquals(Player.RepeatMode.OFF, repeatMode);
|
|
assertEquals(1, player.getNextWindowIndex());
|
|
assertTrue(player.getPreviousWindowIndex() < 0);
|
|
},
|
|
|
|
/** Tests setting an invalid repeat mode value. */
|
|
testSetRepeatMode_invalid_noStateChange() {
|
|
let repeatMode;
|
|
player.addPlayerListener((playerState) => {
|
|
repeatMode = playerState.repeatMode;
|
|
});
|
|
|
|
sendMessage(createMessage(
|
|
'player.setRepeatMode',
|
|
{ repeatMode: "UNKNOWN" }
|
|
));
|
|
assertEquals(Player.RepeatMode.OFF, player.repeatMode_);
|
|
assertUndefined(repeatMode);
|
|
player.invalidate();
|
|
assertEquals(Player.RepeatMode.OFF, repeatMode);
|
|
},
|
|
|
|
/** Tests enabling and disabling shuffle mode. */
|
|
testSetShuffleModeEnabled() {
|
|
const enableMessage = createMessage('player.setShuffleModeEnabled', {
|
|
shuffleModeEnabled: true,
|
|
});
|
|
const disableMessage = createMessage('player.setShuffleModeEnabled', {
|
|
shuffleModeEnabled: false,
|
|
});
|
|
let shuffleModeEnabled;
|
|
player.addPlayerListener((state) => {
|
|
shuffleModeEnabled = state.shuffleModeEnabled;
|
|
});
|
|
assertFalse(player.shuffleModeEnabled_);
|
|
sendMessage(enableMessage);
|
|
assertTrue(shuffleModeEnabled);
|
|
sendMessage(disableMessage);
|
|
assertFalse(shuffleModeEnabled);
|
|
},
|
|
|
|
/** Tests adding a single media item to the queue. */
|
|
testAddMediaItem_single() {
|
|
const suffix = '0';
|
|
const jsonMessage = createMessage('player.addItems', {
|
|
index: 0,
|
|
items: [
|
|
createMediaItem(suffix),
|
|
],
|
|
shuffleOrder: [0],
|
|
});
|
|
|
|
sendMessage(jsonMessage);
|
|
assertEquals(1, queue.length);
|
|
assertEquals('uuid0', queue[0].uuid);
|
|
assertEquals('uri0', queue[0].media.uri);
|
|
assertArrayEquals([0], player.shuffleOrder_);
|
|
},
|
|
|
|
/** Tests adding multiple media items to the queue. */
|
|
testAddMediaItem_multiple() {
|
|
const shuffleOrder = [0, 2, 1];
|
|
const jsonMessage = createMessage('player.addItems', {
|
|
index: 0,
|
|
items: [
|
|
createMediaItem('0'),
|
|
createMediaItem('1'),
|
|
createMediaItem('2'),
|
|
],
|
|
shuffleOrder: shuffleOrder,
|
|
});
|
|
|
|
sendMessage(jsonMessage);
|
|
assertArrayEquals(['uuid0', 'uuid1', 'uuid2'], queue.map((x) => x.uuid));
|
|
assertArrayEquals(shuffleOrder, player.shuffleOrder_);
|
|
},
|
|
|
|
/** Tests adding a media item to end of the queue by omitting the index. */
|
|
testAddMediaItem_noindex_addstoend() {
|
|
const shuffleOrder = [1, 3, 2, 0];
|
|
const jsonMessage = createMessage('player.addItems', {
|
|
items: [createMediaItem('99')],
|
|
shuffleOrder: shuffleOrder,
|
|
});
|
|
player.addQueueItems(0, util.queue.slice(0, 3), [0, 2, 1]);
|
|
let queue = [];
|
|
player.addPlayerListener((playerState) => {
|
|
queue = playerState.mediaQueue;
|
|
});
|
|
sendMessage(jsonMessage);
|
|
assertEquals(4, queue.length);
|
|
assertEquals('uuid99', queue[3].uuid);
|
|
assertArrayEquals(shuffleOrder, player.shuffleOrder_);
|
|
},
|
|
|
|
/** Tests adding items with a shuffle order of invalid length. */
|
|
testAddMediaItems_invalidShuffleOrderLength() {
|
|
const shuffleOrder = [1, 3, 2];
|
|
const jsonMessage = createMessage('player.addItems', {
|
|
items: [createMediaItem('99')],
|
|
shuffleOrder: shuffleOrder,
|
|
});
|
|
player.addQueueItems(0, util.queue.slice(0, 3), [0, 2, 1]);
|
|
let queue = [];
|
|
player.addPlayerListener((playerState) => {
|
|
queue = playerState.mediaQueue;
|
|
});
|
|
sendMessage(jsonMessage);
|
|
assertEquals(4, queue.length);
|
|
assertEquals('uuid99', queue[3].uuid);
|
|
assertEquals(4, player.shuffleOrder_.length);
|
|
},
|
|
|
|
/** Tests inserting a media item to the queue. */
|
|
testAddMediaItem_insert() {
|
|
const index = 1;
|
|
const shuffleOrder = [1, 0, 3, 2, 4];
|
|
const firstInsertionMessage = createMessage('player.addItems', {
|
|
index,
|
|
items: [
|
|
createMediaItem('99'),
|
|
createMediaItem('100'),
|
|
],
|
|
shuffleOrder,
|
|
});
|
|
const prepareMessage = createMessage('player.prepare', {});
|
|
const secondInsertionMessage = createMessage('player.addItems', {
|
|
index,
|
|
items: [
|
|
createMediaItem('199'),
|
|
createMediaItem('1100'),
|
|
],
|
|
shuffleOrder,
|
|
});
|
|
// fill with three items
|
|
player.addQueueItems(0, util.queue.slice(0, 3), [0, 2, 1]);
|
|
player.seekToUuid('uuid99', 0);
|
|
|
|
sendMessage(firstInsertionMessage);
|
|
// The window index does not change when IDLE.
|
|
assertEquals(1, player.getCurrentWindowIndex());
|
|
assertEquals(5, queue.length);
|
|
assertArrayEquals(shuffleOrder, player.shuffleOrder_);
|
|
util.assertUuidIndexMap(player.queueUuidIndexMap_, queue);
|
|
|
|
// Prepare sets the index by the uuid to which we seeked.
|
|
sendMessage(prepareMessage);
|
|
assertEquals(1, player.getCurrentWindowIndex());
|
|
util.assertUuidIndexMap(player.queueUuidIndexMap_, queue);
|
|
// Add two items at the current window index.
|
|
sendMessage(secondInsertionMessage);
|
|
// Current window index is adjusted.
|
|
assertEquals(3, player.getCurrentWindowIndex());
|
|
assertEquals(7, queue.length);
|
|
assertEquals('uuid199', queue[index].uuid);
|
|
assertEquals(7, player.shuffleOrder_.length);
|
|
util.assertUuidIndexMap(player.queueUuidIndexMap_, queue);
|
|
},
|
|
|
|
/** Tests adding a media item with an index larger than the queue size. */
|
|
testAddMediaItem_indexLargerThanQueueSize_addsToEnd() {
|
|
const index = 4;
|
|
const jsonMessage = createMessage('player.addItems', {
|
|
index: index,
|
|
items: [
|
|
createMediaItem('99'),
|
|
createMediaItem('100'),
|
|
],
|
|
shuffleOrder: [],
|
|
});
|
|
player.addQueueItems(0, util.queue.slice(0, 3), [0, 2, 1]);
|
|
|
|
assertArrayEquals(['uuid0', 'uuid1', 'uuid2'], queue.map((x) => x.uuid));
|
|
sendMessage(jsonMessage);
|
|
assertArrayEquals(['uuid0', 'uuid1', 'uuid2', 'uuid99', 'uuid100'],
|
|
queue.map((x) => x.uuid));
|
|
util.assertUuidIndexMap(player.queueUuidIndexMap_, queue);
|
|
},
|
|
|
|
/** Tests removing an item from the queue. */
|
|
testRemoveMediaItem() {
|
|
const jsonMessage =
|
|
createMessage('player.removeItems', {uuids: ['uuid1', 'uuid0']});
|
|
player.addQueueItems(0, util.queue.slice(0, 3), [0, 2, 1]);
|
|
assertArrayEquals(['uuid0', 'uuid1', 'uuid2'], queue.map((x) => x.uuid));
|
|
|
|
sendMessage(jsonMessage);
|
|
assertArrayEquals(['uuid2'], queue.map((x) => x.uuid));
|
|
assertArrayEquals([0], player.shuffleOrder_);
|
|
util.assertUuidIndexMap(player.queueUuidIndexMap_, queue);
|
|
},
|
|
|
|
/** Tests removing the currently playing item from the queue. */
|
|
async testRemoveMediaItem_currentItem() {
|
|
const jsonMessage =
|
|
createMessage('player.removeItems', {uuids: ['uuid1', 'uuid0']});
|
|
player.addQueueItems(0, util.queue.slice(0, 3), [0, 2, 1]);
|
|
player.seekToWindow(1, 0);
|
|
player.prepare();
|
|
|
|
await sendMessage(jsonMessage);
|
|
assertArrayEquals(['uuid2'], queue.map((x) => x.uuid));
|
|
assertEquals(0, player.getCurrentWindowIndex());
|
|
assertEquals(util.queue[2].media.uri, shakaFake.getMediaElement().src);
|
|
assertArrayEquals([0], player.shuffleOrder_);
|
|
util.assertUuidIndexMap(player.queueUuidIndexMap_, queue);
|
|
},
|
|
|
|
/** Tests removing items which affect the current window index. */
|
|
async testRemoveMediaItem_affectsWindowIndex() {
|
|
const jsonMessage =
|
|
createMessage('player.removeItems', {uuids: ['uuid1', 'uuid0']});
|
|
const currentUri = util.queue[4].media.uri;
|
|
player.addQueueItems(0, util.queue.slice(0, 6), [3, 2, 1, 4, 0, 5]);
|
|
player.prepare();
|
|
await player.seekToWindow(4, 2000);
|
|
assertEquals(currentUri, shakaFake.getMediaElement().src);
|
|
|
|
sendMessage(jsonMessage);
|
|
assertEquals(4, queue.length);
|
|
assertEquals('uuid4', queue[player.getCurrentWindowIndex()].uuid);
|
|
assertEquals(2, player.getCurrentWindowIndex());
|
|
assertEquals(currentUri, shakaFake.getMediaElement().src);
|
|
assertArrayEquals([1, 0, 2, 3], player.shuffleOrder_);
|
|
util.assertUuidIndexMap(player.queueUuidIndexMap_, queue);
|
|
},
|
|
|
|
/** Tests removing the last item of the queue. */
|
|
testRemoveMediaItem_firstItem_windowIndexIsCorrect() {
|
|
const jsonMessage =
|
|
createMessage('player.removeItems', {uuids: ['uuid0']});
|
|
player.addQueueItems(0, util.queue.slice(0, 3), [0, 2, 1]);
|
|
player.seekToWindow(1, 0);
|
|
|
|
sendMessage(jsonMessage);
|
|
assertArrayEquals(['uuid1', 'uuid2'], queue.map((x) => x.uuid));
|
|
assertEquals(0, player.getCurrentWindowIndex());
|
|
assertArrayEquals([1, 0], player.shuffleOrder_);
|
|
util.assertUuidIndexMap(player.queueUuidIndexMap_, queue);
|
|
},
|
|
|
|
/** Tests removing the last item of the queue. */
|
|
testRemoveMediaItem_lastItem_windowIndexIsCorrect() {
|
|
const jsonMessage =
|
|
createMessage('player.removeItems', {uuids: ['uuid2']});
|
|
player.addQueueItems(0, util.queue.slice(0, 3), [0, 2, 1]);
|
|
player.seekToWindow(2, 0);
|
|
player.prepare();
|
|
|
|
mocks.state().isSilent = true;
|
|
const states = [];
|
|
player.addPlayerListener((playerState) => {
|
|
states.push(playerState);
|
|
});
|
|
sendMessage(jsonMessage);
|
|
assertArrayEquals(['uuid0', 'uuid1'], queue.map((x) => x.uuid));
|
|
assertEquals(1, player.getCurrentWindowIndex());
|
|
assertArrayEquals([0, 1], player.shuffleOrder_);
|
|
assertEquals(1, states.length);
|
|
assertEquals(Player.PlaybackState.BUFFERING, states[0].playbackState);
|
|
assertEquals(
|
|
Player.DiscontinuityReason.PERIOD_TRANSITION,
|
|
states[0].playbackPosition.discontinuityReason);
|
|
assertEquals(
|
|
Player.DUMMY_MEDIA_ITEM_INFO, states[0].mediaItemsInfo['uuid1']);
|
|
util.assertUuidIndexMap(player.queueUuidIndexMap_, queue);
|
|
},
|
|
|
|
/** Tests removing items all items. */
|
|
testRemoveMediaItem_removeAll() {
|
|
const jsonMessage = createMessage('player.removeItems',
|
|
{uuids: ['uuid1', 'uuid0', 'uuid2']});
|
|
player.addQueueItems(0, util.queue.slice(0, 3));
|
|
player.seekToWindow(2, 2000);
|
|
player.prepare();
|
|
let playerState;
|
|
player.addPlayerListener((state) => {
|
|
playerState = state;
|
|
});
|
|
|
|
sendMessage(jsonMessage);
|
|
assertInitialState(playerState, 'ENDED');
|
|
assertEquals(0, player.getCurrentWindowIndex());
|
|
assertArrayEquals([], player.shuffleOrder_);
|
|
assertEquals(Player.PlaybackState.ENDED, player.getPlaybackState());
|
|
util.assertUuidIndexMap(player.queueUuidIndexMap_, []);
|
|
},
|
|
|
|
/** Tests moving an item in the queue. */
|
|
testMoveItem() {
|
|
let shuffleOrder = [0, 2, 1];
|
|
const jsonMessage = createMessage('player.moveItem', {
|
|
uuid: 'uuid2',
|
|
index: 0,
|
|
shuffleOrder: shuffleOrder,
|
|
});
|
|
player.addQueueItems(0, util.queue.slice(0, 3));
|
|
|
|
assertArrayEquals(['uuid0', 'uuid1', 'uuid2'], queue.map((x) => x.uuid));
|
|
sendMessage(jsonMessage);
|
|
assertArrayEquals(['uuid2', 'uuid0', 'uuid1'], queue.map((x) => x.uuid));
|
|
assertArrayEquals(shuffleOrder, player.shuffleOrder_);
|
|
util.assertUuidIndexMap(player.queueUuidIndexMap_, queue);
|
|
},
|
|
|
|
/** Tests moving the currently playing item in the queue. */
|
|
testMoveItem_currentWindowIndex() {
|
|
let shuffleOrder = [0, 2, 1];
|
|
const jsonMessage = createMessage('player.moveItem', {
|
|
uuid: 'uuid2',
|
|
index: 0,
|
|
shuffleOrder: shuffleOrder,
|
|
});
|
|
player.addQueueItems(0, util.queue.slice(0, 3));
|
|
player.prepare();
|
|
player.seekToUuid('uuid2', 0);
|
|
assertEquals(2, player.getCurrentWindowIndex());
|
|
|
|
assertArrayEquals(['uuid0', 'uuid1', 'uuid2'], queue.map((x) => x.uuid));
|
|
sendMessage(jsonMessage);
|
|
assertArrayEquals(['uuid2', 'uuid0', 'uuid1'], queue.map((x) => x.uuid));
|
|
assertEquals(0, player.getCurrentWindowIndex());
|
|
assertArrayEquals(shuffleOrder, player.shuffleOrder_);
|
|
util.assertUuidIndexMap(player.queueUuidIndexMap_, queue);
|
|
},
|
|
|
|
/** Tests moving an item from before to after the currently playing item. */
|
|
testMoveItem_decreaseCurrentWindowIndex() {
|
|
const jsonMessage = createMessage('player.moveItem', {
|
|
uuid: 'uuid0',
|
|
index: 5,
|
|
shuffleOrder: [],
|
|
});
|
|
player.addQueueItems(0, util.queue.slice(0, 6));
|
|
player.prepare();
|
|
player.seekToWindow(2, 0);
|
|
assertEquals(2, player.getCurrentWindowIndex());
|
|
|
|
assertArrayEquals(['uuid0', 'uuid1', 'uuid2', 'uuid3', 'uuid4', 'uuid5'],
|
|
queue.map((x) => x.uuid));
|
|
sendMessage(jsonMessage);
|
|
assertArrayEquals(['uuid1', 'uuid2', 'uuid3', 'uuid4', 'uuid5', 'uuid0'],
|
|
queue.map((x) => x.uuid));
|
|
assertEquals(1, player.getCurrentWindowIndex());
|
|
util.assertUuidIndexMap(player.queueUuidIndexMap_, queue);
|
|
},
|
|
|
|
/** Tests moving an item from after to before the currently playing item. */
|
|
testMoveItem_increaseCurrentWindowIndex() {
|
|
const jsonMessage = createMessage('player.moveItem', {
|
|
uuid: 'uuid5',
|
|
index: 0,
|
|
shuffleOrder: [],
|
|
});
|
|
player.addQueueItems(0, util.queue.slice(0, 6));
|
|
player.prepare();
|
|
player.seekToWindow(2, 0);
|
|
assertEquals(2, player.getCurrentWindowIndex());
|
|
|
|
assertArrayEquals(['uuid0', 'uuid1', 'uuid2', 'uuid3', 'uuid4', 'uuid5'],
|
|
queue.map((x) => x.uuid));
|
|
sendMessage(jsonMessage);
|
|
assertArrayEquals(['uuid5', 'uuid0', 'uuid1', 'uuid2', 'uuid3', 'uuid4'],
|
|
queue.map((x) => x.uuid));
|
|
assertEquals(3, player.getCurrentWindowIndex());
|
|
util.assertUuidIndexMap(player.queueUuidIndexMap_, queue);
|
|
},
|
|
|
|
/** Tests moving an item from after to the current window index. */
|
|
testMoveItem_toCurrentWindowIndex_fromAfter() {
|
|
const jsonMessage = createMessage('player.moveItem', {
|
|
uuid: 'uuid5',
|
|
index: 2,
|
|
shuffleOrder: [],
|
|
});
|
|
player.addQueueItems(0, util.queue.slice(0, 6));
|
|
player.prepare();
|
|
player.seekToWindow(2, 0);
|
|
assertEquals(2, player.getCurrentWindowIndex());
|
|
|
|
assertArrayEquals(['uuid0', 'uuid1', 'uuid2', 'uuid3', 'uuid4', 'uuid5'],
|
|
queue.map((x) => x.uuid));
|
|
sendMessage(jsonMessage);
|
|
assertArrayEquals(['uuid0', 'uuid1', 'uuid5', 'uuid2', 'uuid3', 'uuid4'],
|
|
queue.map((x) => x.uuid));
|
|
assertEquals(3, player.getCurrentWindowIndex());
|
|
util.assertUuidIndexMap(player.queueUuidIndexMap_, queue);
|
|
},
|
|
|
|
/** Tests moving an item from before to the current window index. */
|
|
testMoveItem_toCurrentWindowIndex_fromBefore() {
|
|
const jsonMessage = createMessage('player.moveItem', {
|
|
uuid: 'uuid0',
|
|
index: 2,
|
|
shuffleOrder: [],
|
|
});
|
|
player.addQueueItems(0, util.queue.slice(0, 6));
|
|
player.prepare();
|
|
player.seekToWindow(2, 0);
|
|
assertEquals(2, player.getCurrentWindowIndex());
|
|
|
|
assertArrayEquals(['uuid0', 'uuid1', 'uuid2', 'uuid3', 'uuid4', 'uuid5'],
|
|
queue.map((x) => x.uuid));
|
|
sendMessage(jsonMessage);
|
|
assertArrayEquals(['uuid1', 'uuid2', 'uuid0', 'uuid3', 'uuid4', 'uuid5'],
|
|
queue.map((x) => x.uuid));
|
|
assertEquals(1, player.getCurrentWindowIndex());
|
|
util.assertUuidIndexMap(player.queueUuidIndexMap_, queue);
|
|
},
|
|
|
|
/** Tests seekTo. */
|
|
testSeekTo() {
|
|
const jsonMessage = createMessage('player.seekTo',
|
|
{
|
|
'uuid': 'uuid1',
|
|
'positionMs': 2000
|
|
});
|
|
player.addQueueItems(0, util.queue.slice(0, 3));
|
|
player.prepare();
|
|
sendMessage(jsonMessage);
|
|
assertEquals(2000, player.getCurrentPositionMs());
|
|
assertEquals(1, player.getCurrentWindowIndex());
|
|
},
|
|
|
|
/** Tests seekTo to unknown uuid. */
|
|
testSeekTo_unknownUuid() {
|
|
const jsonMessage = createMessage('player.seekTo',
|
|
{
|
|
'uuid': 'unknown',
|
|
});
|
|
player.addQueueItems(0, util.queue.slice(0, 3));
|
|
player.prepare();
|
|
player.seekToWindow(1, 2000);
|
|
assertEquals(2000, player.getCurrentPositionMs());
|
|
assertEquals(1, player.getCurrentWindowIndex());
|
|
|
|
sendMessage(jsonMessage);
|
|
assertEquals(2000, player.getCurrentPositionMs());
|
|
assertEquals(1, player.getCurrentWindowIndex());
|
|
},
|
|
|
|
/** Tests seekTo without position. */
|
|
testSeekTo_noPosition_defaultsToZero() {
|
|
const jsonMessage = createMessage('player.seekTo',
|
|
{
|
|
'uuid': 'uuid1',
|
|
});
|
|
player.addQueueItems(0, util.queue.slice(0, 3));
|
|
player.prepare();
|
|
sendMessage(jsonMessage);
|
|
assertEquals(0, player.getCurrentPositionMs());
|
|
assertEquals(1, player.getCurrentWindowIndex());
|
|
},
|
|
|
|
/** Tests seekTo to negative position. */
|
|
testSeekTo_negativePosition_defaultsToZero() {
|
|
const jsonMessage = createMessage('player.seekTo',
|
|
{
|
|
'uuid': 'uuid2',
|
|
'positionMs': -1,
|
|
});
|
|
player.addQueueItems(0, util.queue.slice(0, 3));
|
|
player.prepare();
|
|
player.seekToWindow(1, 2000);
|
|
assertEquals(2000, player.getCurrentPositionMs());
|
|
assertEquals(1, player.getCurrentWindowIndex());
|
|
|
|
sendMessage(jsonMessage);
|
|
assertEquals(0, player.getCurrentPositionMs());
|
|
assertEquals(2, player.getCurrentWindowIndex());
|
|
},
|
|
|
|
/** Tests whether validation is turned on. */
|
|
testMediaItemValidation_isOn() {
|
|
const index = 0;
|
|
const mediaItem = createMediaItem('99');
|
|
delete mediaItem.uuid;
|
|
const jsonMessage = createMessage('player.addItems', {
|
|
index: index,
|
|
items: [mediaItem],
|
|
shuffleOrder: [],
|
|
});
|
|
|
|
sendMessage(jsonMessage);
|
|
assertEquals(0, queue.length);
|
|
},
|
|
|
|
/** Tests whether the state is sent to sender apps on state transition. */
|
|
testPlayerStateIsSent_withCorrectSequenceNumber() {
|
|
assertUndefined(mocks.state().outputMessages['sender0']);
|
|
const playMessage =
|
|
createMessage('player.setPlayWhenReady', {playWhenReady: true});
|
|
sendMessage(playMessage);
|
|
|
|
const playerState = mocks.state().outputMessages['sender0'][0];
|
|
assertTrue(playerState.playWhenReady);
|
|
assertEquals(playMessage.sequenceNumber, playerState.sequenceNumber);
|
|
},
|
|
|
|
/** Tests whether a connect of a sender app sends the current player state. */
|
|
testSenderConnection() {
|
|
const onSenderConnected = mocks.state().onSenderConnected;
|
|
assertTrue(goog.isFunction(onSenderConnected));
|
|
onSenderConnected({senderId: 'sender0'});
|
|
|
|
const playerState = mocks.state().outputMessages['sender0'][0];
|
|
assertEquals(Player.RepeatMode.OFF, playerState.repeatMode);
|
|
assertEquals('IDLE', playerState.playbackState);
|
|
assertArrayEquals([], playerState.mediaQueue);
|
|
assertEquals(-1, playerState.sequenceNumber);
|
|
},
|
|
|
|
/** Tests whether a disconnect of a sender notifies the message dispatcher. */
|
|
testSenderDisconnection_callsMessageDispatcher() {
|
|
mocks.setUp();
|
|
let notifiedSenderId;
|
|
const myPlayer = new Player(mocks.createShakaFake());
|
|
const myManagerFake = mocks.createCastReceiverContextFake();
|
|
new Receiver(myPlayer, myManagerFake, {
|
|
registerActionHandler() {},
|
|
notifySenderDisconnected(senderId) {
|
|
notifiedSenderId = senderId;
|
|
},
|
|
});
|
|
|
|
const onSenderDisconnected = mocks.state().onSenderDisconnected;
|
|
assertTrue(goog.isFunction(onSenderDisconnected));
|
|
onSenderDisconnected({senderId: 'sender0'});
|
|
assertEquals('sender0', notifiedSenderId);
|
|
},
|
|
|
|
/**
|
|
* Tests whether the state right after creation of the player matches
|
|
* expectations.
|
|
*/
|
|
testInitialState() {
|
|
mocks.state().isSilent = true;
|
|
let playerState;
|
|
player.addPlayerListener((state) => {
|
|
playerState = state;
|
|
});
|
|
assertEquals(0, player.getCurrentPositionMs());
|
|
// Dump a player state to the listener.
|
|
player.invalidate();
|
|
// Asserts the state just after creation.
|
|
assertInitialState(playerState, 'IDLE');
|
|
},
|
|
|
|
/** Tests whether user properties can be changed when in IDLE state */
|
|
testChangingUserPropertiesWhenIdle() {
|
|
mocks.state().isSilent = true;
|
|
const states = [];
|
|
let counter = 0;
|
|
player.addPlayerListener((state) => {
|
|
states.push(state);
|
|
});
|
|
// Adding items when IDLE.
|
|
player.addQueueItems(0, util.queue.slice(0, 3));
|
|
counter++;
|
|
assertEquals(counter, states.length);
|
|
assertEquals(Player.PlaybackState.IDLE, states[counter - 1].playbackState);
|
|
assertArrayEquals(
|
|
['uuid0', 'uuid1', 'uuid2'],
|
|
states[counter - 1].mediaQueue.map((i) => i.uuid));
|
|
|
|
// Set playWhenReady when IDLE.
|
|
assertFalse(player.getPlayWhenReady());
|
|
player.setPlayWhenReady(true);
|
|
counter++;
|
|
assertTrue(player.getPlayWhenReady());
|
|
assertEquals(counter, states.length);
|
|
assertEquals(Player.PlaybackState.IDLE, states[counter - 1].playbackState);
|
|
|
|
// Seeking when IDLE.
|
|
player.seekToUuid('uuid2', 1000);
|
|
counter++;
|
|
// Window index not set when idle.
|
|
assertEquals(2, player.getCurrentWindowIndex());
|
|
assertEquals(1000, player.getCurrentPositionMs());
|
|
assertEquals(counter, states.length);
|
|
assertEquals(Player.PlaybackState.IDLE, states[counter - 1].playbackState);
|
|
// But window index is set when prepared.
|
|
player.prepare();
|
|
assertEquals(2, player.getCurrentWindowIndex());
|
|
},
|
|
|
|
/** Tests the state after calling prepare. */
|
|
testPrepare() {
|
|
mocks.state().isSilent = true;
|
|
const states = [];
|
|
let counter = 0;
|
|
player.addPlayerListener((state) => {
|
|
states.push(state);
|
|
});
|
|
const prepareMessage = createMessage('player.prepare', {});
|
|
|
|
player.addQueueItems(0, util.queue.slice(0, 3));
|
|
player.seekToWindow(1, 1000);
|
|
counter += 2;
|
|
|
|
// Sends prepare message.
|
|
sendMessage(prepareMessage);
|
|
counter++;
|
|
assertEquals(counter, states.length);
|
|
assertEquals('uuid1', states[counter - 1].playbackPosition.uuid);
|
|
assertEquals(
|
|
Player.PlaybackState.BUFFERING, states[counter - 1].playbackState);
|
|
|
|
// Fakes Shaka events.
|
|
mocks.state().isSilent = false;
|
|
mocks.notifyListeners('streaming');
|
|
mocks.notifyListeners('loadeddata');
|
|
counter += 2;
|
|
assertEquals(counter, states.length);
|
|
assertEquals(Player.PlaybackState.READY, states[counter - 1].playbackState);
|
|
},
|
|
|
|
/** Tests stopping the player with `reset=true`. */
|
|
testStop_resetTrue() {
|
|
mocks.state().isSilent = true;
|
|
let playerState;
|
|
player.addPlayerListener((state) => {
|
|
playerState = state;
|
|
});
|
|
const stopMessage = createMessage('player.stop', {reset: true});
|
|
|
|
player.setRepeatMode(Player.RepeatMode.ALL);
|
|
player.setShuffleModeEnabled(true);
|
|
player.setPlayWhenReady(true);
|
|
player.addQueueItems(0, util.queue.slice(0, 3));
|
|
player.prepare();
|
|
mocks.state().isSilent = false;
|
|
mocks.notifyListeners('loadeddata');
|
|
assertArrayEquals(['uuid0', 'uuid1', 'uuid2'], queue.map((i) => i.uuid));
|
|
assertEquals(0, playerState.windowIndex);
|
|
assertNotEquals(Player.DUMMY_MEDIA_ITEM_INFO, player.windowMediaItemInfo_);
|
|
assertEquals(1, player.playbackType_);
|
|
// Stop the player.
|
|
sendMessage(stopMessage);
|
|
// Asserts the state looks the same as just after creation.
|
|
assertInitialState(playerState, 'IDLE');
|
|
assertNull(playerState.playbackPosition);
|
|
// Assert player properties are preserved.
|
|
assertTrue(playerState.shuffleModeEnabled);
|
|
assertTrue(playerState.playWhenReady);
|
|
assertEquals(Player.RepeatMode.ALL, playerState.repeatMode);
|
|
},
|
|
|
|
/** Tests stopping the player with `reset=false`. */
|
|
testStop_resetFalse() {
|
|
mocks.state().isSilent = true;
|
|
let playerState;
|
|
player.addPlayerListener((state) => {
|
|
playerState = state;
|
|
});
|
|
const stopMessage = createMessage('player.stop', {reset: false});
|
|
|
|
player.addQueueItems(0, util.queue.slice(0, 3));
|
|
player.prepare();
|
|
player.seekToUuid('uuid1', 1000);
|
|
mocks.state().isSilent = false;
|
|
mocks.notifyListeners('streaming');
|
|
mocks.notifyListeners('trackschanged');
|
|
mocks.notifyListeners('loadeddata');
|
|
assertArrayEquals(['uuid0', 'uuid1', 'uuid2'], queue.map((i) => i.uuid));
|
|
assertEquals(1, playerState.windowIndex);
|
|
assertNotEquals(Player.DUMMY_MEDIA_ITEM_INFO, player.windowMediaItemInfo_);
|
|
assertEquals(2, player.playbackType_);
|
|
// Stop the player.
|
|
sendMessage(stopMessage);
|
|
assertEquals('IDLE', playerState.playbackState);
|
|
assertUndefined(playerState.playbackError);
|
|
// Assert the timeline is preserved.
|
|
assertEquals(3, queue.length);
|
|
assertEquals(3, playerState.windowCount);
|
|
assertEquals(1, player.windowIndex_);
|
|
assertEquals(1, playerState.windowIndex);
|
|
// Assert the playback position is correct.
|
|
assertEquals(1000, playerState.playbackPosition.positionMs);
|
|
assertEquals('uuid1', playerState.playbackPosition.uuid);
|
|
assertEquals(0, playerState.playbackPosition.periodId);
|
|
assertNull(playerState.playbackPosition.discontinuityReason);
|
|
assertEquals(1000, player.getCurrentPositionMs());
|
|
// Assert player properties are preserved.
|
|
assertEquals(20000, player.getDurationMs());
|
|
assertEquals(2, Object.entries(player.mediaItemInfoMap_).length);
|
|
assertEquals(0, player.windowPeriodIndex_);
|
|
assertEquals(1, player.getCurrentWindowIndex());
|
|
assertEquals(1, player.windowIndex_);
|
|
assertNotEquals(Player.DUMMY_MEDIA_ITEM_INFO, player.windowMediaItemInfo_);
|
|
assertEquals(999, player.playbackType_);
|
|
assertEquals('uuid1', player.uuidToPrepare_);
|
|
},
|
|
|
|
/**
|
|
* Tests the state after having removed the last item in the queue. This
|
|
* resolves to the same state like calling `stop(true)` except that the state
|
|
* is ENDED and the queue is naturally empty and hence the windowIndex is
|
|
* unset.
|
|
*/
|
|
testRemoveLastQueueItem() {
|
|
mocks.state().isSilent = true;
|
|
let playerState;
|
|
player.addPlayerListener((state) => {
|
|
playerState = state;
|
|
});
|
|
const removeAllItemsMessage = createMessage(
|
|
'player.removeItems', {uuids: ['uuid0', 'uuid1', 'uuid2']});
|
|
|
|
player.addQueueItems(0, util.queue.slice(0, 3));
|
|
player.seekToWindow(0, 1000);
|
|
player.prepare();
|
|
mocks.state().isSilent = false;
|
|
mocks.notifyListeners('loadeddata');
|
|
// Remove all items.
|
|
sendMessage(removeAllItemsMessage);
|
|
// Assert the state after removal of all items.
|
|
assertInitialState(playerState, 'ENDED');
|
|
},
|
|
|
|
/** Tests whether a player state is sent when no item is added. */
|
|
testAddItem_noop() {
|
|
mocks.state().isSilent = true;
|
|
let playerStates = [];
|
|
player.addPlayerListener((state) => {
|
|
playerStates.push(state);
|
|
});
|
|
const noOpMessage = createMessage('player.addItems', {
|
|
index: 0,
|
|
items: [
|
|
util.queue[0],
|
|
],
|
|
shuffleOrder: [0],
|
|
});
|
|
player.addQueueItems(0, [util.queue[0]], []);
|
|
player.prepare();
|
|
assertEquals(2, playerStates.length);
|
|
assertEquals(2, mocks.state().outputMessages['sender0'].length);
|
|
sendMessage(noOpMessage);
|
|
assertEquals(2, playerStates.length);
|
|
assertEquals(3, mocks.state().outputMessages['sender0'].length);
|
|
},
|
|
|
|
/** Tests whether a player state is sent when no item is removed. */
|
|
testRemoveItem_noop() {
|
|
mocks.state().isSilent = true;
|
|
let playerStates = [];
|
|
player.addPlayerListener((state) => {
|
|
playerStates.push(state);
|
|
});
|
|
const noOpMessage =
|
|
createMessage('player.removeItems', {uuids: ['uuid00']});
|
|
player.addQueueItems(0, util.queue.slice(0, 3));
|
|
player.prepare();
|
|
assertEquals(2, playerStates.length);
|
|
assertEquals(2, mocks.state().outputMessages['sender0'].length);
|
|
sendMessage(noOpMessage);
|
|
assertEquals(2, playerStates.length);
|
|
assertEquals(3, mocks.state().outputMessages['sender0'].length);
|
|
},
|
|
|
|
/** Tests whether a player state is sent when item is not moved. */
|
|
testMoveItem_noop() {
|
|
mocks.state().isSilent = true;
|
|
let playerStates = [];
|
|
player.addPlayerListener((state) => {
|
|
playerStates.push(state);
|
|
});
|
|
const noOpMessage = createMessage('player.moveItem', {
|
|
uuid: 'uuid00',
|
|
index: 0,
|
|
shuffleOrder: [],
|
|
});
|
|
player.addQueueItems(0, util.queue.slice(0, 3));
|
|
player.prepare();
|
|
assertEquals(2, playerStates.length);
|
|
assertEquals(2, mocks.state().outputMessages['sender0'].length);
|
|
sendMessage(noOpMessage);
|
|
assertEquals(2, playerStates.length);
|
|
assertEquals(3, mocks.state().outputMessages['sender0'].length);
|
|
},
|
|
|
|
/** Tests whether playback actions send a state when no-op */
|
|
testNoOpPlaybackActionsSendPlayerState() {
|
|
mocks.state().isSilent = true;
|
|
let playerStates = [];
|
|
let parsedMessage;
|
|
player.addPlayerListener((state) => {
|
|
playerStates.push(state);
|
|
});
|
|
player.addQueueItems(0, util.queue.slice(0, 3));
|
|
player.prepare();
|
|
|
|
const outputMessages = mocks.state().outputMessages['sender0'];
|
|
const setupMessageCount = playerStates.length;
|
|
let totalMessageCount = setupMessageCount;
|
|
assertEquals(setupMessageCount, playerStates.length);
|
|
assertEquals(totalMessageCount, outputMessages.length);
|
|
|
|
const firstNoOpMessage = createMessage('player.setPlayWhenReady', {
|
|
playWhenReady: false,
|
|
});
|
|
let expectedSequenceNumber = firstNoOpMessage.sequenceNumber;
|
|
|
|
sendMessage(firstNoOpMessage);
|
|
totalMessageCount++;
|
|
assertEquals(setupMessageCount, playerStates.length);
|
|
assertEquals(totalMessageCount, outputMessages.length);
|
|
parsedMessage = outputMessages[totalMessageCount - 1];
|
|
assertEquals(expectedSequenceNumber++, parsedMessage.sequenceNumber);
|
|
|
|
sendMessage(createMessage('player.setRepeatMode', {
|
|
repeatMode: 'OFF',
|
|
}));
|
|
totalMessageCount++;
|
|
assertEquals(setupMessageCount, playerStates.length);
|
|
assertEquals(totalMessageCount, outputMessages.length);
|
|
parsedMessage = outputMessages[totalMessageCount - 1];
|
|
assertEquals(expectedSequenceNumber++, parsedMessage.sequenceNumber);
|
|
|
|
sendMessage(createMessage('player.setShuffleModeEnabled', {
|
|
shuffleModeEnabled: false,
|
|
}));
|
|
totalMessageCount++;
|
|
assertEquals(setupMessageCount, playerStates.length);
|
|
assertEquals(totalMessageCount, outputMessages.length);
|
|
parsedMessage = outputMessages[totalMessageCount - 1];
|
|
assertEquals(expectedSequenceNumber++, parsedMessage.sequenceNumber);
|
|
|
|
sendMessage(createMessage('player.seekTo', {
|
|
uuid: 'not_existing',
|
|
positionMs: 0,
|
|
}));
|
|
totalMessageCount++;
|
|
assertEquals(setupMessageCount, playerStates.length);
|
|
assertEquals(totalMessageCount, outputMessages.length);
|
|
parsedMessage = outputMessages[totalMessageCount - 1];
|
|
assertEquals(expectedSequenceNumber++, parsedMessage.sequenceNumber);
|
|
},
|
|
});
|