diff --git a/config/tweaks.json b/config/tweaks.json index ff0eecbb..1987c15e 100644 --- a/config/tweaks.json +++ b/config/tweaks.json @@ -1459,28 +1459,44 @@ ], "link": "https://winutil.christitus.com/code-reference/tweaks/customize-preferences/scrollbars" }, - "WPFToggleMultiplaneOverlay": { + "WPFMultiplaneOverlay": { "Content": "Multiplane Overlay", - "Description": "Multiplane Overlay compose multiple image layers, which can sometimes cause issues with graphics cards.", + "Description": "Multiplane Overlay composes multiple image layers, which can sometimes cause issues with graphics cards. Changes to this preference are applied immediately.", "category": "Customize Preferences", "panel": "2", - "Type": "Toggle", + "Type": "Combobox", + "ComboItems": [ + "Enabled", + "Disabled (Compatibility)", + "Fully Disabled" + ], + "ComboDescriptions": { + "Enabled": "Uses Windows' default overlay behavior.", + "Disabled (Compatibility)": "Disables MPO using OverlayTestMode=5, the less aggressive compatibility method.", + "Fully Disabled": "Disables MPO using OverlayTestMode=5 and DisableOverlays=1, the more aggressive method." + }, "registry": [ { "Path": "HKLM:\\SOFTWARE\\Microsoft\\Windows\\Dwm", "Name": "OverlayTestMode", - "Value": "0", "Type": "DWord", - "OriginalValue": "5", - "DefaultState": "true" + "DefaultValue": "0", + "Values": { + "Enabled": "", + "Disabled (Compatibility)": "5", + "Fully Disabled": "5" + } }, { "Path": "HKLM:\\SYSTEM\\CurrentControlSet\\Control\\GraphicsDrivers", "Name": "DisableOverlays", - "Value": "0", "Type": "DWord", - "OriginalValue": "1", - "DefaultState": "true" + "DefaultValue": "0", + "Values": { + "Enabled": "", + "Disabled (Compatibility)": "", + "Fully Disabled": "1" + } } ], "link": "https://winutil.christitus.com/code-reference/tweaks/customize-preferences/multiplaneoverlay" diff --git a/docs/src/content/docs/code-reference/architecture.mdx b/docs/src/content/docs/code-reference/architecture.mdx index 4d7ffa3f..000e6211 100644 --- a/docs/src/content/docs/code-reference/architecture.mdx +++ b/docs/src/content/docs/code-reference/architecture.mdx @@ -385,6 +385,8 @@ Update UI - `Description`: What it does - `category`: Essential/Advanced/Customize - `registry`: Registry changes to make +- `registry[].Values`: Per-state values for a registry-backed combobox +- `registry[].DefaultValue`: Effective value when the registry entry is absent - `service`: Services to change - `OriginalValue/State`: For undo functionality diff --git a/functions/private/Get-WinUtilRegistryComboState.ps1 b/functions/private/Get-WinUtilRegistryComboState.ps1 new file mode 100644 index 00000000..91e1fb21 --- /dev/null +++ b/functions/private/Get-WinUtilRegistryComboState.ps1 @@ -0,0 +1,36 @@ +function Get-WinUtilRegistryComboState { + <# + .SYNOPSIS + Finds the configured combo-box state matching the current registry values. + + .PARAMETER Registry + Registry settings containing a value mapping for each supported state. + + .OUTPUTS + The name of the matching state. + #> + param( + [Parameter(Mandatory)] + $Registry + ) + + foreach ($state in $Registry[0].Values.PSObject.Properties) { + $stateMatches = $true + foreach ($setting in @($Registry)) { + $currentValue = Get-WinUtilRegistryComboValue -Setting $setting + $actualValue = if ($currentValue.Exists -and $null -ne $currentValue.Value) { $currentValue.Value } else { $setting.DefaultValue } + $configuredValue = $setting.Values.PSObject.Properties[$state.Name].Value + # Removal represents the effective Windows default when matching the current state. + $expectedValue = if ($configuredValue -eq "") { $setting.DefaultValue } else { $configuredValue } + if ([string]$actualValue -ne [string]$expectedValue) { + $stateMatches = $false + break + } + } + if ($stateMatches) { + return $state.Name + } + } + + throw "Registry values do not match a supported state." +} diff --git a/functions/private/Get-WinUtilRegistryComboValue.ps1 b/functions/private/Get-WinUtilRegistryComboValue.ps1 new file mode 100644 index 00000000..bc2a280c --- /dev/null +++ b/functions/private/Get-WinUtilRegistryComboValue.ps1 @@ -0,0 +1,24 @@ +function Get-WinUtilRegistryComboValue { + <# + .SYNOPSIS + Reads one registry value for a registry-backed combo-box state. + + .PARAMETER Setting + The registry setting from the combo-box configuration. + #> + param( + [Parameter(Mandatory)] + $Setting + ) + + try { + $item = Get-ItemProperty -Path $Setting.Path -Name $Setting.Name -ErrorAction Stop + $property = $item.PSObject.Properties[$Setting.Name] + return [pscustomobject]@{ Exists = $null -ne $property; Value = $property.Value } + } catch [System.Management.Automation.PSArgumentException] { + # The registry provider uses PSArgumentException when a named value is absent. + return [pscustomobject]@{ Exists = $false; Value = $null } + } catch [System.Management.Automation.ItemNotFoundException] { + return [pscustomobject]@{ Exists = $false; Value = $null } + } +} diff --git a/functions/private/Invoke-WinUtilCurrentSystem.ps1 b/functions/private/Invoke-WinUtilCurrentSystem.ps1 index ed0f4a3d..68594ab6 100644 --- a/functions/private/Invoke-WinUtilCurrentSystem.ps1 +++ b/functions/private/Invoke-WinUtilCurrentSystem.ps1 @@ -61,7 +61,7 @@ Function Invoke-WinUtilCurrentSystem { $serviceKeys = $entry.service $entryType = $entry.Type - if ($registryKeys -or $serviceKeys) { + if (($registryKeys -or $serviceKeys) -and $entryType -ne "Combobox") { $Values = @() if ($entryType -eq "Toggle") { diff --git a/functions/private/Invoke-WinUtilTweaks.ps1 b/functions/private/Invoke-WinUtilTweaks.ps1 index 406efec0..87673ea8 100644 --- a/functions/private/Invoke-WinUtilTweaks.ps1 +++ b/functions/private/Invoke-WinUtilTweaks.ps1 @@ -62,7 +62,7 @@ function Invoke-WinUtilTweaks { } } if ($sync.configs.tweaks.$CheckBox.registry) { - $sync.configs.tweaks.$CheckBox.registry | ForEach-Object { + $sync.configs.tweaks.$CheckBox.registry | Where-Object { -not $psitem.Values } | ForEach-Object { Set-WinUtilRegistry -Name $psitem.Name -Path $psitem.Path -Type $psitem.Type -Value $psitem.$($values.registry) } } diff --git a/functions/private/Set-WinUtilRegistryComboState.ps1 b/functions/private/Set-WinUtilRegistryComboState.ps1 new file mode 100644 index 00000000..544b8c1e --- /dev/null +++ b/functions/private/Set-WinUtilRegistryComboState.ps1 @@ -0,0 +1,78 @@ +function Set-WinUtilRegistryComboState { + <# + .SYNOPSIS + Applies and verifies a config-defined registry combo-box state. + + .PARAMETER Registry + Registry settings containing a value mapping for each supported state. + + .PARAMETER State + The state name to apply. + #> + param( + [Parameter(Mandatory)] + $Registry, + + [Parameter(Mandatory)] + [string]$State + ) + + if ($Registry[0].Values.PSObject.Properties.Name -notcontains $State) { + throw "Unknown registry state '$State'." + } + + # Preserve exact prior values so a partial update can be rolled back. + $previousValues = foreach ($setting in @($Registry)) { + $currentValue = Get-WinUtilRegistryComboValue -Setting $setting + [pscustomobject]@{ Setting = $setting; Exists = $currentValue.Exists; Value = $currentValue.Value } + } + + try { + foreach ($setting in @($Registry)) { + $configuredValue = $setting.Values.PSObject.Properties[$State].Value + $previousValue = $previousValues | Where-Object Setting -EQ $setting + if ($configuredValue -ne "" -or $previousValue.Exists) { + Set-WinUtilRegistry -Name $setting.Name -Path $setting.Path -Type $setting.Type -Value $configuredValue + } + } + + # Set-WinUtilRegistry reports write errors without throwing, so verify each result explicitly. + foreach ($setting in @($Registry)) { + $configuredValue = $setting.Values.PSObject.Properties[$State].Value + $currentValue = Get-WinUtilRegistryComboValue -Setting $setting + $writeMatches = if ($configuredValue -eq "") { + -not $currentValue.Exists + } else { + $currentValue.Exists -and [string]$currentValue.Value -eq [string]$configuredValue + } + if (-not $writeMatches) { + throw "The registry values did not match the requested state." + } + } + } catch { + $applyError = $_.Exception.Message + if ([string]::IsNullOrWhiteSpace($applyError)) { + $applyError = "The registry values did not match the requested state." + } + $rollbackFailed = $false + foreach ($previousValue in $previousValues) { + try { + $currentValue = Get-WinUtilRegistryComboValue -Setting $previousValue.Setting + if ($previousValue.Exists -or $currentValue.Exists) { + $rollbackValue = if ($previousValue.Exists) { $previousValue.Value } else { "" } + Set-WinUtilRegistry -Name $previousValue.Setting.Name -Path $previousValue.Setting.Path -Type $previousValue.Setting.Type -Value $rollbackValue + } + $restoredValue = Get-WinUtilRegistryComboValue -Setting $previousValue.Setting + if ($restoredValue.Exists -ne $previousValue.Exists -or ($restoredValue.Exists -and [string]$restoredValue.Value -ne [string]$previousValue.Value)) { + $rollbackFailed = $true + } + } catch { + $rollbackFailed = $true + } + } + if ($rollbackFailed) { + throw "Unable to apply registry state '$State': $applyError. The previous registry state could not be restored." + } + throw "Unable to apply registry state '$State': $applyError" + } +} diff --git a/functions/public/Invoke-WPFUIElements.ps1 b/functions/public/Invoke-WPFUIElements.ps1 index 83ea2730..29629942 100644 --- a/functions/public/Invoke-WPFUIElements.ps1 +++ b/functions/public/Invoke-WPFUIElements.ps1 @@ -76,6 +76,8 @@ function Invoke-WPFUIElements { Description = $entryInfo.description Type = $entryInfo.type ComboItems = $entryInfo.ComboItems + ComboDescriptions = $entryInfo.ComboDescriptions + Registry = $entryInfo.registry Checked = $entryInfo.Checked ButtonWidth = $entryInfo.ButtonWidth GroupName = $entryInfo.GroupName # Added for RadioButton groupings @@ -247,6 +249,7 @@ function Invoke-WPFUIElements { $label = New-Object Windows.Controls.Label $label.Content = $entryInfo.Content $label.HorizontalAlignment = "Left" + $label.ToolTip = $entryInfo.Description $label.VerticalAlignment = "Center" $label.SetResourceReference([Windows.Controls.Control]::FontSizeProperty, "ButtonFontSize") $label.UseLayoutRounding = $true @@ -261,11 +264,27 @@ function Invoke-WPFUIElements { $comboBox.SetResourceReference([Windows.Controls.Control]::MarginProperty, "ButtonMargin") $comboBox.SetResourceReference([Windows.Controls.Control]::FontSizeProperty, "ButtonFontSize") $comboBox.UseLayoutRounding = $true + $comboBox.Tag = [pscustomobject]@{ + Registry = $entryInfo.Registry + State = $null + } [System.Windows.Automation.AutomationProperties]::SetName($comboBox, $entryInfo.Content) - foreach ($comboitem in ($entryInfo.ComboItems -split " ")) { + $comboItems = if ($entryInfo.ComboItems -is [string]) { + $entryInfo.ComboItems -split " " + } else { + @($entryInfo.ComboItems) + } + + foreach ($comboitem in $comboItems) { $comboBoxItem = New-Object Windows.Controls.ComboBoxItem $comboBoxItem.Content = $comboitem + if ($entryInfo.ComboDescriptions) { + $comboDescription = $entryInfo.ComboDescriptions.PSObject.Properties[$comboitem].Value + if ($comboDescription) { + $comboBoxItem.ToolTip = $comboDescription + } + } $comboBoxItem.SetResourceReference([Windows.Controls.Control]::FontSizeProperty, "ButtonFontSize") $comboBoxItem.UseLayoutRounding = $true $comboBox.Items.Add($comboBoxItem) | Out-Null @@ -274,22 +293,82 @@ function Invoke-WPFUIElements { $horizontalStackPanel.Children.Add($comboBox) | Out-Null $itemsControl.Items.Add($horizontalStackPanel) | Out-Null - $comboBox.SelectedIndex = 0 + if ($entryInfo.Registry -and @($entryInfo.Registry)[0].Values) { + try { + $comboBox.Tag.State = Get-WinUtilRegistryComboState -Registry $entryInfo.Registry + $comboBox.SelectedIndex = @($comboBox.Items.Content).IndexOf([string]$comboBox.Tag.State) + } catch { + $unknownStateItem = New-Object Windows.Controls.ComboBoxItem + $unknownStateItem.Content = "Custom / Unknown - select a state" + $unknownStateItem.IsEnabled = $false + $unknownStateItem.ToolTip = "$($_.Exception.Message) Select one of the supported states to replace these values." + $comboBox.Items.Add($unknownStateItem) | Out-Null + $comboBox.SelectedItem = $unknownStateItem + $comboBox.ToolTip = $unknownStateItem.ToolTip + } + } else { + $comboBox.SelectedIndex = 0 + } # Set initial text if ($comboBox.Items.Count -gt 0) { - $comboBox.Text = $comboBox.Items[0].Content + $comboBox.Text = $comboBox.SelectedItem.Content } + $sync[$entryInfo.Name] = $comboBox + # Add SelectionChanged event handler to update the text property $comboBox.Add_SelectionChanged({ $selectedItem = $this.SelectedItem if ($selectedItem) { $this.Text = $selectedItem.Content + $registry = $this.Tag.Registry + if ($registry -and $selectedItem.IsEnabled -and $selectedItem.Content -ne $this.Tag.State) { + try { + Set-WinUtilRegistryComboState -Registry $registry -State $selectedItem.Content + $this.Tag.State = $selectedItem.Content + $this.ToolTip = $null + $unknownStateItem = @($this.Items) | Where-Object Content -EQ "Custom / Unknown - select a state" | Select-Object -First 1 + if ($unknownStateItem) { + $this.Items.Remove($unknownStateItem) + } + } catch { + $applyError = $_.Exception.Message + if ([string]::IsNullOrWhiteSpace($applyError)) { + $applyError = "Unable to apply registry state '$($selectedItem.Content)'." + } + $previousState = if ($this.Tag.State) { $this.Tag.State } else { "Custom / Unknown - select a state" } + $this.SelectedItem = @($this.Items) | Where-Object Content -EQ $previousState | Select-Object -First 1 + [System.Windows.MessageBox]::Show( + $applyError, + "WinUtil", + [System.Windows.MessageBoxButton]::OK, + [System.Windows.MessageBoxImage]::Warning + ) | Out-Null + } + } } }) - $sync[$entryInfo.Name] = $comboBox + if ($entryInfo.Registry -and @($entryInfo.Registry)[0].Values -and $entryInfo.Link) { + $textBlock = New-Object Windows.Controls.TextBlock + $textBlock.Name = $comboBox.Name + "Link" + $textBlock.Text = "(?)" + $textBlock.ToolTip = $entryInfo.Link + $textBlock.Style = $HoverTextBlockStyle + $textBlock.UseLayoutRounding = $true + $textBlock.VerticalAlignment = "Center" + $textBlock.SetResourceReference([Windows.Controls.Control]::FontSizeProperty, "FontSize") + $textBlock.Tag = $comboBox + + $textBlock.Add_MouseUp({ + [System.Object]$Sender = $args[0] + Start-Process $Sender.ToolTip -ErrorAction Stop + }) + + $horizontalStackPanel.Children.Add($textBlock) | Out-Null + $sync[$textBlock.Name] = $textBlock + } } "Button" { diff --git a/pester/configs.Tests.ps1 b/pester/configs.Tests.ps1 index 5fa20f38..9f0705b6 100644 --- a/pester/configs.Tests.ps1 +++ b/pester/configs.Tests.ps1 @@ -189,6 +189,14 @@ Describe "Tweaks config" { foreach ($registryEntry in @($tweak.Value.registry)) { if ($null -eq $registryEntry) { continue } + if ($registryEntry.Values) { + if ($registryEntry.PSObject.Properties.Name -notcontains "DefaultValue" -or + [string]::IsNullOrWhiteSpace([string]$registryEntry.DefaultValue)) { + $invalidTweaks.Add("$($tweak.Name),registry") + } + continue + } + if ($registryEntry.PSObject.Properties.Name -notcontains "OriginalValue" -or [string]::IsNullOrWhiteSpace([string]$registryEntry.OriginalValue)) { $invalidTweaks.Add("$($tweak.Name),registry") @@ -429,6 +437,19 @@ Describe "UI-rendered config entries" { if (-not (Test-WinUtilHasNonEmptyProperty -Object $entry.Value -Name "ComboItems")) { $invalidEntries.Add("$($entry.Name) combobox missing ComboItems") } + $statefulRegistry = @($entry.Value.registry | Where-Object Values) + if ($statefulRegistry.Count -gt 0) { + $comboItems = @($entry.Value.ComboItems) + if ($statefulRegistry.Count -ne @($entry.Value.registry).Count) { + $invalidEntries.Add("$($entry.Name) registry states must all use Values") + } else { + foreach ($setting in $statefulRegistry) { + if (Compare-Object $comboItems @($setting.Values.PSObject.Properties.Name)) { + $invalidEntries.Add("$($entry.Name) ComboItems and registry states do not match") + } + } + } + } } else { if (-not (Test-WinUtilHasNonEmptyProperty -Object $entry.Value -Name "Description")) { $invalidEntries.Add("$($entry.Name) missing Description") @@ -442,7 +463,12 @@ Describe "UI-rendered config entries" { foreach ($registryEntry in @($entry.Value.registry)) { if ($null -eq $registryEntry) { continue } - foreach ($missingField in (Get-WinUtilMissingRequiredFields -EntryName "$($entry.Name),registry" -Entry $registryEntry -RequiredFields @("Path", "Name", "Type", "Value", "OriginalValue"))) { + $requiredRegistryFields = if ($entry.Value.Type -eq "Combobox" -and $statefulRegistry.Count -gt 0) { + @("Path", "Name", "Type", "DefaultValue", "Values") + } else { + @("Path", "Name", "Type", "Value", "OriginalValue") + } + foreach ($missingField in (Get-WinUtilMissingRequiredFields -EntryName "$($entry.Name),registry" -Entry $registryEntry -RequiredFields $requiredRegistryFields)) { $invalidEntries.Add($missingField) } } diff --git a/pester/multiplane-overlay.Tests.ps1 b/pester/multiplane-overlay.Tests.ps1 new file mode 100644 index 00000000..dee7b900 --- /dev/null +++ b/pester/multiplane-overlay.Tests.ps1 @@ -0,0 +1,178 @@ +#=========================================================================== +# Tests - Multiplane Overlay +#=========================================================================== + +BeforeAll { + $script:repoRoot = (Resolve-Path (Join-Path $PSScriptRoot "..")).Path + $script:config = Get-Content (Join-Path $script:repoRoot "config\tweaks.json") -Raw | ConvertFrom-Json + $script:states = $script:config.WPFMultiplaneOverlay.registry + . (Join-Path $script:repoRoot "functions\private\Get-WinUtilRegistryComboState.ps1") + . (Join-Path $script:repoRoot "functions\private\Get-WinUtilRegistryComboValue.ps1") + . (Join-Path $script:repoRoot "functions\private\Set-WinUtilRegistryComboState.ps1") + + function Set-WinUtilRegistry { + param($Name, $Path, $Type, $Value) + } +} + +Describe "Multiplane Overlay configuration" { + It "keeps every state and registry action in tweaks.json" { + $script:config.WPFMultiplaneOverlay.Type | Should -Be "Combobox" + $script:config.WPFMultiplaneOverlay.ComboItems | Should -Be @("Enabled", "Disabled (Compatibility)", "Fully Disabled") + $script:states.Count | Should -Be 2 + $script:states[0].Values.PSObject.Properties.Name | Should -Be $script:config.WPFMultiplaneOverlay.ComboItems + $script:states[0].Values.PSObject.Properties.Value | Should -Be @("", "5", "5") + $script:states[1].Values.PSObject.Properties.Value | Should -Be @("", "", "1") + } + + It "uses the generic combo registry handler" { + $renderer = Get-Content (Join-Path $script:repoRoot "functions\public\Invoke-WPFUIElements.ps1") -Raw + + $renderer | Should -Match 'Get-WinUtilRegistryComboState' + $renderer | Should -Match 'Set-WinUtilRegistryComboState' + $renderer | Should -Not -Match 'WPFMultiplaneOverlay' + } +} + +Describe "Get-WinUtilRegistryComboState" { + It "treats missing registry properties and paths as absent" -TestCases @( + @{ Exception = [System.Management.Automation.PSArgumentException]::new("Property is missing") } + @{ Exception = [System.Management.Automation.ItemNotFoundException]::new("Path is missing") } + ) { + param($Exception) + Mock Get-ItemProperty { throw $Exception } + + Get-WinUtilRegistryComboState -Registry $script:states | Should -Be "Enabled" + } + + It "reports Enabled when the values are absent or zero" -TestCases @( + @{ OverlayTestMode = $null; DisableOverlays = $null } + @{ OverlayTestMode = 0; DisableOverlays = 0 } + ) { + param($OverlayTestMode, $DisableOverlays) + Mock Get-ItemProperty { + if ($Name -eq "OverlayTestMode") { + return [pscustomobject]@{ OverlayTestMode = $OverlayTestMode } + } + [pscustomobject]@{ DisableOverlays = $DisableOverlays } + } + + Get-WinUtilRegistryComboState -Registry $script:states | Should -Be "Enabled" + } + + It "reports each disabled state" -TestCases @( + @{ OverlayTestMode = 5; DisableOverlays = $null; Expected = "Disabled (Compatibility)" } + @{ OverlayTestMode = 5; DisableOverlays = 1; Expected = "Fully Disabled" } + ) { + param($OverlayTestMode, $DisableOverlays, $Expected) + Mock Get-ItemProperty { + if ($Name -eq "OverlayTestMode") { + return [pscustomobject]@{ OverlayTestMode = $OverlayTestMode } + } + [pscustomobject]@{ DisableOverlays = $DisableOverlays } + } + + Get-WinUtilRegistryComboState -Registry $script:states | Should -Be $Expected + } + + It "rejects an unsupported combination" { + Mock Get-ItemProperty { + if ($Name -eq "OverlayTestMode") { + return [pscustomobject]@{ OverlayTestMode = 0 } + } + [pscustomobject]@{ DisableOverlays = 1 } + } + + { Get-WinUtilRegistryComboState -Registry $script:states } | Should -Throw "Registry values do not match a supported state." + } +} + +Describe "Set-WinUtilRegistryComboState" { + BeforeEach { + $script:registryValues = @{ OverlayTestMode = 0; DisableOverlays = 0 } + Mock Get-ItemProperty { + param($Path, $Name) + $registryName = [string]$Name + if ($script:registryValues.ContainsKey($registryName)) { + $result = [pscustomobject]@{} + $result | Add-Member -NotePropertyName $registryName -NotePropertyValue $script:registryValues[$registryName] + return $result + } + [pscustomobject]@{} + } + Mock Set-WinUtilRegistry { + param($Name, $Path, $Type, $Value) + if ($Value -eq "") { + $script:registryValues.Remove($Name) + } else { + $script:registryValues[$Name] = [int]$Value + } + } + } + + It "applies each configured state" -TestCases @( + @{ State = "Enabled"; OverlayTestMode = $null; DisableOverlays = $null } + @{ State = "Disabled (Compatibility)"; OverlayTestMode = 5; DisableOverlays = $null } + @{ State = "Fully Disabled"; OverlayTestMode = 5; DisableOverlays = 1 } + ) { + param($State, $OverlayTestMode, $DisableOverlays) + + Set-WinUtilRegistryComboState -Registry $script:states -State $State + + $script:registryValues.OverlayTestMode | Should -Be $OverlayTestMode + $script:registryValues.DisableOverlays | Should -Be $DisableOverlays + } + + It "restores previous values when verification fails" { + Mock Set-WinUtilRegistry { + param($Name, $Path, $Type, $Value) + if ($Name -eq "DisableOverlays" -and $Value -eq 1) { + return + } + if ($Value -eq "") { + $script:registryValues.Remove($Name) + } else { + $script:registryValues[$Name] = [int]$Value + } + } + + { Set-WinUtilRegistryComboState -Registry $script:states -State "Fully Disabled" } | Should -Throw "Unable to apply registry state*" + $script:registryValues.OverlayTestMode | Should -Be 0 + $script:registryValues.DisableOverlays | Should -Be 0 + } + + It "restores absence when a state cannot be applied" { + $script:registryValues.Clear() + Mock Set-WinUtilRegistry { + param($Name, $Path, $Type, $Value) + if ($Name -eq "DisableOverlays" -and $Value -eq 1) { + return + } + if ($Value -eq "") { + $script:registryValues.Remove($Name) + } else { + $script:registryValues[$Name] = [int]$Value + } + } + + { Set-WinUtilRegistryComboState -Registry $script:states -State "Fully Disabled" } | Should -Throw "Unable to apply registry state*" + $script:registryValues.ContainsKey("OverlayTestMode") | Should -BeFalse + $script:registryValues.ContainsKey("DisableOverlays") | Should -BeFalse + } + + It "reports when the previous values cannot be restored" { + Mock Set-WinUtilRegistry { + param($Name, $Path, $Type, $Value) + if (($Name -eq "DisableOverlays" -and $Value -eq 1) -or ($Name -eq "OverlayTestMode" -and $Value -eq 0)) { + return + } + if ($Value -eq "") { + $script:registryValues.Remove($Name) + } else { + $script:registryValues[$Name] = [int]$Value + } + } + + { Set-WinUtilRegistryComboState -Registry $script:states -State "Fully Disabled" } | Should -Throw "*previous registry state could not be restored*" + } +} diff --git a/pester/system-helpers.Tests.ps1 b/pester/system-helpers.Tests.ps1 index 84b05d74..356f542c 100644 --- a/pester/system-helpers.Tests.ps1 +++ b/pester/system-helpers.Tests.ps1 @@ -148,7 +148,6 @@ Describe "Set-WinUtilRegistry" { $registryPath = "HKLM:\Software\WinUtilTest" $script:testPathResults["HKU:\"] = $true $script:testPathResults[$registryPath] = $true - Set-WinUtilRegistry -Path $registryPath -Name "ObsoleteValue" -Type "String" -Value "" Should -Invoke -CommandName Set-ItemProperty -Times 0 -Exactly @@ -159,6 +158,7 @@ Describe "Set-WinUtilRegistry" { $ErrorAction -eq "Stop" } } + } Describe "Set-WinUtilService" { diff --git a/pester/tweaks.Tests.ps1 b/pester/tweaks.Tests.ps1 index 2f076df0..c502749c 100644 --- a/pester/tweaks.Tests.ps1 +++ b/pester/tweaks.Tests.ps1 @@ -169,6 +169,7 @@ Describe "Invoke-WinUtilTweaks" { $Name -eq "DiagTrack" -and $StartupType -eq "Disabled" } } + } Describe "Invoke-WPFtweaksbutton" {