@telorun/test 0.2.0 → 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/suite.d.ts +1 -0
- package/dist/suite.js +29 -15
- package/package.json +3 -3
- package/src/suite.ts +29 -15
package/dist/suite.d.ts
CHANGED
|
@@ -14,6 +14,7 @@ export declare const schema: import("@sinclair/typebox").TObject<{
|
|
|
14
14
|
include: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TArray<import("@sinclair/typebox").TString>>;
|
|
15
15
|
exclude: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TArray<import("@sinclair/typebox").TString>>;
|
|
16
16
|
filter: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
17
|
+
concurrency: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TInteger>;
|
|
17
18
|
}>;
|
|
18
19
|
type SuiteManifest = Static<typeof schema>;
|
|
19
20
|
export declare function create(manifest: SuiteManifest, ctx: ResourceContext): Promise<Runnable>;
|
package/dist/suite.js
CHANGED
|
@@ -4,6 +4,7 @@ import * as fs from "fs";
|
|
|
4
4
|
import * as path from "path";
|
|
5
5
|
import { Writable } from "stream";
|
|
6
6
|
import { fileURLToPath } from "url";
|
|
7
|
+
const DEFAULT_CONCURRENCY = 3;
|
|
7
8
|
class BufferedWritable extends Writable {
|
|
8
9
|
chunks = [];
|
|
9
10
|
_write(chunk, _encoding, cb) {
|
|
@@ -24,6 +25,7 @@ export const schema = Type.Object({
|
|
|
24
25
|
include: Type.Optional(Type.Array(Type.String())),
|
|
25
26
|
exclude: Type.Optional(Type.Array(Type.String())),
|
|
26
27
|
filter: Type.Optional(Type.String()),
|
|
28
|
+
concurrency: Type.Optional(Type.Integer({ minimum: 1 })),
|
|
27
29
|
});
|
|
28
30
|
function createColors(stream) {
|
|
29
31
|
const useColor = stream.isTTY ?? false;
|
|
@@ -177,24 +179,36 @@ export async function create(manifest, ctx) {
|
|
|
177
179
|
}
|
|
178
180
|
const singleTest = tests.length === 1;
|
|
179
181
|
const results = [];
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
182
|
+
const requestedConcurrency = manifest.concurrency ?? DEFAULT_CONCURRENCY;
|
|
183
|
+
const concurrency = singleTest ? 1 : Math.max(1, Math.min(requestedConcurrency, tests.length));
|
|
184
|
+
let nextIdx = 0;
|
|
185
|
+
const worker = async () => {
|
|
186
|
+
while (true) {
|
|
187
|
+
const i = nextIdx++;
|
|
188
|
+
if (i >= tests.length)
|
|
189
|
+
return;
|
|
190
|
+
const testPath = tests[i];
|
|
191
|
+
const label = labelFor(testPath, baseDir);
|
|
192
|
+
const result = await runOneTest(testPath, !singleTest, ctx.stdout, ctx.stderr);
|
|
193
|
+
result.label = label;
|
|
194
|
+
results.push(result);
|
|
195
|
+
if (result.passed) {
|
|
196
|
+
ctx.stdout.write(green("PASS") + " " + dim(label) + " " + dim(`(${result.durationMs}ms)`) + "\n");
|
|
192
197
|
}
|
|
193
|
-
|
|
194
|
-
|
|
198
|
+
else {
|
|
199
|
+
// Compose the whole FAIL block into one write so a parallel
|
|
200
|
+
// worker's output can't interleave between the header, captured
|
|
201
|
+
// output, and error line on stderr.
|
|
202
|
+
let block = red("FAIL") + " " + label + " " + dim(`(${result.durationMs}ms)`) + "\n";
|
|
203
|
+
if (result.output)
|
|
204
|
+
block += result.output;
|
|
205
|
+
if (result.error)
|
|
206
|
+
block += dim(` ${result.error}`) + "\n";
|
|
207
|
+
ctx.stderr.write(block);
|
|
195
208
|
}
|
|
196
209
|
}
|
|
197
|
-
}
|
|
210
|
+
};
|
|
211
|
+
await Promise.all(Array.from({ length: concurrency }, () => worker()));
|
|
198
212
|
const passed = results.filter((r) => r.passed);
|
|
199
213
|
const failed = results.filter((r) => !r.passed);
|
|
200
214
|
if (!singleTest) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@telorun/test",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Telo Test module - Test runner for Telo manifests.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"telo",
|
|
@@ -33,8 +33,8 @@
|
|
|
33
33
|
],
|
|
34
34
|
"dependencies": {
|
|
35
35
|
"@sinclair/typebox": "^0.34.48",
|
|
36
|
-
"@telorun/kernel": "0.
|
|
37
|
-
"@telorun/sdk": "0.
|
|
36
|
+
"@telorun/kernel": "0.7.0",
|
|
37
|
+
"@telorun/sdk": "0.7.0"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
40
|
"@types/node": "^20.0.0",
|
package/src/suite.ts
CHANGED
|
@@ -6,6 +6,8 @@ import * as path from "path";
|
|
|
6
6
|
import { Writable } from "stream";
|
|
7
7
|
import { fileURLToPath } from "url";
|
|
8
8
|
|
|
9
|
+
const DEFAULT_CONCURRENCY = 3;
|
|
10
|
+
|
|
9
11
|
class BufferedWritable extends Writable {
|
|
10
12
|
private chunks: Buffer[] = [];
|
|
11
13
|
|
|
@@ -34,6 +36,7 @@ export const schema = Type.Object({
|
|
|
34
36
|
Type.Array(Type.String()),
|
|
35
37
|
),
|
|
36
38
|
filter: Type.Optional(Type.String()),
|
|
39
|
+
concurrency: Type.Optional(Type.Integer({ minimum: 1 })),
|
|
37
40
|
});
|
|
38
41
|
|
|
39
42
|
type SuiteManifest = Static<typeof schema>;
|
|
@@ -219,24 +222,35 @@ export async function create(
|
|
|
219
222
|
const singleTest = tests.length === 1;
|
|
220
223
|
const results: TestResult[] = [];
|
|
221
224
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
const result = await runOneTest(testPath, !singleTest, ctx.stdout, ctx.stderr);
|
|
225
|
-
result.label = label;
|
|
226
|
-
results.push(result);
|
|
225
|
+
const requestedConcurrency = manifest.concurrency ?? DEFAULT_CONCURRENCY;
|
|
226
|
+
const concurrency = singleTest ? 1 : Math.max(1, Math.min(requestedConcurrency, tests.length));
|
|
227
227
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
if (
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
228
|
+
let nextIdx = 0;
|
|
229
|
+
const worker = async () => {
|
|
230
|
+
while (true) {
|
|
231
|
+
const i = nextIdx++;
|
|
232
|
+
if (i >= tests.length) return;
|
|
233
|
+
const testPath = tests[i];
|
|
234
|
+
const label = labelFor(testPath, baseDir);
|
|
235
|
+
const result = await runOneTest(testPath, !singleTest, ctx.stdout, ctx.stderr);
|
|
236
|
+
result.label = label;
|
|
237
|
+
results.push(result);
|
|
238
|
+
|
|
239
|
+
if (result.passed) {
|
|
240
|
+
ctx.stdout.write(green("PASS") + " " + dim(label) + " " + dim(`(${result.durationMs}ms)`) + "\n");
|
|
241
|
+
} else {
|
|
242
|
+
// Compose the whole FAIL block into one write so a parallel
|
|
243
|
+
// worker's output can't interleave between the header, captured
|
|
244
|
+
// output, and error line on stderr.
|
|
245
|
+
let block = red("FAIL") + " " + label + " " + dim(`(${result.durationMs}ms)`) + "\n";
|
|
246
|
+
if (result.output) block += result.output;
|
|
247
|
+
if (result.error) block += dim(` ${result.error}`) + "\n";
|
|
248
|
+
ctx.stderr.write(block);
|
|
237
249
|
}
|
|
238
250
|
}
|
|
239
|
-
}
|
|
251
|
+
};
|
|
252
|
+
|
|
253
|
+
await Promise.all(Array.from({ length: concurrency }, () => worker()));
|
|
240
254
|
|
|
241
255
|
const passed = results.filter((r) => r.passed);
|
|
242
256
|
const failed = results.filter((r) => !r.passed);
|