From a6f7f968b55e39a53b7b610459f6df8b8c9ca7bd Mon Sep 17 00:00:00 2001 From: dschlueter Date: Wed, 20 May 2026 23:47:06 +0200 Subject: [PATCH] =?UTF-8?q?fix:=20Test-Timeout=20verhindert=20h=C3=A4ngend?= =?UTF-8?q?en=20Loop?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit runTestsParallel() wrappte jede Test-Suite mit 'timeout N bash -c ...' (Standard: 120s). Exit 124 wird als Timeout erkannt und im Output markiert. Neues Flag --test-timeout N für Integration-Tests die länger brauchen: /optimize "..." --test-timeout 300 Co-Authored-By: Claude Sonnet 4.6 --- pi-coder-judge-extension.ts | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/pi-coder-judge-extension.ts b/pi-coder-judge-extension.ts index f79cd24..a3d5c41 100644 --- a/pi-coder-judge-extension.ts +++ b/pi-coder-judge-extension.ts @@ -452,10 +452,16 @@ async function detectTestCommands( async function runTestsParallel( pi: ExtensionAPI, ctx: ExtensionCommandContext, - cmds: string[] + cmds: string[], + timeoutSecs: number = 120 ): Promise { const results = await Promise.all( - cmds.map(cmd => pi.exec("bash", ["-c", cmd], { cwd: ctx.cwd })) + // timeout-Wrapper: verhindert hängende Tests (Exit 124 = Timeout) + cmds.map(cmd => pi.exec( + "bash", + ["-c", `timeout ${timeoutSecs} bash -c ${JSON.stringify(cmd)}`], + { cwd: ctx.cwd } + )) ); const MAX_PER = Math.max(1000, Math.floor(6000 / cmds.length)); return results.map((r, i) => { @@ -463,7 +469,9 @@ async function runTestsParallel( const out = raw.length > MAX_PER ? raw.slice(0, MAX_PER) + `\n[… gekürzt, ${raw.length} Zeichen]` : raw || "(kein Output)"; - const status = r.code === 0 ? "✓ OK" : `✗ Exit ${r.code}`; + const status = r.code === 0 ? "✓ OK" + : r.code === 124 ? `✗ Timeout (>${timeoutSecs}s)` + : `✗ Exit ${r.code}`; return `=== ${cmds[i]} [${status}] ===\n${out}`; }).join("\n\n"); } @@ -706,17 +714,19 @@ export default function (pi: ExtensionAPI) { // ── Automatische Optimierungsschleife ──────────────────────────────────── pi.registerCommand("optimize", { - description: "Coder→Judge→Fix-Schleife bis PASS. Tests werden automatisch erkannt und parallel ausgeführt. /optimize [--rounds N] [--with-doku] [--continue] [--test-cmd \"override\"]", + description: "Coder→Judge→Fix-Schleife bis PASS. Tests werden automatisch erkannt und parallel ausgeführt. /optimize [--rounds N] [--with-doku] [--continue] [--test-cmd \"override\"] [--test-timeout N]", handler: async function (args: string, ctx: ExtensionCommandContext) { const roundsMatch = (args || "").match(/--rounds\s+(\d+)/); const maxRounds = roundsMatch ? Math.max(1, parseInt(roundsMatch[1], 10)) : 3; const withDoku = /--with-doku/.test(args || ""); const continueMode = /--continue/.test(args || ""); - // --test-cmd "befehl" oder --test-cmd befehl (ohne Leerzeichen im Befehl) const testCmdMatch = (args || "").match(/--test-cmd\s+"([^"]+)"|--test-cmd\s+(\S+)/); const testCmd: string | null = testCmdMatch ? (testCmdMatch[1] ?? testCmdMatch[2]) : null; + const testTimeoutMatch = (args || "").match(/--test-timeout\s+(\d+)/); + const testTimeout = testTimeoutMatch ? parseInt(testTimeoutMatch[1], 10) : 120; const task = (args || "") .replace(/--rounds\s+\d+/, "") + .replace(/--test-timeout\s+\d+/, "") .replace(/--with-doku/, "") .replace(/--continue/, "") .replace(/--test-cmd\s+"[^"]*"/, "") @@ -793,8 +803,8 @@ export default function (pi: ExtensionAPI) { const label = autoTestCmds.length === 1 ? autoTestCmds[0].split(" ")[0] : `${autoTestCmds.length} Suiten parallel`; - ctx.ui.setStatus("optimize", `${prog} Runde ${round}/${maxRounds}: Tests laufen (${label})…`); - const testOutput = await runTestsParallel(pi, ctx, autoTestCmds); + ctx.ui.setStatus("optimize", `${prog} Runde ${round}/${maxRounds}: Tests laufen (${label}, max. ${testTimeout}s)…`); + const testOutput = await runTestsParallel(pi, ctx, autoTestCmds, testTimeout); ctx.ui.setStatus("optimize", `${prog} Runde ${round}/${maxRounds}: Judge analysiert Test-Ergebnis…`); await sendAndWait(pi, ctx, judgeWithTestsPrompt(testOutput, "")); } else {