128 lines
7.3 KiB
PowerShell
128 lines
7.3 KiB
PowerShell
Set-StrictMode -Version Latest
|
|
$ErrorActionPreference = 'Stop'
|
|
. (Join-Path $PSScriptRoot '..\windows\planet-launcher.ps1')
|
|
|
|
function Assert-Equal($Expected, $Actual, $Label) {
|
|
if ($Expected -cne $Actual) { throw "$Label expected [$Expected], got [$Actual]" }
|
|
}
|
|
|
|
foreach ($prefix in @('\\wsl$\', '\\wsl.localhost\')) {
|
|
$target = Get-PlanetTarget ($prefix + 'Ubuntu-24.04\home\alice\planet app')
|
|
Assert-Equal 'Ubuntu-24.04' $target.Distro 'UNC distribution'
|
|
Assert-Equal '/home/alice/planet app' $target.LinuxProjectRoot 'UNC Linux path'
|
|
}
|
|
$specialRoot = '/home/alice/planet ' + [char]0x5730 + [char]0x7403 + " ' & % ! `$ (demo)"
|
|
$target = Get-PlanetTarget ('\\wsl$\Debian' + $specialRoot.Replace('/', '\'))
|
|
Assert-Equal $specialRoot $target.LinuxProjectRoot 'Special characters'
|
|
|
|
function Invoke-WslText {
|
|
param([string[]]$Arguments)
|
|
if ($Arguments[1] -eq 'printenv') { return 'Ubuntu' }
|
|
Assert-Equal 'C:\Projects\Planet Space' $Arguments[-1] 'Windows path remains one argument'
|
|
return '/mnt/c/Projects/Planet Space'
|
|
}
|
|
$driveTarget = Get-PlanetTarget 'C:\Projects\Planet Space'
|
|
Assert-Equal 'Ubuntu' $driveTarget.Distro 'Default distribution'
|
|
Assert-Equal '/mnt/c/Projects/Planet Space' $driveTarget.LinuxProjectRoot 'Mounted drive'
|
|
|
|
foreach ($elevated in @($false, $true)) {
|
|
$bootstrap = Get-PlanetBootstrap $target -RunElevated:$elevated
|
|
$tokens = $null; $errors = $null
|
|
[void][Management.Automation.Language.Parser]::ParseInput($bootstrap, [ref]$tokens, [ref]$errors)
|
|
if ($errors.Count) { throw ($errors | Out-String) }
|
|
$encoded = ConvertTo-PlanetEncodedCommand $bootstrap
|
|
Assert-Equal $bootstrap ([Text.Encoding]::Unicode.GetString([Convert]::FromBase64String($encoded))) 'Encoded command roundtrip'
|
|
if ($bootstrap.Contains($specialRoot)) { throw 'Project path must remain encoded data' }
|
|
$match = [regex]::Match($bootstrap, "FromBase64String\('([A-Za-z0-9+/=]+)'\)")
|
|
$decoded = [Text.Encoding]::UTF8.GetString([Convert]::FromBase64String($match.Groups[1].Value)) | ConvertFrom-Json
|
|
Assert-Equal $specialRoot $decoded.LinuxProjectRoot 'Bound target roundtrip'
|
|
}
|
|
|
|
function Test-PlanetAdministrator { return $true }
|
|
function wsl.exe {
|
|
$script:CapturedWslArguments = $args
|
|
$global:LASTEXITCODE = 17
|
|
Write-Output 'live-wsl-output'
|
|
}
|
|
$liveOutput = Start-PlanetRestart $target -AlreadyElevated
|
|
Assert-Equal 'live-wsl-output' $liveOutput 'WSL output is streamed, not returned as an exit-code array'
|
|
Assert-Equal 17 $script:PlanetLauncherExitCode 'WSL exit status preserved'
|
|
Assert-Equal $specialRoot $script:CapturedWslArguments[3] 'Working directory is one native argument'
|
|
Assert-Equal ($specialRoot + '/scripts/windows/restart.zsh') $script:CapturedWslArguments[8] 'Bridge path is one native argument'
|
|
|
|
$testDirectory = Join-Path ([IO.Path]::GetTempPath()) ('planet-launcher-tests-' + [guid]::NewGuid().ToString('N'))
|
|
[void][IO.Directory]::CreateDirectory($testDirectory)
|
|
try {
|
|
$launcher = Install-PlanetDesktopLauncher $target -Desktop $testDirectory
|
|
Assert-Equal 'Planet.cmd' ([IO.Path]::GetFileName($launcher)) 'First install'
|
|
$content = [IO.File]::ReadAllText($launcher)
|
|
if ($content -notmatch 'DisableDelayedExpansion' -or $content -notmatch '--install-only') {
|
|
throw 'Generated launcher lacks safe CMD mode or install-only option'
|
|
}
|
|
Assert-Equal $launcher (Install-PlanetDesktopLauncher $target -Desktop $testDirectory) 'Repeat install'
|
|
$other = Install-PlanetDesktopLauncher $driveTarget -Desktop $testDirectory
|
|
if ($other -eq $launcher) { throw 'Different projects must not overwrite each other' }
|
|
Assert-Equal $content ([IO.File]::ReadAllText($launcher)) 'Original target preserved'
|
|
[IO.File]::WriteAllText($launcher, 'unrelated user file')
|
|
$alternate = Install-PlanetDesktopLauncher $target -Desktop $testDirectory
|
|
if ($alternate -eq $launcher) { throw 'Unrelated file overwritten' }
|
|
Assert-Equal 'unrelated user file' ([IO.File]::ReadAllText($launcher)) 'User file preserved'
|
|
$missing = @{ Distro = 'Ubuntu'; LinuxProjectRoot = '/missing/planet-launcher-test-' + [guid]::NewGuid() }
|
|
try { Assert-PlanetTarget $missing; throw 'Missing project accepted' }
|
|
catch { if ($_.Exception.Message -notmatch 'P_WINDOWS_LAUNCHER_TARGET_MISSING') { throw } }
|
|
Write-Host 'PASS: UNC aliases, drive mapping, special paths, encoding, repeat installation, collisions, missing project'
|
|
} finally {
|
|
$resolved = [IO.Path]::GetFullPath($testDirectory)
|
|
$tempRoot = [IO.Path]::GetFullPath([IO.Path]::GetTempPath())
|
|
if (-not $resolved.StartsWith($tempRoot, [StringComparison]::OrdinalIgnoreCase) -or
|
|
[IO.Path]::GetFileName($resolved) -notlike 'planet-launcher-tests-*') { throw 'Unsafe cleanup path' }
|
|
Remove-Item -LiteralPath $resolved -Recurse -Force
|
|
}
|
|
|
|
function Get-Command {
|
|
param([string]$Name, [string]$CommandType, [string]$ErrorAction)
|
|
if ($script:AvailableLaunchCommands.ContainsKey($Name)) {
|
|
return [pscustomobject]@{ Source = $script:AvailableLaunchCommands[$Name] }
|
|
}
|
|
}
|
|
$pwshPath = 'C:\Program Files\PowerShell\7\pwsh.exe'
|
|
$terminalPath = 'C:\Users\Test\AppData\Local\Microsoft\WindowsApps\wt.exe'
|
|
$legacyPath = 'C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe'
|
|
foreach ($hasTerminal in @($true, $false)) {
|
|
foreach ($hasPwsh in @($true, $false)) {
|
|
$script:AvailableLaunchCommands = @{ 'powershell.exe' = $legacyPath }
|
|
if ($hasTerminal) { $script:AvailableLaunchCommands['wt.exe'] = $terminalPath }
|
|
if ($hasPwsh) { $script:AvailableLaunchCommands['pwsh.exe'] = $pwshPath }
|
|
$launch = Get-PlanetTerminalLaunch 'TESTPAYLOAD'
|
|
$expectedShell = if ($hasPwsh) { $pwshPath } else { $legacyPath }
|
|
$expectedExecutable = if ($hasTerminal) { $terminalPath } else { $expectedShell }
|
|
Assert-Equal $expectedExecutable $launch.FilePath 'Terminal/shell priority'
|
|
if ($hasTerminal -and -not $launch.ArgumentList.Contains('"' + $expectedShell + '"')) {
|
|
throw 'Terminal shell path must be quoted, including Program Files paths'
|
|
}
|
|
if ($launch.ArgumentList -notmatch '-EncodedCommand TESTPAYLOAD$') {
|
|
throw 'Encoded bootstrap was not preserved'
|
|
}
|
|
}
|
|
}
|
|
function Test-PlanetAdministrator { return $script:IsTestAdministrator }
|
|
function Start-Process {
|
|
param($FilePath, $ArgumentList, $WindowStyle, $Verb)
|
|
$script:CapturedLaunch = @{ FilePath = $FilePath; ArgumentList = $ArgumentList; Verb = $Verb }
|
|
$script:LaunchCount++
|
|
}
|
|
$script:AvailableLaunchCommands = @{ 'pwsh.exe' = $pwshPath; 'wt.exe' = $terminalPath }
|
|
$script:LaunchCount = 0
|
|
foreach ($isAdmin in @($false, $true)) {
|
|
$script:IsTestAdministrator = $isAdmin
|
|
Start-PlanetRestart $target
|
|
Assert-Equal $terminalPath $script:CapturedLaunch.FilePath 'Admin and ordinary entry both use Terminal'
|
|
if ($isAdmin -and $script:CapturedLaunch.Verb) { throw 'Already elevated entry must not request UAC again' }
|
|
if (-not $isAdmin) { Assert-Equal 'RunAs' $script:CapturedLaunch.Verb 'Ordinary entry requires elevation' }
|
|
}
|
|
$script:IsTestAdministrator = $false
|
|
try { Start-PlanetRestart $target -AlreadyElevated; throw 'Unelevated child accepted' }
|
|
catch { if ($_.Exception.Message -ne 'Administrator elevation was not granted.') { throw } }
|
|
Assert-Equal 2 $script:LaunchCount 'Child must not recursively launch more windows'
|
|
Write-Host 'PASS: Terminal/PowerShell priority, quoted paths, elevation and no recursive dispatch'
|