@simplysm/core-common 13.0.0-beta.2 → 13.0.0-beta.21
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/common.types.js +4 -4
- package/dist/errors/argument-error.js +1 -1
- package/dist/errors/not-implemented-error.js +1 -1
- package/dist/errors/timeout-error.js +1 -1
- package/dist/extensions/arr-ext.helpers.js +4 -4
- package/dist/extensions/arr-ext.js +9 -9
- package/dist/features/debounce-queue.js +2 -2
- package/dist/features/serial-queue.js +3 -3
- package/dist/index.js +30 -30
- package/dist/types/date-only.js +2 -2
- package/dist/types/date-time.js +2 -2
- package/dist/types/time.js +2 -2
- package/dist/types/uuid.js +1 -1
- package/dist/utils/bytes.js +1 -1
- package/dist/utils/json.js +8 -8
- package/dist/utils/obj.js +5 -5
- package/dist/utils/primitive.js +5 -5
- package/dist/utils/transferable.js +4 -4
- package/dist/utils/wait.js +1 -1
- package/package.json +7 -4
- package/.cache/typecheck-browser.tsbuildinfo +0 -1
- package/.cache/typecheck-node.tsbuildinfo +0 -1
- package/.cache/typecheck-tests-browser.tsbuildinfo +0 -1
- package/.cache/typecheck-tests-node.tsbuildinfo +0 -1
- package/src/common.types.ts +0 -91
- package/src/env.ts +0 -11
- package/src/errors/argument-error.ts +0 -40
- package/src/errors/not-implemented-error.ts +0 -32
- package/src/errors/sd-error.ts +0 -53
- package/src/errors/timeout-error.ts +0 -36
- package/src/extensions/arr-ext.helpers.ts +0 -53
- package/src/extensions/arr-ext.ts +0 -777
- package/src/extensions/arr-ext.types.ts +0 -258
- package/src/extensions/map-ext.ts +0 -86
- package/src/extensions/set-ext.ts +0 -68
- package/src/features/debounce-queue.ts +0 -116
- package/src/features/event-emitter.ts +0 -112
- package/src/features/serial-queue.ts +0 -94
- package/src/globals.ts +0 -12
- package/src/index.ts +0 -55
- package/src/types/date-only.ts +0 -329
- package/src/types/date-time.ts +0 -294
- package/src/types/lazy-gc-map.ts +0 -244
- package/src/types/time.ts +0 -210
- package/src/types/uuid.ts +0 -113
- package/src/utils/bytes.ts +0 -160
- package/src/utils/date-format.ts +0 -239
- package/src/utils/json.ts +0 -230
- package/src/utils/num.ts +0 -97
- package/src/utils/obj.ts +0 -956
- package/src/utils/path.ts +0 -40
- package/src/utils/primitive.ts +0 -33
- package/src/utils/str.ts +0 -252
- package/src/utils/template-strings.ts +0 -132
- package/src/utils/transferable.ts +0 -269
- package/src/utils/wait.ts +0 -40
- package/src/utils/xml.ts +0 -105
- package/src/zip/sd-zip.ts +0 -218
- package/tests/errors/errors.spec.ts +0 -196
- package/tests/extensions/array-extension.spec.ts +0 -790
- package/tests/extensions/map-extension.spec.ts +0 -147
- package/tests/extensions/set-extension.spec.ts +0 -74
- package/tests/types/date-only.spec.ts +0 -636
- package/tests/types/date-time.spec.ts +0 -391
- package/tests/types/lazy-gc-map.spec.ts +0 -692
- package/tests/types/time.spec.ts +0 -559
- package/tests/types/types.spec.ts +0 -55
- package/tests/types/uuid.spec.ts +0 -91
- package/tests/utils/bytes-utils.spec.ts +0 -230
- package/tests/utils/date-format.spec.ts +0 -371
- package/tests/utils/debounce-queue.spec.ts +0 -272
- package/tests/utils/json.spec.ts +0 -475
- package/tests/utils/number.spec.ts +0 -184
- package/tests/utils/object.spec.ts +0 -827
- package/tests/utils/path.spec.ts +0 -78
- package/tests/utils/primitive.spec.ts +0 -55
- package/tests/utils/sd-event-emitter.spec.ts +0 -216
- package/tests/utils/serial-queue.spec.ts +0 -365
- package/tests/utils/string.spec.ts +0 -294
- package/tests/utils/template-strings.spec.ts +0 -96
- package/tests/utils/transferable.spec.ts +0 -698
- package/tests/utils/wait.spec.ts +0 -145
- package/tests/utils/xml.spec.ts +0 -146
- package/tests/zip/sd-zip.spec.ts +0 -234
|
@@ -1,692 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
|
2
|
-
import { LazyGcMap } from "@simplysm/core-common";
|
|
3
|
-
|
|
4
|
-
describe("LazyGcMap", () => {
|
|
5
|
-
beforeEach(() => {
|
|
6
|
-
vi.useFakeTimers();
|
|
7
|
-
});
|
|
8
|
-
|
|
9
|
-
afterEach(() => {
|
|
10
|
-
vi.useRealTimers();
|
|
11
|
-
});
|
|
12
|
-
|
|
13
|
-
//#region 기본 Map 동작
|
|
14
|
-
|
|
15
|
-
describe("기본 Map 동작", () => {
|
|
16
|
-
it("set/get으로 값을 저장하고 가져온다", () => {
|
|
17
|
-
const map = new LazyGcMap<string, number>({
|
|
18
|
-
gcInterval: 1000,
|
|
19
|
-
expireTime: 5000,
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
map.set("key1", 100);
|
|
23
|
-
expect(map.get("key1")).toBe(100);
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
it("has로 키 존재 여부를 확인한다", () => {
|
|
27
|
-
const map = new LazyGcMap<string, number>({
|
|
28
|
-
gcInterval: 1000,
|
|
29
|
-
expireTime: 5000,
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
map.set("key1", 100);
|
|
33
|
-
expect(map.has("key1")).toBe(true);
|
|
34
|
-
expect(map.has("key2")).toBe(false);
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
it("delete로 값을 삭제한다", () => {
|
|
38
|
-
const map = new LazyGcMap<string, number>({
|
|
39
|
-
gcInterval: 1000,
|
|
40
|
-
expireTime: 5000,
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
map.set("key1", 100);
|
|
44
|
-
expect(map.delete("key1")).toBe(true);
|
|
45
|
-
expect(map.has("key1")).toBe(false);
|
|
46
|
-
expect(map.delete("key1")).toBe(false); // 이미 삭제됨
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
it("dispose로 모든 값을 삭제한다", () => {
|
|
50
|
-
const map = new LazyGcMap<string, number>({
|
|
51
|
-
gcInterval: 1000,
|
|
52
|
-
expireTime: 5000,
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
map.set("key1", 100);
|
|
56
|
-
map.set("key2", 200);
|
|
57
|
-
expect(map.size).toBe(2);
|
|
58
|
-
|
|
59
|
-
map.dispose();
|
|
60
|
-
expect(map.size).toBe(0);
|
|
61
|
-
expect(map.has("key1")).toBe(false);
|
|
62
|
-
expect(map.has("key2")).toBe(false);
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
it("size로 크기를 확인한다", () => {
|
|
66
|
-
const map = new LazyGcMap<string, number>({
|
|
67
|
-
gcInterval: 1000,
|
|
68
|
-
expireTime: 5000,
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
expect(map.size).toBe(0);
|
|
72
|
-
map.set("key1", 100);
|
|
73
|
-
expect(map.size).toBe(1);
|
|
74
|
-
map.set("key2", 200);
|
|
75
|
-
expect(map.size).toBe(2);
|
|
76
|
-
map.delete("key1");
|
|
77
|
-
expect(map.size).toBe(1);
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
it("없는 키를 get하면 undefined를 반환한다", () => {
|
|
81
|
-
const map = new LazyGcMap<string, number>({
|
|
82
|
-
gcInterval: 1000,
|
|
83
|
-
expireTime: 5000,
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
expect(map.get("nonexistent")).toBe(undefined);
|
|
87
|
-
});
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
//#endregion
|
|
91
|
-
|
|
92
|
-
//#region getOrCreate
|
|
93
|
-
|
|
94
|
-
describe("getOrCreate()", () => {
|
|
95
|
-
it("없는 키는 factory로 생성한다", () => {
|
|
96
|
-
const map = new LazyGcMap<string, number>({
|
|
97
|
-
gcInterval: 1000,
|
|
98
|
-
expireTime: 5000,
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
const value = map.getOrCreate("key1", () => 100);
|
|
102
|
-
expect(value).toBe(100);
|
|
103
|
-
expect(map.get("key1")).toBe(100);
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
it("있는 키는 기존 값을 반환한다", () => {
|
|
107
|
-
const map = new LazyGcMap<string, number>({
|
|
108
|
-
gcInterval: 1000,
|
|
109
|
-
expireTime: 5000,
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
map.set("key1", 100);
|
|
113
|
-
const factoryCalls: number[] = [];
|
|
114
|
-
const value = map.getOrCreate("key1", () => {
|
|
115
|
-
factoryCalls.push(1);
|
|
116
|
-
return 200;
|
|
117
|
-
});
|
|
118
|
-
|
|
119
|
-
expect(value).toBe(100); // 기존 값
|
|
120
|
-
expect(factoryCalls).toHaveLength(0); // factory 호출 안 됨
|
|
121
|
-
});
|
|
122
|
-
|
|
123
|
-
it("factory는 호출될 때마다 새 값을 생성한다", () => {
|
|
124
|
-
const map = new LazyGcMap<string, { id: number }>({
|
|
125
|
-
gcInterval: 1000,
|
|
126
|
-
expireTime: 5000,
|
|
127
|
-
});
|
|
128
|
-
|
|
129
|
-
const value1 = map.getOrCreate("key1", () => ({ id: 1 }));
|
|
130
|
-
const value2 = map.getOrCreate("key2", () => ({ id: 2 }));
|
|
131
|
-
|
|
132
|
-
expect(value1).toEqual({ id: 1 });
|
|
133
|
-
expect(value2).toEqual({ id: 2 });
|
|
134
|
-
expect(value1).not.toBe(value2);
|
|
135
|
-
});
|
|
136
|
-
});
|
|
137
|
-
|
|
138
|
-
//#endregion
|
|
139
|
-
|
|
140
|
-
//#region 자동 만료 (GC)
|
|
141
|
-
|
|
142
|
-
describe("자동 만료 (GC)", () => {
|
|
143
|
-
it("expireTime 이후 미접근 시 자동 삭제된다", async () => {
|
|
144
|
-
const map = new LazyGcMap<string, number>({
|
|
145
|
-
gcInterval: 100, // 100ms마다 GC
|
|
146
|
-
expireTime: 200, // 200ms 후 만료
|
|
147
|
-
});
|
|
148
|
-
|
|
149
|
-
map.set("key1", 100);
|
|
150
|
-
expect(map.has("key1")).toBe(true);
|
|
151
|
-
|
|
152
|
-
// expireTime(200) + gcInterval(100) = 300ms 후 GC 실행됨
|
|
153
|
-
await vi.advanceTimersByTimeAsync(350);
|
|
154
|
-
|
|
155
|
-
expect(map.has("key1")).toBe(false);
|
|
156
|
-
});
|
|
157
|
-
|
|
158
|
-
it("접근하면 만료 시간이 갱신된다 (LRU)", async () => {
|
|
159
|
-
const map = new LazyGcMap<string, number>({
|
|
160
|
-
gcInterval: 100,
|
|
161
|
-
expireTime: 200,
|
|
162
|
-
});
|
|
163
|
-
|
|
164
|
-
map.set("key1", 100);
|
|
165
|
-
|
|
166
|
-
// 150ms마다 접근 (expireTime 200ms보다 짧음)
|
|
167
|
-
await vi.advanceTimersByTimeAsync(150);
|
|
168
|
-
map.get("key1"); // 접근 → 시간 갱신
|
|
169
|
-
|
|
170
|
-
await vi.advanceTimersByTimeAsync(150);
|
|
171
|
-
map.get("key1"); // 접근 → 시간 갱신
|
|
172
|
-
|
|
173
|
-
await vi.advanceTimersByTimeAsync(150);
|
|
174
|
-
|
|
175
|
-
// 계속 접근했으므로 만료 안 됨
|
|
176
|
-
expect(map.has("key1")).toBe(true);
|
|
177
|
-
});
|
|
178
|
-
|
|
179
|
-
it("has()는 접근 시간을 갱신하지 않는다", async () => {
|
|
180
|
-
const map = new LazyGcMap<string, number>({
|
|
181
|
-
gcInterval: 100,
|
|
182
|
-
expireTime: 200,
|
|
183
|
-
});
|
|
184
|
-
|
|
185
|
-
map.set("key1", 100);
|
|
186
|
-
|
|
187
|
-
// 150ms마다 has()만 호출 (get()이 아님)
|
|
188
|
-
await vi.advanceTimersByTimeAsync(150);
|
|
189
|
-
map.has("key1"); // has()는 접근 시간을 갱신하지 않음
|
|
190
|
-
|
|
191
|
-
await vi.advanceTimersByTimeAsync(150);
|
|
192
|
-
map.has("key1"); // has()는 접근 시간을 갱신하지 않음
|
|
193
|
-
|
|
194
|
-
await vi.advanceTimersByTimeAsync(100);
|
|
195
|
-
|
|
196
|
-
// has()는 접근 시간을 갱신하지 않으므로 만료됨
|
|
197
|
-
expect(map.has("key1")).toBe(false);
|
|
198
|
-
});
|
|
199
|
-
|
|
200
|
-
it("getOrCreate도 접근 시간을 갱신한다", async () => {
|
|
201
|
-
const map = new LazyGcMap<string, number>({
|
|
202
|
-
gcInterval: 100,
|
|
203
|
-
expireTime: 200,
|
|
204
|
-
});
|
|
205
|
-
|
|
206
|
-
map.set("key1", 100);
|
|
207
|
-
|
|
208
|
-
await vi.advanceTimersByTimeAsync(150);
|
|
209
|
-
map.getOrCreate("key1", () => 200); // 접근 → 시간 갱신
|
|
210
|
-
|
|
211
|
-
await vi.advanceTimersByTimeAsync(150);
|
|
212
|
-
|
|
213
|
-
expect(map.has("key1")).toBe(true);
|
|
214
|
-
});
|
|
215
|
-
|
|
216
|
-
it("여러 항목 중 만료된 것만 삭제한다", async () => {
|
|
217
|
-
const map = new LazyGcMap<string, number>({
|
|
218
|
-
gcInterval: 100,
|
|
219
|
-
expireTime: 200,
|
|
220
|
-
});
|
|
221
|
-
|
|
222
|
-
map.set("key1", 100);
|
|
223
|
-
await vi.advanceTimersByTimeAsync(150);
|
|
224
|
-
map.set("key2", 200); // key1보다 150ms 늦게 추가
|
|
225
|
-
|
|
226
|
-
await vi.advanceTimersByTimeAsync(200);
|
|
227
|
-
|
|
228
|
-
// key1은 만료, key2는 아직 살아있음
|
|
229
|
-
expect(map.has("key1")).toBe(false);
|
|
230
|
-
expect(map.has("key2")).toBe(true);
|
|
231
|
-
});
|
|
232
|
-
|
|
233
|
-
it("모든 항목이 만료되면 GC 타이머가 중지된다", async () => {
|
|
234
|
-
const map = new LazyGcMap<string, number>({
|
|
235
|
-
gcInterval: 100,
|
|
236
|
-
expireTime: 200,
|
|
237
|
-
});
|
|
238
|
-
|
|
239
|
-
map.set("key1", 100);
|
|
240
|
-
|
|
241
|
-
// 만료 대기
|
|
242
|
-
await vi.advanceTimersByTimeAsync(350);
|
|
243
|
-
|
|
244
|
-
expect(map.size).toBe(0);
|
|
245
|
-
// GC 타이머 중지 확인 (더 기다려도 문제 없음)
|
|
246
|
-
await vi.advanceTimersByTimeAsync(200);
|
|
247
|
-
expect(map.size).toBe(0);
|
|
248
|
-
});
|
|
249
|
-
});
|
|
250
|
-
|
|
251
|
-
//#endregion
|
|
252
|
-
|
|
253
|
-
//#region onExpire 콜백
|
|
254
|
-
|
|
255
|
-
describe("onExpire 콜백", () => {
|
|
256
|
-
it("만료 시 onExpire 콜백이 호출된다", async () => {
|
|
257
|
-
const expired: Array<[string, number]> = [];
|
|
258
|
-
const map = new LazyGcMap<string, number>({
|
|
259
|
-
gcInterval: 100,
|
|
260
|
-
expireTime: 200,
|
|
261
|
-
onExpire: (key, value) => {
|
|
262
|
-
expired.push([key, value]);
|
|
263
|
-
},
|
|
264
|
-
});
|
|
265
|
-
|
|
266
|
-
map.set("key1", 100);
|
|
267
|
-
await vi.advanceTimersByTimeAsync(350);
|
|
268
|
-
|
|
269
|
-
expect(expired).toEqual([["key1", 100]]);
|
|
270
|
-
});
|
|
271
|
-
|
|
272
|
-
it("비동기 onExpire 콜백도 지원한다", async () => {
|
|
273
|
-
const expired: Array<[string, number]> = [];
|
|
274
|
-
const map = new LazyGcMap<string, number>({
|
|
275
|
-
gcInterval: 100,
|
|
276
|
-
expireTime: 200,
|
|
277
|
-
onExpire: async (key, value) => {
|
|
278
|
-
await new Promise((r) => setTimeout(r, 10));
|
|
279
|
-
expired.push([key, value]);
|
|
280
|
-
},
|
|
281
|
-
});
|
|
282
|
-
|
|
283
|
-
map.set("key1", 100);
|
|
284
|
-
// expireTime(200) + gcInterval(100) + callback(10) = 310ms
|
|
285
|
-
await vi.advanceTimersByTimeAsync(350);
|
|
286
|
-
|
|
287
|
-
expect(expired).toEqual([["key1", 100]]);
|
|
288
|
-
});
|
|
289
|
-
|
|
290
|
-
it("여러 항목 만료 시 각각 onExpire가 호출된다", async () => {
|
|
291
|
-
const expired: Array<[string, number]> = [];
|
|
292
|
-
const map = new LazyGcMap<string, number>({
|
|
293
|
-
gcInterval: 100,
|
|
294
|
-
expireTime: 200,
|
|
295
|
-
onExpire: (key, value) => {
|
|
296
|
-
expired.push([key, value]);
|
|
297
|
-
},
|
|
298
|
-
});
|
|
299
|
-
|
|
300
|
-
map.set("key1", 100);
|
|
301
|
-
map.set("key2", 200);
|
|
302
|
-
await vi.advanceTimersByTimeAsync(350);
|
|
303
|
-
|
|
304
|
-
expect(expired).toHaveLength(2);
|
|
305
|
-
expect(expired).toContainEqual(["key1", 100]);
|
|
306
|
-
expect(expired).toContainEqual(["key2", 200]);
|
|
307
|
-
});
|
|
308
|
-
|
|
309
|
-
it("onExpire 에러는 무시한다", async () => {
|
|
310
|
-
const expired: Array<[string, number]> = [];
|
|
311
|
-
const map = new LazyGcMap<string, number>({
|
|
312
|
-
gcInterval: 100,
|
|
313
|
-
expireTime: 200,
|
|
314
|
-
onExpire: (key, value) => {
|
|
315
|
-
expired.push([key, value]);
|
|
316
|
-
throw new Error("callback error");
|
|
317
|
-
},
|
|
318
|
-
});
|
|
319
|
-
|
|
320
|
-
map.set("key1", 100);
|
|
321
|
-
await vi.advanceTimersByTimeAsync(350);
|
|
322
|
-
|
|
323
|
-
// 에러가 발생해도 만료는 정상 처리됨
|
|
324
|
-
expect(expired).toEqual([["key1", 100]]);
|
|
325
|
-
expect(map.has("key1")).toBe(false);
|
|
326
|
-
});
|
|
327
|
-
|
|
328
|
-
it("onExpire 중 같은 키로 set() 호출 시 새 값이 유지된다", async () => {
|
|
329
|
-
let map: LazyGcMap<string, number>;
|
|
330
|
-
const expired: Array<[string, number]> = [];
|
|
331
|
-
|
|
332
|
-
map = new LazyGcMap<string, number>({
|
|
333
|
-
gcInterval: 100,
|
|
334
|
-
expireTime: 200,
|
|
335
|
-
onExpire: (key, value) => {
|
|
336
|
-
expired.push([key, value]);
|
|
337
|
-
// onExpire 콜백 중에 같은 키로 새 값 등록
|
|
338
|
-
map.set(key, value + 1000);
|
|
339
|
-
},
|
|
340
|
-
});
|
|
341
|
-
|
|
342
|
-
map.set("key1", 100);
|
|
343
|
-
await vi.advanceTimersByTimeAsync(350);
|
|
344
|
-
|
|
345
|
-
// onExpire는 호출되었지만, 새로 등록된 값은 삭제되지 않음
|
|
346
|
-
expect(expired).toEqual([["key1", 100]]);
|
|
347
|
-
expect(map.has("key1")).toBe(true);
|
|
348
|
-
expect(map.get("key1")).toBe(1100);
|
|
349
|
-
});
|
|
350
|
-
|
|
351
|
-
it("onExpire 중 다른 키로 set() 호출은 영향 없다", async () => {
|
|
352
|
-
let map: LazyGcMap<string, number>;
|
|
353
|
-
const expired: Array<[string, number]> = [];
|
|
354
|
-
|
|
355
|
-
map = new LazyGcMap<string, number>({
|
|
356
|
-
gcInterval: 100,
|
|
357
|
-
expireTime: 200,
|
|
358
|
-
onExpire: (key, value) => {
|
|
359
|
-
expired.push([key, value]);
|
|
360
|
-
// onExpire 콜백 중에 다른 키로 새 값 등록 (key1 만료 시에만)
|
|
361
|
-
if (key === "key1") {
|
|
362
|
-
map.set("key2", 200);
|
|
363
|
-
}
|
|
364
|
-
},
|
|
365
|
-
});
|
|
366
|
-
|
|
367
|
-
map.set("key1", 100);
|
|
368
|
-
// expireTime(200) + gcInterval(100) = 300ms 후 GC
|
|
369
|
-
// key2가 등록된 후에도 아직 만료되지 않아야 함
|
|
370
|
-
await vi.advanceTimersByTimeAsync(350);
|
|
371
|
-
|
|
372
|
-
// key1은 만료 삭제됨
|
|
373
|
-
expect(expired).toEqual([["key1", 100]]);
|
|
374
|
-
expect(map.has("key1")).toBe(false);
|
|
375
|
-
// key2는 새로 등록됨 (아직 만료 전)
|
|
376
|
-
expect(map.has("key2")).toBe(true);
|
|
377
|
-
expect(map.get("key2")).toBe(200);
|
|
378
|
-
});
|
|
379
|
-
});
|
|
380
|
-
|
|
381
|
-
//#endregion
|
|
382
|
-
|
|
383
|
-
//#region dispose (타이머 및 리소스 정리)
|
|
384
|
-
|
|
385
|
-
describe("dispose() - 타이머 정리", () => {
|
|
386
|
-
it("dispose 후 타이머가 정리되어 GC 콜백이 호출되지 않는다", async () => {
|
|
387
|
-
const expired: Array<[string, number]> = [];
|
|
388
|
-
const map = new LazyGcMap<string, number>({
|
|
389
|
-
gcInterval: 100,
|
|
390
|
-
expireTime: 200,
|
|
391
|
-
onExpire: (key, value) => {
|
|
392
|
-
expired.push([key, value]);
|
|
393
|
-
},
|
|
394
|
-
});
|
|
395
|
-
|
|
396
|
-
map.set("key1", 100);
|
|
397
|
-
expect(map.has("key1")).toBe(true);
|
|
398
|
-
|
|
399
|
-
// dispose 호출로 타이머 정리
|
|
400
|
-
map.dispose();
|
|
401
|
-
expect(map.size).toBe(0);
|
|
402
|
-
|
|
403
|
-
// expireTime + gcInterval 이상 대기
|
|
404
|
-
await vi.advanceTimersByTimeAsync(400);
|
|
405
|
-
|
|
406
|
-
// GC 콜백이 호출되지 않아야 함 (dispose로 이미 정리됨)
|
|
407
|
-
expect(expired).toHaveLength(0);
|
|
408
|
-
});
|
|
409
|
-
|
|
410
|
-
it("dispose 후 set은 무시된다", () => {
|
|
411
|
-
const map = new LazyGcMap<string, number>({
|
|
412
|
-
gcInterval: 100,
|
|
413
|
-
expireTime: 200,
|
|
414
|
-
});
|
|
415
|
-
|
|
416
|
-
map.set("key1", 100);
|
|
417
|
-
map.dispose();
|
|
418
|
-
|
|
419
|
-
// dispose 후 set은 무시됨
|
|
420
|
-
map.set("key2", 200);
|
|
421
|
-
expect(map.has("key2")).toBe(false);
|
|
422
|
-
expect(map.get("key2")).toBeUndefined();
|
|
423
|
-
expect(map.size).toBe(0);
|
|
424
|
-
});
|
|
425
|
-
|
|
426
|
-
it("dispose는 여러 번 호출해도 안전하다", () => {
|
|
427
|
-
const map = new LazyGcMap<string, number>({
|
|
428
|
-
gcInterval: 1000,
|
|
429
|
-
expireTime: 5000,
|
|
430
|
-
});
|
|
431
|
-
|
|
432
|
-
map.set("key1", 100);
|
|
433
|
-
|
|
434
|
-
// 여러 번 호출해도 에러 없음
|
|
435
|
-
map.dispose();
|
|
436
|
-
map.dispose();
|
|
437
|
-
map.dispose();
|
|
438
|
-
|
|
439
|
-
expect(map.size).toBe(0);
|
|
440
|
-
});
|
|
441
|
-
|
|
442
|
-
it("using 문으로 자동 dispose된다", async () => {
|
|
443
|
-
const expired: Array<[string, number]> = [];
|
|
444
|
-
{
|
|
445
|
-
using map = new LazyGcMap<string, number>({
|
|
446
|
-
gcInterval: 100,
|
|
447
|
-
expireTime: 200,
|
|
448
|
-
onExpire: (key, value) => {
|
|
449
|
-
expired.push([key, value]);
|
|
450
|
-
},
|
|
451
|
-
});
|
|
452
|
-
map.set("key1", 100);
|
|
453
|
-
expect(map.has("key1")).toBe(true);
|
|
454
|
-
} // using 블록 종료 시 dispose 자동 호출
|
|
455
|
-
await vi.advanceTimersByTimeAsync(350);
|
|
456
|
-
// dispose로 정리됨 (onExpire 미호출)
|
|
457
|
-
expect(expired).toHaveLength(0);
|
|
458
|
-
});
|
|
459
|
-
});
|
|
460
|
-
|
|
461
|
-
//#endregion
|
|
462
|
-
|
|
463
|
-
//#region clear
|
|
464
|
-
|
|
465
|
-
describe("clear()", () => {
|
|
466
|
-
it("모든 항목을 삭제한다", () => {
|
|
467
|
-
const map = new LazyGcMap<string, number>({
|
|
468
|
-
gcInterval: 1000,
|
|
469
|
-
expireTime: 5000,
|
|
470
|
-
});
|
|
471
|
-
|
|
472
|
-
map.set("key1", 100);
|
|
473
|
-
map.set("key2", 200);
|
|
474
|
-
expect(map.size).toBe(2);
|
|
475
|
-
|
|
476
|
-
map.clear();
|
|
477
|
-
expect(map.size).toBe(0);
|
|
478
|
-
expect(map.has("key1")).toBe(false);
|
|
479
|
-
expect(map.has("key2")).toBe(false);
|
|
480
|
-
});
|
|
481
|
-
|
|
482
|
-
it("clear 후 새 항목을 추가할 수 있다", () => {
|
|
483
|
-
const map = new LazyGcMap<string, number>({
|
|
484
|
-
gcInterval: 1000,
|
|
485
|
-
expireTime: 5000,
|
|
486
|
-
});
|
|
487
|
-
|
|
488
|
-
map.set("key1", 100);
|
|
489
|
-
map.clear();
|
|
490
|
-
|
|
491
|
-
map.set("key2", 200);
|
|
492
|
-
expect(map.has("key2")).toBe(true);
|
|
493
|
-
expect(map.get("key2")).toBe(200);
|
|
494
|
-
});
|
|
495
|
-
|
|
496
|
-
it("clear는 여러 번 호출해도 안전하다", () => {
|
|
497
|
-
const map = new LazyGcMap<string, number>({
|
|
498
|
-
gcInterval: 1000,
|
|
499
|
-
expireTime: 5000,
|
|
500
|
-
});
|
|
501
|
-
|
|
502
|
-
map.set("key1", 100);
|
|
503
|
-
|
|
504
|
-
// 여러 번 호출해도 에러 없음
|
|
505
|
-
map.clear();
|
|
506
|
-
map.clear();
|
|
507
|
-
map.clear();
|
|
508
|
-
|
|
509
|
-
expect(map.size).toBe(0);
|
|
510
|
-
});
|
|
511
|
-
|
|
512
|
-
it("clear 후 GC가 정상 작동한다", async () => {
|
|
513
|
-
const map = new LazyGcMap<string, number>({
|
|
514
|
-
gcInterval: 100,
|
|
515
|
-
expireTime: 200,
|
|
516
|
-
});
|
|
517
|
-
|
|
518
|
-
map.set("key1", 100);
|
|
519
|
-
map.clear();
|
|
520
|
-
|
|
521
|
-
// clear 후 새 항목 추가
|
|
522
|
-
map.set("key2", 200);
|
|
523
|
-
|
|
524
|
-
// GC가 정상 작동하는지 확인
|
|
525
|
-
await vi.advanceTimersByTimeAsync(350);
|
|
526
|
-
expect(map.has("key2")).toBe(false);
|
|
527
|
-
});
|
|
528
|
-
});
|
|
529
|
-
|
|
530
|
-
//#endregion
|
|
531
|
-
|
|
532
|
-
//#region SharedArrayBuffer 지원
|
|
533
|
-
|
|
534
|
-
describe("SharedArrayBuffer 지원", () => {
|
|
535
|
-
it("SharedArrayBuffer를 값으로 사용할 수 있다", () => {
|
|
536
|
-
// SharedArrayBuffer는 일부 환경에서 보안상 비활성화될 수 있음
|
|
537
|
-
if (typeof SharedArrayBuffer === "undefined") {
|
|
538
|
-
expect(true).toBe(true); // 환경에서 지원하지 않으면 스킵
|
|
539
|
-
return;
|
|
540
|
-
}
|
|
541
|
-
|
|
542
|
-
const map = new LazyGcMap<string, SharedArrayBuffer>({
|
|
543
|
-
gcInterval: 1000,
|
|
544
|
-
expireTime: 5000,
|
|
545
|
-
});
|
|
546
|
-
|
|
547
|
-
const buffer = new SharedArrayBuffer(16);
|
|
548
|
-
map.set("key1", buffer);
|
|
549
|
-
|
|
550
|
-
expect(map.get("key1")).toBe(buffer);
|
|
551
|
-
expect(map.get("key1")?.byteLength).toBe(16);
|
|
552
|
-
});
|
|
553
|
-
});
|
|
554
|
-
|
|
555
|
-
//#endregion
|
|
556
|
-
|
|
557
|
-
//#region Iterator
|
|
558
|
-
|
|
559
|
-
describe("Iterator", () => {
|
|
560
|
-
it("values()로 값들을 순회한다", () => {
|
|
561
|
-
const map = new LazyGcMap<string, number>({
|
|
562
|
-
gcInterval: 1000,
|
|
563
|
-
expireTime: 5000,
|
|
564
|
-
});
|
|
565
|
-
|
|
566
|
-
map.set("key1", 100);
|
|
567
|
-
map.set("key2", 200);
|
|
568
|
-
map.set("key3", 300);
|
|
569
|
-
|
|
570
|
-
const values = Array.from(map.values());
|
|
571
|
-
expect(values).toHaveLength(3);
|
|
572
|
-
expect(values).toContain(100);
|
|
573
|
-
expect(values).toContain(200);
|
|
574
|
-
expect(values).toContain(300);
|
|
575
|
-
});
|
|
576
|
-
|
|
577
|
-
it("keys()로 키들을 순회한다", () => {
|
|
578
|
-
const map = new LazyGcMap<string, number>({
|
|
579
|
-
gcInterval: 1000,
|
|
580
|
-
expireTime: 5000,
|
|
581
|
-
});
|
|
582
|
-
|
|
583
|
-
map.set("key1", 100);
|
|
584
|
-
map.set("key2", 200);
|
|
585
|
-
|
|
586
|
-
const keys = Array.from(map.keys());
|
|
587
|
-
expect(keys).toEqual(["key1", "key2"]);
|
|
588
|
-
});
|
|
589
|
-
|
|
590
|
-
it("entries()로 엔트리를 순회한다", () => {
|
|
591
|
-
const map = new LazyGcMap<string, number>({
|
|
592
|
-
gcInterval: 1000,
|
|
593
|
-
expireTime: 5000,
|
|
594
|
-
});
|
|
595
|
-
|
|
596
|
-
map.set("key1", 100);
|
|
597
|
-
map.set("key2", 200);
|
|
598
|
-
|
|
599
|
-
const entries = Array.from(map.entries());
|
|
600
|
-
expect(entries).toEqual([
|
|
601
|
-
["key1", 100],
|
|
602
|
-
["key2", 200],
|
|
603
|
-
]);
|
|
604
|
-
});
|
|
605
|
-
});
|
|
606
|
-
|
|
607
|
-
//#endregion
|
|
608
|
-
|
|
609
|
-
//#region dispose 후 안전성
|
|
610
|
-
|
|
611
|
-
describe("dispose 후 안전성", () => {
|
|
612
|
-
it("dispose 후 get은 undefined를 반환한다", () => {
|
|
613
|
-
const map = new LazyGcMap<string, number>({
|
|
614
|
-
gcInterval: 10000,
|
|
615
|
-
expireTime: 60000,
|
|
616
|
-
});
|
|
617
|
-
map.set("a", 1);
|
|
618
|
-
map.dispose();
|
|
619
|
-
expect(map.get("a")).toBeUndefined();
|
|
620
|
-
});
|
|
621
|
-
|
|
622
|
-
it("dispose 후 has는 false를 반환한다", () => {
|
|
623
|
-
const map = new LazyGcMap<string, number>({
|
|
624
|
-
gcInterval: 10000,
|
|
625
|
-
expireTime: 60000,
|
|
626
|
-
});
|
|
627
|
-
map.set("a", 1);
|
|
628
|
-
map.dispose();
|
|
629
|
-
expect(map.has("a")).toBe(false);
|
|
630
|
-
});
|
|
631
|
-
|
|
632
|
-
it("dispose 후 delete는 false를 반환한다", () => {
|
|
633
|
-
const map = new LazyGcMap<string, number>({
|
|
634
|
-
gcInterval: 10000,
|
|
635
|
-
expireTime: 60000,
|
|
636
|
-
});
|
|
637
|
-
map.set("a", 1);
|
|
638
|
-
map.dispose();
|
|
639
|
-
expect(map.delete("a")).toBe(false);
|
|
640
|
-
});
|
|
641
|
-
|
|
642
|
-
it("dispose 후 getOrCreate는 에러를 던진다", () => {
|
|
643
|
-
const map = new LazyGcMap<string, number>({
|
|
644
|
-
gcInterval: 10000,
|
|
645
|
-
expireTime: 60000,
|
|
646
|
-
});
|
|
647
|
-
map.dispose();
|
|
648
|
-
expect(() => map.getOrCreate("a", () => 1)).toThrow();
|
|
649
|
-
});
|
|
650
|
-
|
|
651
|
-
it("dispose 후 clear는 에러 없이 무시된다", () => {
|
|
652
|
-
const map = new LazyGcMap<string, number>({
|
|
653
|
-
gcInterval: 10000,
|
|
654
|
-
expireTime: 60000,
|
|
655
|
-
});
|
|
656
|
-
map.dispose();
|
|
657
|
-
expect(() => map.clear()).not.toThrow();
|
|
658
|
-
});
|
|
659
|
-
|
|
660
|
-
it("dispose 후 values는 빈 이터레이터를 반환한다", () => {
|
|
661
|
-
const map = new LazyGcMap<string, number>({
|
|
662
|
-
gcInterval: 10000,
|
|
663
|
-
expireTime: 60000,
|
|
664
|
-
});
|
|
665
|
-
map.set("a", 1);
|
|
666
|
-
map.dispose();
|
|
667
|
-
expect([...map.values()]).toEqual([]);
|
|
668
|
-
});
|
|
669
|
-
|
|
670
|
-
it("dispose 후 keys는 빈 이터레이터를 반환한다", () => {
|
|
671
|
-
const map = new LazyGcMap<string, number>({
|
|
672
|
-
gcInterval: 10000,
|
|
673
|
-
expireTime: 60000,
|
|
674
|
-
});
|
|
675
|
-
map.set("a", 1);
|
|
676
|
-
map.dispose();
|
|
677
|
-
expect([...map.keys()]).toEqual([]);
|
|
678
|
-
});
|
|
679
|
-
|
|
680
|
-
it("dispose 후 entries는 빈 이터레이터를 반환한다", () => {
|
|
681
|
-
const map = new LazyGcMap<string, number>({
|
|
682
|
-
gcInterval: 10000,
|
|
683
|
-
expireTime: 60000,
|
|
684
|
-
});
|
|
685
|
-
map.set("a", 1);
|
|
686
|
-
map.dispose();
|
|
687
|
-
expect([...map.entries()]).toEqual([]);
|
|
688
|
-
});
|
|
689
|
-
});
|
|
690
|
-
|
|
691
|
-
//#endregion
|
|
692
|
-
});
|