Merge branch 'master' into opt/win-ssache
Project CI / AI game creator shell Rust shard 1/4 (pull_request) Successful in 3m50s
Project CI / AI game creator shell Rust shard 2/4 (pull_request) Successful in 3m33s
Project CI / AI game creator shell Rust smoke (pull_request) Successful in 2m0s
Project CI / AI game creator shell Rust crates (pull_request) Successful in 2m13s
Project CI / AI game creator shell Rust shard 3/4 (pull_request) Successful in 4m15s
Project CI / AI game creator shell Rust shard 4/4 (pull_request) Successful in 4m25s
Project CI / Frontend tests (pull_request) Successful in 3m59s
Project CI / Repository checks (pull_request) Successful in 3m18s
Project CI / Native shell tests (pull_request) Successful in 6m14s
Project CI / Backend tests (pull_request) Successful in 7m6s
Project CI / AI game creator shell web tests (pull_request) Successful in 2m57s
Project CI / AI game creator shell Rust shard 1/4 (pull_request) Successful in 3m50s
Project CI / AI game creator shell Rust shard 2/4 (pull_request) Successful in 3m33s
Project CI / AI game creator shell Rust smoke (pull_request) Successful in 2m0s
Project CI / AI game creator shell Rust crates (pull_request) Successful in 2m13s
Project CI / AI game creator shell Rust shard 3/4 (pull_request) Successful in 4m15s
Project CI / AI game creator shell Rust shard 4/4 (pull_request) Successful in 4m25s
Project CI / Frontend tests (pull_request) Successful in 3m59s
Project CI / Repository checks (pull_request) Successful in 3m18s
Project CI / Native shell tests (pull_request) Successful in 6m14s
Project CI / Backend tests (pull_request) Successful in 7m6s
Project CI / AI game creator shell web tests (pull_request) Successful in 2m57s
This commit is contained in:
@@ -6,18 +6,25 @@
|
||||
// **同一进程内**互相干扰(见 development-workflow 的 Tauri suite 单线程口径)。代价是
|
||||
// 整套用例串行跑满 507 秒,占掉 CI 上 `AI game creator shell Rust tests` job 的大头。
|
||||
//
|
||||
// 这里保留「片内串行」的既有口径,只把用例集合切成 N 片、让每片在**独立进程**里并行:
|
||||
// 当年线程并行的两个根因(进程内全局锁、异步终态)在多进程下不存在,每片还会拿到自己的
|
||||
// TMPDIR,避免 tempfile 目录互相踩。片并集必须等于全集、且不得重复,数量不符即失败,
|
||||
// 防止分片规则改动后静默漏跑。
|
||||
// 这里保留「片内串行」的既有口径,只把用例集合切成 N 片:
|
||||
// - CI 用 `--shard-index=<i>` 让**每个 job 只跑一片**,靠多个 job 并发把整套用例摊开;
|
||||
// - 本地不传 `--shard-index` 时把 N 片放进 N 个**独立进程**并行(--concurrency 可调),
|
||||
// 保留一条命令跑全量的入口。
|
||||
// 片并集必须等于全集、且不得重复,数量不符即失败,防止分片规则改动后静默漏跑;该校验
|
||||
// 与「只跑一片」无关,因此在每个 job 上都会执行。
|
||||
//
|
||||
// 注意:同一容器内多进程并行这套用例(共享 HOME、target、固定临时路径)实测会互相拖慢,
|
||||
// 比串行还慢,所以 CI 走「一个 job 一片」而不是单 job 内并行。
|
||||
//
|
||||
// 用法:
|
||||
// node scripts/run-rust-shell-test-shards.mjs [--shards=4] [--concurrency=4]
|
||||
// node scripts/run-rust-shell-test-shards.mjs --shards=4 --shard-index=2 # CI:只跑第 2 片
|
||||
// node scripts/run-rust-shell-test-shards.mjs [--shards=4] [--concurrency=4] # 本地:全量
|
||||
// node scripts/run-rust-shell-test-shards.mjs --manifest=<Cargo.toml> --target-kind=lib --no-locked
|
||||
//
|
||||
// 参数:
|
||||
// --shards=<n> 分片数,默认 4
|
||||
// --concurrency=<n> 同时运行的片数,默认等于分片数
|
||||
// --shard-index=<i> 只跑第 i 片(1..shards);不传则跑全部分片
|
||||
// --concurrency=<n> 同时运行的片数,默认等于分片数;--shard-index 时恒为 1
|
||||
// --manifest=<path> Cargo.toml,默认 ../src-tauri/Cargo.toml(相对本脚本)
|
||||
// --target-kind=<k> bin | lib,默认 bin(本地自测小 crate 时用 lib)
|
||||
// --bin=<name> bin target 名,默认 genarrative-ai-game-creator-shell
|
||||
@@ -49,6 +56,7 @@ function parsePositiveInteger(name, rawValue) {
|
||||
|
||||
const options = {
|
||||
shards: 4,
|
||||
shardIndex: undefined,
|
||||
concurrency: undefined,
|
||||
manifestPath: path.join(shellRoot, 'src-tauri', 'Cargo.toml'),
|
||||
targetKind: 'bin',
|
||||
@@ -75,6 +83,9 @@ for (const rawArgument of process.argv.slice(2)) {
|
||||
case 'shards':
|
||||
options.shards = parsePositiveInteger('--shards', value);
|
||||
break;
|
||||
case 'shard-index':
|
||||
options.shardIndex = parsePositiveInteger('--shard-index', value);
|
||||
break;
|
||||
case 'concurrency':
|
||||
options.concurrency = parsePositiveInteger('--concurrency', value);
|
||||
break;
|
||||
@@ -105,6 +116,12 @@ if (!fs.existsSync(options.manifestPath)) {
|
||||
fail(`manifest does not exist: ${options.manifestPath}`);
|
||||
}
|
||||
|
||||
if (options.shardIndex !== undefined && options.shardIndex > options.shards) {
|
||||
fail(
|
||||
`--shard-index (${options.shardIndex}) must be within --shards (${options.shards})`,
|
||||
);
|
||||
}
|
||||
|
||||
const concurrency = options.concurrency ?? options.shards;
|
||||
const crateRoot = path.dirname(options.manifestPath);
|
||||
|
||||
@@ -380,17 +397,35 @@ async function main() {
|
||||
const shards = splitTestNames(testNames, options.shards);
|
||||
assertShardsCoverEveryTest(testNames, shards);
|
||||
|
||||
console.log(
|
||||
`[rust-shards] ${testNames.length} tests, ${shards.length} shard(s), concurrency ${Math.min(concurrency, shards.length)}`,
|
||||
);
|
||||
shards.forEach((shardTestNames, index) => {
|
||||
console.log(
|
||||
`[rust-shards] shard ${index + 1}/${shards.length}: ${shardTestNames.length} test(s)`,
|
||||
);
|
||||
});
|
||||
const selectedShards =
|
||||
options.shardIndex === undefined
|
||||
? shards.map((shardTestNames, index) => ({ index, shardTestNames }))
|
||||
: [
|
||||
{
|
||||
index: options.shardIndex - 1,
|
||||
shardTestNames: shards[options.shardIndex - 1],
|
||||
},
|
||||
];
|
||||
|
||||
const results = await runWithConcurrency(shards, (shardTestNames, index) =>
|
||||
runShard(executable, index, shards.length, shardTestNames),
|
||||
if (options.shardIndex === undefined) {
|
||||
console.log(
|
||||
`[rust-shards] ${testNames.length} tests, ${shards.length} shard(s), concurrency ${Math.min(concurrency, shards.length)}`,
|
||||
);
|
||||
for (const { index, shardTestNames } of selectedShards) {
|
||||
console.log(
|
||||
`[rust-shards] shard ${index + 1}/${shards.length}: ${shardTestNames.length} test(s)`,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
console.log(
|
||||
`[rust-shards] ${testNames.length} tests, ${shards.length} shard(s), running shard ${options.shardIndex}/${shards.length} (${selectedShards[0].shardTestNames.length} test(s))`,
|
||||
);
|
||||
}
|
||||
|
||||
const results = await runWithConcurrency(
|
||||
selectedShards,
|
||||
({ index, shardTestNames }) =>
|
||||
runShard(executable, index, shards.length, shardTestNames),
|
||||
);
|
||||
|
||||
let failed = false;
|
||||
|
||||
Reference in New Issue
Block a user