Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
1 change: 0 additions & 1 deletion src/BootCampPerformanceControl/UI/AppCompositionRoot.cs
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,6 @@ internal static MainApplicationComposition CreateMainApplication(IApplicationLog
compatibilityReportService,
new WpfCompatibilityReportDialogService(logger),
logger,
new WpfUserConfirmationService(),
profileRestoreService: profileRestoreService,
ownershipReader: ownershipStore,
gamingOptimisedRestoreCoordinator: gamingOptimisedRestoreCoordinator,
Expand Down
6 changes: 0 additions & 6 deletions src/BootCampPerformanceControl/UI/IUserConfirmationService.cs

This file was deleted.

23 changes: 0 additions & 23 deletions src/BootCampPerformanceControl/UI/MainViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<TimeSpan, CancellationToken, Task> _fanPollingDelayAsync;
private readonly SemaphoreSlim _fanOperationGate = new(1, 1);
private readonly object _fanMonitoringSync = new();
private readonly HashSet<string> _acknowledgedUntestedModels = new(StringComparer.OrdinalIgnoreCase);

private ModelVerificationResult _lastVerificationResult = ModelVerificationResult.Unknown();
private bool _lastPowerStateReadSucceeded;
Expand Down Expand Up @@ -98,7 +96,6 @@ public MainViewModel(
ICompatibilityReportService compatibilityReportService,
ICompatibilityReportDialogService compatibilityReportDialogService,
IApplicationLogger logger,
IUserConfirmationService? userConfirmationService = null,
TimeSpan? fanPollingInterval = null,
Func<TimeSpan, CancellationToken, Task>? fanPollingDelayAsync = null,
ProfileRestoreService? profileRestoreService = null)
Expand All @@ -117,7 +114,6 @@ public MainViewModel(
compatibilityReportService,
compatibilityReportDialogService,
logger,
userConfirmationService,
fanPollingInterval,
fanPollingDelayAsync,
profileRestoreService,
Expand All @@ -142,7 +138,6 @@ internal MainViewModel(
ICompatibilityReportService compatibilityReportService,
ICompatibilityReportDialogService compatibilityReportDialogService,
IApplicationLogger logger,
IUserConfirmationService? userConfirmationService = null,
TimeSpan? fanPollingInterval = null,
Func<TimeSpan, CancellationToken, Task>? fanPollingDelayAsync = null,
ProfileRestoreService? profileRestoreService = null,
Expand Down Expand Up @@ -190,7 +185,6 @@ internal MainViewModel(
_compatibilityReportService = compatibilityReportService;
_compatibilityReportDialogService = compatibilityReportDialogService;
_logger = logger;
_userConfirmationService = userConfirmationService ?? new WpfUserConfirmationService();
LoadApplicationOptions();
_fanPollingInterval = fanPollingInterval ?? DefaultFanPollingInterval;
if (_fanPollingInterval <= TimeSpan.Zero)
Expand Down Expand Up @@ -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..."
Expand Down
25 changes: 0 additions & 25 deletions src/BootCampPerformanceControl/UI/WpfUserConfirmationService.cs

This file was deleted.

68 changes: 13 additions & 55 deletions tests/BootCampPerformanceControl.Tests/UI/MainViewModelTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1823,46 +1823,18 @@ public async Task ProfileButtons_GamingOptimisedDisabledWhenPowerReadFails()
}

[Fact]
public void GamingButton_NotIndividuallyTested_ShowsConfirmationDialog_CancelAbortsApplyWithoutWrites()
public async Task GamingButton_NotIndividuallyTested_AppliesDirectlyOnceAndFanOnlyResumeDoesNotRewriteProcessorState()
{
var verification = new ModelVerificationResult(
"Apple Inc.",
VerifiedHardwareModels.MacBookPro14_3,
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(),
Expand All @@ -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();
Expand All @@ -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]
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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; } =
Expand Down