From ac0282716cd1bc80a898fcf126bfe0a007b8048b Mon Sep 17 00:00:00 2001 From: Rijad Zuzo Date: Sun, 5 Jul 2026 23:24:17 +0200 Subject: [PATCH] fix: run project scenarios in quality gate --- docs/local_quality_gate.md | 15 ++++++---- tools/quality.ps1 | 59 ++++++++++++++++++++++++++++---------- tools/quality.sh | 37 ++++++++++++++++++------ 3 files changed, 82 insertions(+), 29 deletions(-) diff --git a/docs/local_quality_gate.md b/docs/local_quality_gate.md index 86229dd..e111073 100644 --- a/docs/local_quality_gate.md +++ b/docs/local_quality_gate.md @@ -20,17 +20,17 @@ parser errors, and LLM-generated garbage before committing. ./tools/quality.sh # Windows PowerShell -pwsh ./tools/quality.ps1 +& ./tools/quality.ps1 ``` ### Fast changed-files mode Only runs `gdformat` / `gdlint` on `.gd` files modified since the last commit. -The Godot parser check still runs on the whole project. +The Godot dependency check and all project scenarios still run. ```bash ./tools/quality.sh --changed -pwsh ./tools/quality.ps1 -Changed +& ./tools/quality.ps1 -Changed ``` ### Auto-format (not part of the gate) @@ -43,9 +43,10 @@ pwsh ./tools/quality.ps1 -Changed | Check | Tool | What it detects | |---|---|---| -| **gdformat** | `gdformat --check .` | Unformatted code (indentation, spacing) | -| **gdlint** | `gdlint .` | Lint violations (unused args, long lines, naming, etc.) | -| **godot-check** | `godot --headless --check-only` | Parser/syntax errors, missing dependencies | +| **gdformat** | Game-owned GDScript roots | Formatting without rewriting addons or demos | +| **gdlint** | Game-owned GDScript roots | Lint violations such as unused arguments and naming | +| **godot-check** | Headless definitions scenario | Parser errors and missing dependencies | +| **scenarios** | Every `tests/*_test.gd` script | Simulation, persistence, navigation, and presentation regressions | | **gut** | `godot -s gut_cmdln.gd` | Failing GUT unit tests (only if addon exists) | ## Output @@ -58,6 +59,7 @@ QUALITY RESULT: PASS gdformat PASS gdlint PASS godot-check PASS + scenarios PASS gut SKIPPED Full logs: @@ -72,6 +74,7 @@ QUALITY RESULT: FAIL gdformat FAIL gdlint FAIL godot-check PASS + scenarios PASS gut SKIPPED res://simulation/SimNPC.gd:42 Unused argument 'delta' diff --git a/tools/quality.ps1 b/tools/quality.ps1 index c169964..fa75ac2 100644 --- a/tools/quality.ps1 +++ b/tools/quality.ps1 @@ -13,6 +13,7 @@ New-Item -ItemType Directory -Path $LOG -Force | Out-Null $OVERALL = $false $ERRORS = @() $FIXES = @() +$OWNED_GDSCRIPT_ROOTS = @("player", "simulation", "tests", "tools", "world") # -- tool detection ----------------------------------------------------------- function Find-Godot { @@ -26,6 +27,10 @@ function Find-Godot { "$env:USERPROFILE\AppData\Local\Godot\godot.exe" ) foreach ($p in $common) { if (Test-Path $p) { return $p } } + $bundled = Get-ChildItem "${env:ProgramFiles(x86)}\Godot" -Filter "Godot*_console.exe" -ErrorAction SilentlyContinue | + Sort-Object Name -Descending | + Select-Object -First 1 + if ($bundled) { return $bundled.FullName } return $null } @@ -86,24 +91,27 @@ function To-ResPath($p) { } function Invoke-GodotWithTimeout { - param([string]$Args) + param([string]$ArgumentString) $psi = New-Object Diagnostics.ProcessStartInfo $psi.FileName = $GODOT - $psi.Arguments = $Args + $psi.Arguments = $ArgumentString $psi.RedirectStandardOutput = $true $psi.RedirectStandardError = $true $psi.UseShellExecute = $false $psi.CreateNoWindow = $true $p = [Diagnostics.Process]::Start($psi) + $outputTask = $p.StandardOutput.ReadToEndAsync() + $errorTask = $p.StandardError.ReadToEndAsync() $exited = $p.WaitForExit(30000) if (-not $exited) { $p.Kill() - $output = $p.StandardOutput.ReadToEnd() - $err = $p.StandardError.ReadToEnd() + $p.WaitForExit() + $output = $outputTask.Result + $err = $errorTask.Result return $output + $err, $null } - $output = $p.StandardOutput.ReadToEnd() - $err = $p.StandardError.ReadToEnd() + $output = $outputTask.Result + $err = $errorTask.Result return ($output + $err), $p.ExitCode } @@ -139,7 +147,8 @@ if (-not $fmtAvail) { Add-Content -Path "$LOG/gdformat.log" -Value $msg -Encoding utf8 } } else { - $out = Invoke-GdFormat "--check" "." + $formatArgs = @("--check") + $OWNED_GDSCRIPT_ROOTS + $out = Invoke-GdFormat @formatArgs $out | Add-Content -Path "$LOG/gdformat.log" -Encoding utf8 } } @@ -175,7 +184,7 @@ if (-not $lintAvail) { } } } else { - $out = Invoke-GdLint "." + $out = Invoke-GdLint @OWNED_GDSCRIPT_ROOTS $out | Add-Content -Path "$LOG/gdlint.log" -Encoding utf8 } } @@ -201,12 +210,12 @@ if ($lintLines) { # -- 3. godot headless check -------------------------------------------------- $godotResult = "PASS" Set-Content -Path "$LOG/godot-check.log" -Value "" -Encoding utf8 -$godotArgs = "--headless --path `"$ROOT`" --quit" -$godotOutput, $godotExit = Invoke-GodotWithTimeout -Args $godotArgs +$godotArgs = "--headless --path `"$ROOT`" --script res://tests/simulation_definitions_test.gd" +$godotOutput, $godotExit = Invoke-GodotWithTimeout -ArgumentString $godotArgs $godotOutput | Out-File -FilePath "$LOG/godot-check.log" -Encoding utf8 if ($godotExit -eq $null) { - $godotResult = "PASS" - Add-Content -Path "$LOG/godot-check.log" -Value "[timed out after 30s - project runs continuously]" -Encoding utf8 + $godotResult = "FAIL" + Add-Content -Path "$LOG/godot-check.log" -Value "[timed out after 30s]" -Encoding utf8 } elseif ($godotExit -eq 0) { $godotResult = "PASS" } else { @@ -217,12 +226,30 @@ if ($godotExit -eq $null) { $FIXES += "Fix Godot parser errors" } -# -- 4. GUT (optional) -------------------------------------------------------- +# -- 4. project scenario tests ------------------------------------------------ +$scenarioResult = "PASS" +Set-Content -Path "$LOG/scenarios.log" -Value "" -Encoding utf8 +$scenarioTests = Get-ChildItem "tests" -Filter "*_test.gd" | Sort-Object Name +foreach ($test in $scenarioTests) { + Add-Content -Path "$LOG/scenarios.log" -Value "[RUN] $($test.Name)" -Encoding utf8 + $testArgs = "--headless --path `"$ROOT`" --script res://tests/$($test.Name)" + $testOutput, $testExit = Invoke-GodotWithTimeout -ArgumentString $testArgs + $testOutput | Add-Content -Path "$LOG/scenarios.log" -Encoding utf8 + if ($testExit -eq $null -or $testExit -ne 0) { + $scenarioResult = "FAIL" + $ERRORS += "scenario: $($test.Name) failed or timed out" + } +} +if ($scenarioResult -eq "FAIL") { + $FIXES += "Fix failing project scenario tests" +} + +# -- 5. GUT (optional) -------------------------------------------------------- $gutResult = "SKIPPED" Set-Content -Path "$LOG/gut.log" -Value "" -Encoding utf8 if (Test-Path "addons/gut/gut_cmdln.gd") { $gutArgs = "--headless --path `"$ROOT`" -s addons/gut/gut_cmdln.gd --quit" - $gutOutput, $gutExit = Invoke-GodotWithTimeout -Args $gutArgs + $gutOutput, $gutExit = Invoke-GodotWithTimeout -ArgumentString $gutArgs $gutOutput | Out-File -FilePath "$LOG/gut.log" -Encoding utf8 if ($gutExit -eq $null) { $gutResult = "FAIL" @@ -241,7 +268,7 @@ if (Test-Path "addons/gut/gut_cmdln.gd") { } # -- summary ------------------------------------------------------------------ -if ($fmtResult -eq 'FAIL' -or $lintResult -eq 'FAIL' -or $godotResult -eq 'FAIL' -or $gutResult -eq 'FAIL') { +if ($fmtResult -eq 'FAIL' -or $lintResult -eq 'FAIL' -or $godotResult -eq 'FAIL' -or $scenarioResult -eq 'FAIL' -or $gutResult -eq 'FAIL') { $OVERALL = $true } @@ -251,6 +278,7 @@ Write-Host "" Write-Host " gdformat $fmtResult" Write-Host " gdlint $lintResult" Write-Host " godot-check $godotResult" +Write-Host " scenarios $scenarioResult" Write-Host " gut $gutResult" $seen = @{} @@ -286,6 +314,7 @@ QUALITY RESULT: $resultText gdformat $fmtResult gdlint $lintResult godot-check $godotResult + scenarios $scenarioResult gut $gutResult "@ Set-Content -Path "$LOG/summary.txt" -Value $summary -Encoding utf8 diff --git a/tools/quality.sh b/tools/quality.sh index 359f89d..4917903 100644 --- a/tools/quality.sh +++ b/tools/quality.sh @@ -15,6 +15,7 @@ CHANGED=false OVERALL=false ERRORS=() FIXES=() +OWNED_GDSCRIPT_ROOTS=(player simulation tests tools world) # -- tool detection ----------------------------------------------------------- find_godot() { @@ -86,7 +87,7 @@ else echo "($bad/$total files need formatting)" >> "$LOG/gdformat.log" fi else - $fmt_tool --check . >> "$LOG/gdformat.log" 2>&1 || true + $fmt_tool --check "${OWNED_GDSCRIPT_ROOTS[@]}" >> "$LOG/gdformat.log" 2>&1 || true fi fi @@ -119,7 +120,7 @@ else done <<< "$files" fi else - $lint_tool . >> "$LOG/gdlint.log" 2>&1 || true + $lint_tool "${OWNED_GDSCRIPT_ROOTS[@]}" >> "$LOG/gdlint.log" 2>&1 || true fi fi @@ -146,21 +147,20 @@ fi # -- 3. godot headless check -------------------------------------------------- godot_result="PASS" : > "$LOG/godot-check.log" -GODOT_CMD="$GODOT --headless --path \"$ROOT\" --check-only --quit" if need_timeout; then - if timeout 30 $GODOT_CMD >> "$LOG/godot-check.log" 2>&1; then + if timeout 30 "$GODOT" --headless --path "$ROOT" --script res://tests/simulation_definitions_test.gd >> "$LOG/godot-check.log" 2>&1; then godot_result="PASS" else ec=$? if [[ $ec -eq 124 ]]; then - godot_result="PASS" + godot_result="FAIL" echo "[timed out after 30s — project runs continuously]" >> "$LOG/godot-check.log" else godot_result="FAIL" fi fi else - if $GODOT --headless --path "$ROOT" --quit >> "$LOG/godot-check.log" 2>&1; then + if "$GODOT" --headless --path "$ROOT" --script res://tests/simulation_definitions_test.gd >> "$LOG/godot-check.log" 2>&1; then godot_result="PASS" else godot_result="FAIL" @@ -173,7 +173,26 @@ if [[ "$godot_result" == "FAIL" ]]; then FIXES+=("Fix Godot parser errors") fi -# -- 4. GUT (optional) -------------------------------------------------------- +# -- 4. project scenario tests ------------------------------------------------ +scenario_result="PASS" +: > "$LOG/scenarios.log" +for test in tests/*_test.gd; do + echo "[RUN] $(basename "$test")" >> "$LOG/scenarios.log" + if need_timeout; then + if ! timeout 30 "$GODOT" --headless --path "$ROOT" --script "res://$test" >> "$LOG/scenarios.log" 2>&1; then + scenario_result="FAIL" + ERRORS+=("scenario: $(basename "$test") failed or timed out") + fi + elif ! "$GODOT" --headless --path "$ROOT" --script "res://$test" >> "$LOG/scenarios.log" 2>&1; then + scenario_result="FAIL" + ERRORS+=("scenario: $(basename "$test") failed") + fi +done +if [[ "$scenario_result" == "FAIL" ]]; then + FIXES+=("Fix failing project scenario tests") +fi + +# -- 5. GUT (optional) -------------------------------------------------------- gut_result="SKIPPED" : > "$LOG/gut.log" if [[ -f "addons/gut/gut_cmdln.gd" ]]; then @@ -189,7 +208,7 @@ if [[ -f "addons/gut/gut_cmdln.gd" ]]; then fi # -- summary ------------------------------------------------------------------ -if [[ "$fmt_result" == "FAIL" || "$lint_result" == "FAIL" || "$godot_result" == "FAIL" || "$gut_result" == "FAIL" ]]; then +if [[ "$fmt_result" == "FAIL" || "$lint_result" == "FAIL" || "$godot_result" == "FAIL" || "$scenario_result" == "FAIL" || "$gut_result" == "FAIL" ]]; then OVERALL=true fi @@ -203,6 +222,7 @@ echo "" echo " gdformat $fmt_result" echo " gdlint $lint_result" echo " godot-check $godot_result" +echo " scenarios $scenario_result" echo " gut $gut_result" seen_err=() @@ -236,6 +256,7 @@ echo "" echo " gdformat $fmt_result" echo " gdlint $lint_result" echo " godot-check $godot_result" + echo " scenarios $scenario_result" echo " gut $gut_result" } > "$LOG/summary.txt"