@isograph/react-disposable-state 0.0.0-main-4ef7c123
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/README.md +144 -0
- package/dist/CacheItem.d.ts +54 -0
- package/dist/CacheItem.js +265 -0
- package/dist/ParentCache.d.ts +39 -0
- package/dist/ParentCache.js +86 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +24 -0
- package/dist/useCachedPrecommitValue.d.ts +36 -0
- package/dist/useCachedPrecommitValue.js +93 -0
- package/dist/useDisposableState.d.ts +9 -0
- package/dist/useDisposableState.js +68 -0
- package/dist/useHasCommittedRef.d.ts +5 -0
- package/dist/useHasCommittedRef.js +15 -0
- package/dist/useLazyDisposableState.d.ts +13 -0
- package/dist/useLazyDisposableState.js +39 -0
- package/dist/useUpdatableDisposableState.d.ts +39 -0
- package/dist/useUpdatableDisposableState.js +92 -0
- package/docs/managing-complex-state.md +151 -0
- package/package.json +28 -0
- package/src/CacheItem.test.ts +788 -0
- package/src/CacheItem.ts +364 -0
- package/src/ParentCache.test.ts +70 -0
- package/src/ParentCache.ts +100 -0
- package/src/index.ts +9 -0
- package/src/useCachedPrecommitValue.test.tsx +587 -0
- package/src/useCachedPrecommitValue.ts +104 -0
- package/src/useDisposableState.ts +92 -0
- package/src/useHasCommittedRef.ts +12 -0
- package/src/useLazyDisposableState.ts +48 -0
- package/src/useUpdatableDisposableState.test.tsx +482 -0
- package/src/useUpdatableDisposableState.ts +134 -0
- package/tsconfig.pkg.json +9 -0
@@ -0,0 +1,788 @@
|
|
1
|
+
import {
|
2
|
+
describe,
|
3
|
+
assert,
|
4
|
+
test,
|
5
|
+
vi,
|
6
|
+
beforeEach,
|
7
|
+
afterEach,
|
8
|
+
expect,
|
9
|
+
} from "vitest";
|
10
|
+
import {
|
11
|
+
CacheItem,
|
12
|
+
CacheItemState,
|
13
|
+
createTemporarilyRetainedCacheItem,
|
14
|
+
} from "./CacheItem";
|
15
|
+
import { ItemCleanupPair } from "@isograph/disposable-types";
|
16
|
+
|
17
|
+
function getState<T>(cacheItem: CacheItem<T>): CacheItemState<T> {
|
18
|
+
return (cacheItem as any).__state as CacheItemState<T>;
|
19
|
+
}
|
20
|
+
|
21
|
+
describe("CacheItem", () => {
|
22
|
+
beforeEach(() => {
|
23
|
+
vi.useFakeTimers();
|
24
|
+
});
|
25
|
+
afterEach(() => {
|
26
|
+
vi.useRealTimers();
|
27
|
+
});
|
28
|
+
|
29
|
+
describe("Item that is temporarily retained once", () => {
|
30
|
+
test("Temporarily retained cache item gets created in the expected state", () => {
|
31
|
+
const removeFromParentCache = vi.fn();
|
32
|
+
const disposeItem = vi.fn();
|
33
|
+
const factory = vi.fn(() => {
|
34
|
+
const ret: ItemCleanupPair<number> = [1, disposeItem];
|
35
|
+
return ret;
|
36
|
+
});
|
37
|
+
const [cacheItem, _disposeTemporaryRetain] =
|
38
|
+
createTemporarilyRetainedCacheItem<number>(
|
39
|
+
factory,
|
40
|
+
removeFromParentCache
|
41
|
+
);
|
42
|
+
|
43
|
+
expect(factory.mock.calls.length).toEqual(1);
|
44
|
+
|
45
|
+
const state = getState(cacheItem);
|
46
|
+
assert(state.kind === "InParentCacheAndNotDisposed");
|
47
|
+
expect(state.value).toEqual(1);
|
48
|
+
expect(state.permanentRetainCount).toEqual(0);
|
49
|
+
expect(state.temporaryRetainCount).toEqual(1);
|
50
|
+
expect(state.disposeValue).toEqual(disposeItem);
|
51
|
+
expect(state.removeFromParentCache).toEqual(removeFromParentCache);
|
52
|
+
expect(disposeItem.mock.calls.length).toEqual(0);
|
53
|
+
expect(removeFromParentCache.mock.calls.length).toEqual(0);
|
54
|
+
});
|
55
|
+
|
56
|
+
test("Disposal of temporarily retained cache item", () => {
|
57
|
+
const removeFromParentCache = vi.fn();
|
58
|
+
const disposeItem = vi.fn();
|
59
|
+
const factory = vi.fn(() => {
|
60
|
+
const ret: ItemCleanupPair<number> = [1, disposeItem];
|
61
|
+
return ret;
|
62
|
+
});
|
63
|
+
const [cacheItem, disposeTemporaryRetain] =
|
64
|
+
createTemporarilyRetainedCacheItem<number>(
|
65
|
+
factory,
|
66
|
+
removeFromParentCache
|
67
|
+
);
|
68
|
+
|
69
|
+
expect(factory.mock.calls.length).toEqual(1);
|
70
|
+
|
71
|
+
disposeTemporaryRetain();
|
72
|
+
|
73
|
+
const state = getState(cacheItem);
|
74
|
+
|
75
|
+
expect(state.kind).toEqual("NotInParentCacheAndDisposed");
|
76
|
+
expect(removeFromParentCache.mock.calls.length).toEqual(1);
|
77
|
+
expect(disposeItem.mock.calls.length).toEqual(1);
|
78
|
+
});
|
79
|
+
|
80
|
+
test("Expiration of temporarily retained cache item", () => {
|
81
|
+
const removeFromParentCache = vi.fn();
|
82
|
+
const disposeItem = vi.fn();
|
83
|
+
|
84
|
+
const factory = vi.fn(() => {
|
85
|
+
const ret: ItemCleanupPair<number> = [1, disposeItem];
|
86
|
+
return ret;
|
87
|
+
});
|
88
|
+
const [cacheItem, _disposeTemporaryRetain] =
|
89
|
+
createTemporarilyRetainedCacheItem<number>(
|
90
|
+
factory,
|
91
|
+
removeFromParentCache
|
92
|
+
);
|
93
|
+
|
94
|
+
expect(factory.mock.calls.length).toEqual(1);
|
95
|
+
|
96
|
+
vi.advanceTimersToNextTimer();
|
97
|
+
|
98
|
+
const state = getState(cacheItem);
|
99
|
+
|
100
|
+
assert(state.kind === "NotInParentCacheAndDisposed");
|
101
|
+
expect(removeFromParentCache.mock.calls.length).toEqual(1);
|
102
|
+
expect(disposeItem.mock.calls.length).toEqual(1);
|
103
|
+
});
|
104
|
+
|
105
|
+
test("Disposal of expired temporarily retained cache item", () => {
|
106
|
+
const removeFromParentCache = vi.fn();
|
107
|
+
const disposeItem = vi.fn();
|
108
|
+
|
109
|
+
const factory = vi.fn(() => {
|
110
|
+
const ret: ItemCleanupPair<number> = [1, disposeItem];
|
111
|
+
return ret;
|
112
|
+
});
|
113
|
+
const [cacheItem, disposeTemporaryRetain] =
|
114
|
+
createTemporarilyRetainedCacheItem<number>(
|
115
|
+
factory,
|
116
|
+
removeFromParentCache
|
117
|
+
);
|
118
|
+
|
119
|
+
expect(factory.mock.calls.length).toEqual(1);
|
120
|
+
|
121
|
+
vi.advanceTimersToNextTimer();
|
122
|
+
|
123
|
+
const state = getState(cacheItem);
|
124
|
+
|
125
|
+
assert(state.kind === "NotInParentCacheAndDisposed");
|
126
|
+
expect(removeFromParentCache.mock.calls.length).toEqual(1);
|
127
|
+
expect(disposeItem.mock.calls.length).toEqual(1);
|
128
|
+
|
129
|
+
expect(() => {
|
130
|
+
disposeTemporaryRetain();
|
131
|
+
}).not.toThrow();
|
132
|
+
});
|
133
|
+
|
134
|
+
test("Repeated disposal of temporarily retained cache item", () => {
|
135
|
+
const removeFromParentCache = vi.fn();
|
136
|
+
const disposeItem = vi.fn();
|
137
|
+
|
138
|
+
const factory = vi.fn(() => {
|
139
|
+
const ret: ItemCleanupPair<number> = [1, disposeItem];
|
140
|
+
return ret;
|
141
|
+
});
|
142
|
+
const [cacheItem, disposeTemporaryRetain] =
|
143
|
+
createTemporarilyRetainedCacheItem<number>(
|
144
|
+
factory,
|
145
|
+
removeFromParentCache
|
146
|
+
);
|
147
|
+
|
148
|
+
expect(factory.mock.calls.length).toEqual(1);
|
149
|
+
|
150
|
+
disposeTemporaryRetain();
|
151
|
+
const state = getState(cacheItem);
|
152
|
+
|
153
|
+
assert(state.kind === "NotInParentCacheAndDisposed");
|
154
|
+
expect(removeFromParentCache.mock.calls.length).toEqual(1);
|
155
|
+
expect(disposeItem.mock.calls.length).toEqual(1);
|
156
|
+
|
157
|
+
expect(() => {
|
158
|
+
disposeTemporaryRetain();
|
159
|
+
}).toThrow();
|
160
|
+
});
|
161
|
+
});
|
162
|
+
|
163
|
+
describe("Item that is temporarily retained once and then permanently retained", () => {
|
164
|
+
test("Permanent retain removes the item from the parent", () => {
|
165
|
+
const removeFromParentCache = vi.fn();
|
166
|
+
const disposeItem = vi.fn();
|
167
|
+
|
168
|
+
const factory = vi.fn(() => {
|
169
|
+
const ret: ItemCleanupPair<number> = [1, disposeItem];
|
170
|
+
return ret;
|
171
|
+
});
|
172
|
+
const [cacheItem, disposeTemporaryRetain] =
|
173
|
+
createTemporarilyRetainedCacheItem<number>(
|
174
|
+
factory,
|
175
|
+
removeFromParentCache
|
176
|
+
);
|
177
|
+
|
178
|
+
const mockedDisposeTemporaryRetain = vi.fn(disposeTemporaryRetain);
|
179
|
+
|
180
|
+
expect(factory.mock.calls.length).toEqual(1);
|
181
|
+
|
182
|
+
const [item, _disposePermanentRetain] =
|
183
|
+
cacheItem.permanentRetainIfNotDisposed(mockedDisposeTemporaryRetain)!;
|
184
|
+
|
185
|
+
expect(item).toEqual(1);
|
186
|
+
expect(mockedDisposeTemporaryRetain.mock.calls.length).toEqual(1);
|
187
|
+
|
188
|
+
const state = getState(cacheItem);
|
189
|
+
|
190
|
+
assert(state.kind === "NotInParentCacheAndNotDisposed");
|
191
|
+
expect(state.disposeValue).toEqual(disposeItem);
|
192
|
+
expect(state.permanentRetainCount).toEqual(1);
|
193
|
+
expect(state.value).toEqual(1);
|
194
|
+
expect(disposeItem.mock.calls.length).toEqual(0);
|
195
|
+
expect(removeFromParentCache.mock.calls.length).toEqual(1);
|
196
|
+
});
|
197
|
+
|
198
|
+
test("Disposing the temporary retain after permanent retain throws", () => {
|
199
|
+
const removeFromParentCache = vi.fn();
|
200
|
+
const disposeItem = vi.fn();
|
201
|
+
|
202
|
+
const factory = vi.fn(() => {
|
203
|
+
const ret: ItemCleanupPair<number> = [1, disposeItem];
|
204
|
+
return ret;
|
205
|
+
});
|
206
|
+
const [cacheItem, disposeTemporaryRetain] =
|
207
|
+
createTemporarilyRetainedCacheItem<number>(
|
208
|
+
factory,
|
209
|
+
removeFromParentCache
|
210
|
+
);
|
211
|
+
|
212
|
+
expect(factory.mock.calls.length).toEqual(1);
|
213
|
+
|
214
|
+
const [item, _disposePermanentRetain] =
|
215
|
+
cacheItem.permanentRetainIfNotDisposed(disposeTemporaryRetain)!;
|
216
|
+
|
217
|
+
expect(() => {
|
218
|
+
disposeTemporaryRetain();
|
219
|
+
}).toThrow();
|
220
|
+
});
|
221
|
+
|
222
|
+
test("Item is disposed when the permanently retain is disposed", () => {
|
223
|
+
const removeFromParentCache = vi.fn();
|
224
|
+
const disposeItem = vi.fn();
|
225
|
+
|
226
|
+
const factory = vi.fn(() => {
|
227
|
+
const ret: ItemCleanupPair<number> = [1, disposeItem];
|
228
|
+
return ret;
|
229
|
+
});
|
230
|
+
const [cacheItem, disposeTemporaryRetain] =
|
231
|
+
createTemporarilyRetainedCacheItem<number>(
|
232
|
+
factory,
|
233
|
+
removeFromParentCache
|
234
|
+
);
|
235
|
+
|
236
|
+
const mockedDisposeTemporaryRetain = vi.fn(disposeTemporaryRetain);
|
237
|
+
|
238
|
+
expect(factory.mock.calls.length).toEqual(1);
|
239
|
+
|
240
|
+
const [item, disposePermanentRetain] =
|
241
|
+
cacheItem.permanentRetainIfNotDisposed(mockedDisposeTemporaryRetain)!;
|
242
|
+
|
243
|
+
disposePermanentRetain();
|
244
|
+
|
245
|
+
const state = getState(cacheItem);
|
246
|
+
assert(state.kind === "NotInParentCacheAndDisposed");
|
247
|
+
expect(removeFromParentCache.mock.calls.length).toEqual(1);
|
248
|
+
expect(disposeItem.mock.calls.length).toEqual(1);
|
249
|
+
});
|
250
|
+
|
251
|
+
test("Repeated disposal of permanently retained cache item throws", () => {
|
252
|
+
const removeFromParentCache = vi.fn();
|
253
|
+
const disposeItem = vi.fn();
|
254
|
+
|
255
|
+
const factory = vi.fn(() => {
|
256
|
+
const ret: ItemCleanupPair<number> = [1, disposeItem];
|
257
|
+
return ret;
|
258
|
+
});
|
259
|
+
const [cacheItem, disposeTemporaryRetain] =
|
260
|
+
createTemporarilyRetainedCacheItem<number>(
|
261
|
+
factory,
|
262
|
+
removeFromParentCache
|
263
|
+
);
|
264
|
+
|
265
|
+
const mockedDisposeTemporaryRetain = vi.fn(disposeTemporaryRetain);
|
266
|
+
|
267
|
+
expect(factory.mock.calls.length).toEqual(1);
|
268
|
+
|
269
|
+
const [item, disposePermanentRetain] =
|
270
|
+
cacheItem.permanentRetainIfNotDisposed(mockedDisposeTemporaryRetain)!;
|
271
|
+
|
272
|
+
disposePermanentRetain();
|
273
|
+
expect(() => {
|
274
|
+
disposePermanentRetain();
|
275
|
+
}).toThrow();
|
276
|
+
});
|
277
|
+
|
278
|
+
test("Permanent retain of cache item whose temporary retain has expired", () => {
|
279
|
+
const removeFromParentCache = vi.fn();
|
280
|
+
const disposeItem = vi.fn();
|
281
|
+
|
282
|
+
const factory = vi.fn(() => {
|
283
|
+
const ret: ItemCleanupPair<number> = [1, disposeItem];
|
284
|
+
return ret;
|
285
|
+
});
|
286
|
+
const [cacheItem, disposeTemporaryRetain] =
|
287
|
+
createTemporarilyRetainedCacheItem<number>(
|
288
|
+
factory,
|
289
|
+
removeFromParentCache
|
290
|
+
);
|
291
|
+
|
292
|
+
expect(factory.mock.calls.length).toEqual(1);
|
293
|
+
|
294
|
+
vi.advanceTimersToNextTimer();
|
295
|
+
|
296
|
+
const state = getState(cacheItem);
|
297
|
+
|
298
|
+
assert(state.kind === "NotInParentCacheAndDisposed");
|
299
|
+
|
300
|
+
assert(
|
301
|
+
cacheItem.permanentRetainIfNotDisposed(disposeTemporaryRetain) === null
|
302
|
+
);
|
303
|
+
|
304
|
+
expect(() => {
|
305
|
+
cacheItem.permanentRetain();
|
306
|
+
}).toThrow();
|
307
|
+
});
|
308
|
+
|
309
|
+
test("It is invalid to temporarily retain after the permanent retain", () => {
|
310
|
+
const removeFromParentCache = vi.fn();
|
311
|
+
const disposeItem = vi.fn();
|
312
|
+
|
313
|
+
const factory = vi.fn(() => {
|
314
|
+
const ret: ItemCleanupPair<number> = [1, disposeItem];
|
315
|
+
return ret;
|
316
|
+
});
|
317
|
+
const [cacheItem, disposeTemporaryRetain1] =
|
318
|
+
createTemporarilyRetainedCacheItem<number>(
|
319
|
+
factory,
|
320
|
+
removeFromParentCache
|
321
|
+
);
|
322
|
+
|
323
|
+
const [_value, disposeOfPermanentRetain1] =
|
324
|
+
cacheItem.permanentRetainIfNotDisposed(disposeTemporaryRetain1)!;
|
325
|
+
|
326
|
+
expect(() => {
|
327
|
+
cacheItem.temporaryRetain();
|
328
|
+
}).toThrow();
|
329
|
+
});
|
330
|
+
});
|
331
|
+
|
332
|
+
describe("Item that temporarily retained twice", () => {
|
333
|
+
test("Cache item is not disposed after first temporary retain expires", () => {
|
334
|
+
const removeFromParentCache = vi.fn();
|
335
|
+
const disposeItem = vi.fn();
|
336
|
+
|
337
|
+
const factory = vi.fn(() => {
|
338
|
+
const ret: ItemCleanupPair<number> = [1, disposeItem];
|
339
|
+
return ret;
|
340
|
+
});
|
341
|
+
const [cacheItem, _disposeTemporaryRetain1] =
|
342
|
+
createTemporarilyRetainedCacheItem<number>(
|
343
|
+
factory,
|
344
|
+
removeFromParentCache
|
345
|
+
);
|
346
|
+
|
347
|
+
vi.advanceTimersByTime(1000);
|
348
|
+
|
349
|
+
const _disposeTemporaryRetain2 = cacheItem.temporaryRetain();
|
350
|
+
|
351
|
+
vi.advanceTimersToNextTimer();
|
352
|
+
|
353
|
+
const state = getState(cacheItem);
|
354
|
+
assert(state.kind === "InParentCacheAndNotDisposed");
|
355
|
+
expect(state.value).toEqual(1);
|
356
|
+
expect(state.permanentRetainCount).toEqual(0);
|
357
|
+
expect(state.temporaryRetainCount).toEqual(1);
|
358
|
+
expect(state.disposeValue).toEqual(disposeItem);
|
359
|
+
expect(state.removeFromParentCache).toEqual(removeFromParentCache);
|
360
|
+
expect(disposeItem.mock.calls.length).toEqual(0);
|
361
|
+
expect(removeFromParentCache.mock.calls.length).toEqual(0);
|
362
|
+
});
|
363
|
+
|
364
|
+
test("Cache item is not disposed after first temporary retain is disposed", () => {
|
365
|
+
const removeFromParentCache = vi.fn();
|
366
|
+
const disposeItem = vi.fn();
|
367
|
+
|
368
|
+
const factory = vi.fn(() => {
|
369
|
+
const ret: ItemCleanupPair<number> = [1, disposeItem];
|
370
|
+
return ret;
|
371
|
+
});
|
372
|
+
const [cacheItem, disposeTemporaryRetain1] =
|
373
|
+
createTemporarilyRetainedCacheItem<number>(
|
374
|
+
factory,
|
375
|
+
removeFromParentCache
|
376
|
+
);
|
377
|
+
|
378
|
+
vi.advanceTimersByTime(1000);
|
379
|
+
const _disposeTemporaryRetain2 = cacheItem.temporaryRetain();
|
380
|
+
|
381
|
+
disposeTemporaryRetain1();
|
382
|
+
|
383
|
+
const state = getState(cacheItem);
|
384
|
+
assert(state.kind === "InParentCacheAndNotDisposed");
|
385
|
+
expect(state.value).toEqual(1);
|
386
|
+
expect(state.permanentRetainCount).toEqual(0);
|
387
|
+
expect(state.temporaryRetainCount).toEqual(1);
|
388
|
+
expect(state.disposeValue).toEqual(disposeItem);
|
389
|
+
expect(state.removeFromParentCache).toEqual(removeFromParentCache);
|
390
|
+
expect(disposeItem.mock.calls.length).toEqual(0);
|
391
|
+
expect(removeFromParentCache.mock.calls.length).toEqual(0);
|
392
|
+
});
|
393
|
+
|
394
|
+
test("Disposing the first temporary retain after it expires is a no-op", () => {
|
395
|
+
const removeFromParentCache = vi.fn();
|
396
|
+
const disposeItem = vi.fn();
|
397
|
+
|
398
|
+
const factory = vi.fn(() => {
|
399
|
+
const ret: ItemCleanupPair<number> = [1, disposeItem];
|
400
|
+
return ret;
|
401
|
+
});
|
402
|
+
const [cacheItem, disposeTemporaryRetain1] =
|
403
|
+
createTemporarilyRetainedCacheItem<number>(
|
404
|
+
factory,
|
405
|
+
removeFromParentCache
|
406
|
+
);
|
407
|
+
|
408
|
+
vi.advanceTimersByTime(1000);
|
409
|
+
const _disposeTemporaryRetain2 = cacheItem.temporaryRetain();
|
410
|
+
vi.advanceTimersToNextTimer();
|
411
|
+
disposeTemporaryRetain1();
|
412
|
+
|
413
|
+
const state = getState(cacheItem);
|
414
|
+
assert(state.kind === "InParentCacheAndNotDisposed");
|
415
|
+
expect(state.temporaryRetainCount).toEqual(1);
|
416
|
+
});
|
417
|
+
|
418
|
+
test("Item is not disposed if only the second temporary retain is disposed", () => {
|
419
|
+
const removeFromParentCache = vi.fn();
|
420
|
+
const disposeItem = vi.fn();
|
421
|
+
|
422
|
+
const factory = vi.fn(() => {
|
423
|
+
const ret: ItemCleanupPair<number> = [1, disposeItem];
|
424
|
+
return ret;
|
425
|
+
});
|
426
|
+
const [cacheItem, disposeTemporaryRetain1] =
|
427
|
+
createTemporarilyRetainedCacheItem<number>(
|
428
|
+
factory,
|
429
|
+
removeFromParentCache
|
430
|
+
);
|
431
|
+
|
432
|
+
vi.advanceTimersByTime(1000);
|
433
|
+
const disposeTemporaryRetain2 = cacheItem.temporaryRetain();
|
434
|
+
disposeTemporaryRetain2();
|
435
|
+
|
436
|
+
const state = getState(cacheItem);
|
437
|
+
assert(state.kind === "InParentCacheAndNotDisposed");
|
438
|
+
expect(state.temporaryRetainCount).toEqual(1);
|
439
|
+
});
|
440
|
+
|
441
|
+
test("Item is disposed if both temporary retains are disposed", () => {
|
442
|
+
const removeFromParentCache = vi.fn();
|
443
|
+
const disposeItem = vi.fn();
|
444
|
+
|
445
|
+
const factory = vi.fn(() => {
|
446
|
+
const ret: ItemCleanupPair<number> = [1, disposeItem];
|
447
|
+
return ret;
|
448
|
+
});
|
449
|
+
const [cacheItem, disposeTemporaryRetain1] =
|
450
|
+
createTemporarilyRetainedCacheItem<number>(
|
451
|
+
factory,
|
452
|
+
removeFromParentCache
|
453
|
+
);
|
454
|
+
|
455
|
+
const disposeTemporaryRetain2 = cacheItem.temporaryRetain();
|
456
|
+
const state1 = getState(cacheItem);
|
457
|
+
assert(state1.kind === "InParentCacheAndNotDisposed");
|
458
|
+
expect(state1.temporaryRetainCount).toEqual(2);
|
459
|
+
disposeTemporaryRetain1();
|
460
|
+
expect(state1.temporaryRetainCount).toEqual(1);
|
461
|
+
disposeTemporaryRetain2();
|
462
|
+
|
463
|
+
const state2 = getState(cacheItem);
|
464
|
+
assert(state2.kind === "NotInParentCacheAndDisposed");
|
465
|
+
});
|
466
|
+
|
467
|
+
test("Item is disposed if both temporary retains are disposed in reverse order", () => {
|
468
|
+
const removeFromParentCache = vi.fn();
|
469
|
+
const disposeItem = vi.fn();
|
470
|
+
|
471
|
+
const factory = vi.fn(() => {
|
472
|
+
const ret: ItemCleanupPair<number> = [1, disposeItem];
|
473
|
+
return ret;
|
474
|
+
});
|
475
|
+
const [cacheItem, disposeTemporaryRetain1] =
|
476
|
+
createTemporarilyRetainedCacheItem<number>(
|
477
|
+
factory,
|
478
|
+
removeFromParentCache
|
479
|
+
);
|
480
|
+
|
481
|
+
const disposeTemporaryRetain2 = cacheItem.temporaryRetain();
|
482
|
+
const state1 = getState(cacheItem);
|
483
|
+
assert(state1.kind === "InParentCacheAndNotDisposed");
|
484
|
+
expect(state1.temporaryRetainCount).toEqual(2);
|
485
|
+
disposeTemporaryRetain2();
|
486
|
+
expect(state1.temporaryRetainCount).toEqual(1);
|
487
|
+
disposeTemporaryRetain1();
|
488
|
+
|
489
|
+
const state2 = getState(cacheItem);
|
490
|
+
assert(state2.kind === "NotInParentCacheAndDisposed");
|
491
|
+
});
|
492
|
+
|
493
|
+
test("Item is disposed if both temporary retains expire", () => {
|
494
|
+
const removeFromParentCache = vi.fn();
|
495
|
+
const disposeItem = vi.fn();
|
496
|
+
|
497
|
+
const factory = vi.fn(() => {
|
498
|
+
const ret: ItemCleanupPair<number> = [1, disposeItem];
|
499
|
+
return ret;
|
500
|
+
});
|
501
|
+
const [cacheItem, _disposeTemporaryRetain1] =
|
502
|
+
createTemporarilyRetainedCacheItem<number>(
|
503
|
+
factory,
|
504
|
+
removeFromParentCache
|
505
|
+
);
|
506
|
+
|
507
|
+
vi.advanceTimersByTime(1000);
|
508
|
+
const _disposeTemporaryRetain2 = cacheItem.temporaryRetain();
|
509
|
+
|
510
|
+
const state1 = getState(cacheItem);
|
511
|
+
assert(state1.kind === "InParentCacheAndNotDisposed");
|
512
|
+
expect(state1.temporaryRetainCount).toEqual(2);
|
513
|
+
|
514
|
+
vi.advanceTimersToNextTimer();
|
515
|
+
expect(state1.temporaryRetainCount).toEqual(1);
|
516
|
+
|
517
|
+
vi.advanceTimersToNextTimer();
|
518
|
+
const state = getState(cacheItem);
|
519
|
+
assert(state.kind === "NotInParentCacheAndDisposed");
|
520
|
+
expect(disposeItem.mock.calls.length).toEqual(1);
|
521
|
+
expect(removeFromParentCache.mock.calls.length).toEqual(1);
|
522
|
+
});
|
523
|
+
});
|
524
|
+
|
525
|
+
describe("Item that is temporarily retained twice and then permanently retained", () => {
|
526
|
+
test("Item is not removed from the parent cache when the permanent retain is created", () => {
|
527
|
+
const removeFromParentCache = vi.fn();
|
528
|
+
const disposeItem = vi.fn();
|
529
|
+
|
530
|
+
const factory = vi.fn(() => {
|
531
|
+
const ret: ItemCleanupPair<number> = [1, disposeItem];
|
532
|
+
return ret;
|
533
|
+
});
|
534
|
+
const [cacheItem, disposeTemporaryRetain1] =
|
535
|
+
createTemporarilyRetainedCacheItem<number>(
|
536
|
+
factory,
|
537
|
+
removeFromParentCache
|
538
|
+
);
|
539
|
+
|
540
|
+
vi.advanceTimersByTime(1000);
|
541
|
+
const _disposeTemporaryRetain2 = cacheItem.temporaryRetain();
|
542
|
+
|
543
|
+
const [_value, _disposeOfPermanentRetain] =
|
544
|
+
cacheItem.permanentRetainIfNotDisposed(disposeTemporaryRetain1)!;
|
545
|
+
|
546
|
+
const state = getState(cacheItem);
|
547
|
+
|
548
|
+
assert(state.kind === "InParentCacheAndNotDisposed");
|
549
|
+
expect(state.value).toEqual(1);
|
550
|
+
expect(state.permanentRetainCount).toEqual(1);
|
551
|
+
expect(state.temporaryRetainCount).toEqual(1);
|
552
|
+
});
|
553
|
+
|
554
|
+
test("Item is removed from the parent cache when the second temporary retain is disposed", () => {
|
555
|
+
const removeFromParentCache = vi.fn();
|
556
|
+
const disposeItem = vi.fn();
|
557
|
+
|
558
|
+
const factory = vi.fn(() => {
|
559
|
+
const ret: ItemCleanupPair<number> = [1, disposeItem];
|
560
|
+
return ret;
|
561
|
+
});
|
562
|
+
const [cacheItem, disposeTemporaryRetain1] =
|
563
|
+
createTemporarilyRetainedCacheItem<number>(
|
564
|
+
factory,
|
565
|
+
removeFromParentCache
|
566
|
+
);
|
567
|
+
|
568
|
+
vi.advanceTimersByTime(1000);
|
569
|
+
const disposeTemporaryRetain2 = cacheItem.temporaryRetain();
|
570
|
+
|
571
|
+
const [_value, _disposeOfPermanentRetain] =
|
572
|
+
cacheItem.permanentRetainIfNotDisposed(disposeTemporaryRetain1)!;
|
573
|
+
|
574
|
+
const state1 = getState(cacheItem);
|
575
|
+
assert(state1.kind === "InParentCacheAndNotDisposed");
|
576
|
+
expect(state1.permanentRetainCount).toEqual(1);
|
577
|
+
expect(state1.temporaryRetainCount).toEqual(1);
|
578
|
+
|
579
|
+
disposeTemporaryRetain2();
|
580
|
+
|
581
|
+
const state2 = getState(cacheItem);
|
582
|
+
assert(state2.kind === "NotInParentCacheAndNotDisposed");
|
583
|
+
expect(state2.permanentRetainCount).toEqual(1);
|
584
|
+
});
|
585
|
+
|
586
|
+
test("Item is removed from the parent cache when the second temporary retain expires", () => {
|
587
|
+
const removeFromParentCache = vi.fn();
|
588
|
+
const disposeItem = vi.fn();
|
589
|
+
|
590
|
+
const factory = vi.fn(() => {
|
591
|
+
const ret: ItemCleanupPair<number> = [1, disposeItem];
|
592
|
+
return ret;
|
593
|
+
});
|
594
|
+
const [cacheItem, disposeTemporaryRetain1] =
|
595
|
+
createTemporarilyRetainedCacheItem<number>(
|
596
|
+
factory,
|
597
|
+
removeFromParentCache
|
598
|
+
);
|
599
|
+
|
600
|
+
vi.advanceTimersByTime(1000);
|
601
|
+
const _disposeTemporaryRetain2 = cacheItem.temporaryRetain();
|
602
|
+
|
603
|
+
const [_value, _disposeOfPermanentRetain] =
|
604
|
+
cacheItem.permanentRetainIfNotDisposed(disposeTemporaryRetain1)!;
|
605
|
+
|
606
|
+
const state1 = getState(cacheItem);
|
607
|
+
assert(state1.kind === "InParentCacheAndNotDisposed");
|
608
|
+
expect(state1.permanentRetainCount).toEqual(1);
|
609
|
+
expect(state1.temporaryRetainCount).toEqual(1);
|
610
|
+
|
611
|
+
vi.advanceTimersToNextTimer();
|
612
|
+
|
613
|
+
const state2 = getState(cacheItem);
|
614
|
+
assert(state2.kind === "NotInParentCacheAndNotDisposed");
|
615
|
+
expect(state2.permanentRetainCount).toEqual(1);
|
616
|
+
});
|
617
|
+
|
618
|
+
test("Item is not removed from the parent cache when the permanent retain is disposed", () => {
|
619
|
+
const removeFromParentCache = vi.fn();
|
620
|
+
const disposeItem = vi.fn();
|
621
|
+
|
622
|
+
const factory = vi.fn(() => {
|
623
|
+
const ret: ItemCleanupPair<number> = [1, disposeItem];
|
624
|
+
return ret;
|
625
|
+
});
|
626
|
+
const [cacheItem, disposeTemporaryRetain1] =
|
627
|
+
createTemporarilyRetainedCacheItem<number>(
|
628
|
+
factory,
|
629
|
+
removeFromParentCache
|
630
|
+
);
|
631
|
+
|
632
|
+
vi.advanceTimersByTime(1000);
|
633
|
+
const _disposeTemporaryRetain2 = cacheItem.temporaryRetain();
|
634
|
+
|
635
|
+
const [_value, disposeOfPermanentRetain] =
|
636
|
+
cacheItem.permanentRetainIfNotDisposed(disposeTemporaryRetain1)!;
|
637
|
+
|
638
|
+
disposeOfPermanentRetain();
|
639
|
+
|
640
|
+
const state = getState(cacheItem);
|
641
|
+
assert(state.kind === "InParentCacheAndNotDisposed");
|
642
|
+
expect(state.permanentRetainCount).toEqual(0);
|
643
|
+
expect(state.temporaryRetainCount).toEqual(1);
|
644
|
+
});
|
645
|
+
|
646
|
+
test("Item is disposed the permanent retain is disposed and the second temporary retain expires", () => {
|
647
|
+
const removeFromParentCache = vi.fn();
|
648
|
+
const disposeItem = vi.fn();
|
649
|
+
|
650
|
+
const factory = vi.fn(() => {
|
651
|
+
const ret: ItemCleanupPair<number> = [1, disposeItem];
|
652
|
+
return ret;
|
653
|
+
});
|
654
|
+
const [cacheItem, disposeTemporaryRetain1] =
|
655
|
+
createTemporarilyRetainedCacheItem<number>(
|
656
|
+
factory,
|
657
|
+
removeFromParentCache
|
658
|
+
);
|
659
|
+
|
660
|
+
vi.advanceTimersByTime(1000);
|
661
|
+
const _disposeTemporaryRetain2 = cacheItem.temporaryRetain();
|
662
|
+
|
663
|
+
const [_value, disposeOfPermanentRetain] =
|
664
|
+
cacheItem.permanentRetainIfNotDisposed(disposeTemporaryRetain1)!;
|
665
|
+
|
666
|
+
disposeOfPermanentRetain();
|
667
|
+
vi.advanceTimersToNextTimer();
|
668
|
+
|
669
|
+
const state = getState(cacheItem);
|
670
|
+
assert(state.kind === "NotInParentCacheAndDisposed");
|
671
|
+
});
|
672
|
+
|
673
|
+
test("Item is disposed when the permanent retain is disposed and the second temporary retain is disposed", () => {
|
674
|
+
const removeFromParentCache = vi.fn();
|
675
|
+
const disposeItem = vi.fn();
|
676
|
+
|
677
|
+
const factory = vi.fn(() => {
|
678
|
+
const ret: ItemCleanupPair<number> = [1, disposeItem];
|
679
|
+
return ret;
|
680
|
+
});
|
681
|
+
const [cacheItem, disposeTemporaryRetain1] =
|
682
|
+
createTemporarilyRetainedCacheItem<number>(
|
683
|
+
factory,
|
684
|
+
removeFromParentCache
|
685
|
+
);
|
686
|
+
|
687
|
+
vi.advanceTimersByTime(1000);
|
688
|
+
const disposeTemporaryRetain2 = cacheItem.temporaryRetain();
|
689
|
+
|
690
|
+
const [_value, disposeOfPermanentRetain] =
|
691
|
+
cacheItem.permanentRetainIfNotDisposed(disposeTemporaryRetain1)!;
|
692
|
+
|
693
|
+
disposeOfPermanentRetain();
|
694
|
+
disposeTemporaryRetain2();
|
695
|
+
|
696
|
+
const state = getState(cacheItem);
|
697
|
+
assert(state.kind === "NotInParentCacheAndDisposed");
|
698
|
+
});
|
699
|
+
|
700
|
+
test("Item is disposed when the second temporary is disposed and the permanent retain is disposed", () => {
|
701
|
+
const removeFromParentCache = vi.fn();
|
702
|
+
const disposeItem = vi.fn();
|
703
|
+
|
704
|
+
const factory = vi.fn(() => {
|
705
|
+
const ret: ItemCleanupPair<number> = [1, disposeItem];
|
706
|
+
return ret;
|
707
|
+
});
|
708
|
+
const [cacheItem, disposeTemporaryRetain1] =
|
709
|
+
createTemporarilyRetainedCacheItem<number>(
|
710
|
+
factory,
|
711
|
+
removeFromParentCache
|
712
|
+
);
|
713
|
+
|
714
|
+
vi.advanceTimersByTime(1000);
|
715
|
+
const disposeTemporaryRetain2 = cacheItem.temporaryRetain();
|
716
|
+
|
717
|
+
const [_value, disposeOfPermanentRetain] =
|
718
|
+
cacheItem.permanentRetainIfNotDisposed(disposeTemporaryRetain1)!;
|
719
|
+
|
720
|
+
disposeTemporaryRetain2();
|
721
|
+
disposeOfPermanentRetain();
|
722
|
+
|
723
|
+
const state = getState(cacheItem);
|
724
|
+
assert(state.kind === "NotInParentCacheAndDisposed");
|
725
|
+
});
|
726
|
+
|
727
|
+
test("Item is disposed when the second temporary expires and the permanent retain is disposed", () => {
|
728
|
+
const removeFromParentCache = vi.fn();
|
729
|
+
const disposeItem = vi.fn();
|
730
|
+
|
731
|
+
const factory = vi.fn(() => {
|
732
|
+
const ret: ItemCleanupPair<number> = [1, disposeItem];
|
733
|
+
return ret;
|
734
|
+
});
|
735
|
+
const [cacheItem, disposeTemporaryRetain1] =
|
736
|
+
createTemporarilyRetainedCacheItem<number>(
|
737
|
+
factory,
|
738
|
+
removeFromParentCache
|
739
|
+
);
|
740
|
+
|
741
|
+
vi.advanceTimersByTime(1000);
|
742
|
+
const _disposeTemporaryRetain2 = cacheItem.temporaryRetain();
|
743
|
+
|
744
|
+
const [_value, disposeOfPermanentRetain] =
|
745
|
+
cacheItem.permanentRetainIfNotDisposed(disposeTemporaryRetain1)!;
|
746
|
+
|
747
|
+
vi.advanceTimersToNextTimer();
|
748
|
+
disposeOfPermanentRetain();
|
749
|
+
|
750
|
+
const state = getState(cacheItem);
|
751
|
+
assert(state.kind === "NotInParentCacheAndDisposed");
|
752
|
+
});
|
753
|
+
});
|
754
|
+
|
755
|
+
describe("Multiple permanent retains", () => {
|
756
|
+
test("Item is only disposed when second permanent retain is disposed", () => {
|
757
|
+
const removeFromParentCache = vi.fn();
|
758
|
+
const disposeItem = vi.fn();
|
759
|
+
|
760
|
+
const factory = vi.fn(() => {
|
761
|
+
const ret: ItemCleanupPair<number> = [1, disposeItem];
|
762
|
+
return ret;
|
763
|
+
});
|
764
|
+
const [cacheItem, disposeTemporaryRetain1] =
|
765
|
+
createTemporarilyRetainedCacheItem<number>(
|
766
|
+
factory,
|
767
|
+
removeFromParentCache
|
768
|
+
);
|
769
|
+
|
770
|
+
vi.advanceTimersByTime(1000);
|
771
|
+
const disposeTemporaryRetain2 = cacheItem.temporaryRetain();
|
772
|
+
|
773
|
+
const [_value, disposeOfPermanentRetain1] =
|
774
|
+
cacheItem.permanentRetainIfNotDisposed(disposeTemporaryRetain1)!;
|
775
|
+
const [_value2, disposeOfPermanentRetain2] =
|
776
|
+
cacheItem.permanentRetainIfNotDisposed(disposeTemporaryRetain2)!;
|
777
|
+
|
778
|
+
disposeOfPermanentRetain1();
|
779
|
+
const state = getState(cacheItem);
|
780
|
+
assert(state.kind === "NotInParentCacheAndNotDisposed");
|
781
|
+
expect(state.permanentRetainCount).toEqual(1);
|
782
|
+
|
783
|
+
disposeOfPermanentRetain2();
|
784
|
+
const state2 = getState(cacheItem);
|
785
|
+
assert(state2.kind === "NotInParentCacheAndDisposed");
|
786
|
+
});
|
787
|
+
});
|
788
|
+
});
|