@@ -31,7 +31,13 @@ function Write-Json {
3131}
3232
3333function New-TestBundle {
34- param ([string ]$Name )
34+ param (
35+ [string ]$Name ,
36+ [string ]$BundleVersion = " 0.2.0" ,
37+ [string ]$AppVersion = $BundleVersion ,
38+ [string ]$CoreCpuVersion = $BundleVersion ,
39+ [string ]$CoreCudaVersion = $BundleVersion
40+ )
3541 $root = Join-Path $testRoot $Name
3642 New-Item - ItemType Directory - Path (Join-Path $root " updater" ) - Force | Out-Null
3743 Copy-Item - LiteralPath (Join-Path $repoRoot " update.bat" ) - Destination (Join-Path $root " update.bat" )
@@ -48,11 +54,17 @@ function New-TestBundle {
4854 ) | Set-Content - LiteralPath $verifier - Encoding ASCII
4955 $version = [ordered ]@ {
5056 product = " audiocpp-portable"
51- version = " 0.2.0 "
57+ version = $BundleVersion
5258 channel = " stable"
5359 platform = " windows-x64"
5460 python = " 3.11"
55- components = [ordered ]@ { app = " 0.2.0" ; core_cpu = " 0.2.0" ; core_cuda = " 0.2.0" ; python_env = " 0.2.0" ; updater = $testUpdaterVersion }
61+ components = [ordered ]@ {
62+ app = $AppVersion
63+ core_cpu = $CoreCpuVersion
64+ core_cuda = $CoreCudaVersion
65+ python_env = " 0.2.0"
66+ updater = $testUpdaterVersion
67+ }
5668 }
5769 Write-Json $version (Join-Path $root " version.json" )
5870 return $root
@@ -63,7 +75,8 @@ function New-TestArchive {
6375 [string ]$Directory ,
6476 [string ]$FileName ,
6577 [string ]$ManagedPath ,
66- [string ]$Content = " new managed content"
78+ [string ]$Content = " new managed content" ,
79+ [string ]$Component = " app"
6780 )
6881 $stage = Join-Path $Directory (" stage-" + [guid ]::NewGuid().ToString(" N" ))
6982 $payload = Join-Path $stage " payload"
@@ -73,7 +86,7 @@ function New-TestArchive {
7386 $file = Get-Item - LiteralPath $filePath
7487 $index = [ordered ]@ {
7588 schema = 1
76- component = " app "
89+ component = $Component
7790 files = @ ([ordered ]@ {
7891 path = $ManagedPath
7992 size = [int64 ]$file.Length
@@ -135,14 +148,16 @@ function New-CustomManifest {
135148 [string ]$Directory ,
136149 [object []]$Components ,
137150 [string ]$MinimumUpdater = $testUpdaterVersion ,
138- [string []]$HealthChecks = @ ()
151+ [string []]$HealthChecks = @ (),
152+ [string ]$ManifestVersion = " 0.2.1" ,
153+ [string ]$SupportedFrom = " >=0.2.0 <0.3.0"
139154 )
140155 $value = [ordered ]@ {
141156 schema = 1
142157 product = " audiocpp-portable"
143158 platform = " windows-x64"
144- version = " 0.2.1 "
145- supported_from = " >=0.2.0 <0.3.0 "
159+ version = $ManifestVersion
160+ supported_from = $SupportedFrom
146161 minimum_updater = $MinimumUpdater
147162 components = $Components
148163 preserve = $requiredPreserve
@@ -468,6 +483,62 @@ function Test-UpdaterSelfUpdate {
468483 Assert-True ((Read-JsonFile (Join-Path $root " version.json" )).components.updater -eq " 1.2.0" ) " version.json did not record the new updater version"
469484}
470485
486+ function Test-UpgradeMatrixFromSupportedVersions {
487+ $cases = @ (
488+ [pscustomobject ]@ {
489+ Version = " 0.2.0"
490+ App = " 0.2.0"
491+ CoreCpu = " 0.2.0"
492+ CoreCuda = " 0.2.0"
493+ },
494+ [pscustomobject ]@ {
495+ Version = " 0.2.1"
496+ App = " 0.2.1"
497+ CoreCpu = " 0.2.1"
498+ CoreCuda = " 0.2.1"
499+ },
500+ [pscustomobject ]@ {
501+ Version = " 0.2.2"
502+ App = " 0.2.1"
503+ CoreCpu = " 0.2.1"
504+ CoreCuda = " 0.2.2"
505+ }
506+ )
507+
508+ foreach ($case in $cases ) {
509+ $root = New-TestBundle " upgrade-$ ( $case.Version ) " `
510+ - BundleVersion $case.Version `
511+ - AppVersion $case.App `
512+ - CoreCpuVersion $case.CoreCpu `
513+ - CoreCudaVersion $case.CoreCuda
514+ New-Item - ItemType Directory - Path (Join-Path $root " models" ) - Force | Out-Null
515+ Set-Content - LiteralPath (Join-Path $root " models\user-model.bin" ) - Value " preserve me" - Encoding ASCII
516+
517+ $assets = Join-Path $root " assets"
518+ New-Item - ItemType Directory - Path $assets - Force | Out-Null
519+ $appArchive = New-TestArchive $assets " app-v0.3.0.zip" " webui\upgrade-matrix.txt" " app 0.3.0" " app"
520+ $cpuArchive = New-TestArchive $assets " core-cpu-v0.3.0.zip" " cpu\audiocpp_cli.exe" " cpu 0.3.0" " core-cpu"
521+ $cudaArchive = New-TestArchive $assets " core-cuda-v0.3.0.zip" " gpu\audiocpp_cli.exe" " cuda 0.3.0" " core-cuda"
522+ $components = @ (
523+ (New-ComponentDescriptor " app" " 0.3.0" $appArchive ),
524+ (New-ComponentDescriptor " core-cpu" " 0.3.0" $cpuArchive ),
525+ (New-ComponentDescriptor " core-cuda" " 0.3.0" $cudaArchive )
526+ )
527+ $manifest = New-CustomManifest $assets $components `
528+ - ManifestVersion " 0.3.0" `
529+ - SupportedFrom " >=0.2.0"
530+
531+ $result = Invoke-TestUpdater $root " --apply" $manifest
532+ Assert-True ($result.ExitCode -eq 0 ) " upgrade from $ ( $case.Version ) failed: $ ( $result.Output ) "
533+ $installed = Read-JsonFile (Join-Path $root " version.json" )
534+ Assert-True ([string ]$installed.version -eq " 0.3.0" ) " upgrade from $ ( $case.Version ) did not commit target version"
535+ Assert-True ([string ]$installed.components.app -eq " 0.3.0" ) " upgrade from $ ( $case.Version ) did not update app"
536+ Assert-True ([string ]$installed.components.core_cpu -eq " 0.3.0" ) " upgrade from $ ( $case.Version ) did not update core-cpu"
537+ Assert-True ([string ]$installed.components.core_cuda -eq " 0.3.0" ) " upgrade from $ ( $case.Version ) did not update core-cuda"
538+ Assert-True ((Get-Content - LiteralPath (Join-Path $root " models\user-model.bin" ) - Raw) -match " preserve me" ) " upgrade from $ ( $case.Version ) changed preserved model data"
539+ }
540+ }
541+
471542function Test-ReleaseBuilder {
472543 $root = Join-Path $testRoot " release-builder"
473544 $portable = Join-Path $root " portable"
@@ -499,13 +570,15 @@ function Test-ReleaseBuilder {
499570 Expand-Archive - LiteralPath $pythonDepsZip - DestinationPath $pythonDepsSource
500571 Remove-Item - LiteralPath (Join-Path $pythonDepsSource " files.json" ) - Force
501572 $output = Join-Path $root " output"
573+ $releaseNotes = Join-Path $root " custom-release-notes.md"
574+ Set-Content - LiteralPath $releaseNotes - Value " # Custom release notes" - Encoding UTF8
502575 $builder = Join-Path $repoRoot " scripts\build_update_release.ps1"
503576 $previousErrorAction = $ErrorActionPreference
504577 $ErrorActionPreference = " Continue"
505578 $builderOutput = @ (& powershell.exe - NoLogo - NoProfile - ExecutionPolicy Bypass - File $builder `
506579 - Version " 0.2.1" - PortableRoot $portable - OutputDir $output - MinisignSecretKey $secret `
507580 - MinisignPublicKey $publicKey - MinisignPath $signer - PythonDepsSource $pythonDepsSource `
508- - IncludeUpdater - UpdaterMinisignBinary $signer - Stable - AllowDirty 2>&1 )
581+ - IncludeUpdater - UpdaterMinisignBinary $signer - ReleaseNotesPath $releaseNotes - Stable - AllowDirty 2>&1 )
509582 $exitCode = $LASTEXITCODE
510583 $ErrorActionPreference = $previousErrorAction
511584 Assert-True ($exitCode -eq 0 ) " release builder failed: $ ( $builderOutput -join " `n " ) "
@@ -528,6 +601,18 @@ function Test-ReleaseBuilder {
528601 Assert-True (@ ($manifest.components ).Count -eq 5 ) " release manifest does not contain all five component types"
529602 Assert-True (@ ($manifest.preserve ) -contains " webui/voice/**" ) " release manifest omitted voice preservation"
530603 Assert-True ([string ]$manifest.supported_from -eq " >=0.2.0" ) " release manifest does not keep bootstrap installs eligible for later versions"
604+ Assert-True ((Get-Content - LiteralPath (Join-Path $output " release-notes.md" ) - Raw) -match " Custom release notes" ) " release builder did not use custom release notes"
605+ $appExpanded = Join-Path $root " app-expanded"
606+ Expand-Archive - LiteralPath (Join-Path $output " audiocpp-app-v0.2.1.zip" ) - DestinationPath $appExpanded
607+ foreach ($relative in @ (
608+ " payload\tools\model_manager.py" ,
609+ " payload\model_specs\qwen3_asr.json" ,
610+ " payload\assets\framework\models\marblenet_vad\marblenet_vad.safetensors" ,
611+ " payload\assets\framework\models\marblenet_vad\marblenet_vad_config.json" ,
612+ " payload\assets\framework\models\marblenet_vad\marblenet_vad_labels.txt"
613+ )) {
614+ Assert-True (Test-Path - LiteralPath (Join-Path $appExpanded $relative ) - PathType Leaf) " app ZIP omitted $relative "
615+ }
531616 foreach ($directory in @ (" cpu" , " gpu" )) {
532617 $archive = Join-Path $output " audiocpp-core-$ ( $directory -replace ' gpu' , ' cuda' ) -win-x64-v0.2.1.zip"
533618 $expanded = Join-Path $root " core-$directory -expanded"
@@ -565,6 +650,7 @@ try {
565650 " Test-ProtectedPathAndLock" ,
566651 " Test-PythonDependencyUpdateAndRollback" ,
567652 " Test-UpdaterSelfUpdate" ,
653+ " Test-UpgradeMatrixFromSupportedVersions" ,
568654 " Test-ReleaseBuilder"
569655 )
570656 foreach ($test in $tests ) {
0 commit comments