diff --git a/CHANGELOG.md b/CHANGELOG.md index 2d1ace4..d988dd2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ The project follows Semantic Versioning. Release candidates are pre-release buil ### Development +- Removed the legacy per-model confirmation dialog before Gaming Optimised on `SupportedIntelMac` systems. Model validation remains informational; the global `95%` / Boost Disabled CPU policy is unchanged. - Advanced the `main` development identity to `0.5.0-rc.2` after publication of `v0.5.0-rc.1` so post-release source builds cannot be confused with the immutable published RC artifact. - `0.5.0-rc.2` is a development target only at this point. No tag or GitHub Release has been created for it. - Published stable `v0.4.0` and pre-release `v0.5.0-rc.1` remain unchanged. diff --git a/src/BootCampPerformanceControl/UI/AppCompositionRoot.cs b/src/BootCampPerformanceControl/UI/AppCompositionRoot.cs index 436e2fc..fbe3bf9 100644 --- a/src/BootCampPerformanceControl/UI/AppCompositionRoot.cs +++ b/src/BootCampPerformanceControl/UI/AppCompositionRoot.cs @@ -107,7 +107,6 @@ internal static MainApplicationComposition CreateMainApplication(IApplicationLog compatibilityReportService, new WpfCompatibilityReportDialogService(logger), logger, - new WpfUserConfirmationService(), profileRestoreService: profileRestoreService, ownershipReader: ownershipStore, gamingOptimisedRestoreCoordinator: gamingOptimisedRestoreCoordinator, diff --git a/src/BootCampPerformanceControl/UI/IUserConfirmationService.cs b/src/BootCampPerformanceControl/UI/IUserConfirmationService.cs deleted file mode 100644 index 8a702f9..0000000 --- a/src/BootCampPerformanceControl/UI/IUserConfirmationService.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace BootCampPerformanceControl.UI; - -public interface IUserConfirmationService -{ - bool ConfirmUntestedModelApply(string modelName); -} diff --git a/src/BootCampPerformanceControl/UI/MainViewModel.cs b/src/BootCampPerformanceControl/UI/MainViewModel.cs index 67828ad..400bc1e 100644 --- a/src/BootCampPerformanceControl/UI/MainViewModel.cs +++ b/src/BootCampPerformanceControl/UI/MainViewModel.cs @@ -39,12 +39,10 @@ public sealed class MainViewModel : ViewModelBase private readonly ICompatibilityReportService _compatibilityReportService; private readonly ICompatibilityReportDialogService _compatibilityReportDialogService; private readonly IApplicationLogger _logger; - private readonly IUserConfirmationService _userConfirmationService; private readonly TimeSpan _fanPollingInterval; private readonly Func _fanPollingDelayAsync; private readonly SemaphoreSlim _fanOperationGate = new(1, 1); private readonly object _fanMonitoringSync = new(); - private readonly HashSet _acknowledgedUntestedModels = new(StringComparer.OrdinalIgnoreCase); private ModelVerificationResult _lastVerificationResult = ModelVerificationResult.Unknown(); private bool _lastPowerStateReadSucceeded; @@ -98,7 +96,6 @@ public MainViewModel( ICompatibilityReportService compatibilityReportService, ICompatibilityReportDialogService compatibilityReportDialogService, IApplicationLogger logger, - IUserConfirmationService? userConfirmationService = null, TimeSpan? fanPollingInterval = null, Func? fanPollingDelayAsync = null, ProfileRestoreService? profileRestoreService = null) @@ -117,7 +114,6 @@ public MainViewModel( compatibilityReportService, compatibilityReportDialogService, logger, - userConfirmationService, fanPollingInterval, fanPollingDelayAsync, profileRestoreService, @@ -142,7 +138,6 @@ internal MainViewModel( ICompatibilityReportService compatibilityReportService, ICompatibilityReportDialogService compatibilityReportDialogService, IApplicationLogger logger, - IUserConfirmationService? userConfirmationService = null, TimeSpan? fanPollingInterval = null, Func? fanPollingDelayAsync = null, ProfileRestoreService? profileRestoreService = null, @@ -190,7 +185,6 @@ internal MainViewModel( _compatibilityReportService = compatibilityReportService; _compatibilityReportDialogService = compatibilityReportDialogService; _logger = logger; - _userConfirmationService = userConfirmationService ?? new WpfUserConfirmationService(); LoadApplicationOptions(); _fanPollingInterval = fanPollingInterval ?? DefaultFanPollingInterval; if (_fanPollingInterval <= TimeSpan.Zero) @@ -978,23 +972,6 @@ private async Task ApplyProfileAsync(string profileId, CancellationToken cancell && _gamingOptimisedSessionState == GamingOptimisedSessionState.PartialCpuOnly && _lastVerificationResult.PlatformSupport == PlatformSupportStatus.SupportedIntelMac; - if (string.Equals(profileId, "gaming-optimised", StringComparison.OrdinalIgnoreCase) - && _lastVerificationResult.ValidationLevel == ModelValidationLevel.NotIndividuallyTested) - { - if (!_acknowledgedUntestedModels.Contains(_lastVerificationResult.Model)) - { - var confirmed = _userConfirmationService.ConfirmUntestedModelApply(_lastVerificationResult.Model); - if (!confirmed) - { - StatusMessage = "Profile application canceled."; - _logger.Info($"Profile application canceled by user for untested model: {_lastVerificationResult.Model}."); - return; - } - - _acknowledgedUntestedModels.Add(_lastVerificationResult.Model); - } - } - IsBusy = true; StatusMessage = isPartialGamingFanResume ? "Re-enabling Maximum Safe RPM fans..." diff --git a/src/BootCampPerformanceControl/UI/WpfUserConfirmationService.cs b/src/BootCampPerformanceControl/UI/WpfUserConfirmationService.cs deleted file mode 100644 index efd5a4a..0000000 --- a/src/BootCampPerformanceControl/UI/WpfUserConfirmationService.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System.Windows; - -namespace BootCampPerformanceControl.UI; - -public sealed class WpfUserConfirmationService : IUserConfirmationService -{ - public bool ConfirmUntestedModelApply(string modelName) - { - const string title = "Untested Mac Model"; - var message = - "This Mac model has not been individually performance-tested." + Environment.NewLine + Environment.NewLine + - "Gaming Optimised will limit the maximum processor state to 95% and disable CPU Boost." + Environment.NewLine + Environment.NewLine + - "Thermal and performance behaviour may vary by model." + Environment.NewLine + Environment.NewLine + - "Your original processor power settings will be saved before changes are applied." + Environment.NewLine + Environment.NewLine + - "Do you want to continue?"; - - var result = System.Windows.MessageBox.Show( - message, - title, - MessageBoxButton.OKCancel, - MessageBoxImage.Warning); - - return result == MessageBoxResult.OK; - } -} diff --git a/tests/BootCampPerformanceControl.Tests/UI/MainViewModelTests.cs b/tests/BootCampPerformanceControl.Tests/UI/MainViewModelTests.cs index f626543..260fe5d 100644 --- a/tests/BootCampPerformanceControl.Tests/UI/MainViewModelTests.cs +++ b/tests/BootCampPerformanceControl.Tests/UI/MainViewModelTests.cs @@ -1823,7 +1823,7 @@ public async Task ProfileButtons_GamingOptimisedDisabledWhenPowerReadFails() } [Fact] - public void GamingButton_NotIndividuallyTested_ShowsConfirmationDialog_CancelAbortsApplyWithoutWrites() + public async Task GamingButton_NotIndividuallyTested_AppliesDirectlyOnceAndFanOnlyResumeDoesNotRewriteProcessorState() { var verification = new ModelVerificationResult( "Apple Inc.", @@ -1831,38 +1831,10 @@ public void GamingButton_NotIndividuallyTested_ShowsConfirmationDialog_CancelAbo PlatformSupportStatus.SupportedIntelMac, ModelValidationLevel.NotIndividuallyTested, "Not individually tested."); - var confirmationService = new FakeUserConfirmationService { Result = false }; - var powerManagementService = new FakePowerManagementService(InitialPowerState()); - var logger = new TestApplicationLogger(); - var viewModel = CreateViewModel( - new FakeHardwareDetectionService(verification), - powerManagementService, - logger: logger, - userConfirmationService: confirmationService); - - viewModel.RefreshCommand.Execute(null); - GetProfile(viewModel, "gaming-optimised").Command!.Execute(null); - - Assert.Equal(1, confirmationService.CallCount); - Assert.Equal(VerifiedHardwareModels.MacBookPro14_3, confirmationService.LastModelName); - Assert.Equal("Profile application canceled.", viewModel.StatusMessage); - Assert.Equal(0, powerManagementService.GuardedApplyCallCount); - Assert.Equal(0, powerManagementService.UnguardedApplyCallCount); - } - - [Fact] - public async Task GamingButton_NotIndividuallyTested_ShowsConfirmationDialog_ConfirmAllowsApplyAndRemembersSession() - { - var verification = new ModelVerificationResult( - "Apple Inc.", - VerifiedHardwareModels.MacBookPro14_3, - PlatformSupportStatus.SupportedIntelMac, - ModelValidationLevel.NotIndividuallyTested, - "Not individually tested."); - var confirmationService = new FakeUserConfirmationService { Result = true }; var expectedStateBefore = InitialPowerState(); var requestedSettings = new ProcessorPowerSettings(95, 95, 0, 0); var refreshedState = GamingOptimisedPowerState(); + var restoreSnapshotStore = new InMemoryRestoreSnapshotStore(); var powerManagementService = new FakePowerManagementService( SuccessfulPowerOperation(expectedStateBefore, requestedSettings), InitialPowerState(), @@ -1875,30 +1847,33 @@ public async Task GamingButton_NotIndividuallyTested_ShowsConfirmationDialog_Con var viewModel = CreateViewModel( new FakeHardwareDetectionService(verification), powerManagementService, - userConfirmationService: confirmationService, + restoreSnapshotStore, fanExecutionSessionFactory: sessionFactory); viewModel.RefreshCommand.Execute(null); GetProfile(viewModel, "gaming-optimised").Command!.Execute(null); await WaitForIdleAsync(viewModel); - Assert.Equal(1, confirmationService.CallCount); Assert.Equal(1, powerManagementService.GuardedApplyCallCount); + Assert.Equal(0, powerManagementService.UnguardedApplyCallCount); + Assert.Equal(requestedSettings, powerManagementService.LastGuardedSettings); Assert.Contains("applied successfully", viewModel.StatusMessage, StringComparison.OrdinalIgnoreCase); // Second activation in the same partial CPU-only session is fan-only: - // it must not prompt again or rewrite the processor snapshot/settings. + // it must not rewrite the processor snapshot/settings. GetProfile(viewModel, "gaming-optimised").Command!.Execute(null); await WaitForIdleAsync(viewModel); - Assert.Equal(1, confirmationService.CallCount); Assert.Equal(1, powerManagementService.GuardedApplyCallCount); + Assert.Equal(0, powerManagementService.UnguardedApplyCallCount); + Assert.Equal( + expectedStateBefore, + await restoreSnapshotStore.GetOriginalRestoreSnapshotAsync(CancellationToken.None)); } [Fact] - public async Task GamingButton_PerformanceValidated_DoesNotShowConfirmationDialog() + public async Task GamingButton_PerformanceValidated_StillExecutesNormalGuardedApplyPath() { - var confirmationService = new FakeUserConfirmationService { Result = true }; var expectedStateBefore = InitialPowerState(); var requestedSettings = new ProcessorPowerSettings(95, 95, 0, 0); var refreshedState = GamingOptimisedPowerState(); @@ -1909,15 +1884,14 @@ public async Task GamingButton_PerformanceValidated_DoesNotShowConfirmationDialo refreshedState); var viewModel = CreateViewModel( new FakeHardwareDetectionService(VerifiedMacBookPro16_1()), - powerManagementService, - userConfirmationService: confirmationService); + powerManagementService); viewModel.RefreshCommand.Execute(null); GetProfile(viewModel, "gaming-optimised").Command!.Execute(null); await WaitForIdleAsync(viewModel); - Assert.Equal(0, confirmationService.CallCount); Assert.Equal(1, powerManagementService.GuardedApplyCallCount); + Assert.Equal(0, powerManagementService.UnguardedApplyCallCount); } [Fact] @@ -3760,7 +3734,6 @@ private static MainViewModel CreateViewModel( FakeDiagnosticReportFileSaveService? diagnosticReportFileSaveService = null, FakeCompatibilityReportService? compatibilityReportService = null, FakeCompatibilityReportDialogService? compatibilityReportDialogService = null, - IUserConfirmationService? userConfirmationService = null, FakeFanControlService? fanControlService = null, FakeAppleSmcBackendElevationLauncher? elevationLauncher = null, IApplicationOptionsService? applicationOptionsService = null, @@ -3827,7 +3800,6 @@ private static MainViewModel CreateViewModel( compatibilityReportService ?? new FakeCompatibilityReportService(), compatibilityReportDialogService ?? new FakeCompatibilityReportDialogService(), logger ?? new TestApplicationLogger(), - userConfirmationService, fanPollingInterval: TimeSpan.FromSeconds(2), fanPollingDelayAsync: fanPollingDelayAsync, profileRestoreService: profileRestoreService, @@ -4567,20 +4539,6 @@ private static async Task WaitForTickAsync( } } - private sealed class FakeUserConfirmationService : IUserConfirmationService - { - public bool Result { get; set; } = true; - public int CallCount { get; private set; } - public string? LastModelName { get; private set; } - - public bool ConfirmUntestedModelApply(string modelName) - { - CallCount++; - LastModelName = modelName; - return Result; - } - } - private sealed class FakeApplicationOptionsService : IApplicationOptionsService { public ApplicationOptionsSnapshot Options { get; init; } =