From a7cc28134d6b0184e280580513393b564217f1a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E5=BE=B7=E5=AE=87?= Date: Thu, 1 Oct 2026 16:42:20 +0800 Subject: [PATCH] =?UTF-8?q?AGC=20Rust=20=E5=88=86=E7=89=87=E5=A4=B1?= =?UTF-8?q?=E8=B4=A5=E5=A4=8D=E6=A0=B8=E6=94=B9=E6=8C=89=E5=90=8D=E5=8D=95?= =?UTF-8?q?=E7=B2=BE=E7=A1=AE=E5=8C=B9=E9=85=8D=EF=BC=8C=E8=A6=86=E7=9B=96?= =?UTF-8?q?=20bin=20=E7=9B=AE=E6=A0=87=E7=94=A8=E4=BE=8B=E5=90=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - run-rust-shell-test-shards.mjs:extractFailingTestNames 改为按本片名单精确匹配用例名,不再用 `tests::` / `process_session::tests::` 前缀白名单——bin 目标的 `agent::…::tests::…` 原先永远进不了复核,偶发假红直接判红 - run-rust-shell-test-shards.mjs:复核也失败时同时保留首次失败现场,不再只留复核那一次 - run-rust-shell-test-shards.test.mjs:新增「命名空间用例名也会进复核」的回归用例 - 验证:node --test apps/ai-game-creator-shell/scripts/run-rust-shell-test-shards.test.mjs 3 项通过 --- .../scripts/run-rust-shell-test-shards.mjs | 21 ++++++++++-------- .../run-rust-shell-test-shards.test.mjs | 22 +++++++++++++++++++ 2 files changed, 34 insertions(+), 9 deletions(-) diff --git a/apps/ai-game-creator-shell/scripts/run-rust-shell-test-shards.mjs b/apps/ai-game-creator-shell/scripts/run-rust-shell-test-shards.mjs index 2698de093..a4a4cde9e 100644 --- a/apps/ai-game-creator-shell/scripts/run-rust-shell-test-shards.mjs +++ b/apps/ai-game-creator-shell/scripts/run-rust-shell-test-shards.mjs @@ -447,7 +447,12 @@ async function runWithConcurrency(shards, runner) { return results; } -function extractFailingTestNames(result) { +// 只认**本片名单里真实存在**的用例名:先按 `---- stdout ----` / 失败清单行归一化, +// 再拿名单做精确匹配。不要再用名字前缀白名单猜用例(历史上只放行 `tests::` 与 +// `process_session::tests::`,而 bin 目标的用例名形如 `agent::thread_manager::tests::…`, +// 于是绝大多数用例永远进不了复核,偶发假红就直接判红)。 +function extractFailingTestNames(result, shardTestNames) { + const known = new Set(shardTestNames); const names = new Set(); for (const line of result.failures) { const normalized = line @@ -457,11 +462,8 @@ function extractFailingTestNames(result) { .replace(/\s*stdout\s*----$/, '') .replace(/\s*\(\d+\)\s*$/, '') .trim(); - const match = normalized.match( - /^(process_session::tests::[A-Za-z0-9_:]+|tests::[A-Za-z0-9_:]+)$/, - ); - if (match) { - names.add(match[1]); + if (known.has(normalized)) { + names.add(normalized); } } return [...names]; @@ -518,9 +520,7 @@ async function main() { } // 片内串行的时序型用例在高负载 CI 上会偶发假红。只对失败用例做一次 // 有界复核:复核通过按 flaky 记录,复核失败才判红,避免把真实回归洗掉。 - const failingTestNames = extractFailingTestNames(result).filter((name) => - shardTestNames.includes(name), - ); + const failingTestNames = extractFailingTestNames(result, shardTestNames); if (failingTestNames.length === 0) { return result; } @@ -531,10 +531,13 @@ async function main() { failingTestNames, ); if (!retry.ok) { + // 复核也失败就判红,但**两次的现场都留**:首次是整片的上下文,复核只跑失败用例, + // 丢掉首次现场会让"到底整片跑成什么样"这件事在日志里消失。 return { ...result, failures: [ `re-run of ${failingTestNames.length} failing test(s) also failed`, + ...result.failures, ...retry.failures, ], }; diff --git a/apps/ai-game-creator-shell/scripts/run-rust-shell-test-shards.test.mjs b/apps/ai-game-creator-shell/scripts/run-rust-shell-test-shards.test.mjs index cf86a6a60..9eec73d7d 100644 --- a/apps/ai-game-creator-shell/scripts/run-rust-shell-test-shards.test.mjs +++ b/apps/ai-game-creator-shell/scripts/run-rust-shell-test-shards.test.mjs @@ -68,3 +68,25 @@ test('successful shard keeps its compact summary', (t) => { assert.match(result.output, /shard 1\/1 ok: 1 test\(s\)/); assert.doesNotMatch(result.output, /test passing_case \.\.\. ok/); }); + +// bin 目标的用例名带命名空间(`agent::thread_manager::tests::…`)。复核名单必须靠 +// 「本片名单精确匹配」认用例,不能靠名字前缀白名单,否则这类用例永远进不了重跑。 +test('namespaced failing test is re-run once before the shard is judged', (t) => { + const result = runFixture( + t, + ` +mod agent { + pub mod thread_manager { + #[test] + fn flaky_case() { + assert_eq!(1, 2, "namespaced panic evidence"); + } + } +} +`, + ); + assert.equal(result.status, 1, result.output); + assert.match(result.output, /re-run of 1 failing test\(s\) also failed/); + assert.match(result.output, /agent::thread_manager::flaky_case/); + assert.match(result.output, /namespaced panic evidence/); +});