diff --git a/src/TUnit.Assertions/Conditions/TypeAssertionExtensions.cs b/src/TUnit.Assertions/Conditions/TypeAssertionExtensions.cs index f81a4aadb04..2c9594a9a7b 100644 --- a/src/TUnit.Assertions/Conditions/TypeAssertionExtensions.cs +++ b/src/TUnit.Assertions/Conditions/TypeAssertionExtensions.cs @@ -1,4 +1,5 @@ using TUnit.Assertions.Attributes; +using TUnit.Assertions.Core; namespace TUnit.Assertions.Conditions; @@ -83,4 +84,21 @@ namespace TUnit.Assertions.Conditions; [AssertionFrom(nameof(Type.IsCOMObject), CustomName = "IsNotCOMObject", NegateLogic = true, ExpectationMessage = "be a COM object")] public static partial class TypeAssertionExtensions { + [GenerateAssertion(ExpectationMessage = "be assignable to {expectedType}", InlineMethodBody = true)] + public static AssertionResult IsAssignableTo(this Type value, Type expectedType) + => expectedType switch + { + null => AssertionResult.Failed("expected type was null"), + _ when expectedType.IsAssignableFrom(value) => AssertionResult.Passed, + _ => AssertionResult.Failed($"type {value.Name} is not assignable to {expectedType.Name}"), + }; + + [GenerateAssertion(ExpectationMessage = "be assignable from {sourceType}", InlineMethodBody = true)] + public static AssertionResult IsAssignableFrom(this Type value, Type sourceType) + => sourceType switch + { + null => AssertionResult.Failed("source type was null"), + _ when value.IsAssignableFrom(sourceType) => AssertionResult.Passed, + _ => AssertionResult.Failed($"type {value.Name} is not assignable from {sourceType.Name}"), + }; } diff --git a/src/TUnit.Assertions/Conditions/TypeOfAssertion.cs b/src/TUnit.Assertions/Conditions/TypeOfAssertion.cs index 31fba25b9e5..a644f935b35 100644 --- a/src/TUnit.Assertions/Conditions/TypeOfAssertion.cs +++ b/src/TUnit.Assertions/Conditions/TypeOfAssertion.cs @@ -147,18 +147,61 @@ protected override async Task CheckAsync(EvaluationMetadata $"to be assignable to {_targetType.Name}"; } +/// +/// Asserts that a represented is assignable to a target type while +/// retaining the represented type as the assertion value. +/// +public sealed class TypeIsAssignableToAssertion : Assertion +{ + private readonly Type _targetType = typeof(TTarget); + + public TypeIsAssignableToAssertion(AssertionContext context) + : base(context) + { + } + + protected override Task CheckAsync(EvaluationMetadata metadata) + { + if (metadata.Exception is { } exception) + { + return Task.FromResult(AssertionResult.Failed($"threw {exception.GetType().Name}", exception)); + } + + if (metadata.Value is not { } representedType) + { + return Task.FromResult(AssertionResult.Failed("value was null")); + } + + return _targetType.IsAssignableFrom(representedType) + ? AssertionResult._passedTask + : Task.FromResult(AssertionResult.Failed( + $"type {representedType.Name} is not assignable to {_targetType.Name}")); + } + + protected override string GetExpectation() => $"to be assignable to {_targetType.Name}"; +} + /// /// Asserts that a value's type is NOT assignable to a specific type. /// Works with both direct value assertions and exception assertions (via .And after Throws). /// public class IsNotAssignableToAssertion : Assertion { + private readonly bool _useRepresentedType; private readonly Type _targetType; public IsNotAssignableToAssertion( AssertionContext context) + : this(context, useRepresentedType: false) + { + } + + internal IsNotAssignableToAssertion( + AssertionContext context, + bool useRepresentedType) : base(context) { + _useRepresentedType = useRepresentedType; _targetType = typeof(TTarget); } @@ -184,7 +227,9 @@ protected override Task CheckAsync(EvaluationMetadata m return Task.FromResult(AssertionResult.Failed("value was null")); } - var actualType = objectToCheck.GetType(); + var actualType = _useRepresentedType && objectToCheck is Type representedType + ? representedType + : objectToCheck.GetType(); if (!_targetType.IsAssignableFrom(actualType)) { @@ -204,12 +249,21 @@ protected override Task CheckAsync(EvaluationMetadata m /// public class IsAssignableFromAssertion : Assertion { + private readonly bool _useRepresentedType; private readonly Type _sourceType; public IsAssignableFromAssertion( AssertionContext context) + : this(context, useRepresentedType: false) + { + } + + internal IsAssignableFromAssertion( + AssertionContext context, + bool useRepresentedType) : base(context) { + _useRepresentedType = useRepresentedType; _sourceType = typeof(TSource); } @@ -233,7 +287,9 @@ protected override Task CheckAsync(EvaluationMetadata m return Task.FromResult(AssertionResult.Failed("value was null")); } - var actualType = objectToCheck.GetType(); + var actualType = _useRepresentedType && objectToCheck is Type representedType + ? representedType + : objectToCheck.GetType(); if (actualType.IsAssignableFrom(_sourceType)) { @@ -252,12 +308,21 @@ protected override Task CheckAsync(EvaluationMetadata m /// public class IsNotAssignableFromAssertion : Assertion { + private readonly bool _useRepresentedType; private readonly Type _sourceType; public IsNotAssignableFromAssertion( AssertionContext context) + : this(context, useRepresentedType: false) + { + } + + internal IsNotAssignableFromAssertion( + AssertionContext context, + bool useRepresentedType) : base(context) { + _useRepresentedType = useRepresentedType; _sourceType = typeof(TSource); } @@ -281,7 +346,9 @@ protected override Task CheckAsync(EvaluationMetadata m return Task.FromResult(AssertionResult.Failed("value was null")); } - var actualType = objectToCheck.GetType(); + var actualType = _useRepresentedType && objectToCheck is Type representedType + ? representedType + : objectToCheck.GetType(); if (!actualType.IsAssignableFrom(_sourceType)) { diff --git a/src/TUnit.Assertions/Extensions/Assert.cs b/src/TUnit.Assertions/Extensions/Assert.cs index 0c19e1bfa72..a4eeabe6156 100644 --- a/src/TUnit.Assertions/Extensions/Assert.cs +++ b/src/TUnit.Assertions/Extensions/Assert.cs @@ -254,6 +254,17 @@ public static HashSetAssertion That( return new CollectionAssertion(value.Cast(), expression); } + /// + /// Creates an assertion for a represented . + /// + [OverloadResolutionPriority(1)] + public static TypeValueAssertion That( + Type? value, + [CallerArgumentExpression(nameof(value))] string? expression = null) + { + return new TypeValueAssertion(value, expression); + } + /// /// Creates an assertion for an immediate value. /// Example: await Assert.That(42).IsEqualTo(42); diff --git a/src/TUnit.Assertions/Sources/TypeValueAssertion.cs b/src/TUnit.Assertions/Sources/TypeValueAssertion.cs new file mode 100644 index 00000000000..d6536a1908a --- /dev/null +++ b/src/TUnit.Assertions/Sources/TypeValueAssertion.cs @@ -0,0 +1,68 @@ +using TUnit.Assertions.Conditions; +using TUnit.Assertions.Core; + +namespace TUnit.Assertions.Sources; + +/// +/// Source assertion for represented types. +/// +public sealed class TypeValueAssertion : ValueAssertion +{ + public TypeValueAssertion(Type? value, string? expression) + : base(value, expression) + { + } + + /// + /// Asserts that the represented type is assignable to . + /// The assertion retains the represented for awaiting and chaining. + /// + /// + /// Represented-type semantics apply only when this is the first assertion after Assert.That(type). + /// After .And or .Or, assignability assertions inspect the runtime type instead. + /// + public new TypeIsAssignableToAssertion IsAssignableTo() + { + Context.ExpressionBuilder.Append($".IsAssignableTo<{typeof(TTarget).Name}>()"); + return new TypeIsAssignableToAssertion(Context); + } + + /// + /// Asserts that the represented type is not assignable to . + /// + /// + /// Represented-type semantics apply only when this is the first assertion after Assert.That(type). + /// After .And or .Or, assignability assertions inspect the runtime type instead. + /// + public new IsNotAssignableToAssertion IsNotAssignableTo() + { + Context.ExpressionBuilder.Append($".IsNotAssignableTo<{typeof(TTarget).Name}>()"); + return new IsNotAssignableToAssertion(Context, useRepresentedType: true); + } + + /// + /// Asserts that is assignable to the represented type. + /// + /// + /// Represented-type semantics apply only when this is the first assertion after Assert.That(type). + /// After .And or .Or, assignability assertions inspect the runtime type instead. + /// + public new IsAssignableFromAssertion IsAssignableFrom() + { + Context.ExpressionBuilder.Append($".IsAssignableFrom<{typeof(TSource).Name}>()"); + return new IsAssignableFromAssertion(Context, useRepresentedType: true); + } + + /// + /// Asserts that is not assignable to the represented type. + /// + /// + /// Represented-type semantics apply only when this is the first assertion after Assert.That(type). + /// After .And or .Or, assignability assertions inspect the runtime type instead. + /// + public new IsNotAssignableFromAssertion IsNotAssignableFrom() + { + Context.ExpressionBuilder.Append($".IsNotAssignableFrom<{typeof(TSource).Name}>()"); + return new IsNotAssignableFromAssertion(Context, useRepresentedType: true); + } +} diff --git a/tests/TUnit.Assertions.Tests/TypeAssertionTests.cs b/tests/TUnit.Assertions.Tests/TypeAssertionTests.cs index 6d8cb5107a4..81f3698939e 100644 --- a/tests/TUnit.Assertions.Tests/TypeAssertionTests.cs +++ b/tests/TUnit.Assertions.Tests/TypeAssertionTests.cs @@ -1,10 +1,15 @@ +using System.Reflection; +using TUnit.Assertions.Core; using TUnit.Assertions.Extensions; -using TUnit.Assertions.Extensions; +using TUnit.Assertions.Sources; namespace TUnit.Assertions.Tests; public class TypeAssertionTests { + private class Animal { } + private class Dog : Animal { } + // Test types for various scenarios private class TestClass { } private interface ITestInterface { } @@ -459,6 +464,109 @@ public async Task Test_Type_IsNotCOMObject_TestClass() await Assert.That(type).IsNotCOMObject(); } + [Test] + public async Task Test_Type_IsAssignableTo_Generic_UsesRepresentedType() + { + Type? result = await Assert.That(typeof(Dog)).IsAssignableTo(); + + await Assert.That(result).IsSameReferenceAs(typeof(Dog)); + } + + [Test] + public async Task Test_Type_IsAssignableTo_Generic_RetainsTypeForChaining() + { + await Assert.That(typeof(Dog)) + .IsAssignableTo() + .And.IsClass(); + } + + [Test] + public async Task Test_TypeInfo_GenericAssignability_UsesRepresentedType() + { + System.Reflection.TypeInfo animalType = typeof(Animal).GetTypeInfo(); + System.Reflection.TypeInfo dogType = typeof(Dog).GetTypeInfo(); + + await Assert.That(dogType).IsAssignableTo(); + await Assert.That(dogType).IsNotAssignableTo(); + await Assert.That(animalType).IsAssignableFrom(); + await Assert.That(dogType).IsNotAssignableFrom(); + } + + [Test] + public async Task Test_Type_IsAssignableTo_GenericSource_RetainsRuntimeTypeSemantics() + { + IAssertionSource source = new TypeValueAssertion(typeof(Dog), null); + + await Assert.That(async () => await source.IsAssignableTo()) + .Throws(); + } + + [Test] + public async Task Test_Type_IsAssignableTo_AfterAnd_RetainsRuntimeTypeSemantics() + { + var action = async () => await Assert.That(typeof(Dog)) + .IsClass() + .And.IsAssignableTo(); + + await Assert.That(action).Throws(); + } + + [Test] + public async Task Test_Type_OtherGenericSources_RetainRuntimeTypeSemantics() + { + IAssertionSource source = new TypeValueAssertion(typeof(Animal), null); + + await source.IsNotAssignableTo(); + await Assert.That(async () => await source.IsAssignableFrom()) + .Throws(); + await source.IsNotAssignableFrom(); + } + + [Test] + public async Task Test_Type_DirectGenericAssertions_UseRepresentedType() + { + await Assert.That(typeof(Animal)).IsNotAssignableTo(); + await Assert.That(typeof(Animal)).IsAssignableFrom(); + await Assert.That(async () => await Assert.That(typeof(Animal)).IsNotAssignableFrom()) + .Throws(); + } + + [Test] + public async Task Test_Type_IsAssignableFrom_Generic_UsesRepresentedType() + { + await Assert.That(typeof(Animal)).IsAssignableFrom(); + } + + [Test] + public async Task Test_Type_IsAssignableTo_RuntimeTypeOverload_Passes() + { + await Assert.That(typeof(Dog)).IsAssignableTo(typeof(Animal)); + } + + [Test] + public async Task Test_Type_IsAssignableFrom_RuntimeTypeOverload_Passes() + { + await Assert.That(typeof(Animal)).IsAssignableFrom(typeof(Dog)); + } + + [Test] + public async Task Test_Type_IsAssignableTo_NullRuntimeType_FailsAssertion() + { + var action = async () => await Assert.That(typeof(Dog)).IsAssignableTo(null!); + + var exception = await Assert.That(action).Throws(); + await Assert.That(exception.Message).Contains("expected type was null"); + } + + [Test] + public async Task Test_Type_IsAssignableFrom_NullRuntimeType_FailsAssertion() + { + var action = async () => await Assert.That(typeof(Animal)).IsAssignableFrom(null!); + + var exception = await Assert.That(action).Throws(); + await Assert.That(exception.Message).Contains("source type was null"); + } + #if NET5_0_OR_GREATER // IsByRefLike / IsNotByRefLike (NET5+) [Test] diff --git a/tests/TUnit.PublicAPI/Tests.Assertions_Library_Has_No_API_Changes.DotNet10_0.verified.txt b/tests/TUnit.PublicAPI/Tests.Assertions_Library_Has_No_API_Changes.DotNet10_0.verified.txt index aaef3f7c8ea..a3bc5816772 100644 --- a/tests/TUnit.PublicAPI/Tests.Assertions_Library_Has_No_API_Changes.DotNet10_0.verified.txt +++ b/tests/TUnit.PublicAPI/Tests.Assertions_Library_Has_No_API_Changes.DotNet10_0.verified.txt @@ -188,6 +188,8 @@ namespace public static . That(.IEnumerable value, [.("value")] string? expression = null) { } public static . That(<.> action, [.("action")] string? expression = null) { } public static . That(. task, [.("task")] string? expression = null) { } + [.(1)] + public static . That(? value, [.("value")] string? expression = null) { } [.(2)] public static . That(.StringValue value, [.("value")] string? expression = null) { } [.(3)] @@ -2287,7 +2289,19 @@ namespace .Conditions [.<>("IsValueType", ExpectationMessage="be a value type")] [.<>("IsVisible", CustomName="IsNotVisible", ExpectationMessage="be visible", NegateLogic=true)] [.<>("IsVisible", ExpectationMessage="be visible")] - public static class TypeAssertionExtensions { } + public static class TypeAssertionExtensions + { + [.(ExpectationMessage="be assignable from {sourceType}", InlineMethodBody=true)] + public static . IsAssignableFrom(this value, sourceType) { } + [.(ExpectationMessage="be assignable to {expectedType}", InlineMethodBody=true)] + public static . IsAssignableTo(this value, expectedType) { } + } + public sealed class TypeIsAssignableToAssertion : .<> + { + public TypeIsAssignableToAssertion(.<> context) { } + protected override .<.> CheckAsync(.<> metadata) { } + protected override string GetExpectation() { } + } public class TypeOfAssertion : . { public TypeOfAssertion(. parentContext) { } @@ -6692,6 +6706,12 @@ namespace .Extensions public static . DoesNotContainGenericParameters(this .<> source) { } public static . IsAbstract(this .<> source) { } public static . IsArray(this .<> source) { } + [.("Trimming", "IL2091", Justification="Generic type parameter is only used for property access, not instantiation")] + public static ._IsAssignableFrom_Type_Assertion IsAssignableFrom(this . source, sourceType, [.("sourceType")] string? sourceTypeExpression = null) + where TActual : { } + [.("Trimming", "IL2091", Justification="Generic type parameter is only used for property access, not instantiation")] + public static ._IsAssignableTo_Type_Assertion IsAssignableTo(this . source, expectedType, [.("expectedType")] string? expectedTypeExpression = null) + where TActual : { } public static . IsByRef(this .<> source) { } public static . IsByRefLike(this .<> source) { } public static . IsCOMObject(this .<> source) { } @@ -6873,6 +6893,18 @@ namespace .Extensions protected override .<.> CheckAsync(.<> metadata) { } protected override string GetExpectation() { } } + public sealed class Type_IsAssignableFrom_Type_Assertion : .<> + { + public Type_IsAssignableFrom_Type_Assertion(.<> context, sourceType) { } + protected override .<.> CheckAsync(.<> metadata) { } + protected override string GetExpectation() { } + } + public sealed class Type_IsAssignableTo_Type_Assertion : .<> + { + public Type_IsAssignableTo_Type_Assertion(.<> context, expectedType) { } + protected override .<.> CheckAsync(.<> metadata) { } + protected override string GetExpectation() { } + } public sealed class UInt16_IsEven_Assertion : . { public UInt16_IsEven_Assertion(. context) { } @@ -7526,6 +7558,14 @@ namespace .Sources public . ThrowsExactly() where TException : { } } + public sealed class TypeValueAssertion : .<> + { + public TypeValueAssertion(? value, string? expression) { } + public new . IsAssignableFrom() { } + public new . IsAssignableTo() { } + public new . IsNotAssignableFrom() { } + public new . IsNotAssignableTo() { } + } public class ValueAssertion : ., . { protected ValueAssertion(. context) { } diff --git a/tests/TUnit.PublicAPI/Tests.Assertions_Library_Has_No_API_Changes.DotNet8_0.verified.txt b/tests/TUnit.PublicAPI/Tests.Assertions_Library_Has_No_API_Changes.DotNet8_0.verified.txt index 6320c1887c2..f088c0c775e 100644 --- a/tests/TUnit.PublicAPI/Tests.Assertions_Library_Has_No_API_Changes.DotNet8_0.verified.txt +++ b/tests/TUnit.PublicAPI/Tests.Assertions_Library_Has_No_API_Changes.DotNet8_0.verified.txt @@ -188,6 +188,7 @@ namespace public static . That(.IEnumerable value, [.("value")] string? expression = null) { } public static . That(<.> action, [.("action")] string? expression = null) { } public static . That(. task, [.("task")] string? expression = null) { } + public static . That(? value, [.("value")] string? expression = null) { } public static . That(.StringValue value, [.("value")] string? expression = null) { } public static . That(.? value, [.("value")] string? expression = null) { } public static . That(. value, [.("value")] string? expression = null) { } @@ -2270,7 +2271,19 @@ namespace .Conditions [.<>("IsValueType", ExpectationMessage="be a value type")] [.<>("IsVisible", CustomName="IsNotVisible", ExpectationMessage="be visible", NegateLogic=true)] [.<>("IsVisible", ExpectationMessage="be visible")] - public static class TypeAssertionExtensions { } + public static class TypeAssertionExtensions + { + [.(ExpectationMessage="be assignable from {sourceType}", InlineMethodBody=true)] + public static . IsAssignableFrom(this value, sourceType) { } + [.(ExpectationMessage="be assignable to {expectedType}", InlineMethodBody=true)] + public static . IsAssignableTo(this value, expectedType) { } + } + public sealed class TypeIsAssignableToAssertion : .<> + { + public TypeIsAssignableToAssertion(.<> context) { } + protected override .<.> CheckAsync(.<> metadata) { } + protected override string GetExpectation() { } + } public class TypeOfAssertion : . { public TypeOfAssertion(. parentContext) { } @@ -6599,6 +6612,12 @@ namespace .Extensions public static . DoesNotContainGenericParameters(this .<> source) { } public static . IsAbstract(this .<> source) { } public static . IsArray(this .<> source) { } + [.("Trimming", "IL2091", Justification="Generic type parameter is only used for property access, not instantiation")] + public static ._IsAssignableFrom_Type_Assertion IsAssignableFrom(this . source, sourceType, [.("sourceType")] string? sourceTypeExpression = null) + where TActual : { } + [.("Trimming", "IL2091", Justification="Generic type parameter is only used for property access, not instantiation")] + public static ._IsAssignableTo_Type_Assertion IsAssignableTo(this . source, expectedType, [.("expectedType")] string? expectedTypeExpression = null) + where TActual : { } public static . IsByRef(this .<> source) { } public static . IsByRefLike(this .<> source) { } public static . IsCOMObject(this .<> source) { } @@ -6780,6 +6799,18 @@ namespace .Extensions protected override .<.> CheckAsync(.<> metadata) { } protected override string GetExpectation() { } } + public sealed class Type_IsAssignableFrom_Type_Assertion : .<> + { + public Type_IsAssignableFrom_Type_Assertion(.<> context, sourceType) { } + protected override .<.> CheckAsync(.<> metadata) { } + protected override string GetExpectation() { } + } + public sealed class Type_IsAssignableTo_Type_Assertion : .<> + { + public Type_IsAssignableTo_Type_Assertion(.<> context, expectedType) { } + protected override .<.> CheckAsync(.<> metadata) { } + protected override string GetExpectation() { } + } public sealed class UInt16_IsEven_Assertion : . { public UInt16_IsEven_Assertion(. context) { } @@ -7432,6 +7463,14 @@ namespace .Sources public . ThrowsExactly() where TException : { } } + public sealed class TypeValueAssertion : .<> + { + public TypeValueAssertion(? value, string? expression) { } + public new . IsAssignableFrom() { } + public new . IsAssignableTo() { } + public new . IsNotAssignableFrom() { } + public new . IsNotAssignableTo() { } + } public class ValueAssertion : ., . { protected ValueAssertion(. context) { } diff --git a/tests/TUnit.PublicAPI/Tests.Assertions_Library_Has_No_API_Changes.DotNet9_0.verified.txt b/tests/TUnit.PublicAPI/Tests.Assertions_Library_Has_No_API_Changes.DotNet9_0.verified.txt index bad033c441a..3af6e98b1a7 100644 --- a/tests/TUnit.PublicAPI/Tests.Assertions_Library_Has_No_API_Changes.DotNet9_0.verified.txt +++ b/tests/TUnit.PublicAPI/Tests.Assertions_Library_Has_No_API_Changes.DotNet9_0.verified.txt @@ -188,6 +188,8 @@ namespace public static . That(.IEnumerable value, [.("value")] string? expression = null) { } public static . That(<.> action, [.("action")] string? expression = null) { } public static . That(. task, [.("task")] string? expression = null) { } + [.(1)] + public static . That(? value, [.("value")] string? expression = null) { } [.(2)] public static . That(.StringValue value, [.("value")] string? expression = null) { } [.(3)] @@ -2287,7 +2289,19 @@ namespace .Conditions [.<>("IsValueType", ExpectationMessage="be a value type")] [.<>("IsVisible", CustomName="IsNotVisible", ExpectationMessage="be visible", NegateLogic=true)] [.<>("IsVisible", ExpectationMessage="be visible")] - public static class TypeAssertionExtensions { } + public static class TypeAssertionExtensions + { + [.(ExpectationMessage="be assignable from {sourceType}", InlineMethodBody=true)] + public static . IsAssignableFrom(this value, sourceType) { } + [.(ExpectationMessage="be assignable to {expectedType}", InlineMethodBody=true)] + public static . IsAssignableTo(this value, expectedType) { } + } + public sealed class TypeIsAssignableToAssertion : .<> + { + public TypeIsAssignableToAssertion(.<> context) { } + protected override .<.> CheckAsync(.<> metadata) { } + protected override string GetExpectation() { } + } public class TypeOfAssertion : . { public TypeOfAssertion(. parentContext) { } @@ -6692,6 +6706,12 @@ namespace .Extensions public static . DoesNotContainGenericParameters(this .<> source) { } public static . IsAbstract(this .<> source) { } public static . IsArray(this .<> source) { } + [.("Trimming", "IL2091", Justification="Generic type parameter is only used for property access, not instantiation")] + public static ._IsAssignableFrom_Type_Assertion IsAssignableFrom(this . source, sourceType, [.("sourceType")] string? sourceTypeExpression = null) + where TActual : { } + [.("Trimming", "IL2091", Justification="Generic type parameter is only used for property access, not instantiation")] + public static ._IsAssignableTo_Type_Assertion IsAssignableTo(this . source, expectedType, [.("expectedType")] string? expectedTypeExpression = null) + where TActual : { } public static . IsByRef(this .<> source) { } public static . IsByRefLike(this .<> source) { } public static . IsCOMObject(this .<> source) { } @@ -6873,6 +6893,18 @@ namespace .Extensions protected override .<.> CheckAsync(.<> metadata) { } protected override string GetExpectation() { } } + public sealed class Type_IsAssignableFrom_Type_Assertion : .<> + { + public Type_IsAssignableFrom_Type_Assertion(.<> context, sourceType) { } + protected override .<.> CheckAsync(.<> metadata) { } + protected override string GetExpectation() { } + } + public sealed class Type_IsAssignableTo_Type_Assertion : .<> + { + public Type_IsAssignableTo_Type_Assertion(.<> context, expectedType) { } + protected override .<.> CheckAsync(.<> metadata) { } + protected override string GetExpectation() { } + } public sealed class UInt16_IsEven_Assertion : . { public UInt16_IsEven_Assertion(. context) { } @@ -7526,6 +7558,14 @@ namespace .Sources public . ThrowsExactly() where TException : { } } + public sealed class TypeValueAssertion : .<> + { + public TypeValueAssertion(? value, string? expression) { } + public new . IsAssignableFrom() { } + public new . IsAssignableTo() { } + public new . IsNotAssignableFrom() { } + public new . IsNotAssignableTo() { } + } public class ValueAssertion : ., . { protected ValueAssertion(. context) { } diff --git a/tests/TUnit.PublicAPI/Tests.Assertions_Library_Has_No_API_Changes.Net4_7.verified.txt b/tests/TUnit.PublicAPI/Tests.Assertions_Library_Has_No_API_Changes.Net4_7.verified.txt index 8e38d658c79..5091dcc607d 100644 --- a/tests/TUnit.PublicAPI/Tests.Assertions_Library_Has_No_API_Changes.Net4_7.verified.txt +++ b/tests/TUnit.PublicAPI/Tests.Assertions_Library_Has_No_API_Changes.Net4_7.verified.txt @@ -151,6 +151,7 @@ namespace public static . That(.IEnumerable value, [.("value")] string? expression = null) { } public static . That(<.> action, [.("action")] string? expression = null) { } public static . That(. task, [.("task")] string? expression = null) { } + public static . That(? value, [.("value")] string? expression = null) { } public static . That(.StringValue value, [.("value")] string? expression = null) { } public static . That(.? value, [.("value")] string? expression = null) { } public static . That(.? value, [.("value")] string? expression = null) { } @@ -2039,7 +2040,19 @@ namespace .Conditions [.<>("IsValueType", ExpectationMessage="be a value type")] [.<>("IsVisible", CustomName="IsNotVisible", ExpectationMessage="be visible", NegateLogic=true)] [.<>("IsVisible", ExpectationMessage="be visible")] - public static class TypeAssertionExtensions { } + public static class TypeAssertionExtensions + { + [.(ExpectationMessage="be assignable from {sourceType}", InlineMethodBody=true)] + public static . IsAssignableFrom(this value, sourceType) { } + [.(ExpectationMessage="be assignable to {expectedType}", InlineMethodBody=true)] + public static . IsAssignableTo(this value, expectedType) { } + } + public sealed class TypeIsAssignableToAssertion : .<> + { + public TypeIsAssignableToAssertion(.<> context) { } + protected override .<.> CheckAsync(.<> metadata) { } + protected override string GetExpectation() { } + } public class TypeOfAssertion : . { public TypeOfAssertion(. parentContext) { } @@ -5722,6 +5735,10 @@ namespace .Extensions public static . DoesNotContainGenericParameters(this .<> source) { } public static . IsAbstract(this .<> source) { } public static . IsArray(this .<> source) { } + public static ._IsAssignableFrom_Type_Assertion IsAssignableFrom(this . source, sourceType, [.("sourceType")] string? sourceTypeExpression = null) + where TActual : { } + public static ._IsAssignableTo_Type_Assertion IsAssignableTo(this . source, expectedType, [.("expectedType")] string? expectedTypeExpression = null) + where TActual : { } public static . IsByRef(this .<> source) { } public static . IsCOMObject(this .<> source) { } public static . IsClass(this .<> source) { } @@ -5903,6 +5920,18 @@ namespace .Extensions protected override .<.> CheckAsync(.<> metadata) { } protected override string GetExpectation() { } } + public sealed class Type_IsAssignableFrom_Type_Assertion : .<> + { + public Type_IsAssignableFrom_Type_Assertion(.<> context, sourceType) { } + protected override .<.> CheckAsync(.<> metadata) { } + protected override string GetExpectation() { } + } + public sealed class Type_IsAssignableTo_Type_Assertion : .<> + { + public Type_IsAssignableTo_Type_Assertion(.<> context, expectedType) { } + protected override .<.> CheckAsync(.<> metadata) { } + protected override string GetExpectation() { } + } public sealed class UInt16_IsEven_Assertion : . { public UInt16_IsEven_Assertion(. context) { } @@ -6486,6 +6515,14 @@ namespace .Sources public . ThrowsExactly() where TException : { } } + public sealed class TypeValueAssertion : .<> + { + public TypeValueAssertion(? value, string? expression) { } + public new . IsAssignableFrom() { } + public new . IsAssignableTo() { } + public new . IsNotAssignableFrom() { } + public new . IsNotAssignableTo() { } + } public class ValueAssertion : ., . { protected ValueAssertion(. context) { }