From 2a0ec35190b90ba2aea3ce44c85d71587a40468d Mon Sep 17 00:00:00 2001 From: Manfred Brands Date: Sat, 2 Nov 2024 18:04:14 +0800 Subject: [PATCH] Add support for Generic Nullable parameters (#4872) * Add support for Generic Nullable parameters * Tests `null` can be resolved if matching non-null value --- .../framework/Internal/GenericMethodHelper.cs | 4 ++ .../framework/Internal/Reflect.cs | 2 +- .../Attributes/TestCaseAttributeTests.cs | 42 +++++++++++++++++++ .../tests/Attributes/TestCaseSourceTests.cs | 18 ++++++++ 4 files changed, 65 insertions(+), 1 deletion(-) diff --git a/src/NUnitFramework/framework/Internal/GenericMethodHelper.cs b/src/NUnitFramework/framework/Internal/GenericMethodHelper.cs index 06087330e2..e468c25acb 100644 --- a/src/NUnitFramework/framework/Internal/GenericMethodHelper.cs +++ b/src/NUnitFramework/framework/Internal/GenericMethodHelper.cs @@ -108,6 +108,10 @@ private void TryApplyArgType(Type parmType, Type argType) TryApplyArgType(genericArgTypes[i], argTypes[i]); } } + else if (Reflect.IsNullable(parmType) && !argType.IsClass) + { + ApplyArgType(genericArgTypes[0], argType); + } } } diff --git a/src/NUnitFramework/framework/Internal/Reflect.cs b/src/NUnitFramework/framework/Internal/Reflect.cs index 0df19d477a..5976da875a 100644 --- a/src/NUnitFramework/framework/Internal/Reflect.cs +++ b/src/NUnitFramework/framework/Internal/Reflect.cs @@ -336,7 +336,7 @@ internal static bool IsAssignableFromNull(Type type) return !type.IsValueType || IsNullable(type); } - private static bool IsNullable(Type type) + internal static bool IsNullable(Type type) { // Compare with https://github.com/dotnet/coreclr/blob/bb01fb0d954c957a36f3f8c7aad19657afc2ceda/src/mscorlib/src/System/Nullable.cs#L152-L157 return type.IsGenericType diff --git a/src/NUnitFramework/tests/Attributes/TestCaseAttributeTests.cs b/src/NUnitFramework/tests/Attributes/TestCaseAttributeTests.cs index ea92ac27f6..4381ef1e21 100644 --- a/src/NUnitFramework/tests/Attributes/TestCaseAttributeTests.cs +++ b/src/NUnitFramework/tests/Attributes/TestCaseAttributeTests.cs @@ -875,5 +875,47 @@ public void ExplicitTypeArgsWithGenericConstraintSatisfied(int input) Assert.That(convertedValue, Is.TypeOf()); Assert.That(convertedValue, Is.Not.TypeOf(input.GetType())); } + + [TestCase(0, TypeArgs = [typeof(int)])] +#if NET6_0_OR_GREATER + [TestCase(0)] +#endif + [TestCase(0)] + [TestCase(1L)] + [TestCase(2UL)] + [TestCase(3F)] + [TestCase(4D)] + public void GenericNullable(TValue? value) + where TValue : struct, IConvertible + { + Assert.That(value, Is.Not.Null); + int index = value.Value.ToInt32(null); +#pragma warning disable NUnit2021 // Incompatible types for EqualTo constraint + Assert.That(value.Value, Is.EqualTo(index)); +#pragma warning restore NUnit2021 // Incompatible types for EqualTo constraint + Assert.That(value.Value, Is.InstanceOf(ExpectedType[index])); + } + + private static readonly Type[] ExpectedType = [typeof(int), typeof(long), typeof(ulong), typeof(float), typeof(double)]; + + [TestCase("Hello", null)] + [TestCase(null, "World")] + [TestCase("Hello", "World")] + public void GenericNullableClass(TValue? greeting, TValue? to) + where TValue : class + { + Assert.That(greeting, Is.Null.Or.Not.Null); + Assert.That(to, Is.Not.Null.Or.Null); + } + + [TestCase(3, null)] + [TestCase(null, 4.0)] + [TestCase(3, 4)] + public void GenericNullableStruct(TValue? greeting, TValue? to) + where TValue : struct + { + Assert.That(greeting, Is.Null.Or.Not.Null); + Assert.That(to, Is.Not.Null.Or.Null); + } } } diff --git a/src/NUnitFramework/tests/Attributes/TestCaseSourceTests.cs b/src/NUnitFramework/tests/Attributes/TestCaseSourceTests.cs index 0067197922..ebc41cf1b9 100644 --- a/src/NUnitFramework/tests/Attributes/TestCaseSourceTests.cs +++ b/src/NUnitFramework/tests/Attributes/TestCaseSourceTests.cs @@ -756,6 +756,24 @@ private static async IAsyncEnumerable AsyncEnumerableTestCases() await Task.Delay(100); // Simulate another asynchronous operation yield return new TestCaseData(51, 51); } + + #region Generic Nullable + + [TestCaseSource(nameof(GenericNullableSource))] + public void GenericNullableTest(TValue? value) + where TValue : struct, IConvertible + { + Assert.That(value, Is.Not.Null); + int index = value.Value.ToInt32(null); +#pragma warning disable NUnit2021 // Incompatible types for EqualTo constraint + Assert.That(value.Value, Is.EqualTo(index)); +#pragma warning restore NUnit2021 // Incompatible types for EqualTo constraint + Assert.That(value.Value, Is.InstanceOf(GenericNullableSource[index].GetType())); + } + + private static readonly object[] GenericNullableSource = [0, 1L, 2UL, 3F, 4D]; + + #endregion } public class TestSourceMayBeInherited