1 | import {createSymbolBucket} from '../../test/unit/lib/create_symbol_layer';
|
2 | import Tile from '../source/tile';
|
3 | import GeoJSONWrapper, {Feature} from '../source/geojson_wrapper';
|
4 | import {OverscaledTileID} from '../source/tile_id';
|
5 | import fs from 'fs';
|
6 | import path from 'path';
|
7 | import vtpbf from 'vt-pbf';
|
8 | import FeatureIndex from '../data/feature_index';
|
9 | import {CollisionBoxArray} from '../data/array_types.g';
|
10 | import {extend} from '../util/util';
|
11 | import {serialize, deserialize} from '../util/web_worker_transfer';
|
12 |
|
13 | describe('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 |
|
133 | describe('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 |
|
177 | describe('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 |
|
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 |
|
229 |
|
230 |
|
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 |
|
254 | describe('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 |
|
258 | const symbolBucket = createSymbolBucket('test', 'Test', 'test', new CollisionBoxArray());
|
259 |
|
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 |
|
275 | function createRawTileData() {
|
276 | return fs.readFileSync(path.join(__dirname, '../../test/unit/assets/mbsv5-6-18-23.vector.pbf'));
|
277 | }
|
278 |
|
279 | function 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 |
|
288 | function createPainter(styleStub = {}) {
|
289 | return {style: styleStub};
|
290 | }
|