test(core-tests): 收敛函数式与状态测试的低风险 warning

- 补齐 WaitForTask、ResourceManager、State 与 StateMachine 测试中的低风险 ConfigureAwait(false)
- 更新 AsyncKeyLock、ResultExtensions、ResultT 与 Pipe 测试中的 culture 和异步等待写法
This commit is contained in:
gewuyou 2026-04-25 10:08:59 +08:00
parent 737d0b5037
commit b7560fcc08
8 changed files with 68 additions and 64 deletions

View File

@ -25,7 +25,7 @@ public sealed class AsyncKeyLockManagerTests
using var manager = new AsyncKeyLockManager();
// Act
await using var handle = await manager.AcquireLockAsync("test-key");
await using var handle = await manager.AcquireLockAsync("test-key").ConfigureAwait(false);
// Assert
Assert.That(handle, Is.Not.Null);
@ -47,13 +47,13 @@ public sealed class AsyncKeyLockManagerTests
var index = i;
tasks.Add(Task.Run(async () =>
{
await using var handle = await manager.AcquireLockAsync("same-key");
await using var handle = await manager.AcquireLockAsync("same-key").ConfigureAwait(false);
executionOrder.Add(index);
await Task.Delay(10);
await Task.Delay(10).ConfigureAwait(false);
}));
}
await Task.WhenAll(tasks);
await Task.WhenAll(tasks).ConfigureAwait(false);
// Assert
Assert.That(executionOrder.Count, Is.EqualTo(5));
@ -75,15 +75,15 @@ public sealed class AsyncKeyLockManagerTests
var key = $"key-{i}";
tasks.Add(Task.Run(async () =>
{
await using var handle = await manager.AcquireLockAsync(key);
await using var handle = await manager.AcquireLockAsync(key).ConfigureAwait(false);
var current = Interlocked.Increment(ref concurrentCount);
maxConcurrent = Math.Max(maxConcurrent, current);
await Task.Delay(50);
await Task.Delay(50).ConfigureAwait(false);
Interlocked.Decrement(ref concurrentCount);
}));
}
await Task.WhenAll(tasks);
await Task.WhenAll(tasks).ConfigureAwait(false);
// Assert
Assert.That(maxConcurrent, Is.GreaterThan(1));
@ -94,13 +94,13 @@ public sealed class AsyncKeyLockManagerTests
{
// Arrange
using var manager = new AsyncKeyLockManager();
var handle = await manager.AcquireLockAsync("test-key");
var handle = await manager.AcquireLockAsync("test-key").ConfigureAwait(false);
// Act
await handle.DisposeAsync();
await handle.DisposeAsync().ConfigureAwait(false);
// Assert - 应该能再次获取锁
await using var handle2 = await manager.AcquireLockAsync("test-key");
await using var handle2 = await manager.AcquireLockAsync("test-key").ConfigureAwait(false);
Assert.That(handle2, Is.Not.Null);
}
@ -117,8 +117,8 @@ public sealed class AsyncKeyLockManagerTests
var key = $"key-{i % 10}";
tasks.Add(Task.Run(async () =>
{
await using var handle = await manager.AcquireLockAsync(key);
await Task.Delay(1);
await using var handle = await manager.AcquireLockAsync(key).ConfigureAwait(false);
await Task.Delay(1).ConfigureAwait(false);
}));
}
@ -139,14 +139,14 @@ public sealed class AsyncKeyLockManagerTests
{
tasks.Add(Task.Run(async () =>
{
await using var handle = await manager.AcquireLockAsync("same-key");
await using var handle = await manager.AcquireLockAsync("same-key").ConfigureAwait(false);
var temp = counter;
await Task.Delay(1);
await Task.Delay(1).ConfigureAwait(false);
counter = temp + 1;
}));
}
await Task.WhenAll(tasks);
await Task.WhenAll(tasks).ConfigureAwait(false);
// Assert
Assert.That(counter, Is.EqualTo(100));
@ -161,13 +161,13 @@ public sealed class AsyncKeyLockManagerTests
lockTimeout: TimeSpan.FromMilliseconds(200));
// Act
await using (var handle = await manager.AcquireLockAsync("temp-key"))
await using (var handle = await manager.AcquireLockAsync("temp-key").ConfigureAwait(false))
{
// 持有锁
}
// 等待清理
await Task.Delay(400);
await Task.Delay(400).ConfigureAwait(false);
var stats = manager.GetStatistics();
@ -184,10 +184,10 @@ public sealed class AsyncKeyLockManagerTests
lockTimeout: TimeSpan.FromMilliseconds(200));
// Act
await using var handle = await manager.AcquireLockAsync("active-key");
await using var handle = await manager.AcquireLockAsync("active-key").ConfigureAwait(false);
// 等待清理尝试
await Task.Delay(400);
await Task.Delay(400).ConfigureAwait(false);
var activeLocks = manager.GetActiveLocks();
@ -202,9 +202,9 @@ public sealed class AsyncKeyLockManagerTests
using var manager = new AsyncKeyLockManager();
// Act
await using (await manager.AcquireLockAsync("key1"))
await using (await manager.AcquireLockAsync("key1").ConfigureAwait(false))
{
await using var handle2 = await manager.AcquireLockAsync("key2");
await using var handle2 = await manager.AcquireLockAsync("key2").ConfigureAwait(false);
var stats = manager.GetStatistics();
// Assert
@ -223,8 +223,8 @@ public sealed class AsyncKeyLockManagerTests
using var manager = new AsyncKeyLockManager();
// Act
await using var handle1 = await manager.AcquireLockAsync("key1");
await using var handle2 = await manager.AcquireLockAsync("key2");
await using var handle1 = await manager.AcquireLockAsync("key1").ConfigureAwait(false);
await using var handle2 = await manager.AcquireLockAsync("key2").ConfigureAwait(false);
var activeLocks = manager.GetActiveLocks();
@ -254,14 +254,14 @@ public sealed class AsyncKeyLockManagerTests
using var cts = new CancellationTokenSource();
// 先获取锁
await using var handle = await manager.AcquireLockAsync("test-key", cts.Token);
await using var handle = await manager.AcquireLockAsync("test-key", cts.Token).ConfigureAwait(false);
// Act
await cts.CancelAsync();
await cts.CancelAsync().ConfigureAwait(false);
// Assert
Assert.CatchAsync<OperationCanceledException>(async () =>
await manager.AcquireLockAsync("test-key", cts.Token));
await manager.AcquireLockAsync("test-key", cts.Token).ConfigureAwait(false));
}
[Test]
@ -295,8 +295,8 @@ public sealed class AsyncKeyLockManagerTests
{
for (var j = 0; j < 10; j++)
{
await using var handle = await manager.AcquireLockAsync($"key-{j % 5}");
await Task.Delay(10);
await using var handle = await manager.AcquireLockAsync($"key-{j % 5}").ConfigureAwait(false);
await Task.Delay(10).ConfigureAwait(false);
}
}));
}
@ -324,11 +324,11 @@ public sealed class AsyncKeyLockManagerTests
{
// Arrange
using var manager = new AsyncKeyLockManager();
var handle = await manager.AcquireLockAsync("test-key");
var handle = await manager.AcquireLockAsync("test-key").ConfigureAwait(false);
// Act
await handle.DisposeAsync();
await handle.DisposeAsync();
await handle.DisposeAsync().ConfigureAwait(false);
await handle.DisposeAsync().ConfigureAwait(false);
handle.Dispose();
// Assert - 不应该抛出异常

View File

@ -284,13 +284,13 @@ public class WaitForTaskTests
var expectedValue = 123;
var task = Task.Run(async () =>
{
await Task.Delay(50);
await Task.Delay(50).ConfigureAwait(false);
return expectedValue;
});
var wait = new WaitForTask<int>(task);
await task;
await task.ConfigureAwait(false);
Task.Delay(100).Wait();
@ -313,4 +313,4 @@ public class WaitForTaskTests
Assert.That(wait.IsDone, Is.True);
}
}
}

View File

@ -11,6 +11,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
using System.Globalization;
using GFramework.Core.Functional;
namespace GFramework.Core.Tests.Extensions;
@ -122,7 +123,7 @@ public class ResultExtensionsTests
public void Map_Should_Transform_Success_Value()
{
var result = Result<int>.Succeed(42);
var mapped = result.Map(x => x.ToString());
var mapped = result.Map(x => x.ToString(CultureInfo.InvariantCulture));
Assert.That(mapped.IsSuccess, Is.True);
Assert.That(mapped.Match(succ: v => v, fail: _ => ""), Is.EqualTo("42"));
}
@ -135,7 +136,7 @@ public class ResultExtensionsTests
{
var exception = new Exception("Error");
var result = Result<int>.Fail(exception);
var mapped = result.Map(x => x.ToString());
var mapped = result.Map(x => x.ToString(CultureInfo.InvariantCulture));
Assert.That(mapped.IsFaulted, Is.True);
Assert.That(mapped.Exception, Is.SameAs(exception));
}
@ -157,7 +158,7 @@ public class ResultExtensionsTests
public void Bind_Should_Chain_Success_Results()
{
var result = Result<int>.Succeed(42);
var bound = result.Bind(x => Result<string>.Succeed(x.ToString()));
var bound = result.Bind(x => Result<string>.Succeed(x.ToString(CultureInfo.InvariantCulture)));
Assert.That(bound.IsSuccess, Is.True);
Assert.That(bound.Match(succ: v => v, fail: _ => ""), Is.EqualTo("42"));
}
@ -170,7 +171,7 @@ public class ResultExtensionsTests
{
var exception = new Exception("Error");
var result = Result<int>.Fail(exception);
var bound = result.Bind(x => Result<string>.Succeed(x.ToString()));
var bound = result.Bind(x => Result<string>.Succeed(x.ToString(CultureInfo.InvariantCulture)));
Assert.That(bound.IsFaulted, Is.True);
Assert.That(bound.Exception, Is.SameAs(exception));
}
@ -449,9 +450,9 @@ public class ResultExtensionsTests
{
var result = await ResultExtensions.TryAsync(async () =>
{
await Task.Delay(1);
await Task.Delay(1).ConfigureAwait(false);
return 42;
});
}).ConfigureAwait(false);
Assert.That(result.IsSuccess, Is.True);
Assert.That(result.Match(succ: v => v, fail: _ => 0), Is.EqualTo(42));
}
@ -464,9 +465,9 @@ public class ResultExtensionsTests
{
var result = await ResultExtensions.TryAsync<int>(async () =>
{
await Task.Delay(1);
await Task.Delay(1).ConfigureAwait(false);
throw new InvalidOperationException("Error");
});
}).ConfigureAwait(false);
Assert.That(result.IsFaulted, Is.True);
Assert.That(result.Exception, Is.TypeOf<InvalidOperationException>());
}
@ -477,7 +478,8 @@ public class ResultExtensionsTests
[Test]
public async Task TryAsync_Should_Handle_Synchronous_Exceptions()
{
var result = await ResultExtensions.TryAsync<int>(() => throw new InvalidOperationException("Sync error"));
var result = await ResultExtensions.TryAsync<int>(() => throw new InvalidOperationException("Sync error"))
.ConfigureAwait(false);
Assert.That(result.IsFaulted, Is.True);
}

View File

@ -1,3 +1,4 @@
using System.Globalization;
using GFramework.Core.Functional.Pipe;
using NUnit.Framework;
@ -123,7 +124,7 @@ public class PipeExtensionsTests
var result = value
.Pipe(x => x * 2)
.Pipe(x => x + 10)
.Pipe(x => x.ToString());
.Pipe(x => x.ToString(CultureInfo.InvariantCulture));
// Assert
Assert.That(result, Is.EqualTo("20"));
@ -139,7 +140,7 @@ public class PipeExtensionsTests
var value = 42;
// Act
var result = value.Let(x => x.ToString());
var result = value.Let(x => x.ToString(CultureInfo.InvariantCulture));
// Assert
Assert.That(result, Is.EqualTo("42"));
@ -171,7 +172,7 @@ public class PipeExtensionsTests
var result = value.Let(s => new
{
Original = s,
Upper = s.ToUpper(),
Upper = s.ToUpperInvariant(),
Length = s.Length
});
@ -280,4 +281,4 @@ public class PipeExtensionsTests
// Assert
Assert.That(result, Is.EqualTo("Large: 20"));
}
}
}

View File

@ -11,6 +11,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
using System.Globalization;
using GFramework.Core.Functional;
using NUnit.Framework;
@ -314,7 +315,7 @@ public class ResultTTests
public void Map_Should_Transform_Value_When_Success()
{
var result = Result<int>.Succeed(42);
var mapped = result.Map(x => x.ToString());
var mapped = result.Map(x => x.ToString(CultureInfo.InvariantCulture));
Assert.That(mapped.IsSuccess, Is.True);
Assert.That(mapped.Match(succ: v => v, fail: _ => ""), Is.EqualTo("42"));
}
@ -327,7 +328,7 @@ public class ResultTTests
{
var exception = new Exception("Error");
var result = Result<int>.Fail(exception);
var mapped = result.Map(x => x.ToString());
var mapped = result.Map(x => x.ToString(CultureInfo.InvariantCulture));
Assert.That(mapped.IsFaulted, Is.True);
Assert.That(mapped.Exception, Is.SameAs(exception));
}
@ -360,7 +361,7 @@ public class ResultTTests
public void Bind_Should_Chain_Success_Results()
{
var result = Result<int>.Succeed(42);
var bound = result.Bind(x => Result<string>.Succeed(x.ToString()));
var bound = result.Bind(x => Result<string>.Succeed(x.ToString(CultureInfo.InvariantCulture)));
Assert.That(bound.IsSuccess, Is.True);
Assert.That(bound.Match(succ: v => v, fail: _ => ""), Is.EqualTo("42"));
}
@ -373,7 +374,7 @@ public class ResultTTests
{
var exception = new Exception("Error");
var result = Result<int>.Fail(exception);
var bound = result.Bind(x => Result<string>.Succeed(x.ToString()));
var bound = result.Bind(x => Result<string>.Succeed(x.ToString(CultureInfo.InvariantCulture)));
Assert.That(bound.IsFaulted, Is.True);
Assert.That(bound.Exception, Is.SameAs(exception));
}
@ -414,7 +415,7 @@ public class ResultTTests
var mapped = await result.MapAsync(async x =>
{
await Task.Delay(1);
return x.ToString();
return x.ToString(CultureInfo.InvariantCulture);
});
Assert.That(mapped.IsSuccess, Is.True);
Assert.That(mapped.Match(succ: v => v, fail: _ => ""), Is.EqualTo("42"));
@ -431,7 +432,7 @@ public class ResultTTests
var mapped = await result.MapAsync(async x =>
{
await Task.Delay(1);
return x.ToString();
return x.ToString(CultureInfo.InvariantCulture);
});
Assert.That(mapped.IsFaulted, Is.True);
Assert.That(mapped.Exception, Is.SameAs(exception));
@ -804,4 +805,4 @@ public class ResultTTests
var bottom2 = Result<int>.Bottom;
Assert.That(bottom1.Equals(bottom2), Is.True);
}
}
}

View File

@ -33,7 +33,7 @@ public class TestResourceLoader : IResourceLoader<TestResource>
public async Task<TestResource> LoadAsync(string path)
{
await Task.Delay(10); // 模拟异步加载
await Task.Delay(10).ConfigureAwait(false); // 模拟异步加载
return Load(path);
}
@ -99,7 +99,7 @@ public class ResourceManagerTests
[Test]
public async Task LoadAsync_Should_Load_Resource()
{
var resource = await _resourceManager.LoadAsync<TestResource>("test/resource1.txt");
var resource = await _resourceManager.LoadAsync<TestResource>("test/resource1.txt").ConfigureAwait(false);
Assert.That(resource, Is.Not.Null);
Assert.That(resource!.Content, Is.EqualTo("Content 1"));
@ -404,4 +404,4 @@ public class ResourceManagerTests
handle2!.Dispose();
Assert.That(_resourceManager.IsLoaded("test/resource2.txt"), Is.False);
}
}
}

View File

@ -525,7 +525,7 @@ public sealed class TestAsyncState : IState, IAsyncState
/// <param name="from">从哪个状态进入</param>
public async Task OnEnterAsync(IState? from)
{
await Task.Delay(1);
await Task.Delay(1).ConfigureAwait(false);
EnterCalled = true;
EnterCallCount++;
EnterFrom = from;
@ -537,7 +537,7 @@ public sealed class TestAsyncState : IState, IAsyncState
/// <param name="to">离开到哪个状态</param>
public async Task OnExitAsync(IState? to)
{
await Task.Delay(1);
await Task.Delay(1).ConfigureAwait(false);
ExitCalled = true;
ExitCallCount++;
ExitTo = to;
@ -550,7 +550,7 @@ public sealed class TestAsyncState : IState, IAsyncState
/// <returns>是否允许转换</returns>
public async Task<bool> CanTransitionToAsync(IState target)
{
await Task.Delay(1);
await Task.Delay(1).ConfigureAwait(false);
CanTransitionToCallCount++;
return AllowTransition;
}

View File

@ -674,7 +674,7 @@ public sealed class ConcreteAsyncStateV2 : IState, IAsyncState
/// <param name="from">从哪个状态进入</param>
public async Task OnEnterAsync(IState? from)
{
await Task.Delay(1);
await Task.Delay(1).ConfigureAwait(false);
EnterCalled = true;
EnterCallCount++;
EnterFrom = from;
@ -686,7 +686,7 @@ public sealed class ConcreteAsyncStateV2 : IState, IAsyncState
/// <param name="to">退出到哪个状态</param>
public async Task OnExitAsync(IState? to)
{
await Task.Delay(1);
await Task.Delay(1).ConfigureAwait(false);
ExitCalled = true;
ExitCallCount++;
ExitTo = to;
@ -699,7 +699,7 @@ public sealed class ConcreteAsyncStateV2 : IState, IAsyncState
/// <returns>如果可以转换则返回true否则返回false</returns>
public async Task<bool> CanTransitionToAsync(IState target)
{
await Task.Delay(1);
await Task.Delay(1).ConfigureAwait(false);
CanTransitionToAsyncAction?.Invoke(target);
return AllowTransitions;
}
@ -731,4 +731,4 @@ public sealed class ConcreteAsyncStateV2 : IState, IAsyncState
{
throw new InvalidOperationException("Sync CanTransitionTo should not be called for async state");
}
}
}