From 48a70e362c58fc4981d8cbd46264084930f62d22 Mon Sep 17 00:00:00 2001 From: laurentiu021 Date: Tue, 19 May 2026 09:40:59 +0300 Subject: [PATCH 1/2] test: fix flaky thread-safety test, update README tech stack --- CHANGELOG.md | 9 +++++++++ README.md | 4 +++- .../SysManager.Tests/OperationLockServiceTests.cs | 15 +++++++++------ 3 files changed, 21 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a094f1f2..9e45f22b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,15 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] +### Fixed +- **OperationLockServiceTests** — replaced flaky `Barrier` + `Thread.Sleep` + thread-safety test with deterministic `CountdownEvent` + `ManualResetEventSlim` + synchronization; asserts exactly 1 acquisition instead of `>= 1`. + +### Changed +- **README.md** — added missing tech stack entries: Microsoft.Extensions.DependencyInjection, + H.NotifyIcon.Wpf, NSubstitute. + ## [0.48.34] - 2026-05-19 ### Fixed diff --git a/README.md b/README.md index 34453abd..89304308 100644 --- a/README.md +++ b/README.md @@ -452,10 +452,12 @@ and elevation state in a format ready to paste into a bug report. - .NET 9 (WPF, C# 13) - CommunityToolkit.Mvvm for MVVM plumbing +- Microsoft.Extensions.DependencyInjection for IoC - ModernWpfUI for the modern title bar - LiveCharts2 for the real-time latency chart +- H.NotifyIcon.Wpf for system tray integration - Serilog for structured logging -- xUnit and FlaUI for unit, integration, and UI-automation tests +- xUnit, NSubstitute, and FlaUI for unit, integration, and UI-automation tests ## Privacy diff --git a/SysManager/SysManager.Tests/OperationLockServiceTests.cs b/SysManager/SysManager.Tests/OperationLockServiceTests.cs index 86f0d872..27ffe412 100644 --- a/SysManager/SysManager.Tests/OperationLockServiceTests.cs +++ b/SysManager/SysManager.Tests/OperationLockServiceTests.cs @@ -93,20 +93,23 @@ public void GetActiveOperationName_ReturnsNull_WhenNotLocked() public async Task TryAcquire_IsThreadSafe() { int successCount = 0; - var barrier = new Barrier(10); + var ready = new CountdownEvent(10); + var go = new ManualResetEventSlim(false); var tasks = Enumerable.Range(0, 10).Select(_ => Task.Run(() => { - barrier.SignalAndWait(); - using var handle = Sut.TryAcquire(OperationCategory.Network, "Race"); + ready.Signal(); + go.Wait(); + using var handle = Sut.TryAcquire(OperationCategory.SystemModification, "Race"); if (handle != null) Interlocked.Increment(ref successCount); - Thread.Sleep(50); })).ToArray(); + ready.Wait(); + go.Set(); await Task.WhenAll(tasks); - // Only one thread should have acquired the lock at a time - Assert.True(successCount >= 1); + // Exactly one thread should have acquired the lock + Assert.Equal(1, successCount); } } From 2fb22aa9702b22ca55bfe9533d4f00b7ed915710 Mon Sep 17 00:00:00 2001 From: laurentiu021 Date: Tue, 19 May 2026 09:47:50 +0300 Subject: [PATCH 2/2] test: hold lock during concurrent acquisition attempts to prevent false positives --- SysManager/SysManager.Tests/OperationLockServiceTests.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/SysManager/SysManager.Tests/OperationLockServiceTests.cs b/SysManager/SysManager.Tests/OperationLockServiceTests.cs index 27ffe412..e11f4db8 100644 --- a/SysManager/SysManager.Tests/OperationLockServiceTests.cs +++ b/SysManager/SysManager.Tests/OperationLockServiceTests.cs @@ -95,14 +95,19 @@ public async Task TryAcquire_IsThreadSafe() int successCount = 0; var ready = new CountdownEvent(10); var go = new ManualResetEventSlim(false); + var allTried = new CountdownEvent(10); var tasks = Enumerable.Range(0, 10).Select(_ => Task.Run(() => { ready.Signal(); go.Wait(); - using var handle = Sut.TryAcquire(OperationCategory.SystemModification, "Race"); + var handle = Sut.TryAcquire(OperationCategory.SystemModification, "Race"); if (handle != null) Interlocked.Increment(ref successCount); + // Wait until all threads have attempted acquisition before releasing + allTried.Signal(); + allTried.Wait(); + handle?.Dispose(); })).ToArray(); ready.Wait();