DSDSWeb/static/js/Cesium-1.53/Specs/Core/RequestSchedulerSpec.js

887 lines
28 KiB
JavaScript

defineSuite([
'Core/RequestScheduler',
'Core/Request',
'Core/RequestState',
'ThirdParty/when'
], function(
RequestScheduler,
Request,
RequestState,
when) {
'use strict';
var originalMaximumRequests;
var originalMaximumRequestsPerServer;
var originalPriorityHeapLength;
var originalRequestsByServer;
beforeAll(function() {
originalMaximumRequests = RequestScheduler.maximumRequests;
originalMaximumRequestsPerServer = RequestScheduler.maximumRequestsPerServer;
originalPriorityHeapLength = RequestScheduler.priorityHeapLength;
originalRequestsByServer = RequestScheduler.requestsByServer;
});
beforeEach(function() {
RequestScheduler.clearForSpecs();
RequestScheduler.requestsByServer = {};
});
afterEach(function() {
RequestScheduler.maximumRequests = originalMaximumRequests;
RequestScheduler.maximumRequestsPerServer = originalMaximumRequestsPerServer;
RequestScheduler.priorityHeapLength = originalPriorityHeapLength;
RequestScheduler.requestsByServer = originalRequestsByServer;
});
it('request throws when request is undefined', function() {
expect(function() {
RequestScheduler.request();
}).toThrowDeveloperError();
});
it('request throws when request.url is undefined', function() {
expect(function() {
RequestScheduler.request(new Request({
requestFunction : function(url) {
return undefined;
}
}));
}).toThrowDeveloperError();
});
it('request throws when request.requestFunction is undefined', function() {
expect(function() {
RequestScheduler.request(new Request({
url : 'file/path'
}));
}).toThrowDeveloperError();
});
it('getServer throws if url is undefined', function() {
expect(function() {
RequestScheduler.getServerKey();
}).toThrowDeveloperError();
});
it('getServer with https', function() {
var server = RequestScheduler.getServerKey('https://test.invalid/1');
expect(server).toEqual('test.invalid:443');
});
it('getServer with http', function() {
var server = RequestScheduler.getServerKey('http://test.invalid/1');
expect(server).toEqual('test.invalid:80');
});
it('honors maximumRequests', function() {
RequestScheduler.maximumRequests = 2;
var statistics = RequestScheduler.statistics;
var deferreds = [];
function requestFunction() {
var deferred = when.defer();
deferreds.push(deferred);
return deferred.promise;
}
function createRequest() {
return new Request({
url : 'http://test.invalid/1',
requestFunction : requestFunction,
throttle : true
});
}
var promise1 = RequestScheduler.request(createRequest());
var promise2 = RequestScheduler.request(createRequest());
RequestScheduler.update();
// Scheduler is full, promise3 will be undefined
var promise3 = RequestScheduler.request(createRequest());
RequestScheduler.update();
expect(statistics.numberOfActiveRequests).toBe(2);
expect(promise1).toBeDefined();
expect(promise2).toBeDefined();
expect(promise3).not.toBeDefined();
// Scheduler now has an empty slot, promise4 goes through
deferreds[0].resolve();
RequestScheduler.update();
expect(statistics.numberOfActiveRequests).toBe(1);
var promise4 = RequestScheduler.request(createRequest());
RequestScheduler.update();
expect(statistics.numberOfActiveRequests).toBe(2);
expect(promise4).toBeDefined();
// Scheduler is full, promise5 will be undefined
var promise5 = RequestScheduler.request(createRequest());
RequestScheduler.update();
expect(statistics.numberOfActiveRequests).toBe(2);
expect(promise5).not.toBeDefined();
// maximumRequests increases, promise6 goes through
RequestScheduler.maximumRequests = 3;
var promise6 = RequestScheduler.request(createRequest());
RequestScheduler.update();
expect(statistics.numberOfActiveRequests).toBe(3);
expect(promise6).toBeDefined();
var length = deferreds.length;
for (var i = 0; i < length; ++i) {
deferreds[i].resolve();
}
});
it('honors maximumRequestsPerServer', function() {
RequestScheduler.maximumRequestsPerServer = 2;
var deferreds = [];
function requestFunction() {
var deferred = when.defer();
deferreds.push(deferred);
return deferred.promise;
}
var url = 'http://test.invalid/1';
var server = RequestScheduler.getServerKey(url);
function createRequest() {
return new Request({
url : url,
requestFunction : requestFunction,
throttleByServer : true
});
}
var promise1 = RequestScheduler.request(createRequest());
var promise2 = RequestScheduler.request(createRequest());
RequestScheduler.update();
// Scheduler is full, promise3 will be undefined
var promise3 = RequestScheduler.request(createRequest());
RequestScheduler.update();
expect(RequestScheduler.numberOfActiveRequestsByServer(server)).toBe(2);
expect(promise1).toBeDefined();
expect(promise2).toBeDefined();
expect(promise3).not.toBeDefined();
// Scheduler now has an empty slot, promise4 goes through
deferreds[0].resolve();
RequestScheduler.update();
expect(RequestScheduler.numberOfActiveRequestsByServer(server)).toBe(1);
var promise4 = RequestScheduler.request(createRequest());
RequestScheduler.update();
expect(RequestScheduler.numberOfActiveRequestsByServer(server)).toBe(2);
expect(promise4).toBeDefined();
// Scheduler is full, promise5 will be undefined
var promise5 = RequestScheduler.request(createRequest());
RequestScheduler.update();
expect(RequestScheduler.numberOfActiveRequestsByServer(server)).toBe(2);
expect(promise5).not.toBeDefined();
// maximumRequests increases, promise6 goes through
RequestScheduler.maximumRequestsPerServer = 3;
var promise6 = RequestScheduler.request(createRequest());
RequestScheduler.update();
expect(RequestScheduler.numberOfActiveRequestsByServer(server)).toBe(3);
expect(promise6).toBeDefined();
var length = deferreds.length;
for (var i = 0; i < length; ++i) {
deferreds[i].resolve();
}
});
it('honors priorityHeapLength', function() {
var deferreds = [];
var requests = [];
function requestFunction() {
var deferred = when.defer();
deferreds.push(deferred);
return deferred.promise;
}
function createRequest(priority) {
var request = new Request({
url : 'http://test.invalid/1',
requestFunction : requestFunction,
throttle : true,
priority : priority
});
requests.push(request);
return request;
}
RequestScheduler.priorityHeapLength = 1;
var firstRequest = createRequest(0.0);
var promise = RequestScheduler.request(firstRequest);
expect(promise).toBeDefined();
promise = RequestScheduler.request(createRequest(1.0));
expect(promise).toBeUndefined();
RequestScheduler.priorityHeapLength = 3;
promise = RequestScheduler.request(createRequest(2.0));
promise = RequestScheduler.request(createRequest(3.0));
expect(promise).toBeDefined();
promise = RequestScheduler.request(createRequest(4.0));
expect(promise).toBeUndefined();
// A request is cancelled to accommodate the new heap length
RequestScheduler.priorityHeapLength = 2;
expect(firstRequest.state).toBe(RequestState.CANCELLED);
var length = deferreds.length;
for (var i = 0; i < length; ++i) {
deferreds[i].resolve();
}
});
function testImmediateRequest(url, dataOrBlobUri) {
var statistics = RequestScheduler.statistics;
var deferreds = [];
function requestFunction() {
var deferred = when.defer();
deferreds.push(deferred);
return deferred.promise;
}
var request = new Request({
url : url,
requestFunction : requestFunction
});
var promise = RequestScheduler.request(request);
expect(promise).toBeDefined();
if (dataOrBlobUri) {
expect(request.serverKey).toBeUndefined();
expect(statistics.numberOfActiveRequests).toBe(0);
} else {
expect(statistics.numberOfActiveRequests).toBe(1);
expect(RequestScheduler.numberOfActiveRequestsByServer(request.serverKey)).toBe(1);
}
deferreds[0].resolve();
return promise.then(function() {
expect(request.state).toBe(RequestState.RECEIVED);
expect(statistics.numberOfActiveRequests).toBe(0);
if (!dataOrBlobUri) {
expect(RequestScheduler.numberOfActiveRequestsByServer(request.serverKey)).toBe(0);
}
});
}
it('data uri goes through immediately', function() {
var dataUri = 'data:text/plain;base64,SGVsbG8sIFdvcmxkIQ%3D%3D';
testImmediateRequest(dataUri, true);
});
it('blob uri goes through immediately', function() {
var uint8Array = new Uint8Array(4);
var blob = new Blob([uint8Array], {
type : 'application/octet-stream'
});
var blobUrl = window.URL.createObjectURL(blob);
testImmediateRequest(blobUrl, true);
});
it('request goes through immediately when throttle is false', function() {
var url = 'https://test.invalid/1';
testImmediateRequest(url, false);
});
it('makes a throttled request', function() {
var deferreds = [];
function requestFunction() {
var deferred = when.defer();
deferreds.push(deferred);
return deferred.promise;
}
var request = new Request({
throttle : true,
url : 'https://test.invalid/1',
requestFunction : requestFunction
});
expect(request.state).toBe(RequestState.UNISSUED);
var promise = RequestScheduler.request(request);
expect(promise).toBeDefined();
expect(request.state).toBe(RequestState.ISSUED);
RequestScheduler.update();
expect(request.state).toBe(RequestState.ACTIVE);
deferreds[0].resolve();
expect(request.state).toBe(RequestState.RECEIVED);
});
it('cancels an issued request', function() {
var statistics = RequestScheduler.statistics;
function requestFunction() {
return when.resolve();
}
var request = new Request({
throttle : true,
url : 'https://test.invalid/1',
requestFunction : requestFunction
});
var promise = RequestScheduler.request(request);
expect(request.state).toBe(RequestState.ISSUED);
request.cancel();
RequestScheduler.update();
expect(request.state).toBe(RequestState.CANCELLED);
expect(statistics.numberOfCancelledRequests).toBe(1);
expect(statistics.numberOfCancelledActiveRequests).toBe(0);
return promise.then(function() {
fail('should not be called');
}).otherwise(function(error) {
expect(request.state).toBe(RequestState.CANCELLED);
});
});
it('cancels an active request', function() {
var statistics = RequestScheduler.statistics;
var cancelFunction = jasmine.createSpy('cancelFunction');
function requestFunction() {
return when.defer().promise;
}
var request = new Request({
throttle : true,
url : 'https://test.invalid/1',
requestFunction : requestFunction,
cancelFunction : cancelFunction
});
var promise = RequestScheduler.request(request);
RequestScheduler.update();
expect(request.state).toBe(RequestState.ACTIVE);
request.cancel();
RequestScheduler.update();
expect(request.state).toBe(RequestState.CANCELLED);
expect(statistics.numberOfCancelledRequests).toBe(1);
expect(statistics.numberOfCancelledActiveRequests).toBe(1);
expect(RequestScheduler.numberOfActiveRequestsByServer(request.serverKey)).toBe(0);
expect(cancelFunction).toHaveBeenCalled();
return promise.then(function() {
fail('should not be called');
}).otherwise(function(error) {
expect(request.state).toBe(RequestState.CANCELLED);
});
});
it('handles request failure', function() {
var statistics = RequestScheduler.statistics;
var deferreds = [];
function requestFunction() {
var deferred = when.defer();
deferreds.push(deferred);
return deferred.promise;
}
var request = new Request({
url : 'https://test.invalid/1',
requestFunction : requestFunction
});
var promise = RequestScheduler.request(request);
expect(request.state).toBe(RequestState.ACTIVE);
expect(statistics.numberOfActiveRequests).toBe(1);
deferreds[0].reject('Request failed');
RequestScheduler.update();
expect(statistics.numberOfActiveRequests).toBe(0);
return promise.then(function() {
fail('should not be called');
}).otherwise(function(error) {
expect(error).toBe('Request failed');
});
});
it('prioritizes requests', function() {
var currentPriority = 0.0;
function getRequestFunction(priority) {
return function() {
expect(priority).toBeGreaterThan(currentPriority);
currentPriority = priority;
return when.resolve();
};
}
function createRequest(priority) {
return new Request({
throttle : true,
url : 'https://test.invalid/1',
requestFunction : getRequestFunction(priority),
priority : priority
});
}
var length = RequestScheduler.priorityHeapLength;
for (var i = 0; i < length; ++i) {
var priority = Math.random();
RequestScheduler.request(createRequest(priority));
}
RequestScheduler.update();
expect(currentPriority).toBeGreaterThan(0.0); // Ensures that the expect in getRequestFunction is actually called
});
it('updates priority', function() {
var invertPriority = false;
function getPriorityFunction(priority) {
return function() {
if (invertPriority) {
return 1.0 - priority;
}
return priority;
};
}
function requestFunction() {
return when.resolve();
}
function createRequest(priority) {
return new Request({
throttle : true,
url : 'https://test.invalid/1',
requestFunction : requestFunction,
priorityFunction : getPriorityFunction(priority)
});
}
var i;
var request;
var length = RequestScheduler.priorityHeapLength;
for (i = 0; i < length; ++i) {
var priority = i / (length - 1);
request = createRequest(priority);
request.testId = i;
RequestScheduler.request(request);
}
// Update priorities while not letting any requests go through
RequestScheduler.maximumRequests = 0;
RequestScheduler.update();
var requestHeap = RequestScheduler.requestHeap;
var requests = [];
var currentTestId = 0;
while (requestHeap.length > 0) {
request = requestHeap.pop();
requests.push(request);
expect(request.testId).toBeGreaterThanOrEqualTo(currentTestId);
currentTestId = request.testId;
}
for (i = 0; i < length; ++i) {
requestHeap.insert(requests[i]);
}
invertPriority = true;
RequestScheduler.update();
while (requestHeap.length > 0) {
request = requestHeap.pop();
expect(request.testId).toBeLessThanOrEqualTo(currentTestId);
currentTestId = request.testId;
}
});
it('handles low priority requests', function() {
function requestFunction() {
return when.resolve();
}
function createRequest(priority) {
return new Request({
throttle : true,
url : 'https://test.invalid/1',
requestFunction : requestFunction,
priority : priority
});
}
var highPriority = 0.0;
var mediumPriority = 0.5;
var lowPriority = 1.0;
var length = RequestScheduler.priorityHeapLength;
for (var i = 0; i < length; ++i) {
RequestScheduler.request(createRequest(mediumPriority));
}
// Heap is full so low priority request is not even issued
var promise = RequestScheduler.request(createRequest(lowPriority));
expect(promise).toBeUndefined();
expect(RequestScheduler.statistics.numberOfCancelledRequests).toBe(0);
// Heap is full so high priority request bumps off lower priority request
promise = RequestScheduler.request(createRequest(highPriority));
expect(promise).toBeDefined();
expect(RequestScheduler.statistics.numberOfCancelledRequests).toBe(1);
});
it('unthrottled requests starve throttled requests', function() {
var deferreds = [];
function requestFunction() {
var deferred = when.defer();
deferreds.push(deferred);
return deferred.promise;
}
function createRequest(throttle) {
return new Request({
url : 'http://test.invalid/1',
requestFunction : requestFunction,
throttle : throttle
});
}
var throttledRequest = createRequest(true);
RequestScheduler.request(throttledRequest);
for (var i = 0; i < RequestScheduler.maximumRequests; ++i) {
RequestScheduler.request(createRequest(false));
}
RequestScheduler.update();
expect(throttledRequest.state).toBe(RequestState.ISSUED);
// Resolve one of the unthrottled requests
deferreds[0].resolve();
RequestScheduler.update();
expect(throttledRequest.state).toBe(RequestState.ACTIVE);
var length = deferreds.length;
for (var j = 0; j < length; ++j) {
deferreds[j].resolve();
}
});
it('request throttled by server is cancelled', function() {
var deferreds = [];
function requestFunction() {
var deferred = when.defer();
deferreds.push(deferred);
return deferred.promise;
}
function createRequest(throttleByServer) {
return new Request({
url : 'http://test.invalid/1',
requestFunction : requestFunction,
throttleByServer : throttleByServer
});
}
for (var i = 0; i < RequestScheduler.maximumRequestsPerServer - 1; ++i) {
RequestScheduler.request(createRequest(false));
}
var throttledRequest = createRequest(true);
RequestScheduler.request(throttledRequest);
RequestScheduler.request(createRequest(false));
RequestScheduler.update();
expect(throttledRequest.state).toBe(RequestState.CANCELLED);
var length = deferreds.length;
for (var j = 0; j < length; ++j) {
deferreds[j].resolve();
}
});
it('throttleRequests', function() {
RequestScheduler.maximumRequests = 0;
function requestFunction() {
return when.resolve();
}
RequestScheduler.throttleRequests = true;
var request = new Request({
throttle : true,
url : 'https://test.invalid/1',
requestFunction : requestFunction
});
var promise = RequestScheduler.request(request);
expect(promise).toBeUndefined();
RequestScheduler.throttleRequests = false;
request = new Request({
throttle : true,
url : 'https://test.invalid/1',
requestFunction : requestFunction
});
promise = RequestScheduler.request(request);
expect(promise).toBeDefined();
RequestScheduler.throttleRequests = true;
});
it('debugShowStatistics', function() {
spyOn(console, 'log');
RequestScheduler.debugShowStatistics = true;
var deferreds = [];
function requestFunction() {
var deferred = when.defer();
deferreds.push(deferred);
return deferred.promise;
}
function createRequest() {
return new Request({
url : 'https://test.invalid/1',
requestFunction : requestFunction
});
}
var requestToCancel = createRequest();
RequestScheduler.request(createRequest());
RequestScheduler.request(createRequest());
RequestScheduler.request(requestToCancel);
RequestScheduler.update();
expect(console.log).toHaveBeenCalledWith('Number of attempted requests: 3');
expect(console.log).toHaveBeenCalledWith('Number of active requests: 3');
deferreds[0].reject();
requestToCancel.cancel();
RequestScheduler.update();
expect(console.log).toHaveBeenCalledWith('Number of cancelled requests: 1');
expect(console.log).toHaveBeenCalledWith('Number of cancelled active requests: 1');
expect(console.log).toHaveBeenCalledWith('Number of failed requests: 1');
var length = deferreds.length;
for (var i = 0; i < length; ++i) {
deferreds[i].resolve();
}
RequestScheduler.debugShowStatistics = false;
});
it('successful request causes requestCompletedEvent to be raised', function() {
var deferred;
function requestFunction() {
deferred = when.defer();
return deferred.promise;
}
var request = new Request({
url : 'https://test.invalid/1',
requestFunction : requestFunction
});
var promise = RequestScheduler.request(request);
expect(promise).toBeDefined();
var eventRaised = false;
var removeListenerCallback = RequestScheduler.requestCompletedEvent.addEventListener(function() {
eventRaised = true;
});
deferred.resolve();
return promise.then(function() {
expect(eventRaised).toBe(true);
}).always(function() {
removeListenerCallback();
});
});
it('successful data request causes requestCompletedEvent to be raised', function() {
var deferred;
function requestFunction() {
deferred = when.defer();
return deferred.promise;
}
var request = new Request({
url : 'data:text/plain;base64,SGVsbG8sIFdvcmxkIQ%3D%3D',
requestFunction : requestFunction
});
var eventRaised = false;
var removeListenerCallback = RequestScheduler.requestCompletedEvent.addEventListener(function() {
eventRaised = true;
});
var promise = RequestScheduler.request(request);
expect(promise).toBeDefined();
deferred.resolve();
RequestScheduler.update();
return promise.then(function() {
expect(eventRaised).toBe(true);
}).always(function() {
removeListenerCallback();
});
});
it('successful blob request causes requestCompletedEvent to be raised', function() {
var deferred;
function requestFunction() {
deferred = when.defer();
return deferred.promise;
}
var uint8Array = new Uint8Array(4);
var blob = new Blob([uint8Array], {
type : 'application/octet-stream'
});
var blobUrl = window.URL.createObjectURL(blob);
var request = new Request({
url : blobUrl,
requestFunction : requestFunction
});
var eventRaised = false;
var removeListenerCallback = RequestScheduler.requestCompletedEvent.addEventListener(function() {
eventRaised = true;
});
var promise = RequestScheduler.request(request);
expect(promise).toBeDefined();
deferred.resolve();
RequestScheduler.update();
return promise.then(function() {
expect(eventRaised).toBe(true);
}).always(function() {
removeListenerCallback();
});
});
it('unsuccessful requests raise requestCompletedEvent with error', function() {
var deferred;
function requestFunction() {
deferred = when.defer();
return deferred.promise;
}
var request = new Request({
url : 'https://test.invalid/1',
requestFunction : requestFunction
});
var eventRaised = false;
var removeListenerCallback = RequestScheduler.requestCompletedEvent.addEventListener(function(error) {
eventRaised = true;
expect(error).toBeDefined();
});
var promise = RequestScheduler.request(request);
expect(promise).toBeDefined();
deferred.reject({
error: 'error'
});
RequestScheduler.update();
return promise.then(function() {
expect(eventRaised).toBe(true);
}).always(function() {
removeListenerCallback();
});
});
it('canceled requests do not cause requestCompletedEvent to be raised', function() {
var cancelDeferred;
function requestCancelFunction() {
cancelDeferred = when.defer();
return cancelDeferred.promise;
}
var requestToCancel = new Request({
url : 'https://test.invalid/1',
requestFunction : requestCancelFunction
});
RequestScheduler.request(requestToCancel);
var removeListenerCallback = RequestScheduler.requestCompletedEvent.addEventListener(function() {
fail('should not be called');
});
requestToCancel.cancel();
RequestScheduler.update();
cancelDeferred.resolve();
removeListenerCallback();
});
it('RequestScheduler.requestsByServer allows for custom maximum requests', function() {
var promise;
RequestScheduler.requestsByServer['test.invalid:80'] = 23;
for (var i = 0; i < 23; i++) {
promise = RequestScheduler.request(new Request({
url: 'http://test.invalid/1',
throttle: true,
throttleByServer: true,
requestFunction: function() { return when.defer(); }
}));
RequestScheduler.update();
expect(promise).toBeDefined();
}
promise = RequestScheduler.request(new Request({
url: 'http://test.invalid/1',
throttle: true,
throttleByServer: true,
requestFunction: function() { return when.defer(); }
}));
expect(promise).toBeUndefined();
});
});