@@ -4,18 +4,185 @@ import {StyleLayerIndex} from '../style/style_layer_index';
44import { OverscaledTileID } from '../tile/tile_id' ;
55import perf from '../util/performance' ;
66import { type LayerSpecification } from '@maplibre/maplibre-gl-style-spec' ;
7- import { type Actor } from '../util/actor' ;
8- import { type WorkerTileParameters } from './worker_source' ;
7+ import { type Actor , type IActor } from '../util/actor' ;
8+ import { type TileParameters , type WorkerTileParameters , type WorkerTileResult } from './worker_source' ;
99import { setPerformance , sleep } from '../util/test/util' ;
1010import { type FakeServer , fakeServer } from 'nise' ;
1111import { GEOJSON_TILE_LAYER_NAME } from '@maplibre/vt-pbf' ;
12+ import { SubdivisionGranularitySetting } from '../render/subdivision_granularity_settings' ;
13+ import { type WorkerTile } from './worker_tile' ;
1214
1315const actor = { send : ( ) => { } } as any as Actor ;
1416
1517beforeEach ( ( ) => {
1618 setPerformance ( ) ;
1719} ) ;
1820
21+ afterEach ( ( ) => {
22+ vi . clearAllMocks ( ) ;
23+ } ) ;
24+
25+ describe ( 'geojson tile worker source' , ( ) => {
26+ const actor : IActor = { sendAsync : ( ) => Promise . resolve ( { } ) } as any as IActor ;
27+
28+ test ( 'GeoJSONWorkerSource.removeTile removes loaded tile' , async ( ) => {
29+ const source = new GeoJSONWorkerSource ( actor , new StyleLayerIndex ( ) , [ ] ) ;
30+
31+ source . tileState . loaded = {
32+ '0' : { } as WorkerTile
33+ } ;
34+
35+ const res = await source . removeTile ( {
36+ source : 'source' ,
37+ uid : 0
38+ } as any as TileParameters ) ;
39+ expect ( res ) . toBeUndefined ( ) ;
40+
41+ expect ( source . tileState . loaded ) . toEqual ( { } ) ;
42+ } ) ;
43+
44+ test ( 'GeoJSONWorkerSource.reloadTile reloads a previously-loaded tile' , async ( ) => {
45+ const source = new GeoJSONWorkerSource ( actor , new StyleLayerIndex ( ) , [ ] ) ;
46+ const parse = vi . fn ( ) . mockResolvedValue ( { } as WorkerTileResult ) ;
47+
48+ source . tileState . loaded = {
49+ '0' : {
50+ status : 'done' ,
51+ vectorTile : { } ,
52+ parse
53+ } as any as WorkerTile
54+ } ;
55+
56+ const reloadPromise = source . reloadTile ( { uid : 0 } as any as WorkerTileParameters ) ;
57+ expect ( parse ) . toHaveBeenCalledTimes ( 1 ) ;
58+ await expect ( reloadPromise ) . resolves . toBeTruthy ( ) ;
59+ } ) ;
60+
61+ test ( 'GeoJSONWorkerSource.reloadTile returns parse result without rawTileData when parsing state was already consumed' , async ( ) => {
62+ const source = new GeoJSONWorkerSource ( actor , new StyleLayerIndex ( ) , [ ] ) ;
63+ const parseResult = { buckets : [ ] } as any as WorkerTileResult ;
64+ const parse = vi . fn ( ) . mockResolvedValue ( parseResult ) ;
65+
66+ source . tileState . loaded = {
67+ '0' : {
68+ status : 'parsing' ,
69+ vectorTile : { } ,
70+ parse
71+ } as any as WorkerTile
72+ } ;
73+
74+ const result = await source . reloadTile ( { uid : 0 } as any as WorkerTileParameters ) ;
75+
76+ expect ( parse ) . toHaveBeenCalledTimes ( 1 ) ;
77+ expect ( result ) . toBe ( parseResult ) ;
78+ expect ( result . rawTileData ) . toBeUndefined ( ) ;
79+ } ) ;
80+
81+ test ( 'GeoJSONWorkerSource.loadTile reparses tile if reloadTile has been called during parsing' , async ( ) => {
82+ const layerIndex = new StyleLayerIndex ( [ {
83+ id : 'test' ,
84+ source : 'source' ,
85+ 'source-layer' : '_geojsonTileLayer' ,
86+ type : 'symbol' ,
87+ layout : {
88+ 'icon-image' : 'hello' ,
89+ 'text-font' : [ 'StandardFont-Bold' ] ,
90+ 'text-field' : '{name}'
91+ }
92+ } ] ) ;
93+
94+ const actor = {
95+ sendAsync : ( message : { type : string ; data : unknown } , abortController : AbortController ) => {
96+ return new Promise ( ( resolve , _reject ) => {
97+ const res = setTimeout ( ( ) => {
98+ const response = message . type === 'getImages' ?
99+ { 'hello' : { width : 1 , height : 1 , data : new Uint8Array ( [ 0 ] ) } } :
100+ { 'StandardFont-Bold' : { width : 1 , height : 1 , data : new Uint8Array ( [ 0 ] ) } } ;
101+ resolve ( response ) ;
102+ } , 100 ) ;
103+ abortController . signal . addEventListener ( 'abort' , ( ) => {
104+ clearTimeout ( res ) ;
105+ } ) ;
106+ } ) ;
107+ }
108+ } ;
109+
110+ const source = new GeoJSONWorkerSource ( actor as any , layerIndex , [ 'hello' ] ) ;
111+
112+ const geoJson = {
113+ type : 'FeatureCollection' ,
114+ features : [ {
115+ type : 'Feature' ,
116+ id : 1 ,
117+ geometry : {
118+ type : 'Point' ,
119+ coordinates : [ 0 , 0 ]
120+ } ,
121+ properties : {
122+ name : 'test'
123+ }
124+ } ]
125+ } as GeoJSON . GeoJSON ;
126+
127+ await source . loadData ( { source : 'source' , data : geoJson } as LoadGeoJSONParameters ) ;
128+
129+ source . loadTile ( {
130+ source : 'source' ,
131+ uid : 0 ,
132+ tileID : { overscaledZ : 0 , wrap : 0 , canonical : { x : 0 , y : 0 , z : 0 , w : 0 } } ,
133+ subdivisionGranularity : SubdivisionGranularitySetting . noSubdivision ,
134+ } as any as WorkerTileParameters ) . then ( ( ) => expect ( false ) . toBeTruthy ( ) ) ;
135+
136+ // allow promise to run
137+ await sleep ( 0 ) ;
138+
139+ const res = await source . reloadTile ( {
140+ source : 'source' ,
141+ uid : 0 ,
142+ tileID : { overscaledZ : 0 , wrap : 0 , canonical : { x : 0 , y : 0 , z : 0 , w : 0 } } ,
143+ subdivisionGranularity : SubdivisionGranularitySetting . noSubdivision ,
144+ } as any as WorkerTileParameters ) ;
145+
146+ expect ( res ) . toBeDefined ( ) ;
147+ expect ( res . rawTileData ) . toBeDefined ( ) ;
148+ } ) ;
149+
150+ test ( 'GeoJSONWorkerSource.loadTile returns null for an empty tile' , async ( ) => {
151+ const source = new GeoJSONWorkerSource ( actor , new StyleLayerIndex ( ) , [ ] ) ;
152+ await source . loadData ( { source : 'source' , data : { type : 'FeatureCollection' , features : [ ] } } as LoadGeoJSONParameters ) ;
153+
154+ const result = await source . loadTile ( {
155+ source : 'source' ,
156+ uid : 0 ,
157+ tileID : { overscaledZ : 0 , wrap : 0 , canonical : { x : 0 , y : 0 , z : 0 , w : 0 } } ,
158+ } as any as WorkerTileParameters ) ;
159+
160+ expect ( result ) . toBeNull ( ) ;
161+ } ) ;
162+
163+ test ( 'GeoJSONWorkerSource.loadTile throws error when data has not been loaded' , async ( ) => {
164+ const source = new GeoJSONWorkerSource ( actor , new StyleLayerIndex ( ) , [ ] ) ;
165+
166+ await expect ( source . loadTile ( {
167+ source : 'source' ,
168+ uid : 0 ,
169+ tileID : { overscaledZ : 0 , wrap : 0 , canonical : { x : 0 , y : 0 , z : 0 , w : 0 } } ,
170+ } as any as WorkerTileParameters ) ) . rejects . toThrowError ( / U n a b l e t o p a r s e t h e d a t a i n t o a c l u s t e r o r g e o j s o n / ) ;
171+ } ) ;
172+
173+ test ( 'GeoJSONWorkerSource.abortTile aborts tile state' , async ( ) => {
174+ const source = new GeoJSONWorkerSource ( actor , new StyleLayerIndex ( ) , [ ] ) ;
175+ const abortSpy = vi . spyOn ( source . tileState , 'abort' ) ;
176+
177+ await source . abortTile ( {
178+ source : 'source' ,
179+ uid : 0
180+ } as any as TileParameters ) ;
181+
182+ expect ( abortSpy ) . toHaveBeenCalledWith ( 0 ) ;
183+ } ) ;
184+ } ) ;
185+
19186describe ( 'reloadTile' , ( ) => {
20187 test ( 'does not rebuild vector data unless data has changed' , async ( ) => {
21188 const layers = [
0 commit comments