| $signature = @' | |
| using System; | |
| using System.Runtime.InteropServices; | |
| public class Win32 { | |
| [DllImport("kernel32.dll", SetLastError = true)] | |
| public static extern IntPtr GetStdHandle(int nStdHandle); | |
| [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)] | |
| public static extern bool WriteConsoleInputW( | |
| IntPtr hConsoleInput, | |
| byte[] lpBuffer, | |
| uint nLength, | |
| out uint lpNumberOfEventsWritten | |
| ); | |
| [DllImport("kernel32.dll", SetLastError = true)] | |
| public static extern bool AllocConsole(); | |
| [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)] | |
| public static extern IntPtr CreateFile( | |
| string lpFileName, | |
| uint dwDesiredAccess, | |
| uint dwShareMode, | |
| IntPtr lpSecurityAttributes, | |
| uint dwCreationDisposition, | |
| uint dwFlagsAndAttributes, | |
| IntPtr hTemplateFile | |
| ); | |
| } | |
| '@ | |
| Add-Type -TypeDefinition $signature | |
| [Win32]::AllocConsole() | |
| $GENERIC_READ = [uint32]2147483648 # 0x80000000, used decimal to avoid signed overflow in PowerShell | |
| $GENERIC_WRITE = [uint32]0x40000000 | |
| $FILE_SHARE_READ = 1 | |
| $OPEN_EXISTING = 3 | |
| $hStdin = [Win32]::CreateFile("CONIN$", $GENERIC_READ -bor $GENERIC_WRITE, $FILE_SHARE_READ, [IntPtr]::Zero, $OPEN_EXISTING, 0, [IntPtr]::Zero) | |
| if ($hStdin -eq [IntPtr]::Zero -or $hStdin.ToInt64() -eq -1) { | |
| throw "Failed to open CONIN$. Error code: $([System.Runtime.InteropServices.Marshal]::GetLastWin32Error())" | |
| } | |
| function Send-Key([uint16]$virtualKeyCode, [uint16]$char = 0) { | |
| $bytes = New-Object byte[] 20 | |
| # EventType = 1 (KEY_EVENT) | |
| $bytes[0] = 1 | |
| $bytes[1] = 0 | |
| # bKeyDown = 1 | |
| $bytes[4] = 1 | |
| $bytes[5] = 0 | |
| $bytes[6] = 0 | |
| $bytes[7] = 0 | |
| # wRepeatCount = 1 | |
| $bytes[8] = 1 | |
| $bytes[9] = 0 | |
| # wVirtualKeyCode | |
| $bytes[10] = $virtualKeyCode -band 0xFF | |
| $bytes[11] = ($virtualKeyCode -shr 8) -band 0xFF | |
| # wVirtualScanCode = 0 | |
| $bytes[12] = 0 | |
| $bytes[13] = 0 | |
| # uChar | |
| $bytes[14] = $char -band 0xFF | |
| $bytes[15] = ($char -shr 8) -band 0xFF | |
| # dwControlKeyState = 0 | |
| $bytes[16] = 0 | |
| $bytes[17] = 0 | |
| $bytes[18] = 0 | |
| $bytes[19] = 0 | |
| $written = 0 | |
| $result = [Win32]::WriteConsoleInputW($hStdin, $bytes, 1, [ref]$written) | |
| Write-Host "WriteConsoleInput result: $result, written: $written" | |
| if (-not $result) { | |
| throw "WriteConsoleInput failed" | |
| } | |
| } | |
| $outputFile = "integration_output.txt" | |
| if (Test-Path $outputFile) { Remove-Item $outputFile } | |
| # Start Dart process | |
| $process = Start-Process dart -ArgumentList "test/bin/echo_stdin.dart", $outputFile -NoNewWindow -PassThru | |
| # Wait for READY | |
| $timeout = 10 | |
| $elapsed = 0 | |
| while ($elapsed -lt $timeout) { | |
| if (Test-Path $outputFile) { | |
| $content = Get-Content $outputFile | |
| if ($content -contains "READY") { | |
| break | |
| } | |
| } | |
| Start-Sleep -Milliseconds 500 | |
| $elapsed += 0.5 | |
| } | |
| if ($elapsed -ge $timeout) { | |
| if ($process) { $process | Stop-Process -Force } | |
| throw "Timed out waiting for READY" | |
| } | |
| # Send keys | |
| Send-Key -virtualKeyCode 0x41 -char 97 # 'a' | |
| Start-Sleep -Milliseconds 200 | |
| Send-Key -virtualKeyCode 0x26 # Up Arrow | |
| Start-Sleep -Milliseconds 200 | |
| Send-Key -virtualKeyCode 0x20 -char 32 # Space | |
| Start-Sleep -Milliseconds 200 | |
| Send-Key -virtualKeyCode 0x0D # Enter | |
| Start-Sleep -Milliseconds 200 | |
| # Kill process | |
| $process | Stop-Process -Force | |
| # Verify output | |
| $content = Get-Content $outputFile | |
| Write-Host "Output content:" | |
| $content | ForEach-Object { Write-Host $_ } | |
| $expectedA = "[97]" | |
| $expectedUp = "[27,91,65]" | |
| $expectedSpace = "[32]" | |
| $expectedEnter = "[13]" | |
| $hasA = $false | |
| $hasUp = $false | |
| $hasSpace = $false | |
| $hasEnter = $false | |
| foreach ($line in $content) { | |
| if ($line -eq $expectedA) { $hasA = $true } | |
| if ($line -eq $expectedUp) { $hasUp = $true } | |
| if ($line -eq $expectedSpace) { $hasSpace = $true } | |
| if ($line -eq $expectedEnter) { $hasEnter = $true } | |
| } | |
| if (-not $hasA) { throw "Failed to find 'a' in output" } | |
| if (-not $hasUp) { throw "Failed to find Up Arrow in output" } | |
| if (-not $hasSpace) { throw "Failed to find Space in output" } | |
| if (-not $hasEnter) { throw "Failed to find Enter in output" } | |
| Write-Host "Integration test passed!" | |
| Remove-Item $outputFile |