100 lines
7.9 KiB
PowerShell
100 lines
7.9 KiB
PowerShell
param([string]$Executable = (Join-Path $PSScriptRoot '../build/KSwitcher.exe'), [switch]$Network)
|
|
$ErrorActionPreference='Stop'
|
|
. (Join-Path $PSScriptRoot '../resources/update.ps1') -Library
|
|
$script:checks=0
|
|
function Assert($Value,[string]$Name) { if (!$Value) { throw $Name }; ++$script:checks }
|
|
function Reject([scriptblock]$Action,[string]$Name) { $rejected=$false; try { & $Action } catch { $rejected=$true }; Assert $rejected $Name }
|
|
$root=Join-Path ([IO.Path]::GetTempPath()) ('KSwitcher-update-tests-'+[guid]::NewGuid().ToString('N'))
|
|
[IO.Directory]::CreateDirectory($root) | Out-Null
|
|
try {
|
|
$binaryInfo = [Diagnostics.FileVersionInfo]::GetVersionInfo((Resolve-Path -LiteralPath $Executable).Path)
|
|
$binaryVersion = Get-StableVersion $binaryInfo.FileVersion
|
|
Assert ($null -ne $binaryVersion) 'executable has a stable version'
|
|
Assert ($binaryInfo.FileDescription -ceq ('KSwitcher ' + [char]0x2014 + ' native keyboard and clipboard utility')) 'executable description preserves Unicode em dash'
|
|
Assert ((Get-StableVersion 'v0.10.0') -gt (Get-StableVersion 'v0.2.9')) 'numeric versions'
|
|
Assert ($null -eq (Get-StableVersion 'v1.0.0-beta')) 'prerelease rejected'
|
|
Assert ($null -eq (Get-StableVersion 'v1.0.0;whoami')) 'invalid version rejected'
|
|
$assets=@(
|
|
@{name='KSwitcher-win-x64.zip';browser_download_url='https://gitea.hrynco.com/hrynco/hrynco-kswitcher/releases/download/v0.2.1/KSwitcher-win-x64.zip'},
|
|
@{name='SHA256SUMS.txt';browser_download_url='https://gitea.hrynco.com/hrynco/hrynco-kswitcher/releases/download/v0.2.1/SHA256SUMS.txt'}
|
|
)
|
|
$release=@{draft=$false;prerelease=$false;tag_name='v0.2.1';assets=$assets}
|
|
Assert ((Select-Release @($release) ([version]'0.2.0')).version -eq 'v0.2.1') 'newer release'
|
|
Assert ($null -eq (Select-Release @($release) ([version]'0.2.1'))) 'same version'
|
|
Assert ($null -eq (Select-Release @($release) ([version]'0.3.0'))) 'no downgrade'
|
|
$draft=@{draft=$true;prerelease=$false;tag_name='v9.0.0';assets=$assets}
|
|
Assert ((Select-Release @($draft,$release) ([version]'0.2.0')).version -eq 'v0.2.1') 'draft ignored'
|
|
Reject { Select-Release @(@{draft=$false;prerelease=$false;tag_name='v2.0.0';assets=@()}) ([version]'0.2.0') } 'incomplete release rejected'
|
|
foreach ($url in @('http://gitea.hrynco.com/hrynco/hrynco-kswitcher/releases/download/v1/a','https://evil.example/a','https://gitea.hrynco.com/other/a','https://gitea.hrynco.com:8443/hrynco/hrynco-kswitcher/releases/download/v1/a')) {
|
|
Reject { Assert-ReleaseUrl $url } 'untrusted URL rejected'
|
|
}
|
|
$source=Join-Path $root 'source'; [IO.Directory]::CreateDirectory($source) | Out-Null
|
|
Copy-Item -LiteralPath $Executable -Destination (Join-Path $source 'KSwitcher.exe')
|
|
$configurator = Join-Path (Split-Path $Executable) 'KSwitcher.Config.exe'
|
|
if (Test-Path -LiteralPath $configurator) {
|
|
Copy-Item -LiteralPath $configurator -Destination (Join-Path $source 'KSwitcher.Config.exe')
|
|
}
|
|
foreach ($name in @('LICENSE','json-LICENSE.txt','README.md')) { [IO.File]::WriteAllText((Join-Path $source $name),'fixture') }
|
|
[IO.Directory]::CreateDirectory((Join-Path $source 'dictionaries')) | Out-Null
|
|
[IO.File]::WriteAllText((Join-Path $source 'dictionaries/en.txt'),'new dictionary')
|
|
Add-Type -AssemblyName System.IO.Compression.FileSystem
|
|
$archive=Join-Path $root 'release.zip'
|
|
[IO.Compression.ZipFile]::CreateFromDirectory($source,$archive)
|
|
$sum=Join-Path $root 'SHA256SUMS.txt'
|
|
((Get-FileHash -LiteralPath $archive -Algorithm SHA256).Hash+' KSwitcher-win-x64.zip') | Set-Content -LiteralPath $sum
|
|
Assert-Checksum $archive $sum; ++$script:checks
|
|
$stage=Join-Path $root 'stage'
|
|
Expand-Release $archive $stage $binaryVersion; ++$script:checks
|
|
Reject { Expand-Release $archive (Join-Path $root 'wrong-version') ([version]'9.0.0') } 'version mismatch rejected'
|
|
('0'*64+' KSwitcher-win-x64.zip') | Set-Content -LiteralPath $sum
|
|
Reject { Assert-Checksum $archive $sum } 'corrupt package rejected'
|
|
$bad=Join-Path $root 'bad.zip'
|
|
$z=[IO.Compression.ZipFile]::Open($bad,[IO.Compression.ZipArchiveMode]::Create)
|
|
$null=$z.CreateEntry('../escape.exe'); $z.Dispose()
|
|
Reject { Expand-Release $bad (Join-Path $root 'badstage') ([version]'0.2.1') } 'zip traversal rejected'
|
|
$target=Join-Path $root 'install'; $backup=Join-Path $root 'backup'
|
|
[IO.Directory]::CreateDirectory((Join-Path $target 'data')) | Out-Null
|
|
[IO.Directory]::CreateDirectory((Join-Path $target 'dictionaries')) | Out-Null
|
|
[IO.File]::WriteAllText((Join-Path $target 'data/settings.json'),'personal settings')
|
|
[IO.File]::WriteAllText((Join-Path $target 'dictionaries/en.txt'),'personal dictionary')
|
|
[IO.File]::WriteAllText((Join-Path $target 'KSwitcher.exe'),'old binary fixture')
|
|
$changed=[Collections.Generic.List[string]]::new()
|
|
Install-Files $stage $target $backup $changed
|
|
Assert ((Get-FileHash -LiteralPath (Join-Path $target 'KSwitcher.exe')).Hash -eq (Get-FileHash -LiteralPath $Executable).Hash) 'replacement installed'
|
|
if (Test-Path -LiteralPath $configurator) {
|
|
Assert ((Get-FileHash -LiteralPath (Join-Path $target 'KSwitcher.Config.exe')).Hash -eq (Get-FileHash -LiteralPath $configurator).Hash) 'configurator installed'
|
|
}
|
|
Assert ([IO.File]::ReadAllText((Join-Path $target 'data/settings.json')) -eq 'personal settings') 'settings preserved'
|
|
Assert ([IO.File]::ReadAllText((Join-Path $target 'dictionaries/en.txt')) -eq 'personal dictionary') 'dictionary preserved'
|
|
Undo-Files $target $backup $changed
|
|
Assert ([IO.File]::ReadAllText((Join-Path $target 'KSwitcher.exe')) -eq 'old binary fixture') 'rollback restores original'
|
|
Assert (!(Test-Path -LiteralPath (Join-Path $target 'README.md'))) 'rollback removes newly introduced files'
|
|
Assert (!(Test-Path -LiteralPath (Join-Path $target 'KSwitcher.Config.exe'))) 'rollback removes newly introduced configurator'
|
|
# Fail after the original exe has moved to backup, as if the staged file vanished.
|
|
$stage2=Join-Path $root 'stage2'; [IO.Directory]::CreateDirectory($stage2) | Out-Null
|
|
[IO.File]::WriteAllText((Join-Path $stage2 'KSwitcher.exe'),'new binary fixture')
|
|
$locked=[IO.File]::Open((Join-Path $stage2 'KSwitcher.exe'),[IO.FileMode]::Open,[IO.FileAccess]::Read,[IO.FileShare]::None)
|
|
$changed2=[Collections.Generic.List[string]]::new()
|
|
try { Reject { Install-Files $stage2 $target $backup $changed2 } 'locked staged file fails safely' } finally { $locked.Dispose() }
|
|
Undo-Files $target $backup $changed2
|
|
Assert ([IO.File]::ReadAllText((Join-Path $target 'KSwitcher.exe')) -eq 'old binary fixture') 'partial install rollback'
|
|
if ($Network) {
|
|
$metadata=Join-Path $root 'live-releases.json'
|
|
Receive-File 'https://gitea.hrynco.com/api/v1/repos/hrynco/hrynco-kswitcher/releases?limit=100' $metadata 2MB
|
|
$live=Select-Release (Get-Content -LiteralPath $metadata -Raw | ConvertFrom-Json) ([version]'0.0.0')
|
|
Assert ($null -ne $live) 'live repository exposes a complete stable release'
|
|
$liveZip=Join-Path $root 'live.zip'; $liveSum=Join-Path $root 'live-sums.txt'
|
|
Receive-File $live.zip $liveZip 50MB
|
|
Receive-File $live.checksum $liveSum 64KB
|
|
Assert-Checksum $liveZip $liveSum; ++$script:checks
|
|
Expand-Release $liveZip (Join-Path $root 'live-stage') (Get-StableVersion $live.version); ++$script:checks
|
|
Reject { Receive-File 'https://gitea.hrynco.com/api/v1/repos/hrynco/kswitcher-updater-missing-test-repository' (Join-Path $root 'missing') 2MB } 'server failure reported'
|
|
Write-Output "Downloaded and validated $($live.version) in a disposable test directory; no application was updated."
|
|
}
|
|
Write-Output "Passed $script:checks updater regression checks"
|
|
} finally {
|
|
if ([IO.Path]::GetDirectoryName($root) -eq [IO.Path]::GetTempPath().TrimEnd('\') -and (Split-Path -Leaf $root) -like 'KSwitcher-update-tests-*') {
|
|
Remove-Item -LiteralPath $root -Recurse -Force
|
|
}
|
|
}
|