diff --git a/.github/workflows/ahk-lint-format-compile.yml b/.github/workflows/ahk-lint-format-compile.yml index d6b76ab..7ce5828 100644 --- a/.github/workflows/ahk-lint-format-compile.yml +++ b/.github/workflows/ahk-lint-format-compile.yml @@ -16,7 +16,7 @@ jobs: lint: runs-on: windows-latest steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@v4 - name: Install AutoHotkey v1.1 (Portable) run: choco install autohotkey.portable --version=1.1.36.01 -y @@ -83,8 +83,7 @@ jobs: $syntaxErrors += $rel } - # Format check - $content = Get-Content $_.FullName -Raw + # Format check (reuses $content already read above) $problems = @() if (($content -match "(?m)^\t") -and ($content -match "(?m)^ ")) { $problems += "mixed indent" } if ($content -match "[ \t]+`r?`n") { $problems += "trailing space" } @@ -112,3 +111,58 @@ jobs: exit 1 } shell: pwsh + + - name: Run Tests + run: | + function Test-IsAhkV2Script { + param( + [Parameter(Mandatory = $true)] + [string]$Path + ) + + $header = Get-Content -Path $Path -TotalCount 20 -ErrorAction SilentlyContinue + return $header -match '^\s*#Requires\s+AutoHotkey\s+v2(\.0)?\b' + } + + $ahkV2 = "$env:ProgramFiles\AutoHotkey\v2\AutoHotkey64.exe" + $searchRoots = @("tests", "ahk", "Lib") | Where-Object { Test-Path $_ } + $testFiles = @() + + foreach ($root in $searchRoots) { + $testFiles += Get-ChildItem -Path $root -Recurse -Filter *.ahk -File | + Where-Object { + ($_.Name -like "test_*" -or $_.Name -like "*_Test.ahk") -and + (Test-IsAhkV2Script -Path $_.FullName) + } + } + + $failed = @() + + if (-not $testFiles) { + Write-Host "No AutoHotkey v2 test files found under tests/ or ahk/." + exit 0 + } + + foreach ($tf in $testFiles) { + $rel = $tf.FullName -replace [regex]::Escape("$(Get-Location)\"), "" + $proc = Start-Process -FilePath $ahkV2 -ArgumentList "`"$($tf.FullName)`"" -PassThru -NoNewWindow + if (!$proc.WaitForExit(30000)) { + $proc.Kill() + Write-Host "❌ $rel - timed out" -ForegroundColor Red + $failed += $rel + } elseif ($proc.ExitCode -ne 0) { + Write-Host "❌ $rel - exit $($proc.ExitCode)" -ForegroundColor Red + $failed += $rel + } else { + Write-Host "✓ $rel" -ForegroundColor Green + } + } + + Write-Host "`nRan: $($testFiles.Count) | Failed: $($failed.Count)" + + if ($failed) { + Write-Host "`nFailed tests:" -ForegroundColor Red + $failed | % { Write-Host " $_" } + exit 1 + } + shell: pwsh diff --git a/.github/workflows/build-cached.yml b/.github/workflows/build-cached.yml index 42bf31f..36216a5 100644 --- a/.github/workflows/build-cached.yml +++ b/.github/workflows/build-cached.yml @@ -16,7 +16,7 @@ jobs: build-release: runs-on: windows-latest steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@v4 - name: Cache AutoHotkey and Ahk2Exe uses: actions/cache@v5 @@ -44,7 +44,8 @@ jobs: Expand-Archive "$env:TEMP\Ahk2Exe.zip" -DestinationPath "C:\AutoHotkey-Portable\Compiler" -Force $requiredFiles = @( "C:\AutoHotkey-Portable\Compiler\Ahk2Exe.exe", - "C:\AutoHotkey-Portable\Compiler\AutoHotkeySC.bin" + "C:\AutoHotkey-Portable\Compiler\Unicode 32-bit.bin", + "C:\AutoHotkey-Portable\Compiler\Unicode 64-bit.bin" ) $missingFiles = $requiredFiles | Where-Object { -not (Test-Path $_) } if ($missingFiles) { @@ -88,7 +89,7 @@ jobs: shell: pwsh - name: Create Release - uses: softprops/action-gh-release@v3 + uses: softprops/action-gh-release@v2 with: files: release/*.exe fail_on_unmatched_files: true diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ba44947..671bf1d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -16,7 +16,7 @@ jobs: build-release: runs-on: windows-latest steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@v4 - name: Install Scoop shell: pwsh @@ -89,7 +89,7 @@ jobs: shell: pwsh - name: Create Release - uses: softprops/action-gh-release@v3 + uses: softprops/action-gh-release@v2 with: files: release/*.exe fail_on_unmatched_files: true diff --git a/.github/workflows/copilot-setup-steps.yml b/.github/workflows/copilot-setup-steps.yml index a9f31ac..c6a8972 100644 --- a/.github/workflows/copilot-setup-steps.yml +++ b/.github/workflows/copilot-setup-steps.yml @@ -16,9 +16,9 @@ jobs: copilot-setup-steps: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@v4 - - uses: actions/setup-node@v6 + - uses: actions/setup-node@v4 with: node-version: "24" diff --git a/.github/workflows/powershell.yml b/.github/workflows/powershell.yml index 84a7e36..05e398b 100644 --- a/.github/workflows/powershell.yml +++ b/.github/workflows/powershell.yml @@ -29,7 +29,7 @@ jobs: name: PSScriptAnalyzer runs-on: ubuntu-latest steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@v4 - name: Run PSScriptAnalyzer uses: microsoft/psscriptanalyzer-action@7a0da25f33985767f15f93140306528900744195 @@ -44,6 +44,6 @@ jobs: # Upload the SARIF file generated in the previous step - name: Upload SARIF results file - uses: github/codeql-action/upload-sarif@v4 + uses: github/codeql-action/upload-sarif@v3 with: sarif_file: results.sarif diff --git a/Lib/v2/WindowManager.ahk b/Lib/v2/WindowManager.ahk index 874447c..f35d566 100644 --- a/Lib/v2/WindowManager.ahk +++ b/Lib/v2/WindowManager.ahk @@ -2,8 +2,8 @@ ; WindowManager – borderless/fullscreen utilities -static STYLE_DECORATIONS := 0xEC0000 ; title/menu/frame/sysmodal combined -static STYLE_UNDECORATED := -0xEC0000 +global STYLE_DECORATIONS := "+0xEC0000" ; title/menu/frame/sysmodal combined +global STYLE_UNDECORATED := "-0xEC0000" ToggleFakeFullscreen(winTitle := "A", api := "") { if !api diff --git a/ahk/Keys.ahk b/ahk/Keys.ahk index e8297d2..3db7e9f 100644 --- a/ahk/Keys.ahk +++ b/ahk/Keys.ahk @@ -1,8 +1,8 @@ #Requires AutoHotkey v2.0 +#SingleInstance Force +#Include ..\Lib\v2\AHK_Common.ahk -#Include %A_ScriptDir%\..\Lib\v2\AHK_Common.ahk -InitScript(true, false, true) ; UIA required, no admin, optimize - +InitScript(true, false, true) SetTitleMatchMode(3) SetNumLockState("AlwaysOn") SetCapsLockState("AlwaysOff") diff --git a/ahk/test_WindowManager.ahk b/ahk/test_WindowManager.ahk index 41cf9e0..7d599b2 100644 --- a/ahk/test_WindowManager.ahk +++ b/ahk/test_WindowManager.ahk @@ -55,22 +55,22 @@ TestToggleFakeFullscreen_MakeFullscreen() { ; Verify calls if (mockApi.calls.Length != 3) { FileOpen("*", "w `n").Write("Fail: Expected 3 calls, got " mockApi.calls.Length "`n") - return false + ExitApp(1) } if (mockApi.calls[1].method != "WinGetStyle") { FileOpen("*", "w `n").Write("Fail: First call was not WinGetStyle`n") - return false + ExitApp(1) } if (mockApi.calls[2].method != "WinSetStyle") { FileOpen("*", "w `n").Write("Fail: Second call was not WinSetStyle`n") - return false + ExitApp(1) } if (mockApi.calls[3].method != "WinMove") { FileOpen("*", "w `n").Write("Fail: Third call was not WinMove`n") - return false + ExitApp(1) } FileOpen("*", "w `n").Write("Pass: MakeFullscreen`n") @@ -86,17 +86,17 @@ TestToggleFakeFullscreen_RestoreWindow() { ; Verify calls if (mockApi.calls.Length != 3) { FileOpen("*", "w `n").Write("Fail: Expected 3 calls, got " mockApi.calls.Length "`n") - return false + ExitApp(1) } if (mockApi.calls[2].method != "WinSetStyle") { FileOpen("*", "w `n").Write("Fail: Second call was not WinSetStyle`n") - return false + ExitApp(1) } if (mockApi.calls[3].method != "WinRestore") { FileOpen("*", "w `n").Write("Fail: Third call was not WinRestore`n") - return false + ExitApp(1) } FileOpen("*", "w `n").Write("Pass: RestoreWindow`n") @@ -112,7 +112,7 @@ TestGetMonitorAtPos_InsideMonitor() { res := GetMonitorAtPos(500, 500, mockApi) if (res != 1) { FileOpen("*", "w `n").Write("Fail: Expected 1, got " res "`n") - return false + ExitApp(1) } FileOpen("*", "w `n").Write("Pass: GetMonitorAtPos_InsideMonitor`n") return true @@ -127,7 +127,7 @@ TestGetMonitorAtPos_OutsideMonitors() { res := GetMonitorAtPos(-100, 500, mockApi) if (res != -1) { FileOpen("*", "w `n").Write("Fail: Expected -1, got " res "`n") - return false + ExitApp(1) } FileOpen("*", "w `n").Write("Pass: GetMonitorAtPos_OutsideMonitors`n") return true @@ -143,7 +143,7 @@ TestGetMonitorAtPos_MultipleMonitors() { res := GetMonitorAtPos(-500, 500, mockApi) if (res != 2) { FileOpen("*", "w `n").Write("Fail: Expected 2, got " res "`n") - return false + ExitApp(1) } FileOpen("*", "w `n").Write("Pass: GetMonitorAtPos_MultipleMonitors`n") return true @@ -157,4 +157,4 @@ TestGetMonitorAtPos_OutsideMonitors() TestGetMonitorAtPos_MultipleMonitors() FileOpen("*", "w `n").Write("All tests complete.`n") -ExitApp +ExitApp(0)