param( [string]$MsiPath = (Join-Path $PSScriptRoot '..\..\installer\output\Housing.msi'), [string]$FixtureRoot = (Join-Path $PSScriptRoot '..\..\obj\installer-checks') ) $ErrorActionPreference = 'Stop' $MsiPath = (Resolve-Path -LiteralPath $MsiPath).Path $fixture = Join-Path ([IO.Path]::GetFullPath($FixtureRoot)) ([Guid]::NewGuid().ToString('N')) $desktop = Join-Path $fixture 'OneDrive\Desktop' $localData = Join-Path $fixture 'LocalAppData' New-Item -ItemType Directory -Force -Path $desktop, $localData | Out-Null $script:checks = 0 function Assert-Check($Condition, [string]$Message) { if (-not $Condition) { throw "FAIL: $Message" } $script:checks++ } # Open the database read-only. No InstallInitialize/InstallExecute/InstallFinalize actions run. $installer = New-Object -ComObject WindowsInstaller.Installer $database = $installer.OpenDatabase($MsiPath, 0) function Read-Rows([string]$Query, [int]$Columns) { $view = $database.OpenView($Query) [void]$view.Execute() while ($record = $view.Fetch()) { $values = @(for ($column = 1; $column -le $Columns; $column++) { $record.StringData($column) }) ,$values } [void]$view.Close() } $folder = @(Read-Rows "SELECT Directory_Parent, DefaultDir FROM Directory WHERE Directory = 'DESKTOPSETTINGSFOLDER'" 2) Assert-Check ($folder.Count -eq 1 -and $folder[0][0] -eq 'DesktopFolder' -and $folder[0][1] -eq 'Housing') 'desktop Housing folder' $shortcut = @(Read-Rows "SELECT Directory_, Target, WkDir, Icon_ FROM Shortcut WHERE Shortcut = 'HousingDesktopShortcut'" 4) Assert-Check ($shortcut.Count -eq 1 -and $shortcut[0][0] -eq 'DesktopFolder' -and $shortcut[0][1] -eq '[INSTALLFOLDER]Housing.exe' -and $shortcut[0][2] -eq 'INSTALLFOLDER' -and $shortcut[0][3] -eq 'HousingIcon.exe') 'desktop shortcut target, working directory and icon' $names = @('Hardware', 'Database', 'InspectionSettings') $defaultConditions = @{} $migrationConditions = @{} foreach ($name in $names) { $component = @(Read-Rows "SELECT Directory_, Attributes, Condition FROM Component WHERE Component = 'Desktop${name}Settings'" 3) Assert-Check ($component.Count -eq 1 -and $component[0][0] -eq 'DESKTOPSETTINGSFOLDER') "$name destination" $attributes = [int]$component[0][1] Assert-Check (($attributes -band 16) -and ($attributes -band 128)) "$name Permanent and NeverOverwrite" $defaultConditions[$name] = $component[0][2] $migration = @(Read-Rows "SELECT Condition FROM Component WHERE Component = 'Migrate${name}Settings'" 1) $migrationConditions[$name] = $migration[0][0] $copy = @(Read-Rows "SELECT SourceFolder, DestFolder, Options FROM MoveFile WHERE FileKey = 'CopyLegacy$name'" 3) Assert-Check ($copy.Count -eq 1 -and $copy[0][0] -eq ('LEGACY_' + $name.ToUpperInvariant()) -and $copy[0][1] -eq 'DESKTOPSETTINGSFOLDER' -and $copy[0][2] -eq '0') "$name migration copies without deleting source" } Add-Type @' using System; using System.Text; using System.Runtime.InteropServices; public static class SettingsMsiCheck { [DllImport("msi.dll", CharSet = CharSet.Unicode)] public static extern uint MsiOpenPackageEx(string path, uint options, out uint handle); [DllImport("msi.dll", CharSet = CharSet.Unicode)] public static extern uint MsiSetProperty(uint handle, string name, string value); [DllImport("msi.dll", CharSet = CharSet.Unicode)] public static extern uint MsiGetProperty(uint handle, string name, StringBuilder value, ref uint size); [DllImport("msi.dll", CharSet = CharSet.Unicode)] public static extern uint MsiDoAction(uint handle, string action); [DllImport("msi.dll", CharSet = CharSet.Unicode)] public static extern int MsiEvaluateCondition(uint handle, string condition); [DllImport("msi.dll")] public static extern uint MsiCloseHandle(uint handle); [DllImport("msi.dll")] public static extern uint MsiSetInternalUI(uint level, IntPtr owner); } '@ function Get-MsiProperty([uint32]$Handle, [string]$Name) { $value = New-Object Text.StringBuilder 4096 [uint32]$size = $value.Capacity $result = [SettingsMsiCheck]::MsiGetProperty($Handle, $Name, $value, [ref]$size) if ($result -ne 0) { throw "MsiGetProperty failed: $result" } $value.ToString() } $previousUi = [SettingsMsiCheck]::MsiSetInternalUI(2, [IntPtr]::Zero) try { foreach ($scenario in @('fresh', 'legacy', 'existing-desktop')) { if ($scenario -eq 'legacy') { New-Item -ItemType Directory -Force -Path (Join-Path $localData 'Housing'), (Join-Path $localData 'AMO Housing') | Out-Null foreach ($name in $names) { Set-Content -LiteralPath (Join-Path $localData "Housing\$name.ini") -Value 'old settings' } Set-Content -LiteralPath (Join-Path $localData 'AMO Housing\InspectionSettings.ini') -Value 'saved limits' } if ($scenario -eq 'existing-desktop') { New-Item -ItemType Directory -Force -Path (Join-Path $desktop 'Housing') | Out-Null foreach ($name in $names) { Set-Content -LiteralPath (Join-Path $desktop "Housing\$name.ini") -Value 'desktop settings' } } [uint32]$handle = 0 $result = [SettingsMsiCheck]::MsiOpenPackageEx($MsiPath, 1, [ref]$handle) if ($result -ne 0) { throw "MsiOpenPackageEx failed: $result" } try { foreach ($entry in @{ DesktopFolder = $desktop + '\'; LocalAppDataFolder = $localData + '\' }.GetEnumerator()) { $result = [SettingsMsiCheck]::MsiSetProperty($handle, $entry.Key, $entry.Value) if ($result -ne 0) { throw "MsiSetProperty failed: $result" } } $result = [SettingsMsiCheck]::MsiDoAction($handle, 'AppSearch') if ($result -ne 0) { throw "AppSearch failed: $result" } if (Get-MsiProperty $handle 'LEGACY_SAVED_INSPECTION') { $result = [SettingsMsiCheck]::MsiDoAction($handle, 'SetLEGACY_INSPECTIONSETTINGS') if ($result -ne 0) { throw "Inspection migration selection failed: $result" } Assert-Check ((Get-MsiProperty $handle 'LEGACY_INSPECTIONSETTINGS') -eq (Join-Path $localData 'AMO Housing\InspectionSettings.ini')) "$scenario saved limits precedence" } foreach ($name in $names) { Assert-Check ([SettingsMsiCheck]::MsiEvaluateCondition($handle, $defaultConditions[$name]) -eq [int]($scenario -eq 'fresh')) "$scenario $name default selection" Assert-Check ([SettingsMsiCheck]::MsiEvaluateCondition($handle, $migrationConditions[$name]) -eq [int]($scenario -eq 'legacy')) "$scenario $name migration selection" } } finally { [void][SettingsMsiCheck]::MsiCloseHandle($handle) } } } finally { [void][SettingsMsiCheck]::MsiSetInternalUI($previousUi, [IntPtr]::Zero) } Write-Host "PASS: $script:checks installer checks; no product was installed."