@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
package/tests/utils/path.spec.ts
DELETED
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from "vitest";
|
|
2
|
-
import { pathJoin, pathBasename, pathExtname } from "@simplysm/core-common";
|
|
3
|
-
|
|
4
|
-
describe("path utils", () => {
|
|
5
|
-
describe("pathJoin()", () => {
|
|
6
|
-
it("경로 세그먼트를 결합한다", () => {
|
|
7
|
-
expect(pathJoin("a", "b", "c")).toBe("a/b/c");
|
|
8
|
-
});
|
|
9
|
-
|
|
10
|
-
it("선행 슬래시를 유지한다", () => {
|
|
11
|
-
expect(pathJoin("/a", "b")).toBe("/a/b");
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
it("중복 슬래시를 제거한다", () => {
|
|
15
|
-
expect(pathJoin("a/", "/b/", "/c")).toBe("a/b/c");
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
it("빈 세그먼트를 무시한다", () => {
|
|
19
|
-
expect(pathJoin("a", "", "b")).toBe("a/b");
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
it("단일 세그먼트를 반환한다", () => {
|
|
23
|
-
expect(pathJoin("a")).toBe("a");
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
it("빈 입력은 빈 문자열을 반환한다", () => {
|
|
27
|
-
expect(pathJoin()).toBe("");
|
|
28
|
-
});
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
describe("pathBasename()", () => {
|
|
32
|
-
it("파일명을 추출한다", () => {
|
|
33
|
-
expect(pathBasename("a/b/file.txt")).toBe("file.txt");
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
it("확장자를 제거한다", () => {
|
|
37
|
-
expect(pathBasename("a/b/file.txt", ".txt")).toBe("file");
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
it("일치하지 않는 확장자는 무시한다", () => {
|
|
41
|
-
expect(pathBasename("a/b/file.txt", ".md")).toBe("file.txt");
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
it("경로 없는 파일명을 처리한다", () => {
|
|
45
|
-
expect(pathBasename("file.txt")).toBe("file.txt");
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
it("빈 문자열은 빈 문자열을 반환한다", () => {
|
|
49
|
-
expect(pathBasename("")).toBe("");
|
|
50
|
-
});
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
describe("pathExtname()", () => {
|
|
54
|
-
it("확장자를 추출한다", () => {
|
|
55
|
-
expect(pathExtname("file.txt")).toBe(".txt");
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
it("마지막 확장자만 추출한다", () => {
|
|
59
|
-
expect(pathExtname("archive.tar.gz")).toBe(".gz");
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
it("확장자 없는 파일은 빈 문자열을 반환한다", () => {
|
|
63
|
-
expect(pathExtname("Makefile")).toBe("");
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
it("숨김 파일은 빈 문자열을 반환한다", () => {
|
|
67
|
-
expect(pathExtname(".gitignore")).toBe("");
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
it("경로 포함 파일의 확장자를 추출한다", () => {
|
|
71
|
-
expect(pathExtname("a/b/file.ts")).toBe(".ts");
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
it("빈 문자열은 빈 문자열을 반환한다", () => {
|
|
75
|
-
expect(pathExtname("")).toBe("");
|
|
76
|
-
});
|
|
77
|
-
});
|
|
78
|
-
});
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from "vitest";
|
|
2
|
-
import { getPrimitiveTypeStr, DateTime, DateOnly, Time, Uuid } from "@simplysm/core-common";
|
|
3
|
-
|
|
4
|
-
describe("primitive utils", () => {
|
|
5
|
-
describe("getPrimitiveTypeStr()", () => {
|
|
6
|
-
it("string을 반환한다", () => {
|
|
7
|
-
expect(getPrimitiveTypeStr("hello")).toBe("string");
|
|
8
|
-
});
|
|
9
|
-
|
|
10
|
-
it("빈 문자열도 string을 반환한다", () => {
|
|
11
|
-
expect(getPrimitiveTypeStr("")).toBe("string");
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
it("number를 반환한다", () => {
|
|
15
|
-
expect(getPrimitiveTypeStr(42)).toBe("number");
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
it("0도 number를 반환한다", () => {
|
|
19
|
-
expect(getPrimitiveTypeStr(0)).toBe("number");
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
it("NaN도 number를 반환한다", () => {
|
|
23
|
-
expect(getPrimitiveTypeStr(NaN)).toBe("number");
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
it("boolean을 반환한다", () => {
|
|
27
|
-
expect(getPrimitiveTypeStr(true)).toBe("boolean");
|
|
28
|
-
expect(getPrimitiveTypeStr(false)).toBe("boolean");
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
it("DateTime을 반환한다", () => {
|
|
32
|
-
expect(getPrimitiveTypeStr(new DateTime())).toBe("DateTime");
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
it("DateOnly를 반환한다", () => {
|
|
36
|
-
expect(getPrimitiveTypeStr(new DateOnly())).toBe("DateOnly");
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
it("Time을 반환한다", () => {
|
|
40
|
-
expect(getPrimitiveTypeStr(new Time())).toBe("Time");
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
it("Uuid를 반환한다", () => {
|
|
44
|
-
expect(getPrimitiveTypeStr(Uuid.new())).toBe("Uuid");
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
it("Uint8Array는 Bytes를 반환한다", () => {
|
|
48
|
-
expect(getPrimitiveTypeStr(new Uint8Array([1, 2]))).toBe("Bytes");
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
it("지원하지 않는 타입은 에러를 던진다", () => {
|
|
52
|
-
expect(() => getPrimitiveTypeStr({} as never)).toThrow();
|
|
53
|
-
});
|
|
54
|
-
});
|
|
55
|
-
});
|
|
@@ -1,216 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, vi } from "vitest";
|
|
2
|
-
import { EventEmitter } from "@simplysm/core-common";
|
|
3
|
-
|
|
4
|
-
interface TestEvents {
|
|
5
|
-
message: string;
|
|
6
|
-
count: number;
|
|
7
|
-
data: { id: number; name: string };
|
|
8
|
-
empty: void;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
describe("SdEventEmitter", () => {
|
|
12
|
-
//#region on/emit
|
|
13
|
-
|
|
14
|
-
describe("on() / emit()", () => {
|
|
15
|
-
it("이벤트를 발생시키고 수신한다", () => {
|
|
16
|
-
const emitter = new EventEmitter<TestEvents>();
|
|
17
|
-
const listener = vi.fn();
|
|
18
|
-
|
|
19
|
-
emitter.on("message", listener);
|
|
20
|
-
emitter.emit("message", "hello");
|
|
21
|
-
|
|
22
|
-
expect(listener).toHaveBeenCalledWith("hello");
|
|
23
|
-
expect(listener).toHaveBeenCalledTimes(1);
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
it("여러 번 emit하면 리스너가 여러 번 호출된다", () => {
|
|
27
|
-
const emitter = new EventEmitter<TestEvents>();
|
|
28
|
-
const listener = vi.fn();
|
|
29
|
-
|
|
30
|
-
emitter.on("count", listener);
|
|
31
|
-
emitter.emit("count", 1);
|
|
32
|
-
emitter.emit("count", 2);
|
|
33
|
-
emitter.emit("count", 3);
|
|
34
|
-
|
|
35
|
-
expect(listener).toHaveBeenCalledTimes(3);
|
|
36
|
-
expect(listener).toHaveBeenNthCalledWith(1, 1);
|
|
37
|
-
expect(listener).toHaveBeenNthCalledWith(2, 2);
|
|
38
|
-
expect(listener).toHaveBeenNthCalledWith(3, 3);
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
it("객체 데이터를 전달한다", () => {
|
|
42
|
-
const emitter = new EventEmitter<TestEvents>();
|
|
43
|
-
const listener = vi.fn();
|
|
44
|
-
|
|
45
|
-
emitter.on("data", listener);
|
|
46
|
-
emitter.emit("data", { id: 1, name: "test" });
|
|
47
|
-
|
|
48
|
-
expect(listener).toHaveBeenCalledWith({ id: 1, name: "test" });
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
it("void 이벤트를 처리한다", () => {
|
|
52
|
-
const emitter = new EventEmitter<TestEvents>();
|
|
53
|
-
const listener = vi.fn();
|
|
54
|
-
|
|
55
|
-
emitter.on("empty", listener);
|
|
56
|
-
emitter.emit("empty");
|
|
57
|
-
|
|
58
|
-
expect(listener).toHaveBeenCalledTimes(1);
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
it("같은 이벤트에 여러 리스너를 등록할 수 있다", () => {
|
|
62
|
-
const emitter = new EventEmitter<TestEvents>();
|
|
63
|
-
const listener1 = vi.fn();
|
|
64
|
-
const listener2 = vi.fn();
|
|
65
|
-
|
|
66
|
-
emitter.on("message", listener1);
|
|
67
|
-
emitter.on("message", listener2);
|
|
68
|
-
emitter.emit("message", "test");
|
|
69
|
-
|
|
70
|
-
expect(listener1).toHaveBeenCalledWith("test");
|
|
71
|
-
expect(listener2).toHaveBeenCalledWith("test");
|
|
72
|
-
});
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
//#endregion
|
|
76
|
-
|
|
77
|
-
//#region off
|
|
78
|
-
|
|
79
|
-
describe("off()", () => {
|
|
80
|
-
it("리스너를 해제한다", () => {
|
|
81
|
-
const emitter = new EventEmitter<TestEvents>();
|
|
82
|
-
const listener = vi.fn();
|
|
83
|
-
|
|
84
|
-
emitter.on("message", listener);
|
|
85
|
-
emitter.off("message", listener);
|
|
86
|
-
emitter.emit("message", "test");
|
|
87
|
-
|
|
88
|
-
expect(listener).not.toHaveBeenCalled();
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
it("해제된 리스너만 제거되고 다른 리스너는 유지된다", () => {
|
|
92
|
-
const emitter = new EventEmitter<TestEvents>();
|
|
93
|
-
const listener1 = vi.fn();
|
|
94
|
-
const listener2 = vi.fn();
|
|
95
|
-
|
|
96
|
-
emitter.on("message", listener1);
|
|
97
|
-
emitter.on("message", listener2);
|
|
98
|
-
emitter.off("message", listener1);
|
|
99
|
-
emitter.emit("message", "test");
|
|
100
|
-
|
|
101
|
-
expect(listener1).not.toHaveBeenCalled();
|
|
102
|
-
expect(listener2).toHaveBeenCalledWith("test");
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
it("등록되지 않은 리스너를 해제해도 에러가 발생하지 않는다", () => {
|
|
106
|
-
const emitter = new EventEmitter<TestEvents>();
|
|
107
|
-
const listener = vi.fn();
|
|
108
|
-
|
|
109
|
-
// 에러 없이 실행되어야 함
|
|
110
|
-
expect(() => {
|
|
111
|
-
emitter.off("message", listener);
|
|
112
|
-
}).not.toThrow();
|
|
113
|
-
});
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
//#endregion
|
|
117
|
-
|
|
118
|
-
//#region listenerCount
|
|
119
|
-
|
|
120
|
-
describe("listenerCount()", () => {
|
|
121
|
-
it("리스너 수를 정확히 카운트한다", () => {
|
|
122
|
-
const emitter = new EventEmitter<TestEvents>();
|
|
123
|
-
|
|
124
|
-
expect(emitter.listenerCount("message")).toBe(0);
|
|
125
|
-
|
|
126
|
-
emitter.on("message", () => {});
|
|
127
|
-
expect(emitter.listenerCount("message")).toBe(1);
|
|
128
|
-
|
|
129
|
-
emitter.on("message", () => {});
|
|
130
|
-
expect(emitter.listenerCount("message")).toBe(2);
|
|
131
|
-
});
|
|
132
|
-
|
|
133
|
-
it("off 후 카운트가 감소한다", () => {
|
|
134
|
-
const emitter = new EventEmitter<TestEvents>();
|
|
135
|
-
const listener = vi.fn();
|
|
136
|
-
|
|
137
|
-
emitter.on("message", listener);
|
|
138
|
-
expect(emitter.listenerCount("message")).toBe(1);
|
|
139
|
-
|
|
140
|
-
emitter.off("message", listener);
|
|
141
|
-
expect(emitter.listenerCount("message")).toBe(0);
|
|
142
|
-
});
|
|
143
|
-
|
|
144
|
-
it("등록되지 않은 이벤트 타입의 카운트는 0이다", () => {
|
|
145
|
-
const emitter = new EventEmitter<TestEvents>();
|
|
146
|
-
|
|
147
|
-
expect(emitter.listenerCount("message")).toBe(0);
|
|
148
|
-
expect(emitter.listenerCount("count")).toBe(0);
|
|
149
|
-
});
|
|
150
|
-
|
|
151
|
-
it("다른 이벤트 타입의 카운트는 독립적이다", () => {
|
|
152
|
-
const emitter = new EventEmitter<TestEvents>();
|
|
153
|
-
|
|
154
|
-
emitter.on("message", () => {});
|
|
155
|
-
emitter.on("message", () => {});
|
|
156
|
-
emitter.on("count", () => {});
|
|
157
|
-
|
|
158
|
-
expect(emitter.listenerCount("message")).toBe(2);
|
|
159
|
-
expect(emitter.listenerCount("count")).toBe(1);
|
|
160
|
-
});
|
|
161
|
-
});
|
|
162
|
-
|
|
163
|
-
//#endregion
|
|
164
|
-
|
|
165
|
-
//#region 중복 등록 방지
|
|
166
|
-
|
|
167
|
-
describe("중복 등록 방지", () => {
|
|
168
|
-
it("동일한 리스너를 중복 등록하면 무시된다", () => {
|
|
169
|
-
const emitter = new EventEmitter<TestEvents>();
|
|
170
|
-
const listener = vi.fn();
|
|
171
|
-
|
|
172
|
-
emitter.on("message", listener);
|
|
173
|
-
emitter.on("message", listener); // 중복 등록 시도
|
|
174
|
-
emitter.on("message", listener); // 중복 등록 시도
|
|
175
|
-
|
|
176
|
-
expect(emitter.listenerCount("message")).toBe(1);
|
|
177
|
-
|
|
178
|
-
emitter.emit("message", "test");
|
|
179
|
-
expect(listener).toHaveBeenCalledTimes(1);
|
|
180
|
-
});
|
|
181
|
-
|
|
182
|
-
it("동일한 리스너를 서로 다른 이벤트에 등록할 수 있다", () => {
|
|
183
|
-
const emitter = new EventEmitter<TestEvents>();
|
|
184
|
-
const listener = vi.fn();
|
|
185
|
-
|
|
186
|
-
emitter.on("message", listener);
|
|
187
|
-
emitter.on("count", listener as any);
|
|
188
|
-
|
|
189
|
-
expect(emitter.listenerCount("message")).toBe(1);
|
|
190
|
-
expect(emitter.listenerCount("count")).toBe(1);
|
|
191
|
-
|
|
192
|
-
emitter.emit("message", "test");
|
|
193
|
-
emitter.emit("count", 123);
|
|
194
|
-
|
|
195
|
-
expect(listener).toHaveBeenCalledTimes(2);
|
|
196
|
-
expect(listener).toHaveBeenNthCalledWith(1, "test");
|
|
197
|
-
expect(listener).toHaveBeenNthCalledWith(2, 123);
|
|
198
|
-
});
|
|
199
|
-
|
|
200
|
-
it("중복 등록 후 off 한 번 호출로 제거된다", () => {
|
|
201
|
-
const emitter = new EventEmitter<TestEvents>();
|
|
202
|
-
const listener = vi.fn();
|
|
203
|
-
|
|
204
|
-
emitter.on("message", listener);
|
|
205
|
-
emitter.on("message", listener); // 중복 등록 시도
|
|
206
|
-
|
|
207
|
-
emitter.off("message", listener);
|
|
208
|
-
expect(emitter.listenerCount("message")).toBe(0);
|
|
209
|
-
|
|
210
|
-
emitter.emit("message", "test");
|
|
211
|
-
expect(listener).not.toHaveBeenCalled();
|
|
212
|
-
});
|
|
213
|
-
});
|
|
214
|
-
|
|
215
|
-
//#endregion
|
|
216
|
-
});
|
|
@@ -1,365 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
|
2
|
-
import { SerialQueue, SdError } from "@simplysm/core-common";
|
|
3
|
-
|
|
4
|
-
describe("SerialQueue", () => {
|
|
5
|
-
beforeEach(() => {
|
|
6
|
-
vi.useFakeTimers();
|
|
7
|
-
});
|
|
8
|
-
|
|
9
|
-
afterEach(() => {
|
|
10
|
-
vi.useRealTimers();
|
|
11
|
-
});
|
|
12
|
-
|
|
13
|
-
//#region 순차 실행
|
|
14
|
-
|
|
15
|
-
describe("순차 실행", () => {
|
|
16
|
-
it("큐에 추가된 함수들을 순서대로 실행한다", async () => {
|
|
17
|
-
const queue = new SerialQueue();
|
|
18
|
-
const calls: number[] = [];
|
|
19
|
-
|
|
20
|
-
queue.run(() => {
|
|
21
|
-
calls.push(1);
|
|
22
|
-
});
|
|
23
|
-
queue.run(() => {
|
|
24
|
-
calls.push(2);
|
|
25
|
-
});
|
|
26
|
-
queue.run(() => {
|
|
27
|
-
calls.push(3);
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
await vi.advanceTimersByTimeAsync(50);
|
|
31
|
-
|
|
32
|
-
expect(calls).toEqual([1, 2, 3]);
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
it("이전 작업이 완료된 후 다음 작업을 실행한다", async () => {
|
|
36
|
-
const queue = new SerialQueue();
|
|
37
|
-
const calls: number[] = [];
|
|
38
|
-
const timestamps: number[] = [];
|
|
39
|
-
|
|
40
|
-
queue.run(async () => {
|
|
41
|
-
timestamps.push(Date.now());
|
|
42
|
-
calls.push(1);
|
|
43
|
-
await new Promise((r) => setTimeout(r, 50));
|
|
44
|
-
});
|
|
45
|
-
queue.run(async () => {
|
|
46
|
-
timestamps.push(Date.now());
|
|
47
|
-
calls.push(2);
|
|
48
|
-
await new Promise((r) => setTimeout(r, 50));
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
await vi.advanceTimersByTimeAsync(200);
|
|
52
|
-
|
|
53
|
-
expect(calls).toEqual([1, 2]);
|
|
54
|
-
// 두 번째 작업은 첫 작업 완료 후 시작 (정확히 50ms)
|
|
55
|
-
expect(timestamps[1] - timestamps[0]).toBe(50);
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
it("실행 중에 추가된 작업도 순차적으로 실행한다", async () => {
|
|
59
|
-
const queue = new SerialQueue();
|
|
60
|
-
const calls: number[] = [];
|
|
61
|
-
|
|
62
|
-
queue.run(async () => {
|
|
63
|
-
calls.push(1);
|
|
64
|
-
await new Promise((r) => setTimeout(r, 50));
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
// 첫 작업 시작 대기
|
|
68
|
-
await vi.advanceTimersByTimeAsync(10);
|
|
69
|
-
|
|
70
|
-
// 실행 중에 추가
|
|
71
|
-
queue.run(() => {
|
|
72
|
-
calls.push(2);
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
await vi.advanceTimersByTimeAsync(100);
|
|
76
|
-
|
|
77
|
-
expect(calls).toEqual([1, 2]);
|
|
78
|
-
});
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
//#endregion
|
|
82
|
-
|
|
83
|
-
//#region gap 간격
|
|
84
|
-
|
|
85
|
-
describe("gap 간격", () => {
|
|
86
|
-
it("gap이 설정되면 작업 사이에 대기한다", async () => {
|
|
87
|
-
const queue = new SerialQueue(50);
|
|
88
|
-
const timestamps: number[] = [];
|
|
89
|
-
|
|
90
|
-
queue.run(() => {
|
|
91
|
-
timestamps.push(Date.now());
|
|
92
|
-
});
|
|
93
|
-
queue.run(() => {
|
|
94
|
-
timestamps.push(Date.now());
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
await vi.advanceTimersByTimeAsync(150);
|
|
98
|
-
|
|
99
|
-
expect(timestamps).toHaveLength(2);
|
|
100
|
-
// 두 작업 사이에 정확히 50ms 간격
|
|
101
|
-
expect(timestamps[1] - timestamps[0]).toBe(50);
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
it("gap이 0이면 즉시 다음 작업을 실행한다", async () => {
|
|
105
|
-
const queue = new SerialQueue(0);
|
|
106
|
-
const timestamps: number[] = [];
|
|
107
|
-
|
|
108
|
-
queue.run(() => {
|
|
109
|
-
timestamps.push(Date.now());
|
|
110
|
-
});
|
|
111
|
-
queue.run(() => {
|
|
112
|
-
timestamps.push(Date.now());
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
await vi.advanceTimersByTimeAsync(50);
|
|
116
|
-
|
|
117
|
-
expect(timestamps).toHaveLength(2);
|
|
118
|
-
// 간격이 0
|
|
119
|
-
expect(timestamps[1] - timestamps[0]).toBe(0);
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
it("gap은 기본값 0이다", async () => {
|
|
123
|
-
const queue = new SerialQueue();
|
|
124
|
-
const timestamps: number[] = [];
|
|
125
|
-
|
|
126
|
-
queue.run(() => {
|
|
127
|
-
timestamps.push(Date.now());
|
|
128
|
-
});
|
|
129
|
-
queue.run(() => {
|
|
130
|
-
timestamps.push(Date.now());
|
|
131
|
-
});
|
|
132
|
-
|
|
133
|
-
await vi.advanceTimersByTimeAsync(50);
|
|
134
|
-
|
|
135
|
-
expect(timestamps).toHaveLength(2);
|
|
136
|
-
expect(timestamps[1] - timestamps[0]).toBe(0);
|
|
137
|
-
});
|
|
138
|
-
|
|
139
|
-
it("마지막 작업 후에는 gap을 대기하지 않는다", async () => {
|
|
140
|
-
const queue = new SerialQueue(100);
|
|
141
|
-
const timestamps: number[] = [];
|
|
142
|
-
|
|
143
|
-
queue.run(() => {
|
|
144
|
-
timestamps.push(Date.now());
|
|
145
|
-
});
|
|
146
|
-
queue.run(() => {
|
|
147
|
-
timestamps.push(Date.now());
|
|
148
|
-
});
|
|
149
|
-
|
|
150
|
-
// 충분히 대기 (작업1 + gap100 + 작업2)
|
|
151
|
-
await vi.advanceTimersByTimeAsync(200);
|
|
152
|
-
|
|
153
|
-
// gap이 실제로 적용되었는지 확인 (정확히 100ms)
|
|
154
|
-
expect(timestamps).toHaveLength(2);
|
|
155
|
-
expect(timestamps[1] - timestamps[0]).toBe(100);
|
|
156
|
-
});
|
|
157
|
-
});
|
|
158
|
-
|
|
159
|
-
//#endregion
|
|
160
|
-
|
|
161
|
-
//#region 에러 처리
|
|
162
|
-
|
|
163
|
-
describe("에러 처리", () => {
|
|
164
|
-
it("에러 발생 시 error 이벤트를 발생시킨다", async () => {
|
|
165
|
-
const queue = new SerialQueue();
|
|
166
|
-
const errors: SdError[] = [];
|
|
167
|
-
|
|
168
|
-
queue.on("error", (err) => {
|
|
169
|
-
errors.push(err);
|
|
170
|
-
});
|
|
171
|
-
|
|
172
|
-
queue.run(() => {
|
|
173
|
-
throw new Error("test error");
|
|
174
|
-
});
|
|
175
|
-
|
|
176
|
-
await vi.advanceTimersByTimeAsync(50);
|
|
177
|
-
|
|
178
|
-
expect(errors).toHaveLength(1);
|
|
179
|
-
expect(errors[0]).toBeInstanceOf(SdError);
|
|
180
|
-
expect(errors[0].message).toContain("큐 작업 실행 중 오류 발생");
|
|
181
|
-
expect(errors[0].message).toContain("test error");
|
|
182
|
-
});
|
|
183
|
-
|
|
184
|
-
it("에러가 발생해도 다음 작업은 계속 실행한다", async () => {
|
|
185
|
-
const queue = new SerialQueue();
|
|
186
|
-
const calls: number[] = [];
|
|
187
|
-
const errors: SdError[] = [];
|
|
188
|
-
|
|
189
|
-
// 에러 리스너 추가하여 unhandled rejection 방지
|
|
190
|
-
queue.on("error", (err) => {
|
|
191
|
-
errors.push(err);
|
|
192
|
-
});
|
|
193
|
-
|
|
194
|
-
queue.run(() => {
|
|
195
|
-
calls.push(1);
|
|
196
|
-
throw new Error("error");
|
|
197
|
-
});
|
|
198
|
-
queue.run(() => {
|
|
199
|
-
calls.push(2);
|
|
200
|
-
});
|
|
201
|
-
queue.run(() => {
|
|
202
|
-
calls.push(3);
|
|
203
|
-
});
|
|
204
|
-
|
|
205
|
-
await vi.advanceTimersByTimeAsync(100);
|
|
206
|
-
|
|
207
|
-
expect(calls).toEqual([1, 2, 3]);
|
|
208
|
-
expect(errors).toHaveLength(1);
|
|
209
|
-
});
|
|
210
|
-
|
|
211
|
-
it("여러 작업에서 에러가 발생해도 모두 실행한다", async () => {
|
|
212
|
-
const queue = new SerialQueue();
|
|
213
|
-
const calls: number[] = [];
|
|
214
|
-
const errors: SdError[] = [];
|
|
215
|
-
|
|
216
|
-
queue.on("error", (err) => {
|
|
217
|
-
errors.push(err);
|
|
218
|
-
});
|
|
219
|
-
|
|
220
|
-
queue.run(() => {
|
|
221
|
-
calls.push(1);
|
|
222
|
-
throw new Error("error 1");
|
|
223
|
-
});
|
|
224
|
-
queue.run(() => {
|
|
225
|
-
calls.push(2);
|
|
226
|
-
});
|
|
227
|
-
queue.run(() => {
|
|
228
|
-
calls.push(3);
|
|
229
|
-
throw new Error("error 3");
|
|
230
|
-
});
|
|
231
|
-
|
|
232
|
-
await vi.advanceTimersByTimeAsync(100);
|
|
233
|
-
|
|
234
|
-
expect(calls).toEqual([1, 2, 3]);
|
|
235
|
-
expect(errors).toHaveLength(2);
|
|
236
|
-
expect(errors[0].message).toContain("error 1");
|
|
237
|
-
expect(errors[1].message).toContain("error 3");
|
|
238
|
-
});
|
|
239
|
-
});
|
|
240
|
-
|
|
241
|
-
//#endregion
|
|
242
|
-
|
|
243
|
-
//#region dispose
|
|
244
|
-
|
|
245
|
-
describe("dispose()", () => {
|
|
246
|
-
it("대기 중인 큐를 비운다", async () => {
|
|
247
|
-
const queue = new SerialQueue();
|
|
248
|
-
const calls: number[] = [];
|
|
249
|
-
|
|
250
|
-
// 첫 작업 실행 중
|
|
251
|
-
queue.run(async () => {
|
|
252
|
-
calls.push(1);
|
|
253
|
-
await new Promise((r) => setTimeout(r, 100));
|
|
254
|
-
});
|
|
255
|
-
|
|
256
|
-
// 대기 중인 작업들 추가
|
|
257
|
-
queue.run(() => {
|
|
258
|
-
calls.push(2);
|
|
259
|
-
});
|
|
260
|
-
queue.run(() => {
|
|
261
|
-
calls.push(3);
|
|
262
|
-
});
|
|
263
|
-
|
|
264
|
-
// 첫 작업 시작 후 dispose
|
|
265
|
-
await vi.advanceTimersByTimeAsync(20);
|
|
266
|
-
queue.dispose();
|
|
267
|
-
|
|
268
|
-
// 모든 작업 완료 대기
|
|
269
|
-
await vi.advanceTimersByTimeAsync(150);
|
|
270
|
-
|
|
271
|
-
// 첫 작업만 실행됨 (실행 중인 작업은 완료됨)
|
|
272
|
-
expect(calls).toEqual([1]);
|
|
273
|
-
});
|
|
274
|
-
|
|
275
|
-
it("dispose 후 새 작업은 정상 실행된다", async () => {
|
|
276
|
-
const queue = new SerialQueue();
|
|
277
|
-
const calls: number[] = [];
|
|
278
|
-
|
|
279
|
-
queue.run(() => {
|
|
280
|
-
calls.push(1);
|
|
281
|
-
});
|
|
282
|
-
queue.dispose();
|
|
283
|
-
|
|
284
|
-
// dispose 후 새 작업 추가
|
|
285
|
-
queue.run(() => {
|
|
286
|
-
calls.push(2);
|
|
287
|
-
});
|
|
288
|
-
|
|
289
|
-
await vi.advanceTimersByTimeAsync(50);
|
|
290
|
-
|
|
291
|
-
expect(calls).toContain(2);
|
|
292
|
-
});
|
|
293
|
-
|
|
294
|
-
it("여러 번 호출해도 안전하다", () => {
|
|
295
|
-
const queue = new SerialQueue();
|
|
296
|
-
|
|
297
|
-
// 여러 번 호출해도 에러 없음
|
|
298
|
-
queue.dispose();
|
|
299
|
-
queue.dispose();
|
|
300
|
-
queue.dispose();
|
|
301
|
-
});
|
|
302
|
-
|
|
303
|
-
it("using 문으로 자동 dispose된다", async () => {
|
|
304
|
-
const calls: number[] = [];
|
|
305
|
-
{
|
|
306
|
-
using queue = new SerialQueue();
|
|
307
|
-
queue.run(async () => {
|
|
308
|
-
calls.push(1);
|
|
309
|
-
await new Promise((r) => setTimeout(r, 100));
|
|
310
|
-
});
|
|
311
|
-
queue.run(() => {
|
|
312
|
-
calls.push(2);
|
|
313
|
-
});
|
|
314
|
-
await vi.advanceTimersByTimeAsync(20);
|
|
315
|
-
} // using 블록 종료 시 dispose 자동 호출
|
|
316
|
-
await vi.advanceTimersByTimeAsync(150);
|
|
317
|
-
// 첫 작업(실행 중)은 완료되지만, 대기 중인 작업은 실행되지 않음
|
|
318
|
-
expect(calls).toEqual([1]);
|
|
319
|
-
});
|
|
320
|
-
});
|
|
321
|
-
|
|
322
|
-
//#endregion
|
|
323
|
-
|
|
324
|
-
//#region 동기 함수 지원
|
|
325
|
-
|
|
326
|
-
describe("동기 함수 지원", () => {
|
|
327
|
-
it("동기 함수도 실행할 수 있다", async () => {
|
|
328
|
-
const queue = new SerialQueue();
|
|
329
|
-
const calls: number[] = [];
|
|
330
|
-
|
|
331
|
-
queue.run(() => {
|
|
332
|
-
calls.push(1);
|
|
333
|
-
});
|
|
334
|
-
queue.run(() => {
|
|
335
|
-
calls.push(2);
|
|
336
|
-
});
|
|
337
|
-
|
|
338
|
-
await vi.advanceTimersByTimeAsync(50);
|
|
339
|
-
|
|
340
|
-
expect(calls).toEqual([1, 2]);
|
|
341
|
-
});
|
|
342
|
-
|
|
343
|
-
it("동기/비동기 함수를 혼합해서 사용할 수 있다", async () => {
|
|
344
|
-
const queue = new SerialQueue();
|
|
345
|
-
const calls: number[] = [];
|
|
346
|
-
|
|
347
|
-
queue.run(() => {
|
|
348
|
-
calls.push(1);
|
|
349
|
-
});
|
|
350
|
-
queue.run(async () => {
|
|
351
|
-
await new Promise((r) => setTimeout(r, 10));
|
|
352
|
-
calls.push(2);
|
|
353
|
-
});
|
|
354
|
-
queue.run(() => {
|
|
355
|
-
calls.push(3);
|
|
356
|
-
});
|
|
357
|
-
|
|
358
|
-
await vi.advanceTimersByTimeAsync(100);
|
|
359
|
-
|
|
360
|
-
expect(calls).toEqual([1, 2, 3]);
|
|
361
|
-
});
|
|
362
|
-
});
|
|
363
|
-
|
|
364
|
-
//#endregion
|
|
365
|
-
});
|