Skip to content

Commit 22c9553

Browse files
committed
Merge branch 'ui-tests'
There have been way too many regressions in Cygwin as of late, in particular in the console handling. The worst part? Many of those bugs were introduced _in bug fix patches_! Here are a bunch of tests that are designed to help Git for Windows increase confidence in upgrades to newer Cygwin versions. Signed-off-by: Johannes Schindelin <[email protected]>
2 parents 387251b + ce42fe3 commit 22c9553

File tree

6 files changed

+542
-0
lines changed

6 files changed

+542
-0
lines changed

.github/workflows/build.yaml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,14 @@ jobs:
118118
with:
119119
git-artifacts-extract-location: ${{ needs.minimal-sdk-artifact.outputs.git-artifacts-extract-location }}
120120

121+
ui-tests:
122+
needs: build
123+
uses: ./.github/workflows/ui-tests.yml
124+
with:
125+
msys2-runtime-artifact-name: install
126+
permissions:
127+
contents: read
128+
121129
generate-msys2-tests-matrix:
122130
runs-on: ubuntu-latest
123131
outputs:

.github/workflows/ui-tests.yml

Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
name: ui-tests
2+
3+
on:
4+
workflow_call:
5+
inputs:
6+
msys2-runtime-artifact-name:
7+
required: true
8+
type: string
9+
10+
env:
11+
AUTOHOTKEY_VERSION: 2.0.19
12+
WT_VERSION: 1.22.11141.0
13+
WIN32_OPENSSH_VERSION: 9.8.3.0p2-Preview
14+
15+
jobs:
16+
ui-tests:
17+
strategy:
18+
fail-fast: false
19+
matrix:
20+
# Corresponds to Windows Server versions
21+
# See https://github.com/actions/runner-images?tab=readme-ov-file#available-images
22+
os: [windows-2022, windows-2025]
23+
24+
runs-on: ${{ matrix.os }}
25+
steps:
26+
- uses: actions/download-artifact@v5
27+
with:
28+
name: ${{ inputs.msys2-runtime-artifact-name }}
29+
path: ${{ runner.temp }}/artifacts
30+
- name: replace MSYS2 runtime
31+
run: |
32+
$p = Get-ChildItem -Recurse "${env:RUNNER_TEMP}\artifacts" | where {$_.Name -eq "msys-2.0.dll"} | Select -ExpandProperty VersionInfo | Select -First 1 -ExpandProperty FileName
33+
cp $p "c:/Program Files/Git/usr/bin/msys-2.0.dll"
34+
35+
- uses: actions/cache/restore@v4
36+
id: restore-wt
37+
with:
38+
key: wt-${{ env.WT_VERSION }}
39+
path: ${{ runner.temp }}/wt.zip
40+
- name: Download Windows Terminal
41+
if: steps.restore-wt.outputs.cache-hit != 'true'
42+
shell: bash
43+
run: |
44+
curl -fLo "$RUNNER_TEMP/wt.zip" \
45+
https://github.com/microsoft/terminal/releases/download/v$WT_VERSION/Microsoft.WindowsTerminal_${WT_VERSION}_x64.zip
46+
- uses: actions/cache/save@v4
47+
if: steps.restore-wt.outputs.cache-hit != 'true'
48+
with:
49+
key: wt-${{ env.WT_VERSION }}
50+
path: ${{ runner.temp }}/wt.zip
51+
- name: Install Windows Terminal
52+
shell: bash
53+
working-directory: ${{ runner.temp }}
54+
run: |
55+
"$WINDIR/system32/tar.exe" -xf "$RUNNER_TEMP/wt.zip" &&
56+
cygpath -aw terminal-$WT_VERSION >>$GITHUB_PATH
57+
- uses: actions/cache/restore@v4
58+
id: restore-ahk
59+
with:
60+
key: ahk-${{ env.AUTOHOTKEY_VERSION }}
61+
path: ${{ runner.temp }}/ahk.zip
62+
- name: Download AutoHotKey2
63+
if: steps.restore-ahk.outputs.cache-hit != 'true'
64+
shell: bash
65+
run: |
66+
curl -L -o "$RUNNER_TEMP/ahk.zip" \
67+
https://github.com/AutoHotkey/AutoHotkey/releases/download/v$AUTOHOTKEY_VERSION/AutoHotkey_$AUTOHOTKEY_VERSION.zip
68+
- uses: actions/cache/save@v4
69+
if: steps.restore-ahk.outputs.cache-hit != 'true'
70+
with:
71+
key: ahk-${{ env.AUTOHOTKEY_VERSION }}
72+
path: ${{ runner.temp }}/ahk.zip
73+
- name: Install AutoHotKey2
74+
shell: bash
75+
run: |
76+
mkdir -p "$RUNNER_TEMP/ahk" &&
77+
"$WINDIR/system32/tar.exe" -C "$RUNNER_TEMP/ahk" -xf "$RUNNER_TEMP/ahk.zip" &&
78+
cygpath -aw "$RUNNER_TEMP/ahk" >>$GITHUB_PATH
79+
- uses: actions/setup-node@v5 # the hook uses node for the background process
80+
- uses: actions/cache/restore@v4
81+
id: restore-win32-openssh
82+
with:
83+
key: win32-openssh-${{ env.WIN32_OPENSSH_VERSION }}
84+
path: ${{ runner.temp }}/win32-openssh.zip
85+
- name: Download Win32-OpenSSH
86+
if: steps.restore-win32-openssh.outputs.cache-hit != 'true'
87+
shell: bash
88+
run: |
89+
curl -fLo "$RUNNER_TEMP/win32-openssh.zip" \
90+
https://github.com/PowerShell/Win32-OpenSSH/releases/download/v$WIN32_OPENSSH_VERSION/OpenSSH-Win64.zip
91+
- uses: actions/cache/save@v4
92+
if: steps.restore-win32-openssh.outputs.cache-hit != 'true'
93+
with:
94+
key: win32-openssh-${{ env.WIN32_OPENSSH_VERSION }}
95+
path: ${{ runner.temp }}/win32-openssh.zip
96+
- name: Unpack Win32-OpenSSH
97+
shell: bash
98+
run: |
99+
"$WINDIR/system32/tar.exe" -C "$RUNNER_TEMP" -xvf "$RUNNER_TEMP/win32-openssh.zip" &&
100+
echo "OPENSSH_FOR_WINDOWS_DIRECTORY=$(cygpath -aw "$RUNNER_TEMP/OpenSSH-Win64")" >>$GITHUB_ENV
101+
102+
- uses: actions/checkout@v5
103+
with:
104+
sparse-checkout: |
105+
ui-tests
106+
- name: Run UI tests
107+
id: ui-tests
108+
timeout-minutes: 10
109+
working-directory: ui-tests
110+
run: |
111+
$exitCode = 0
112+
& "${env:RUNNER_TEMP}\ahk\AutoHotKey64.exe" /ErrorStdOut /force background-hook.ahk "$PWD\bg-hook" 2>&1 | Out-Default
113+
if (!$?) { $exitCode = 1; echo "::error::Test failed!" } else { echo "::notice::Test log" }
114+
type bg-hook.log
115+
$env:LARGE_FILES_DIRECTORY = "${env:RUNNER_TEMP}\large"
116+
& "${env:RUNNER_TEMP}\ahk\AutoHotKey64.exe" /ErrorStdOut /force ctrl-c.ahk "$PWD\ctrl-c" 2>&1 | Out-Default
117+
if (!$?) { $exitCode = 1; echo "::error::Ctrl+C Test failed!" } else { echo "::notice::Ctrl+C Test log" }
118+
type ctrl-c.log
119+
exit $exitCode
120+
- name: Show logs
121+
if: always()
122+
working-directory: ui-tests
123+
run: |
124+
type bg-hook.log
125+
type ctrl-c.log
126+
- name: Take screenshot, if canceled
127+
id: take-screenshot
128+
if: cancelled() || failure()
129+
shell: powershell
130+
run: |
131+
Add-Type -TypeDefinition @"
132+
using System;
133+
using System.Runtime.InteropServices;
134+
135+
public class DpiHelper {
136+
[DllImport("user32.dll")]
137+
public static extern bool SetProcessDpiAwarenessContext(IntPtr dpiContext);
138+
139+
[DllImport("Shcore.dll")]
140+
public static extern int GetDpiForMonitor(IntPtr hmonitor, int dpiType, out uint dpiX, out uint dpiY);
141+
142+
[DllImport("User32.dll")]
143+
public static extern IntPtr MonitorFromPoint(System.Drawing.Point pt, uint dwFlags);
144+
145+
[DllImport("user32.dll")]
146+
public static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
147+
148+
public static uint GetDPI() {
149+
// DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 = -4
150+
SetProcessDpiAwarenessContext((IntPtr)(-4));
151+
152+
uint dpiX, dpiY;
153+
IntPtr monitor = MonitorFromPoint(new System.Drawing.Point(0, 0), 2); // MONITOR_DEFAULTTONEAREST
154+
GetDpiForMonitor(monitor, 0, out dpiX, out dpiY); // MDT_EFFECTIVE_DPI
155+
return (dpiX + dpiY) / 2;
156+
}
157+
}
158+
"@ -ReferencedAssemblies "System.Drawing.dll"
159+
160+
# First, minimize the Console window in which this script is running
161+
$hwnd = (Get-Process -Id $PID).MainWindowHandle
162+
$SW_MINIMIZE = 6
163+
164+
[DpiHelper]::ShowWindow($hwnd, $SW_MINIMIZE)
165+
166+
# Now, get the DPI
167+
$dpi = [DpiHelper]::GetDPI()
168+
169+
# This function takes a screenshot and saves it as a PNG file
170+
[Reflection.Assembly]::LoadWithPartialName("System.Drawing")
171+
function screenshot([Drawing.Rectangle]$bounds, $path) {
172+
$bmp = New-Object Drawing.Bitmap $bounds.width, $bounds.height
173+
$graphics = [Drawing.Graphics]::FromImage($bmp)
174+
$graphics.CopyFromScreen($bounds.Location, [Drawing.Point]::Empty, $bounds.size)
175+
$bmp.Save($path)
176+
$graphics.Dispose()
177+
$bmp.Dispose()
178+
}
179+
Add-Type -AssemblyName System.Windows.Forms
180+
$screen = [System.Windows.Forms.Screen]::PrimaryScreen
181+
$bounds = [Drawing.Rectangle]::FromLTRB(0, 0, $screen.Bounds.Width * $dpi / 96, $screen.Bounds.Height * $dpi / 96)
182+
screenshot $bounds "ui-tests/screenshot.png"
183+
- name: Upload test results
184+
if: always()
185+
uses: actions/upload-artifact@v4
186+
with:
187+
name: ui-tests-${{ matrix.os }}
188+
path: ui-tests

ui-tests/.gitattributes

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
*.ahk eol=lf

ui-tests/background-hook.ahk

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
#Requires AutoHotkey v2.0
2+
#Include ui-test-library.ahk
3+
4+
; This script is an integration test for the following scenario:
5+
; A Git hook spawns a background process that outputs some text
6+
; to the console even after Git has exited.
7+
8+
; At some point in time, the Cygwin/MSYS2 runtime left the console
9+
; in a state where it was not possible to navigate the history via
10+
; CursorUp/Down, as reported in https://github.com/microsoft/git/issues/730.
11+
; This was fixed in the Cygwin/MSYS2 runtime, but then regressed again.
12+
; This test is meant to verify that the issue is fixed and remains so.
13+
14+
SetWorkTree('git-test-background-hook')
15+
16+
if not FileExist('.git/hooks') and not DirCreate('.git/hooks')
17+
ExitWithError 'Could not create hooks directory: ' workTree
18+
19+
FileAppend("#!/bin/sh`npowershell -command 'for ($i = 0; $i -lt 50; $i++) { echo $i; sleep -milliseconds 10 }' &`n", '.git/hooks/pre-commit')
20+
if A_LastError
21+
ExitWithError 'Could not create pre-commit hook: ' A_LastError
22+
23+
Run 'wt.exe -d . ' A_ComSpec ' /d', , , &childPid
24+
if A_LastError
25+
ExitWithError 'Error launching CMD: ' A_LastError
26+
Info 'Launched CMD: ' childPid
27+
if not WinWait(A_ComSpec, , 9)
28+
ExitWithError 'CMD window did not appear'
29+
Info 'Got window'
30+
WinActivate
31+
CloseWindow := true
32+
WinMove 0, 0
33+
Info 'Moved window to top left (so that the bottom is not cut off)'
34+
35+
Info('Setting committer identity')
36+
Send('git config user.name Test{Enter}git config user.email [email protected]{Enter}')
37+
38+
Info('Committing')
39+
Send('git commit --allow-empty -m zOMG{Enter}')
40+
; Wait for the hook to finish printing
41+
WaitForRegExInWindowsTerminal('`n49$', 'Timed out waiting for commit to finish', 'Hook finished', 100000)
42+
43+
; Verify that CursorUp shows the previous command
44+
Send('{Up}')
45+
Sleep 150
46+
Text := CaptureTextFromWindowsTerminal()
47+
if not RegExMatch(Text, 'git commit --allow-empty -m zOMG *$')
48+
ExitWithError 'Cursor Up did not work: ' Text
49+
Info('Match!')
50+
51+
Send('^C')
52+
Send('exit{Enter}')
53+
Sleep 50
54+
CleanUpWorkTree()

0 commit comments

Comments
 (0)