@navaramap/core 0.2.1 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +116 -91
- package/dist/index.js +273 -483
- package/dist/index.umd.cjs +1 -1
- package/package.json +1 -1
- package/src/color/ColorMap.test.ts +15 -0
- package/src/color/ColorMap.ts +7 -6
- package/src/event/JobDispatcher.test.ts +484 -0
- package/src/event/JobDispatcher.ts +387 -0
- package/src/event/index.ts +1 -2
- package/src/event/mock.ts +2 -2
- package/src/globe/Globe.ts +3 -26
- package/src/utils/ReturnedConstructedTerrainMeshLike.ts +5 -0
- package/src/utils/polyline/ConstructedPolylineGeometryLike.ts +23 -16
- package/src/event/EventManager.test.ts +0 -635
- package/src/event/EventManager.ts +0 -438
- package/src/event/TransactionManager.test.ts +0 -126
- package/src/event/TransactionManager.ts +0 -78
|
@@ -1,635 +0,0 @@
|
|
|
1
|
-
import type { RenderableFeatureAddedEvent } from "@navaramap/engine";
|
|
2
|
-
import { expect, it, vi } from "vitest";
|
|
3
|
-
|
|
4
|
-
import { wait } from "../time";
|
|
5
|
-
|
|
6
|
-
import { EventManager } from "./EventManager";
|
|
7
|
-
import { makeEvent, makeRenderableFeatures } from "./mock";
|
|
8
|
-
|
|
9
|
-
it("should handle renderable feature event", async () => {
|
|
10
|
-
const eventManager = new EventManager();
|
|
11
|
-
|
|
12
|
-
const mockFnAdded = vi.fn();
|
|
13
|
-
const mockFnRemoved = vi.fn();
|
|
14
|
-
const mockFnChanged = vi.fn();
|
|
15
|
-
|
|
16
|
-
// First frame
|
|
17
|
-
eventManager.pushEvents(
|
|
18
|
-
makeEvent({
|
|
19
|
-
renderable_feature_added: [
|
|
20
|
-
makeRenderableFeatures(1, 1),
|
|
21
|
-
makeRenderableFeatures(1, 2),
|
|
22
|
-
makeRenderableFeatures(1, 3),
|
|
23
|
-
makeRenderableFeatures(1, 4),
|
|
24
|
-
],
|
|
25
|
-
renderable_feature_removed: [],
|
|
26
|
-
renderable_feature_changed: [],
|
|
27
|
-
}),
|
|
28
|
-
);
|
|
29
|
-
|
|
30
|
-
eventManager.processTransactionEvents(
|
|
31
|
-
"renderableFeature",
|
|
32
|
-
{
|
|
33
|
-
add: {
|
|
34
|
-
key: "renderable_feature_added",
|
|
35
|
-
max: 3,
|
|
36
|
-
},
|
|
37
|
-
remove: {
|
|
38
|
-
key: "renderable_feature_removed",
|
|
39
|
-
},
|
|
40
|
-
change: {
|
|
41
|
-
key: "renderable_feature_changed",
|
|
42
|
-
},
|
|
43
|
-
},
|
|
44
|
-
async ({ type }) => {
|
|
45
|
-
switch (type) {
|
|
46
|
-
case "add":
|
|
47
|
-
await wait(100);
|
|
48
|
-
mockFnAdded();
|
|
49
|
-
break;
|
|
50
|
-
case "remove":
|
|
51
|
-
await wait(100);
|
|
52
|
-
mockFnRemoved();
|
|
53
|
-
break;
|
|
54
|
-
case "change":
|
|
55
|
-
await wait(100);
|
|
56
|
-
mockFnChanged();
|
|
57
|
-
break;
|
|
58
|
-
}
|
|
59
|
-
},
|
|
60
|
-
);
|
|
61
|
-
|
|
62
|
-
await vi.waitFor(() => expect(mockFnAdded).toBeCalledTimes(3));
|
|
63
|
-
expect(mockFnRemoved).not.toBeCalled();
|
|
64
|
-
expect(mockFnChanged).not.toBeCalled();
|
|
65
|
-
|
|
66
|
-
mockFnAdded.mockReset();
|
|
67
|
-
|
|
68
|
-
// Second frame
|
|
69
|
-
eventManager.pushEvents(
|
|
70
|
-
makeEvent({
|
|
71
|
-
renderable_feature_added: [],
|
|
72
|
-
renderable_feature_removed: [
|
|
73
|
-
makeRenderableFeatures(1, 1),
|
|
74
|
-
makeRenderableFeatures(1, 4),
|
|
75
|
-
],
|
|
76
|
-
renderable_feature_changed: [
|
|
77
|
-
makeRenderableFeatures(1, 1),
|
|
78
|
-
makeRenderableFeatures(1, 2),
|
|
79
|
-
makeRenderableFeatures(1, 3),
|
|
80
|
-
],
|
|
81
|
-
}),
|
|
82
|
-
);
|
|
83
|
-
|
|
84
|
-
eventManager.processTransactionEvents(
|
|
85
|
-
"renderableFeature",
|
|
86
|
-
{
|
|
87
|
-
add: {
|
|
88
|
-
key: "renderable_feature_added",
|
|
89
|
-
max: 3,
|
|
90
|
-
},
|
|
91
|
-
remove: {
|
|
92
|
-
key: "renderable_feature_removed",
|
|
93
|
-
},
|
|
94
|
-
change: {
|
|
95
|
-
key: "renderable_feature_changed",
|
|
96
|
-
},
|
|
97
|
-
},
|
|
98
|
-
async ({ type }) => {
|
|
99
|
-
switch (type) {
|
|
100
|
-
case "add":
|
|
101
|
-
await wait(100);
|
|
102
|
-
mockFnAdded();
|
|
103
|
-
break;
|
|
104
|
-
case "remove":
|
|
105
|
-
await wait(100);
|
|
106
|
-
mockFnRemoved();
|
|
107
|
-
break;
|
|
108
|
-
case "change":
|
|
109
|
-
await wait(100);
|
|
110
|
-
mockFnChanged();
|
|
111
|
-
break;
|
|
112
|
-
}
|
|
113
|
-
},
|
|
114
|
-
);
|
|
115
|
-
|
|
116
|
-
expect(mockFnAdded).not.toBeCalled();
|
|
117
|
-
await vi.waitFor(() => expect(mockFnRemoved).toBeCalledTimes(1));
|
|
118
|
-
expect(mockFnChanged).not.toBeCalled();
|
|
119
|
-
|
|
120
|
-
mockFnRemoved.mockReset();
|
|
121
|
-
|
|
122
|
-
// Third frame
|
|
123
|
-
eventManager.processTransactionEvents(
|
|
124
|
-
"renderableFeature",
|
|
125
|
-
{
|
|
126
|
-
add: {
|
|
127
|
-
key: "renderable_feature_added",
|
|
128
|
-
max: 3,
|
|
129
|
-
},
|
|
130
|
-
remove: {
|
|
131
|
-
key: "renderable_feature_removed",
|
|
132
|
-
},
|
|
133
|
-
change: {
|
|
134
|
-
key: "renderable_feature_changed",
|
|
135
|
-
},
|
|
136
|
-
},
|
|
137
|
-
async ({ type }) => {
|
|
138
|
-
switch (type) {
|
|
139
|
-
case "add":
|
|
140
|
-
await wait(100);
|
|
141
|
-
mockFnAdded();
|
|
142
|
-
break;
|
|
143
|
-
case "remove":
|
|
144
|
-
await wait(100);
|
|
145
|
-
mockFnRemoved();
|
|
146
|
-
break;
|
|
147
|
-
case "change":
|
|
148
|
-
await wait(100);
|
|
149
|
-
mockFnChanged();
|
|
150
|
-
break;
|
|
151
|
-
}
|
|
152
|
-
},
|
|
153
|
-
);
|
|
154
|
-
|
|
155
|
-
expect(mockFnAdded).not.toBeCalled();
|
|
156
|
-
expect(mockFnRemoved).not.toBeCalled();
|
|
157
|
-
await vi.waitFor(() => expect(mockFnChanged).toBeCalledTimes(2));
|
|
158
|
-
});
|
|
159
|
-
|
|
160
|
-
it("should remove duplicated renderable feature event", async () => {
|
|
161
|
-
const eventManager = new EventManager();
|
|
162
|
-
|
|
163
|
-
eventManager.pushEvents(
|
|
164
|
-
makeEvent({
|
|
165
|
-
renderable_feature_added: [
|
|
166
|
-
makeRenderableFeatures(1, 1),
|
|
167
|
-
makeRenderableFeatures(1, 2),
|
|
168
|
-
makeRenderableFeatures(1, 3),
|
|
169
|
-
makeRenderableFeatures(1, 4),
|
|
170
|
-
],
|
|
171
|
-
renderable_feature_removed: [
|
|
172
|
-
makeRenderableFeatures(1, 1),
|
|
173
|
-
makeRenderableFeatures(1, 3),
|
|
174
|
-
],
|
|
175
|
-
renderable_feature_changed: [
|
|
176
|
-
makeRenderableFeatures(1, 1),
|
|
177
|
-
makeRenderableFeatures(1, 4),
|
|
178
|
-
],
|
|
179
|
-
}),
|
|
180
|
-
);
|
|
181
|
-
|
|
182
|
-
eventManager.removeDuplicatedTransactionEvents({
|
|
183
|
-
add: {
|
|
184
|
-
key: "renderable_feature_added",
|
|
185
|
-
},
|
|
186
|
-
remove: {
|
|
187
|
-
key: "renderable_feature_removed",
|
|
188
|
-
},
|
|
189
|
-
change: {
|
|
190
|
-
key: "renderable_feature_changed",
|
|
191
|
-
},
|
|
192
|
-
});
|
|
193
|
-
|
|
194
|
-
expect(eventManager.stacks.renderable_feature_added).toEqual([
|
|
195
|
-
makeRenderableFeatures(1, 2),
|
|
196
|
-
makeRenderableFeatures(1, 4),
|
|
197
|
-
]);
|
|
198
|
-
expect(eventManager.stacks.renderable_feature_removed).toEqual([]);
|
|
199
|
-
expect(eventManager.stacks.renderable_feature_changed).toEqual([
|
|
200
|
-
makeRenderableFeatures(1, 4),
|
|
201
|
-
]);
|
|
202
|
-
});
|
|
203
|
-
|
|
204
|
-
it("should handle the increment in async boundary", async () => {
|
|
205
|
-
const eventManager = new EventManager();
|
|
206
|
-
|
|
207
|
-
// Simulate a concurrency limiter that allows max 2 concurrent operations
|
|
208
|
-
const maxConcurrent = 2;
|
|
209
|
-
let currentConcurrent = 0;
|
|
210
|
-
let processedCount = 0;
|
|
211
|
-
|
|
212
|
-
const canIncrement = () => currentConcurrent < maxConcurrent;
|
|
213
|
-
const increment = () => {
|
|
214
|
-
currentConcurrent++;
|
|
215
|
-
};
|
|
216
|
-
const decrement = () => {
|
|
217
|
-
currentConcurrent--;
|
|
218
|
-
};
|
|
219
|
-
|
|
220
|
-
const doAsync = async (t: number) => {
|
|
221
|
-
increment();
|
|
222
|
-
await wait(t);
|
|
223
|
-
decrement();
|
|
224
|
-
processedCount++;
|
|
225
|
-
};
|
|
226
|
-
|
|
227
|
-
const addedFeatures: RenderableFeatureAddedEvent[] = [
|
|
228
|
-
makeRenderableFeatures(1, 1),
|
|
229
|
-
makeRenderableFeatures(1, 2),
|
|
230
|
-
makeRenderableFeatures(1, 3),
|
|
231
|
-
makeRenderableFeatures(1, 4),
|
|
232
|
-
makeRenderableFeatures(1, 5),
|
|
233
|
-
];
|
|
234
|
-
|
|
235
|
-
eventManager.pushEvents(
|
|
236
|
-
makeEvent({
|
|
237
|
-
renderable_feature_added: addedFeatures,
|
|
238
|
-
renderable_feature_removed: [],
|
|
239
|
-
renderable_feature_changed: [],
|
|
240
|
-
}),
|
|
241
|
-
);
|
|
242
|
-
|
|
243
|
-
const waitingTime = 10;
|
|
244
|
-
|
|
245
|
-
eventManager.processTransactionEvents(
|
|
246
|
-
"renderableFeature",
|
|
247
|
-
{
|
|
248
|
-
add: {
|
|
249
|
-
key: "renderable_feature_added",
|
|
250
|
-
max: 10,
|
|
251
|
-
},
|
|
252
|
-
remove: {
|
|
253
|
-
key: "renderable_feature_removed",
|
|
254
|
-
},
|
|
255
|
-
change: {
|
|
256
|
-
key: "renderable_feature_changed",
|
|
257
|
-
},
|
|
258
|
-
},
|
|
259
|
-
async ({ type }) => {
|
|
260
|
-
if (type === "add") {
|
|
261
|
-
await doAsync(waitingTime);
|
|
262
|
-
}
|
|
263
|
-
},
|
|
264
|
-
{
|
|
265
|
-
shouldProcess: ({ type }) => {
|
|
266
|
-
if (type === "add") {
|
|
267
|
-
return canIncrement();
|
|
268
|
-
}
|
|
269
|
-
return true;
|
|
270
|
-
},
|
|
271
|
-
},
|
|
272
|
-
);
|
|
273
|
-
|
|
274
|
-
// Wait until all features is processed.
|
|
275
|
-
await wait(waitingTime * (addedFeatures.length + 1));
|
|
276
|
-
|
|
277
|
-
await vi.waitFor(() => expect(processedCount).toBe(2));
|
|
278
|
-
});
|
|
279
|
-
|
|
280
|
-
it("should keep skipped events in stack when shouldProcess returns false", async () => {
|
|
281
|
-
const eventManager = new EventManager();
|
|
282
|
-
|
|
283
|
-
const processedIds: number[] = [];
|
|
284
|
-
|
|
285
|
-
// Add 5 events to the stack
|
|
286
|
-
eventManager.pushEvents(
|
|
287
|
-
makeEvent({
|
|
288
|
-
renderable_feature_added: [
|
|
289
|
-
makeRenderableFeatures(1, 1),
|
|
290
|
-
makeRenderableFeatures(1, 2),
|
|
291
|
-
makeRenderableFeatures(1, 3),
|
|
292
|
-
makeRenderableFeatures(1, 4),
|
|
293
|
-
makeRenderableFeatures(1, 5),
|
|
294
|
-
],
|
|
295
|
-
renderable_feature_removed: [],
|
|
296
|
-
renderable_feature_changed: [],
|
|
297
|
-
}),
|
|
298
|
-
);
|
|
299
|
-
|
|
300
|
-
// First pass: only process events with odd gen (1, 3, 5)
|
|
301
|
-
await eventManager.forEachStackAsync(
|
|
302
|
-
"renderable_feature_added",
|
|
303
|
-
async (event) => {
|
|
304
|
-
processedIds.push(event.gen);
|
|
305
|
-
},
|
|
306
|
-
10,
|
|
307
|
-
(event) => event.gen % 2 === 1, // Only process odd gen
|
|
308
|
-
);
|
|
309
|
-
|
|
310
|
-
// Should have processed 1, 3, 5
|
|
311
|
-
expect(processedIds).toEqual([1, 3, 5]);
|
|
312
|
-
|
|
313
|
-
// Events with gen 2, 4 should remain in the stack
|
|
314
|
-
expect(eventManager.stacks.renderable_feature_added).toHaveLength(2);
|
|
315
|
-
expect(eventManager.stacks.renderable_feature_added[0]).toMatchObject({
|
|
316
|
-
gen: 2,
|
|
317
|
-
});
|
|
318
|
-
expect(eventManager.stacks.renderable_feature_added[1]).toMatchObject({
|
|
319
|
-
gen: 4,
|
|
320
|
-
});
|
|
321
|
-
|
|
322
|
-
// Second pass: process remaining events
|
|
323
|
-
processedIds.length = 0;
|
|
324
|
-
await eventManager.forEachStackAsync(
|
|
325
|
-
"renderable_feature_added",
|
|
326
|
-
async (event) => {
|
|
327
|
-
processedIds.push(event.gen);
|
|
328
|
-
},
|
|
329
|
-
10,
|
|
330
|
-
);
|
|
331
|
-
|
|
332
|
-
// Should have processed 2, 4
|
|
333
|
-
expect(processedIds).toEqual([2, 4]);
|
|
334
|
-
|
|
335
|
-
// Stack should now be empty
|
|
336
|
-
expect(eventManager.stacks.renderable_feature_added).toHaveLength(0);
|
|
337
|
-
});
|
|
338
|
-
|
|
339
|
-
it("should correctly count max limit including skipped events", async () => {
|
|
340
|
-
const eventManager = new EventManager();
|
|
341
|
-
|
|
342
|
-
const processedIds: number[] = [];
|
|
343
|
-
|
|
344
|
-
// Add 10 events to the stack
|
|
345
|
-
eventManager.pushEvents(
|
|
346
|
-
makeEvent({
|
|
347
|
-
renderable_feature_added: [
|
|
348
|
-
makeRenderableFeatures(1, 1),
|
|
349
|
-
makeRenderableFeatures(1, 2),
|
|
350
|
-
makeRenderableFeatures(1, 3),
|
|
351
|
-
makeRenderableFeatures(1, 4),
|
|
352
|
-
makeRenderableFeatures(1, 5),
|
|
353
|
-
makeRenderableFeatures(1, 6),
|
|
354
|
-
makeRenderableFeatures(1, 7),
|
|
355
|
-
makeRenderableFeatures(1, 8),
|
|
356
|
-
makeRenderableFeatures(1, 9),
|
|
357
|
-
makeRenderableFeatures(1, 10),
|
|
358
|
-
],
|
|
359
|
-
renderable_feature_removed: [],
|
|
360
|
-
renderable_feature_changed: [],
|
|
361
|
-
}),
|
|
362
|
-
);
|
|
363
|
-
|
|
364
|
-
// Process with max=5, but only process odd gen (1, 3, 5)
|
|
365
|
-
// This tests that idx is incremented for skipped events too,
|
|
366
|
-
// so max limit works correctly (should check first 5 events: 1,2,3,4,5)
|
|
367
|
-
await eventManager.forEachStackAsync(
|
|
368
|
-
"renderable_feature_added",
|
|
369
|
-
async (event) => {
|
|
370
|
-
processedIds.push(event.gen);
|
|
371
|
-
},
|
|
372
|
-
5, // max limit
|
|
373
|
-
(event) => event.gen % 2 === 1, // Only process odd gen
|
|
374
|
-
);
|
|
375
|
-
|
|
376
|
-
// Should have processed odd events within first 5: 1, 3, 5
|
|
377
|
-
expect(processedIds).toEqual([1, 3, 5]);
|
|
378
|
-
|
|
379
|
-
// Stack should have: 2, 4 (skipped from first 5) + 6, 7, 8, 9, 10 (not checked)
|
|
380
|
-
expect(eventManager.stacks.renderable_feature_added).toHaveLength(7);
|
|
381
|
-
expect(
|
|
382
|
-
eventManager.stacks.renderable_feature_added.map((e) => e.gen),
|
|
383
|
-
).toEqual([2, 4, 6, 7, 8, 9, 10]);
|
|
384
|
-
});
|
|
385
|
-
|
|
386
|
-
it("should process the remaining events of a batch when one handler rejects", async () => {
|
|
387
|
-
const consoleError = vi.spyOn(console, "error").mockImplementation(() => {});
|
|
388
|
-
const eventManager = new EventManager();
|
|
389
|
-
|
|
390
|
-
eventManager.pushEvents(
|
|
391
|
-
makeEvent({
|
|
392
|
-
renderable_feature_added: [
|
|
393
|
-
makeRenderableFeatures(1, 1),
|
|
394
|
-
makeRenderableFeatures(1, 2),
|
|
395
|
-
makeRenderableFeatures(1, 3),
|
|
396
|
-
],
|
|
397
|
-
}),
|
|
398
|
-
);
|
|
399
|
-
|
|
400
|
-
const processedIds: number[] = [];
|
|
401
|
-
await eventManager.forEachStackAsync(
|
|
402
|
-
"renderable_feature_added",
|
|
403
|
-
async (event) => {
|
|
404
|
-
if (event.gen === 2) throw new Error("boom");
|
|
405
|
-
processedIds.push(event.gen);
|
|
406
|
-
},
|
|
407
|
-
10,
|
|
408
|
-
);
|
|
409
|
-
|
|
410
|
-
expect(processedIds).toEqual([1, 3]);
|
|
411
|
-
// The failed event still counts as processed: it must leave the stack (and
|
|
412
|
-
// be freed) instead of being retried forever.
|
|
413
|
-
expect(eventManager.stacks.renderable_feature_added).toHaveLength(0);
|
|
414
|
-
expect(consoleError).toHaveBeenCalledTimes(1);
|
|
415
|
-
consoleError.mockRestore();
|
|
416
|
-
});
|
|
417
|
-
|
|
418
|
-
it("should abort the remaining in-flight adds even when an onAbort handler throws", async () => {
|
|
419
|
-
const consoleError = vi.spyOn(console, "error").mockImplementation(() => {});
|
|
420
|
-
const eventManager = new EventManager();
|
|
421
|
-
|
|
422
|
-
const aborted: number[] = [];
|
|
423
|
-
let releaseAdds!: () => void;
|
|
424
|
-
const gate = new Promise<void>((resolve) => (releaseAdds = resolve));
|
|
425
|
-
|
|
426
|
-
const options = {
|
|
427
|
-
add: { key: "renderable_feature_added" },
|
|
428
|
-
remove: { key: "renderable_feature_removed" },
|
|
429
|
-
} as const;
|
|
430
|
-
const cb = async ({ type }: { type: string }) => {
|
|
431
|
-
if (type === "add") await gate;
|
|
432
|
-
};
|
|
433
|
-
const handlers = {
|
|
434
|
-
onAbort: (ev: { gen: number }) => {
|
|
435
|
-
if (ev.gen === 1) throw new Error("abort boom");
|
|
436
|
-
aborted.push(ev.gen);
|
|
437
|
-
},
|
|
438
|
-
};
|
|
439
|
-
|
|
440
|
-
// First frame: two adds start and stay pending on the gate.
|
|
441
|
-
eventManager.pushEvents(
|
|
442
|
-
makeEvent({
|
|
443
|
-
renderable_feature_added: [
|
|
444
|
-
makeRenderableFeatures(1, 1),
|
|
445
|
-
makeRenderableFeatures(1, 2),
|
|
446
|
-
],
|
|
447
|
-
}),
|
|
448
|
-
);
|
|
449
|
-
eventManager.processTransactionEvents("workerTask", options, cb, handlers);
|
|
450
|
-
|
|
451
|
-
// Second frame: removals for both pending adds arrive. The first abort
|
|
452
|
-
// handler throws; the second must still run.
|
|
453
|
-
eventManager.pushEvents(
|
|
454
|
-
makeEvent({
|
|
455
|
-
renderable_feature_removed: [
|
|
456
|
-
makeRenderableFeatures(1, 1),
|
|
457
|
-
makeRenderableFeatures(1, 2),
|
|
458
|
-
],
|
|
459
|
-
}),
|
|
460
|
-
);
|
|
461
|
-
eventManager.processTransactionEvents("workerTask", options, cb, handlers);
|
|
462
|
-
|
|
463
|
-
expect(aborted).toEqual([2]);
|
|
464
|
-
expect(consoleError).toHaveBeenCalledTimes(1);
|
|
465
|
-
|
|
466
|
-
// Later frames replay the transaction; the pending adds settle and the
|
|
467
|
-
// remove phase drains the stack.
|
|
468
|
-
releaseAdds();
|
|
469
|
-
await vi.waitFor(() => {
|
|
470
|
-
eventManager.processTransactionEvents("workerTask", options, cb, handlers);
|
|
471
|
-
expect(eventManager.stacks.renderable_feature_removed).toHaveLength(0);
|
|
472
|
-
});
|
|
473
|
-
consoleError.mockRestore();
|
|
474
|
-
});
|
|
475
|
-
|
|
476
|
-
it("should keep in-flight add tracking isolated between transaction keys", async () => {
|
|
477
|
-
const eventManager = new EventManager();
|
|
478
|
-
|
|
479
|
-
const aborted: number[] = [];
|
|
480
|
-
let releaseAdds!: () => void;
|
|
481
|
-
const gate = new Promise<void>((resolve) => (releaseAdds = resolve));
|
|
482
|
-
|
|
483
|
-
const optionsA = {
|
|
484
|
-
add: { key: "renderable_feature_added" },
|
|
485
|
-
remove: { key: "renderable_feature_removed" },
|
|
486
|
-
} as const;
|
|
487
|
-
const cbA = async ({ type }: { type: string }) => {
|
|
488
|
-
if (type === "add") await gate;
|
|
489
|
-
};
|
|
490
|
-
const handlersA = {
|
|
491
|
-
onAbort: (ev: { gen: number }) => {
|
|
492
|
-
aborted.push(ev.gen);
|
|
493
|
-
},
|
|
494
|
-
};
|
|
495
|
-
|
|
496
|
-
// Key A: one add starts and stays pending on the gate.
|
|
497
|
-
eventManager.pushEvents(
|
|
498
|
-
makeEvent({
|
|
499
|
-
renderable_feature_added: [makeRenderableFeatures(1, 1)],
|
|
500
|
-
}),
|
|
501
|
-
);
|
|
502
|
-
eventManager.processTransactionEvents("keyA", optionsA, cbA, handlersA);
|
|
503
|
-
|
|
504
|
-
// Key B: runs its add and remove phases to completion, clearing ITS OWN
|
|
505
|
-
// in-flight tracking. Before the per-key split this wiped key A's ids too
|
|
506
|
-
// and A's abort below never fired.
|
|
507
|
-
let processedB = false;
|
|
508
|
-
eventManager.pushEvents(
|
|
509
|
-
makeEvent({
|
|
510
|
-
texture_fragment_removed: [makeRenderableFeatures(9, 9)],
|
|
511
|
-
}),
|
|
512
|
-
);
|
|
513
|
-
const optionsB = {
|
|
514
|
-
add: { key: "texture_fragment_requested" },
|
|
515
|
-
remove: { key: "texture_fragment_removed" },
|
|
516
|
-
} as const;
|
|
517
|
-
const cbB = async ({ type }: { type: string }) => {
|
|
518
|
-
if (type === "remove") processedB = true;
|
|
519
|
-
};
|
|
520
|
-
// Replay across "frames" until key B's remove phase (which clears its
|
|
521
|
-
// tracking) has run.
|
|
522
|
-
await vi.waitFor(() => {
|
|
523
|
-
eventManager.processTransactionEvents("keyB", optionsB, cbB, {
|
|
524
|
-
onAbort: () => {},
|
|
525
|
-
});
|
|
526
|
-
expect(processedB).toBe(true);
|
|
527
|
-
});
|
|
528
|
-
|
|
529
|
-
// A removal for key A's still-pending add arrives; its abort must fire.
|
|
530
|
-
eventManager.pushEvents(
|
|
531
|
-
makeEvent({
|
|
532
|
-
renderable_feature_removed: [makeRenderableFeatures(1, 1)],
|
|
533
|
-
}),
|
|
534
|
-
);
|
|
535
|
-
eventManager.processTransactionEvents("keyA", optionsA, cbA, handlersA);
|
|
536
|
-
|
|
537
|
-
expect(aborted).toEqual([1]);
|
|
538
|
-
releaseAdds();
|
|
539
|
-
});
|
|
540
|
-
|
|
541
|
-
it("should dispatch the pending stack in the given order across passes", async () => {
|
|
542
|
-
const eventManager = new EventManager();
|
|
543
|
-
const processedIds: number[] = [];
|
|
544
|
-
|
|
545
|
-
// gen doubles as the urgency rank here: lower is more urgent.
|
|
546
|
-
eventManager.pushEvents(
|
|
547
|
-
makeEvent({
|
|
548
|
-
renderable_feature_added: [
|
|
549
|
-
makeRenderableFeatures(1, 3),
|
|
550
|
-
makeRenderableFeatures(1, 2),
|
|
551
|
-
],
|
|
552
|
-
renderable_feature_removed: [],
|
|
553
|
-
renderable_feature_changed: [],
|
|
554
|
-
}),
|
|
555
|
-
);
|
|
556
|
-
const byUrgency = (
|
|
557
|
-
a: RenderableFeatureAddedEvent,
|
|
558
|
-
b: RenderableFeatureAddedEvent,
|
|
559
|
-
) => a.gen - b.gen;
|
|
560
|
-
|
|
561
|
-
// Only one slot free: the more urgent of the two goes first even though it
|
|
562
|
-
// was pushed second.
|
|
563
|
-
let slots = 1;
|
|
564
|
-
await eventManager.forEachStackAsync(
|
|
565
|
-
"renderable_feature_added",
|
|
566
|
-
async (event) => {
|
|
567
|
-
processedIds.push(event.gen);
|
|
568
|
-
},
|
|
569
|
-
10,
|
|
570
|
-
() => slots-- > 0,
|
|
571
|
-
byUrgency,
|
|
572
|
-
);
|
|
573
|
-
expect(processedIds).toEqual([2]);
|
|
574
|
-
|
|
575
|
-
// A more urgent event arriving later overtakes the one left behind.
|
|
576
|
-
eventManager.pushEvents(
|
|
577
|
-
makeEvent({
|
|
578
|
-
renderable_feature_added: [makeRenderableFeatures(1, 1)],
|
|
579
|
-
renderable_feature_removed: [],
|
|
580
|
-
renderable_feature_changed: [],
|
|
581
|
-
}),
|
|
582
|
-
);
|
|
583
|
-
slots = 1;
|
|
584
|
-
await eventManager.forEachStackAsync(
|
|
585
|
-
"renderable_feature_added",
|
|
586
|
-
async (event) => {
|
|
587
|
-
processedIds.push(event.gen);
|
|
588
|
-
},
|
|
589
|
-
10,
|
|
590
|
-
() => slots-- > 0,
|
|
591
|
-
byUrgency,
|
|
592
|
-
);
|
|
593
|
-
expect(processedIds).toEqual([2, 1]);
|
|
594
|
-
expect(eventManager.stacks.renderable_feature_added).toHaveLength(1);
|
|
595
|
-
});
|
|
596
|
-
|
|
597
|
-
it("should merge newly pushed events into the already ordered stack", async () => {
|
|
598
|
-
const eventManager = new EventManager();
|
|
599
|
-
const processed: number[] = [];
|
|
600
|
-
const byUrgency = (
|
|
601
|
-
a: RenderableFeatureAddedEvent,
|
|
602
|
-
b: RenderableFeatureAddedEvent,
|
|
603
|
-
) => a.gen - b.gen;
|
|
604
|
-
const push = (...gens: number[]) =>
|
|
605
|
-
eventManager.pushEvents(
|
|
606
|
-
makeEvent({
|
|
607
|
-
renderable_feature_added: gens.map((g) => makeRenderableFeatures(1, g)),
|
|
608
|
-
renderable_feature_removed: [],
|
|
609
|
-
renderable_feature_changed: [],
|
|
610
|
-
}),
|
|
611
|
-
);
|
|
612
|
-
const pass = async (slots: number) => {
|
|
613
|
-
let left = slots;
|
|
614
|
-
await eventManager.forEachStackAsync(
|
|
615
|
-
"renderable_feature_added",
|
|
616
|
-
async (event) => {
|
|
617
|
-
processed.push(event.gen);
|
|
618
|
-
},
|
|
619
|
-
10,
|
|
620
|
-
() => left-- > 0,
|
|
621
|
-
byUrgency,
|
|
622
|
-
);
|
|
623
|
-
};
|
|
624
|
-
|
|
625
|
-
// Each pass leaves a sorted prefix behind; removals from it and later
|
|
626
|
-
// pushes after it must keep the dispatch order correct across passes.
|
|
627
|
-
push(5, 3, 8);
|
|
628
|
-
await pass(1);
|
|
629
|
-
push(4, 1);
|
|
630
|
-
await pass(2);
|
|
631
|
-
push(6, 2);
|
|
632
|
-
await pass(10);
|
|
633
|
-
expect(processed).toEqual([3, 1, 4, 2, 5, 6, 8]);
|
|
634
|
-
expect(eventManager.stacks.renderable_feature_added).toHaveLength(0);
|
|
635
|
-
});
|