返回 CodeWhale
installer-path-regression.tests.ps1
根目录 / scripts / installer / installer-path-regression.tests.ps1
1 [CmdletBinding()]
2 param(
3 [switch] $AllowUserPathMutation
4 )
5
6 $ErrorActionPreference = 'Stop'
7 Set-StrictMode -Version Latest
8
9 if ([System.Environment]::OSVersion.Platform -ne [System.PlatformID]::Win32NT) {
10 throw 'The installer PATH regression only supports Windows.'
11 }
12 if (-not $AllowUserPathMutation) {
13 throw 'Pass -AllowUserPathMutation to confirm this test may temporarily replace the current-user PATH.'
14 }
15
16 function Get-RawUserPath {
17 param(
18 [Parameter(Mandatory = $true)]
19 [Microsoft.Win32.RegistryKey] $EnvironmentKey
20 )
21
22 if (-not (@($EnvironmentKey.GetValueNames()) -contains 'Path')) {
23 return $null
24 }
25
26 return [string] $EnvironmentKey.GetValue(
27 'Path',
28 $null,
29 [Microsoft.Win32.RegistryValueOptions]::DoNotExpandEnvironmentNames
30 )
31 }
32
33 $repoRoot = (Resolve-Path (Join-Path $PSScriptRoot '..\..')).Path
34 $testRoot = Join-Path `
35 ([System.IO.Path]::GetTempPath()) `
36 "codewhale-installer-path-$PID-$([guid]::NewGuid().ToString('N'))"
37 $stageRoot = Join-Path $testRoot 'source'
38 $stageInstallerDir = Join-Path $stageRoot 'scripts\installer'
39 $installDir = Join-Path $testRoot 'installed'
40 $programsDir = [Environment]::GetFolderPath([Environment+SpecialFolder]::Programs)
41 $shortcutDir = Join-Path $programsDir 'CodeWhale'
42 $shortcut = Join-Path $shortcutDir 'CodeWhale.lnk'
43 $environmentKey = [Microsoft.Win32.Registry]::CurrentUser.CreateSubKey('Environment')
44 if ($null -eq $environmentKey) {
45 throw 'Could not open or create the current-user Environment registry key.'
46 }
47
48 $originalPathExists = @($environmentKey.GetValueNames()) -contains 'Path'
49 if ($originalPathExists) {
50 $originalPath = Get-RawUserPath -EnvironmentKey $environmentKey
51 $originalPathKind = $environmentKey.GetValueKind('Path')
52 }
53 else {
54 $originalPath = $null
55 $originalPathKind = [Microsoft.Win32.RegistryValueKind]::ExpandString
56 }
57
58 try {
59 [void] (New-Item -ItemType Directory -Path $stageInstallerDir -Force)
60 Copy-Item -LiteralPath (Join-Path $repoRoot 'LICENSE') -Destination (Join-Path $stageRoot 'LICENSE')
61 Copy-Item `
62 -LiteralPath (Join-Path $PSScriptRoot 'codewhale.nsi') `
63 -Destination (Join-Path $stageInstallerDir 'codewhale.nsi')
64 Copy-Item `
65 -LiteralPath (Join-Path $PSScriptRoot 'update-user-path.ps1') `
66 -Destination (Join-Path $stageInstallerDir 'update-user-path.ps1')
67 Copy-Item `
68 -LiteralPath (Join-Path $PSScriptRoot 'codewhale.bat') `
69 -Destination (Join-Path $stageInstallerDir 'codewhale.bat')
70
71 foreach ($binary in @('codewhale.exe', 'codew.exe', 'codewhale-tui.exe')) {
72 [System.IO.File]::WriteAllBytes(
73 (Join-Path $stageInstallerDir $binary),
74 [byte[]] @(0x4d, 0x5a)
75 )
76 }
77
78 $makensisCandidates = @(
79 "${env:ProgramFiles(x86)}\NSIS\makensis.exe",
80 "$env:ProgramFiles\NSIS\makensis.exe"
81 )
82 $makensis = $makensisCandidates |
83 Where-Object { Test-Path -LiteralPath $_ -PathType Leaf } |
84 Select-Object -First 1
85 if ([string]::IsNullOrWhiteSpace($makensis)) {
86 throw 'makensis.exe was not found after installing NSIS.'
87 }
88
89 Push-Location $stageInstallerDir
90 try {
91 & $makensis '/DVERSION=0.0.0-test' 'codewhale.nsi'
92 if ($LASTEXITCODE -ne 0) {
93 throw "makensis.exe exited with code $LASTEXITCODE."
94 }
95 }
96 finally {
97 Pop-Location
98 }
99
100 $installer = Join-Path $stageInstallerDir 'CodeWhaleSetup.exe'
101 if (-not (Test-Path -LiteralPath $installer -PathType Leaf)) {
102 throw 'CodeWhaleSetup.exe was not produced.'
103 }
104
105 $canaries = [System.Collections.Generic.List[string]]::new()
106 $index = 1
107 while ((($canaries -join ';').Length) -lt 1800) {
108 $canaries.Add(('C:\CWCanary\DevelopmentTool{0:D4}\bin' -f $index))
109 $index++
110 }
111 $seedPath = $canaries -join ';'
112 $environmentKey.SetValue(
113 'Path',
114 $seedPath,
115 [Microsoft.Win32.RegistryValueKind]::ExpandString
116 )
117
118 $installArguments = [System.Collections.Generic.List[string]]::new()
119 $installArguments.Add('/S')
120 $installArguments.Add("/D=$installDir")
121 $installProcess = Start-Process `
122 -FilePath $installer `
123 -ArgumentList @($installArguments) `
124 -Wait `
125 -PassThru
126 if ($installProcess.ExitCode -ne 0) {
127 throw "CodeWhaleSetup.exe exited with code $($installProcess.ExitCode)."
128 }
129
130 $expectedBin = Join-Path $installDir 'bin'
131 $afterInstall = Get-RawUserPath -EnvironmentKey $environmentKey
132 if (([string] $afterInstall) -cne "$seedPath;$expectedBin") {
133 throw "The installer did not preserve the seeded long PATH exactly. Before=$($seedPath.Length), after=$(([string] $afterInstall).Length)."
134 }
135 if ($environmentKey.GetValueKind('Path') -ne [Microsoft.Win32.RegistryValueKind]::ExpandString) {
136 throw 'The installer changed the user PATH registry value kind.'
137 }
138
139 $launcher = Join-Path $expectedBin 'codewhale.bat'
140 if (-not (Test-Path -LiteralPath $launcher -PathType Leaf)) {
141 throw 'The installer did not install codewhale.bat.'
142 }
143 $launcherText = [System.IO.File]::ReadAllText($launcher)
144 if ($launcherText -notmatch 'where wt') {
145 throw 'Installed codewhale.bat does not prefer Windows Terminal.'
146 }
147 if ($launcherText -notmatch 'codewhale\.exe') {
148 throw 'Installed codewhale.bat does not launch codewhale.exe.'
149 }
150 if (-not (Test-Path -LiteralPath $shortcut -PathType Leaf)) {
151 throw 'The installer did not create the Start Menu shortcut.'
152 }
153 $shell = New-Object -ComObject WScript.Shell
154 try {
155 $lnk = $shell.CreateShortcut($shortcut)
156 $expectedTarget = [System.IO.Path]::GetFullPath($launcher)
157 $actualTarget = [System.IO.Path]::GetFullPath($lnk.TargetPath)
158 if ($actualTarget -cne $expectedTarget) {
159 throw "Start Menu shortcut target was '$($lnk.TargetPath)', expected '$launcher'."
160 }
161 }
162 finally {
163 [void] [System.Runtime.InteropServices.Marshal]::ReleaseComObject($shell)
164 }
165
166 $uninstaller = Join-Path $installDir 'Uninstall.exe'
167 if (-not (Test-Path -LiteralPath $uninstaller -PathType Leaf)) {
168 throw 'Uninstall.exe was not produced.'
169 }
170 $uninstallProcess = Start-Process `
171 -FilePath $uninstaller `
172 -ArgumentList '/S' `
173 -Wait `
174 -PassThru
175 if ($uninstallProcess.ExitCode -ne 0) {
176 throw "Uninstall.exe exited with code $($uninstallProcess.ExitCode)."
177 }
178
179 $afterUninstall = Get-RawUserPath -EnvironmentKey $environmentKey
180 if (([string] $afterUninstall) -cne $seedPath) {
181 throw 'Install followed by uninstall did not restore the seeded long PATH exactly.'
182 }
183 if (Test-Path -LiteralPath $launcher -PathType Leaf) {
184 throw 'Uninstall did not remove codewhale.bat.'
185 }
186 if (Test-Path -LiteralPath $shortcut -PathType Leaf) {
187 throw 'Uninstall did not remove the Start Menu shortcut.'
188 }
189 if (Test-Path -LiteralPath $shortcutDir) {
190 throw 'Uninstall did not remove the Start Menu folder.'
191 }
192
193 Write-Host "Full NSIS installer PATH regression passed with $($seedPath.Length) characters."
194 }
195 finally {
196 $uninstaller = Join-Path $installDir 'Uninstall.exe'
197 if (Test-Path -LiteralPath $uninstaller -PathType Leaf) {
198 try {
199 [void] (Start-Process -FilePath $uninstaller -ArgumentList '/S' -Wait -PassThru)
200 }
201 catch {
202 Write-Warning "Best-effort test uninstallation failed: $_"
203 }
204 }
205
206 if ($originalPathExists) {
207 $environmentKey.SetValue('Path', $originalPath, $originalPathKind)
208 }
209 else {
210 $environmentKey.DeleteValue('Path', $false)
211 }
212 $environmentKey.Close()
213
214 if (Test-Path -LiteralPath $shortcut -PathType Leaf) {
215 Remove-Item -LiteralPath $shortcut -Force -ErrorAction SilentlyContinue
216 }
217 if (Test-Path -LiteralPath $shortcutDir) {
218 Remove-Item -LiteralPath $shortcutDir -Recurse -Force -ErrorAction SilentlyContinue
219 }
220
221 if (Test-Path -LiteralPath $testRoot) {
222 Remove-Item -LiteralPath $testRoot -Recurse -Force -ErrorAction SilentlyContinue
223 }
224 }
225
225 lines Plain Text