Skip to content

fix: functionality fixes — health analyzer, depth sort, history race, multi-disk#405

Merged
laurentiu021 merged 1 commit into
mainfrom
fix/functionality-logic-batch
May 15, 2026
Merged

fix: functionality fixes — health analyzer, depth sort, history race, multi-disk#405
laurentiu021 merged 1 commit into
mainfrom
fix/functionality-logic-batch

Conversation

@laurentiu021

@laurentiu021 laurentiu021 commented May 15, 2026

Copy link
Copy Markdown
Owner

Summary

Addresses MEDIUM functionality/logic findings from the comprehensive code review.

Fixes

  • FUNC-M2 — HealthAnalyzer no longer claims "DNS is clean" when DNS IS bad. When both DNS and game server show trouble, correctly returns Mixed verdict instead of incorrectly blaming only the game server.
  • FUNC-M3 — TuneUpService empty directory removal now sorts by path depth (separator count) instead of string length. Previously, a short-named deep directory could be deleted before its children.
  • FUNC-M4 — SpeedTestHistoryService SaveAsync/ClearAsync now serialize via SemaphoreSlim(1,1) to prevent concurrent load-modify-save races that could lose history entries.
  • FUNC-M5 — FixedDriveService multi-disk enrichment now maps drive letters to physical disks via MSFT_Partition.DiskNumber query, correctly annotating media type and bus type on multi-disk systems (previously only worked with single-disk).

Tests updated

  • HealthAnalyzerTests + HealthAnalyzerEdgeCaseTests: updated DnsAndGameBad test to expect Mixed instead of GameServer.

Files changed (7)

Services: HealthAnalyzer, TuneUpService, SpeedTestHistoryService, FixedDriveService
Tests: HealthAnalyzerTests, HealthAnalyzerEdgeCaseTests
Docs: CHANGELOG.md

Build

0 errors, 0 new warnings.

Summary by CodeRabbit

  • Bug Fixes

    • Enhanced detection of fixed drive properties (media and bus type).
    • Corrected system health verdict when both DNS and gaming connectivity are compromised.
    • Improved reliability of speed test history operations.
  • Tests

    • Updated test cases to reflect corrected health verdict behavior.

Review Change Stack

… multi-disk

- FUNC-M2: HealthAnalyzer no longer claims DNS is clean when DNS is bad
- FUNC-M3: TuneUpService sorts directories by depth not string length
- FUNC-M4: SpeedTestHistoryService serializes Save/Clear with SemaphoreSlim
- FUNC-M5: FixedDriveService maps drive letters to physical disks via partition
@coderabbitai

coderabbitai Bot commented May 15, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

This PR bundles five targeted fixes for System Manager v0.48.25: HealthAnalyzer now returns a Mixed verdict when both DNS and game-server metrics fail simultaneously instead of prioritizing game-server alone; SpeedTestHistoryService serializes file operations with semaphore locking to prevent concurrent race conditions; FixedDriveService enriches drive media and bus types via partition-based drive-letter-to-disk mapping; TuneUpService sorts temp subdirectories by depth instead of string length for safer empty-directory cleanup; and the changelog documents all fixes.

Changes

System Manager v0.48.25 Bug Fixes

Layer / File(s) Summary
HealthAnalyzer Mixed verdict for DNS+game-server failures
SysManager/SysManager/Services/HealthAnalyzer.cs, SysManager/SysManager.Tests/HealthAnalyzerEdgeCaseTests.cs, SysManager/SysManager.Tests/HealthAnalyzerTests.cs
HealthAnalyzer.Analyze now returns HealthVerdict.Mixed when both DNS and game-server metrics are bad, with a dedicated "Multiple layers affected" verdict and early return. The game-server-only verdict now requires DNS to be healthy. Tests updated to expect Mixed in the simultaneous failure case.
SpeedTestHistoryService file operation serialization
SysManager/SysManager/Services/SpeedTestHistoryService.cs
Private SemaphoreSlim synchronizes SaveAsync and ClearAsync to prevent concurrent file read/modify/write races. LoadAsync delegates to new LoadCoreAsync for locking-safe internal use. Both save and clear acquire the lock before loading history and release in finally blocks.
FixedDriveService drive letter to disk mapping
SysManager/SysManager/Services/FixedDriveService.cs
Enumerate() maps drive letters to physical disk IDs via MSFT_Partition queries and enriches each drive's MediaType and BusType individually. Falls back to single-disk annotation when letter-mapping fails but only one disk exists. Partition-query errors remain non-fatal.
TuneUpService temp directory deletion by depth order
SysManager/SysManager/Services/TuneUpService.cs
CleanTempFiles replaces string-length sorting with separator-count depth sorting to process deeper temp subdirectories before shallower ones when removing empty directories.
Release changelog for v0.48.25 fixes
CHANGELOG.md
Changelog documents all five fixes under the Fixed section for version 0.48.25 (dated 2026-05-15).

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • laurentiu021/SystemManager#384: Related update to FixedDriveService.Enumerate() that adjusts WMI ManagementObject disposal in the same drive enrichment loop.

Poem

🐰 Five fixes bundled up with care,
DNS and games share verdicts fair,
Locks protect the history's keep,
While drives map deep, directories sweep—
v0.48.25 hops with grace!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 41.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes four main functionality fixes across HealthAnalyzer, TuneUpService, SpeedTestHistoryService, and FixedDriveService that are the primary changes in the changeset.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/functionality-logic-batch

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@SysManager/SysManager/Services/SpeedTestHistoryService.cs`:
- Around line 39-43: Public LoadAsync currently calls LoadCoreAsync without
acquiring _fileLock, allowing reads to race with SaveAsync/ClearAsync; change
LoadAsync to acquire the same _fileLock used by SaveAsync and ClearAsync, await
LoadCoreAsync while holding the lock, and release the lock in a finally block so
transient JsonException/IOException during concurrent writes don't cause a
temporary empty history to be returned; reference LoadAsync, LoadCoreAsync,
_fileLock, SaveAsync, ClearAsync, File.WriteAllTextAsync and File.Delete when
locating where to add the lock.
- Around line 31-34: The instance-level SemaphoreSlim _fileLock inside
SpeedTestHistoryService doesn't prevent races across multiple
SpeedTestHistoryService instances created by SpeedTestViewModel; change the lock
to a static SemaphoreSlim (e.g., make _fileLock static readonly) so all
instances share the same async mutex, or alternatively change registration so
SpeedTestHistoryService is a singleton in DI or introduce a shared file
coordinator used by SaveAsync/LoadAsync to serialize file access; update
references to _fileLock and ensure SaveAsync/LoadAsync await the shared lock
appropriately.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: d4aed670-ced0-47f2-aecb-bfaa5a0f39e9

📥 Commits

Reviewing files that changed from the base of the PR and between f1ec3ce and 4ade049.

📒 Files selected for processing (7)
  • CHANGELOG.md
  • SysManager/SysManager.Tests/HealthAnalyzerEdgeCaseTests.cs
  • SysManager/SysManager.Tests/HealthAnalyzerTests.cs
  • SysManager/SysManager/Services/FixedDriveService.cs
  • SysManager/SysManager/Services/HealthAnalyzer.cs
  • SysManager/SysManager/Services/SpeedTestHistoryService.cs
  • SysManager/SysManager/Services/TuneUpService.cs
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Build & unit tests
  • GitHub Check: Analyze (csharp)
🔇 Additional comments (7)
CHANGELOG.md (1)

9-24: LGTM!

SysManager/SysManager/Services/TuneUpService.cs (1)

185-190: LGTM!

SysManager/SysManager/Services/HealthAnalyzer.cs (1)

93-109: LGTM!

SysManager/SysManager.Tests/HealthAnalyzerEdgeCaseTests.cs (1)

136-147: LGTM!

SysManager/SysManager.Tests/HealthAnalyzerTests.cs (1)

186-188: LGTM!

SysManager/SysManager/Services/FixedDriveService.cs (1)

72-107: LGTM!

SysManager/SysManager/Services/SpeedTestHistoryService.cs (1)

80-83: LGTM!

Also applies to: 117-120, 128-138, 169-172

Comment on lines +31 to +34
// FUNC-M4: Serialize all file operations to prevent concurrent SaveAsync
// calls from racing (load-modify-save is not atomic). A SemaphoreSlim(1,1)
// acts as an async-compatible mutex.
private readonly SemaphoreSlim _fileLock = new(1, 1);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "DI registrations or manual construction:"
rg -n -C2 '\b(AddSingleton|AddScoped|AddTransient)\s*<\s*SpeedTestHistoryService\b|new\s+SpeedTestHistoryService\s*\('

echo
echo "All references to SpeedTestHistoryService:"
rg -n -C2 '\bSpeedTestHistoryService\b'

Repository: laurentiu021/SystemManager

Length of output: 8857


The instance-level lock does not prevent cross-instance writes; make it static or use a shared coordinator.

The service is instantiated manually in SpeedTestViewModel (line 19: private readonly SpeedTestHistoryService _history = new();), not registered as a singleton. Each ViewModel instance therefore has its own _fileLock, so concurrent saves from different ViewModels still race. Consider a static lock, a shared file coordinator, or registering the service as singleton in DI.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@SysManager/SysManager/Services/SpeedTestHistoryService.cs` around lines 31 -
34, The instance-level SemaphoreSlim _fileLock inside SpeedTestHistoryService
doesn't prevent races across multiple SpeedTestHistoryService instances created
by SpeedTestViewModel; change the lock to a static SemaphoreSlim (e.g., make
_fileLock static readonly) so all instances share the same async mutex, or
alternatively change registration so SpeedTestHistoryService is a singleton in
DI or introduce a shared file coordinator used by SaveAsync/LoadAsync to
serialize file access; update references to _fileLock and ensure
SaveAsync/LoadAsync await the shared lock appropriately.

Comment on lines 39 to +43
public async Task<List<SpeedTestResult>> LoadAsync(CancellationToken ct = default)
=> await LoadCoreAsync(ct).ConfigureAwait(false);

/// <summary>Internal load without locking — called from within locked sections.</summary>
private async Task<List<SpeedTestResult>> LoadCoreAsync(CancellationToken ct)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Lock LoadAsync too.

Line 39 still lets public reads bypass _fileLock, so a concurrent SaveAsync/ClearAsync can overlap with File.WriteAllTextAsync/File.Delete. In that window, LoadAsync can return an empty list from a transient JsonException/IOException, which makes history briefly disappear for callers even though the write succeeds.

Suggested fix
 public async Task<List<SpeedTestResult>> LoadAsync(CancellationToken ct = default)
-    => await LoadCoreAsync(ct).ConfigureAwait(false);
+{
+    await _fileLock.WaitAsync(ct).ConfigureAwait(false);
+    try
+    {
+        return await LoadCoreAsync(ct).ConfigureAwait(false);
+    }
+    finally
+    {
+        _fileLock.Release();
+    }
+}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@SysManager/SysManager/Services/SpeedTestHistoryService.cs` around lines 39 -
43, Public LoadAsync currently calls LoadCoreAsync without acquiring _fileLock,
allowing reads to race with SaveAsync/ClearAsync; change LoadAsync to acquire
the same _fileLock used by SaveAsync and ClearAsync, await LoadCoreAsync while
holding the lock, and release the lock in a finally block so transient
JsonException/IOException during concurrent writes don't cause a temporary empty
history to be returned; reference LoadAsync, LoadCoreAsync, _fileLock,
SaveAsync, ClearAsync, File.WriteAllTextAsync and File.Delete when locating
where to add the lock.

@codecov-commenter

Copy link
Copy Markdown

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

❌ Patch coverage is 80.55556% with 7 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
...ysManager/SysManager/Services/FixedDriveService.cs 70.00% 2 Missing and 4 partials ⚠️
SysManager/SysManager/Services/TuneUpService.cs 0.00% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

@laurentiu021
laurentiu021 merged commit 8e4b1bf into main May 15, 2026
5 checks passed
@laurentiu021
laurentiu021 deleted the fix/functionality-logic-batch branch May 15, 2026 14:34
laurentiu021 added a commit that referenced this pull request May 22, 2026
…ssages (#427)

## Summary

**Batch 7** of the QA bug fix series.

### Issue #405 — Registry modifications not logged
Added Serilog audit logging to all PerformanceService registry
modification methods:
- \SetGameMode\ — logs AllowAutoGameMode value
- \SetXboxGameBar\ — logs AppCaptureEnabled and GameDVR_Enabled values
- \SetGpuMaxPerformance\ — logs DisableDynamicPstate value and subkey
- \SetUiEffects\ — logs visual effects action

### Issue #407 — Generic error messages
Replaced generic \Error: {message}\ with operation-specific context
across 3 ViewModels:
- **PerformanceViewModel** (26 replacements): Power plan change failed,
GPU setting change failed, Game Mode change failed, etc.
- **ServicesViewModel** (6 replacements): Start service failed, Stop
service failed, etc.
- **SystemHealthViewModel** (6 replacements): System health scan failed,
Disk report failed, etc.

### Testing
- Build: 0 errors

Closes #405, Closes #407

Co-authored-by: laurentiu021 <laurentiu021@users.noreply.github.com>
laurentiu021 added a commit that referenced this pull request May 22, 2026
Adds CHANGELOG entries for all 9 releases from the QA bug fix session:

- **v0.28.16** — Dispose lifecycle (#395, #410)
- **v0.28.17** — CTS disposal + bare catch (#396, #413)
- **v0.28.18** — Input validation + null checks (#397, #398)
- **v0.28.19** — JSON error handling (#400)
- **v0.28.20** — Drive scanning + cache eviction + ConfigureAwait (#401,
#402, #403)
- **v0.28.21** — Audit logging + error messages (#405, #407)
- **v0.28.22** — SHA256 verification (#408, #409)
- **v0.28.23** — Service timeout + snapshot persist + traceroute DNS
(#414, #415, #416)
- **v0.28.24** — Accessibility (#411)

18 bugs fixed in total.

Co-authored-by: laurentiu021 <laurentiu021@users.noreply.github.com>
laurentiu021 added a commit that referenced this pull request May 22, 2026
… multi-disk (#405)

- FUNC-M2: HealthAnalyzer no longer claims DNS is clean when DNS is bad
- FUNC-M3: TuneUpService sorts directories by depth not string length
- FUNC-M4: SpeedTestHistoryService serializes Save/Clear with SemaphoreSlim
- FUNC-M5: FixedDriveService maps drive letters to physical disks via partition

Co-authored-by: laurentiu021 <laurentiu021@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants