UNPKG

10.8 kBPlain TextView Raw
1import fs from 'fs';
2import path from 'path';
3import vt from '@mapbox/vector-tile';
4import Protobuf from 'pbf';
5import VectorTileWorkerSource from '../source/vector_tile_worker_source';
6import StyleLayerIndex from '../style/style_layer_index';
7import {fakeServer, FakeServer} from 'nise';
8import Actor from '../util/actor';
9import {TileParameters, WorkerTileParameters} from './worker_source';
10import WorkerTile from './worker_tile';
11import {setPerformance} from '../util/test/util';
12
13describe('vector tile worker source', () => {
14 const actor = {send: () => {}} as any as Actor;
15 let server: FakeServer;
16 let originalGetEntriesByName;
17 let originalMeasure;
18 let originalMark;
19
20 beforeEach(() => {
21 global.fetch = null;
22 server = fakeServer.create();
23 setPerformance();
24 originalGetEntriesByName = window.performance.getEntriesByName;
25 originalMeasure = window.performance.measure;
26 originalMark = window.performance.mark;
27
28 });
29
30 afterEach(() => {
31 server.restore();
32 jest.clearAllMocks();
33 window.performance.getEntriesByName = originalGetEntriesByName;
34 window.performance.measure = originalMeasure;
35 window.performance.mark = originalMark;
36 });
37 test('VectorTileWorkerSource#abortTile aborts pending request', () => {
38 const source = new VectorTileWorkerSource(actor, new StyleLayerIndex(), []);
39
40 source.loadTile({
41 source: 'source',
42 uid: 0,
43 tileID: {overscaledZ: 0, wrap: 0, canonical: {x: 0, y: 0, z: 0, w: 0}},
44 request: {url: 'http://localhost:2900/abort'}
45 } as any as WorkerTileParameters, (err, res) => {
46 expect(err).toBeFalsy();
47 expect(res).toBeFalsy();
48 });
49
50 source.abortTile({
51 source: 'source',
52 uid: 0
53 } as any as TileParameters, (err, res) => {
54 expect(err).toBeFalsy();
55 expect(res).toBeFalsy();
56 });
57
58 expect(source.loading).toEqual({});
59 });
60
61 test('VectorTileWorkerSource#removeTile removes loaded tile', () => {
62 const source = new VectorTileWorkerSource(actor, new StyleLayerIndex(), []);
63
64 source.loaded = {
65 '0': {} as WorkerTile
66 };
67
68 source.removeTile({
69 source: 'source',
70 uid: 0
71 } as any as TileParameters, (err, res) => {
72 expect(err).toBeFalsy();
73 expect(res).toBeFalsy();
74 });
75
76 expect(source.loaded).toEqual({});
77 });
78
79 test('VectorTileWorkerSource#reloadTile reloads a previously-loaded tile', () => {
80 const source = new VectorTileWorkerSource(actor, new StyleLayerIndex(), []);
81 const parse = jest.fn();
82
83 source.loaded = {
84 '0': {
85 status: 'done',
86 vectorTile: {},
87 parse
88 } as any as WorkerTile
89 };
90
91 const callback = jest.fn();
92 source.reloadTile({uid: 0} as any as WorkerTileParameters, callback);
93 expect(parse).toHaveBeenCalledTimes(1);
94
95 parse.mock.calls[0][4]();
96 expect(callback).toHaveBeenCalledTimes(1);
97 });
98
99 test('VectorTileWorkerSource#reloadTile queues a reload when parsing is in progress', () => {
100 const source = new VectorTileWorkerSource(actor, new StyleLayerIndex(), []);
101 const parse = jest.fn();
102
103 source.loaded = {
104 '0': {
105 status: 'done',
106 vectorTile: {},
107 parse
108 } as any as WorkerTile
109 };
110
111 const callback1 = jest.fn();
112 const callback2 = jest.fn();
113 source.reloadTile({uid: 0} as any as WorkerTileParameters, callback1);
114 expect(parse).toHaveBeenCalledTimes(1);
115
116 source.loaded[0].status = 'parsing';
117 source.reloadTile({uid: 0} as any as WorkerTileParameters, callback2);
118 expect(parse).toHaveBeenCalledTimes(1);
119
120 parse.mock.calls[0][4]();
121 expect(parse).toHaveBeenCalledTimes(2);
122 expect(callback1).toHaveBeenCalledTimes(1);
123 expect(callback2).toHaveBeenCalledTimes(0);
124
125 parse.mock.calls[1][4]();
126 expect(callback1).toHaveBeenCalledTimes(1);
127 expect(callback2).toHaveBeenCalledTimes(1);
128 });
129
130 test('VectorTileWorkerSource#reloadTile handles multiple pending reloads', () => {
131 // https://github.com/mapbox/mapbox-gl-js/issues/6308
132 const source = new VectorTileWorkerSource(actor, new StyleLayerIndex(), []);
133 const parse = jest.fn();
134
135 source.loaded = {
136 '0': {
137 status: 'done',
138 vectorTile: {},
139 parse
140 } as any as WorkerTile
141 };
142
143 const callback1 = jest.fn();
144 const callback2 = jest.fn();
145 const callback3 = jest.fn();
146 source.reloadTile({uid: 0} as any as WorkerTileParameters, callback1);
147 expect(parse).toHaveBeenCalledTimes(1);
148
149 source.loaded[0].status = 'parsing';
150 source.reloadTile({uid: 0} as any as WorkerTileParameters, callback2);
151 expect(parse).toHaveBeenCalledTimes(1);
152
153 parse.mock.calls[0][4]();
154 expect(parse).toHaveBeenCalledTimes(2);
155 expect(callback1).toHaveBeenCalledTimes(1);
156 expect(callback2).toHaveBeenCalledTimes(0);
157 expect(callback3).toHaveBeenCalledTimes(0);
158
159 source.reloadTile({uid: 0} as any as WorkerTileParameters, callback3);
160 expect(parse).toHaveBeenCalledTimes(2);
161 expect(callback1).toHaveBeenCalledTimes(1);
162 expect(callback2).toHaveBeenCalledTimes(0);
163 expect(callback3).toHaveBeenCalledTimes(0);
164
165 parse.mock.calls[1][4]();
166 expect(parse).toHaveBeenCalledTimes(3);
167 expect(callback1).toHaveBeenCalledTimes(1);
168 expect(callback2).toHaveBeenCalledTimes(1);
169 expect(callback3).toHaveBeenCalledTimes(0);
170
171 parse.mock.calls[2][4]();
172 expect(callback1).toHaveBeenCalledTimes(1);
173 expect(callback2).toHaveBeenCalledTimes(1);
174 expect(callback3).toHaveBeenCalledTimes(1);
175
176 });
177
178 test('VectorTileWorkerSource#reloadTile does not reparse tiles with no vectorTile data but does call callback', () => {
179 const source = new VectorTileWorkerSource(actor, new StyleLayerIndex(), []);
180 const parse = jest.fn();
181
182 source.loaded = {
183 '0': {
184 status: 'done',
185 parse
186 } as any as WorkerTile
187 };
188
189 const callback = jest.fn();
190
191 source.reloadTile({uid: 0} as any as WorkerTileParameters, callback);
192 expect(parse).not.toHaveBeenCalled();
193 expect(callback).toHaveBeenCalledTimes(1);
194
195 });
196
197 test('VectorTileWorkerSource provides resource timing information', done => {
198 const rawTileData = fs.readFileSync(path.join(__dirname, '/../../test/unit/assets/mbsv5-6-18-23.vector.pbf'));
199
200 function loadVectorData(params, callback) {
201 return callback(null, {
202 vectorTile: new vt.VectorTile(new Protobuf(rawTileData)),
203 rawData: rawTileData,
204 cacheControl: null,
205 expires: null
206 });
207 }
208
209 const exampleResourceTiming = {
210 connectEnd: 473,
211 connectStart: 473,
212 decodedBodySize: 86494,
213 domainLookupEnd: 473,
214 domainLookupStart: 473,
215 duration: 341,
216 encodedBodySize: 52528,
217 entryType: 'resource',
218 fetchStart: 473.5,
219 initiatorType: 'xmlhttprequest',
220 name: 'http://localhost:2900/faketile.pbf',
221 nextHopProtocol: 'http/1.1',
222 redirectEnd: 0,
223 redirectStart: 0,
224 requestStart: 477,
225 responseEnd: 815,
226 responseStart: 672,
227 secureConnectionStart: 0
228 };
229
230 const layerIndex = new StyleLayerIndex([{
231 id: 'test',
232 source: 'source',
233 'source-layer': 'test',
234 type: 'fill'
235 }]);
236
237 const source = new VectorTileWorkerSource(actor, layerIndex, [], loadVectorData);
238
239 window.performance.getEntriesByName = jest.fn().mockReturnValue([ exampleResourceTiming ]);
240
241 source.loadTile({
242 source: 'source',
243 uid: 0,
244 tileID: {overscaledZ: 0, wrap: 0, canonical: {x: 0, y: 0, z: 0, w: 0}},
245 request: {url: 'http://localhost:2900/faketile.pbf', collectResourceTiming: true}
246 } as any as WorkerTileParameters, (err, res) => {
247 expect(err).toBeFalsy();
248 expect(res.resourceTiming[0]).toEqual(exampleResourceTiming);
249 done();
250 });
251 });
252
253 test('VectorTileWorkerSource provides resource timing information (fallback method)', done => {
254 const rawTileData = fs.readFileSync(path.join(__dirname, '/../../test/unit/assets/mbsv5-6-18-23.vector.pbf'));
255
256 function loadVectorData(params, callback) {
257 return callback(null, {
258 vectorTile: new vt.VectorTile(new Protobuf(rawTileData)),
259 rawData: rawTileData,
260 cacheControl: null,
261 expires: null
262 });
263 }
264
265 const layerIndex = new StyleLayerIndex([{
266 id: 'test',
267 source: 'source',
268 'source-layer': 'test',
269 type: 'fill'
270 }]);
271
272 const source = new VectorTileWorkerSource(actor, layerIndex, [], loadVectorData);
273
274 const sampleMarks = [100, 350];
275 const marks = {};
276 const measures = {};
277 window.performance.getEntriesByName = jest.fn().mockImplementation(name => (measures[name] || []));
278 window.performance.mark = jest.fn().mockImplementation(name => {
279 marks[name] = sampleMarks.shift();
280 return null;
281 });
282 window.performance.measure = jest.fn().mockImplementation((name, start, end) => {
283 measures[name] = measures[name] || [];
284 measures[name].push({
285 duration: marks[end] - marks[start],
286 entryType: 'measure',
287 name,
288 startTime: marks[start]
289 });
290 return null;
291 });
292
293 source.loadTile({
294 source: 'source',
295 uid: 0,
296 tileID: {overscaledZ: 0, wrap: 0, canonical: {x: 0, y: 0, z: 0, w: 0}},
297 request: {url: 'http://localhost:2900/faketile.pbf', collectResourceTiming: true}
298 } as any as WorkerTileParameters, (err, res) => {
299 expect(err).toBeFalsy();
300 expect(res.resourceTiming[0]).toEqual(
301 {'duration': 250, 'entryType': 'measure', 'name': 'http://localhost:2900/faketile.pbf', 'startTime': 100}
302 );
303 done();
304 });
305 });
306});