maplibre-gl
Version:
BSD licensed community fork of mapbox-gl, a WebGL interactive maps library
307 lines (258 loc) • 10.8 kB
text/typescript
import fs from 'fs';
import path from 'path';
import vt from '@mapbox/vector-tile';
import Protobuf from 'pbf';
import VectorTileWorkerSource from '../source/vector_tile_worker_source';
import StyleLayerIndex from '../style/style_layer_index';
import {fakeServer, FakeServer} from 'nise';
import Actor from '../util/actor';
import {TileParameters, WorkerTileParameters} from './worker_source';
import WorkerTile from './worker_tile';
import {setPerformance} from '../util/test/util';
describe('vector tile worker source', () => {
const actor = {send: () => {}} as any as Actor;
let server: FakeServer;
let originalGetEntriesByName;
let originalMeasure;
let originalMark;
beforeEach(() => {
global.fetch = null;
server = fakeServer.create();
setPerformance();
originalGetEntriesByName = window.performance.getEntriesByName;
originalMeasure = window.performance.measure;
originalMark = window.performance.mark;
});
afterEach(() => {
server.restore();
jest.clearAllMocks();
window.performance.getEntriesByName = originalGetEntriesByName;
window.performance.measure = originalMeasure;
window.performance.mark = originalMark;
});
test('VectorTileWorkerSource#abortTile aborts pending request', () => {
const source = new VectorTileWorkerSource(actor, new StyleLayerIndex(), []);
source.loadTile({
source: 'source',
uid: 0,
tileID: {overscaledZ: 0, wrap: 0, canonical: {x: 0, y: 0, z: 0, w: 0}},
request: {url: 'http://localhost:2900/abort'}
} as any as WorkerTileParameters, (err, res) => {
expect(err).toBeFalsy();
expect(res).toBeFalsy();
});
source.abortTile({
source: 'source',
uid: 0
} as any as TileParameters, (err, res) => {
expect(err).toBeFalsy();
expect(res).toBeFalsy();
});
expect(source.loading).toEqual({});
});
test('VectorTileWorkerSource#removeTile removes loaded tile', () => {
const source = new VectorTileWorkerSource(actor, new StyleLayerIndex(), []);
source.loaded = {
'0': {} as WorkerTile
};
source.removeTile({
source: 'source',
uid: 0
} as any as TileParameters, (err, res) => {
expect(err).toBeFalsy();
expect(res).toBeFalsy();
});
expect(source.loaded).toEqual({});
});
test('VectorTileWorkerSource#reloadTile reloads a previously-loaded tile', () => {
const source = new VectorTileWorkerSource(actor, new StyleLayerIndex(), []);
const parse = jest.fn();
source.loaded = {
'0': {
status: 'done',
vectorTile: {},
parse
} as any as WorkerTile
};
const callback = jest.fn();
source.reloadTile({uid: 0} as any as WorkerTileParameters, callback);
expect(parse).toHaveBeenCalledTimes(1);
parse.mock.calls[0][4]();
expect(callback).toHaveBeenCalledTimes(1);
});
test('VectorTileWorkerSource#reloadTile queues a reload when parsing is in progress', () => {
const source = new VectorTileWorkerSource(actor, new StyleLayerIndex(), []);
const parse = jest.fn();
source.loaded = {
'0': {
status: 'done',
vectorTile: {},
parse
} as any as WorkerTile
};
const callback1 = jest.fn();
const callback2 = jest.fn();
source.reloadTile({uid: 0} as any as WorkerTileParameters, callback1);
expect(parse).toHaveBeenCalledTimes(1);
source.loaded[0].status = 'parsing';
source.reloadTile({uid: 0} as any as WorkerTileParameters, callback2);
expect(parse).toHaveBeenCalledTimes(1);
parse.mock.calls[0][4]();
expect(parse).toHaveBeenCalledTimes(2);
expect(callback1).toHaveBeenCalledTimes(1);
expect(callback2).toHaveBeenCalledTimes(0);
parse.mock.calls[1][4]();
expect(callback1).toHaveBeenCalledTimes(1);
expect(callback2).toHaveBeenCalledTimes(1);
});
test('VectorTileWorkerSource#reloadTile handles multiple pending reloads', () => {
// https://github.com/mapbox/mapbox-gl-js/issues/6308
const source = new VectorTileWorkerSource(actor, new StyleLayerIndex(), []);
const parse = jest.fn();
source.loaded = {
'0': {
status: 'done',
vectorTile: {},
parse
} as any as WorkerTile
};
const callback1 = jest.fn();
const callback2 = jest.fn();
const callback3 = jest.fn();
source.reloadTile({uid: 0} as any as WorkerTileParameters, callback1);
expect(parse).toHaveBeenCalledTimes(1);
source.loaded[0].status = 'parsing';
source.reloadTile({uid: 0} as any as WorkerTileParameters, callback2);
expect(parse).toHaveBeenCalledTimes(1);
parse.mock.calls[0][4]();
expect(parse).toHaveBeenCalledTimes(2);
expect(callback1).toHaveBeenCalledTimes(1);
expect(callback2).toHaveBeenCalledTimes(0);
expect(callback3).toHaveBeenCalledTimes(0);
source.reloadTile({uid: 0} as any as WorkerTileParameters, callback3);
expect(parse).toHaveBeenCalledTimes(2);
expect(callback1).toHaveBeenCalledTimes(1);
expect(callback2).toHaveBeenCalledTimes(0);
expect(callback3).toHaveBeenCalledTimes(0);
parse.mock.calls[1][4]();
expect(parse).toHaveBeenCalledTimes(3);
expect(callback1).toHaveBeenCalledTimes(1);
expect(callback2).toHaveBeenCalledTimes(1);
expect(callback3).toHaveBeenCalledTimes(0);
parse.mock.calls[2][4]();
expect(callback1).toHaveBeenCalledTimes(1);
expect(callback2).toHaveBeenCalledTimes(1);
expect(callback3).toHaveBeenCalledTimes(1);
});
test('VectorTileWorkerSource#reloadTile does not reparse tiles with no vectorTile data but does call callback', () => {
const source = new VectorTileWorkerSource(actor, new StyleLayerIndex(), []);
const parse = jest.fn();
source.loaded = {
'0': {
status: 'done',
parse
} as any as WorkerTile
};
const callback = jest.fn();
source.reloadTile({uid: 0} as any as WorkerTileParameters, callback);
expect(parse).not.toHaveBeenCalled();
expect(callback).toHaveBeenCalledTimes(1);
});
test('VectorTileWorkerSource provides resource timing information', done => {
const rawTileData = fs.readFileSync(path.join(__dirname, '/../../test/unit/assets/mbsv5-6-18-23.vector.pbf'));
function loadVectorData(params, callback) {
return callback(null, {
vectorTile: new vt.VectorTile(new Protobuf(rawTileData)),
rawData: rawTileData,
cacheControl: null,
expires: null
});
}
const exampleResourceTiming = {
connectEnd: 473,
connectStart: 473,
decodedBodySize: 86494,
domainLookupEnd: 473,
domainLookupStart: 473,
duration: 341,
encodedBodySize: 52528,
entryType: 'resource',
fetchStart: 473.5,
initiatorType: 'xmlhttprequest',
name: 'http://localhost:2900/faketile.pbf',
nextHopProtocol: 'http/1.1',
redirectEnd: 0,
redirectStart: 0,
requestStart: 477,
responseEnd: 815,
responseStart: 672,
secureConnectionStart: 0
};
const layerIndex = new StyleLayerIndex([{
id: 'test',
source: 'source',
'source-layer': 'test',
type: 'fill'
}]);
const source = new VectorTileWorkerSource(actor, layerIndex, [], loadVectorData);
window.performance.getEntriesByName = jest.fn().mockReturnValue([exampleResourceTiming]);
source.loadTile({
source: 'source',
uid: 0,
tileID: {overscaledZ: 0, wrap: 0, canonical: {x: 0, y: 0, z: 0, w: 0}},
request: {url: 'http://localhost:2900/faketile.pbf', collectResourceTiming: true}
} as any as WorkerTileParameters, (err, res) => {
expect(err).toBeFalsy();
expect(res.resourceTiming[0]).toEqual(exampleResourceTiming);
done();
});
});
test('VectorTileWorkerSource provides resource timing information (fallback method)', done => {
const rawTileData = fs.readFileSync(path.join(__dirname, '/../../test/unit/assets/mbsv5-6-18-23.vector.pbf'));
function loadVectorData(params, callback) {
return callback(null, {
vectorTile: new vt.VectorTile(new Protobuf(rawTileData)),
rawData: rawTileData,
cacheControl: null,
expires: null
});
}
const layerIndex = new StyleLayerIndex([{
id: 'test',
source: 'source',
'source-layer': 'test',
type: 'fill'
}]);
const source = new VectorTileWorkerSource(actor, layerIndex, [], loadVectorData);
const sampleMarks = [100, 350];
const marks = {};
const measures = {};
window.performance.getEntriesByName = jest.fn().mockImplementation(name => (measures[name] || []));
window.performance.mark = jest.fn().mockImplementation(name => {
marks[name] = sampleMarks.shift();
return null;
});
window.performance.measure = jest.fn().mockImplementation((name, start, end) => {
measures[name] = measures[name] || [];
measures[name].push({
duration: marks[end] - marks[start],
entryType: 'measure',
name,
startTime: marks[start]
});
return null;
});
source.loadTile({
source: 'source',
uid: 0,
tileID: {overscaledZ: 0, wrap: 0, canonical: {x: 0, y: 0, z: 0, w: 0}},
request: {url: 'http://localhost:2900/faketile.pbf', collectResourceTiming: true}
} as any as WorkerTileParameters, (err, res) => {
expect(err).toBeFalsy();
expect(res.resourceTiming[0]).toEqual(
{'duration': 250, 'entryType': 'measure', 'name': 'http://localhost:2900/faketile.pbf', 'startTime': 100}
);
done();
});
});
});