#=========================================================================== # Tests - Tweak Orchestration #=========================================================================== BeforeAll { $script:repoRoot = (Resolve-Path (Join-Path $PSScriptRoot "..")).Path . (Join-Path $script:repoRoot "functions\private\Measure-WinUtilStep.ps1") . (Join-Path $script:repoRoot "functions\private\Invoke-WinUtilTweaks.ps1") . (Join-Path $script:repoRoot "functions\public\Invoke-WPFtweaksbutton.ps1") . (Join-Path $script:repoRoot "functions\public\Invoke-WPFundoall.ps1") function Set-WinUtilService { param($Name, $StartupType) } function Set-WinUtilRegistry { param($Name, $Path, $Type, $Value) } function Invoke-WinUtilScript { param($Name, [scriptblock]$ScriptBlock) } function Remove-WinUtilAPPX { param($Name) } function Remove-WinUtilProvisionedAPPX { param($PackageList) } function Set-WinUtilDNS { param($DNSProvider) } function Invoke-WPFRunspace { param($ArgumentList, $ParameterList, [scriptblock]$ScriptBlock) } function Invoke-WPFUIThread { param([scriptblock]$ScriptBlock, [hashtable]$Parameters, [switch]$Async) } function Write-WinUtilLog { param($Message, $Level, $Component) } function Start-WinUtilJob { param([string]$Name, [scriptblock]$ScriptBlock, [hashtable]$Parameters, [string]$Description, [switch]$DisableAppList) } function Step-WinUtilJob { param([string]$Status, [int]$Percent, [string]$State, [string]$Overlay, [switch]$Hide) } function Show-WinUtilMessage { param($Message, $Title, $Button, $Icon) } function script:New-WinUtilTweaksConfig { [pscustomobject]@{ WPFTweaksExample = [pscustomobject]@{ service = @( [pscustomobject]@{ Name = "DiagTrack" StartupType = "Disabled" OriginalType = "Automatic" } ) registry = @( [pscustomobject]@{ Path = "HKLM:\Software\WinUtilTest" Name = "AllowTelemetry" Type = "DWord" Value = "0" OriginalValue = "1" } ) InvokeScript = @("Write-Output 'apply tweak'") UndoScript = @("Write-Output 'undo tweak'") appx = @("Microsoft.ExampleApp") } WPFTweaksServiceOnly = [pscustomobject]@{ service = @( [pscustomobject]@{ Name = "DiagTrack" StartupType = "Disabled" OriginalType = "Automatic" } ) } } } } Describe "Invoke-WinUtilTweaks" { BeforeEach { $script:sync = [Hashtable]::Synchronized(@{ configs = @{ tweaks = New-WinUtilTweaksConfig } }) Mock Get-Service { [pscustomobject]@{ Name = "DiagTrack" StartType = "Automatic" } } Mock Set-WinUtilService { } Mock Set-WinUtilRegistry { } Mock Invoke-WinUtilScript { } Mock Remove-WinUtilAPPX { } Mock Remove-WinUtilProvisionedAPPX { } Mock Write-WinUtilLog { } Mock Write-Warning { } } AfterEach { Remove-Variable -Name sync -Scope Script -ErrorAction SilentlyContinue } It "dispatches apply actions to service, registry, script, and AppX helpers" { Invoke-WinUtilTweaks -CheckBox "WPFTweaksExample" Should -Invoke -CommandName Get-Service -Times 1 -Exactly -ParameterFilter { $Name -eq "DiagTrack" -and $ErrorAction -eq "Stop" } Should -Invoke -CommandName Set-WinUtilService -Times 1 -Exactly -ParameterFilter { $Name -eq "DiagTrack" -and $StartupType -eq "Disabled" } Should -Invoke -CommandName Set-WinUtilRegistry -Times 1 -Exactly -ParameterFilter { $Path -eq "HKLM:\Software\WinUtilTest" -and $Name -eq "AllowTelemetry" -and $Type -eq "DWord" -and $Value -eq "0" } Should -Invoke -CommandName Invoke-WinUtilScript -Times 1 -Exactly -ParameterFilter { $Name -eq "WPFTweaksExample" -and $ScriptBlock.ToString() -eq "Write-Output 'apply tweak'" } Should -Invoke -CommandName Remove-WinUtilAPPX -Times 1 -Exactly -ParameterFilter { $Name -eq "Microsoft.ExampleApp" } Should -Invoke -CommandName Remove-WinUtilProvisionedAPPX -Times 1 -Exactly -ParameterFilter { $PackageList.Count -eq 1 -and $PackageList[0] -eq "Microsoft.ExampleApp" } } It "uses original registry values and service startup types in undo mode" { Invoke-WinUtilTweaks -CheckBox "WPFTweaksExample" -undo $true Should -Invoke -CommandName Get-Service -Times 0 -Exactly Should -Invoke -CommandName Set-WinUtilService -Times 1 -Exactly -ParameterFilter { $Name -eq "DiagTrack" -and $StartupType -eq "Automatic" } Should -Invoke -CommandName Set-WinUtilRegistry -Times 1 -Exactly -ParameterFilter { $Path -eq "HKLM:\Software\WinUtilTest" -and $Name -eq "AllowTelemetry" -and $Type -eq "DWord" -and $Value -eq "1" } Should -Invoke -CommandName Invoke-WinUtilScript -Times 1 -Exactly -ParameterFilter { $Name -eq "WPFTweaksExample" -and $ScriptBlock.ToString() -eq "Write-Output 'undo tweak'" } Should -Invoke -CommandName Remove-WinUtilAPPX -Times 0 -Exactly Should -Invoke -CommandName Remove-WinUtilProvisionedAPPX -Times 0 -Exactly } It "keeps a user-changed service startup type by default" { Mock Get-Service { [pscustomobject]@{ Name = "DiagTrack" StartType = "Manual" } } -ParameterFilter { $Name -eq "DiagTrack" } Invoke-WinUtilTweaks -CheckBox "WPFTweaksServiceOnly" Should -Invoke -CommandName Get-Service -Times 1 -Exactly Should -Invoke -CommandName Set-WinUtilService -Times 0 -Exactly } It "forces a service startup type when KeepServiceStartup is disabled" { Invoke-WinUtilTweaks -CheckBox "WPFTweaksServiceOnly" -KeepServiceStartup $false Should -Invoke -CommandName Get-Service -Times 0 -Exactly Should -Invoke -CommandName Set-WinUtilService -Times 1 -Exactly -ParameterFilter { $Name -eq "DiagTrack" -and $StartupType -eq "Disabled" } } It "skips a missing service and applies the remaining services and tweaks" { $script:sync.configs.tweaks.WPFTweaksExample.service = @( [pscustomobject]@{ Name = "CscService" StartupType = "Disabled" OriginalType = "Manual" } ) + $script:sync.configs.tweaks.WPFTweaksExample.service Mock Get-Service { $exception = [Microsoft.PowerShell.Commands.ServiceCommandException]::new("Cannot find any service with service name '$Name'.") $errorRecord = [System.Management.Automation.ErrorRecord]::new( $exception, "NoServiceFoundForGivenName,Microsoft.PowerShell.Commands.GetServiceCommand", [System.Management.Automation.ErrorCategory]::ObjectNotFound, $Name ) throw $errorRecord } -ParameterFilter { $Name -eq "CscService" -and $ErrorAction -eq "Stop" } foreach ($tweak in @("WPFTweaksExample", "WPFTweaksServiceOnly")) { Invoke-WinUtilTweaks -CheckBox $tweak } Should -Invoke -CommandName Write-Warning -Times 1 -Exactly -ParameterFilter { $Message -eq "Service CscService was not found." } Should -Invoke -CommandName Write-WinUtilLog -Times 1 -Exactly -ParameterFilter { $Level -eq "WARN" -and $Component -eq "Service" -and $Message -eq "Service CscService was not found." } Should -Invoke -CommandName Set-WinUtilService -Times 0 -Exactly -ParameterFilter { $Name -eq "CscService" } Should -Invoke -CommandName Set-WinUtilService -Times 2 -Exactly -ParameterFilter { $Name -eq "DiagTrack" -and $StartupType -eq "Disabled" } Should -Invoke -CommandName Invoke-WinUtilScript -Times 1 -Exactly -ParameterFilter { $Name -eq "WPFTweaksExample" } } It "rethrows service lookup errors that do not identify a missing service" { Mock Get-Service { $exception = [Microsoft.PowerShell.Commands.ServiceCommandException]::new("Service lookup failed.") $errorRecord = [System.Management.Automation.ErrorRecord]::new( $exception, "ServiceLookupFailed", [System.Management.Automation.ErrorCategory]::OpenError, $Name ) throw $errorRecord } { Invoke-WinUtilTweaks -CheckBox "WPFTweaksExample" } | Should -Throw -ExpectedMessage "*Service lookup failed.*" Should -Invoke -CommandName Set-WinUtilService -Times 0 -Exactly Should -Invoke -CommandName Set-WinUtilRegistry -Times 0 -Exactly Should -Invoke -CommandName Invoke-WinUtilScript -Times 0 -Exactly Should -Invoke -CommandName Write-Warning -Times 0 -Exactly } } Describe "Invoke-WinUtilTweaks completion status" { BeforeAll { . (Join-Path $script:repoRoot "functions\private\Write-WinUtilLog.ps1") . (Join-Path $script:repoRoot "functions\private\Invoke-WinUtilScript.ps1") } BeforeEach { $script:testRoot = Join-Path ([System.IO.Path]::GetTempPath()) "winutil-tweaks-$([guid]::NewGuid())" $script:logPath = Join-Path $script:testRoot "logs\winutil_2026-09-15_12-00-00.log" $script:sync = [Hashtable]::Synchronized(@{ logPath = $script:logPath configs = @{ tweaks = [pscustomobject]@{ WPFTweaksFailing = [pscustomobject]@{ InvokeScript = @("throw 'simulated icacls failure'") UndoScript = @("throw 'simulated icacls undo failure'") } WPFTweaksClean = [pscustomobject]@{ InvokeScript = @("Write-Output 'apply tweak'") } # A job worker logging an error from its own runspace lands in the shared # list without passing through this runspace's logger WPFTweaksDuringJob = [pscustomobject]@{ InvokeScript = @("`$null = `$sync.LoggedErrors.Add('[Job] error from a concurrent job'); Write-Output 'apply tweak'") } } } # Seeded with an earlier error: only errors logged during this tweak may count LoggedErrors = [System.Collections.ArrayList]::Synchronized([System.Collections.ArrayList]::new(@("[UI] earlier unrelated failure"))) }) # Toggle switches run the tweak on the UI thread, outside any job worker. The runspace # counter starts non-zero: only errors logged during this tweak may count Remove-Variable -Name WinUtilIsJobWorker -Scope Global -ErrorAction SilentlyContinue $global:WinUtilJobErrorCount = 3 Mock Write-Host { } Mock Write-Warning { } } AfterEach { Remove-Variable -Name sync -Scope Script -ErrorAction SilentlyContinue Remove-Variable -Name WinUtilJobErrorCount -Scope Global -ErrorAction SilentlyContinue Remove-Item -Path $script:testRoot -Recurse -Force -ErrorAction SilentlyContinue } It "warns instead of reporting completion when a tweak step logged an error" { Invoke-WinUtilTweaks -CheckBox "WPFTweaksFailing" $log = Get-Content -Path $script:logPath -Raw $log | Should -Match "\[ERROR\] \[Script\] Runtime exception while running script for WPFTweaksFailing" $log | Should -Match "\[WARN\] \[Tweaks\] Apply tweak finished with 1 error\(s\): WPFTweaksFailing" $log | Should -Not -Match "tweak completed: WPFTweaksFailing" } It "warns when an undo step logged an error" { Invoke-WinUtilTweaks -CheckBox "WPFTweaksFailing" -undo $true $log = Get-Content -Path $script:logPath -Raw $log | Should -Match "\[ERROR\] \[Script\] Runtime exception while running script for WPFTweaksFailing" $log | Should -Match "\[WARN\] \[Tweaks\] Undo tweak finished with 1 error\(s\): WPFTweaksFailing" $log | Should -Not -Match "tweak completed: WPFTweaksFailing" } It "reports completion when every tweak step succeeded" { Invoke-WinUtilTweaks -CheckBox "WPFTweaksClean" $log = Get-Content -Path $script:logPath -Raw $log | Should -Match "\[INFO\] \[Tweaks\] Apply tweak completed: WPFTweaksClean" $log | Should -Not -Match "\[WARN\]" $log | Should -Not -Match "\[ERROR\]" } It "ignores an error another runspace logged while the tweak ran" { Invoke-WinUtilTweaks -CheckBox "WPFTweaksDuringJob" $log = Get-Content -Path $script:logPath -Raw $log | Should -Match "\[INFO\] \[Tweaks\] Apply tweak completed: WPFTweaksDuringJob" $log | Should -Not -Match "tweak finished with" } } Describe "Invoke-WPFtweaksbutton" { BeforeEach { $script:sync = [Hashtable]::Synchronized(@{ ActiveJob = $null selectedTweaks = [System.Collections.Generic.List[string]]::new() WPFchangedns = [pscustomobject]@{ text = "Cloudflare" } }) $script:capturedTweaksJob = $null Mock Invoke-WinUtilTweaks { } # the real one returns $true on success, and the workflow now stops when it does not Mock Set-WinUtilDNS { return $true } Mock Invoke-WPFUIThread { } Mock Write-WinUtilLog { } Mock Step-WinUtilJob { } Mock Show-WinUtilMessage { "OK" } Mock Write-Host { } Mock Start-WinUtilJob { $script:capturedTweaksJob = [pscustomobject]@{ ScriptBlock = $ScriptBlock Parameters = $Parameters } } } AfterEach { Remove-Variable -Name sync -Scope Script -ErrorAction SilentlyContinue Remove-Variable -Name capturedTweaksJob -Scope Script -ErrorAction SilentlyContinue } It "prompts and exits when nothing is selected and DNS is left at the default" { $script:sync.WPFchangedns.text = "Default" Invoke-WPFtweaksbutton Should -Invoke -CommandName Show-WinUtilMessage -Times 1 -Exactly -ParameterFilter { $Message -eq "Please check the tweaks you wish to perform." } Should -Invoke -CommandName Start-WinUtilJob -Times 0 -Exactly } It "queues a tweak job with the selection and DNS provider" { $script:sync.selectedTweaks.Add("WPFTweaksTelemetry") $script:sync.selectedTweaks.Add("WPFTweaksServices") Invoke-WPFtweaksbutton Should -Invoke -CommandName Start-WinUtilJob -Times 1 -Exactly -ParameterFilter { $Name -eq "Tweaks" -and $ScriptBlock -is [scriptblock] } $script:capturedTweaksJob.Parameters.Tweaks | Should -HaveCount 2 $script:capturedTweaksJob.Parameters.Tweaks[0] | Should -Be "WPFTweaksTelemetry" $script:capturedTweaksJob.Parameters.DnsProvider | Should -Be "Cloudflare" } It "applies every selected tweak and the DNS provider inside the job body" { $script:sync.selectedTweaks.Add("WPFTweaksTelemetry") $script:sync.selectedTweaks.Add("WPFTweaksServices") Invoke-WPFtweaksbutton $jobParameters = $script:capturedTweaksJob.Parameters & $script:capturedTweaksJob.ScriptBlock @jobParameters Should -Invoke -CommandName Set-WinUtilDNS -Times 1 -Exactly -ParameterFilter { $DNSProvider -eq "Cloudflare" } Should -Invoke -CommandName Invoke-WinUtilTweaks -Times 2 -Exactly Should -Invoke -CommandName Step-WinUtilJob -Times 1 -Exactly -ParameterFilter { $Status -eq "Applying WPFTweaksTelemetry (1/2)" -and $Percent -eq 0 } Should -Invoke -CommandName Step-WinUtilJob -Times 1 -Exactly -ParameterFilter { $Status -eq "Applying WPFTweaksServices (2/2)" -and $Percent -eq 50 } } It "stops the run when the DNS change fails" { # carrying on would leave the machine half configured, so the job ends and reports it $script:sync.selectedTweaks.Add("WPFTweaksTelemetry") Mock Set-WinUtilDNS { return $false } Invoke-WPFtweaksbutton $jobParameters = $script:capturedTweaksJob.Parameters { & $script:capturedTweaksJob.ScriptBlock @jobParameters } | Should -Throw -ExpectedMessage "*DNS change to Cloudflare failed*" Should -Invoke -CommandName Invoke-WinUtilTweaks -Times 0 -Exactly } It "carries on when the DNS change succeeds" { $script:sync.selectedTweaks.Add("WPFTweaksTelemetry") Mock Set-WinUtilDNS { return $true } Invoke-WPFtweaksbutton $jobParameters = $script:capturedTweaksJob.Parameters & $script:capturedTweaksJob.ScriptBlock @jobParameters Should -Invoke -CommandName Invoke-WinUtilTweaks -Times 1 -Exactly } It "takes the restore point before any other tweak runs" { $script:sync.selectedTweaks.Add("WPFTweaksRestorePoint") $script:sync.selectedTweaks.Add("WPFTweaksTelemetry") $script:appliedOrder = [System.Collections.Generic.List[string]]::new() Mock Invoke-WinUtilTweaks { $script:appliedOrder.Add($CheckBox) } Invoke-WPFtweaksbutton $jobParameters = $script:capturedTweaksJob.Parameters & $script:capturedTweaksJob.ScriptBlock @jobParameters $script:appliedOrder | Should -Be @("WPFTweaksRestorePoint", "WPFTweaksTelemetry") Should -Invoke -CommandName Step-WinUtilJob -Times 1 -Exactly -ParameterFilter { $Status -eq "Creating restore point" -and $Percent -eq 0 } Should -Invoke -CommandName Step-WinUtilJob -Times 1 -Exactly -ParameterFilter { $Status -eq "Applying WPFTweaksTelemetry (2/2)" -and $Percent -eq 50 } } } Describe "Invoke-WPFundoall" { BeforeEach { $script:sync = [Hashtable]::Synchronized(@{ ActiveJob = $null selectedTweaks = [System.Collections.Generic.List[string]]::new() }) $script:capturedUndoJob = $null Mock Invoke-WinUtilTweaks { } Mock Write-WinUtilLog { } Mock Step-WinUtilJob { } Mock Show-WinUtilMessage { "OK" } Mock Write-Host { } Mock Start-WinUtilJob { $script:capturedUndoJob = [pscustomobject]@{ ScriptBlock = $ScriptBlock Parameters = $Parameters } } } AfterEach { Remove-Variable -Name sync -Scope Script -ErrorAction SilentlyContinue Remove-Variable -Name capturedUndoJob -Scope Script -ErrorAction SilentlyContinue } It "prompts and exits when nothing is selected" { Invoke-WPFundoall Should -Invoke -CommandName Show-WinUtilMessage -Times 1 -Exactly -ParameterFilter { $Message -eq "Please check the tweaks you wish to undo." } Should -Invoke -CommandName Start-WinUtilJob -Times 0 -Exactly } It "undoes every selected tweak inside the job body" { $script:sync.selectedTweaks.Add("WPFTweaksTelemetry") $script:sync.selectedTweaks.Add("WPFTweaksServices") Invoke-WPFundoall Should -Invoke -CommandName Start-WinUtilJob -Times 1 -Exactly -ParameterFilter { $Name -eq "Undo tweaks" } $jobParameters = $script:capturedUndoJob.Parameters & $script:capturedUndoJob.ScriptBlock @jobParameters Should -Invoke -CommandName Invoke-WinUtilTweaks -Times 2 -Exactly -ParameterFilter { $undo -eq $true } Should -Invoke -CommandName Step-WinUtilJob -Times 1 -Exactly -ParameterFilter { $Status -eq "Undoing WPFTweaksTelemetry (1/2)" -and $Percent -eq 0 } Should -Invoke -CommandName Step-WinUtilJob -Times 1 -Exactly -ParameterFilter { $Status -eq "Undoing WPFTweaksServices (2/2)" -and $Percent -eq 50 } } }