using System.Diagnostics;
using GFramework.Core.extensions;
using NUnit.Framework;
namespace GFramework.Core.Tests.extensions;
///
/// 测试 AsyncExtensions 扩展方法的功能
///
[TestFixture]
public class AsyncExtensionsTests
{
[Test]
public async Task WithTimeout_Should_Return_Result_When_Task_Completes_Before_Timeout()
{
// Act
var result = await AsyncExtensions.WithTimeout(
_ => Task.FromResult(42),
TimeSpan.FromSeconds(1));
// Assert
Assert.That(result, Is.EqualTo(42));
}
[Test]
public void WithTimeout_Should_Throw_TimeoutException_When_Task_Exceeds_Timeout()
{
// Act & Assert
Assert.ThrowsAsync(async () =>
await AsyncExtensions.WithTimeout(
async ct =>
{
await Task.Delay(TimeSpan.FromSeconds(2), ct);
return 42;
},
TimeSpan.FromMilliseconds(100)));
}
[Test]
public void WithTimeout_Should_Throw_OperationCanceledException_When_Cancellation_Requested()
{
// Arrange
using var cts = new CancellationTokenSource();
cts.Cancel();
// Act & Assert
Assert.ThrowsAsync(async () =>
await AsyncExtensions.WithTimeout(
async ct =>
{
await Task.Delay(TimeSpan.FromSeconds(2), ct);
return 42;
},
TimeSpan.FromSeconds(1),
cts.Token));
}
[Test]
public void WithTimeout_Should_Cancel_Inner_Task_When_Timeout_Elapses()
{
// Arrange
var innerTaskCanceled = false;
// Act & Assert
Assert.ThrowsAsync(async () =>
await AsyncExtensions.WithTimeout(
async ct =>
{
try
{
await Task.Delay(TimeSpan.FromSeconds(5), ct);
return 0;
}
catch (OperationCanceledException)
{
innerTaskCanceled = true;
throw;
}
},
TimeSpan.FromMilliseconds(100)));
Assert.That(innerTaskCanceled, Is.True, "内部任务应在超时时收到取消信号");
}
[Test]
public async Task WithTimeout_NoResult_Should_Complete_When_Task_Completes_Before_Timeout()
{
// Arrange
var stopwatch = Stopwatch.StartNew();
// Act
await AsyncExtensions.WithTimeout(
_ => Task.CompletedTask,
TimeSpan.FromSeconds(1));
stopwatch.Stop();
// Assert
Assert.That(stopwatch.ElapsedMilliseconds, Is.LessThan(1000), "Task should complete before timeout");
Assert.Pass("Task completed successfully within timeout period");
}
[Test]
public void WithTimeout_NoResult_Should_Throw_TimeoutException_When_Task_Exceeds_Timeout()
{
// Act & Assert
Assert.ThrowsAsync(async () =>
await AsyncExtensions.WithTimeout(
ct => Task.Delay(TimeSpan.FromSeconds(2), ct),
TimeSpan.FromMilliseconds(100)));
}
[Test]
public void WithTimeout_NoResult_Should_Cancel_Inner_Task_When_Timeout_Elapses()
{
// Arrange
var innerTaskCanceled = false;
// Act & Assert
Assert.ThrowsAsync(async () =>
await AsyncExtensions.WithTimeout(
async ct =>
{
try
{
await Task.Delay(TimeSpan.FromSeconds(5), ct);
}
catch (OperationCanceledException)
{
innerTaskCanceled = true;
throw;
}
},
TimeSpan.FromMilliseconds(100)));
Assert.That(innerTaskCanceled, Is.True, "内部任务应在超时时收到取消信号");
}
[Test]
public async Task WithRetry_Should_Return_Result_When_Task_Succeeds()
{
// Arrange
var attemptCount = 0;
Func> taskFactory = () =>
{
attemptCount++;
return Task.FromResult(42);
};
// Act
var result = await taskFactory.WithRetry(3, TimeSpan.FromMilliseconds(10));
// Assert
Assert.That(result, Is.EqualTo(42));
Assert.That(attemptCount, Is.EqualTo(1));
}
[Test]
public async Task WithRetry_Should_Retry_On_Failure()
{
// Arrange
var attemptCount = 0;
Func> taskFactory = () =>
{
attemptCount++;
if (attemptCount < 3)
throw new InvalidOperationException("Temporary failure");
return Task.FromResult(42);
};
// Act
var result = await taskFactory.WithRetry(3, TimeSpan.FromMilliseconds(10));
// Assert
Assert.That(result, Is.EqualTo(42));
Assert.That(attemptCount, Is.EqualTo(3));
}
[Test]
public void WithRetry_Should_Throw_AggregateException_When_All_Retries_Fail()
{
// Arrange
var attemptCount = 0;
Func> taskFactory = () =>
{
attemptCount++;
throw new InvalidOperationException("Permanent failure");
};
// Act & Assert
Assert.ThrowsAsync(async () =>
await taskFactory.WithRetry(2, TimeSpan.FromMilliseconds(10)));
}
[Test]
public async Task WithRetry_Should_Respect_ShouldRetry_Predicate()
{
// Arrange
var attemptCount = 0;
Func> taskFactory = () =>
{
attemptCount++;
throw new ArgumentException("Should not retry");
};
// Act & Assert
Assert.ThrowsAsync(async () =>
await taskFactory.WithRetry(3, TimeSpan.FromMilliseconds(10),
ex => ex is not ArgumentException));
await Task.Delay(50); // 等待任务完成
Assert.That(attemptCount, Is.EqualTo(1)); // 不应该重试
}
[Test]
public async Task TryAsync_Should_Return_Success_When_Task_Succeeds()
{
// Arrange
Func> func = () => Task.FromResult(42);
// Act
var result = await func.TryAsync();
// Assert
Assert.That(result.IsSuccess, Is.True);
Assert.That(result.IfFail(0), Is.EqualTo(42));
}
[Test]
public async Task TryAsync_Should_Return_Failure_When_Task_Throws()
{
// Arrange
Func> func = () => throw new InvalidOperationException("Test error");
// Act
var result = await func.TryAsync();
// Assert
Assert.That(result.IsFaulted, Is.True);
}
[Test]
public async Task WhenAll_Should_Wait_For_All_Tasks()
{
// Arrange
var task1 = Task.Delay(10);
var task2 = Task.Delay(20);
var task3 = Task.Delay(30);
var tasks = new[] { task1, task2, task3 };
// Act
await tasks.WhenAll();
// Assert
Assert.That(task1.IsCompleted, Is.True);
Assert.That(task2.IsCompleted, Is.True);
Assert.That(task3.IsCompleted, Is.True);
}
[Test]
public async Task WhenAll_WithResults_Should_Return_All_Results()
{
// Arrange
var task1 = Task.FromResult(1);
var task2 = Task.FromResult(2);
var task3 = Task.FromResult(3);
var tasks = new[] { task1, task2, task3 };
// Act
var results = await tasks.WhenAll();
// Assert
Assert.That(results, Is.EqualTo(new[] { 1, 2, 3 }));
}
[Test]
public async Task WithFallback_Should_Return_Result_When_Task_Succeeds()
{
// Arrange
var task = Task.FromResult(42);
// Act
var result = await task.WithFallback(_ => -1);
// Assert
Assert.That(result, Is.EqualTo(42));
}
[Test]
public async Task WithFallback_Should_Return_Fallback_Value_When_Task_Fails()
{
// Arrange
var task = Task.FromException(new InvalidOperationException("Test error"));
// Act
var result = await task.WithFallback(_ => -1);
// Assert
Assert.That(result, Is.EqualTo(-1));
}
[Test]
public async Task WithFallback_Should_Pass_Exception_To_Fallback()
{
// Arrange
var expectedException = new InvalidOperationException("Test error");
var task = Task.FromException(expectedException);
Exception? capturedEx = null;
// Act
await task.WithFallback(ex =>
{
capturedEx = ex;
return -1;
});
// Assert
Assert.That(capturedEx, Is.SameAs(expectedException));
}
}