UNPKG

10.3 kBPlain TextView Raw
1import {createSymbolBucket} from '../../test/unit/lib/create_symbol_layer';
2import Tile from '../source/tile';
3import GeoJSONWrapper, {Feature} from '../source/geojson_wrapper';
4import {OverscaledTileID} from '../source/tile_id';
5import fs from 'fs';
6import path from 'path';
7import vtpbf from 'vt-pbf';
8import FeatureIndex from '../data/feature_index';
9import {CollisionBoxArray} from '../data/array_types.g';
10import {extend} from '../util/util';
11import {serialize, deserialize} from '../util/web_worker_transfer';
12
13describe('querySourceFeatures', () => {
14 const features = [{
15 type: 1,
16 geometry: [0, 0],
17 tags: {oneway: true}
18 } as any as Feature];
19
20 test('geojson tile', () => {
21 const tile = new Tile(new OverscaledTileID(3, 0, 2, 1, 2), undefined);
22 let result;
23
24 result = [];
25 tile.querySourceFeatures(result);
26 expect(result).toHaveLength(0);
27
28 const geojsonWrapper = new GeoJSONWrapper(features);
29 geojsonWrapper.name = '_geojsonTileLayer';
30 tile.loadVectorData(
31 createVectorData({rawTileData: vtpbf({layers: {'_geojsonTileLayer': geojsonWrapper}})}),
32 createPainter()
33 );
34
35 result = [];
36 tile.querySourceFeatures(result);
37 expect(result).toHaveLength(1);
38 expect(result[0].geometry.coordinates[0]).toEqual([-90, 0]);
39 result = [];
40 tile.querySourceFeatures(result, {} as any);
41 expect(result).toHaveLength(1);
42 expect(result[0].properties).toEqual(features[0].tags);
43 result = [];
44 tile.querySourceFeatures(result, {sourceLayer: undefined, filter: ['==', 'oneway', true]});
45 expect(result).toHaveLength(1);
46 result = [];
47 tile.querySourceFeatures(result, {sourceLayer: undefined, filter: ['!=', 'oneway', true]});
48 expect(result).toHaveLength(0);
49 result = [];
50 const polygon = {type: 'Polygon', coordinates: [[[-91, -1], [-89, -1], [-89, 1], [-91, 1], [-91, -1]]]};
51 tile.querySourceFeatures(result, {sourceLayer: undefined, filter: ['within', polygon]});
52 expect(result).toHaveLength(1);
53 });
54
55 test('empty geojson tile', () => {
56 const tile = new Tile(new OverscaledTileID(1, 0, 1, 1, 1), undefined);
57 let result;
58
59 result = [];
60 tile.querySourceFeatures(result);
61 expect(result).toHaveLength(0);
62
63 const geojsonWrapper = new GeoJSONWrapper([]);
64 geojsonWrapper.name = '_geojsonTileLayer';
65
66 result = [];
67 expect(() => { tile.querySourceFeatures(result); }).not.toThrow();
68 expect(result).toHaveLength(0);
69 });
70
71 test('vector tile', () => {
72 const tile = new Tile(new OverscaledTileID(1, 0, 1, 1, 1), undefined);
73 let result;
74
75 result = [];
76 tile.querySourceFeatures(result);
77 expect(result).toHaveLength(0);
78
79 tile.loadVectorData(
80 createVectorData({rawTileData: createRawTileData()}),
81 createPainter()
82 );
83
84 result = [];
85 tile.querySourceFeatures(result, {sourceLayer: 'does-not-exist', filter: undefined});
86 expect(result).toHaveLength(0);
87
88 result = [];
89 tile.querySourceFeatures(result, {sourceLayer: 'road', filter: undefined});
90 expect(result).toHaveLength(3);
91
92 result = [];
93 tile.querySourceFeatures(result, {sourceLayer: 'road', filter: ['==', 'class', 'main']});
94 expect(result).toHaveLength(1);
95 result = [];
96 tile.querySourceFeatures(result, {sourceLayer: 'road', filter: ['!=', 'class', 'main']});
97 expect(result).toHaveLength(2);
98
99 });
100
101 test('loadVectorData unloads existing data before overwriting it', () => {
102 const tile = new Tile(new OverscaledTileID(1, 0, 1, 1, 1), undefined);
103 tile.state = 'loaded';
104 const spy = jest.spyOn(tile, 'unloadVectorData');
105 const painter = {};
106
107 tile.loadVectorData(null, painter);
108
109 expect(spy).toHaveBeenCalledWith();
110 });
111
112 test('loadVectorData preserves the most recent rawTileData', () => {
113 const tile = new Tile(new OverscaledTileID(1, 0, 1, 1, 1), undefined);
114 tile.state = 'loaded';
115
116 tile.loadVectorData(
117 createVectorData({rawTileData: createRawTileData()}),
118 createPainter()
119 );
120 tile.loadVectorData(
121 createVectorData(),
122 createPainter()
123 );
124
125 const features = [];
126 tile.querySourceFeatures(features, {sourceLayer: 'road', filter: undefined});
127 expect(features).toHaveLength(3);
128
129 });
130
131});
132
133describe('Tile#isLessThan', () => {
134 test('correctly sorts tiles', () => {
135 const tiles = [
136 new OverscaledTileID(9, 0, 9, 146, 195),
137 new OverscaledTileID(9, 0, 9, 147, 195),
138 new OverscaledTileID(9, 0, 9, 148, 195),
139 new OverscaledTileID(9, 0, 9, 149, 195),
140 new OverscaledTileID(9, 1, 9, 144, 196),
141 new OverscaledTileID(9, 0, 9, 145, 196),
142 new OverscaledTileID(9, 0, 9, 146, 196),
143 new OverscaledTileID(9, 1, 9, 147, 196),
144 new OverscaledTileID(9, 0, 9, 145, 194),
145 new OverscaledTileID(9, 0, 9, 149, 196),
146 new OverscaledTileID(10, 0, 10, 293, 391),
147 new OverscaledTileID(10, 0, 10, 291, 390),
148 new OverscaledTileID(10, 1, 10, 293, 390),
149 new OverscaledTileID(10, 0, 10, 294, 390),
150 new OverscaledTileID(10, 0, 10, 295, 390),
151 new OverscaledTileID(10, 0, 10, 291, 391),
152 ];
153
154 const sortedTiles = tiles.sort((a, b) => { return a.isLessThan(b) ? -1 : b.isLessThan(a) ? 1 : 0; });
155
156 expect(sortedTiles).toEqual([
157 new OverscaledTileID(9, 0, 9, 145, 194),
158 new OverscaledTileID(9, 0, 9, 145, 196),
159 new OverscaledTileID(9, 0, 9, 146, 195),
160 new OverscaledTileID(9, 0, 9, 146, 196),
161 new OverscaledTileID(9, 0, 9, 147, 195),
162 new OverscaledTileID(9, 0, 9, 148, 195),
163 new OverscaledTileID(9, 0, 9, 149, 195),
164 new OverscaledTileID(9, 0, 9, 149, 196),
165 new OverscaledTileID(10, 0, 10, 291, 390),
166 new OverscaledTileID(10, 0, 10, 291, 391),
167 new OverscaledTileID(10, 0, 10, 293, 391),
168 new OverscaledTileID(10, 0, 10, 294, 390),
169 new OverscaledTileID(10, 0, 10, 295, 390),
170 new OverscaledTileID(9, 1, 9, 144, 196),
171 new OverscaledTileID(9, 1, 9, 147, 196),
172 new OverscaledTileID(10, 1, 10, 293, 390),
173 ]);
174 });
175});
176
177describe('expiring tiles', () => {
178 test('regular tiles do not expire', () => {
179 const tile = new Tile(new OverscaledTileID(1, 0, 1, 1, 1), undefined);
180 tile.state = 'loaded';
181 tile.timeAdded = Date.now();
182
183 expect(tile.getExpiryTimeout()).toBeFalsy();
184
185 });
186
187 test('set, get expiry', () => {
188 const tile = new Tile(new OverscaledTileID(1, 0, 1, 1, 1), undefined);
189 tile.state = 'loaded';
190 tile.timeAdded = Date.now();
191
192 tile.setExpiryData({
193 cacheControl: 'max-age=60'
194 });
195
196 // times are fuzzy, so we'll give this a little leeway:
197 let expiryTimeout = tile.getExpiryTimeout();
198 expect(expiryTimeout >= 56000 && expiryTimeout <= 60000).toBeTruthy();
199
200 const date = new Date();
201 date.setMinutes(date.getMinutes() + 10);
202 date.setMilliseconds(0);
203
204 tile.setExpiryData({
205 expires: date.toString()
206 });
207
208 expiryTimeout = tile.getExpiryTimeout();
209 expect(expiryTimeout > 598000 && expiryTimeout < 600000).toBeTruthy();
210
211 });
212
213 test('exponential backoff handling', () => {
214 const tile = new Tile(new OverscaledTileID(1, 0, 1, 1, 1), undefined);
215 tile.state = 'loaded';
216 tile.timeAdded = Date.now();
217
218 tile.setExpiryData({
219 cacheControl: 'max-age=10'
220 });
221
222 const expiryTimeout = tile.getExpiryTimeout();
223 expect(expiryTimeout >= 8000 && expiryTimeout <= 10000).toBeTruthy();
224
225 const justNow = new Date();
226 justNow.setSeconds(justNow.getSeconds() - 1);
227
228 // every time we set a tile's expiration to a date already expired,
229 // it assumes it comes from a new HTTP response, so this is counted
230 // as an extra expired tile request
231 tile.setExpiryData({
232 expires: justNow
233 });
234 expect(tile.getExpiryTimeout()).toBe(1000);
235
236 tile.setExpiryData({
237 expires: justNow
238 });
239 expect(tile.getExpiryTimeout()).toBe(2000);
240 tile.setExpiryData({
241 expires: justNow
242 });
243 expect(tile.getExpiryTimeout()).toBe(4000);
244
245 tile.setExpiryData({
246 expires: justNow
247 });
248 expect(tile.getExpiryTimeout()).toBe(8000);
249
250 });
251
252});
253
254describe('rtl text detection', () => {
255 test('Tile#hasRTLText is true when a tile loads a symbol bucket with rtl text', () => {
256 const tile = new Tile(new OverscaledTileID(1, 0, 1, 1, 1), undefined);
257 // Create a stub symbol bucket
258 const symbolBucket = createSymbolBucket('test', 'Test', 'test', new CollisionBoxArray());
259 // symbolBucket has not been populated yet so we force override the value in the stub
260 symbolBucket.hasRTLText = true;
261 tile.loadVectorData(
262 createVectorData({rawTileData: createRawTileData(), buckets: [symbolBucket]}),
263 createPainter({
264 getLayer() {
265 return symbolBucket.layers[0];
266 }
267 })
268 );
269
270 expect(tile.hasRTLText).toBeTruthy();
271 });
272
273});
274
275function createRawTileData() {
276 return fs.readFileSync(path.join(__dirname, '../../test/unit/assets/mbsv5-6-18-23.vector.pbf'));
277}
278
279function createVectorData(options?) {
280 const collisionBoxArray = new CollisionBoxArray();
281 return extend({
282 collisionBoxArray: deserialize(serialize(collisionBoxArray)),
283 featureIndex: deserialize(serialize(new FeatureIndex(new OverscaledTileID(1, 0, 1, 1, 1)))),
284 buckets: []
285 }, options);
286}
287
288function createPainter(styleStub = {}) {
289 return {style: styleStub};
290}