Files
shaka-player/spec/content_database_spec.js
T
Jacob Trimble e74ad98eaa Added setNetworkCallback method to dash and offline video sources.
This allows the app to intercept media requests to modify its URLs.
The callback accepts the URL for the request and returns a modified
URL or null to use the original.

Closes #148

Change-Id: I08352754ace05f318706fd93910097c0fa7696f0
2015-08-24 09:35:27 -07:00

382 lines
12 KiB
JavaScript

/**
* Copyright 2014 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.
*
* @fileoverview content_database.js unit tests.
*/
goog.require('shaka.media.SegmentIndex');
goog.require('shaka.media.SegmentReference');
goog.require('shaka.media.StreamInfo');
goog.require('shaka.player.DrmSchemeInfo');
goog.require('shaka.util.ContentDatabase');
goog.require('shaka.util.ContentDatabaseReader');
goog.require('shaka.util.ContentDatabaseWriter');
goog.require('shaka.util.PublicPromise');
describe('ContentDatabase', function() {
var fakeIndexSource, fakeInitSource;
var reader, writer, p, testIndex, testReferences, streamInfo;
var originalTimeout, originalFailoverUri, originalName;
const url = 'http://example.com';
const mime = 'video/phony';
const codecs = 'phony';
const duration = 100;
const testInitData = new ArrayBuffer(1024);
const keySystem = 'test.widevine.com';
const licenseServerUrl = 'www.licenseServer.com';
const expectedReferences = [
{ start_time: 0, end_time: 2 },
{ start_time: 2, end_time: 4 },
{ start_time: 4, end_time: null }
];
const drmScheme = new shaka.player.DrmSchemeInfo(
keySystem, licenseServerUrl, false, null);
var customMatchers = {
toMatchReference: function(util) {
return {
compare: function(actual, expected) {
var result = {};
result.pass =
util.equals(actual.start_time, expected.start_time) &&
util.equals(actual.end_time, expected.end_time) &&
actual.url.toString().match(/idb\:\/\/.+\/.+/);
return result;
}
};
}
};
beforeAll(function() {
jasmine.addMatchers(customMatchers);
// Change the timeout.
originalTimeout = jasmine.DEFAULT_TIMEOUT_INTERVAL;
jasmine.DEFAULT_TIMEOUT_INTERVAL = 5000; // ms
// Set up mock FailoverUri.
originalFailoverUri = shaka.util.FailoverUri;
var mockFailoverUri = function(callback, url, startByte, endByte) {
return {
fetch: function() {
return Promise.resolve(new ArrayBuffer(768 * 1024));
}
};
};
shaka.util.FailoverUri = mockFailoverUri;
testReferences = [
new shaka.media.SegmentReference(0, 1, createFailover(url, 0, 5)),
new shaka.media.SegmentReference(1, 2, createFailover(url, 6, 9)),
new shaka.media.SegmentReference(2, 3, createFailover(url, 10, 15)),
new shaka.media.SegmentReference(3, 4, createFailover(url, 16, 19)),
new shaka.media.SegmentReference(4, null, createFailover(url, 20, null))
];
testIndex = new shaka.media.SegmentIndex(testReferences);
// Use a database name which will not affect the test app.
originalName = shaka.util.ContentDatabase.DB_NAME;
shaka.util.ContentDatabase.DB_NAME += '_test';
// Start each test run with a clean slate.
(new shaka.util.ContentDatabase('readwrite', null)).deleteDatabase();
fakeIndexSource = {
destroy: function() {},
create: function() { return Promise.resolve(testIndex); }
};
fakeInitSource = {
destroy: function() {},
create: function() { return Promise.resolve(testInitData); }
};
});
beforeEach(function() {
reader = new shaka.util.ContentDatabaseReader();
writer = new shaka.util.ContentDatabaseWriter(null, null);
p = reader.setUpDatabase().then(
function() {
return writer.setUpDatabase();
});
streamInfo = new shaka.media.StreamInfo();
streamInfo.mimeType = mime;
streamInfo.codecs = codecs;
streamInfo.segmentIndexSource = fakeIndexSource;
streamInfo.segmentInitSource = fakeInitSource;
});
afterEach(function() {
reader.closeDatabaseConnection();
writer.closeDatabaseConnection();
});
afterAll(function() {
// Restore the timeout.
jasmine.DEFAULT_TIMEOUT_INTERVAL = originalTimeout;
// Restore FailoverUri.
shaka.util.FailoverUri = originalFailoverUri;
// Restore DB name.
shaka.util.ContentDatabase.DB_NAME = originalName;
});
it('deletes the database', function(done) {
p.then(function() {
reader.closeDatabaseConnection();
return writer.deleteDatabase();
}).then(function() {
var p = new shaka.util.PublicPromise();
var request = window.indexedDB.open(shaka.util.ContentDatabase.DB_NAME);
// onupgradeneeded is only called if the database does not already exist.
request.onupgradeneeded = function(e) {
// Cancel the creation of a new database.
e.target.transaction.abort();
p.resolve(true);
};
request.onsuccess = function() { p.resolve(false); };
request.onerror = function(e) { p.reject(request.error); };
return p;
}).then(function(isDatabaseDeleted) {
expect(isDatabaseDeleted).toBe(true);
done();
}).catch(function(err) {
fail(err);
done();
});
});
it('stores a stream and retrieves its index', function(done) {
p.then(function() {
return writer.insertStream_(
streamInfo, testIndex, testInitData, testReferences.length, 0);
}).then(function(streamId) {
return reader.retrieveStreamIndex(streamId);
}).then(function(streamIndex) {
expect(streamIndex.references[0]).toMatchReference(expectedReferences[0]);
expect(streamIndex.references[1]).toMatchReference(expectedReferences[1]);
expect(streamIndex.references[2]).toMatchReference(expectedReferences[2]);
done();
}).catch(function(err) {
fail(err);
done();
});
});
it('stores a stream with a single segment and retrieves its index',
function(done) {
var references = [
new shaka.media.SegmentReference(0, null, createFailover(url, 6))
];
var index = new shaka.media.SegmentIndex(references);
streamInfo.segmentIndexSource = {
create: function() { return Promise.resolve(index); }
};
p.then(function() {
return writer.insertStream_(streamInfo, index, testInitData, 1, 0);
}).then(function(streamId) {
return reader.retrieveStreamIndex(streamId);
}).then(function(streamIndex) {
expect(streamIndex.references[0]).toMatchReference(
{ start_time: 0, end_time: null });
expect(streamIndex.codecs).toEqual(codecs);
expect(streamIndex.mime_type).toEqual(mime);
expect(streamIndex.init_segment).toEqual(testInitData);
done();
}).catch(function(err) {
fail(err);
done();
});
});
it('throws an error when trying to store an invalid stream', function(done) {
p.then(function() {
return writer.insertStream_(null, null, null, 0, 0);
}).then(function() {
fail();
done();
}).catch(function(err) {
expect(err).not.toBeNull();
done();
});
});
it('deletes a stream index and throws error on retrieval', function(done) {
var streamId;
p.then(function() {
return writer.insertStream_(
streamInfo, testIndex, testInitData, testReferences.length, 0);
}).then(function(data) {
streamId = data;
return writer.deleteStream_(streamId);
}).then(function() {
return reader.retrieveStreamIndex(streamId);
}).then(function(streamIndex) {
fail();
done();
}).catch(function(err) {
expect(err.type).toEqual('storage');
done();
});
});
it('retrieves a segment', function(done) {
p.then(function() {
return writer.insertStream_(
streamInfo, testIndex, testInitData, testReferences.length, 0);
}).then(function(streamId) {
return reader.retrieveSegment(streamId, 0);
}).then(function(data) {
expect(data).not.toBeUndefined();
done();
}).catch(function(err) {
fail(err);
done();
});
});
it('throws an error when non-existent segment requested', function(done) {
p.then(function() {
return reader.retrieveSegment(-1, -1);
}).then(function(streamIndex) {
fail();
done();
}).catch(function(err) {
expect(err.type).toEqual('storage');
done();
});
});
it('stores and retrieves a group information', function(done) {
p.then(function() {
return writer.insertGroup(
[streamInfo], ['ABCD', 'EFG'], duration, drmScheme);
}).then(function(groupId) {
return reader.retrieveGroup(groupId);
}).then(function(groupInformation) {
expect(groupInformation.group_id).toEqual(jasmine.any(Number));
expect(groupInformation.stream_ids.length).toEqual(1);
expect(groupInformation.session_ids).toEqual(['ABCD', 'EFG']);
expect(groupInformation.duration).toEqual(duration);
expect(groupInformation.key_system).toEqual(keySystem);
done();
}).catch(function(err) {
fail(err);
done();
});
});
it('retrieves a list of the stored group IDs', function(done) {
var initalGroupIdsLength = 0;
p.then(function() {
return reader.retrieveGroupIds();
}).then(function(groupIds) {
initalGroupIdsLength = groupIds.length;
return writer.insertGroup([streamInfo], ['HIJK'], duration, drmScheme);
}).then(function() {
return reader.retrieveGroupIds();
}).then(function(groupIds) {
expect(groupIds.length - initalGroupIdsLength).toBe(1);
done();
}).catch(function(err) {
fail(err);
done();
});
});
it('deletes group information and throws error on retrieval', function(done) {
var groupId;
p.then(function() {
return writer.insertGroup([streamInfo], [], duration, drmScheme);
}).then(function(resultingGroupId) {
groupId = resultingGroupId;
return writer.deleteGroup(groupId);
}).then(function() {
return reader.retrieveGroup(groupId);
}).then(function(groupInformation) {
fail();
done();
}).catch(function(err) {
expect(err.type).toEqual('storage');
done();
});
});
it('deletes streams in group and throws error on retrieval', function(done) {
var streamIds, groupId;
p.then(function() {
return writer.insertGroup([streamInfo], [], duration, drmScheme);
}).then(function(id) {
groupId = id;
return reader.retrieveGroup(groupId);
}).then(function(data) {
streamIds = data['stream_ids'];
return writer.deleteGroup(groupId);
}).then(function() {
return reader.retrieveStreamIndex(streamIds[0]);
}).then(function(streamIndex) {
fail();
done();
}).catch(function(err) {
expect(err.type).toEqual('storage');
done();
});
});
it('converts old format of data to new format', function(done) {
var streamId, groupId;
p.then(function() {
return writer.insertGroup([streamInfo], [], duration, drmScheme);
}).then(function(currentGroupId) {
groupId = currentGroupId;
return reader.retrieveGroup(currentGroupId);
}).then(function(groupInfo) {
var p = shaka.util.PublicPromise();
delete groupInfo.duration;
delete groupInfo.key_system;
streamId = groupInfo.stream_ids[0];
var groupStore = writer.getGroupStore();
var request = groupStore.put(groupInfo);
request.onsuccess = function() { p.resolve(); };
request.onerror = function(e) { p.reject(request.error); };
return p;
}).then(function() {
return reader.retrieveStreamIndex(streamId);
}).then(function(streamIndex) {
var p = shaka.util.PublicPromise();
streamIndex.duration = 25;
streamIndex.key_system = 'test.key.system';
var indexStore = writer.getIndexStore();
var request = indexStore.put(streamIndex);
request.onsuccess = function() { p.resolve(); };
request.onerror = function(e) { p.reject(request.error); };
return p;
}).then(function() {
return reader.retrieveGroup(groupId);
}).then(function(groupInfo) {
expect(groupInfo.duration).toEqual(25);
expect(groupInfo.key_system).toEqual('test.key.system');
done();
}).catch(function(err) {
fail(err);
done();
});
});
});