repterm 0.1.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/api/describe.d.ts +18 -0
- package/dist/api/describe.d.ts.map +1 -0
- package/dist/api/describe.js +33 -0
- package/dist/api/describe.js.map +1 -0
- package/dist/api/expect.d.ts +43 -0
- package/dist/api/expect.d.ts.map +1 -0
- package/dist/api/expect.js +167 -0
- package/dist/api/expect.js.map +1 -0
- package/dist/api/hooks.d.ts +178 -0
- package/dist/api/hooks.d.ts.map +1 -0
- package/dist/api/hooks.js +231 -0
- package/dist/api/hooks.js.map +1 -0
- package/dist/api/steps.d.ts +45 -0
- package/dist/api/steps.d.ts.map +1 -0
- package/dist/api/steps.js +106 -0
- package/dist/api/steps.js.map +1 -0
- package/dist/api/test.d.ts +101 -0
- package/dist/api/test.d.ts.map +1 -0
- package/dist/api/test.js +207 -0
- package/dist/api/test.js.map +1 -0
- package/dist/cli/index.d.ts +7 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +203 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/reporter.d.ts +108 -0
- package/dist/cli/reporter.d.ts.map +1 -0
- package/dist/cli/reporter.js +368 -0
- package/dist/cli/reporter.js.map +1 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +24 -0
- package/dist/index.js.map +1 -0
- package/dist/plugin/index.d.ts +47 -0
- package/dist/plugin/index.d.ts.map +1 -0
- package/dist/plugin/index.js +86 -0
- package/dist/plugin/index.js.map +1 -0
- package/dist/plugin/withPlugins.d.ts +71 -0
- package/dist/plugin/withPlugins.d.ts.map +1 -0
- package/dist/plugin/withPlugins.js +101 -0
- package/dist/plugin/withPlugins.js.map +1 -0
- package/dist/recording/recorder.d.ts +45 -0
- package/dist/recording/recorder.d.ts.map +1 -0
- package/dist/recording/recorder.js +97 -0
- package/dist/recording/recorder.js.map +1 -0
- package/dist/runner/artifacts.d.ts +39 -0
- package/dist/runner/artifacts.d.ts.map +1 -0
- package/dist/runner/artifacts.js +59 -0
- package/dist/runner/artifacts.js.map +1 -0
- package/dist/runner/config.d.ts +46 -0
- package/dist/runner/config.d.ts.map +1 -0
- package/dist/runner/config.js +65 -0
- package/dist/runner/config.js.map +1 -0
- package/dist/runner/filter.d.ts +26 -0
- package/dist/runner/filter.d.ts.map +1 -0
- package/dist/runner/filter.js +88 -0
- package/dist/runner/filter.js.map +1 -0
- package/dist/runner/loader.d.ts +32 -0
- package/dist/runner/loader.d.ts.map +1 -0
- package/dist/runner/loader.js +252 -0
- package/dist/runner/loader.js.map +1 -0
- package/dist/runner/models.d.ts +261 -0
- package/dist/runner/models.d.ts.map +1 -0
- package/dist/runner/models.js +5 -0
- package/dist/runner/models.js.map +1 -0
- package/dist/runner/runner.d.ts +36 -0
- package/dist/runner/runner.d.ts.map +1 -0
- package/dist/runner/runner.js +216 -0
- package/dist/runner/runner.js.map +1 -0
- package/dist/runner/scheduler.d.ts +59 -0
- package/dist/runner/scheduler.d.ts.map +1 -0
- package/dist/runner/scheduler.js +157 -0
- package/dist/runner/scheduler.js.map +1 -0
- package/dist/runner/worker-runner.d.ts +6 -0
- package/dist/runner/worker-runner.d.ts.map +1 -0
- package/dist/runner/worker-runner.js +51 -0
- package/dist/runner/worker-runner.js.map +1 -0
- package/dist/runner/worker.d.ts +54 -0
- package/dist/runner/worker.d.ts.map +1 -0
- package/dist/runner/worker.js +112 -0
- package/dist/runner/worker.js.map +1 -0
- package/dist/terminal/session.d.ts +56 -0
- package/dist/terminal/session.d.ts.map +1 -0
- package/dist/terminal/session.js +126 -0
- package/dist/terminal/session.js.map +1 -0
- package/dist/terminal/terminal.d.ts +284 -0
- package/dist/terminal/terminal.d.ts.map +1 -0
- package/dist/terminal/terminal.js +1167 -0
- package/dist/terminal/terminal.js.map +1 -0
- package/dist/utils/dependencies.d.ts +19 -0
- package/dist/utils/dependencies.d.ts.map +1 -0
- package/dist/utils/dependencies.js +58 -0
- package/dist/utils/dependencies.js.map +1 -0
- package/dist/utils/timing.d.ts +55 -0
- package/dist/utils/timing.d.ts.map +1 -0
- package/dist/utils/timing.js +87 -0
- package/dist/utils/timing.js.map +1 -0
- package/package.json +43 -0
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Single-runner execution pipeline
|
|
3
|
+
* Executes test suites and cases, manages lifecycle with onion execution model
|
|
4
|
+
*/
|
|
5
|
+
import { createTerminal } from '../terminal/terminal.js';
|
|
6
|
+
import { hooksRegistry } from '../api/hooks.js';
|
|
7
|
+
import { clearSteps } from '../api/steps.js';
|
|
8
|
+
/**
|
|
9
|
+
* Extract parameter names from a test function
|
|
10
|
+
* Parses destructured parameters like ({ terminal, tmpDir }) => ...
|
|
11
|
+
*/
|
|
12
|
+
function getTestFunctionParameters(fn) {
|
|
13
|
+
const fnStr = fn.toString();
|
|
14
|
+
// Match destructured object parameter: ({ param1, param2, ... })
|
|
15
|
+
const match = fnStr.match(/\(\s*\{\s*([^}]*)\s*\}/);
|
|
16
|
+
if (!match)
|
|
17
|
+
return [];
|
|
18
|
+
return match[1]
|
|
19
|
+
.split(',')
|
|
20
|
+
.map((p) => {
|
|
21
|
+
// Handle cases like "param: Type" or "param = default" or just "param"
|
|
22
|
+
const trimmed = p.trim();
|
|
23
|
+
const paramName = trimmed.split(/[:\s=]/)[0].trim();
|
|
24
|
+
return paramName;
|
|
25
|
+
})
|
|
26
|
+
.filter(Boolean);
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Execute tests in a suite (without lifecycle hooks)
|
|
30
|
+
* This is the internal function for running tests only
|
|
31
|
+
*/
|
|
32
|
+
async function runTestsInSuite(suite, options, inheritedContext = {}) {
|
|
33
|
+
const results = [];
|
|
34
|
+
for (const testCase of suite.tests) {
|
|
35
|
+
const result = await runTest(testCase, suite, options, inheritedContext);
|
|
36
|
+
results.push(result);
|
|
37
|
+
}
|
|
38
|
+
return results;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Execute a single test suite with lifecycle hooks (onion model)
|
|
42
|
+
*
|
|
43
|
+
* Execution order:
|
|
44
|
+
* 1. Run beforeAll hooks for this suite
|
|
45
|
+
* 2. Merge beforeAll context with inherited context
|
|
46
|
+
* 3. Run tests in this suite
|
|
47
|
+
* 4. Recursively run child suites
|
|
48
|
+
* 5. Run afterAll hooks for this suite
|
|
49
|
+
*/
|
|
50
|
+
export async function runSuite(suite, options, inheritedContext = {}) {
|
|
51
|
+
const results = [];
|
|
52
|
+
let suiteContext = { ...inheritedContext };
|
|
53
|
+
try {
|
|
54
|
+
// 1. Run beforeAll hooks for this suite
|
|
55
|
+
suiteContext = await hooksRegistry.runBeforeAllFor(suite, inheritedContext);
|
|
56
|
+
// 2. Run tests in this suite with the merged context
|
|
57
|
+
const testResults = await runTestsInSuite(suite, options, suiteContext);
|
|
58
|
+
results.push(...testResults);
|
|
59
|
+
// 3. Recursively run child suites with the merged context
|
|
60
|
+
if (suite.suites && suite.suites.length > 0) {
|
|
61
|
+
for (const childSuite of suite.suites) {
|
|
62
|
+
const childResults = await runSuite(childSuite, options, suiteContext);
|
|
63
|
+
results.push(...childResults);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
finally {
|
|
68
|
+
// 4. Run afterAll hooks for this suite (always, even if tests failed)
|
|
69
|
+
try {
|
|
70
|
+
await hooksRegistry.runAfterAllFor(suite, suiteContext);
|
|
71
|
+
}
|
|
72
|
+
catch (hookError) {
|
|
73
|
+
console.error(`Error in afterAll hook for suite "${suite.name}":`, hookError);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return results;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* 获取从 suite 继承的 record 配置
|
|
80
|
+
*/
|
|
81
|
+
function getInheritedRecordConfig(suite) {
|
|
82
|
+
if (suite.options?.record !== undefined) {
|
|
83
|
+
return suite.options.record;
|
|
84
|
+
}
|
|
85
|
+
if (suite.parent) {
|
|
86
|
+
return getInheritedRecordConfig(suite.parent);
|
|
87
|
+
}
|
|
88
|
+
return undefined;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Execute a single test case
|
|
92
|
+
*/
|
|
93
|
+
export async function runTest(testCase, suite, options, inheritedContext = {}) {
|
|
94
|
+
// Build suite path
|
|
95
|
+
const suitePath = [suite.name];
|
|
96
|
+
let parent = suite.parent;
|
|
97
|
+
while (parent && parent.id !== 'default' && !parent.id.startsWith('file-') && !parent.id.startsWith('dir-')) {
|
|
98
|
+
suitePath.unshift(parent.name);
|
|
99
|
+
parent = parent.parent;
|
|
100
|
+
}
|
|
101
|
+
const startTime = Date.now();
|
|
102
|
+
const { config, artifactManager, onResult, onTestStart } = options;
|
|
103
|
+
// 确定执行模式:
|
|
104
|
+
// 1. testRecordConfig: 测试级别或 suite 级别的 record 配置
|
|
105
|
+
// 2. cliRecordMode: CLI --record 标志
|
|
106
|
+
// 3. shouldRecord: 只有 CLI 和 test 都启用时才完整录制(asciinema + tmux + 打字效果)
|
|
107
|
+
// 4. shouldUsePtyOnly: test 启用但 CLI 未启用时使用 PTY-only(无录制、无打字效果)
|
|
108
|
+
const testRecordConfig = testCase.options?.record ?? getInheritedRecordConfig(suite);
|
|
109
|
+
const cliRecordMode = config.record?.enabled ?? false;
|
|
110
|
+
const shouldRecord = cliRecordMode && testRecordConfig; // 完整录制模式
|
|
111
|
+
const shouldUsePtyOnly = testRecordConfig && !cliRecordMode; // PTY-only 模式
|
|
112
|
+
// Get recording path for this test (only in recording mode)
|
|
113
|
+
const recordingPath = shouldRecord
|
|
114
|
+
? artifactManager.getCastPath(testCase.id)
|
|
115
|
+
: undefined;
|
|
116
|
+
// 在 hooks 运行前通知测试即将开始,确保 suite 名称先于 hook 输出打印
|
|
117
|
+
onTestStart?.({ suitePath, testName: testCase.name });
|
|
118
|
+
// Create terminal for test
|
|
119
|
+
// Use user's actual terminal size (like simple-example.js)
|
|
120
|
+
const terminal = createTerminal({
|
|
121
|
+
cols: process.stdout.columns || 120,
|
|
122
|
+
rows: process.stdout.rows || 40,
|
|
123
|
+
recording: shouldRecord,
|
|
124
|
+
ptyOnly: shouldUsePtyOnly,
|
|
125
|
+
recordingPath,
|
|
126
|
+
promptLineCount: config.terminal?.promptLineCount,
|
|
127
|
+
});
|
|
128
|
+
// Build initial test context with inherited context from beforeAll hooks
|
|
129
|
+
let context = {
|
|
130
|
+
terminal,
|
|
131
|
+
...inheritedContext,
|
|
132
|
+
...testCase.fixtures,
|
|
133
|
+
};
|
|
134
|
+
// Track which fixtures were actually executed (for cleanup)
|
|
135
|
+
let executedFixtures = new Set();
|
|
136
|
+
try {
|
|
137
|
+
// Extract required fixtures from test function parameters
|
|
138
|
+
const requiredFixtures = new Set(getTestFunctionParameters(testCase.fn));
|
|
139
|
+
// Run beforeEach hooks only for requested fixtures (lazy execution)
|
|
140
|
+
const hookResult = await hooksRegistry.runBeforeEachFor(context, suite, requiredFixtures);
|
|
141
|
+
context = hookResult.context;
|
|
142
|
+
executedFixtures = hookResult.executedFixtures;
|
|
143
|
+
// Set timeout
|
|
144
|
+
const timeout = testCase.timeout ?? config.timeouts.testMs;
|
|
145
|
+
await Promise.race([
|
|
146
|
+
testCase.fn(context),
|
|
147
|
+
new Promise((_, reject) => setTimeout(() => reject(new Error(`Test timeout after ${timeout}ms`)), timeout)),
|
|
148
|
+
]);
|
|
149
|
+
// Test passed
|
|
150
|
+
const durationMs = Date.now() - startTime;
|
|
151
|
+
const result = {
|
|
152
|
+
id: `result-${testCase.id}`,
|
|
153
|
+
suiteId: suite.id,
|
|
154
|
+
caseId: testCase.id,
|
|
155
|
+
suiteName: suite.name,
|
|
156
|
+
suitePath,
|
|
157
|
+
caseName: testCase.name,
|
|
158
|
+
status: 'pass',
|
|
159
|
+
durationMs,
|
|
160
|
+
artifacts: [],
|
|
161
|
+
recordingPath,
|
|
162
|
+
};
|
|
163
|
+
onResult?.(result);
|
|
164
|
+
return result;
|
|
165
|
+
}
|
|
166
|
+
catch (error) {
|
|
167
|
+
// Test failed
|
|
168
|
+
const durationMs = Date.now() - startTime;
|
|
169
|
+
const result = {
|
|
170
|
+
id: `result-${testCase.id}`,
|
|
171
|
+
suiteId: suite.id,
|
|
172
|
+
caseId: testCase.id,
|
|
173
|
+
suiteName: suite.name,
|
|
174
|
+
suitePath,
|
|
175
|
+
caseName: testCase.name,
|
|
176
|
+
status: 'fail',
|
|
177
|
+
durationMs,
|
|
178
|
+
error: {
|
|
179
|
+
message: error.message,
|
|
180
|
+
stack: error.stack,
|
|
181
|
+
expected: error.expected,
|
|
182
|
+
actual: error.actual,
|
|
183
|
+
commandLogs: terminal.getCommandLogs(),
|
|
184
|
+
},
|
|
185
|
+
artifacts: [],
|
|
186
|
+
recordingPath,
|
|
187
|
+
};
|
|
188
|
+
onResult?.(result);
|
|
189
|
+
return result;
|
|
190
|
+
}
|
|
191
|
+
finally {
|
|
192
|
+
// Cleanup
|
|
193
|
+
clearSteps();
|
|
194
|
+
// Run afterEach hooks only for fixtures that were actually executed
|
|
195
|
+
try {
|
|
196
|
+
await hooksRegistry.runAfterEachFor(context, suite, executedFixtures);
|
|
197
|
+
}
|
|
198
|
+
catch (hookError) {
|
|
199
|
+
console.error('Error in afterEach hook:', hookError);
|
|
200
|
+
}
|
|
201
|
+
// Cleanup terminal
|
|
202
|
+
await terminal.close();
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Execute all test suites with lifecycle hooks
|
|
207
|
+
*/
|
|
208
|
+
export async function runAllSuites(suites, options) {
|
|
209
|
+
const allResults = [];
|
|
210
|
+
for (const suite of suites) {
|
|
211
|
+
const results = await runSuite(suite, options);
|
|
212
|
+
allResults.push(...results);
|
|
213
|
+
}
|
|
214
|
+
return allResults;
|
|
215
|
+
}
|
|
216
|
+
//# sourceMappingURL=runner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runner.js","sourceRoot":"","sources":["../../src/runner/runner.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Scheduler + aggregation for parallel test execution
|
|
3
|
+
* Distributes tests across workers and aggregates results
|
|
4
|
+
*/
|
|
5
|
+
import type { TestSuite, RunResult } from './models.js';
|
|
6
|
+
import type { RunConfig } from './config.js';
|
|
7
|
+
export interface SchedulerOptions {
|
|
8
|
+
config: RunConfig;
|
|
9
|
+
artifactBaseDir: string;
|
|
10
|
+
onResult?: (result: RunResult) => void;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Parallel test scheduler
|
|
14
|
+
*/
|
|
15
|
+
export declare class Scheduler {
|
|
16
|
+
private options;
|
|
17
|
+
private workers;
|
|
18
|
+
private results;
|
|
19
|
+
private pendingSuites;
|
|
20
|
+
private activeSuites;
|
|
21
|
+
constructor(options: SchedulerOptions);
|
|
22
|
+
/**
|
|
23
|
+
* Run test suites in parallel across workers
|
|
24
|
+
*/
|
|
25
|
+
run(suites: TestSuite[]): Promise<RunResult[]>;
|
|
26
|
+
/**
|
|
27
|
+
* Initialize worker pool
|
|
28
|
+
*/
|
|
29
|
+
private initWorkers;
|
|
30
|
+
/**
|
|
31
|
+
* Wait for all workers to signal ready
|
|
32
|
+
*/
|
|
33
|
+
private waitForWorkers;
|
|
34
|
+
/**
|
|
35
|
+
* Distribute suites to available workers
|
|
36
|
+
*/
|
|
37
|
+
private distributeSuites;
|
|
38
|
+
/**
|
|
39
|
+
* Handle worker completion
|
|
40
|
+
*/
|
|
41
|
+
private handleWorkerDone;
|
|
42
|
+
/**
|
|
43
|
+
* Handle worker error
|
|
44
|
+
*/
|
|
45
|
+
private handleWorkerError;
|
|
46
|
+
/**
|
|
47
|
+
* Wait for all work to complete
|
|
48
|
+
*/
|
|
49
|
+
private waitForCompletion;
|
|
50
|
+
/**
|
|
51
|
+
* Cleanup worker pool
|
|
52
|
+
*/
|
|
53
|
+
private cleanupWorkers;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Create a scheduler
|
|
57
|
+
*/
|
|
58
|
+
export declare function createScheduler(options: SchedulerOptions): Scheduler;
|
|
59
|
+
//# sourceMappingURL=scheduler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scheduler.d.ts","sourceRoot":"","sources":["../../src/runner/scheduler.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAG7C,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,SAAS,CAAC;IAClB,eAAe,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,SAAS,KAAK,IAAI,CAAC;CACxC;AAED;;GAEG;AACH,qBAAa,SAAS;IACpB,OAAO,CAAC,OAAO,CAAmB;IAClC,OAAO,CAAC,OAAO,CAAgB;IAC/B,OAAO,CAAC,OAAO,CAAmB;IAClC,OAAO,CAAC,aAAa,CAAmB;IACxC,OAAO,CAAC,YAAY,CAAK;gBAEb,OAAO,EAAE,gBAAgB;IAIrC;;OAEG;IACG,GAAG,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC;IA4BpD;;OAEG;YACW,WAAW;IA6BzB;;OAEG;IACH,OAAO,CAAC,cAAc;IAgBtB;;OAEG;YACW,gBAAgB;IAgB9B;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAcxB;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAQzB;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAazB;;OAEG;IACH,OAAO,CAAC,cAAc;CAMvB;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,gBAAgB,GAAG,SAAS,CAEpE"}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Scheduler + aggregation for parallel test execution
|
|
3
|
+
* Distributes tests across workers and aggregates results
|
|
4
|
+
*/
|
|
5
|
+
import { createWorker } from './worker.js';
|
|
6
|
+
/**
|
|
7
|
+
* Parallel test scheduler
|
|
8
|
+
*/
|
|
9
|
+
export class Scheduler {
|
|
10
|
+
options;
|
|
11
|
+
workers = [];
|
|
12
|
+
results = [];
|
|
13
|
+
pendingSuites = [];
|
|
14
|
+
activeSuites = 0;
|
|
15
|
+
constructor(options) {
|
|
16
|
+
this.options = options;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Run test suites in parallel across workers
|
|
20
|
+
*/
|
|
21
|
+
async run(suites) {
|
|
22
|
+
const workerCount = this.options.config.parallel.workers;
|
|
23
|
+
if (workerCount === 1) {
|
|
24
|
+
// Single worker mode - run sequentially (already handled by caller)
|
|
25
|
+
throw new Error('Scheduler should not be used for single worker');
|
|
26
|
+
}
|
|
27
|
+
// Initialize workers
|
|
28
|
+
await this.initWorkers(workerCount);
|
|
29
|
+
// Queue all suites
|
|
30
|
+
this.pendingSuites = [...suites];
|
|
31
|
+
this.results = [];
|
|
32
|
+
this.activeSuites = 0;
|
|
33
|
+
// Start distributing work
|
|
34
|
+
await this.distributeSuites();
|
|
35
|
+
// Wait for all work to complete
|
|
36
|
+
await this.waitForCompletion();
|
|
37
|
+
// Cleanup workers
|
|
38
|
+
this.cleanupWorkers();
|
|
39
|
+
return this.results;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Initialize worker pool
|
|
43
|
+
*/
|
|
44
|
+
async initWorkers(count) {
|
|
45
|
+
for (let i = 0; i < count; i++) {
|
|
46
|
+
const worker = createWorker({
|
|
47
|
+
workerId: i,
|
|
48
|
+
config: this.options.config,
|
|
49
|
+
artifactBaseDir: this.options.artifactBaseDir,
|
|
50
|
+
});
|
|
51
|
+
// Set up event handlers
|
|
52
|
+
worker.on('done', (results) => {
|
|
53
|
+
this.handleWorkerDone(worker, results);
|
|
54
|
+
});
|
|
55
|
+
worker.on('result', (result) => {
|
|
56
|
+
this.options.onResult?.(result);
|
|
57
|
+
});
|
|
58
|
+
worker.on('error', (error) => {
|
|
59
|
+
this.handleWorkerError(worker, error);
|
|
60
|
+
});
|
|
61
|
+
this.workers.push(worker);
|
|
62
|
+
worker.start();
|
|
63
|
+
}
|
|
64
|
+
// Wait for all workers to be ready
|
|
65
|
+
await this.waitForWorkers();
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Wait for all workers to signal ready
|
|
69
|
+
*/
|
|
70
|
+
waitForWorkers() {
|
|
71
|
+
return new Promise((resolve) => {
|
|
72
|
+
let readyCount = 0;
|
|
73
|
+
const targetCount = this.workers.length;
|
|
74
|
+
for (const worker of this.workers) {
|
|
75
|
+
worker.once('ready', () => {
|
|
76
|
+
readyCount++;
|
|
77
|
+
if (readyCount === targetCount) {
|
|
78
|
+
resolve();
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Distribute suites to available workers
|
|
86
|
+
*/
|
|
87
|
+
async distributeSuites() {
|
|
88
|
+
for (const worker of this.workers) {
|
|
89
|
+
if (this.pendingSuites.length === 0) {
|
|
90
|
+
break;
|
|
91
|
+
}
|
|
92
|
+
if (!worker.isBusy()) {
|
|
93
|
+
const suite = this.pendingSuites.shift();
|
|
94
|
+
if (suite) {
|
|
95
|
+
this.activeSuites++;
|
|
96
|
+
worker.runSuite(suite);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Handle worker completion
|
|
103
|
+
*/
|
|
104
|
+
handleWorkerDone(worker, results) {
|
|
105
|
+
this.results.push(...results);
|
|
106
|
+
this.activeSuites--;
|
|
107
|
+
// Assign next suite if available
|
|
108
|
+
if (this.pendingSuites.length > 0) {
|
|
109
|
+
const suite = this.pendingSuites.shift();
|
|
110
|
+
if (suite) {
|
|
111
|
+
this.activeSuites++;
|
|
112
|
+
worker.runSuite(suite);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Handle worker error
|
|
118
|
+
*/
|
|
119
|
+
handleWorkerError(worker, error) {
|
|
120
|
+
console.error(`Worker ${worker.getWorkerId()} error:`, error);
|
|
121
|
+
this.activeSuites--;
|
|
122
|
+
// Try to assign next suite to another worker
|
|
123
|
+
this.distributeSuites();
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Wait for all work to complete
|
|
127
|
+
*/
|
|
128
|
+
waitForCompletion() {
|
|
129
|
+
return new Promise((resolve) => {
|
|
130
|
+
const checkComplete = () => {
|
|
131
|
+
if (this.activeSuites === 0 && this.pendingSuites.length === 0) {
|
|
132
|
+
resolve();
|
|
133
|
+
}
|
|
134
|
+
else {
|
|
135
|
+
setTimeout(checkComplete, 100);
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
checkComplete();
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Cleanup worker pool
|
|
143
|
+
*/
|
|
144
|
+
cleanupWorkers() {
|
|
145
|
+
for (const worker of this.workers) {
|
|
146
|
+
worker.stop();
|
|
147
|
+
}
|
|
148
|
+
this.workers = [];
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Create a scheduler
|
|
153
|
+
*/
|
|
154
|
+
export function createScheduler(options) {
|
|
155
|
+
return new Scheduler(options);
|
|
156
|
+
}
|
|
157
|
+
//# sourceMappingURL=scheduler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scheduler.js","sourceRoot":"","sources":["../../src/runner/scheduler.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"worker-runner.d.ts","sourceRoot":"","sources":["../../src/runner/worker-runner.ts"],"names":[],"mappings":"AAAA;;;GAGG"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Worker runner script
|
|
3
|
+
* Executed in worker process to run tests
|
|
4
|
+
*/
|
|
5
|
+
import { runSuite } from './runner.js';
|
|
6
|
+
import { ArtifactManager } from './artifacts.js';
|
|
7
|
+
/**
|
|
8
|
+
* Handle messages from parent process
|
|
9
|
+
*/
|
|
10
|
+
process.on('message', async (message) => {
|
|
11
|
+
if (message.type === 'run' && message.data) {
|
|
12
|
+
try {
|
|
13
|
+
const { suite, config, artifactBaseDir } = message.data;
|
|
14
|
+
// Create artifact manager for this worker
|
|
15
|
+
const artifactManager = new ArtifactManager({
|
|
16
|
+
baseDir: artifactBaseDir,
|
|
17
|
+
runId: `worker-${process.pid}-${Date.now()}`,
|
|
18
|
+
});
|
|
19
|
+
artifactManager.init();
|
|
20
|
+
// Run the suite
|
|
21
|
+
const results = await runSuite(suite, {
|
|
22
|
+
config,
|
|
23
|
+
artifactManager,
|
|
24
|
+
onResult: (result) => {
|
|
25
|
+
process.send?.({
|
|
26
|
+
type: 'result',
|
|
27
|
+
data: result,
|
|
28
|
+
});
|
|
29
|
+
},
|
|
30
|
+
});
|
|
31
|
+
// Send results back to parent
|
|
32
|
+
process.send?.({
|
|
33
|
+
type: 'done',
|
|
34
|
+
data: results,
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
catch (error) {
|
|
38
|
+
// Send error back to parent
|
|
39
|
+
process.send?.({
|
|
40
|
+
type: 'error',
|
|
41
|
+
data: {
|
|
42
|
+
message: error.message,
|
|
43
|
+
stack: error.stack,
|
|
44
|
+
},
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
// Signal ready
|
|
50
|
+
process.send?.({ type: 'ready' });
|
|
51
|
+
//# sourceMappingURL=worker-runner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"worker-runner.js","sourceRoot":"","sources":["../../src/runner/worker-runner.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAWjD;;GAEG;AACH,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,KAAK,EAAE,OAAsB,EAAE,EAAE;IACrD,IAAI,OAAO,CAAC,IAAI,KAAK,KAAK,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;QAC3C,IAAI,CAAC;YACH,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,eAAe,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;YAExD,0CAA0C;YAC1C,MAAM,eAAe,GAAG,IAAI,eAAe,CAAC;gBAC1C,OAAO,EAAE,eAAe;gBACxB,KAAK,EAAE,UAAU,OAAO,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,EAAE,EAAE;aAC7C,CAAC,CAAC;YACH,eAAe,CAAC,IAAI,EAAE,CAAC;YAEvB,gBAAgB;YAChB,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,KAAK,EAAE;gBACpC,MAAM;gBACN,eAAe;gBACf,QAAQ,EAAE,CAAC,MAAM,EAAE,EAAE;oBACnB,OAAO,CAAC,IAAI,EAAE,CAAC;wBACb,IAAI,EAAE,QAAQ;wBACd,IAAI,EAAE,MAAM;qBACb,CAAC,CAAC;gBACL,CAAC;aACF,CAAC,CAAC;YAEH,8BAA8B;YAC9B,OAAO,CAAC,IAAI,EAAE,CAAC;gBACb,IAAI,EAAE,MAAM;gBACZ,IAAI,EAAE,OAAO;aACd,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,4BAA4B;YAC5B,OAAO,CAAC,IAAI,EAAE,CAAC;gBACb,IAAI,EAAE,OAAO;gBACb,IAAI,EAAE;oBACJ,OAAO,EAAG,KAAe,CAAC,OAAO;oBACjC,KAAK,EAAG,KAAe,CAAC,KAAK;iBAC9B;aACF,CAAC,CAAC;QACL,CAAC;IACH,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,eAAe;AACf,OAAO,CAAC,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Worker process runner for parallel test execution
|
|
3
|
+
* Runs tests in isolated worker processes
|
|
4
|
+
*/
|
|
5
|
+
import type { TestSuite } from './models.js';
|
|
6
|
+
import type { RunConfig } from './config.js';
|
|
7
|
+
import { EventEmitter } from 'events';
|
|
8
|
+
export interface WorkerConfig {
|
|
9
|
+
workerId: number;
|
|
10
|
+
config: RunConfig;
|
|
11
|
+
artifactBaseDir: string;
|
|
12
|
+
}
|
|
13
|
+
export interface WorkerMessage {
|
|
14
|
+
type: 'ready' | 'result' | 'error' | 'done';
|
|
15
|
+
data?: unknown;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Worker process manager
|
|
19
|
+
*/
|
|
20
|
+
export declare class Worker extends EventEmitter {
|
|
21
|
+
private process;
|
|
22
|
+
private config;
|
|
23
|
+
private busy;
|
|
24
|
+
constructor(config: WorkerConfig);
|
|
25
|
+
/**
|
|
26
|
+
* Start the worker process
|
|
27
|
+
*/
|
|
28
|
+
start(): void;
|
|
29
|
+
/**
|
|
30
|
+
* Run a test suite in the worker
|
|
31
|
+
*/
|
|
32
|
+
runSuite(suite: TestSuite): void;
|
|
33
|
+
/**
|
|
34
|
+
* Stop the worker
|
|
35
|
+
*/
|
|
36
|
+
stop(): void;
|
|
37
|
+
/**
|
|
38
|
+
* Check if worker is busy
|
|
39
|
+
*/
|
|
40
|
+
isBusy(): boolean;
|
|
41
|
+
/**
|
|
42
|
+
* Get worker ID
|
|
43
|
+
*/
|
|
44
|
+
getWorkerId(): number;
|
|
45
|
+
/**
|
|
46
|
+
* Handle message from worker
|
|
47
|
+
*/
|
|
48
|
+
private handleMessage;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Create a worker
|
|
52
|
+
*/
|
|
53
|
+
export declare function createWorker(config: WorkerConfig): Worker;
|
|
54
|
+
//# sourceMappingURL=worker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"worker.d.ts","sourceRoot":"","sources":["../../src/runner/worker.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AAEtC,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,SAAS,CAAC;IAClB,eAAe,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,OAAO,GAAG,QAAQ,GAAG,OAAO,GAAG,MAAM,CAAC;IAC5C,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED;;GAEG;AACH,qBAAa,MAAO,SAAQ,YAAY;IACtC,OAAO,CAAC,OAAO,CAA6B;IAC5C,OAAO,CAAC,MAAM,CAAe;IAC7B,OAAO,CAAC,IAAI,CAAS;gBAET,MAAM,EAAE,YAAY;IAKhC;;OAEG;IACH,KAAK,IAAI,IAAI;IA0Bb;;OAEG;IACH,QAAQ,CAAC,KAAK,EAAE,SAAS,GAAG,IAAI;IAsBhC;;OAEG;IACH,IAAI,IAAI,IAAI;IAOZ;;OAEG;IACH,MAAM,IAAI,OAAO;IAIjB;;OAEG;IACH,WAAW,IAAI,MAAM;IAIrB;;OAEG;IACH,OAAO,CAAC,aAAa;CAqBtB;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,YAAY,GAAG,MAAM,CAEzD"}
|