mirror of
https://github.com/shaka-project/shaka-player.git
synced 2026-06-25 17:45:03 +03:00
064089152b
This is an automated fix to be inline with Google style guides, this was created with eslint's --fix option. Change-Id: I860eecbc8152603e730aa17a1393f16d26b3b6fc
405 lines
13 KiB
JavaScript
405 lines
13 KiB
JavaScript
/**
|
|
* @license
|
|
* Copyright 2016 Google Inc.
|
|
*
|
|
* 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.
|
|
*/
|
|
|
|
describe('CastReceiver', function() {
|
|
const CastReceiver = shaka.cast.CastReceiver;
|
|
const CastUtils = shaka.cast.CastUtils;
|
|
|
|
const originalCast = window['cast'];
|
|
const originalUserAgent = navigator.userAgent;
|
|
|
|
let eventManager = new shaka.util.EventManager();
|
|
|
|
let mockReceiverManager;
|
|
let mockReceiverApi;
|
|
let mockShakaMessageBus;
|
|
let mockGenericMessageBus;
|
|
|
|
/** @type {shaka.cast.CastReceiver} */
|
|
let receiver;
|
|
/** @type {shaka.Player} */
|
|
let player;
|
|
/** @type {HTMLVideoElement} */
|
|
let video;
|
|
|
|
/** @type {shaka.util.PublicPromise} */
|
|
let messageWaitPromise;
|
|
|
|
/** @type {Array.<function()>} */
|
|
let toRestore;
|
|
let pendingWaitWrapperCalls = 0;
|
|
|
|
/** @type {boolean} */
|
|
let isChrome;
|
|
/** @type {boolean} */
|
|
let isChromecast;
|
|
/** @type {!Object.<string, ?shakaExtern.DrmSupportType>} */
|
|
let support = {};
|
|
|
|
let fakeInitState;
|
|
|
|
/**
|
|
* Before running the test, check if this is Chrome or Chromecast, and maybe
|
|
* if Widevine is supported.
|
|
* @param {function(function()=)} test
|
|
* @param {boolean=} opt_checkKeySystems
|
|
* @return {function(function())}
|
|
*/
|
|
function checkAndRun(test, opt_checkKeySystems) {
|
|
let check = function(done) {
|
|
if (opt_checkKeySystems && !support['com.widevine.alpha']) {
|
|
pending('Skipping DrmEngine tests.');
|
|
} else if (!isChromecast && !isChrome) {
|
|
pending(
|
|
'Skipping CastReceiver tests for non-Chrome and non-Chromecast');
|
|
} else {
|
|
test(done);
|
|
}
|
|
};
|
|
// Account for tests with a done argument, and tests without.
|
|
if (test.length == 1) {
|
|
return (done) => check(done);
|
|
}
|
|
return () => check(undefined);
|
|
}
|
|
|
|
/**
|
|
* Before running the test, check if this is Chrome or Chromecast, and if
|
|
* Widevine is supported.
|
|
* @param {function(function()=)} test
|
|
* @return {function(function())}
|
|
*/
|
|
function checkAndRunWithDrm(test) {
|
|
return checkAndRun(test, /* opt_checkKeySystems */ true);
|
|
}
|
|
|
|
beforeAll(function(done) {
|
|
let supportTest = shaka.media.DrmEngine.probeSupport()
|
|
.then(function(result) { support = result; })
|
|
.catch(fail);
|
|
|
|
// The receiver is only meant to run on the Chromecast, so we have the
|
|
// ability to use modern APIs there that may not be available on all of the
|
|
// browsers our library supports. Because of this, CastReceiver tests will
|
|
// only be run on Chrome and Chromecast.
|
|
isChromecast = navigator.userAgent.indexOf('CrKey') >= 0;
|
|
let isEdge = navigator.userAgent.indexOf('Edge/') >= 0;
|
|
// Edge also has "Chrome/" in its user agent string.
|
|
isChrome = navigator.userAgent.indexOf('Chrome/') >= 0 && !isEdge;
|
|
|
|
// Don't do any more work here if the tests will not end up running.
|
|
if (!isChromecast && !isChrome) return;
|
|
|
|
// In uncompiled mode, there is a UA check for Chromecast in order to make
|
|
// manual testing easier. For these automated tests, we want to act as if
|
|
// we are running on the Chromecast, even in Chrome.
|
|
// Since we can't write to window.navigator or navigator.userAgent, we use
|
|
// Object.defineProperty.
|
|
Object.defineProperty(window['navigator'],
|
|
'userAgent', {value: 'CrKey', configurable: true});
|
|
|
|
shaka.net.NetworkingEngine.registerScheme('test', shaka.test.TestScheme);
|
|
shaka.media.ManifestParser.registerParserByMime(
|
|
'application/x-test-manifest',
|
|
shaka.test.TestScheme.ManifestParser);
|
|
let createManifests = shaka.test.TestScheme.createManifests(shaka, '');
|
|
|
|
Promise.all([createManifests, supportTest]).then(done);
|
|
});
|
|
|
|
beforeEach(checkAndRun(() => {
|
|
mockReceiverApi = createMockReceiverApi();
|
|
|
|
let mockCanDisplayType = jasmine.createSpy('canDisplayType');
|
|
mockCanDisplayType.and.returnValue(true);
|
|
|
|
// We're using quotes to access window.cast because the compiler
|
|
// knows about lots of Cast-specific APIs we aren't mocking. We
|
|
// don't need this mock strictly type-checked.
|
|
window['cast'] = {
|
|
receiver: mockReceiverApi,
|
|
__platform__: {canDisplayType: mockCanDisplayType}
|
|
};
|
|
|
|
mockReceiverManager = createMockReceiverManager();
|
|
mockShakaMessageBus = createMockMessageBus();
|
|
mockGenericMessageBus = createMockMessageBus();
|
|
|
|
video = /** @type {!HTMLVideoElement} */ (document.createElement('video'));
|
|
video.width = 600;
|
|
video.height = 400;
|
|
video.muted = true;
|
|
document.body.appendChild(video);
|
|
|
|
player = new shaka.Player(video);
|
|
receiver = new CastReceiver(video, player);
|
|
|
|
toRestore = [];
|
|
pendingWaitWrapperCalls = 0;
|
|
|
|
fakeInitState = {
|
|
player: {
|
|
configure: {}
|
|
},
|
|
playerAfterLoad: {
|
|
setTextTrackVisibility: true
|
|
},
|
|
video: {
|
|
loop: true,
|
|
playbackRate: 5
|
|
},
|
|
manifest: 'test:sintel_no_text',
|
|
startTime: 0
|
|
};
|
|
}));
|
|
|
|
afterEach(function(done) {
|
|
toRestore.forEach(function(restoreCallback) {
|
|
restoreCallback();
|
|
});
|
|
|
|
receiver.destroy().catch(fail).then(function() {
|
|
document.body.removeChild(video);
|
|
|
|
player = null;
|
|
video = null;
|
|
receiver = null;
|
|
|
|
done();
|
|
});
|
|
});
|
|
|
|
afterAll(function() {
|
|
if (originalUserAgent) {
|
|
window['cast'] = originalCast;
|
|
Object.defineProperty(window['navigator'],
|
|
'userAgent', {value: originalUserAgent});
|
|
}
|
|
});
|
|
|
|
drm_it('sends reasonably-sized updates', checkAndRunWithDrm((done) => {
|
|
// Use an encrypted asset, to make sure DRM info doesn't balloon the size.
|
|
fakeInitState.manifest = 'test:sintel-enc';
|
|
|
|
eventManager.listenOnce(video, 'loadeddata', function() {
|
|
// Wait for an update message.
|
|
waitForUpdateMessage().then(function(message) {
|
|
// Check that the update message is of a reasonable size.
|
|
expect(message.length).toBeLessThan(5000);
|
|
}).then(done);
|
|
});
|
|
addOnError(done);
|
|
|
|
// Start the process of loading by sending a fake init message.
|
|
fakeConnectedSenders(1);
|
|
fakeIncomingMessage({
|
|
type: 'init',
|
|
initState: fakeInitState,
|
|
appData: {}
|
|
}, mockShakaMessageBus);
|
|
}));
|
|
|
|
drm_it('has a reasonable average message size', checkAndRunWithDrm((done) => {
|
|
// Use an encrypted asset, to make sure DRM info doesn't balloon the size.
|
|
fakeInitState.manifest = 'test:sintel-enc';
|
|
|
|
eventManager.listenOnce(video, 'loadeddata', function() {
|
|
// Collect 50 update messages, and average their length.
|
|
// Not all properties are passed along on every update message, so
|
|
// the average length is expected to be lower than the length of the first
|
|
// update message.
|
|
let totalLength = 0;
|
|
let waitForUpdate = Promise.resolve();
|
|
for (let i = 0; i < 50; i++) {
|
|
waitForUpdate = waitForUpdate.then(function() {
|
|
return waitForUpdateMessage();
|
|
}).then(function(message) {
|
|
totalLength += message.length;
|
|
});
|
|
}
|
|
waitForUpdate.then(function() {
|
|
expect(totalLength / 50).toBeLessThan(3000);
|
|
}).then(done);
|
|
});
|
|
addOnError(done);
|
|
|
|
// Start the process of loading by sending a fake init message.
|
|
fakeConnectedSenders(1);
|
|
fakeIncomingMessage({
|
|
type: 'init',
|
|
initState: fakeInitState,
|
|
appData: {}
|
|
}, mockShakaMessageBus);
|
|
}));
|
|
|
|
it('sends update messages at every stage of loading', checkAndRun((done) => {
|
|
// Add wrappers to various methods along player.load to make sure that,
|
|
// at each stage, the cast receiver can form an update message without
|
|
// causing an error.
|
|
waitForUpdateMessageWrapper(
|
|
shaka.media.ManifestParser, 'ManifestParser', 'getFactory');
|
|
waitForUpdateMessageWrapper(shaka.test.TestScheme.ManifestParser.prototype,
|
|
'ManifestParser', 'start');
|
|
waitForUpdateMessageWrapper(
|
|
shaka.media.DrmEngine.prototype, 'DrmEngine', 'init');
|
|
waitForUpdateMessageWrapper(
|
|
shaka.media.DrmEngine.prototype, 'DrmEngine', 'attach');
|
|
waitForUpdateMessageWrapper(
|
|
shaka.media.StreamingEngine.prototype, 'StreamingEngine', 'init');
|
|
|
|
eventManager.listenOnce(video, 'loadeddata', function() {
|
|
// Make sure that each of the methods covered by
|
|
// waitForUpdateMessageWrapper is called by this point.
|
|
expect(pendingWaitWrapperCalls).toBe(0);
|
|
|
|
// Wait for a final update message before proceeding.
|
|
waitForUpdateMessage().then(done);
|
|
});
|
|
addOnError(done);
|
|
|
|
// Start the process of loading by sending a fake init message.
|
|
fakeConnectedSenders(1);
|
|
fakeIncomingMessage({
|
|
type: 'init',
|
|
initState: fakeInitState,
|
|
appData: {}
|
|
}, mockShakaMessageBus);
|
|
}));
|
|
|
|
/**
|
|
* Creates a wrapper around a method on a given prototype, which makes it
|
|
* wait on waitForUpdateMessage before returning, and registers that wrapper
|
|
* to be uninstalled afterwards.
|
|
* The replaced method is expected to be a method that returns a promise.
|
|
* @param {!Object} prototype
|
|
* @param {!string} name
|
|
* @param {!string} methodName
|
|
*/
|
|
function waitForUpdateMessageWrapper(prototype, name, methodName) {
|
|
pendingWaitWrapperCalls += 1;
|
|
let original = prototype[methodName];
|
|
prototype[methodName] = /** @this {Object} @return {*} */ function() {
|
|
pendingWaitWrapperCalls -= 1;
|
|
shaka.log.debug(
|
|
'Waiting for update message before calling ' +
|
|
name + '.' + methodName + '...');
|
|
let originalArguments = arguments;
|
|
return waitForUpdateMessage().then(function() {
|
|
return original.apply(this, originalArguments);
|
|
}.bind(this));
|
|
};
|
|
toRestore.push(function() {
|
|
prototype[methodName] = original;
|
|
});
|
|
}
|
|
|
|
function addOnError(done) {
|
|
let onError = function(event) {
|
|
fail(event.detail);
|
|
done();
|
|
};
|
|
player.addEventListener('error', onError);
|
|
}
|
|
|
|
function waitForUpdateMessage() {
|
|
messageWaitPromise = new shaka.util.PublicPromise();
|
|
return messageWaitPromise;
|
|
}
|
|
|
|
function createMockReceiverApi() {
|
|
return {
|
|
CastReceiverManager: {
|
|
getInstance: function() { return mockReceiverManager; }
|
|
}
|
|
};
|
|
}
|
|
|
|
function createMockReceiverManager() {
|
|
return {
|
|
start: jasmine.createSpy('CastReceiverManager.start'),
|
|
stop: jasmine.createSpy('CastReceiverManager.stop'),
|
|
setSystemVolumeLevel:
|
|
jasmine.createSpy('CastReceiverManager.setSystemVolumeLevel'),
|
|
setSystemVolumeMuted:
|
|
jasmine.createSpy('CastReceiverManager.setSystemVolumeMuted'),
|
|
getSenders: jasmine.createSpy('CastReceiverManager.getSenders'),
|
|
getSystemVolume: function() { return {level: 1, muted: false}; },
|
|
getCastMessageBus: function(namespace) {
|
|
if (namespace == CastUtils.SHAKA_MESSAGE_NAMESPACE) {
|
|
return mockShakaMessageBus;
|
|
}
|
|
|
|
return mockGenericMessageBus;
|
|
}
|
|
};
|
|
}
|
|
|
|
function createMockMessageBus() {
|
|
let bus = {
|
|
messages: [],
|
|
broadcast: jasmine.createSpy('CastMessageBus.broadcast'),
|
|
getCastChannel: jasmine.createSpy('CastMessageBus.getCastChannel')
|
|
};
|
|
// For convenience, deserialize and store sent messages.
|
|
bus.broadcast.and.callFake(function(message) {
|
|
bus.messages.push(CastUtils.deserialize(message));
|
|
// Check to see if it's an update message.
|
|
let parsed = CastUtils.deserialize(message);
|
|
if (parsed.type == 'update' && messageWaitPromise) {
|
|
shaka.log.debug('Received update message. Proceeding...');
|
|
messageWaitPromise.resolve(message);
|
|
messageWaitPromise = null;
|
|
}
|
|
});
|
|
let channel = {
|
|
messages: [],
|
|
send: function(message) {
|
|
channel.messages.push(CastUtils.deserialize(message));
|
|
}
|
|
};
|
|
bus.getCastChannel.and.returnValue(channel);
|
|
return bus;
|
|
}
|
|
|
|
/**
|
|
* @param {number} num
|
|
*/
|
|
function fakeConnectedSenders(num) {
|
|
let senderArray = [];
|
|
while (num--) {
|
|
senderArray.push('senderId');
|
|
}
|
|
|
|
mockReceiverManager.getSenders.and.returnValue(senderArray);
|
|
mockReceiverManager.onSenderConnected();
|
|
}
|
|
|
|
/**
|
|
* @param {?} message
|
|
* @param {!Object} bus
|
|
* @param {string=} opt_senderId
|
|
*/
|
|
function fakeIncomingMessage(message, bus, opt_senderId) {
|
|
let serialized = CastUtils.serialize(message);
|
|
let messageEvent = {
|
|
senderId: opt_senderId,
|
|
data: serialized
|
|
};
|
|
bus.onMessage(messageEvent);
|
|
}
|
|
});
|