mirror of
https://github.com/GeWuYou/GFramework.git
synced 2026-03-22 10:34:30 +08:00
feat(tests): 添加函数式编程扩展和Option类型测试
- 添加ControlExtensions的TakeIfValue、TakeUnlessValue、When、RepeatUntil和Retry方法测试 - 添加FunctionExtensions的Compose、AndThen、Curry、Uncurry、Defer和Once方法测试 - 添加Option<T>类型的完整测试套件,包括工厂方法、取值、变换、过滤、模式匹配等功能 - 添加PipeExtensions的Tap、Pipe、Let、PipeIf方法测试 - 移除AsyncExtensionsTests中的WhenAll相关测试 - 移除NumericExtensionsTests中的Clamp测试 - 更新ObjectExtensionsTests中Do方法为Also方法的测试 - 修复ControlExtensions文档中的XML代码标签格式 - 在AsyncExtensionsTests中添加对GFramework.Core.Functional.Async的引用 - 在ObjectExtensionsTests中添加对GFramework.Core.functional.pipe的引用
This commit is contained in:
parent
28ce315d29
commit
59fbb2253b
@ -1,5 +1,6 @@
|
||||
using System.Diagnostics;
|
||||
using GFramework.Core.extensions;
|
||||
using GFramework.Core.Functional.Async;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace GFramework.Core.Tests.extensions;
|
||||
@ -237,40 +238,6 @@ public class AsyncExtensionsTests
|
||||
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()
|
||||
{
|
||||
|
||||
@ -9,55 +9,6 @@ namespace GFramework.Core.Tests.extensions;
|
||||
[TestFixture]
|
||||
public class NumericExtensionsTests
|
||||
{
|
||||
[Test]
|
||||
public void Clamp_Should_Return_Min_When_Value_Is_Less_Than_Min()
|
||||
{
|
||||
// Arrange
|
||||
var value = -10;
|
||||
|
||||
// Act
|
||||
var result = value.Clamp(0, 100);
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.EqualTo(0));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Clamp_Should_Return_Max_When_Value_Is_Greater_Than_Max()
|
||||
{
|
||||
// Arrange
|
||||
var value = 150;
|
||||
|
||||
// Act
|
||||
var result = value.Clamp(0, 100);
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.EqualTo(100));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Clamp_Should_Return_Value_When_Value_Is_Within_Range()
|
||||
{
|
||||
// Arrange
|
||||
var value = 50;
|
||||
|
||||
// Act
|
||||
var result = value.Clamp(0, 100);
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.EqualTo(50));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Clamp_Should_Throw_ArgumentException_When_Min_Is_Greater_Than_Max()
|
||||
{
|
||||
// Arrange
|
||||
var value = 50;
|
||||
|
||||
// Act & Assert
|
||||
Assert.Throws<ArgumentException>(() => value.Clamp(100, 0));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Between_Should_Return_True_When_Value_Is_Within_Range()
|
||||
{
|
||||
@ -212,4 +163,4 @@ public class NumericExtensionsTests
|
||||
// Arrange & Act & Assert
|
||||
Assert.Throws<DivideByZeroException>(() => 50f.InverseLerp(100f, 100f));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,4 +1,5 @@
|
||||
using GFramework.Core.extensions;
|
||||
using GFramework.Core.functional.pipe;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace GFramework.Core.Tests.extensions;
|
||||
@ -138,29 +139,29 @@ public class ObjectExtensionsTests
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证Do方法执行操作并返回对象本身
|
||||
/// 验证Also方法执行操作并返回对象本身
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void Do_Should_Execute_Action_And_Return_Object()
|
||||
{
|
||||
var obj = new TestClass { Value = 5 };
|
||||
|
||||
var result = obj.Do(x => x.Value = 10);
|
||||
var result = obj.Also(x => x.Value = 10);
|
||||
|
||||
Assert.That(result, Is.SameAs(obj));
|
||||
Assert.That(obj.Value, Is.EqualTo(10));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证Do方法支持链式调用
|
||||
/// 验证Also方法支持链式调用
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void Do_Should_Support_Chaining()
|
||||
{
|
||||
var obj = new TestClass { Value = 1, Name = "A" };
|
||||
|
||||
obj.Do(x => x.Value = 2)
|
||||
.Do(x => x.Name = "B");
|
||||
obj.Also(x => x.Value = 2)
|
||||
.Also(x => x.Name = "B");
|
||||
|
||||
Assert.That(obj.Value, Is.EqualTo(2));
|
||||
Assert.That(obj.Name, Is.EqualTo("B"));
|
||||
|
||||
@ -11,8 +11,8 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using GFramework.Core.Extensions;
|
||||
using GFramework.Core.Functional;
|
||||
using GFramework.Core.functional.result;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace GFramework.Core.Tests.Extensions;
|
||||
|
||||
@ -55,7 +55,7 @@ public class StringExtensionsTests
|
||||
string? text = null;
|
||||
|
||||
// Act
|
||||
var result = text.IsNullOrWhiteSpace();
|
||||
var result = string.IsNullOrWhiteSpace(text);
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.True);
|
||||
@ -68,7 +68,7 @@ public class StringExtensionsTests
|
||||
var text = " ";
|
||||
|
||||
// Act
|
||||
var result = text.IsNullOrWhiteSpace();
|
||||
var result = string.IsNullOrWhiteSpace(text);
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.True);
|
||||
@ -81,7 +81,7 @@ public class StringExtensionsTests
|
||||
var text = "Hello";
|
||||
|
||||
// Act
|
||||
var result = text.IsNullOrWhiteSpace();
|
||||
var result = string.IsNullOrWhiteSpace(text);
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.False);
|
||||
@ -230,4 +230,4 @@ public class StringExtensionsTests
|
||||
// Act & Assert
|
||||
Assert.Throws<ArgumentNullException>(() => words.Join(null!));
|
||||
}
|
||||
}
|
||||
}
|
||||
388
GFramework.Core.Tests/functional/OptionTests.cs
Normal file
388
GFramework.Core.Tests/functional/OptionTests.cs
Normal file
@ -0,0 +1,388 @@
|
||||
// Copyright (c) 2025 GeWuYou
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using GFramework.Core.Functional;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace GFramework.Core.Tests.Functional;
|
||||
|
||||
/// <summary>
|
||||
/// Option<T> 类型测试类
|
||||
/// </summary>
|
||||
[TestFixture]
|
||||
public class OptionTests
|
||||
{
|
||||
[Test]
|
||||
public void Some_WithValue_Should_Create_Some_Option()
|
||||
{
|
||||
var option = Option<int>.Some(42);
|
||||
Assert.That(option.IsSome, Is.True);
|
||||
Assert.That(option.IsNone, Is.False);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Some_WithNull_Should_Throw_ArgumentNullException()
|
||||
{
|
||||
Assert.Throws<ArgumentNullException>(() => Option<string>.Some(null!));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void None_Should_Create_None_Option()
|
||||
{
|
||||
var option = Option<int>.None;
|
||||
Assert.That(option.IsSome, Is.False);
|
||||
Assert.That(option.IsNone, Is.True);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetOrElse_WithSome_Should_Return_Value()
|
||||
{
|
||||
var option = Option<int>.Some(42);
|
||||
var result = option.GetOrElse(0);
|
||||
Assert.That(result, Is.EqualTo(42));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetOrElse_WithNone_Should_Return_Default_Value()
|
||||
{
|
||||
var option = Option<int>.None;
|
||||
var result = option.GetOrElse(99);
|
||||
Assert.That(result, Is.EqualTo(99));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetOrElse_WithFactory_WithSome_Should_Return_Value_Without_Calling_Factory()
|
||||
{
|
||||
var option = Option<int>.Some(42);
|
||||
var factoryCalled = false;
|
||||
|
||||
var result = option.GetOrElse(() =>
|
||||
{
|
||||
factoryCalled = true;
|
||||
return 99;
|
||||
});
|
||||
|
||||
Assert.That(result, Is.EqualTo(42));
|
||||
Assert.That(factoryCalled, Is.False);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetOrElse_WithFactory_WithNone_Should_Call_Factory()
|
||||
{
|
||||
var option = Option<int>.None;
|
||||
var result = option.GetOrElse(() => 99);
|
||||
Assert.That(result, Is.EqualTo(99));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetOrElse_WithNullFactory_Should_Throw_ArgumentNullException()
|
||||
{
|
||||
var option = Option<int>.None;
|
||||
Assert.Throws<ArgumentNullException>(() => option.GetOrElse(null!));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Map_WithSome_Should_Map_Value()
|
||||
{
|
||||
var option = Option<int>.Some(42);
|
||||
var mapped = option.Map(x => x.ToString());
|
||||
Assert.That(mapped.IsSome, Is.True);
|
||||
Assert.That(mapped.GetOrElse(""), Is.EqualTo("42"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Map_WithNone_Should_Return_None()
|
||||
{
|
||||
var option = Option<int>.None;
|
||||
var mapped = option.Map(x => x.ToString());
|
||||
Assert.That(mapped.IsNone, Is.True);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Map_WithNullMapper_Should_Throw_ArgumentNullException()
|
||||
{
|
||||
var option = Option<int>.Some(42);
|
||||
Assert.Throws<ArgumentNullException>(() => option.Map<string>(null!));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Bind_WithSome_Should_Bind_Value()
|
||||
{
|
||||
var option = Option<string>.Some("42");
|
||||
var bound = option.Bind(s => int.TryParse(s, out var i)
|
||||
? Option<int>.Some(i)
|
||||
: Option<int>.None);
|
||||
|
||||
Assert.That(bound.IsSome, Is.True);
|
||||
Assert.That(bound.GetOrElse(0), Is.EqualTo(42));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Bind_WithSome_Can_Return_None()
|
||||
{
|
||||
var option = Option<string>.Some("invalid");
|
||||
var bound = option.Bind(s => int.TryParse(s, out var i)
|
||||
? Option<int>.Some(i)
|
||||
: Option<int>.None);
|
||||
|
||||
Assert.That(bound.IsNone, Is.True);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Bind_WithNone_Should_Return_None()
|
||||
{
|
||||
var option = Option<string>.None;
|
||||
var bound = option.Bind(s => Option<int>.Some(42));
|
||||
Assert.That(bound.IsNone, Is.True);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Bind_WithNullBinder_Should_Throw_ArgumentNullException()
|
||||
{
|
||||
var option = Option<int>.Some(42);
|
||||
Assert.Throws<ArgumentNullException>(() => option.Bind<string>(null!));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Filter_WithSome_PredicateTrue_Should_Return_Some()
|
||||
{
|
||||
var option = Option<int>.Some(42);
|
||||
var filtered = option.Filter(x => x > 0);
|
||||
Assert.That(filtered.IsSome, Is.True);
|
||||
Assert.That(filtered.GetOrElse(0), Is.EqualTo(42));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Filter_WithSome_PredicateFalse_Should_Return_None()
|
||||
{
|
||||
var option = Option<int>.Some(42);
|
||||
var filtered = option.Filter(x => x < 0);
|
||||
Assert.That(filtered.IsNone, Is.True);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Filter_WithNone_Should_Return_None()
|
||||
{
|
||||
var option = Option<int>.None;
|
||||
var filtered = option.Filter(x => true);
|
||||
Assert.That(filtered.IsNone, Is.True);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Filter_WithNullPredicate_Should_Throw_ArgumentNullException()
|
||||
{
|
||||
var option = Option<int>.Some(42);
|
||||
Assert.Throws<ArgumentNullException>(() => option.Filter(null!));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Match_WithSome_Should_Call_Some_Function()
|
||||
{
|
||||
var option = Option<int>.Some(42);
|
||||
var result = option.Match(
|
||||
some: value => $"Value: {value}",
|
||||
none: () => "No value"
|
||||
);
|
||||
Assert.That(result, Is.EqualTo("Value: 42"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Match_WithNone_Should_Call_None_Function()
|
||||
{
|
||||
var option = Option<int>.None;
|
||||
var result = option.Match(
|
||||
some: value => $"Value: {value}",
|
||||
none: () => "No value"
|
||||
);
|
||||
Assert.That(result, Is.EqualTo("No value"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Match_WithNullSomeFunction_Should_Throw_ArgumentNullException()
|
||||
{
|
||||
var option = Option<int>.Some(42);
|
||||
Assert.Throws<ArgumentNullException>(() =>
|
||||
option.Match<string>(null!, () => ""));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Match_WithNullNoneFunction_Should_Throw_ArgumentNullException()
|
||||
{
|
||||
var option = Option<int>.Some(42);
|
||||
Assert.Throws<ArgumentNullException>(() =>
|
||||
option.Match(value => "", null!));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Match_Action_WithSome_Should_Call_Some_Action()
|
||||
{
|
||||
var option = Option<int>.Some(42);
|
||||
var someCalled = false;
|
||||
var noneCalled = false;
|
||||
|
||||
option.Match(
|
||||
some: _ => someCalled = true,
|
||||
none: () => noneCalled = true
|
||||
);
|
||||
|
||||
Assert.That(someCalled, Is.True);
|
||||
Assert.That(noneCalled, Is.False);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Match_Action_WithNone_Should_Call_None_Action()
|
||||
{
|
||||
var option = Option<int>.None;
|
||||
var someCalled = false;
|
||||
var noneCalled = false;
|
||||
|
||||
option.Match(
|
||||
some: _ => someCalled = true,
|
||||
none: () => noneCalled = true
|
||||
);
|
||||
|
||||
Assert.That(someCalled, Is.False);
|
||||
Assert.That(noneCalled, Is.True);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ToResult_WithSome_Should_Return_Success_Result()
|
||||
{
|
||||
var option = Option<int>.Some(42);
|
||||
var result = option.ToResult("Error");
|
||||
Assert.That(result.IsSuccess, Is.True);
|
||||
Assert.That(result.Match(succ: v => v, fail: _ => 0), Is.EqualTo(42));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ToResult_WithNone_Should_Return_Failure_Result()
|
||||
{
|
||||
var option = Option<int>.None;
|
||||
var result = option.ToResult("Value not found");
|
||||
Assert.That(result.IsFaulted, Is.True);
|
||||
Assert.That(result.Exception, Is.TypeOf<InvalidOperationException>());
|
||||
Assert.That(result.Exception.Message, Is.EqualTo("Value not found"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ToResult_WithNullOrWhiteSpaceMessage_Should_Throw_ArgumentException()
|
||||
{
|
||||
var option = Option<int>.None;
|
||||
Assert.Throws<ArgumentException>(() => option.ToResult(""));
|
||||
Assert.Throws<ArgumentException>(() => option.ToResult(" "));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ToEnumerable_WithSome_Should_Return_Sequence_With_One_Element()
|
||||
{
|
||||
var option = Option<int>.Some(42);
|
||||
var enumerable = option.ToEnumerable().ToList();
|
||||
Assert.That(enumerable, Has.Count.EqualTo(1));
|
||||
Assert.That(enumerable[0], Is.EqualTo(42));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ToEnumerable_WithNone_Should_Return_Empty_Sequence()
|
||||
{
|
||||
var option = Option<int>.None;
|
||||
var enumerable = option.ToEnumerable().ToList();
|
||||
Assert.That(enumerable, Is.Empty);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ImplicitConversion_FromValue_Should_Create_Some_Option()
|
||||
{
|
||||
Option<int> option = 42;
|
||||
Assert.That(option.IsSome, Is.True);
|
||||
Assert.That(option.GetOrElse(0), Is.EqualTo(42));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ImplicitConversion_FromNull_Should_Create_None_Option()
|
||||
{
|
||||
Option<string> option = null!;
|
||||
Assert.That(option.IsNone, Is.True);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Equals_TwoSomeWithSameValue_Should_Return_True()
|
||||
{
|
||||
var option1 = Option<int>.Some(42);
|
||||
var option2 = Option<int>.Some(42);
|
||||
Assert.That(option1.Equals(option2), Is.True);
|
||||
Assert.That(option1 == option2, Is.True);
|
||||
Assert.That(option1 != option2, Is.False);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Equals_TwoSomeWithDifferentValue_Should_Return_False()
|
||||
{
|
||||
var option1 = Option<int>.Some(42);
|
||||
var option2 = Option<int>.Some(99);
|
||||
Assert.That(option1.Equals(option2), Is.False);
|
||||
Assert.That(option1 == option2, Is.False);
|
||||
Assert.That(option1 != option2, Is.True);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Equals_TwoNone_Should_Return_True()
|
||||
{
|
||||
var option1 = Option<int>.None;
|
||||
var option2 = Option<int>.None;
|
||||
Assert.That(option1.Equals(option2), Is.True);
|
||||
Assert.That(option1 == option2, Is.True);
|
||||
Assert.That(option1 != option2, Is.False);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Equals_SomeAndNone_Should_Return_False()
|
||||
{
|
||||
var option1 = Option<int>.Some(42);
|
||||
var option2 = Option<int>.None;
|
||||
Assert.That(option1.Equals(option2), Is.False);
|
||||
Assert.That(option1 == option2, Is.False);
|
||||
Assert.That(option1 != option2, Is.True);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetHashCode_TwoSomeWithSameValue_Should_Return_Same_HashCode()
|
||||
{
|
||||
var option1 = Option<int>.Some(42);
|
||||
var option2 = Option<int>.Some(42);
|
||||
Assert.That(option1.GetHashCode(), Is.EqualTo(option2.GetHashCode()));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetHashCode_TwoNone_Should_Return_Same_HashCode()
|
||||
{
|
||||
var option1 = Option<int>.None;
|
||||
var option2 = Option<int>.None;
|
||||
Assert.That(option1.GetHashCode(), Is.EqualTo(option2.GetHashCode()));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ToString_WithSome_Should_Return_Formatted_String()
|
||||
{
|
||||
var option = Option<int>.Some(42);
|
||||
var result = option.ToString();
|
||||
Assert.That(result, Is.EqualTo("Some(42)"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ToString_WithNone_Should_Return_None()
|
||||
{
|
||||
var option = Option<int>.None;
|
||||
var result = option.ToString();
|
||||
Assert.That(result, Is.EqualTo("None"));
|
||||
}
|
||||
}
|
||||
@ -72,4 +72,335 @@ public class ControlExtensionsTests
|
||||
// Assert
|
||||
Assert.That(result, Is.Null);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TakeIfValue_Should_Return_Value_When_Condition_Is_True()
|
||||
{
|
||||
// Arrange
|
||||
var value = 42;
|
||||
|
||||
// Act
|
||||
var result = value.TakeIfValue(x => x > 0);
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.EqualTo(42));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TakeIfValue_Should_Return_Null_When_Condition_Is_False()
|
||||
{
|
||||
// Arrange
|
||||
var value = -5;
|
||||
|
||||
// Act
|
||||
var result = value.TakeIfValue(x => x > 0);
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.Null);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TakeIfValue_WithNullPredicate_Should_Throw_ArgumentNullException()
|
||||
{
|
||||
// Arrange
|
||||
var value = 42;
|
||||
|
||||
// Act & Assert
|
||||
Assert.Throws<ArgumentNullException>(() => value.TakeIfValue(null!));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TakeUnlessValue_Should_Return_Value_When_Condition_Is_False()
|
||||
{
|
||||
// Arrange
|
||||
var value = 42;
|
||||
|
||||
// Act
|
||||
var result = value.TakeUnlessValue(x => x < 0);
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.EqualTo(42));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TakeUnlessValue_Should_Return_Null_When_Condition_Is_True()
|
||||
{
|
||||
// Arrange
|
||||
var value = -5;
|
||||
|
||||
// Act
|
||||
var result = value.TakeUnlessValue(x => x < 0);
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.Null);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TakeUnlessValue_WithNullPredicate_Should_Throw_ArgumentNullE()
|
||||
{
|
||||
// Arrange
|
||||
var value = 42;
|
||||
|
||||
// Act & Assert
|
||||
Assert.Throws<ArgumentNullException>(() => value.TakeUnlessValue(null!));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void When_Should_Execute_Action_When_Condition_Is_True()
|
||||
{
|
||||
// Arrange
|
||||
var value = 42;
|
||||
var executed = false;
|
||||
|
||||
// Act
|
||||
var result = value.When(x => x > 0, x => executed = true);
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.EqualTo(42));
|
||||
Assert.That(executed, Is.True);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void When_Should_Not_Executehen_Condition_Is_False()
|
||||
{
|
||||
// Arrange
|
||||
var value = -5;
|
||||
var executed = false;
|
||||
|
||||
// Act
|
||||
var result = value.When(x => x > 0, x => executed = true);
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.EqualTo(-5));
|
||||
Assert.That(executed, Is.False);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void When_WithNullPredicate_Should_Throw_ArgumentNullException()
|
||||
{
|
||||
// Arrange
|
||||
var value = 42;
|
||||
|
||||
// Act & Assert
|
||||
Assert.Throws<ArgumentNullException>(() =>
|
||||
value.When(null!, x => { }));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void When_WithNullAction_Should_Throw_ArgumentNullException()
|
||||
{
|
||||
// Arrange
|
||||
var value = 42;
|
||||
|
||||
// Act & Assert
|
||||
Assert.Throws<ArgumentNullException>(() =>
|
||||
value.When(x => true, null!));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void When_Should_Allow_Chaining()
|
||||
{
|
||||
// Arrange
|
||||
var value = 10;
|
||||
var log = new List<string>();
|
||||
|
||||
// Act
|
||||
var result = value
|
||||
.When(x => x > 5, x => log.Add("Greater than 5"))
|
||||
.When(x => x % 2 == 0, x => log.Add("Even number"));
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.EqualTo(10));
|
||||
Assert.That(log, Has.Count.EqualTo(2));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void RepeatUntil_Should_Repeat_Until_Condition_Is_Met()
|
||||
{
|
||||
// Arrange
|
||||
var value = 1;
|
||||
|
||||
// Act
|
||||
var result = value.RepeatUntil(
|
||||
x => x * 2,
|
||||
x => x >= 100,
|
||||
maxIterations: 10
|
||||
);
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.EqualTo(128));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void RepeatUntil_Should_Return_Initial_Value_If_Condition_Already_Met()
|
||||
{
|
||||
// Arrange
|
||||
var value = 100;
|
||||
|
||||
// Act
|
||||
var result = value.RepeatUntil(
|
||||
x => x * 2,
|
||||
x => x >= 100,
|
||||
maxIterations: 10
|
||||
);
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.EqualTo(100));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void RepeatUntil_Should_Throw_When_Max_Iterations_Reached()
|
||||
{
|
||||
// Arrange
|
||||
var value = 1;
|
||||
|
||||
// Act & Assert
|
||||
Assert.Throws<InvalidOperationException>(() =>
|
||||
value.RepeatUntil(
|
||||
x => x + 1,
|
||||
x => x > 1000,
|
||||
maxIterations: 10
|
||||
));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void RepeatUntil_WithNullFunc_Should_Throw_ArgumentNullException()
|
||||
{
|
||||
// Arrange
|
||||
var value = 1;
|
||||
|
||||
// Act & Assert
|
||||
Assert.Throws<ArgumentNullException>(() =>
|
||||
value.RepeatUntil(null!, x => true));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void RepeatUntil_WithNullPredicate_Should_Throw_ArgumentNullException()
|
||||
{
|
||||
// Arrange
|
||||
var value = 1;
|
||||
|
||||
// Act & Assert
|
||||
Assert.Throws<ArgumentNullException>(() =>
|
||||
value.RepeatUntil(x => x, null!));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void RepeatUntil_WithInvalidMaxIterations_Should_Throw_ArgumentOutOfRangeException()
|
||||
{
|
||||
// Arrange
|
||||
var value = 1;
|
||||
|
||||
// Act & Assert
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() =>
|
||||
value.RepeatUntil(x => x, x => true, maxIterations: 0));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Retry_Should_Return_Result_On_First_Success()
|
||||
{
|
||||
// Arrange
|
||||
var counter = 0;
|
||||
Func<int> func = () => ++counter;
|
||||
|
||||
// Act
|
||||
var result = ControlExtensions.Retry(func, maxRetries: 3);
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.EqualTo(1));
|
||||
Assert.That(counter, Is.EqualTo(1));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Retry_Should_Retry_On_Failure()
|
||||
{
|
||||
// Arrange
|
||||
var counter = 0;
|
||||
Func<int> func = () =>
|
||||
{
|
||||
counter++;
|
||||
if (counter < 3)
|
||||
throw new InvalidOperationException("Not ready");
|
||||
return counter;
|
||||
};
|
||||
|
||||
// Act
|
||||
var result = ControlExtensions.Retry(func, maxRetries: 3);
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.EqualTo(3));
|
||||
Assert.That(counter, Is.EqualTo(3));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Retry_Should_Throw_AggregateException_When_All_Retries_Fail()
|
||||
{
|
||||
// Arrange
|
||||
var counter = 0;
|
||||
Func<int> func = () =>
|
||||
{
|
||||
counter++;
|
||||
throw new InvalidOperationException($"Attempt {counter}");
|
||||
};
|
||||
|
||||
// Act & Assert
|
||||
var ex = Assert.Throws<AggregateException>(() =>
|
||||
ControlExtensions.Retry(func, maxRetries: 2));
|
||||
|
||||
Assert.That(counter, Is.EqualTo(3)); // 1 initial + 2 retries
|
||||
Assert.That(ex!.InnerExceptions, Has.Count.EqualTo(3));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Retry_WithNullFunc_Should_Throw_ArgumentNullException()
|
||||
{
|
||||
// Act & Assert
|
||||
Assert.Throws<ArgumentNullException>(() =>
|
||||
ControlExtensions.Retry<int>(null!, maxRetries: 3));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Retry_WithNegativeMaxRetries_Should_Throw_ArgumentOutOfRangeException()
|
||||
{
|
||||
// Arrange
|
||||
Func<int> func = () => 42;
|
||||
|
||||
// Act & Assert
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() =>
|
||||
ControlExtensions.Retry(func, maxRetries: -1));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Retry_WithNegativeDelay_Should_Throw_ArgumentOutOfRangeException()
|
||||
{
|
||||
// Arrange
|
||||
Func<int> func = () => 42;
|
||||
|
||||
// Act & Assert
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() =>
|
||||
ControlExtensions.Retry(func, maxRetries: 3, delayMilliseconds: -1));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Retry_Should_Delay_Between_Retries()
|
||||
{
|
||||
// Arrange
|
||||
var counter = 0;
|
||||
var startTime = DateTime.UtcNow;
|
||||
Func<int> func = () =>
|
||||
{
|
||||
counter++;
|
||||
if (counter < 3)
|
||||
throw new InvalidOperationException("Not ready");
|
||||
return counter;
|
||||
};
|
||||
|
||||
// Act
|
||||
var result = ControlExtensions.Retry(func, maxRetries: 3, delayMilliseconds: 50);
|
||||
var elapsed = DateTime.UtcNow - startTime;
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.EqualTo(3));
|
||||
Assert.That(elapsed.TotalMilliseconds, Is.GreaterThanOrEqualTo(100)); // 2 delays of 50ms
|
||||
}
|
||||
}
|
||||
@ -43,4 +43,234 @@ public class FunctionExtensionsTests
|
||||
// 2 -> 4 -> 8 -> 16 (3次重复)
|
||||
Assert.That(result, Is.EqualTo(16));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Compose_Should_Apply_Functions_In_Reverse_Order()
|
||||
{
|
||||
// Arrange
|
||||
int AddOne(int x) => x + 1;
|
||||
Func<int, int> multiplyTwo = x => x * 2;
|
||||
|
||||
// Act
|
||||
var composed = multiplyTwo.Compose((Func<int, int>)AddOne); // (x + 1) * 2
|
||||
|
||||
// Assert
|
||||
Assert.That(composed(5), Is.EqualTo(12)); // (5 + 1) * 2 = 12
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Compose_WithNullOuterFunction_Should_Throw_ArgumentNullException()
|
||||
{
|
||||
// Arrange
|
||||
Func<int, int> addOne = x => x + 1;
|
||||
|
||||
// Act & Assert
|
||||
Assert.Throws<ArgumentNullException>(() =>
|
||||
((Func<int, int>)null!).Compose(addOne));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Compose_WithNullInnerFunction_Should_Throw_ArgumentNullException()
|
||||
{
|
||||
// Arrange
|
||||
Func<int, int> multiplyTwo = x => x * 2;
|
||||
|
||||
// Act & Assert
|
||||
Assert.Throws<ArgumentNullException>(() =>
|
||||
multiplyTwo.Compose<int, int, int>(null!));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AndThen_Should_Apply_Functions_In_Order()
|
||||
{
|
||||
// Arrange
|
||||
Func<int, int> addOne = x => x + 1;
|
||||
int MultiplyTwo(int x) => x * 2;
|
||||
|
||||
// Act
|
||||
var chained = addOne.AndThen((Func<int, int>)MultiplyTwo); // (x + 1) * 2
|
||||
|
||||
// Assert
|
||||
Assert.That(chained(5), Is.EqualTo(12)); // (5 + 1) * 2 = 12
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AndThen_WithNullFirstFunction_Should_Throw_ArgumentNullException()
|
||||
{
|
||||
// Arrange
|
||||
Func<int, int> multiplyTwo = x => x * 2;
|
||||
|
||||
// Act & Assert
|
||||
Assert.Throws<ArgumentNullException>(() =>
|
||||
((Func<int, int>)null!).AndThen(multiplyTwo));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AndThen_WithNullSecondFunction_Should_Throw_ArgumentNullException()
|
||||
{
|
||||
// Arrange
|
||||
Func<int, int> addOne = x => x + 1;
|
||||
|
||||
// Act & Assert
|
||||
Assert.Throws<ArgumentNullException>(() =>
|
||||
addOne.AndThen<int, int, int>(null!));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Curry_TwoParameters_Should_Return_Nested_Functions()
|
||||
{
|
||||
// Arrange
|
||||
Func<int, int, int> add = (x, y) => x + y;
|
||||
|
||||
// Act
|
||||
var curriedAdd = add.Curry();
|
||||
var add5 = curriedAdd(5);
|
||||
var result = add5(3);
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.EqualTo(8));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Curry_ThreeParameters_Should_Return_Nested_Functions()
|
||||
{
|
||||
// Arrange
|
||||
Func<int, int, int, int> add3 = (x, y, z) => x + y + z;
|
||||
|
||||
// Act
|
||||
var curriedAdd = add3.Curry();
|
||||
var result = curriedAdd(1)(2)(3);
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.EqualTo(6));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Curry_WithNullFunction_Should_Throw_ArgumentNullException()
|
||||
{
|
||||
// Arrange
|
||||
Func<int, int, int> func = null!;
|
||||
|
||||
// Act & Assert
|
||||
Assert.Throws<ArgumentNullException>(() => func.Curry());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Uncurry_Should_Restore_Multi_Parameter_Function()
|
||||
{
|
||||
// Arrange
|
||||
Func<int, Func<int, int>> curriedAdd = x => y => x + y;
|
||||
|
||||
// Act
|
||||
var add = curriedAdd.Uncurry();
|
||||
var result = add(5, 3);
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.EqualTo(8));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Uncurry_WithNullFunction_Should_Throw_ArgumentNullException()
|
||||
{
|
||||
// Arrange
|
||||
Func<int, Func<int, int>> func = null!;
|
||||
|
||||
// Act & Assert
|
||||
Assert.Throws<ArgumentNullException>(() => func.Uncurry());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Curry_Then_Uncurry_Should_Be_Identity()
|
||||
{
|
||||
// Arrange
|
||||
Func<int, int, int> original = (x, y) => x * y;
|
||||
|
||||
// Act
|
||||
var restored = original.Curry().Uncurry();
|
||||
var result = restored(6, 7);
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.EqualTo(42));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Defer_Should_Not_Execute_Immediately()
|
||||
{
|
||||
// Arrange
|
||||
var executed = false;
|
||||
Func<int> func = () =>
|
||||
{
|
||||
executed = true;
|
||||
return 42;
|
||||
};
|
||||
|
||||
// Act
|
||||
var lazy = func.Defer();
|
||||
|
||||
// Assert
|
||||
Assert.That(executed, Is.False);
|
||||
Assert.That(lazy.Value, Is.EqualTo(42));
|
||||
Assert.That(executed, Is.True);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Defer_WithNullFunction_Should_Throw_ArgumentNullException()
|
||||
{
|
||||
// Arrange
|
||||
Func<int> func = null!;
|
||||
|
||||
// Act & Assert
|
||||
Assert.Throws<ArgumentNullException>(() => func.Defer());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Once_Should_Execute_Function_Only_Once()
|
||||
{
|
||||
// Arrange
|
||||
var counter = 0;
|
||||
Func<int> func = () => ++counter;
|
||||
|
||||
// Act
|
||||
var once = func.Once();
|
||||
var result1 = once();
|
||||
var result2 = once();
|
||||
var result3 = once();
|
||||
|
||||
// Assert
|
||||
Assert.That(result1, Is.EqualTo(1));
|
||||
Assert.That(result2, Is.EqualTo(1));
|
||||
Assert.That(result3, Is.EqualTo(1));
|
||||
Assert.That(counter, Is.EqualTo(1));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Once_WithNullFunction_Should_Throw_ArgumentNullException()
|
||||
{
|
||||
// Arrange
|
||||
Func<int> func = null!;
|
||||
|
||||
// Act & Assert
|
||||
Assert.Throws<ArgumentNullException>(() => func.Once());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Once_Should_Be_Thread_Safe()
|
||||
{
|
||||
// Arrange
|
||||
var counter = 0;
|
||||
Func<int> func = () => Interlocked.Increment(ref counter);
|
||||
|
||||
var once = func.Once();
|
||||
|
||||
// Act
|
||||
var tasks = Enumerable.Range(0, 10)
|
||||
.Select(_ => Task.Run(() => once()))
|
||||
.ToArray();
|
||||
|
||||
Task.WaitAll(tasks.Cast<Task>().ToArray());
|
||||
|
||||
// Assert
|
||||
Assert.That(counter, Is.EqualTo(1));
|
||||
Assert.That(tasks.Select(t => t.Result).Distinct().Count(), Is.EqualTo(1));
|
||||
}
|
||||
}
|
||||
@ -27,4 +27,212 @@ public class PipeExtensionsTests
|
||||
Assert.That(result, Is.EqualTo(42));
|
||||
Assert.That(capturedValue, Is.EqualTo(42));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Tap_Should_Execute_Action_And_Return_Original_Value()
|
||||
{
|
||||
// Arrange
|
||||
var value = 42;
|
||||
var capturedValue = 0;
|
||||
|
||||
// Act
|
||||
var result = value.Tap(x => capturedValue = x);
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.EqualTo(42));
|
||||
Assert.That(capturedValue, Is.EqualTo(42));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Tap_WithNullAction_Should_Throw_ArgumentNullException()
|
||||
{
|
||||
// Arrange
|
||||
var value = 42;
|
||||
|
||||
// Act & Assert
|
||||
Assert.Throws<ArgumentNullException>(() => value.Tap(null!));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Tap_Should_Allow_Chaining()
|
||||
{
|
||||
// Arrange
|
||||
var value = 10;
|
||||
var log = new List<string>();
|
||||
|
||||
// Act
|
||||
var result = value
|
||||
.Tap(x => log.Add($"Step 1: {x}"))
|
||||
.Tap(x => log.Add($"Step 2: {x}"));
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.EqualTo(10));
|
||||
Assert.That(log, Has.Count.EqualTo(2));
|
||||
Assert.That(log[0], Is.EqualTo("Step 1: 10"));
|
||||
Assert.That(log[1], Is.EqualTo("Step 2: 10"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Pipe_Should_Transform_Value()
|
||||
{
|
||||
// Arrange
|
||||
var value = 42;
|
||||
|
||||
// Act
|
||||
var result = value.Pipe(x => x * 2);
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.EqualTo(84));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Pipe_WithNullFunction_Should_Throw_ArgumentNullException()
|
||||
{
|
||||
// Arrange
|
||||
var value = 42;
|
||||
|
||||
// Act & Assert
|
||||
Assert.Throws<ArgumentNullException>(() => value.Pipe<int, int>(null!));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Pipe_Should_Allow_Chaining()
|
||||
{
|
||||
// Arrange
|
||||
var value = 5;
|
||||
|
||||
// Act
|
||||
var result = value
|
||||
.Pipe(x => x * 2)
|
||||
.Pipe(x => x + 10)
|
||||
.Pipe(x => x.ToString());
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.EqualTo("20"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Let_Should_Transform_Value()
|
||||
{
|
||||
// Arrange
|
||||
var value = 42;
|
||||
|
||||
// Act
|
||||
var result = value.Let(x => x.ToString());
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.EqualTo("42"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Let_WithNullTransform_Should_Throw_ArgumentNullException()
|
||||
{
|
||||
// Arrange
|
||||
var value = 42;
|
||||
|
||||
// Act & Assert
|
||||
Assert.Throws<ArgumentNullException>(() => value.Let<int, string>(null!));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Let_Should_Allow_Complex_Transformations()
|
||||
{
|
||||
// Arrange
|
||||
var value = "hello";
|
||||
|
||||
// Act
|
||||
var result = value.Let(s => new
|
||||
{
|
||||
Original = s,
|
||||
Upper = s.ToUpper(),
|
||||
Length = s.Length
|
||||
});
|
||||
|
||||
// Assert
|
||||
Assert.That(result.Original, Is.EqualTo("hello"));
|
||||
Assert.That(result.Upper, Is.EqualTo("HELLO"));
|
||||
Assert.That(result.Length, Is.EqualTo(5));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PipeIf_WithTruePredicate_Should_Apply_IfTrue_Function()
|
||||
{
|
||||
// Arrange
|
||||
var value = 42;
|
||||
|
||||
// Act
|
||||
var result = value.PipeIf(
|
||||
x => x > 0,
|
||||
x => $"Positive: {x}",
|
||||
x => $"Non-positive: {x}"
|
||||
);
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.EqualTo("Positive: 42"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PipeIf_WithFalsePredicate_Should_Apply_IfFalse_Function()
|
||||
{
|
||||
// Arrange
|
||||
var value = -5;
|
||||
|
||||
// Act
|
||||
var result = value.PipeIf(
|
||||
x => x > 0,
|
||||
x => $"Positive: {x}",
|
||||
x => $"Non-positive: {x}"
|
||||
);
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.EqualTo("Non-positive: -5"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PipeIf_WithNullPredicate_Should_Throw_ArgumentNullException()
|
||||
{
|
||||
// Arrange
|
||||
var value = 42;
|
||||
|
||||
// Act & Assert
|
||||
Assert.Throws<ArgumentNullException>(() =>
|
||||
value.PipeIf<int, string>(null!, x => "", x => ""));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PipeIf_WithNullIfTrue_Should_Throw_ArgumentNullException()
|
||||
{
|
||||
// Arrange
|
||||
var value = 42;
|
||||
|
||||
// Act & Assert
|
||||
Assert.Throws<ArgumentNullException>(() =>
|
||||
value.PipeIf(x => true, null!, x => ""));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PipeIf_WithNullIfFalse_Should_Throw_ArgumentNullException()
|
||||
{
|
||||
// Arrange
|
||||
var value = 42;
|
||||
|
||||
// Act & Assert
|
||||
Assert.Throws<ArgumentNullException>(() =>
|
||||
value.PipeIf(x => true, x => "", null!));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PipeIf_Should_Allow_Chaining()
|
||||
{
|
||||
// Arrange
|
||||
var value = 10;
|
||||
|
||||
// Act
|
||||
var result = value
|
||||
.PipeIf(x => x > 5, x => x * 2, x => x + 10)
|
||||
.PipeIf(x => x > 15, x => $"Large: {x}", x => $"Small: {x}");
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.EqualTo("Large: 20"));
|
||||
}
|
||||
}
|
||||
@ -57,10 +57,10 @@ public static class ControlExtensions
|
||||
/// <returns>条件为真时返回原值,否则返回 null</returns>
|
||||
/// <exception cref="ArgumentNullException">当 predicate 为 null 时抛出</exception>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// <code><![CDATA[
|
||||
/// var result = 42.TakeIfValue(x => x > 0); // 42
|
||||
/// var result2 = -5.TakeIfValue(x => x > 0); // null
|
||||
/// </code>
|
||||
/// ]]></code>
|
||||
/// </example>
|
||||
public static TSource? TakeIfValue<TSource>(
|
||||
this TSource value,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user