import assert from 'node:assert/strict'; import { spawnSync } from 'node:child_process'; import fs from 'node:fs'; import os from 'node:os'; import path from 'node:path'; import test from 'node:test'; import { fileURLToPath } from 'node:url'; const runner = fileURLToPath( new URL('./run-rust-shell-test-shards.mjs', import.meta.url), ); function runFixture(t, source) { const root = fs.mkdtempSync(path.join(os.tmpdir(), 'agc-shard-output-')); t.after(() => fs.rmSync(root, { recursive: true, force: true })); fs.mkdirSync(path.join(root, 'src')); fs.writeFileSync( path.join(root, 'Cargo.toml'), '[package]\nname = "shard-output-fixture"\nversion = "0.1.0"\nedition = "2021"\n', ); fs.writeFileSync(path.join(root, 'src/lib.rs'), source); const result = spawnSync( process.execPath, [ runner, `--manifest=${path.join(root, 'Cargo.toml')}`, '--target-kind=lib', '--no-locked', '--shards=1', `--shard-tmp-root=${path.join(root, 'tmp')}`, ], { encoding: 'utf8', timeout: 60_000, windowsHide: true, env: { ...process.env, CARGO_TARGET_DIR: path.join(root, 'target') }, }, ); assert.ifError(result.error); return { status: result.status, output: result.stdout + result.stderr }; } test('failed shard retains panic details and separates selected count from failures', (t) => { const result = runFixture( t, ` #[test] fn passing_case() {} #[test] fn failing_case() { assert_eq!(1, 2, "shard panic evidence"); } `, ); assert.equal(result.status, 1, result.output); assert.match(result.output, /FAILED \(selected 2 test\(s\)\)/); assert.match(result.output, /failing_case/); assert.match(result.output, /panicked at src[\\/]lib\.rs:/); assert.match(result.output, /shard panic evidence/); assert.match(result.output, /left: 1/); assert.match(result.output, /right: 2/); assert.match(result.output, /1 passed; 1 failed/); }); test('compile failure retains the diagnostic code, source location and cause', (t) => { const result = runFixture( t, '#[test]\nfn compile_failure() { let _: u32 = "not a number"; }\n', ); assert.equal(result.status, 1, result.output); assert.match(result.output, /error\[E0308\]/); assert.match(result.output, /src[\\/]lib\.rs:2:/); assert.match(result.output, /mismatched types/); assert.match(result.output, /expected `u32`, found `&str`/); }); test('successful shard keeps its compact summary', (t) => { const result = runFixture(t, '#[test]\nfn passing_case() {}\n'); assert.equal(result.status, 0, result.output); 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/); });