From 8f95739bb1c1f525bcad5a8edd4e0f3f0bed5ccd Mon Sep 17 00:00:00 2001 From: Pawel Gerr Date: Thu, 19 Sep 2024 07:40:21 +0200 Subject: [PATCH] A union can be a struct or a ref struct. --- README.md | 11 +- .../TextOrNumberRefStruct.cs | 4 + .../DiscriminatedUnions/TextOrNumberStruct.cs | 4 + .../ThinktectureRuntimeExtensionsAnalyzer.cs | 43 +- .../CodeAnalysis/DiagnosticsDescriptors.cs | 4 +- .../DiscriminatedUnions/UnionCodeGenerator.cs | 26 +- .../UnionSourceGenState.cs | 8 +- .../UnionSourceGenerator.cs | 3 +- .../SmartEnums/SmartEnumSourceGenerator.cs | 2 +- .../CodeAnalysis/TypedMemberStateFactory.cs | 8 +- .../Extensions/TypeSymbolExtensions.cs | 18 +- .../UnionAttribute.cs | 2 +- .../TTRESG004_TypeMustBeClassOrStruct.cs | 76 +- .../TTRESG006_TypeMustBePartial.cs | 94 + ...sValueObjectsAndUnionsMustNotBeGeneric.cs} | 361 +-- .../TTRESG043_PrimaryConstructorNotAllowed.cs | 38 + .../TTRESG047_TypeMustBeClass.cs | 50 - ...bleMustBeInitializedWithNonDefaultValue.cs | 63 + .../UnionSourceGeneratorTests.cs | 605 +++++ .../TestUnion_ref_struct_string_int.cs | 6 + .../TestUnions/TestUnion_struct_string_int.cs | 6 + ...tUnion_struct_string_int_case_sensitive.cs | 7 + .../UnionTests/AsValue.cs | 5 + .../UnionTests/EqualityOperator.cs | 4 + .../UnionTests/Equals.cs | 4 + .../UnionTests/ExplicitCasts.cs | 6 + .../UnionTests/GetHashCode.cs | 4 + .../UnionTests/ImplicitCasts.cs | 6 + .../UnionTests/InequalityOperator.cs | 4 + .../UnionTests/IsValue.cs | 5 + .../UnionTests/Map.cs | 172 +- .../UnionTests/MapPartially.cs | 383 +-- .../UnionTests/Switch.cs | 1108 +++++---- .../UnionTests/SwitchPartially.cs | 2060 +++++++++-------- .../UnionTests/ToString.cs | 3 + .../UnionTests/Value.cs | 3 + 36 files changed, 3298 insertions(+), 1908 deletions(-) create mode 100644 samples/Thinktecture.Runtime.Extensions.Samples/DiscriminatedUnions/TextOrNumberRefStruct.cs create mode 100644 samples/Thinktecture.Runtime.Extensions.Samples/DiscriminatedUnions/TextOrNumberStruct.cs rename test/Thinktecture.Runtime.Extensions.SourceGenerator.Tests/AnalyzerAndCodeFixTests/{TTRESG033_EnumsAndValueObjectsMustNotBeGeneric.cs => TTRESG033_EnumsValueObjectsAndUnionsMustNotBeGeneric.cs} (88%) delete mode 100644 test/Thinktecture.Runtime.Extensions.SourceGenerator.Tests/AnalyzerAndCodeFixTests/TTRESG047_TypeMustBeClass.cs create mode 100644 test/Thinktecture.Runtime.Extensions.SourceGenerator.Tests/AnalyzerAndCodeFixTests/TTRESG047_VariableMustBeInitializedWithNonDefaultValue.cs create mode 100644 test/Thinktecture.Runtime.Extensions.Tests.Shared/TestUnions/TestUnion_ref_struct_string_int.cs create mode 100644 test/Thinktecture.Runtime.Extensions.Tests.Shared/TestUnions/TestUnion_struct_string_int.cs create mode 100644 test/Thinktecture.Runtime.Extensions.Tests.Shared/TestUnions/TestUnion_struct_string_int_case_sensitive.cs diff --git a/README.md b/README.md index 3fc50ada..f35c55f9 100644 --- a/README.md +++ b/README.md @@ -474,12 +474,21 @@ Features: * Renaming of properties * Definition of nullable reference types -Definition of a basic union with 2 types: +Definition of a basic union with 2 types using a `class`, a `struct` or `ref struct`: ```csharp +// class [Union] public partial class TextOrNumber; +// struct +[Union] +public partial struct TextOrNumber; + +// ref struct +[Union] +public ref partial struct TextOrNumber; + // Up to 5 types [Union] public partial class MyUnion; diff --git a/samples/Thinktecture.Runtime.Extensions.Samples/DiscriminatedUnions/TextOrNumberRefStruct.cs b/samples/Thinktecture.Runtime.Extensions.Samples/DiscriminatedUnions/TextOrNumberRefStruct.cs new file mode 100644 index 00000000..5d56fb07 --- /dev/null +++ b/samples/Thinktecture.Runtime.Extensions.Samples/DiscriminatedUnions/TextOrNumberRefStruct.cs @@ -0,0 +1,4 @@ +namespace Thinktecture.DiscriminatedUnions; + +[Union] +public ref partial struct TextOrNumberRefStruct; diff --git a/samples/Thinktecture.Runtime.Extensions.Samples/DiscriminatedUnions/TextOrNumberStruct.cs b/samples/Thinktecture.Runtime.Extensions.Samples/DiscriminatedUnions/TextOrNumberStruct.cs new file mode 100644 index 00000000..89f21c59 --- /dev/null +++ b/samples/Thinktecture.Runtime.Extensions.Samples/DiscriminatedUnions/TextOrNumberStruct.cs @@ -0,0 +1,4 @@ +namespace Thinktecture.DiscriminatedUnions; + +[Union] +public partial struct TextOrNumberStruct; diff --git a/src/Thinktecture.Runtime.Extensions.SourceGenerator/CodeAnalysis/Diagnostics/ThinktectureRuntimeExtensionsAnalyzer.cs b/src/Thinktecture.Runtime.Extensions.SourceGenerator/CodeAnalysis/Diagnostics/ThinktectureRuntimeExtensionsAnalyzer.cs index 64c2e605..e433e247 100644 --- a/src/Thinktecture.Runtime.Extensions.SourceGenerator/CodeAnalysis/Diagnostics/ThinktectureRuntimeExtensionsAnalyzer.cs +++ b/src/Thinktecture.Runtime.Extensions.SourceGenerator/CodeAnalysis/Diagnostics/ThinktectureRuntimeExtensionsAnalyzer.cs @@ -30,7 +30,7 @@ public sealed class ThinktectureRuntimeExtensionsAnalyzer : DiagnosticAnalyzer DiagnosticsDescriptors.TypeCannotBeNestedClass, DiagnosticsDescriptors.KeyMemberShouldNotBeNullable, DiagnosticsDescriptors.StaticPropertiesAreNotConsideredItems, - DiagnosticsDescriptors.EnumsAndValueObjectsMustNotBeGeneric, + DiagnosticsDescriptors.EnumsValueObjectsAndUnionsMustNotBeGeneric, DiagnosticsDescriptors.BaseClassFieldMustBeReadOnly, DiagnosticsDescriptors.BaseClassPropertyMustBeReadOnly, DiagnosticsDescriptors.EnumKeyShouldNotBeNullable, @@ -42,7 +42,7 @@ public sealed class ThinktectureRuntimeExtensionsAnalyzer : DiagnosticAnalyzer DiagnosticsDescriptors.CustomKeyMemberImplementationNotFound, DiagnosticsDescriptors.CustomKeyMemberImplementationTypeMismatch, DiagnosticsDescriptors.IndexBasedSwitchAndMapMustUseNamedParameters, - DiagnosticsDescriptors.TypeMustBeClass); + DiagnosticsDescriptors.VariableMustBeInitializedWithNonDefaultValue); /// public override void Initialize(AnalysisContext context) @@ -55,6 +55,37 @@ public override void Initialize(AnalysisContext context) context.RegisterOperationAction(AnalyzeUnion, OperationKind.Attribute); context.RegisterOperationAction(AnalyzeMethodCall, OperationKind.Invocation); + context.RegisterOperationAction(AnalyzeDefaultValueAssignment, OperationKind.DefaultValue); + context.RegisterOperationAction(AnalyzeObjectCreation, OperationKind.ObjectCreation); + } + + private void AnalyzeObjectCreation(OperationAnalysisContext context) + { + var operation = (IObjectCreationOperation)context.Operation; + + if (operation.Type is null) + return; + + if (!operation.Type.IsReferenceType + && operation.Arguments.Length == 0 + && operation.Type.IsUnionType(out _)) + { + ReportDiagnostic(context, DiagnosticsDescriptors.VariableMustBeInitializedWithNonDefaultValue, operation.Syntax.GetLocation(), operation.Type); + } + } + + private void AnalyzeDefaultValueAssignment(OperationAnalysisContext context) + { + var operation = (IDefaultValueOperation)context.Operation; + + if (operation.Type is null) + return; + + if (!operation.Type.IsReferenceType + && operation.Type.IsUnionType(out _)) + { + ReportDiagnostic(context, DiagnosticsDescriptors.VariableMustBeInitializedWithNonDefaultValue, operation.Syntax.GetLocation(), operation.Type); + } } private static void AnalyzeMethodCall(OperationAnalysisContext context) @@ -84,7 +115,7 @@ private static void AnalyzeMethodCall(OperationAnalysisContext context) operation, isValidatable); } - else if (operation.Instance.Type.IsUnionAttribute(out attribute) + else if (operation.Instance.Type.IsUnionType(out attribute) && attribute.AttributeClass is not null) { AnalyzeUnionSwitchMap(context, @@ -244,9 +275,9 @@ private static void ValidateUnion(OperationAnalysisContext context, IObjectCreationOperation attribute, Location locationOfFirstDeclaration) { - if (type.IsRecord || type.TypeKind is not TypeKind.Class) + if (type.IsRecord || type.TypeKind is not (TypeKind.Class or TypeKind.Struct)) { - ReportDiagnostic(context, DiagnosticsDescriptors.TypeMustBeClass, locationOfFirstDeclaration, type); + ReportDiagnostic(context, DiagnosticsDescriptors.TypeMustBeClassOrStruct, locationOfFirstDeclaration, type); return; } @@ -533,7 +564,7 @@ private static void ValidateKeyedSmartEnum( private static void TypeMustNotBeGeneric(OperationAnalysisContext context, INamedTypeSymbol type, Location locationOfFirstDeclaration, string typeKind) { if (!type.TypeParameters.IsDefaultOrEmpty) - ReportDiagnostic(context, DiagnosticsDescriptors.EnumsAndValueObjectsMustNotBeGeneric, locationOfFirstDeclaration, typeKind, BuildTypeName(type)); + ReportDiagnostic(context, DiagnosticsDescriptors.EnumsValueObjectsAndUnionsMustNotBeGeneric, locationOfFirstDeclaration, typeKind, BuildTypeName(type)); } private static void Check_ItemLike_StaticProperties( diff --git a/src/Thinktecture.Runtime.Extensions.SourceGenerator/CodeAnalysis/DiagnosticsDescriptors.cs b/src/Thinktecture.Runtime.Extensions.SourceGenerator/CodeAnalysis/DiagnosticsDescriptors.cs index 29d67335..49c51fbe 100644 --- a/src/Thinktecture.Runtime.Extensions.SourceGenerator/CodeAnalysis/DiagnosticsDescriptors.cs +++ b/src/Thinktecture.Runtime.Extensions.SourceGenerator/CodeAnalysis/DiagnosticsDescriptors.cs @@ -18,7 +18,7 @@ internal static class DiagnosticsDescriptors public static readonly DiagnosticDescriptor InnerEnumOnNonFirstLevelMustBePublic = new("TTRESG015", "Non-first-level inner enumerations must be public", "Derived inner enumeration '{0}' on non-first-level must be public", nameof(ThinktectureRuntimeExtensionsAnalyzer), DiagnosticSeverity.Error, true); public static readonly DiagnosticDescriptor TypeCannotBeNestedClass = new("TTRESG016", "The type cannot be a nested class", "The type '{0}' cannot be a nested class", nameof(ThinktectureRuntimeExtensionsAnalyzer), DiagnosticSeverity.Error, true); public static readonly DiagnosticDescriptor KeyMemberShouldNotBeNullable = new("TTRESG017", "The key member must not be nullable", "A key member must not be nullable", nameof(ThinktectureRuntimeExtensionsAnalyzer), DiagnosticSeverity.Error, true); - public static readonly DiagnosticDescriptor EnumsAndValueObjectsMustNotBeGeneric = new("TTRESG033", "Enumerations and value objects must not be generic", "{0} '{1}' must not be generic", nameof(ThinktectureRuntimeExtensionsAnalyzer), DiagnosticSeverity.Error, true); + public static readonly DiagnosticDescriptor EnumsValueObjectsAndUnionsMustNotBeGeneric = new("TTRESG033", "Enumerations, value objects and unions must not be generic", "{0} '{1}' must not be generic", nameof(ThinktectureRuntimeExtensionsAnalyzer), DiagnosticSeverity.Error, true); public static readonly DiagnosticDescriptor BaseClassFieldMustBeReadOnly = new("TTRESG034", "Field of the base class must be read-only", "The field '{0}' of the base class '{1}' must be read-only", nameof(ThinktectureRuntimeExtensionsAnalyzer), DiagnosticSeverity.Error, true); public static readonly DiagnosticDescriptor BaseClassPropertyMustBeReadOnly = new("TTRESG035", "Property of the base class must be read-only", "The property '{0}' of the base class '{1}' must be read-only", nameof(ThinktectureRuntimeExtensionsAnalyzer), DiagnosticSeverity.Error, true); public static readonly DiagnosticDescriptor EnumKeyShouldNotBeNullable = new("TTRESG036", "The key must not be nullable", "The generic type T of SmartEnumAttribute must not be nullable", nameof(ThinktectureRuntimeExtensionsAnalyzer), DiagnosticSeverity.Error, true); @@ -29,7 +29,7 @@ internal static class DiagnosticsDescriptors public static readonly DiagnosticDescriptor CustomKeyMemberImplementationNotFound = new("TTRESG044", "Custom implementation of the key member not found", $"Provide a custom implementation of the key member. Implement a field or property '{{0}}'. Use '{Constants.Attributes.Properties.KEY_MEMBER_NAME}' to change the name.", nameof(ThinktectureRuntimeExtensionsAnalyzer), DiagnosticSeverity.Error, true); public static readonly DiagnosticDescriptor CustomKeyMemberImplementationTypeMismatch = new("TTRESG045", "Key member type mismatch", "The type of the key member '{0}' must be '{2}' instead of '{1}'", nameof(ThinktectureRuntimeExtensionsAnalyzer), DiagnosticSeverity.Error, true); public static readonly DiagnosticDescriptor IndexBasedSwitchAndMapMustUseNamedParameters = new("TTRESG046", "The arguments of \"Switch\" and \"Map\" must named", "Not all arguments of \"Switch/Map\" on type '{0}' are named", nameof(ThinktectureRuntimeExtensionsAnalyzer), DiagnosticSeverity.Error, true); - public static readonly DiagnosticDescriptor TypeMustBeClass = new("TTRESG047", "The type must be a class", "The type '{0}' must be a class", nameof(ThinktectureRuntimeExtensionsAnalyzer), DiagnosticSeverity.Error, true); + public static readonly DiagnosticDescriptor VariableMustBeInitializedWithNonDefaultValue = new("TTRESG047", "Variable must be initialed with non-default value", "Variable of type '{0}' must be initialized with non default value", nameof(ThinktectureRuntimeExtensionsAnalyzer), DiagnosticSeverity.Error, true); public static readonly DiagnosticDescriptor ErrorDuringCodeAnalysis = new("TTRESG098", "Error during code analysis", "Error during code analysis of '{0}': '{1}'", nameof(ThinktectureRuntimeExtensionsAnalyzer), DiagnosticSeverity.Warning, true); public static readonly DiagnosticDescriptor ErrorDuringGeneration = new("TTRESG099", "Error during code generation", "Error during code generation for '{0}': '{1}'", nameof(ThinktectureRuntimeExtensionsAnalyzer), DiagnosticSeverity.Error, true); diff --git a/src/Thinktecture.Runtime.Extensions.SourceGenerator/CodeAnalysis/DiscriminatedUnions/UnionCodeGenerator.cs b/src/Thinktecture.Runtime.Extensions.SourceGenerator/CodeAnalysis/DiscriminatedUnions/UnionCodeGenerator.cs index a228d63c..af673f8b 100644 --- a/src/Thinktecture.Runtime.Extensions.SourceGenerator/CodeAnalysis/DiscriminatedUnions/UnionCodeGenerator.cs +++ b/src/Thinktecture.Runtime.Extensions.SourceGenerator/CodeAnalysis/DiscriminatedUnions/UnionCodeGenerator.cs @@ -50,10 +50,16 @@ private void GenerateUnion(CancellationToken cancellationToken) _sb.Append(@" "); - _sb.Append(_state.IsReferenceType ? "sealed " : "readonly ").Append("partial ").Append(_state.IsReferenceType ? "class" : "struct").Append(" ").Append(_state.Name).Append(" :") - .Append(@" + _sb.Append(_state.IsReferenceType ? "sealed " : "readonly ").Append("partial ").Append(_state.IsReferenceType ? "class" : "struct").Append(" ").Append(_state.Name); + + if (!_state.IsRefStruct) + { + _sb.Append(@" : global::System.IEquatable<").AppendTypeFullyQualified(_state).Append(@">, - global::System.Numerics.IEqualityOperators<").AppendTypeFullyQualified(_state).Append(@", ").AppendTypeFullyQualified(_state).Append(@", bool> + global::System.Numerics.IEqualityOperators<").AppendTypeFullyQualified(_state).Append(@", ").AppendTypeFullyQualified(_state).Append(", bool>"); + } + + _sb.Append(@" { private static readonly int _typeHashCode = typeof(").AppendTypeFullyQualified(_state).Append(@").GetHashCode(); @@ -267,8 +273,20 @@ private void GenerateEquals() /// public override bool Equals(object? other) + {"); + + if (_state.IsRefStruct) { - return other is ").AppendTypeFullyQualified(_state).Append(@" obj && Equals(obj); + _sb.Append(@" + return false;"); + } + else + { + _sb.Append(@" + return other is ").AppendTypeFullyQualified(_state).Append(" obj && Equals(obj);"); + } + + _sb.Append(@" } /// diff --git a/src/Thinktecture.Runtime.Extensions.SourceGenerator/CodeAnalysis/DiscriminatedUnions/UnionSourceGenState.cs b/src/Thinktecture.Runtime.Extensions.SourceGenerator/CodeAnalysis/DiscriminatedUnions/UnionSourceGenState.cs index f15285d5..6dbbdf0c 100644 --- a/src/Thinktecture.Runtime.Extensions.SourceGenerator/CodeAnalysis/DiscriminatedUnions/UnionSourceGenState.cs +++ b/src/Thinktecture.Runtime.Extensions.SourceGenerator/CodeAnalysis/DiscriminatedUnions/UnionSourceGenState.cs @@ -7,8 +7,9 @@ public sealed class UnionSourceGenState : ITypeInformation, IEquatable false; public ImmutableArray MemberTypes { get; } @@ -28,6 +29,7 @@ public UnionSourceGenState( IsReferenceType = type.IsReferenceType; NullableAnnotation = type.NullableAnnotation; IsNullableStruct = type.OriginalDefinition.SpecialType == SpecialType.System_Nullable_T; + IsRefStruct = type is { IsRefLikeType: true, IsReferenceType: false }; } public override bool Equals(object? obj) @@ -44,6 +46,7 @@ public bool Equals(UnionSourceGenState? other) return TypeFullyQualified == other.TypeFullyQualified && IsReferenceType == other.IsReferenceType + && IsRefStruct == other.IsRefStruct && Settings.Equals(other.Settings) && MemberTypes.SequenceEqual(other.MemberTypes); } @@ -54,6 +57,7 @@ public override int GetHashCode() { var hashCode = TypeFullyQualified.GetHashCode(); hashCode = (hashCode * 397) ^ IsReferenceType.GetHashCode(); + hashCode = (hashCode * 397) ^ IsRefStruct.GetHashCode(); hashCode = (hashCode * 397) ^ Settings.GetHashCode(); hashCode = (hashCode * 397) ^ MemberTypes.ComputeHashCode(); diff --git a/src/Thinktecture.Runtime.Extensions.SourceGenerator/CodeAnalysis/DiscriminatedUnions/UnionSourceGenerator.cs b/src/Thinktecture.Runtime.Extensions.SourceGenerator/CodeAnalysis/DiscriminatedUnions/UnionSourceGenerator.cs index f5b89bae..75e507ce 100644 --- a/src/Thinktecture.Runtime.Extensions.SourceGenerator/CodeAnalysis/DiscriminatedUnions/UnionSourceGenerator.cs +++ b/src/Thinktecture.Runtime.Extensions.SourceGenerator/CodeAnalysis/DiscriminatedUnions/UnionSourceGenerator.cs @@ -6,7 +6,7 @@ namespace Thinktecture.CodeAnalysis.DiscriminatedUnions; public class UnionSourceGenerator : ThinktectureSourceGeneratorBase, IIncrementalGenerator { public UnionSourceGenerator() - : base(10_000) + : base(15_000) { } @@ -59,6 +59,7 @@ private bool IsCandidate(SyntaxNode syntaxNode, CancellationToken cancellationTo return syntaxNode switch { ClassDeclarationSyntax classDeclaration when IsUnionCandidate(classDeclaration) => true, + StructDeclarationSyntax structDeclaration when IsUnionCandidate(structDeclaration) => true, _ => false }; } diff --git a/src/Thinktecture.Runtime.Extensions.SourceGenerator/CodeAnalysis/SmartEnums/SmartEnumSourceGenerator.cs b/src/Thinktecture.Runtime.Extensions.SourceGenerator/CodeAnalysis/SmartEnums/SmartEnumSourceGenerator.cs index 527da5a5..6bd5027f 100644 --- a/src/Thinktecture.Runtime.Extensions.SourceGenerator/CodeAnalysis/SmartEnums/SmartEnumSourceGenerator.cs +++ b/src/Thinktecture.Runtime.Extensions.SourceGenerator/CodeAnalysis/SmartEnums/SmartEnumSourceGenerator.cs @@ -6,7 +6,7 @@ namespace Thinktecture.CodeAnalysis.SmartEnums; public sealed class SmartEnumSourceGenerator : ThinktectureSourceGeneratorBase, IIncrementalGenerator { public SmartEnumSourceGenerator() - : base(17_000) + : base(25_000) { } diff --git a/src/Thinktecture.Runtime.Extensions.SourceGenerator/CodeAnalysis/TypedMemberStateFactory.cs b/src/Thinktecture.Runtime.Extensions.SourceGenerator/CodeAnalysis/TypedMemberStateFactory.cs index 168dde46..ef95a07d 100644 --- a/src/Thinktecture.Runtime.Extensions.SourceGenerator/CodeAnalysis/TypedMemberStateFactory.cs +++ b/src/Thinktecture.Runtime.Extensions.SourceGenerator/CodeAnalysis/TypedMemberStateFactory.cs @@ -3,6 +3,7 @@ namespace Thinktecture.CodeAnalysis; public class TypedMemberStateFactory { private const string _SYSTEM_RUNTIME_DLL = "System.Runtime.dll"; + private const string _SYSTEM_CORELIB_DLL = "System.Private.CoreLib.dll"; private readonly TypedMemberStates _boolean; private readonly TypedMemberStates _char; @@ -46,6 +47,7 @@ private TypedMemberStateFactory(Compilation compilation) CreateAndAddStatesForSystemRuntime(compilation, "System.DateOnly", lookup); CreateAndAddStatesForSystemRuntime(compilation, "System.TimeOnly", lookup); CreateAndAddStatesForSystemRuntime(compilation, "System.TimeSpan", lookup); + CreateAndAddStatesForSystemRuntime(compilation, "System.Guid", lookup); } private static TypedMemberStates CreateStates(Compilation compilation, SpecialType specialType) @@ -67,7 +69,7 @@ private static void CreateAndAddStatesForSystemRuntime(Compilation compilation, { type = types[0]; - if (type.ContainingModule.MetadataName != _SYSTEM_RUNTIME_DLL) + if (type.ContainingModule.MetadataName is not (_SYSTEM_RUNTIME_DLL or _SYSTEM_CORELIB_DLL)) return; } else @@ -76,7 +78,7 @@ private static void CreateAndAddStatesForSystemRuntime(Compilation compilation, { var candidate = types[i]; - if (candidate.ContainingModule.MetadataName != _SYSTEM_RUNTIME_DLL) + if (candidate.ContainingModule.MetadataName is not (_SYSTEM_RUNTIME_DLL or _SYSTEM_CORELIB_DLL)) continue; // duplicate? @@ -87,7 +89,7 @@ private static void CreateAndAddStatesForSystemRuntime(Compilation compilation, } } - if (type is null) + if (type is null || type.TypeKind == TypeKind.Error) return; lookup.Add((type.ContainingModule.MetadataName, type.MetadataToken), CreateStates(type)); diff --git a/src/Thinktecture.Runtime.Extensions.SourceGenerator/Extensions/TypeSymbolExtensions.cs b/src/Thinktecture.Runtime.Extensions.SourceGenerator/Extensions/TypeSymbolExtensions.cs index cdfcac3b..0f124d4c 100644 --- a/src/Thinktecture.Runtime.Extensions.SourceGenerator/Extensions/TypeSymbolExtensions.cs +++ b/src/Thinktecture.Runtime.Extensions.SourceGenerator/Extensions/TypeSymbolExtensions.cs @@ -53,11 +53,17 @@ public static bool IsUnionAttribute(this ITypeSymbol? attributeType) return attributeType is { Name: Constants.Attributes.Union.NAME, ContainingNamespace: { Name: Constants.Attributes.Union.NAMESPACE, ContainingNamespace.IsGlobalNamespace: true } }; } - public static bool IsUnionAttribute( + public static bool IsUnionType( [NotNullWhen(true)] this ITypeSymbol? unionType, [NotNullWhen(true)] out AttributeData? unionAttribute) { - unionAttribute = unionType?.FindAttribute(static attributeClass => attributeClass.IsUnionAttribute()); + if (unionType is null || unionType.SpecialType != SpecialType.None) + { + unionAttribute = null; + return false; + } + + unionAttribute = unionType.FindAttribute(static attributeClass => attributeClass.IsUnionAttribute()); return unionAttribute is not null; } @@ -88,7 +94,13 @@ public static bool IsEnum( [NotNullWhen(true)] this ITypeSymbol? enumType, [NotNullWhen(true)] out AttributeData? smartEnumAttribute) { - smartEnumAttribute = enumType?.FindAttribute(static attributeClass => attributeClass.IsSmartEnumAttribute()); + if (enumType is null || enumType.SpecialType != SpecialType.None) + { + smartEnumAttribute = null; + return false; + } + + smartEnumAttribute = enumType.FindAttribute(static attributeClass => attributeClass.IsSmartEnumAttribute()); return smartEnumAttribute is not null; } diff --git a/src/Thinktecture.Runtime.Extensions/UnionAttribute.cs b/src/Thinktecture.Runtime.Extensions/UnionAttribute.cs index 0c6c6585..b697da5a 100644 --- a/src/Thinktecture.Runtime.Extensions/UnionAttribute.cs +++ b/src/Thinktecture.Runtime.Extensions/UnionAttribute.cs @@ -5,7 +5,7 @@ namespace Thinktecture; /// /// One of the types of the discriminated union. /// One of the types of the discriminated union. -[AttributeUsage(AttributeTargets.Class)] +[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct)] public class UnionAttribute : UnionAttributeBase { /// diff --git a/test/Thinktecture.Runtime.Extensions.SourceGenerator.Tests/AnalyzerAndCodeFixTests/TTRESG004_TypeMustBeClassOrStruct.cs b/test/Thinktecture.Runtime.Extensions.SourceGenerator.Tests/AnalyzerAndCodeFixTests/TTRESG004_TypeMustBeClassOrStruct.cs index 4ee28990..f49d73b9 100644 --- a/test/Thinktecture.Runtime.Extensions.SourceGenerator.Tests/AnalyzerAndCodeFixTests/TTRESG004_TypeMustBeClassOrStruct.cs +++ b/test/Thinktecture.Runtime.Extensions.SourceGenerator.Tests/AnalyzerAndCodeFixTests/TTRESG004_TypeMustBeClassOrStruct.cs @@ -28,7 +28,7 @@ public partial record {|#0:TestEnum|} """; var expected = Verifier.Diagnostic(_DIAGNOSTIC_ID).WithLocation(0).WithArguments("TestEnum"); - await Verifier.VerifyAnalyzerAsync(code, new[] { typeof(IEnum<>).Assembly }, expected); + await Verifier.VerifyAnalyzerAsync(code, [typeof(IEnum<>).Assembly], expected); } [Fact] @@ -49,7 +49,7 @@ public partial class TestEnum } """; - await Verifier.VerifyAnalyzerAsync(code, new[] { typeof(IEnum<>).Assembly }); + await Verifier.VerifyAnalyzerAsync(code, [typeof(IEnum<>).Assembly]); } [Fact] @@ -70,7 +70,7 @@ public partial struct TestEnum } """; - await Verifier.VerifyAnalyzerAsync(code, new[] { typeof(IEnum<>).Assembly }); + await Verifier.VerifyAnalyzerAsync(code, [typeof(IEnum<>).Assembly]); } } @@ -94,7 +94,7 @@ public partial record {|#0:TestValueObject|} """; var expected = Verifier.Diagnostic(_DIAGNOSTIC_ID).WithLocation(0).WithArguments("TestValueObject"); - await Verifier.VerifyAnalyzerAsync(code, new[] { typeof(ComplexValueObjectAttribute).Assembly }, expected); + await Verifier.VerifyAnalyzerAsync(code, [typeof(ComplexValueObjectAttribute).Assembly], expected); } [Fact] @@ -114,7 +114,7 @@ public partial class TestValueObject } """; - await Verifier.VerifyAnalyzerAsync(code, new[] { typeof(ComplexValueObjectAttribute).Assembly }); + await Verifier.VerifyAnalyzerAsync(code, [typeof(ComplexValueObjectAttribute).Assembly]); } [Fact] @@ -134,7 +134,7 @@ public partial struct TestValueObject } """; - await Verifier.VerifyAnalyzerAsync(code, new[] { typeof(ComplexValueObjectAttribute).Assembly }); + await Verifier.VerifyAnalyzerAsync(code, [typeof(ComplexValueObjectAttribute).Assembly]); } } @@ -158,7 +158,7 @@ public partial record {|#0:TestValueObject|} """; var expected = Verifier.Diagnostic(_DIAGNOSTIC_ID).WithLocation(0).WithArguments("TestValueObject"); - await Verifier.VerifyAnalyzerAsync(code, new[] { typeof(ComplexValueObjectAttribute).Assembly }, expected); + await Verifier.VerifyAnalyzerAsync(code, [typeof(ComplexValueObjectAttribute).Assembly], expected); } [Fact] @@ -178,7 +178,7 @@ public partial class TestValueObject } """; - await Verifier.VerifyAnalyzerAsync(code, new[] { typeof(ComplexValueObjectAttribute).Assembly }); + await Verifier.VerifyAnalyzerAsync(code, [typeof(ComplexValueObjectAttribute).Assembly]); } [Fact] @@ -198,7 +198,65 @@ public partial struct TestValueObject } """; - await Verifier.VerifyAnalyzerAsync(code, new[] { typeof(ComplexValueObjectAttribute).Assembly }); + await Verifier.VerifyAnalyzerAsync(code, [typeof(ComplexValueObjectAttribute).Assembly]); + } + } + + public class Union_must_be_class_or_struct + { + [Fact] + public async Task Should_trigger_on_record() + { + var code = """ + + using System; + using Thinktecture; + + namespace TestNamespace + { + [Union] + public partial record {|#0:TestUnion|}; + } + """; + + var expected = Verifier.Diagnostic(_DIAGNOSTIC_ID).WithLocation(0).WithArguments("TestUnion"); + await Verifier.VerifyAnalyzerAsync(code, [typeof(UnionAttribute<,>).Assembly], expected); + } + + [Fact] + public async Task Should_not_trigger_on_class() + { + var code = """ + + using System; + using Thinktecture; + + namespace TestNamespace + { + [Union] + public partial class {|#0:TestUnion|}; + } + """; + + await Verifier.VerifyAnalyzerAsync(code, [typeof(UnionAttribute<,>).Assembly]); + } + + [Fact] + public async Task Should_not_trigger_on_struct() + { + var code = """ + + using System; + using Thinktecture; + + namespace TestNamespace + { + [Union] + public partial struct {|#0:TestUnion|}; + } + """; + + await Verifier.VerifyAnalyzerAsync(code, [typeof(UnionAttribute<,>).Assembly]); } } } diff --git a/test/Thinktecture.Runtime.Extensions.SourceGenerator.Tests/AnalyzerAndCodeFixTests/TTRESG006_TypeMustBePartial.cs b/test/Thinktecture.Runtime.Extensions.SourceGenerator.Tests/AnalyzerAndCodeFixTests/TTRESG006_TypeMustBePartial.cs index a6334543..f2188395 100644 --- a/test/Thinktecture.Runtime.Extensions.SourceGenerator.Tests/AnalyzerAndCodeFixTests/TTRESG006_TypeMustBePartial.cs +++ b/test/Thinktecture.Runtime.Extensions.SourceGenerator.Tests/AnalyzerAndCodeFixTests/TTRESG006_TypeMustBePartial.cs @@ -401,5 +401,99 @@ public partial class {|#0:TestUnion|}; await Verifier.VerifyAnalyzerAsync(code, new[] { typeof(ComplexValueObjectAttribute).Assembly }); } + + [Fact] + public async Task Should_trigger_on_non_partial_struct() + { + var code = """ + using System; + using Thinktecture; + + namespace TestNamespace + { + [Union] + public struct {|#0:TestUnion|}; + } + """; + + var expectedCode = """ + using System; + using Thinktecture; + + namespace TestNamespace + { + [Union] + public partial struct {|#0:TestUnion|}; + } + """; + + var expected = Verifier.Diagnostic(_DIAGNOSTIC_ID).WithLocation(0).WithArguments("TestUnion"); + await Verifier.VerifyCodeFixAsync(code, expectedCode, new[] { typeof(ComplexValueObjectAttribute).Assembly }, expected); + } + + [Fact] + public async Task Should_not_trigger_on_partial_struct() + { + var code = """ + + using System; + using Thinktecture; + + namespace TestNamespace + { + [Union] + public partial struct {|#0:TestUnion|}; + } + """; + + await Verifier.VerifyAnalyzerAsync(code, new[] { typeof(ComplexValueObjectAttribute).Assembly }); + } + + [Fact] + public async Task Should_trigger_on_non_partial_ref_struct() + { + var code = """ + using System; + using Thinktecture; + + namespace TestNamespace + { + [Union] + public ref struct {|#0:TestUnion|}; + } + """; + + var expectedCode = """ + using System; + using Thinktecture; + + namespace TestNamespace + { + [Union] + public ref partial struct {|#0:TestUnion|}; + } + """; + + var expected = Verifier.Diagnostic(_DIAGNOSTIC_ID).WithLocation(0).WithArguments("TestUnion"); + await Verifier.VerifyCodeFixAsync(code, expectedCode, new[] { typeof(ComplexValueObjectAttribute).Assembly }, expected); + } + + [Fact] + public async Task Should_not_trigger_on_partial_ref_struct() + { + var code = """ + + using System; + using Thinktecture; + + namespace TestNamespace + { + [Union] + public ref partial struct {|#0:TestUnion|}; + } + """; + + await Verifier.VerifyAnalyzerAsync(code, new[] { typeof(ComplexValueObjectAttribute).Assembly }); + } } } diff --git a/test/Thinktecture.Runtime.Extensions.SourceGenerator.Tests/AnalyzerAndCodeFixTests/TTRESG033_EnumsAndValueObjectsMustNotBeGeneric.cs b/test/Thinktecture.Runtime.Extensions.SourceGenerator.Tests/AnalyzerAndCodeFixTests/TTRESG033_EnumsValueObjectsAndUnionsMustNotBeGeneric.cs similarity index 88% rename from test/Thinktecture.Runtime.Extensions.SourceGenerator.Tests/AnalyzerAndCodeFixTests/TTRESG033_EnumsAndValueObjectsMustNotBeGeneric.cs rename to test/Thinktecture.Runtime.Extensions.SourceGenerator.Tests/AnalyzerAndCodeFixTests/TTRESG033_EnumsValueObjectsAndUnionsMustNotBeGeneric.cs index 8dc99421..8a143111 100644 --- a/test/Thinktecture.Runtime.Extensions.SourceGenerator.Tests/AnalyzerAndCodeFixTests/TTRESG033_EnumsAndValueObjectsMustNotBeGeneric.cs +++ b/test/Thinktecture.Runtime.Extensions.SourceGenerator.Tests/AnalyzerAndCodeFixTests/TTRESG033_EnumsValueObjectsAndUnionsMustNotBeGeneric.cs @@ -1,171 +1,190 @@ -using System.Threading.Tasks; -using Thinktecture.CodeAnalysis.CodeFixes; -using Thinktecture.CodeAnalysis.Diagnostics; -using Thinktecture.Runtime.Tests.Verifiers; - -namespace Thinktecture.Runtime.Tests.AnalyzerAndCodeFixTests; - -// ReSharper disable InconsistentNaming -public class TTRESG033_EnumsAndValueObjectsMustNotBeGeneric -{ - private const string _DIAGNOSTIC_ID = "TTRESG033"; - - public class Enums_must_not_be_generic - { - [Fact] - public async Task Should_trigger_on_generic_class() - { - var code = """ - - using System; - using Thinktecture; - - namespace TestNamespace - { - [SmartEnum(IsValidatable = true)] - public partial class {|#0:TestEnum|} - { - public static readonly TestEnum Item1 = default; - } - } - """; - - var expected = CodeFixVerifier.Diagnostic(_DIAGNOSTIC_ID).WithLocation(0).WithArguments("Enumeration", "TestEnum"); - await CodeFixVerifier.VerifyAnalyzerAsync(code, new[] { typeof(IEnum<>).Assembly }, expected); - } - - [Fact] - public async Task Should_trigger_on_generic_struct() - { - var code = """ - - using System; - using Thinktecture; - - namespace TestNamespace - { - [SmartEnum(IsValidatable = true)] - public partial struct {|#0:TestEnum|} - { - public static readonly TestEnum Item1 = default; - } - } - """; - - var expected = CodeFixVerifier.Diagnostic(_DIAGNOSTIC_ID).WithLocation(0).WithArguments("Enumeration", "TestEnum"); - await CodeFixVerifier.VerifyAnalyzerAsync(code, new[] { typeof(IEnum<>).Assembly }, expected); - } - } - - public class KeyedValue_objects_must_not_be_generic - { - [Fact] - public async Task Should_trigger_on_generic_class() - { - var code = """ - - using System; - using Thinktecture; - - namespace TestNamespace - { - [ValueObject] - public partial class {|#0:TestValueObject|} - { - } - } - """; - - var expected = CodeFixVerifier.Diagnostic(_DIAGNOSTIC_ID).WithLocation(0).WithArguments("Value Object", "TestValueObject"); - await CodeFixVerifier.VerifyAnalyzerAsync(code, new[] { typeof(IEnum<>).Assembly }, expected); - } - - [Fact] - public async Task Should_trigger_on_generic_struct() - { - var code = """ - - using System; - using Thinktecture; - - namespace TestNamespace - { - [ValueObject] - public partial struct {|#0:TestValueObject|} - { - } - } - """; - - var expected = CodeFixVerifier.Diagnostic(_DIAGNOSTIC_ID).WithLocation(0).WithArguments("Value Object", "TestValueObject"); - await CodeFixVerifier.VerifyAnalyzerAsync(code, new[] { typeof(IEnum<>).Assembly }, expected); - } - } - - public class ComplexValue_objects_must_not_be_generic - { - [Fact] - public async Task Should_trigger_on_generic_class() - { - var code = """ - - using System; - using Thinktecture; - - namespace TestNamespace - { - [ComplexValueObject] - public partial class {|#0:TestValueObject|} - { - } - } - """; - - var expected = CodeFixVerifier.Diagnostic(_DIAGNOSTIC_ID).WithLocation(0).WithArguments("Value Object", "TestValueObject"); - await CodeFixVerifier.VerifyAnalyzerAsync(code, new[] { typeof(IEnum<>).Assembly }, expected); - } - - [Fact] - public async Task Should_trigger_on_generic_struct() - { - var code = """ - - using System; - using Thinktecture; - - namespace TestNamespace - { - [ComplexValueObject] - public partial struct {|#0:TestValueObject|} - { - } - } - """; - - var expected = CodeFixVerifier.Diagnostic(_DIAGNOSTIC_ID).WithLocation(0).WithArguments("Value Object", "TestValueObject"); - await CodeFixVerifier.VerifyAnalyzerAsync(code, new[] { typeof(IEnum<>).Assembly }, expected); - } - } - - public class Union_must_not_be_generic - { - [Fact] - public async Task Should_trigger_on_generic_class() - { - var code = """ - - using System; - using Thinktecture; - - namespace TestNamespace - { - [Union] - public partial class {|#0:TestUnion|}; - } - """; - - var expected = CodeFixVerifier.Diagnostic(_DIAGNOSTIC_ID).WithLocation(0).WithArguments("Union", "TestUnion"); - await CodeFixVerifier.VerifyAnalyzerAsync(code, new[] { typeof(IEnum<>).Assembly }, expected); - } - } -} +using System.Threading.Tasks; +using Thinktecture.CodeAnalysis.CodeFixes; +using Thinktecture.CodeAnalysis.Diagnostics; +using Thinktecture.Runtime.Tests.Verifiers; + +namespace Thinktecture.Runtime.Tests.AnalyzerAndCodeFixTests; + +// ReSharper disable InconsistentNaming +public class TTRESG033_EnumsValueObjectsAndUnionsMustNotBeGeneric +{ + private const string _DIAGNOSTIC_ID = "TTRESG033"; + + public class Enums_must_not_be_generic + { + [Fact] + public async Task Should_trigger_on_generic_class() + { + var code = """ + + using System; + using Thinktecture; + + namespace TestNamespace + { + [SmartEnum(IsValidatable = true)] + public partial class {|#0:TestEnum|} + { + public static readonly TestEnum Item1 = default; + } + } + """; + + var expected = CodeFixVerifier.Diagnostic(_DIAGNOSTIC_ID).WithLocation(0).WithArguments("Enumeration", "TestEnum"); + await CodeFixVerifier.VerifyAnalyzerAsync(code, new[] { typeof(IEnum<>).Assembly }, expected); + } + + [Fact] + public async Task Should_trigger_on_generic_struct() + { + var code = """ + + using System; + using Thinktecture; + + namespace TestNamespace + { + [SmartEnum(IsValidatable = true)] + public partial struct {|#0:TestEnum|} + { + public static readonly TestEnum Item1 = default; + } + } + """; + + var expected = CodeFixVerifier.Diagnostic(_DIAGNOSTIC_ID).WithLocation(0).WithArguments("Enumeration", "TestEnum"); + await CodeFixVerifier.VerifyAnalyzerAsync(code, new[] { typeof(IEnum<>).Assembly }, expected); + } + } + + public class KeyedValue_objects_must_not_be_generic + { + [Fact] + public async Task Should_trigger_on_generic_class() + { + var code = """ + + using System; + using Thinktecture; + + namespace TestNamespace + { + [ValueObject] + public partial class {|#0:TestValueObject|} + { + } + } + """; + + var expected = CodeFixVerifier.Diagnostic(_DIAGNOSTIC_ID).WithLocation(0).WithArguments("Value Object", "TestValueObject"); + await CodeFixVerifier.VerifyAnalyzerAsync(code, new[] { typeof(IEnum<>).Assembly }, expected); + } + + [Fact] + public async Task Should_trigger_on_generic_struct() + { + var code = """ + + using System; + using Thinktecture; + + namespace TestNamespace + { + [ValueObject] + public partial struct {|#0:TestValueObject|} + { + } + } + """; + + var expected = CodeFixVerifier.Diagnostic(_DIAGNOSTIC_ID).WithLocation(0).WithArguments("Value Object", "TestValueObject"); + await CodeFixVerifier.VerifyAnalyzerAsync(code, new[] { typeof(IEnum<>).Assembly }, expected); + } + } + + public class ComplexValue_objects_must_not_be_generic + { + [Fact] + public async Task Should_trigger_on_generic_class() + { + var code = """ + + using System; + using Thinktecture; + + namespace TestNamespace + { + [ComplexValueObject] + public partial class {|#0:TestValueObject|} + { + } + } + """; + + var expected = CodeFixVerifier.Diagnostic(_DIAGNOSTIC_ID).WithLocation(0).WithArguments("Value Object", "TestValueObject"); + await CodeFixVerifier.VerifyAnalyzerAsync(code, new[] { typeof(IEnum<>).Assembly }, expected); + } + + [Fact] + public async Task Should_trigger_on_generic_struct() + { + var code = """ + + using System; + using Thinktecture; + + namespace TestNamespace + { + [ComplexValueObject] + public partial struct {|#0:TestValueObject|} + { + } + } + """; + + var expected = CodeFixVerifier.Diagnostic(_DIAGNOSTIC_ID).WithLocation(0).WithArguments("Value Object", "TestValueObject"); + await CodeFixVerifier.VerifyAnalyzerAsync(code, new[] { typeof(IEnum<>).Assembly }, expected); + } + } + + public class Union_must_not_be_generic + { + [Fact] + public async Task Should_trigger_on_generic_class() + { + var code = """ + + using System; + using Thinktecture; + + namespace TestNamespace + { + [Union] + public partial class {|#0:TestUnion|}; + } + """; + + var expected = CodeFixVerifier.Diagnostic(_DIAGNOSTIC_ID).WithLocation(0).WithArguments("Union", "TestUnion"); + await CodeFixVerifier.VerifyAnalyzerAsync(code, new[] { typeof(IEnum<>).Assembly }, expected); + } + + [Fact] + public async Task Should_trigger_on_generic_struct() + { + var code = """ + + using System; + using Thinktecture; + + namespace TestNamespace + { + [Union] + public partial struct {|#0:TestUnion|}; + } + """; + + var expected = CodeFixVerifier.Diagnostic(_DIAGNOSTIC_ID).WithLocation(0).WithArguments("Union", "TestUnion"); + await CodeFixVerifier.VerifyAnalyzerAsync(code, new[] { typeof(IEnum<>).Assembly }, expected); + } + } +} diff --git a/test/Thinktecture.Runtime.Extensions.SourceGenerator.Tests/AnalyzerAndCodeFixTests/TTRESG043_PrimaryConstructorNotAllowed.cs b/test/Thinktecture.Runtime.Extensions.SourceGenerator.Tests/AnalyzerAndCodeFixTests/TTRESG043_PrimaryConstructorNotAllowed.cs index 8f5c1440..d980ca3e 100644 --- a/test/Thinktecture.Runtime.Extensions.SourceGenerator.Tests/AnalyzerAndCodeFixTests/TTRESG043_PrimaryConstructorNotAllowed.cs +++ b/test/Thinktecture.Runtime.Extensions.SourceGenerator.Tests/AnalyzerAndCodeFixTests/TTRESG043_PrimaryConstructorNotAllowed.cs @@ -165,5 +165,43 @@ public partial class {|#0:TestUnion|}(); var expected = Verifier.Diagnostic(_DIAGNOSTIC_ID).WithLocation(0).WithArguments("TestUnion"); await Verifier.VerifyAnalyzerAsync(code, new[] { typeof(IEnum<>).Assembly }, expected); } + + [Fact] + public async Task Should_trigger_if_union_is_struct_and_has_primary_constructor() + { + var code = """ + + using System; + using Thinktecture; + + namespace TestNamespace + { + [Union] + public partial struct {|#0:TestUnion|}(); + } + """; + + var expected = Verifier.Diagnostic(_DIAGNOSTIC_ID).WithLocation(0).WithArguments("TestUnion"); + await Verifier.VerifyAnalyzerAsync(code, new[] { typeof(IEnum<>).Assembly }, expected); + } + + [Fact] + public async Task Should_trigger_if_union_is_ref_struct_and_has_primary_constructor() + { + var code = """ + + using System; + using Thinktecture; + + namespace TestNamespace + { + [Union] + public ref partial struct {|#0:TestUnion|}(); + } + """; + + var expected = Verifier.Diagnostic(_DIAGNOSTIC_ID).WithLocation(0).WithArguments("TestUnion"); + await Verifier.VerifyAnalyzerAsync(code, new[] { typeof(IEnum<>).Assembly }, expected); + } } } diff --git a/test/Thinktecture.Runtime.Extensions.SourceGenerator.Tests/AnalyzerAndCodeFixTests/TTRESG047_TypeMustBeClass.cs b/test/Thinktecture.Runtime.Extensions.SourceGenerator.Tests/AnalyzerAndCodeFixTests/TTRESG047_TypeMustBeClass.cs deleted file mode 100644 index eab28114..00000000 --- a/test/Thinktecture.Runtime.Extensions.SourceGenerator.Tests/AnalyzerAndCodeFixTests/TTRESG047_TypeMustBeClass.cs +++ /dev/null @@ -1,50 +0,0 @@ -using System.Threading.Tasks; -using Verifier = Thinktecture.Runtime.Tests.Verifiers.CodeFixVerifier; - -namespace Thinktecture.Runtime.Tests.AnalyzerAndCodeFixTests; - -// ReSharper disable InconsistentNaming -public class TTRESG047_TypeMustBeClassOrStruct -{ - private const string _DIAGNOSTIC_ID = "TTRESG047"; - - public class Union_must_be_class - { - [Fact] - public async Task Should_trigger_on_record() - { - var code = """ - - using System; - using Thinktecture; - - namespace TestNamespace - { - [Union] - public partial record {|#0:TestUnion|}; - } - """; - - var expected = Verifier.Diagnostic(_DIAGNOSTIC_ID).WithLocation(0).WithArguments("TestUnion"); - await Verifier.VerifyAnalyzerAsync(code, new[] { typeof(UnionAttribute<,>).Assembly }, expected); - } - - [Fact] - public async Task Should_not_trigger_on_class() - { - var code = """ - - using System; - using Thinktecture; - - namespace TestNamespace - { - [Union] - public partial class {|#0:TestUnion|}; - } - """; - - await Verifier.VerifyAnalyzerAsync(code, new[] { typeof(UnionAttribute<,>).Assembly }); - } - } -} diff --git a/test/Thinktecture.Runtime.Extensions.SourceGenerator.Tests/AnalyzerAndCodeFixTests/TTRESG047_VariableMustBeInitializedWithNonDefaultValue.cs b/test/Thinktecture.Runtime.Extensions.SourceGenerator.Tests/AnalyzerAndCodeFixTests/TTRESG047_VariableMustBeInitializedWithNonDefaultValue.cs new file mode 100644 index 00000000..91719c08 --- /dev/null +++ b/test/Thinktecture.Runtime.Extensions.SourceGenerator.Tests/AnalyzerAndCodeFixTests/TTRESG047_VariableMustBeInitializedWithNonDefaultValue.cs @@ -0,0 +1,63 @@ +using System.Threading.Tasks; +using Thinktecture.CodeAnalysis.CodeFixes; +using Thinktecture.CodeAnalysis.Diagnostics; +using Thinktecture.Runtime.Tests.TestUnions; +using Thinktecture.Runtime.Tests.Verifiers; + +namespace Thinktecture.Runtime.Tests.AnalyzerAndCodeFixTests; + +// ReSharper disable InconsistentNaming +public class TTRESG047_VariableMustBeInitializedWithNonDefaultValue +{ + private const string _DIAGNOSTIC_ID = "TTRESG047"; + + [Fact] + public async Task Should_trigger_on_assignment_with_default() + { + var code = """ + + using System; + using Thinktecture; + using Thinktecture.Runtime.Tests.TestUnions; + + namespace TestNamespace + { + public class TestClass + { + public void TestMethod() + { + TestUnion_struct_string_int testStruct = {|#0:default|}; + } + } + } + """; + + var expected = CodeFixVerifier.Diagnostic(_DIAGNOSTIC_ID).WithLocation(0).WithArguments("TestUnion_struct_string_int"); + await CodeFixVerifier.VerifyAnalyzerAsync(code, [typeof(TestUnion_struct_string_int).Assembly, typeof(UnionAttribute<,>).Assembly], expected); + } + + [Fact] + public async Task Should_trigger_on_default_ctor() + { + var code = """ + + using System; + using Thinktecture; + using Thinktecture.Runtime.Tests.TestUnions; + + namespace TestNamespace + { + public class TestClass + { + public void TestMethod() + { + TestUnion_struct_string_int testStruct = {|#0:new()|}; + } + } + } + """; + + var expected = CodeFixVerifier.Diagnostic(_DIAGNOSTIC_ID).WithLocation(0).WithArguments("TestUnion_struct_string_int"); + await CodeFixVerifier.VerifyAnalyzerAsync(code, [typeof(TestUnion_struct_string_int).Assembly, typeof(UnionAttribute<,>).Assembly], expected); + } +} diff --git a/test/Thinktecture.Runtime.Extensions.SourceGenerator.Tests/SourceGeneratorTests/UnionSourceGeneratorTests.cs b/test/Thinktecture.Runtime.Extensions.SourceGenerator.Tests/SourceGeneratorTests/UnionSourceGeneratorTests.cs index 9a5417ae..736d45cb 100644 --- a/test/Thinktecture.Runtime.Extensions.SourceGenerator.Tests/SourceGeneratorTests/UnionSourceGeneratorTests.cs +++ b/test/Thinktecture.Runtime.Extensions.SourceGenerator.Tests/SourceGeneratorTests/UnionSourceGeneratorTests.cs @@ -329,6 +329,610 @@ public override int GetHashCode() """); } + [Fact] + public void Should_generate_struct_with_string_and_int() + { + var source = """ + using System; + + namespace Thinktecture.Tests + { + [Union] + public partial struct TestUnion; + } + """; + var outputs = GetGeneratedOutputs(source, typeof(UnionAttribute<,>).Assembly); + outputs.Should().HaveCount(1); + + var mainOutput = outputs.Single(kvp => kvp.Key.Contains("Thinktecture.Tests.TestUnion.g.cs")).Value; + + AssertOutput(mainOutput, _GENERATED_HEADER + """ + namespace Thinktecture.Tests + { + [global::System.Runtime.InteropServices.StructLayout(global::System.Runtime.InteropServices.LayoutKind.Auto)] + readonly partial struct TestUnion : + global::System.IEquatable, + global::System.Numerics.IEqualityOperators + { + private static readonly int _typeHashCode = typeof(global::Thinktecture.Tests.TestUnion).GetHashCode(); + + private readonly int _valueIndex; + + private readonly string? _string; + private readonly int _int32; + + /// + /// Indication whether the current value is of type string. + /// + public bool IsString => this._valueIndex == 1; + + /// + /// Indication whether the current value is of type int. + /// + public bool IsInt32 => this._valueIndex == 2; + + /// + /// Gets the current value as string. + /// + /// If the current value is not of type string. + public string AsString => IsString ? this._string! : throw new global::System.InvalidOperationException($"'{nameof(global::Thinktecture.Tests.TestUnion)}' is not of type 'string'."); + + /// + /// Gets the current value as int. + /// + /// If the current value is not of type int. + public int AsInt32 => IsInt32 ? this._int32 : throw new global::System.InvalidOperationException($"'{nameof(global::Thinktecture.Tests.TestUnion)}' is not of type 'int'."); + + /// + /// Gets the current value as . + /// + public object Value => this._valueIndex switch + { + 1 => this._string!, + 2 => this._int32, + _ => throw new global::System.IndexOutOfRangeException($"Unexpected value index '{this._valueIndex}'.") + }; + + /// + /// Initializes new instance with . + /// + /// Value to create a new instance for. + public TestUnion(string @string) + { + this._string = @string; + this._valueIndex = 1; + } + + /// + /// Initializes new instance with . + /// + /// Value to create a new instance for. + public TestUnion(int int32) + { + this._int32 = int32; + this._valueIndex = 2; + } + + /// + /// Executes an action depending on the current value. + /// + /// The action to execute if the current value is of type string. + /// The action to execute if the current value is of type int. + public void Switch( + global::System.Action @string, + global::System.Action int32) + { + switch (this._valueIndex) + { + case 1: + @string(this._string!); + return; + case 2: + int32(this._int32); + return; + default: + throw new global::System.IndexOutOfRangeException($"Unexpected value index '{this._valueIndex}'."); + } + } + + /// + /// Executes an action depending on the current value. + /// + /// Context to be passed to the callbacks. + /// The action to execute if the current value is of type string. + /// The action to execute if the current value is of type int. + public void Switch( + TContext context, + global::System.Action @string, + global::System.Action int32) + { + switch (this._valueIndex) + { + case 1: + @string(context, this._string!); + return; + case 2: + int32(context, this._int32); + return; + default: + throw new global::System.IndexOutOfRangeException($"Unexpected value index '{this._valueIndex}'."); + } + } + + /// + /// Executes a function depending on the current value. + /// + /// The function to execute if the current value is of type string. + /// The function to execute if the current value is of type int. + public TResult Switch( + global::System.Func @string, + global::System.Func int32) + { + switch (this._valueIndex) + { + case 1: + return @string(this._string!); + case 2: + return int32(this._int32); + default: + throw new global::System.IndexOutOfRangeException($"Unexpected value index '{this._valueIndex}'."); + } + } + + /// + /// Executes a function depending on the current value. + /// + /// Context to be passed to the callbacks. + /// The function to execute if the current value is of type string. + /// The function to execute if the current value is of type int. + public TResult Switch( + TContext context, + global::System.Func @string, + global::System.Func int32) + { + switch (this._valueIndex) + { + case 1: + return @string(context, this._string!); + case 2: + return int32(context, this._int32); + default: + throw new global::System.IndexOutOfRangeException($"Unexpected value index '{this._valueIndex}'."); + } + } + + /// + /// Maps current value to an instance of type . + /// + /// The instance to return if the current value is of type string. + /// The instance to return if the current value is of type int. + public TResult Map( + TResult @string, + TResult int32) + { + switch (this._valueIndex) + { + case 1: + return @string; + case 2: + return int32; + default: + throw new global::System.ArgumentOutOfRangeException($"Unexpected value index '{this._valueIndex}'."); + } + } + + /// + /// Implicit conversion from type string. + /// + /// Value to covert from. + /// A new instance of converted from . + public static implicit operator global::Thinktecture.Tests.TestUnion(string value) + { + return new global::Thinktecture.Tests.TestUnion(value); + } + + /// + /// Implicit conversion from type int. + /// + /// Value to covert from. + /// A new instance of converted from . + public static implicit operator global::Thinktecture.Tests.TestUnion(int value) + { + return new global::Thinktecture.Tests.TestUnion(value); + } + + /// + /// Implicit conversion to type string. + /// + /// Object to covert. + /// Inner value of type string. + /// If the inner value is not a string. + public static explicit operator string(global::Thinktecture.Tests.TestUnion obj) + { + return obj.AsString; + } + + /// + /// Implicit conversion to type int. + /// + /// Object to covert. + /// Inner value of type int. + /// If the inner value is not a int. + public static explicit operator int(global::Thinktecture.Tests.TestUnion obj) + { + return obj.AsInt32; + } + + /// + /// Compares two instances of . + /// + /// Instance to compare. + /// Another instance to compare. + /// true if objects are equal; otherwise false. + public static bool operator ==(global::Thinktecture.Tests.TestUnion obj, global::Thinktecture.Tests.TestUnion other) + { + return obj.Equals(other); + } + + /// + /// Compares two instances of . + /// + /// Instance to compare. + /// Another instance to compare. + /// false if objects are equal; otherwise true. + public static bool operator !=(global::Thinktecture.Tests.TestUnion obj, global::Thinktecture.Tests.TestUnion other) + { + return !(obj == other); + } + + /// + public override bool Equals(object? other) + { + return other is global::Thinktecture.Tests.TestUnion obj && Equals(obj); + } + + /// + public bool Equals(global::Thinktecture.Tests.TestUnion other) + { + if (this._valueIndex != other._valueIndex) + return false; + + return this._valueIndex switch + { + 1 => this._string is null ? other._string is null : this._string.Equals(other._string, global::System.StringComparison.OrdinalIgnoreCase), + 2 => this._int32.Equals(other._int32), + _ => throw new global::System.IndexOutOfRangeException($"Unexpected value index '{this._valueIndex}'.") + }; + } + + /// + public override int GetHashCode() + { + return this._valueIndex switch + { + 1 => global::System.HashCode.Combine(global::Thinktecture.Tests.TestUnion._typeHashCode, this._string?.GetHashCode(global::System.StringComparison.OrdinalIgnoreCase) ?? 0), + 2 => global::System.HashCode.Combine(global::Thinktecture.Tests.TestUnion._typeHashCode, this._int32.GetHashCode()), + _ => throw new global::System.IndexOutOfRangeException($"Unexpected value index '{this._valueIndex}'.") + }; + } + + /// + public override string? ToString() + { + return this._valueIndex switch + { + 1 => this._string, + 2 => this._int32.ToString(), + _ => throw new global::System.IndexOutOfRangeException($"Unexpected value index '{this._valueIndex}'.") + }; + } + } + } + + """); + } + + [Fact] + public void Should_generate_ref_struct_with_string_and_int() + { + var source = """ + using System; + + namespace Thinktecture.Tests + { + [Union] + public ref partial struct TestUnion; + } + """; + var outputs = GetGeneratedOutputs(source, typeof(UnionAttribute<,>).Assembly); + outputs.Should().HaveCount(1); + + var mainOutput = outputs.Single(kvp => kvp.Key.Contains("Thinktecture.Tests.TestUnion.g.cs")).Value; + + AssertOutput(mainOutput, _GENERATED_HEADER + """ + namespace Thinktecture.Tests + { + [global::System.Runtime.InteropServices.StructLayout(global::System.Runtime.InteropServices.LayoutKind.Auto)] + readonly partial struct TestUnion + { + private static readonly int _typeHashCode = typeof(global::Thinktecture.Tests.TestUnion).GetHashCode(); + + private readonly int _valueIndex; + + private readonly string? _string; + private readonly int _int32; + + /// + /// Indication whether the current value is of type string. + /// + public bool IsString => this._valueIndex == 1; + + /// + /// Indication whether the current value is of type int. + /// + public bool IsInt32 => this._valueIndex == 2; + + /// + /// Gets the current value as string. + /// + /// If the current value is not of type string. + public string AsString => IsString ? this._string! : throw new global::System.InvalidOperationException($"'{nameof(global::Thinktecture.Tests.TestUnion)}' is not of type 'string'."); + + /// + /// Gets the current value as int. + /// + /// If the current value is not of type int. + public int AsInt32 => IsInt32 ? this._int32 : throw new global::System.InvalidOperationException($"'{nameof(global::Thinktecture.Tests.TestUnion)}' is not of type 'int'."); + + /// + /// Gets the current value as . + /// + public object Value => this._valueIndex switch + { + 1 => this._string!, + 2 => this._int32, + _ => throw new global::System.IndexOutOfRangeException($"Unexpected value index '{this._valueIndex}'.") + }; + + /// + /// Initializes new instance with . + /// + /// Value to create a new instance for. + public TestUnion(string @string) + { + this._string = @string; + this._valueIndex = 1; + } + + /// + /// Initializes new instance with . + /// + /// Value to create a new instance for. + public TestUnion(int int32) + { + this._int32 = int32; + this._valueIndex = 2; + } + + /// + /// Executes an action depending on the current value. + /// + /// The action to execute if the current value is of type string. + /// The action to execute if the current value is of type int. + public void Switch( + global::System.Action @string, + global::System.Action int32) + { + switch (this._valueIndex) + { + case 1: + @string(this._string!); + return; + case 2: + int32(this._int32); + return; + default: + throw new global::System.IndexOutOfRangeException($"Unexpected value index '{this._valueIndex}'."); + } + } + + /// + /// Executes an action depending on the current value. + /// + /// Context to be passed to the callbacks. + /// The action to execute if the current value is of type string. + /// The action to execute if the current value is of type int. + public void Switch( + TContext context, + global::System.Action @string, + global::System.Action int32) + { + switch (this._valueIndex) + { + case 1: + @string(context, this._string!); + return; + case 2: + int32(context, this._int32); + return; + default: + throw new global::System.IndexOutOfRangeException($"Unexpected value index '{this._valueIndex}'."); + } + } + + /// + /// Executes a function depending on the current value. + /// + /// The function to execute if the current value is of type string. + /// The function to execute if the current value is of type int. + public TResult Switch( + global::System.Func @string, + global::System.Func int32) + { + switch (this._valueIndex) + { + case 1: + return @string(this._string!); + case 2: + return int32(this._int32); + default: + throw new global::System.IndexOutOfRangeException($"Unexpected value index '{this._valueIndex}'."); + } + } + + /// + /// Executes a function depending on the current value. + /// + /// Context to be passed to the callbacks. + /// The function to execute if the current value is of type string. + /// The function to execute if the current value is of type int. + public TResult Switch( + TContext context, + global::System.Func @string, + global::System.Func int32) + { + switch (this._valueIndex) + { + case 1: + return @string(context, this._string!); + case 2: + return int32(context, this._int32); + default: + throw new global::System.IndexOutOfRangeException($"Unexpected value index '{this._valueIndex}'."); + } + } + + /// + /// Maps current value to an instance of type . + /// + /// The instance to return if the current value is of type string. + /// The instance to return if the current value is of type int. + public TResult Map( + TResult @string, + TResult int32) + { + switch (this._valueIndex) + { + case 1: + return @string; + case 2: + return int32; + default: + throw new global::System.ArgumentOutOfRangeException($"Unexpected value index '{this._valueIndex}'."); + } + } + + /// + /// Implicit conversion from type string. + /// + /// Value to covert from. + /// A new instance of converted from . + public static implicit operator global::Thinktecture.Tests.TestUnion(string value) + { + return new global::Thinktecture.Tests.TestUnion(value); + } + + /// + /// Implicit conversion from type int. + /// + /// Value to covert from. + /// A new instance of converted from . + public static implicit operator global::Thinktecture.Tests.TestUnion(int value) + { + return new global::Thinktecture.Tests.TestUnion(value); + } + + /// + /// Implicit conversion to type string. + /// + /// Object to covert. + /// Inner value of type string. + /// If the inner value is not a string. + public static explicit operator string(global::Thinktecture.Tests.TestUnion obj) + { + return obj.AsString; + } + + /// + /// Implicit conversion to type int. + /// + /// Object to covert. + /// Inner value of type int. + /// If the inner value is not a int. + public static explicit operator int(global::Thinktecture.Tests.TestUnion obj) + { + return obj.AsInt32; + } + + /// + /// Compares two instances of . + /// + /// Instance to compare. + /// Another instance to compare. + /// true if objects are equal; otherwise false. + public static bool operator ==(global::Thinktecture.Tests.TestUnion obj, global::Thinktecture.Tests.TestUnion other) + { + return obj.Equals(other); + } + + /// + /// Compares two instances of . + /// + /// Instance to compare. + /// Another instance to compare. + /// false if objects are equal; otherwise true. + public static bool operator !=(global::Thinktecture.Tests.TestUnion obj, global::Thinktecture.Tests.TestUnion other) + { + return !(obj == other); + } + + /// + public override bool Equals(object? other) + { + return false; + } + + /// + public bool Equals(global::Thinktecture.Tests.TestUnion other) + { + if (this._valueIndex != other._valueIndex) + return false; + + return this._valueIndex switch + { + 1 => this._string is null ? other._string is null : this._string.Equals(other._string, global::System.StringComparison.OrdinalIgnoreCase), + 2 => this._int32.Equals(other._int32), + _ => throw new global::System.IndexOutOfRangeException($"Unexpected value index '{this._valueIndex}'.") + }; + } + + /// + public override int GetHashCode() + { + return this._valueIndex switch + { + 1 => global::System.HashCode.Combine(global::Thinktecture.Tests.TestUnion._typeHashCode, this._string?.GetHashCode(global::System.StringComparison.OrdinalIgnoreCase) ?? 0), + 2 => global::System.HashCode.Combine(global::Thinktecture.Tests.TestUnion._typeHashCode, this._int32.GetHashCode()), + _ => throw new global::System.IndexOutOfRangeException($"Unexpected value index '{this._valueIndex}'.") + }; + } + + /// + public override string? ToString() + { + return this._valueIndex switch + { + 1 => this._string, + 2 => this._int32.ToString(), + _ => throw new global::System.IndexOutOfRangeException($"Unexpected value index '{this._valueIndex}'.") + }; + } + } + } + + """); + } + [Fact] public void Should_generate_class_with_string_and_int_without_implicit_conversion() { @@ -3975,4 +4579,5 @@ public override int GetHashCode() """); } + } diff --git a/test/Thinktecture.Runtime.Extensions.Tests.Shared/TestUnions/TestUnion_ref_struct_string_int.cs b/test/Thinktecture.Runtime.Extensions.Tests.Shared/TestUnions/TestUnion_ref_struct_string_int.cs new file mode 100644 index 00000000..89bb61e6 --- /dev/null +++ b/test/Thinktecture.Runtime.Extensions.Tests.Shared/TestUnions/TestUnion_ref_struct_string_int.cs @@ -0,0 +1,6 @@ +namespace Thinktecture.Runtime.Tests.TestUnions; + +// ReSharper disable once InconsistentNaming +[Union(SwitchMethods = SwitchMapMethodsGeneration.DefaultWithPartialOverloads, + MapMethods = SwitchMapMethodsGeneration.DefaultWithPartialOverloads)] +public ref partial struct TestUnion_ref_struct_string_int; diff --git a/test/Thinktecture.Runtime.Extensions.Tests.Shared/TestUnions/TestUnion_struct_string_int.cs b/test/Thinktecture.Runtime.Extensions.Tests.Shared/TestUnions/TestUnion_struct_string_int.cs new file mode 100644 index 00000000..1e0b74e2 --- /dev/null +++ b/test/Thinktecture.Runtime.Extensions.Tests.Shared/TestUnions/TestUnion_struct_string_int.cs @@ -0,0 +1,6 @@ +namespace Thinktecture.Runtime.Tests.TestUnions; + +// ReSharper disable once InconsistentNaming +[Union(SwitchMethods = SwitchMapMethodsGeneration.DefaultWithPartialOverloads, + MapMethods = SwitchMapMethodsGeneration.DefaultWithPartialOverloads)] +public partial struct TestUnion_struct_string_int; diff --git a/test/Thinktecture.Runtime.Extensions.Tests.Shared/TestUnions/TestUnion_struct_string_int_case_sensitive.cs b/test/Thinktecture.Runtime.Extensions.Tests.Shared/TestUnions/TestUnion_struct_string_int_case_sensitive.cs new file mode 100644 index 00000000..6128ecec --- /dev/null +++ b/test/Thinktecture.Runtime.Extensions.Tests.Shared/TestUnions/TestUnion_struct_string_int_case_sensitive.cs @@ -0,0 +1,7 @@ +using System; + +namespace Thinktecture.Runtime.Tests.TestUnions; + +// ReSharper disable once InconsistentNaming +[Union(DefaultStringComparison = StringComparison.Ordinal)] +public partial struct TestUnion_struct_string_int_case_sensitive; diff --git a/test/Thinktecture.Runtime.Extensions.Tests/UnionTests/AsValue.cs b/test/Thinktecture.Runtime.Extensions.Tests/UnionTests/AsValue.cs index 360ec68a..9c3bcf68 100644 --- a/test/Thinktecture.Runtime.Extensions.Tests/UnionTests/AsValue.cs +++ b/test/Thinktecture.Runtime.Extensions.Tests/UnionTests/AsValue.cs @@ -29,6 +29,11 @@ public void Should_return_correct_value_or_throw_exception_having_2_types() new TestUnion_class_nullable_string_nullable_int(1).AsNullableInt32.Should().Be(1); new TestUnion_class_nullable_string_nullable_int(nullableInt32: null).Invoking(u => u.AsString.Should()).Should().Throw().WithMessage("'TestUnion_class_nullable_string_nullable_int' is not of type 'string?'."); new TestUnion_class_nullable_string_nullable_int(nullableInt32: null).AsNullableInt32.Should().BeNull(); + + new TestUnion_struct_string_int("text").AsString.Should().Be("text"); + new TestUnion_struct_string_int("text").Invoking(u => u.AsInt32.Should()).Should().Throw().WithMessage("'TestUnion_struct_string_int' is not of type 'int'."); + new TestUnion_struct_string_int(1).Invoking(u => u.AsString.Should()).Should().Throw().WithMessage("'TestUnion_struct_string_int' is not of type 'string'."); + new TestUnion_struct_string_int(1).AsInt32.Should().Be(1); } [Fact] diff --git a/test/Thinktecture.Runtime.Extensions.Tests/UnionTests/EqualityOperator.cs b/test/Thinktecture.Runtime.Extensions.Tests/UnionTests/EqualityOperator.cs index b44d18f5..31e2cdc7 100644 --- a/test/Thinktecture.Runtime.Extensions.Tests/UnionTests/EqualityOperator.cs +++ b/test/Thinktecture.Runtime.Extensions.Tests/UnionTests/EqualityOperator.cs @@ -14,6 +14,10 @@ public void Should_compare_unions_with_2_types() Compare(s => new TestUnion_class_string_int(s), n => new TestUnion_class_string_int(n), s => new TestUnion_class_string_int_case_sensitive(s)); + + Compare(s => new TestUnion_struct_string_int(s), + n => new TestUnion_struct_string_int(n), + s => new TestUnion_struct_string_int_case_sensitive(s)); } [Fact] diff --git a/test/Thinktecture.Runtime.Extensions.Tests/UnionTests/Equals.cs b/test/Thinktecture.Runtime.Extensions.Tests/UnionTests/Equals.cs index 047c88d0..af7dd204 100644 --- a/test/Thinktecture.Runtime.Extensions.Tests/UnionTests/Equals.cs +++ b/test/Thinktecture.Runtime.Extensions.Tests/UnionTests/Equals.cs @@ -14,6 +14,10 @@ public void Should_compare_unions_with_2_types() Compare(s => new TestUnion_class_string_int(s), n => new TestUnion_class_string_int(n), s => new TestUnion_class_string_int_case_sensitive(s)); + + Compare(s => new TestUnion_struct_string_int(s), + n => new TestUnion_struct_string_int(n), + s => new TestUnion_struct_string_int_case_sensitive(s)); } [Fact] diff --git a/test/Thinktecture.Runtime.Extensions.Tests/UnionTests/ExplicitCasts.cs b/test/Thinktecture.Runtime.Extensions.Tests/UnionTests/ExplicitCasts.cs index d7f228ae..d40c5c65 100644 --- a/test/Thinktecture.Runtime.Extensions.Tests/UnionTests/ExplicitCasts.cs +++ b/test/Thinktecture.Runtime.Extensions.Tests/UnionTests/ExplicitCasts.cs @@ -13,6 +13,12 @@ public void Should_have_explicit_casts_to_value_having_2_types() ((int)new TestUnion_class_string_int(1)).Should().Be(1); FluentActions.Invoking(() => (string)new TestUnion_class_string_int(1)).Should().Throw().WithMessage("'TestUnion_class_string_int' is not of type 'string'."); + + ((string)new TestUnion_struct_string_int("text")).Should().Be("text"); + FluentActions.Invoking(() => (int)new TestUnion_struct_string_int("text")).Should().Throw().WithMessage("'TestUnion_struct_string_int' is not of type 'int'."); + + ((int)new TestUnion_struct_string_int(1)).Should().Be(1); + FluentActions.Invoking(() => (string)new TestUnion_struct_string_int(1)).Should().Throw().WithMessage("'TestUnion_struct_string_int' is not of type 'string'."); } [Fact] diff --git a/test/Thinktecture.Runtime.Extensions.Tests/UnionTests/GetHashCode.cs b/test/Thinktecture.Runtime.Extensions.Tests/UnionTests/GetHashCode.cs index 9333ea5b..0ac2780e 100644 --- a/test/Thinktecture.Runtime.Extensions.Tests/UnionTests/GetHashCode.cs +++ b/test/Thinktecture.Runtime.Extensions.Tests/UnionTests/GetHashCode.cs @@ -29,6 +29,10 @@ public void Should_return_hashcode_of_the_type_plus_inner_value() ComputeHashCode(new TestUnion_class_string_int_bool_guid_char(true), true); ComputeHashCode(new TestUnion_class_string_int_bool_guid_char(new Guid("15A033FD-5887-465C-97E9-72DBE78AD02C")), new Guid("15A033FD-5887-465C-97E9-72DBE78AD02C")); ComputeHashCode(new TestUnion_class_string_int_bool_guid_char('A'), 'A'); + + ComputeHashCode(new TestUnion_struct_string_int("text"), "text"); + ComputeHashCode(new TestUnion_struct_string_int("text"), "TEXT"); + ComputeHashCode(new TestUnion_struct_string_int(42), 42); } private static void ComputeHashCode(T union, T2 value) diff --git a/test/Thinktecture.Runtime.Extensions.Tests/UnionTests/ImplicitCasts.cs b/test/Thinktecture.Runtime.Extensions.Tests/UnionTests/ImplicitCasts.cs index a11cbe81..e7f2f865 100644 --- a/test/Thinktecture.Runtime.Extensions.Tests/UnionTests/ImplicitCasts.cs +++ b/test/Thinktecture.Runtime.Extensions.Tests/UnionTests/ImplicitCasts.cs @@ -13,6 +13,12 @@ public void Should_have_implicit_casts_from_value_having_2_values() TestUnion_class_string_int intUnion = 42; intUnion.Value.Should().Be(42); + + TestUnion_struct_string_int stringStructUnion = "text"; + stringStructUnion.Value.Should().Be("text"); + + TestUnion_struct_string_int intStructUnion = 42; + intStructUnion.Value.Should().Be(42); } [Fact] diff --git a/test/Thinktecture.Runtime.Extensions.Tests/UnionTests/InequalityOperator.cs b/test/Thinktecture.Runtime.Extensions.Tests/UnionTests/InequalityOperator.cs index 6fff432d..2792c903 100644 --- a/test/Thinktecture.Runtime.Extensions.Tests/UnionTests/InequalityOperator.cs +++ b/test/Thinktecture.Runtime.Extensions.Tests/UnionTests/InequalityOperator.cs @@ -14,6 +14,10 @@ public void Should_compare_unions_with_2_types() Compare(s => new TestUnion_class_string_int(s), n => new TestUnion_class_string_int(n), s => new TestUnion_class_string_int_case_sensitive(s)); + + Compare(s => new TestUnion_struct_string_int(s), + n => new TestUnion_struct_string_int(n), + s => new TestUnion_struct_string_int_case_sensitive(s)); } [Fact] diff --git a/test/Thinktecture.Runtime.Extensions.Tests/UnionTests/IsValue.cs b/test/Thinktecture.Runtime.Extensions.Tests/UnionTests/IsValue.cs index 73a6e5e4..a451232b 100644 --- a/test/Thinktecture.Runtime.Extensions.Tests/UnionTests/IsValue.cs +++ b/test/Thinktecture.Runtime.Extensions.Tests/UnionTests/IsValue.cs @@ -29,6 +29,11 @@ public void Should_use_correct_index_having_2_types() new TestUnion_class_nullable_string_nullable_int(1).IsNullableInt32.Should().BeTrue(); new TestUnion_class_nullable_string_nullable_int(nullableInt32: null).IsString.Should().BeFalse(); new TestUnion_class_nullable_string_nullable_int(nullableInt32: null).IsNullableInt32.Should().BeTrue(); + + new TestUnion_struct_string_int("text").IsString.Should().BeTrue(); + new TestUnion_struct_string_int("text").IsInt32.Should().BeFalse(); + new TestUnion_struct_string_int(1).IsString.Should().BeFalse(); + new TestUnion_struct_string_int(1).IsInt32.Should().BeTrue(); } [Fact] diff --git a/test/Thinktecture.Runtime.Extensions.Tests/UnionTests/Map.cs b/test/Thinktecture.Runtime.Extensions.Tests/UnionTests/Map.cs index 03146c90..c81b51ec 100644 --- a/test/Thinktecture.Runtime.Extensions.Tests/UnionTests/Map.cs +++ b/test/Thinktecture.Runtime.Extensions.Tests/UnionTests/Map.cs @@ -6,93 +6,117 @@ namespace Thinktecture.Runtime.Tests.UnionTests; // ReSharper disable once InconsistentNaming public class Map { - [Theory] - [InlineData(1, "text")] - [InlineData(2, 42)] - public void Should_use_correct_arg_having_2_values(int index, object expected) + public class HavingClass { - var value = index switch + [Theory] + [InlineData(1, "text")] + [InlineData(2, 42)] + public void Should_use_correct_arg_having_2_values(int index, object expected) { - 1 => new TestUnion_class_string_int("text"), - 2 => new TestUnion_class_string_int(42), - _ => throw new Exception() - }; + var value = index switch + { + 1 => new TestUnion_class_string_int("text"), + 2 => new TestUnion_class_string_int(42), + _ => throw new Exception() + }; - var calledActionOn = value.Map(@string: (object)"text", - int32: 42); + var calledActionOn = value.Map(@string: (object)"text", + int32: 42); - calledActionOn.Should().Be(expected); - } + calledActionOn.Should().Be(expected); + } - [Theory] - [InlineData(1, "text")] - [InlineData(2, 42)] - [InlineData(3, true)] - public void Should_use_correct_arg_having_3_values(int index, object expected) - { - var value = index switch + [Theory] + [InlineData(1, "text")] + [InlineData(2, 42)] + [InlineData(3, true)] + public void Should_use_correct_arg_having_3_values(int index, object expected) { - 1 => new TestUnion_class_string_int_bool("text"), - 2 => new TestUnion_class_string_int_bool(42), - 3 => new TestUnion_class_string_int_bool(true), - _ => throw new Exception() - }; + var value = index switch + { + 1 => new TestUnion_class_string_int_bool("text"), + 2 => new TestUnion_class_string_int_bool(42), + 3 => new TestUnion_class_string_int_bool(true), + _ => throw new Exception() + }; - var calledActionOn = value.Map(@string: (object)"text", - int32: 42, - boolean: true); + var calledActionOn = value.Map(@string: (object)"text", + int32: 42, + boolean: true); - calledActionOn.Should().Be(expected); - } + calledActionOn.Should().Be(expected); + } - [Theory] - [InlineData(1, "text")] - [InlineData(2, 42)] - [InlineData(3, true)] - [InlineData(4, "4CB8C761-434B-4E34-83E0-C2E1BD4FAA0B")] - public void Should_use_correct_arg_having_4_values(int index, object expected) - { - var value = index switch + [Theory] + [InlineData(1, "text")] + [InlineData(2, 42)] + [InlineData(3, true)] + [InlineData(4, "4CB8C761-434B-4E34-83E0-C2E1BD4FAA0B")] + public void Should_use_correct_arg_having_4_values(int index, object expected) + { + var value = index switch + { + 1 => new TestUnion_class_string_int_bool_guid("text"), + 2 => new TestUnion_class_string_int_bool_guid(42), + 3 => new TestUnion_class_string_int_bool_guid(true), + 4 => new TestUnion_class_string_int_bool_guid(new Guid("4CB8C761-434B-4E34-83E0-C2E1BD4FAA0B")), + _ => throw new Exception() + }; + + var calledActionOn = value.Map(@string: (object)"text", + int32: 42, + boolean: true, + guid: new Guid("4CB8C761-434B-4E34-83E0-C2E1BD4FAA0B")); + + calledActionOn.Should().Be(index == 4 ? new Guid((string)expected) : expected); + } + + [Theory] + [InlineData(1, "text")] + [InlineData(2, 42)] + [InlineData(3, true)] + [InlineData(4, "4CB8C761-434B-4E34-83E0-C2E1BD4FAA0B")] + [InlineData(5, 'A')] + public void Should_use_correct_arg_having_5_values(int index, object expected) { - 1 => new TestUnion_class_string_int_bool_guid("text"), - 2 => new TestUnion_class_string_int_bool_guid(42), - 3 => new TestUnion_class_string_int_bool_guid(true), - 4 => new TestUnion_class_string_int_bool_guid(new Guid("4CB8C761-434B-4E34-83E0-C2E1BD4FAA0B")), - _ => throw new Exception() - }; - - var calledActionOn = value.Map(@string: (object)"text", - int32: 42, - boolean: true, - guid: new Guid("4CB8C761-434B-4E34-83E0-C2E1BD4FAA0B")); - - calledActionOn.Should().Be(index == 4 ? new Guid((string)expected) : expected); + var value = index switch + { + 1 => new TestUnion_class_string_int_bool_guid_char("text"), + 2 => new TestUnion_class_string_int_bool_guid_char(42), + 3 => new TestUnion_class_string_int_bool_guid_char(true), + 4 => new TestUnion_class_string_int_bool_guid_char(new Guid("4CB8C761-434B-4E34-83E0-C2E1BD4FAA0B")), + 5 => new TestUnion_class_string_int_bool_guid_char('A'), + _ => throw new Exception() + }; + + var calledActionOn = value.Map(@string: (object)"text", + int32: 42, + boolean: true, + guid: new Guid("4CB8C761-434B-4E34-83E0-C2E1BD4FAA0B"), + @char: 'A'); + + calledActionOn.Should().Be(index == 4 ? new Guid((string)expected) : expected); + } } - [Theory] - [InlineData(1, "text")] - [InlineData(2, 42)] - [InlineData(3, true)] - [InlineData(4, "4CB8C761-434B-4E34-83E0-C2E1BD4FAA0B")] - [InlineData(5, 'A')] - public void Should_use_correct_arg_having_5_values(int index, object expected) + public class HavingStruct { - var value = index switch + [Theory] + [InlineData(1, "text")] + [InlineData(2, 42)] + public void Should_use_correct_arg_having_2_values(int index, object expected) { - 1 => new TestUnion_class_string_int_bool_guid_char("text"), - 2 => new TestUnion_class_string_int_bool_guid_char(42), - 3 => new TestUnion_class_string_int_bool_guid_char(true), - 4 => new TestUnion_class_string_int_bool_guid_char(new Guid("4CB8C761-434B-4E34-83E0-C2E1BD4FAA0B")), - 5 => new TestUnion_class_string_int_bool_guid_char('A'), - _ => throw new Exception() - }; - - var calledActionOn = value.Map(@string: (object)"text", - int32: 42, - boolean: true, - guid: new Guid("4CB8C761-434B-4E34-83E0-C2E1BD4FAA0B"), - @char: 'A'); - - calledActionOn.Should().Be(index == 4 ? new Guid((string)expected) : expected); + var value = index switch + { + 1 => new TestUnion_struct_string_int("text"), + 2 => new TestUnion_struct_string_int(42), + _ => throw new Exception() + }; + + var calledActionOn = value.Map(@string: (object)"text", + int32: 42); + + calledActionOn.Should().Be(expected); + } } } diff --git a/test/Thinktecture.Runtime.Extensions.Tests/UnionTests/MapPartially.cs b/test/Thinktecture.Runtime.Extensions.Tests/UnionTests/MapPartially.cs index 66149fab..da993d07 100644 --- a/test/Thinktecture.Runtime.Extensions.Tests/UnionTests/MapPartially.cs +++ b/test/Thinktecture.Runtime.Extensions.Tests/UnionTests/MapPartially.cs @@ -6,187 +6,230 @@ namespace Thinktecture.Runtime.Tests.UnionTests; // ReSharper disable once InconsistentNaming public class MapPartially { - [Theory] - [InlineData(1, "text")] - [InlineData(2, 42)] - public void Should_use_correct_arg_having_2_types(int index, object expected) + public class HavingClass { - var value = index switch + [Theory] + [InlineData(1, "text")] + [InlineData(2, 42)] + public void Should_use_correct_arg_having_2_types(int index, object expected) { - 1 => new TestUnion_class_string_int("text"), - 2 => new TestUnion_class_string_int(42), - _ => throw new Exception() - }; - - var calledActionOn = value.MapPartially(@default: (object)"default", - @string: "text", - int32: 42); - - calledActionOn.Should().Be(expected); - } - - [Theory] - [InlineData(1, "default")] - [InlineData(2, 42)] - public void Should_use_default_having_2_types(int index, object expected) - { - var value = index switch + var value = index switch + { + 1 => new TestUnion_class_string_int("text"), + 2 => new TestUnion_class_string_int(42), + _ => throw new Exception() + }; + + var calledActionOn = value.MapPartially(@default: (object)"default", + @string: "text", + int32: 42); + + calledActionOn.Should().Be(expected); + } + + [Theory] + [InlineData(1, "default")] + [InlineData(2, 42)] + public void Should_use_default_having_2_types(int index, object expected) { - 1 => new TestUnion_class_string_int("text"), - 2 => new TestUnion_class_string_int(42), - _ => throw new Exception() - }; - - var calledActionOn = value.MapPartially(@default: (object)"default", - int32: 42); - - calledActionOn.Should().Be(expected); - } - - [Theory] - [InlineData(1, "text")] - [InlineData(2, 42)] - [InlineData(3, true)] - public void Should_use_correct_arg_having_3_types(int index, object expected) - { - var value = index switch + var value = index switch + { + 1 => new TestUnion_class_string_int("text"), + 2 => new TestUnion_class_string_int(42), + _ => throw new Exception() + }; + + var calledActionOn = value.MapPartially(@default: (object)"default", + int32: 42); + + calledActionOn.Should().Be(expected); + } + + [Theory] + [InlineData(1, "text")] + [InlineData(2, 42)] + [InlineData(3, true)] + public void Should_use_correct_arg_having_3_types(int index, object expected) { - 1 => new TestUnion_class_string_int_bool("text"), - 2 => new TestUnion_class_string_int_bool(42), - 3 => new TestUnion_class_string_int_bool(true), - _ => throw new Exception() - }; - - var calledActionOn = value.MapPartially(@default: (object)"default", - @string: "text", - int32: 42, - boolean: true); - - calledActionOn.Should().Be(expected); - } - - [Theory] - [InlineData(1, "default")] - [InlineData(2, 42)] - [InlineData(3, true)] - public void Should_use_default_having_3_types(int index, object expected) - { - var value = index switch + var value = index switch + { + 1 => new TestUnion_class_string_int_bool("text"), + 2 => new TestUnion_class_string_int_bool(42), + 3 => new TestUnion_class_string_int_bool(true), + _ => throw new Exception() + }; + + var calledActionOn = value.MapPartially(@default: (object)"default", + @string: "text", + int32: 42, + boolean: true); + + calledActionOn.Should().Be(expected); + } + + [Theory] + [InlineData(1, "default")] + [InlineData(2, 42)] + [InlineData(3, true)] + public void Should_use_default_having_3_types(int index, object expected) { - 1 => new TestUnion_class_string_int_bool("text"), - 2 => new TestUnion_class_string_int_bool(42), - 3 => new TestUnion_class_string_int_bool(true), - _ => throw new Exception() - }; - - var calledActionOn = value.MapPartially(@default: (object)"default", - int32: 42, - boolean: true); - - calledActionOn.Should().Be(expected); - } - - [Theory] - [InlineData(1, "text")] - [InlineData(2, 42)] - [InlineData(3, true)] - [InlineData(4, "FCF57B31-DA82-475A-8418-5161AA1A4280")] - public void Should_use_correct_arg_having_4_types(int index, object expected) - { - var value = index switch + var value = index switch + { + 1 => new TestUnion_class_string_int_bool("text"), + 2 => new TestUnion_class_string_int_bool(42), + 3 => new TestUnion_class_string_int_bool(true), + _ => throw new Exception() + }; + + var calledActionOn = value.MapPartially(@default: (object)"default", + int32: 42, + boolean: true); + + calledActionOn.Should().Be(expected); + } + + [Theory] + [InlineData(1, "text")] + [InlineData(2, 42)] + [InlineData(3, true)] + [InlineData(4, "FCF57B31-DA82-475A-8418-5161AA1A4280")] + public void Should_use_correct_arg_having_4_types(int index, object expected) { - 1 => new TestUnion_class_string_int_bool_guid("text"), - 2 => new TestUnion_class_string_int_bool_guid(42), - 3 => new TestUnion_class_string_int_bool_guid(true), - 4 => new TestUnion_class_string_int_bool_guid(new Guid("FCF57B31-DA82-475A-8418-5161AA1A4280")), - _ => throw new Exception() - }; - - var calledActionOn = value.MapPartially(@default: (object)"default", - @string: "text", - int32: 42, - boolean: true, - guid: new Guid("FCF57B31-DA82-475A-8418-5161AA1A4280")); - - calledActionOn.Should().Be(index == 4 ? new Guid((string)expected) : expected); - } - - [Theory] - [InlineData(1, "default")] - [InlineData(2, 42)] - [InlineData(3, true)] - [InlineData(4, "FCF57B31-DA82-475A-8418-5161AA1A4280")] - public void Should_use_default_having_4_types(int index, object expected) - { - var value = index switch + var value = index switch + { + 1 => new TestUnion_class_string_int_bool_guid("text"), + 2 => new TestUnion_class_string_int_bool_guid(42), + 3 => new TestUnion_class_string_int_bool_guid(true), + 4 => new TestUnion_class_string_int_bool_guid(new Guid("FCF57B31-DA82-475A-8418-5161AA1A4280")), + _ => throw new Exception() + }; + + var calledActionOn = value.MapPartially(@default: (object)"default", + @string: "text", + int32: 42, + boolean: true, + guid: new Guid("FCF57B31-DA82-475A-8418-5161AA1A4280")); + + calledActionOn.Should().Be(index == 4 ? new Guid((string)expected) : expected); + } + + [Theory] + [InlineData(1, "default")] + [InlineData(2, 42)] + [InlineData(3, true)] + [InlineData(4, "FCF57B31-DA82-475A-8418-5161AA1A4280")] + public void Should_use_default_having_4_types(int index, object expected) { - 1 => new TestUnion_class_string_int_bool_guid("text"), - 2 => new TestUnion_class_string_int_bool_guid(42), - 3 => new TestUnion_class_string_int_bool_guid(true), - 4 => new TestUnion_class_string_int_bool_guid(new Guid("FCF57B31-DA82-475A-8418-5161AA1A4280")), - _ => throw new Exception() - }; - - var calledActionOn = value.MapPartially(@default: (object)"default", - int32: 42, - boolean: true, - guid: new Guid("FCF57B31-DA82-475A-8418-5161AA1A4280")); - - calledActionOn.Should().Be(index == 4 ? new Guid((string)expected) : expected); - } - - [Theory] - [InlineData(1, "text")] - [InlineData(2, 42)] - [InlineData(3, true)] - [InlineData(4, "FCF57B31-DA82-475A-8418-5161AA1A4280")] - [InlineData(5, 'A')] - public void Should_use_correct_arg_having_5_types(int index, object expected) - { - var value = index switch + var value = index switch + { + 1 => new TestUnion_class_string_int_bool_guid("text"), + 2 => new TestUnion_class_string_int_bool_guid(42), + 3 => new TestUnion_class_string_int_bool_guid(true), + 4 => new TestUnion_class_string_int_bool_guid(new Guid("FCF57B31-DA82-475A-8418-5161AA1A4280")), + _ => throw new Exception() + }; + + var calledActionOn = value.MapPartially(@default: (object)"default", + int32: 42, + boolean: true, + guid: new Guid("FCF57B31-DA82-475A-8418-5161AA1A4280")); + + calledActionOn.Should().Be(index == 4 ? new Guid((string)expected) : expected); + } + + [Theory] + [InlineData(1, "text")] + [InlineData(2, 42)] + [InlineData(3, true)] + [InlineData(4, "FCF57B31-DA82-475A-8418-5161AA1A4280")] + [InlineData(5, 'A')] + public void Should_use_correct_arg_having_5_types(int index, object expected) + { + var value = index switch + { + 1 => new TestUnion_class_string_int_bool_guid_char("text"), + 2 => new TestUnion_class_string_int_bool_guid_char(42), + 3 => new TestUnion_class_string_int_bool_guid_char(true), + 4 => new TestUnion_class_string_int_bool_guid_char(new Guid("FCF57B31-DA82-475A-8418-5161AA1A4280")), + 5 => new TestUnion_class_string_int_bool_guid_char('A'), + _ => throw new Exception() + }; + + var calledActionOn = value.MapPartially(@default: (object)"default", + @string: "text", + int32: 42, + boolean: true, + guid: new Guid("FCF57B31-DA82-475A-8418-5161AA1A4280"), + @char: 'A'); + + calledActionOn.Should().Be(index == 4 ? new Guid((string)expected) : expected); + } + + [Theory] + [InlineData(1, "default")] + [InlineData(2, 42)] + [InlineData(3, true)] + [InlineData(4, "FCF57B31-DA82-475A-8418-5161AA1A4280")] + [InlineData(5, 'A')] + public void Should_use_default_having_5_types(int index, object expected) { - 1 => new TestUnion_class_string_int_bool_guid_char("text"), - 2 => new TestUnion_class_string_int_bool_guid_char(42), - 3 => new TestUnion_class_string_int_bool_guid_char(true), - 4 => new TestUnion_class_string_int_bool_guid_char(new Guid("FCF57B31-DA82-475A-8418-5161AA1A4280")), - 5 => new TestUnion_class_string_int_bool_guid_char('A'), - _ => throw new Exception() - }; - - var calledActionOn = value.MapPartially(@default: (object)"default", - @string: "text", - int32: 42, - boolean: true, - guid: new Guid("FCF57B31-DA82-475A-8418-5161AA1A4280"), - @char: 'A'); - - calledActionOn.Should().Be(index == 4 ? new Guid((string)expected) : expected); + var value = index switch + { + 1 => new TestUnion_class_string_int_bool_guid_char("text"), + 2 => new TestUnion_class_string_int_bool_guid_char(42), + 3 => new TestUnion_class_string_int_bool_guid_char(true), + 4 => new TestUnion_class_string_int_bool_guid_char(new Guid("FCF57B31-DA82-475A-8418-5161AA1A4280")), + 5 => new TestUnion_class_string_int_bool_guid_char('A'), + _ => throw new Exception() + }; + + var calledActionOn = value.MapPartially(@default: (object)"default", + int32: 42, + boolean: true, + guid: new Guid("FCF57B31-DA82-475A-8418-5161AA1A4280"), + @char: 'A'); + + calledActionOn.Should().Be(index == 4 ? new Guid((string)expected) : expected); + } } - [Theory] - [InlineData(1, "default")] - [InlineData(2, 42)] - [InlineData(3, true)] - [InlineData(4, "FCF57B31-DA82-475A-8418-5161AA1A4280")] - [InlineData(5, 'A')] - public void Should_use_default_having_5_types(int index, object expected) + public class HavingStruct { - var value = index switch + [Theory] + [InlineData(1, "text")] + [InlineData(2, 42)] + public void Should_use_correct_arg_having_2_types(int index, object expected) + { + var value = index switch + { + 1 => new TestUnion_struct_string_int("text"), + 2 => new TestUnion_struct_string_int(42), + _ => throw new Exception() + }; + + var calledActionOn = value.MapPartially(@default: (object)"default", + @string: "text", + int32: 42); + + calledActionOn.Should().Be(expected); + } + + [Theory] + [InlineData(1, "default")] + [InlineData(2, 42)] + public void Should_use_default_having_2_types(int index, object expected) { - 1 => new TestUnion_class_string_int_bool_guid_char("text"), - 2 => new TestUnion_class_string_int_bool_guid_char(42), - 3 => new TestUnion_class_string_int_bool_guid_char(true), - 4 => new TestUnion_class_string_int_bool_guid_char(new Guid("FCF57B31-DA82-475A-8418-5161AA1A4280")), - 5 => new TestUnion_class_string_int_bool_guid_char('A'), - _ => throw new Exception() - }; - - var calledActionOn = value.MapPartially(@default: (object)"default", - int32: 42, - boolean: true, - guid: new Guid("FCF57B31-DA82-475A-8418-5161AA1A4280"), - @char: 'A'); - - calledActionOn.Should().Be(index == 4 ? new Guid((string)expected) : expected); + var value = index switch + { + 1 => new TestUnion_struct_string_int("text"), + 2 => new TestUnion_struct_string_int(42), + _ => throw new Exception() + }; + + var calledActionOn = value.MapPartially(@default: (object)"default", + int32: 42); + + calledActionOn.Should().Be(expected); + } } } diff --git a/test/Thinktecture.Runtime.Extensions.Tests/UnionTests/Switch.cs b/test/Thinktecture.Runtime.Extensions.Tests/UnionTests/Switch.cs index 16c9a3d9..6ee45312 100644 --- a/test/Thinktecture.Runtime.Extensions.Tests/UnionTests/Switch.cs +++ b/test/Thinktecture.Runtime.Extensions.Tests/UnionTests/Switch.cs @@ -6,561 +6,681 @@ namespace Thinktecture.Runtime.Tests.UnionTests; // ReSharper disable once InconsistentNaming public class Switch { - public class WithAction + public class HavingClass { - [Theory] - [InlineData(1, "text")] - [InlineData(2, 42)] - public void Should_use_correct_arg_having_2_values(int index, object expected) + public class WithAction { - var value = index switch + [Theory] + [InlineData(1, "text")] + [InlineData(2, 42)] + public void Should_use_correct_arg_having_2_values(int index, object expected) { - 1 => new TestUnion_class_string_int("text"), - 2 => new TestUnion_class_string_int(42), - _ => throw new Exception() - }; - object calledActionOn = null; - - value.Switch(@string: v => - { - calledActionOn = v; - }, - int32: v => - { - calledActionOn = v; - }); - - calledActionOn.Should().Be(expected); - } - - [Theory] - [InlineData(1, "text")] - [InlineData(2, 42)] - [InlineData(3, true)] - public void Should_use_correct_arg_having_3_values(int index, object expected) - { - var value = index switch + var value = index switch + { + 1 => new TestUnion_class_string_int("text"), + 2 => new TestUnion_class_string_int(42), + _ => throw new Exception() + }; + object calledActionOn = null; + + value.Switch(@string: v => + { + calledActionOn = v; + }, + int32: v => + { + calledActionOn = v; + }); + + calledActionOn.Should().Be(expected); + } + + [Theory] + [InlineData(1, "text")] + [InlineData(2, 42)] + [InlineData(3, true)] + public void Should_use_correct_arg_having_3_values(int index, object expected) { - 1 => new TestUnion_class_string_int_bool("text"), - 2 => new TestUnion_class_string_int_bool(42), - 3 => new TestUnion_class_string_int_bool(true), - _ => throw new Exception() - }; - object calledActionOn = null; - - value.Switch(@string: v => - { - calledActionOn = v; - }, - int32: v => - { - calledActionOn = v; - }, - boolean: v => + var value = index switch + { + 1 => new TestUnion_class_string_int_bool("text"), + 2 => new TestUnion_class_string_int_bool(42), + 3 => new TestUnion_class_string_int_bool(true), + _ => throw new Exception() + }; + object calledActionOn = null; + + value.Switch(@string: v => + { + calledActionOn = v; + }, + int32: v => + { + calledActionOn = v; + }, + boolean: v => + { + calledActionOn = v; + }); + + calledActionOn.Should().Be(expected); + } + + [Theory] + [InlineData(1, "text")] + [InlineData(2, 42)] + [InlineData(3, true)] + [InlineData(4, "2A986EEB-1B82-46F8-A7F3-401ADC22BE33")] + public void Should_use_correct_arg_having_4_values(int index, object expected) + { + var value = index switch + { + 1 => new TestUnion_class_string_int_bool_guid("text"), + 2 => new TestUnion_class_string_int_bool_guid(42), + 3 => new TestUnion_class_string_int_bool_guid(true), + 4 => new TestUnion_class_string_int_bool_guid(new Guid("2A986EEB-1B82-46F8-A7F3-401ADC22BE33")), + _ => throw new Exception() + }; + object calledActionOn = null; + + value.Switch(@string: v => + { + calledActionOn = v; + }, + int32: v => + { + calledActionOn = v; + }, + boolean: v => + { + calledActionOn = v; + }, + guid: v => { calledActionOn = v; }); - calledActionOn.Should().Be(expected); - } + calledActionOn.Should().Be(index == 4 ? new Guid((string)expected) : expected); + } - [Theory] - [InlineData(1, "text")] - [InlineData(2, 42)] - [InlineData(3, true)] - [InlineData(4, "2A986EEB-1B82-46F8-A7F3-401ADC22BE33")] - public void Should_use_correct_arg_having_4_values(int index, object expected) - { - var value = index switch + [Theory] + [InlineData(1, "text")] + [InlineData(2, 42)] + [InlineData(3, true)] + [InlineData(4, "2A986EEB-1B82-46F8-A7F3-401ADC22BE33")] + [InlineData(5, 'A')] + public void Should_use_correct_arg_having_5_values(int index, object expected) { - 1 => new TestUnion_class_string_int_bool_guid("text"), - 2 => new TestUnion_class_string_int_bool_guid(42), - 3 => new TestUnion_class_string_int_bool_guid(true), - 4 => new TestUnion_class_string_int_bool_guid(new Guid("2A986EEB-1B82-46F8-A7F3-401ADC22BE33")), - _ => throw new Exception() - }; - object calledActionOn = null; - - value.Switch(@string: v => - { - calledActionOn = v; - }, - int32: v => - { - calledActionOn = v; - }, - boolean: v => + var value = index switch + { + 1 => new TestUnion_class_string_int_bool_guid_char("text"), + 2 => new TestUnion_class_string_int_bool_guid_char(42), + 3 => new TestUnion_class_string_int_bool_guid_char(true), + 4 => new TestUnion_class_string_int_bool_guid_char(new Guid("2A986EEB-1B82-46F8-A7F3-401ADC22BE33")), + 5 => new TestUnion_class_string_int_bool_guid_char('A'), + _ => throw new Exception() + }; + object calledActionOn = null; + + value.Switch(@string: v => + { + calledActionOn = v; + }, + int32: v => + { + calledActionOn = v; + }, + boolean: v => + { + calledActionOn = v; + }, + guid: v => { calledActionOn = v; }, - guid: v => - { - calledActionOn = v; - }); + @char: v => + { + calledActionOn = v; + }); - calledActionOn.Should().Be(index == 4 ? new Guid((string)expected) : expected); + calledActionOn.Should().Be(index == 4 ? new Guid((string)expected) : expected); + } } - [Theory] - [InlineData(1, "text")] - [InlineData(2, 42)] - [InlineData(3, true)] - [InlineData(4, "2A986EEB-1B82-46F8-A7F3-401ADC22BE33")] - [InlineData(5, 'A')] - public void Should_use_correct_arg_having_5_values(int index, object expected) + public class WithActionAndContext { - var value = index switch + [Theory] + [InlineData(1, "text")] + [InlineData(2, 42)] + public void Should_pass_context_having_2_types(int index, object expected) { - 1 => new TestUnion_class_string_int_bool_guid_char("text"), - 2 => new TestUnion_class_string_int_bool_guid_char(42), - 3 => new TestUnion_class_string_int_bool_guid_char(true), - 4 => new TestUnion_class_string_int_bool_guid_char(new Guid("2A986EEB-1B82-46F8-A7F3-401ADC22BE33")), - 5 => new TestUnion_class_string_int_bool_guid_char('A'), - _ => throw new Exception() - }; - object calledActionOn = null; - - value.Switch(@string: v => - { - calledActionOn = v; - }, - int32: v => - { - calledActionOn = v; - }, - boolean: v => - { - calledActionOn = v; - }, - guid: v => - { - calledActionOn = v; - }, - @char: v => - { - calledActionOn = v; - }); - - calledActionOn.Should().Be(index == 4 ? new Guid((string)expected) : expected); - } - } - - public class WithActionAndContext - { - [Theory] - [InlineData(1, "text")] - [InlineData(2, 42)] - public void Should_pass_context_having_2_types(int index, object expected) - { - var value = index switch + var value = index switch + { + 1 => new TestUnion_class_string_int("text"), + 2 => new TestUnion_class_string_int(42), + _ => throw new Exception() + }; + + var context = new object(); + + object calledActionOn = null; + + value.Switch(context, + @string: (ctx, v) => + { + ctx.Should().Be(context); + calledActionOn = v; + }, + int32: (ctx, v) => + { + ctx.Should().Be(context); + calledActionOn = v; + }); + + calledActionOn.Should().Be(expected); + } + + [Theory] + [InlineData(1, "text")] + [InlineData(2, 42)] + [InlineData(3, true)] + public void Should_pass_context_having_3_types(int index, object expected) { - 1 => new TestUnion_class_string_int("text"), - 2 => new TestUnion_class_string_int(42), - _ => throw new Exception() - }; - - var context = new object(); - - object calledActionOn = null; - - value.Switch(context, - @string: (ctx, v) => - { - ctx.Should().Be(context); - calledActionOn = v; - }, - int32: (ctx, v) => - { - ctx.Should().Be(context); - calledActionOn = v; - }); - - calledActionOn.Should().Be(expected); - } - - [Theory] - [InlineData(1, "text")] - [InlineData(2, 42)] - [InlineData(3, true)] - public void Should_pass_context_having_3_types(int index, object expected) - { - var value = index switch + var value = index switch + { + 1 => new TestUnion_class_string_int_bool("text"), + 2 => new TestUnion_class_string_int_bool(42), + 3 => new TestUnion_class_string_int_bool(true), + _ => throw new Exception() + }; + + var context = new object(); + + object calledActionOn = null; + + value.Switch(context, + @string: (ctx, v) => + { + ctx.Should().Be(context); + calledActionOn = v; + }, + int32: (ctx, v) => + { + ctx.Should().Be(context); + calledActionOn = v; + }, + boolean: (ctx, v) => + { + ctx.Should().Be(context); + calledActionOn = v; + }); + + calledActionOn.Should().Be(expected); + } + + [Theory] + [InlineData(1, "text")] + [InlineData(2, 42)] + [InlineData(3, true)] + [InlineData(4, "3E85ABD4-621A-4F58-8926-A842D71BB230")] + public void Should_pass_context_having_4_types(int index, object expected) { - 1 => new TestUnion_class_string_int_bool("text"), - 2 => new TestUnion_class_string_int_bool(42), - 3 => new TestUnion_class_string_int_bool(true), - _ => throw new Exception() - }; - - var context = new object(); - - object calledActionOn = null; - - value.Switch(context, - @string: (ctx, v) => - { - ctx.Should().Be(context); - calledActionOn = v; - }, - int32: (ctx, v) => - { - ctx.Should().Be(context); - calledActionOn = v; - }, - boolean: (ctx, v) => + var value = index switch + { + 1 => new TestUnion_class_string_int_bool_guid("text"), + 2 => new TestUnion_class_string_int_bool_guid(42), + 3 => new TestUnion_class_string_int_bool_guid(true), + 4 => new TestUnion_class_string_int_bool_guid(new Guid("3E85ABD4-621A-4F58-8926-A842D71BB230")), + _ => throw new Exception() + }; + + var context = new object(); + + object calledActionOn = null; + + value.Switch(context, + @string: (ctx, v) => + { + ctx.Should().Be(context); + calledActionOn = v; + }, + int32: (ctx, v) => + { + ctx.Should().Be(context); + calledActionOn = v; + }, + boolean: (ctx, v) => + { + ctx.Should().Be(context); + calledActionOn = v; + }, + guid: (ctx, v) => { ctx.Should().Be(context); calledActionOn = v; }); - calledActionOn.Should().Be(expected); - } + calledActionOn.Should().Be(index == 4 ? new Guid((string)expected) : expected); + } - [Theory] - [InlineData(1, "text")] - [InlineData(2, 42)] - [InlineData(3, true)] - [InlineData(4, "3E85ABD4-621A-4F58-8926-A842D71BB230")] - public void Should_pass_context_having_4_types(int index, object expected) - { - var value = index switch + [Theory] + [InlineData(1, "text")] + [InlineData(2, 42)] + [InlineData(3, true)] + [InlineData(4, "3E85ABD4-621A-4F58-8926-A842D71BB230")] + [InlineData(5, 'A')] + public void Should_pass_context_having_5_types(int index, object expected) { - 1 => new TestUnion_class_string_int_bool_guid("text"), - 2 => new TestUnion_class_string_int_bool_guid(42), - 3 => new TestUnion_class_string_int_bool_guid(true), - 4 => new TestUnion_class_string_int_bool_guid(new Guid("3E85ABD4-621A-4F58-8926-A842D71BB230")), - _ => throw new Exception() - }; - - var context = new object(); - - object calledActionOn = null; - - value.Switch(context, - @string: (ctx, v) => + var value = index switch + { + 1 => new TestUnion_class_string_int_bool_guid_char("text"), + 2 => new TestUnion_class_string_int_bool_guid_char(42), + 3 => new TestUnion_class_string_int_bool_guid_char(true), + 4 => new TestUnion_class_string_int_bool_guid_char(new Guid("3E85ABD4-621A-4F58-8926-A842D71BB230")), + 5 => new TestUnion_class_string_int_bool_guid_char('A'), + _ => throw new Exception() + }; + + var context = new object(); + + object calledActionOn = null; + + value.Switch(context, + @string: (ctx, v) => + { + ctx.Should().Be(context); + calledActionOn = v; + }, + int32: (ctx, v) => + { + ctx.Should().Be(context); + calledActionOn = v; + }, + boolean: (ctx, v) => + { + ctx.Should().Be(context); + calledActionOn = v; + }, + guid: (ctx, v) => { ctx.Should().Be(context); calledActionOn = v; }, - int32: (ctx, v) => - { - ctx.Should().Be(context); - calledActionOn = v; - }, - boolean: (ctx, v) => - { - ctx.Should().Be(context); - calledActionOn = v; - }, - guid: (ctx, v) => - { - ctx.Should().Be(context); - calledActionOn = v; - }); - - calledActionOn.Should().Be(index == 4 ? new Guid((string)expected) : expected); + @char: (ctx, v) => + { + ctx.Should().Be(context); + calledActionOn = v; + }); + + calledActionOn.Should().Be(index == 4 ? new Guid((string)expected) : expected); + } } - [Theory] - [InlineData(1, "text")] - [InlineData(2, 42)] - [InlineData(3, true)] - [InlineData(4, "3E85ABD4-621A-4F58-8926-A842D71BB230")] - [InlineData(5, 'A')] - public void Should_pass_context_having_5_types(int index, object expected) + public class WithFunc { - var value = index switch + [Theory] + [InlineData(1, "text")] + [InlineData(2, 42)] + public void Should_call_correct_arg_having_2_types(int index, object expected) { - 1 => new TestUnion_class_string_int_bool_guid_char("text"), - 2 => new TestUnion_class_string_int_bool_guid_char(42), - 3 => new TestUnion_class_string_int_bool_guid_char(true), - 4 => new TestUnion_class_string_int_bool_guid_char(new Guid("3E85ABD4-621A-4F58-8926-A842D71BB230")), - 5 => new TestUnion_class_string_int_bool_guid_char('A'), - _ => throw new Exception() - }; - - var context = new object(); - - object calledActionOn = null; - - value.Switch(context, - @string: (ctx, v) => - { - ctx.Should().Be(context); - calledActionOn = v; - }, - int32: (ctx, v) => - { - ctx.Should().Be(context); - calledActionOn = v; - }, - boolean: (ctx, v) => - { - ctx.Should().Be(context); - calledActionOn = v; - }, - guid: (ctx, v) => - { - ctx.Should().Be(context); - calledActionOn = v; - }, - @char: (ctx, v) => - { - ctx.Should().Be(context); - calledActionOn = v; - }); - - calledActionOn.Should().Be(index == 4 ? new Guid((string)expected) : expected); - } - } - - public class WithFunc - { - [Theory] - [InlineData(1, "text")] - [InlineData(2, 42)] - public void Should_call_correct_arg_having_2_types(int index, object expected) - { - var value = index switch + var value = index switch + { + 1 => new TestUnion_class_string_int("text"), + 2 => new TestUnion_class_string_int(42), + _ => throw new Exception() + }; + + var calledActionOn = value.Switch(@string: v => (object)v, + int32: v => v); + + calledActionOn.Should().Be(expected); + } + + [Theory] + [InlineData(1, "text")] + [InlineData(2, 42)] + [InlineData(3, true)] + public void Should_call_correct_arg_having_3_types(int index, object expected) { - 1 => new TestUnion_class_string_int("text"), - 2 => new TestUnion_class_string_int(42), - _ => throw new Exception() - }; - - var calledActionOn = value.Switch(@string: v => (object)v, - int32: v => v); - - calledActionOn.Should().Be(expected); - } - - [Theory] - [InlineData(1, "text")] - [InlineData(2, 42)] - [InlineData(3, true)] - public void Should_call_correct_arg_having_3_types(int index, object expected) - { - var value = index switch + var value = index switch + { + 1 => new TestUnion_class_string_int_bool("text"), + 2 => new TestUnion_class_string_int_bool(42), + 3 => new TestUnion_class_string_int_bool(true), + _ => throw new Exception() + }; + + var calledActionOn = value.Switch(@string: v => (object)v, + int32: v => v, + boolean: v => v); + + calledActionOn.Should().Be(expected); + } + + [Theory] + [InlineData(1, "text")] + [InlineData(2, 42)] + [InlineData(3, true)] + [InlineData(4, "F8002E79-5465-4797-AD3F-A6503ADF066E")] + public void Should_call_correct_arg_having_4_types(int index, object expected) { - 1 => new TestUnion_class_string_int_bool("text"), - 2 => new TestUnion_class_string_int_bool(42), - 3 => new TestUnion_class_string_int_bool(true), - _ => throw new Exception() - }; - - var calledActionOn = value.Switch(@string: v => (object)v, - int32: v => v, - boolean: v => v); - - calledActionOn.Should().Be(expected); - } - - [Theory] - [InlineData(1, "text")] - [InlineData(2, 42)] - [InlineData(3, true)] - [InlineData(4, "F8002E79-5465-4797-AD3F-A6503ADF066E")] - public void Should_call_correct_arg_having_4_types(int index, object expected) - { - var value = index switch + var value = index switch + { + 1 => new TestUnion_class_string_int_bool_guid("text"), + 2 => new TestUnion_class_string_int_bool_guid(42), + 3 => new TestUnion_class_string_int_bool_guid(true), + 4 => new TestUnion_class_string_int_bool_guid(new Guid("F8002E79-5465-4797-AD3F-A6503ADF066E")), + _ => throw new Exception() + }; + + var calledActionOn = value.Switch(@string: v => (object)v, + int32: v => v, + boolean: v => v, + guid: v => v); + + calledActionOn.Should().Be(index == 4 ? new Guid((string)expected) : expected); + } + + [Theory] + [InlineData(1, "text")] + [InlineData(2, 42)] + [InlineData(3, true)] + [InlineData(4, "F8002E79-5465-4797-AD3F-A6503ADF066E")] + [InlineData(5, 'A')] + public void Should_call_correct_arg_having_5_types(int index, object expected) { - 1 => new TestUnion_class_string_int_bool_guid("text"), - 2 => new TestUnion_class_string_int_bool_guid(42), - 3 => new TestUnion_class_string_int_bool_guid(true), - 4 => new TestUnion_class_string_int_bool_guid(new Guid("F8002E79-5465-4797-AD3F-A6503ADF066E")), - _ => throw new Exception() - }; - - var calledActionOn = value.Switch(@string: v => (object)v, - int32: v => v, - boolean: v => v, - guid: v => v); - - calledActionOn.Should().Be(index == 4 ? new Guid((string)expected) : expected); + var value = index switch + { + 1 => new TestUnion_class_string_int_bool_guid_char("text"), + 2 => new TestUnion_class_string_int_bool_guid_char(42), + 3 => new TestUnion_class_string_int_bool_guid_char(true), + 4 => new TestUnion_class_string_int_bool_guid_char(new Guid("F8002E79-5465-4797-AD3F-A6503ADF066E")), + 5 => new TestUnion_class_string_int_bool_guid_char('A'), + _ => throw new Exception() + }; + + var calledActionOn = value.Switch(@string: v => (object)v, + int32: v => v, + boolean: v => v, + guid: v => v, + @char: v => v); + + calledActionOn.Should().Be(index == 4 ? new Guid((string)expected) : expected); + } } - [Theory] - [InlineData(1, "text")] - [InlineData(2, 42)] - [InlineData(3, true)] - [InlineData(4, "F8002E79-5465-4797-AD3F-A6503ADF066E")] - [InlineData(5, 'A')] - public void Should_call_correct_arg_having_5_types(int index, object expected) + public class WithFuncAndContext { - var value = index switch + [Theory] + [InlineData(1, "text")] + [InlineData(2, 42)] + public void Should_pass_context_having_2_types(int index, object expected) { - 1 => new TestUnion_class_string_int_bool_guid_char("text"), - 2 => new TestUnion_class_string_int_bool_guid_char(42), - 3 => new TestUnion_class_string_int_bool_guid_char(true), - 4 => new TestUnion_class_string_int_bool_guid_char(new Guid("F8002E79-5465-4797-AD3F-A6503ADF066E")), - 5 => new TestUnion_class_string_int_bool_guid_char('A'), - _ => throw new Exception() - }; - - var calledActionOn = value.Switch(@string: v => (object)v, - int32: v => v, - boolean: v => v, - guid: v => v, - @char: v => v); - - calledActionOn.Should().Be(index == 4 ? new Guid((string)expected) : expected); - } - } - - public class WithFuncAndContext - { - [Theory] - [InlineData(1, "text")] - [InlineData(2, 42)] - public void Should_pass_context_having_2_types(int index, object expected) - { - var value = index switch + var value = index switch + { + 1 => new TestUnion_class_string_int("text"), + 2 => new TestUnion_class_string_int(42), + _ => throw new Exception() + }; + + var context = new object(); + var calledActionOn = value.Switch(context, + @string: (ctx, v) => + { + ctx.Should().Be(context); + return (object)v; + }, + int32: (ctx, v) => + { + ctx.Should().Be(context); + return v; + }); + + calledActionOn.Should().Be(expected); + } + + [Theory] + [InlineData(1, "text")] + [InlineData(2, 42)] + [InlineData(3, true)] + public void Should_pass_context_having_3_types(int index, object expected) { - 1 => new TestUnion_class_string_int("text"), - 2 => new TestUnion_class_string_int(42), - _ => throw new Exception() - }; - - var context = new object(); - var calledActionOn = value.Switch(context, - @string: (ctx, v) => + var value = index switch + { + 1 => new TestUnion_class_string_int_bool("text"), + 2 => new TestUnion_class_string_int_bool(42), + 3 => new TestUnion_class_string_int_bool(true), + _ => throw new Exception() + }; + + var context = new object(); + var calledActionOn = value.Switch(context, + @string: (ctx, v) => + { + ctx.Should().Be(context); + return (object)v; + }, + int32: (ctx, v) => + { + ctx.Should().Be(context); + return v; + }, + boolean: (ctx, v) => + { + ctx.Should().Be(context); + return v; + }); + + calledActionOn.Should().Be(expected); + } + + [Theory] + [InlineData(1, "text")] + [InlineData(2, 42)] + [InlineData(3, true)] + [InlineData(4, "6EF10862-7FC4-4AEB-BC92-21E798AC54D0")] + public void Should_pass_context_having_4_types(int index, object expected) + { + var value = index switch + { + 1 => new TestUnion_class_string_int_bool_guid("text"), + 2 => new TestUnion_class_string_int_bool_guid(42), + 3 => new TestUnion_class_string_int_bool_guid(true), + 4 => new TestUnion_class_string_int_bool_guid(new Guid("6EF10862-7FC4-4AEB-BC92-21E798AC54D0")), + _ => throw new Exception() + }; + + var context = new object(); + var calledActionOn = value.Switch(context, + @string: (ctx, v) => + { + ctx.Should().Be(context); + return (object)v; + }, + int32: (ctx, v) => + { + ctx.Should().Be(context); + return v; + }, + boolean: (ctx, v) => + { + ctx.Should().Be(context); + return v; + }, + guid: (ctx, v) => { ctx.Should().Be(context); - return (object)v; - }, - int32: (ctx, v) => - { - ctx.Should().Be(context); - return v; - }); + return v; + }); - calledActionOn.Should().Be(expected); - } + calledActionOn.Should().Be(index == 4 ? new Guid((string)expected) : expected); + } - [Theory] - [InlineData(1, "text")] - [InlineData(2, 42)] - [InlineData(3, true)] - public void Should_pass_context_having_3_types(int index, object expected) - { - var value = index switch + [Theory] + [InlineData(1, "text")] + [InlineData(2, 42)] + [InlineData(3, true)] + [InlineData(4, "6EF10862-7FC4-4AEB-BC92-21E798AC54D0")] + [InlineData(5, 'A')] + public void Should_pass_context_having_5_types(int index, object expected) { - 1 => new TestUnion_class_string_int_bool("text"), - 2 => new TestUnion_class_string_int_bool(42), - 3 => new TestUnion_class_string_int_bool(true), - _ => throw new Exception() - }; - - var context = new object(); - var calledActionOn = value.Switch(context, - @string: (ctx, v) => - { - ctx.Should().Be(context); - return (object)v; - }, - int32: (ctx, v) => - { - ctx.Should().Be(context); - return v; - }, - boolean: (ctx, v) => + var value = index switch + { + 1 => new TestUnion_class_string_int_bool_guid_char("text"), + 2 => new TestUnion_class_string_int_bool_guid_char(42), + 3 => new TestUnion_class_string_int_bool_guid_char(true), + 4 => new TestUnion_class_string_int_bool_guid_char(new Guid("6EF10862-7FC4-4AEB-BC92-21E798AC54D0")), + 5 => new TestUnion_class_string_int_bool_guid_char('A'), + _ => throw new Exception() + }; + + var context = new object(); + var calledActionOn = value.Switch(context, + @string: (ctx, v) => + { + ctx.Should().Be(context); + return (object)v; + }, + int32: (ctx, v) => + { + ctx.Should().Be(context); + return v; + }, + boolean: (ctx, v) => + { + ctx.Should().Be(context); + return v; + }, + guid: (ctx, v) => { ctx.Should().Be(context); return v; - }); + }, + @char: (ctx, v) => + { + ctx.Should().Be(context); + return v; + }); + + calledActionOn.Should().Be(index == 4 ? new Guid((string)expected) : expected); + } + } + } - calledActionOn.Should().Be(expected); + public class HavingStruct + { + public class WithAction + { + [Theory] + [InlineData(1, "text")] + [InlineData(2, 42)] + public void Should_use_correct_arg_having_2_values(int index, object expected) + { + var value = index switch + { + 1 => new TestUnion_struct_string_int("text"), + 2 => new TestUnion_struct_string_int(42), + _ => throw new Exception() + }; + object calledActionOn = null; + + value.Switch(@string: v => + { + calledActionOn = v; + }, + int32: v => + { + calledActionOn = v; + }); + + calledActionOn.Should().Be(expected); + } } - [Theory] - [InlineData(1, "text")] - [InlineData(2, 42)] - [InlineData(3, true)] - [InlineData(4, "6EF10862-7FC4-4AEB-BC92-21E798AC54D0")] - public void Should_pass_context_having_4_types(int index, object expected) + public class WithActionAndContext { - var value = index switch + [Theory] + [InlineData(1, "text")] + [InlineData(2, 42)] + public void Should_pass_context_having_2_types(int index, object expected) { - 1 => new TestUnion_class_string_int_bool_guid("text"), - 2 => new TestUnion_class_string_int_bool_guid(42), - 3 => new TestUnion_class_string_int_bool_guid(true), - 4 => new TestUnion_class_string_int_bool_guid(new Guid("6EF10862-7FC4-4AEB-BC92-21E798AC54D0")), - _ => throw new Exception() - }; - - var context = new object(); - var calledActionOn = value.Switch(context, - @string: (ctx, v) => - { - ctx.Should().Be(context); - return (object)v; - }, - int32: (ctx, v) => - { - ctx.Should().Be(context); - return v; - }, - boolean: (ctx, v) => - { - ctx.Should().Be(context); - return v; - }, - guid: (ctx, v) => - { - ctx.Should().Be(context); - return v; - }); + var value = index switch + { + 1 => new TestUnion_struct_string_int("text"), + 2 => new TestUnion_struct_string_int(42), + _ => throw new Exception() + }; + + var context = new object(); + + object calledActionOn = null; + + value.Switch(context, + @string: (ctx, v) => + { + ctx.Should().Be(context); + calledActionOn = v; + }, + int32: (ctx, v) => + { + ctx.Should().Be(context); + calledActionOn = v; + }); + + calledActionOn.Should().Be(expected); + } + } - calledActionOn.Should().Be(index == 4 ? new Guid((string)expected) : expected); + public class WithFunc + { + [Theory] + [InlineData(1, "text")] + [InlineData(2, 42)] + public void Should_call_correct_arg_having_2_types(int index, object expected) + { + var value = index switch + { + 1 => new TestUnion_struct_string_int("text"), + 2 => new TestUnion_struct_string_int(42), + _ => throw new Exception() + }; + + var calledActionOn = value.Switch(@string: v => (object)v, + int32: v => v); + + calledActionOn.Should().Be(expected); + } } - [Theory] - [InlineData(1, "text")] - [InlineData(2, 42)] - [InlineData(3, true)] - [InlineData(4, "6EF10862-7FC4-4AEB-BC92-21E798AC54D0")] - [InlineData(5, 'A')] - public void Should_pass_context_having_5_types(int index, object expected) + public class WithFuncAndContext { - var value = index switch + [Theory] + [InlineData(1, "text")] + [InlineData(2, 42)] + public void Should_pass_context_having_2_types(int index, object expected) { - 1 => new TestUnion_class_string_int_bool_guid_char("text"), - 2 => new TestUnion_class_string_int_bool_guid_char(42), - 3 => new TestUnion_class_string_int_bool_guid_char(true), - 4 => new TestUnion_class_string_int_bool_guid_char(new Guid("6EF10862-7FC4-4AEB-BC92-21E798AC54D0")), - 5 => new TestUnion_class_string_int_bool_guid_char('A'), - _ => throw new Exception() - }; - - var context = new object(); - var calledActionOn = value.Switch(context, - @string: (ctx, v) => - { - ctx.Should().Be(context); - return (object)v; - }, - int32: (ctx, v) => - { - ctx.Should().Be(context); - return v; - }, - boolean: (ctx, v) => - { - ctx.Should().Be(context); - return v; - }, - guid: (ctx, v) => - { - ctx.Should().Be(context); - return v; - }, - @char: (ctx, v) => - { - ctx.Should().Be(context); - return v; - }); - - calledActionOn.Should().Be(index == 4 ? new Guid((string)expected) : expected); + var value = index switch + { + 1 => new TestUnion_struct_string_int("text"), + 2 => new TestUnion_struct_string_int(42), + _ => throw new Exception() + }; + + var context = new object(); + var calledActionOn = value.Switch(context, + @string: (ctx, v) => + { + ctx.Should().Be(context); + return (object)v; + }, + int32: (ctx, v) => + { + ctx.Should().Be(context); + return v; + }); + + calledActionOn.Should().Be(expected); + } } } } diff --git a/test/Thinktecture.Runtime.Extensions.Tests/UnionTests/SwitchPartially.cs b/test/Thinktecture.Runtime.Extensions.Tests/UnionTests/SwitchPartially.cs index 519c5a22..5a8435c3 100644 --- a/test/Thinktecture.Runtime.Extensions.Tests/UnionTests/SwitchPartially.cs +++ b/test/Thinktecture.Runtime.Extensions.Tests/UnionTests/SwitchPartially.cs @@ -6,1080 +6,1308 @@ namespace Thinktecture.Runtime.Tests.UnionTests; // ReSharper disable once InconsistentNaming public class SwitchPartially { - public class WithAction + public class HavingClass { - [Theory] - [InlineData(1, "text")] - [InlineData(2, 42)] - public void Should_use_correct_arg_having_2_values(int index, object expected) + public class WithAction { - var value = index switch + [Theory] + [InlineData(1, "text")] + [InlineData(2, 42)] + public void Should_use_correct_arg_having_2_values(int index, object expected) { - 1 => new TestUnion_class_string_int("text"), - 2 => new TestUnion_class_string_int(42), - _ => throw new Exception() - }; - object calledActionOn = null; - - value.SwitchPartially(@string: v => - { - calledActionOn = v; - }, - int32: v => - { - calledActionOn = v; - }); + var value = index switch + { + 1 => new TestUnion_class_string_int("text"), + 2 => new TestUnion_class_string_int(42), + _ => throw new Exception() + }; + object calledActionOn = null; + + value.SwitchPartially(@string: v => + { + calledActionOn = v; + }, + int32: v => + { + calledActionOn = v; + }); - calledActionOn.Should().Be(expected); - } + calledActionOn.Should().Be(expected); + } - [Theory] - [InlineData(1, "text")] - [InlineData(2, 42)] - [InlineData(3, true)] - public void Should_use_correct_arg_having_3_values(int index, object expected) - { - var value = index switch + [Theory] + [InlineData(1, "text")] + [InlineData(2, 42)] + [InlineData(3, true)] + public void Should_use_correct_arg_having_3_values(int index, object expected) { - 1 => new TestUnion_class_string_int_bool("text"), - 2 => new TestUnion_class_string_int_bool(42), - 3 => new TestUnion_class_string_int_bool(true), - _ => throw new Exception() - }; - object calledActionOn = null; - - value.SwitchPartially(@string: v => - { - calledActionOn = v; - }, - int32: v => - { - calledActionOn = v; - }, - boolean: v => + var value = index switch + { + 1 => new TestUnion_class_string_int_bool("text"), + 2 => new TestUnion_class_string_int_bool(42), + 3 => new TestUnion_class_string_int_bool(true), + _ => throw new Exception() + }; + object calledActionOn = null; + + value.SwitchPartially(@string: v => + { + calledActionOn = v; + }, + int32: v => + { + calledActionOn = v; + }, + boolean: v => + { + calledActionOn = v; + }); + + calledActionOn.Should().Be(expected); + } + + [Theory] + [InlineData(1, "text")] + [InlineData(2, 42)] + [InlineData(3, true)] + [InlineData(4, "2A986EEB-1B82-46F8-A7F3-401ADC22BE33")] + public void Should_use_correct_arg_having_4_values(int index, object expected) + { + var value = index switch + { + 1 => new TestUnion_class_string_int_bool_guid("text"), + 2 => new TestUnion_class_string_int_bool_guid(42), + 3 => new TestUnion_class_string_int_bool_guid(true), + 4 => new TestUnion_class_string_int_bool_guid(new Guid("2A986EEB-1B82-46F8-A7F3-401ADC22BE33")), + _ => throw new Exception() + }; + object calledActionOn = null; + + value.SwitchPartially(@string: v => + { + calledActionOn = v; + }, + int32: v => + { + calledActionOn = v; + }, + boolean: v => + { + calledActionOn = v; + }, + guid: v => { calledActionOn = v; }); - calledActionOn.Should().Be(expected); - } + calledActionOn.Should().Be(index == 4 ? new Guid((string)expected) : expected); + } - [Theory] - [InlineData(1, "text")] - [InlineData(2, 42)] - [InlineData(3, true)] - [InlineData(4, "2A986EEB-1B82-46F8-A7F3-401ADC22BE33")] - public void Should_use_correct_arg_having_4_values(int index, object expected) - { - var value = index switch + [Theory] + [InlineData(1, "text")] + [InlineData(2, 42)] + [InlineData(3, true)] + [InlineData(4, "2A986EEB-1B82-46F8-A7F3-401ADC22BE33")] + [InlineData(5, 'A')] + public void Should_use_correct_arg_having_5_values(int index, object expected) { - 1 => new TestUnion_class_string_int_bool_guid("text"), - 2 => new TestUnion_class_string_int_bool_guid(42), - 3 => new TestUnion_class_string_int_bool_guid(true), - 4 => new TestUnion_class_string_int_bool_guid(new Guid("2A986EEB-1B82-46F8-A7F3-401ADC22BE33")), - _ => throw new Exception() - }; - object calledActionOn = null; - - value.SwitchPartially(@string: v => - { - calledActionOn = v; - }, - int32: v => - { - calledActionOn = v; - }, - boolean: v => + var value = index switch + { + 1 => new TestUnion_class_string_int_bool_guid_char("text"), + 2 => new TestUnion_class_string_int_bool_guid_char(42), + 3 => new TestUnion_class_string_int_bool_guid_char(true), + 4 => new TestUnion_class_string_int_bool_guid_char(new Guid("2A986EEB-1B82-46F8-A7F3-401ADC22BE33")), + 5 => new TestUnion_class_string_int_bool_guid_char('A'), + _ => throw new Exception() + }; + object calledActionOn = null; + + value.SwitchPartially(@string: v => + { + calledActionOn = v; + }, + int32: v => + { + calledActionOn = v; + }, + boolean: v => + { + calledActionOn = v; + }, + guid: v => { calledActionOn = v; }, - guid: v => - { - calledActionOn = v; - }); + @char: v => + { + calledActionOn = v; + }); - calledActionOn.Should().Be(index == 4 ? new Guid((string)expected) : expected); - } + calledActionOn.Should().Be(index == 4 ? new Guid((string)expected) : expected); + } - [Theory] - [InlineData(1, "text")] - [InlineData(2, 42)] - [InlineData(3, true)] - [InlineData(4, "2A986EEB-1B82-46F8-A7F3-401ADC22BE33")] - [InlineData(5, 'A')] - public void Should_use_correct_arg_having_5_values(int index, object expected) - { - var value = index switch + [Theory] + [InlineData(1, "default:text")] + [InlineData(2, 42)] + public void Should_use_default_arg_having_2_types(int index, object expected) { - 1 => new TestUnion_class_string_int_bool_guid_char("text"), - 2 => new TestUnion_class_string_int_bool_guid_char(42), - 3 => new TestUnion_class_string_int_bool_guid_char(true), - 4 => new TestUnion_class_string_int_bool_guid_char(new Guid("2A986EEB-1B82-46F8-A7F3-401ADC22BE33")), - 5 => new TestUnion_class_string_int_bool_guid_char('A'), - _ => throw new Exception() - }; - object calledActionOn = null; - - value.SwitchPartially(@string: v => - { - calledActionOn = v; - }, - int32: v => - { - calledActionOn = v; - }, - boolean: v => - { - calledActionOn = v; - }, - guid: v => - { - calledActionOn = v; - }, - @char: v => - { - calledActionOn = v; - }); - - calledActionOn.Should().Be(index == 4 ? new Guid((string)expected) : expected); - } + var value = index switch + { + 1 => new TestUnion_class_string_int("text"), + 2 => new TestUnion_class_string_int(42), + _ => throw new Exception() + }; + object calledActionOn = null; + + value.SwitchPartially(@default: v => + { + calledActionOn = $"default:{v}"; + }, + int32: v => + { + calledActionOn = v; + }); - [Theory] - [InlineData(1, "default:text")] - [InlineData(2, 42)] - public void Should_use_default_arg_having_2_types(int index, object expected) - { - var value = index switch - { - 1 => new TestUnion_class_string_int("text"), - 2 => new TestUnion_class_string_int(42), - _ => throw new Exception() - }; - object calledActionOn = null; + calledActionOn.Should().Be(expected); + } - value.SwitchPartially(@default: v => + [Theory] + [InlineData(1, "default:text")] + [InlineData(2, 42)] + [InlineData(3, true)] + public void Should_use_default_arg_having_3_types(int index, object expected) + { + var value = index switch + { + 1 => new TestUnion_class_string_int_bool("text"), + 2 => new TestUnion_class_string_int_bool(42), + 3 => new TestUnion_class_string_int_bool(true), + _ => throw new Exception() + }; + object calledActionOn = null; + + value.SwitchPartially(@default: v => + { + calledActionOn = $"default:{v}"; + }, + int32: v => { - calledActionOn = $"default:{v}"; + calledActionOn = v; }, - int32: v => - { - calledActionOn = v; - }); - - calledActionOn.Should().Be(expected); - } - - [Theory] - [InlineData(1, "default:text")] - [InlineData(2, 42)] - [InlineData(3, true)] - public void Should_use_default_arg_having_3_types(int index, object expected) - { - var value = index switch + boolean: v => + { + calledActionOn = v; + }); + + calledActionOn.Should().Be(expected); + } + + [Theory] + [InlineData(1, "default:text")] + [InlineData(2, 42)] + [InlineData(3, true)] + [InlineData(4, "DA9B90DD-AF66-4856-B084-1B1BB21DEA9B")] + public void Should_use_default_arg_having_4_types(int index, object expected) { - 1 => new TestUnion_class_string_int_bool("text"), - 2 => new TestUnion_class_string_int_bool(42), - 3 => new TestUnion_class_string_int_bool(true), - _ => throw new Exception() - }; - object calledActionOn = null; - - value.SwitchPartially(@default: v => + var value = index switch + { + 1 => new TestUnion_class_string_int_bool_guid("text"), + 2 => new TestUnion_class_string_int_bool_guid(42), + 3 => new TestUnion_class_string_int_bool_guid(true), + 4 => new TestUnion_class_string_int_bool_guid(new Guid("DA9B90DD-AF66-4856-B084-1B1BB21DEA9B")), + _ => throw new Exception() + }; + object calledActionOn = null; + + value.SwitchPartially(@default: v => + { + calledActionOn = $"default:{v}"; + }, + int32: v => { - calledActionOn = $"default:{v}"; + calledActionOn = v; }, - int32: v => - { - calledActionOn = v; - }, - boolean: v => + boolean: v => + { + calledActionOn = v; + }, + guid: v => { calledActionOn = v; }); - calledActionOn.Should().Be(expected); - } + calledActionOn.Should().Be(index == 4 ? new Guid((string)expected) : expected); + } - [Theory] - [InlineData(1, "default:text")] - [InlineData(2, 42)] - [InlineData(3, true)] - [InlineData(4, "DA9B90DD-AF66-4856-B084-1B1BB21DEA9B")] - public void Should_use_default_arg_having_4_types(int index, object expected) - { - var value = index switch + [Theory] + [InlineData(1, "default:text")] + [InlineData(2, 42)] + [InlineData(3, true)] + [InlineData(4, "DA9B90DD-AF66-4856-B084-1B1BB21DEA9B")] + [InlineData(5, 'A')] + public void Should_use_default_arg_having_5_types(int index, object expected) { - 1 => new TestUnion_class_string_int_bool_guid("text"), - 2 => new TestUnion_class_string_int_bool_guid(42), - 3 => new TestUnion_class_string_int_bool_guid(true), - 4 => new TestUnion_class_string_int_bool_guid(new Guid("DA9B90DD-AF66-4856-B084-1B1BB21DEA9B")), - _ => throw new Exception() - }; - object calledActionOn = null; - - value.SwitchPartially(@default: v => + var value = index switch + { + 1 => new TestUnion_class_string_int_bool_guid_char("text"), + 2 => new TestUnion_class_string_int_bool_guid_char(42), + 3 => new TestUnion_class_string_int_bool_guid_char(true), + 4 => new TestUnion_class_string_int_bool_guid_char(new Guid("DA9B90DD-AF66-4856-B084-1B1BB21DEA9B")), + 5 => new TestUnion_class_string_int_bool_guid_char('A'), + _ => throw new Exception() + }; + object calledActionOn = null; + + value.SwitchPartially(@default: v => + { + calledActionOn = $"default:{v}"; + }, + int32: v => { - calledActionOn = $"default:{v}"; + calledActionOn = v; }, - int32: v => - { - calledActionOn = v; - }, - boolean: v => + boolean: v => + { + calledActionOn = v; + }, + guid: v => { calledActionOn = v; }, - guid: v => - { - calledActionOn = v; - }); + @char: v => + { + calledActionOn = v; + }); - calledActionOn.Should().Be(index == 4 ? new Guid((string)expected) : expected); + calledActionOn.Should().Be(index == 4 ? new Guid((string)expected) : expected); + } } - [Theory] - [InlineData(1, "default:text")] - [InlineData(2, 42)] - [InlineData(3, true)] - [InlineData(4, "DA9B90DD-AF66-4856-B084-1B1BB21DEA9B")] - [InlineData(5, 'A')] - public void Should_use_default_arg_having_5_types(int index, object expected) + public class WithActionAndContext { - var value = index switch + [Theory] + [InlineData(1, "text")] + [InlineData(2, 42)] + public void Should_pass_context_having_2_types(int index, object expected) { - 1 => new TestUnion_class_string_int_bool_guid_char("text"), - 2 => new TestUnion_class_string_int_bool_guid_char(42), - 3 => new TestUnion_class_string_int_bool_guid_char(true), - 4 => new TestUnion_class_string_int_bool_guid_char(new Guid("DA9B90DD-AF66-4856-B084-1B1BB21DEA9B")), - 5 => new TestUnion_class_string_int_bool_guid_char('A'), - _ => throw new Exception() - }; - object calledActionOn = null; - - value.SwitchPartially(@default: v => + var value = index switch + { + 1 => new TestUnion_class_string_int("text"), + 2 => new TestUnion_class_string_int(42), + _ => throw new Exception() + }; + + var context = new object(); + object calledActionOn = null; + + value.SwitchPartially(context, + @string: (ctx, v) => + { + ctx.Should().Be(context); + calledActionOn = v; + }, + int32: (ctx, v) => { - calledActionOn = $"default:{v}"; - }, - int32: v => - { - calledActionOn = v; - }, - boolean: v => - { - calledActionOn = v; - }, - guid: v => - { - calledActionOn = v; - }, - @char: v => - { - calledActionOn = v; - }); - - calledActionOn.Should().Be(index == 4 ? new Guid((string)expected) : expected); - } - } - - public class WithActionAndContext - { - [Theory] - [InlineData(1, "text")] - [InlineData(2, 42)] - public void Should_pass_context_having_2_types(int index, object expected) - { - var value = index switch - { - 1 => new TestUnion_class_string_int("text"), - 2 => new TestUnion_class_string_int(42), - _ => throw new Exception() - }; - - var context = new object(); - object calledActionOn = null; - - value.SwitchPartially(context, - @string: (ctx, v) => - { - ctx.Should().Be(context); - calledActionOn = v; - }, - int32: (ctx, v) => - { - ctx.Should().Be(context); - calledActionOn = v; - }); + ctx.Should().Be(context); + calledActionOn = v; + }); - calledActionOn.Should().Be(expected); - } + calledActionOn.Should().Be(expected); + } - [Theory] - [InlineData(1, "text")] - [InlineData(2, 42)] - [InlineData(3, true)] - public void Should_pass_context_having_3_types(int index, object expected) - { - var value = index switch + [Theory] + [InlineData(1, "text")] + [InlineData(2, 42)] + [InlineData(3, true)] + public void Should_pass_context_having_3_types(int index, object expected) { - 1 => new TestUnion_class_string_int_bool("text"), - 2 => new TestUnion_class_string_int_bool(42), - 3 => new TestUnion_class_string_int_bool(true), - _ => throw new Exception() - }; - - var context = new object(); - object calledActionOn = null; - - value.SwitchPartially(context, - @string: (ctx, v) => - { - ctx.Should().Be(context); - calledActionOn = v; - }, - int32: (ctx, v) => - { - ctx.Should().Be(context); - calledActionOn = v; - }, - boolean: (ctx, v) => + var value = index switch + { + 1 => new TestUnion_class_string_int_bool("text"), + 2 => new TestUnion_class_string_int_bool(42), + 3 => new TestUnion_class_string_int_bool(true), + _ => throw new Exception() + }; + + var context = new object(); + object calledActionOn = null; + + value.SwitchPartially(context, + @string: (ctx, v) => + { + ctx.Should().Be(context); + calledActionOn = v; + }, + int32: (ctx, v) => + { + ctx.Should().Be(context); + calledActionOn = v; + }, + boolean: (ctx, v) => + { + ctx.Should().Be(context); + calledActionOn = v; + }); + + calledActionOn.Should().Be(expected); + } + + [Theory] + [InlineData(1, "text")] + [InlineData(2, 42)] + [InlineData(3, true)] + [InlineData(4, "9FCEBE8E-AEED-4ADE-B597-562AFB9C9733")] + public void Should_pass_context_having_5_types(int index, object expected) + { + var value = index switch + { + 1 => new TestUnion_class_string_int_bool_guid("text"), + 2 => new TestUnion_class_string_int_bool_guid(42), + 3 => new TestUnion_class_string_int_bool_guid(true), + 4 => new TestUnion_class_string_int_bool_guid(new Guid("9FCEBE8E-AEED-4ADE-B597-562AFB9C9733")), + _ => throw new Exception() + }; + + var context = new object(); + object calledActionOn = null; + + value.SwitchPartially(context, + @string: (ctx, v) => + { + ctx.Should().Be(context); + calledActionOn = v; + }, + int32: (ctx, v) => + { + ctx.Should().Be(context); + calledActionOn = v; + }, + boolean: (ctx, v) => + { + ctx.Should().Be(context); + calledActionOn = v; + }, + guid: (ctx, v) => { ctx.Should().Be(context); calledActionOn = v; }); - calledActionOn.Should().Be(expected); - } + calledActionOn.Should().Be(index == 4 ? new Guid((string)expected) : expected); + } - [Theory] - [InlineData(1, "text")] - [InlineData(2, 42)] - [InlineData(3, true)] - [InlineData(4, "9FCEBE8E-AEED-4ADE-B597-562AFB9C9733")] - public void Should_pass_context_having_5_types(int index, object expected) - { - var value = index switch + [Theory] + [InlineData(1, "default:text")] + [InlineData(2, 42)] + public void Should_pass_context_to_default_having_2_types(int index, object expected) { - 1 => new TestUnion_class_string_int_bool_guid("text"), - 2 => new TestUnion_class_string_int_bool_guid(42), - 3 => new TestUnion_class_string_int_bool_guid(true), - 4 => new TestUnion_class_string_int_bool_guid(new Guid("9FCEBE8E-AEED-4ADE-B597-562AFB9C9733")), - _ => throw new Exception() - }; - - var context = new object(); - object calledActionOn = null; - - value.SwitchPartially(context, - @string: (ctx, v) => - { - ctx.Should().Be(context); - calledActionOn = v; - }, - int32: (ctx, v) => - { - ctx.Should().Be(context); - calledActionOn = v; - }, - boolean: (ctx, v) => - { - ctx.Should().Be(context); - calledActionOn = v; - }, - guid: (ctx, v) => - { - ctx.Should().Be(context); - calledActionOn = v; - }); + var value = index switch + { + 1 => new TestUnion_class_string_int("text"), + 2 => new TestUnion_class_string_int(42), + _ => throw new Exception() + }; + + var context = new object(); + object calledActionOn = null; + + value.SwitchPartially(context, + @default: (ctx, v) => + { + ctx.Should().Be(context); + calledActionOn = $"default:{v}"; + }, + int32: (ctx, v) => + { + ctx.Should().Be(context); + calledActionOn = v; + }); - calledActionOn.Should().Be(index == 4 ? new Guid((string)expected) : expected); - } + calledActionOn.Should().Be(expected); + } - [Theory] - [InlineData(1, "default:text")] - [InlineData(2, 42)] - public void Should_pass_context_to_default_having_2_types(int index, object expected) - { - var value = index switch + [Theory] + [InlineData(1, "default:text")] + [InlineData(2, 42)] + [InlineData(3, true)] + public void Should_pass_context_to_default_having_3_types(int index, object expected) { - 1 => new TestUnion_class_string_int("text"), - 2 => new TestUnion_class_string_int(42), - _ => throw new Exception() - }; - - var context = new object(); - object calledActionOn = null; - - value.SwitchPartially(context, - @default: (ctx, v) => + var value = index switch + { + 1 => new TestUnion_class_string_int_bool("text"), + 2 => new TestUnion_class_string_int_bool(42), + 3 => new TestUnion_class_string_int_bool(true), + _ => throw new Exception() + }; + + var context = new object(); + object calledActionOn = null; + + value.SwitchPartially(context, + @default: (ctx, v) => + { + ctx.Should().Be(context); + calledActionOn = $"default:{v}"; + }, + int32: (ctx, v) => { ctx.Should().Be(context); - calledActionOn = $"default:{v}"; + calledActionOn = v; }, - int32: (ctx, v) => - { - ctx.Should().Be(context); - calledActionOn = v; - }); - - calledActionOn.Should().Be(expected); - } - - [Theory] - [InlineData(1, "default:text")] - [InlineData(2, 42)] - [InlineData(3, true)] - public void Should_pass_context_to_default_having_3_types(int index, object expected) - { - var value = index switch + boolean: (ctx, v) => + { + ctx.Should().Be(context); + calledActionOn = v; + }); + + calledActionOn.Should().Be(expected); + } + + [Theory] + [InlineData(1, "default:text")] + [InlineData(2, 42)] + [InlineData(3, true)] + [InlineData(4, "81F697B0-5B9B-4441-89EB-2970A85C1069")] + public void Should_pass_context_to_default_having_4_types(int index, object expected) { - 1 => new TestUnion_class_string_int_bool("text"), - 2 => new TestUnion_class_string_int_bool(42), - 3 => new TestUnion_class_string_int_bool(true), - _ => throw new Exception() - }; - - var context = new object(); - object calledActionOn = null; - - value.SwitchPartially(context, - @default: (ctx, v) => + var value = index switch + { + 1 => new TestUnion_class_string_int_bool_guid("text"), + 2 => new TestUnion_class_string_int_bool_guid(42), + 3 => new TestUnion_class_string_int_bool_guid(true), + 4 => new TestUnion_class_string_int_bool_guid(new Guid("81F697B0-5B9B-4441-89EB-2970A85C1069")), + _ => throw new Exception() + }; + + var context = new object(); + object calledActionOn = null; + + value.SwitchPartially(context, + @default: (ctx, v) => + { + ctx.Should().Be(context); + calledActionOn = $"default:{v}"; + }, + int32: (ctx, v) => { ctx.Should().Be(context); - calledActionOn = $"default:{v}"; + calledActionOn = v; }, - int32: (ctx, v) => - { - ctx.Should().Be(context); - calledActionOn = v; - }, - boolean: (ctx, v) => + boolean: (ctx, v) => + { + ctx.Should().Be(context); + calledActionOn = v; + }, + guid: (ctx, v) => { ctx.Should().Be(context); calledActionOn = v; }); - calledActionOn.Should().Be(expected); - } + calledActionOn.Should().Be(index == 4 ? new Guid((string)expected) : expected); + } - [Theory] - [InlineData(1, "default:text")] - [InlineData(2, 42)] - [InlineData(3, true)] - [InlineData(4, "81F697B0-5B9B-4441-89EB-2970A85C1069")] - public void Should_pass_context_to_default_having_4_types(int index, object expected) - { - var value = index switch + [Theory] + [InlineData(1, "default:text")] + [InlineData(2, 42)] + [InlineData(3, true)] + [InlineData(4, "81F697B0-5B9B-4441-89EB-2970A85C1069")] + [InlineData(5, 'A')] + public void Should_pass_context_to_default_having_5_types(int index, object expected) { - 1 => new TestUnion_class_string_int_bool_guid("text"), - 2 => new TestUnion_class_string_int_bool_guid(42), - 3 => new TestUnion_class_string_int_bool_guid(true), - 4 => new TestUnion_class_string_int_bool_guid(new Guid("81F697B0-5B9B-4441-89EB-2970A85C1069")), - _ => throw new Exception() - }; - - var context = new object(); - object calledActionOn = null; - - value.SwitchPartially(context, - @default: (ctx, v) => + var value = index switch + { + 1 => new TestUnion_class_string_int_bool_guid_char("text"), + 2 => new TestUnion_class_string_int_bool_guid_char(42), + 3 => new TestUnion_class_string_int_bool_guid_char(true), + 4 => new TestUnion_class_string_int_bool_guid_char(new Guid("81F697B0-5B9B-4441-89EB-2970A85C1069")), + 5 => new TestUnion_class_string_int_bool_guid_char('A'), + _ => throw new Exception() + }; + + var context = new object(); + object calledActionOn = null; + + value.SwitchPartially(context, + @default: (ctx, v) => + { + ctx.Should().Be(context); + calledActionOn = $"default:{v}"; + }, + int32: (ctx, v) => { ctx.Should().Be(context); - calledActionOn = $"default:{v}"; + calledActionOn = v; }, - int32: (ctx, v) => - { - ctx.Should().Be(context); - calledActionOn = v; - }, - boolean: (ctx, v) => + boolean: (ctx, v) => + { + ctx.Should().Be(context); + calledActionOn = v; + }, + guid: (ctx, v) => { ctx.Should().Be(context); calledActionOn = v; }, - guid: (ctx, v) => - { - ctx.Should().Be(context); - calledActionOn = v; - }); - - calledActionOn.Should().Be(index == 4 ? new Guid((string)expected) : expected); - } - - [Theory] - [InlineData(1, "default:text")] - [InlineData(2, 42)] - [InlineData(3, true)] - [InlineData(4, "81F697B0-5B9B-4441-89EB-2970A85C1069")] - [InlineData(5, 'A')] - public void Should_pass_context_to_default_having_5_types(int index, object expected) - { - var value = index switch - { - 1 => new TestUnion_class_string_int_bool_guid_char("text"), - 2 => new TestUnion_class_string_int_bool_guid_char(42), - 3 => new TestUnion_class_string_int_bool_guid_char(true), - 4 => new TestUnion_class_string_int_bool_guid_char(new Guid("81F697B0-5B9B-4441-89EB-2970A85C1069")), - 5 => new TestUnion_class_string_int_bool_guid_char('A'), - _ => throw new Exception() - }; - - var context = new object(); - object calledActionOn = null; - - value.SwitchPartially(context, - @default: (ctx, v) => + @char: (ctx, v) => { ctx.Should().Be(context); - calledActionOn = $"default:{v}"; - }, - int32: (ctx, v) => - { - ctx.Should().Be(context); - calledActionOn = v; - }, - boolean: (ctx, v) => - { - ctx.Should().Be(context); - calledActionOn = v; - }, - guid: (ctx, v) => - { - ctx.Should().Be(context); - calledActionOn = v; - }, - @char: (ctx, v) => - { - ctx.Should().Be(context); - calledActionOn = v; - }); - - calledActionOn.Should().Be(index == 4 ? new Guid((string)expected) : expected); - } - } + calledActionOn = v; + }); - public class WithFunc - { - [Theory] - [InlineData(1, "text")] - [InlineData(2, 42)] - public void Should_use_correct_arg_having_2_types(int index, object expected) - { - var value = index switch - { - 1 => new TestUnion_class_string_int("text"), - 2 => new TestUnion_class_string_int(42), - _ => throw new Exception() - }; - - var calledActionOn = value.SwitchPartially(@default: v => $"default:{v}", - @string: v => (object)v, - int32: v => v); - - calledActionOn.Should().Be(expected); + calledActionOn.Should().Be(index == 4 ? new Guid((string)expected) : expected); + } } - [Theory] - [InlineData(1, "text")] - [InlineData(2, 42)] - [InlineData(3, true)] - public void Should_use_correct_arg_having_3_types(int index, object expected) + public class WithFunc { - var value = index switch + [Theory] + [InlineData(1, "text")] + [InlineData(2, 42)] + public void Should_use_correct_arg_having_2_types(int index, object expected) { - 1 => new TestUnion_class_string_int_bool("text"), - 2 => new TestUnion_class_string_int_bool(42), - 3 => new TestUnion_class_string_int_bool(true), - _ => throw new Exception() - }; - - var calledActionOn = value.SwitchPartially(@default: v => $"default:{v}", - @string: v => (object)v, - int32: v => v, - boolean: v => v); - - calledActionOn.Should().Be(expected); - } - - [Theory] - [InlineData(1, "text")] - [InlineData(2, 42)] - [InlineData(3, true)] - [InlineData(4, "D4EF64BB-730B-4D5C-94A1-C019F83EF945")] - public void Should_use_correct_arg_having_4_types(int index, object expected) - { - var value = index switch + var value = index switch + { + 1 => new TestUnion_class_string_int("text"), + 2 => new TestUnion_class_string_int(42), + _ => throw new Exception() + }; + + var calledActionOn = value.SwitchPartially(@default: v => $"default:{v}", + @string: v => (object)v, + int32: v => v); + + calledActionOn.Should().Be(expected); + } + + [Theory] + [InlineData(1, "text")] + [InlineData(2, 42)] + [InlineData(3, true)] + public void Should_use_correct_arg_having_3_types(int index, object expected) { - 1 => new TestUnion_class_string_int_bool_guid("text"), - 2 => new TestUnion_class_string_int_bool_guid(42), - 3 => new TestUnion_class_string_int_bool_guid(true), - 4 => new TestUnion_class_string_int_bool_guid(new Guid("D4EF64BB-730B-4D5C-94A1-C019F83EF945")), - _ => throw new Exception() - }; - - var calledActionOn = value.SwitchPartially(@default: v => $"default:{v}", - @string: v => (object)v, - int32: v => v, - boolean: v => v, - guid: v => v); - - calledActionOn.Should().Be(index == 4 ? new Guid((string)expected) : expected); - } - - [Theory] - [InlineData(1, "text")] - [InlineData(2, 42)] - [InlineData(3, true)] - [InlineData(4, "D4EF64BB-730B-4D5C-94A1-C019F83EF945")] - [InlineData(5, 'A')] - public void Should_use_correct_arg_having_5_types(int index, object expected) - { - var value = index switch + var value = index switch + { + 1 => new TestUnion_class_string_int_bool("text"), + 2 => new TestUnion_class_string_int_bool(42), + 3 => new TestUnion_class_string_int_bool(true), + _ => throw new Exception() + }; + + var calledActionOn = value.SwitchPartially(@default: v => $"default:{v}", + @string: v => (object)v, + int32: v => v, + boolean: v => v); + + calledActionOn.Should().Be(expected); + } + + [Theory] + [InlineData(1, "text")] + [InlineData(2, 42)] + [InlineData(3, true)] + [InlineData(4, "D4EF64BB-730B-4D5C-94A1-C019F83EF945")] + public void Should_use_correct_arg_having_4_types(int index, object expected) { - 1 => new TestUnion_class_string_int_bool_guid_char("text"), - 2 => new TestUnion_class_string_int_bool_guid_char(42), - 3 => new TestUnion_class_string_int_bool_guid_char(true), - 4 => new TestUnion_class_string_int_bool_guid_char(new Guid("D4EF64BB-730B-4D5C-94A1-C019F83EF945")), - 5 => new TestUnion_class_string_int_bool_guid_char('A'), - _ => throw new Exception() - }; - - var calledActionOn = value.SwitchPartially(@default: v => $"default:{v}", - @string: v => (object)v, - int32: v => v, - boolean: v => v, - guid: v => v, - @char: v => v); - - calledActionOn.Should().Be(index == 4 ? new Guid((string)expected) : expected); - } - - [Theory] - [InlineData(1, "default:text")] - [InlineData(2, 42)] - public void Should_use_default_arg_having_2_types(int index, object expected) - { - var value = index switch + var value = index switch + { + 1 => new TestUnion_class_string_int_bool_guid("text"), + 2 => new TestUnion_class_string_int_bool_guid(42), + 3 => new TestUnion_class_string_int_bool_guid(true), + 4 => new TestUnion_class_string_int_bool_guid(new Guid("D4EF64BB-730B-4D5C-94A1-C019F83EF945")), + _ => throw new Exception() + }; + + var calledActionOn = value.SwitchPartially(@default: v => $"default:{v}", + @string: v => (object)v, + int32: v => v, + boolean: v => v, + guid: v => v); + + calledActionOn.Should().Be(index == 4 ? new Guid((string)expected) : expected); + } + + [Theory] + [InlineData(1, "text")] + [InlineData(2, 42)] + [InlineData(3, true)] + [InlineData(4, "D4EF64BB-730B-4D5C-94A1-C019F83EF945")] + [InlineData(5, 'A')] + public void Should_use_correct_arg_having_5_types(int index, object expected) { - 1 => new TestUnion_class_string_int("text"), - 2 => new TestUnion_class_string_int(42), - _ => throw new Exception() - }; - - var calledActionOn = value.SwitchPartially(@default: v => (object)$"default:{v}", - int32: v => v); - - calledActionOn.Should().Be(expected); - } - - [Theory] - [InlineData(1, "default:text")] - [InlineData(2, 42)] - [InlineData(3, true)] - public void Should_use_default_arg_having_3_types(int index, object expected) - { - var value = index switch + var value = index switch + { + 1 => new TestUnion_class_string_int_bool_guid_char("text"), + 2 => new TestUnion_class_string_int_bool_guid_char(42), + 3 => new TestUnion_class_string_int_bool_guid_char(true), + 4 => new TestUnion_class_string_int_bool_guid_char(new Guid("D4EF64BB-730B-4D5C-94A1-C019F83EF945")), + 5 => new TestUnion_class_string_int_bool_guid_char('A'), + _ => throw new Exception() + }; + + var calledActionOn = value.SwitchPartially(@default: v => $"default:{v}", + @string: v => (object)v, + int32: v => v, + boolean: v => v, + guid: v => v, + @char: v => v); + + calledActionOn.Should().Be(index == 4 ? new Guid((string)expected) : expected); + } + + [Theory] + [InlineData(1, "default:text")] + [InlineData(2, 42)] + public void Should_use_default_arg_having_2_types(int index, object expected) { - 1 => new TestUnion_class_string_int_bool("text"), - 2 => new TestUnion_class_string_int_bool(42), - 3 => new TestUnion_class_string_int_bool(true), - _ => throw new Exception() - }; - - var calledActionOn = value.SwitchPartially(@default: v => (object)$"default:{v}", - int32: v => v, - boolean: v => v); - - calledActionOn.Should().Be(expected); - } - - [Theory] - [InlineData(1, "default:text")] - [InlineData(2, 42)] - [InlineData(3, true)] - [InlineData(4, "5C392E64-2AEC-401F-98A9-35E5913B369A")] - public void Should_use_default_arg_having_4_types(int index, object expected) - { - var value = index switch + var value = index switch + { + 1 => new TestUnion_class_string_int("text"), + 2 => new TestUnion_class_string_int(42), + _ => throw new Exception() + }; + + var calledActionOn = value.SwitchPartially(@default: v => (object)$"default:{v}", + int32: v => v); + + calledActionOn.Should().Be(expected); + } + + [Theory] + [InlineData(1, "default:text")] + [InlineData(2, 42)] + [InlineData(3, true)] + public void Should_use_default_arg_having_3_types(int index, object expected) { - 1 => new TestUnion_class_string_int_bool_guid("text"), - 2 => new TestUnion_class_string_int_bool_guid(42), - 3 => new TestUnion_class_string_int_bool_guid(true), - 4 => new TestUnion_class_string_int_bool_guid(new Guid("5C392E64-2AEC-401F-98A9-35E5913B369A")), - _ => throw new Exception() - }; - - var calledActionOn = value.SwitchPartially(@default: v => (object)$"default:{v}", - int32: v => v, - boolean: v => v, - guid: v => v); - - calledActionOn.Should().Be(index == 4 ? new Guid((string)expected) : expected); - } - - [Theory] - [InlineData(1, "default:text")] - [InlineData(2, 42)] - [InlineData(3, true)] - [InlineData(4, "5C392E64-2AEC-401F-98A9-35E5913B369A")] - [InlineData(5, 'A')] - public void Should_use_default_arg_having_5_types(int index, object expected) - { - var value = index switch + var value = index switch + { + 1 => new TestUnion_class_string_int_bool("text"), + 2 => new TestUnion_class_string_int_bool(42), + 3 => new TestUnion_class_string_int_bool(true), + _ => throw new Exception() + }; + + var calledActionOn = value.SwitchPartially(@default: v => (object)$"default:{v}", + int32: v => v, + boolean: v => v); + + calledActionOn.Should().Be(expected); + } + + [Theory] + [InlineData(1, "default:text")] + [InlineData(2, 42)] + [InlineData(3, true)] + [InlineData(4, "5C392E64-2AEC-401F-98A9-35E5913B369A")] + public void Should_use_default_arg_having_4_types(int index, object expected) + { + var value = index switch + { + 1 => new TestUnion_class_string_int_bool_guid("text"), + 2 => new TestUnion_class_string_int_bool_guid(42), + 3 => new TestUnion_class_string_int_bool_guid(true), + 4 => new TestUnion_class_string_int_bool_guid(new Guid("5C392E64-2AEC-401F-98A9-35E5913B369A")), + _ => throw new Exception() + }; + + var calledActionOn = value.SwitchPartially(@default: v => (object)$"default:{v}", + int32: v => v, + boolean: v => v, + guid: v => v); + + calledActionOn.Should().Be(index == 4 ? new Guid((string)expected) : expected); + } + + [Theory] + [InlineData(1, "default:text")] + [InlineData(2, 42)] + [InlineData(3, true)] + [InlineData(4, "5C392E64-2AEC-401F-98A9-35E5913B369A")] + [InlineData(5, 'A')] + public void Should_use_default_arg_having_5_types(int index, object expected) { - 1 => new TestUnion_class_string_int_bool_guid_char("text"), - 2 => new TestUnion_class_string_int_bool_guid_char(42), - 3 => new TestUnion_class_string_int_bool_guid_char(true), - 4 => new TestUnion_class_string_int_bool_guid_char(new Guid("5C392E64-2AEC-401F-98A9-35E5913B369A")), - 5 => new TestUnion_class_string_int_bool_guid_char('A'), - _ => throw new Exception() - }; - - var calledActionOn = value.SwitchPartially(@default: v => (object)$"default:{v}", - int32: v => v, - boolean: v => v, - guid: v => v, - @char: v => v); - - calledActionOn.Should().Be(index == 4 ? new Guid((string)expected) : expected); + var value = index switch + { + 1 => new TestUnion_class_string_int_bool_guid_char("text"), + 2 => new TestUnion_class_string_int_bool_guid_char(42), + 3 => new TestUnion_class_string_int_bool_guid_char(true), + 4 => new TestUnion_class_string_int_bool_guid_char(new Guid("5C392E64-2AEC-401F-98A9-35E5913B369A")), + 5 => new TestUnion_class_string_int_bool_guid_char('A'), + _ => throw new Exception() + }; + + var calledActionOn = value.SwitchPartially(@default: v => (object)$"default:{v}", + int32: v => v, + boolean: v => v, + guid: v => v, + @char: v => v); + + calledActionOn.Should().Be(index == 4 ? new Guid((string)expected) : expected); + } } - } - public class WithFuncAndContext - { - [Theory] - [InlineData(1, "text")] - [InlineData(2, 42)] - public void Should_pass_context_having_2_types(int index, object expected) + public class WithFuncAndContext { - var value = index switch + [Theory] + [InlineData(1, "text")] + [InlineData(2, 42)] + public void Should_pass_context_having_2_types(int index, object expected) { - 1 => new TestUnion_class_string_int("text"), - 2 => new TestUnion_class_string_int(42), - _ => throw new Exception() - }; + var value = index switch + { + 1 => new TestUnion_class_string_int("text"), + 2 => new TestUnion_class_string_int(42), + _ => throw new Exception() + }; + + var context = new object(); + + var calledActionOn = value.SwitchPartially(context, + @default: (ctx, v) => + { + ctx.Should().Be(context); + return (object)$"default:{v}"; + }, + @string: (ctx, v) => + { + ctx.Should().Be(context); + return v; + }, + int32: (ctx, v) => + { + ctx.Should().Be(context); + return v; + }); - var context = new object(); + calledActionOn.Should().Be(expected); + } - var calledActionOn = value.SwitchPartially(context, - @default: (ctx, v) => + [Theory] + [InlineData(1, "text")] + [InlineData(2, 42)] + [InlineData(3, true)] + public void Should_pass_context_having_3_types(int index, object expected) + { + var value = index switch + { + 1 => new TestUnion_class_string_int_bool("text"), + 2 => new TestUnion_class_string_int_bool(42), + 3 => new TestUnion_class_string_int_bool(true), + _ => throw new Exception() + }; + + var context = new object(); + + var calledActionOn = value.SwitchPartially(context, + @default: (ctx, v) => + { + ctx.Should().Be(context); + return (object)$"default:{v}"; + }, + @string: (ctx, v) => + { + ctx.Should().Be(context); + return v; + }, + int32: (ctx, v) => + { + ctx.Should().Be(context); + return v; + }, + boolean: (ctx, v) => + { + ctx.Should().Be(context); + return v; + }); + + calledActionOn.Should().Be(expected); + } + + [Theory] + [InlineData(1, "text")] + [InlineData(2, 42)] + [InlineData(3, true)] + [InlineData(4, "10C287C8-4D64-45CC-859E-873024D53DE3")] + public void Should_pass_context_having_4_types(int index, object expected) + { + var value = index switch + { + 1 => new TestUnion_class_string_int_bool_guid("text"), + 2 => new TestUnion_class_string_int_bool_guid(42), + 3 => new TestUnion_class_string_int_bool_guid(true), + 4 => new TestUnion_class_string_int_bool_guid(new Guid("10C287C8-4D64-45CC-859E-873024D53DE3")), + _ => throw new Exception() + }; + + var context = new object(); + + var calledActionOn = value.SwitchPartially(context, + @default: (ctx, v) => + { + ctx.Should().Be(context); + return (object)$"default:{v}"; + }, + @string: (ctx, v) => + { + ctx.Should().Be(context); + return v; + }, + int32: (ctx, v) => { ctx.Should().Be(context); - return (object)$"default:{v}"; + return v; }, - @string: (ctx, v) => + boolean: (ctx, v) => + { + ctx.Should().Be(context); + return v; + }, + guid: (ctx, v) => { ctx.Should().Be(context); return v; - }, - int32: (ctx, v) => - { - ctx.Should().Be(context); - return v; - }); + }); - calledActionOn.Should().Be(expected); - } + calledActionOn.Should().Be(index == 4 ? new Guid((string)expected) : expected); + } - [Theory] - [InlineData(1, "text")] - [InlineData(2, 42)] - [InlineData(3, true)] - public void Should_pass_context_having_3_types(int index, object expected) - { - var value = index switch + [Theory] + [InlineData(1, "text")] + [InlineData(2, 42)] + [InlineData(3, true)] + [InlineData(4, "10C287C8-4D64-45CC-859E-873024D53DE3")] + [InlineData(5, 'A')] + public void Should_pass_context_having_5_types(int index, object expected) { - 1 => new TestUnion_class_string_int_bool("text"), - 2 => new TestUnion_class_string_int_bool(42), - 3 => new TestUnion_class_string_int_bool(true), - _ => throw new Exception() - }; - - var context = new object(); - - var calledActionOn = value.SwitchPartially(context, - @default: (ctx, v) => + var value = index switch + { + 1 => new TestUnion_class_string_int_bool_guid_char("text"), + 2 => new TestUnion_class_string_int_bool_guid_char(42), + 3 => new TestUnion_class_string_int_bool_guid_char(true), + 4 => new TestUnion_class_string_int_bool_guid_char(new Guid("10C287C8-4D64-45CC-859E-873024D53DE3")), + 5 => new TestUnion_class_string_int_bool_guid_char('A'), + _ => throw new Exception() + }; + + var context = new object(); + + var calledActionOn = value.SwitchPartially(context, + @default: (ctx, v) => + { + ctx.Should().Be(context); + return (object)$"default:{v}"; + }, + @string: (ctx, v) => + { + ctx.Should().Be(context); + return v; + }, + int32: (ctx, v) => { ctx.Should().Be(context); - return (object)$"default:{v}"; + return v; }, - @string: (ctx, v) => + boolean: (ctx, v) => + { + ctx.Should().Be(context); + return v; + }, + guid: (ctx, v) => { ctx.Should().Be(context); return v; }, - int32: (ctx, v) => - { - ctx.Should().Be(context); - return v; - }, - boolean: (ctx, v) => - { - ctx.Should().Be(context); - return v; - }); + @char: (ctx, v) => + { + ctx.Should().Be(context); + return v; + }); - calledActionOn.Should().Be(expected); - } + calledActionOn.Should().Be(index == 4 ? new Guid((string)expected) : expected); + } - [Theory] - [InlineData(1, "text")] - [InlineData(2, 42)] - [InlineData(3, true)] - [InlineData(4, "10C287C8-4D64-45CC-859E-873024D53DE3")] - public void Should_pass_context_having_4_types(int index, object expected) - { - var value = index switch + [Theory] + [InlineData(1, "default:text")] + [InlineData(2, 42)] + public void Should_pass_context_to_default_having_2_types(int index, object expected) { - 1 => new TestUnion_class_string_int_bool_guid("text"), - 2 => new TestUnion_class_string_int_bool_guid(42), - 3 => new TestUnion_class_string_int_bool_guid(true), - 4 => new TestUnion_class_string_int_bool_guid(new Guid("10C287C8-4D64-45CC-859E-873024D53DE3")), - _ => throw new Exception() - }; + var value = index switch + { + 1 => new TestUnion_class_string_int("text"), + 2 => new TestUnion_class_string_int(42), + _ => throw new Exception() + }; + + var context = new object(); + + var calledActionOn = value.SwitchPartially(context, + @default: (ctx, v) => + { + ctx.Should().Be(context); + return (object)$"default:{v}"; + }, + int32: (ctx, v) => + { + ctx.Should().Be(context); + return v; + }); - var context = new object(); + calledActionOn.Should().Be(expected); + } - var calledActionOn = value.SwitchPartially(context, - @default: (ctx, v) => + [Theory] + [InlineData(1, "default:text")] + [InlineData(2, 42)] + [InlineData(3, true)] + public void Should_pass_context_to_default_having_3_types(int index, object expected) + { + var value = index switch + { + 1 => new TestUnion_class_string_int_bool("text"), + 2 => new TestUnion_class_string_int_bool(42), + 3 => new TestUnion_class_string_int_bool(true), + _ => throw new Exception() + }; + + var context = new object(); + + var calledActionOn = value.SwitchPartially(context, + @default: (ctx, v) => + { + ctx.Should().Be(context); + return (object)$"default:{v}"; + }, + int32: (ctx, v) => { ctx.Should().Be(context); - return (object)$"default:{v}"; + return v; }, - @string: (ctx, v) => - { - ctx.Should().Be(context); - return v; - }, - int32: (ctx, v) => - { - ctx.Should().Be(context); - return v; - }, - boolean: (ctx, v) => + boolean: (ctx, v) => + { + ctx.Should().Be(context); + return v; + }); + + calledActionOn.Should().Be(expected); + } + + [Theory] + [InlineData(1, "default:text")] + [InlineData(2, 42)] + [InlineData(3, true)] + [InlineData(4, "1F99EE8B-862C-4068-B5E1-015EA81AA470")] + public void Should_pass_context_to_default_having_4_types(int index, object expected) + { + var value = index switch + { + 1 => new TestUnion_class_string_int_bool_guid("text"), + 2 => new TestUnion_class_string_int_bool_guid(42), + 3 => new TestUnion_class_string_int_bool_guid(true), + 4 => new TestUnion_class_string_int_bool_guid(new Guid("1F99EE8B-862C-4068-B5E1-015EA81AA470")), + _ => throw new Exception() + }; + + var context = new object(); + + var calledActionOn = value.SwitchPartially(context, + @default: (ctx, v) => + { + ctx.Should().Be(context); + return (object)$"default:{v}"; + }, + int32: (ctx, v) => + { + ctx.Should().Be(context); + return v; + }, + boolean: (ctx, v) => + { + ctx.Should().Be(context); + return v; + }, + guid: (ctx, v) => { ctx.Should().Be(context); return v; - }, - guid: (ctx, v) => - { - ctx.Should().Be(context); - return v; - }); + }); - calledActionOn.Should().Be(index == 4 ? new Guid((string)expected) : expected); - } + calledActionOn.Should().Be(index == 4 ? new Guid((string)expected) : expected); + } - [Theory] - [InlineData(1, "text")] - [InlineData(2, 42)] - [InlineData(3, true)] - [InlineData(4, "10C287C8-4D64-45CC-859E-873024D53DE3")] - [InlineData(5, 'A')] - public void Should_pass_context_having_5_types(int index, object expected) - { - var value = index switch + [Theory] + [InlineData(1, "default:text")] + [InlineData(2, 42)] + [InlineData(3, true)] + [InlineData(4, "1F99EE8B-862C-4068-B5E1-015EA81AA470")] + [InlineData(5, 'A')] + public void Should_pass_context_to_default_having_5_types(int index, object expected) { - 1 => new TestUnion_class_string_int_bool_guid_char("text"), - 2 => new TestUnion_class_string_int_bool_guid_char(42), - 3 => new TestUnion_class_string_int_bool_guid_char(true), - 4 => new TestUnion_class_string_int_bool_guid_char(new Guid("10C287C8-4D64-45CC-859E-873024D53DE3")), - 5 => new TestUnion_class_string_int_bool_guid_char('A'), - _ => throw new Exception() - }; - - var context = new object(); - - var calledActionOn = value.SwitchPartially(context, - @default: (ctx, v) => + var value = index switch + { + 1 => new TestUnion_class_string_int_bool_guid_char("text"), + 2 => new TestUnion_class_string_int_bool_guid_char(42), + 3 => new TestUnion_class_string_int_bool_guid_char(true), + 4 => new TestUnion_class_string_int_bool_guid_char(new Guid("1F99EE8B-862C-4068-B5E1-015EA81AA470")), + 5 => new TestUnion_class_string_int_bool_guid_char('A'), + _ => throw new Exception() + }; + + var context = new object(); + + var calledActionOn = value.SwitchPartially(context, + @default: (ctx, v) => + { + ctx.Should().Be(context); + return (object)$"default:{v}"; + }, + int32: (ctx, v) => { ctx.Should().Be(context); - return (object)$"default:{v}"; + return v; }, - @string: (ctx, v) => - { - ctx.Should().Be(context); - return v; - }, - int32: (ctx, v) => - { - ctx.Should().Be(context); - return v; - }, - boolean: (ctx, v) => + boolean: (ctx, v) => + { + ctx.Should().Be(context); + return v; + }, + guid: (ctx, v) => { ctx.Should().Be(context); return v; }, - guid: (ctx, v) => - { - ctx.Should().Be(context); - return v; - }, - @char: (ctx, v) => - { - ctx.Should().Be(context); - return v; - }); - - calledActionOn.Should().Be(index == 4 ? new Guid((string)expected) : expected); + @char: (ctx, v) => + { + ctx.Should().Be(context); + return v; + }); + + calledActionOn.Should().Be(index == 4 ? new Guid((string)expected) : expected); + } } + } - [Theory] - [InlineData(1, "default:text")] - [InlineData(2, 42)] - public void Should_pass_context_to_default_having_2_types(int index, object expected) + public class HavingStrcut + { + public class WithAction { - var value = index switch + [Theory] + [InlineData(1, "text")] + [InlineData(2, 42)] + public void Should_use_correct_arg_having_2_values(int index, object expected) { - 1 => new TestUnion_class_string_int("text"), - 2 => new TestUnion_class_string_int(42), - _ => throw new Exception() - }; + var value = index switch + { + 1 => new TestUnion_struct_string_int("text"), + 2 => new TestUnion_struct_string_int(42), + _ => throw new Exception() + }; + object calledActionOn = null; + + value.SwitchPartially(@string: v => + { + calledActionOn = v; + }, + int32: v => + { + calledActionOn = v; + }); - var context = new object(); + calledActionOn.Should().Be(expected); + } - var calledActionOn = value.SwitchPartially(context, - @default: (ctx, v) => - { - ctx.Should().Be(context); - return (object)$"default:{v}"; - }, - int32: (ctx, v) => - { - ctx.Should().Be(context); - return v; - }); + [Theory] + [InlineData(1, "default:text")] + [InlineData(2, 42)] + public void Should_use_default_arg_having_2_types(int index, object expected) + { + var value = index switch + { + 1 => new TestUnion_struct_string_int("text"), + 2 => new TestUnion_struct_string_int(42), + _ => throw new Exception() + }; + object calledActionOn = null; + + value.SwitchPartially(@default: v => + { + calledActionOn = $"default:{v}"; + }, + int32: v => + { + calledActionOn = v; + }); - calledActionOn.Should().Be(expected); + calledActionOn.Should().Be(expected); + } } - [Theory] - [InlineData(1, "default:text")] - [InlineData(2, 42)] - [InlineData(3, true)] - public void Should_pass_context_to_default_having_3_types(int index, object expected) + public class WithActionAndContext { - var value = index switch + [Theory] + [InlineData(1, "text")] + [InlineData(2, 42)] + public void Should_pass_context_having_2_types(int index, object expected) { - 1 => new TestUnion_class_string_int_bool("text"), - 2 => new TestUnion_class_string_int_bool(42), - 3 => new TestUnion_class_string_int_bool(true), - _ => throw new Exception() - }; + var value = index switch + { + 1 => new TestUnion_struct_string_int("text"), + 2 => new TestUnion_struct_string_int(42), + _ => throw new Exception() + }; + + var context = new object(); + object calledActionOn = null; + + value.SwitchPartially(context, + @string: (ctx, v) => + { + ctx.Should().Be(context); + calledActionOn = v; + }, + int32: (ctx, v) => + { + ctx.Should().Be(context); + calledActionOn = v; + }); - var context = new object(); + calledActionOn.Should().Be(expected); + } - var calledActionOn = value.SwitchPartially(context, - @default: (ctx, v) => - { - ctx.Should().Be(context); - return (object)$"default:{v}"; - }, - int32: (ctx, v) => - { - ctx.Should().Be(context); - return v; - }, - boolean: (ctx, v) => - { - ctx.Should().Be(context); - return v; - }); + [Theory] + [InlineData(1, "default:text")] + [InlineData(2, 42)] + public void Should_pass_context_to_default_having_2_types(int index, object expected) + { + var value = index switch + { + 1 => new TestUnion_struct_string_int("text"), + 2 => new TestUnion_struct_string_int(42), + _ => throw new Exception() + }; + + var context = new object(); + object calledActionOn = null; + + value.SwitchPartially(context, + @default: (ctx, v) => + { + ctx.Should().Be(context); + calledActionOn = $"default:{v}"; + }, + int32: (ctx, v) => + { + ctx.Should().Be(context); + calledActionOn = v; + }); - calledActionOn.Should().Be(expected); + calledActionOn.Should().Be(expected); + } } - [Theory] - [InlineData(1, "default:text")] - [InlineData(2, 42)] - [InlineData(3, true)] - [InlineData(4, "1F99EE8B-862C-4068-B5E1-015EA81AA470")] - public void Should_pass_context_to_default_having_4_types(int index, object expected) + public class WithFunc { - var value = index switch + [Theory] + [InlineData(1, "text")] + [InlineData(2, 42)] + public void Should_use_correct_arg_having_2_types(int index, object expected) { - 1 => new TestUnion_class_string_int_bool_guid("text"), - 2 => new TestUnion_class_string_int_bool_guid(42), - 3 => new TestUnion_class_string_int_bool_guid(true), - 4 => new TestUnion_class_string_int_bool_guid(new Guid("1F99EE8B-862C-4068-B5E1-015EA81AA470")), - _ => throw new Exception() - }; - - var context = new object(); + var value = index switch + { + 1 => new TestUnion_struct_string_int("text"), + 2 => new TestUnion_struct_string_int(42), + _ => throw new Exception() + }; + + var calledActionOn = value.SwitchPartially(@default: v => $"default:{v}", + @string: v => (object)v, + int32: v => v); + + calledActionOn.Should().Be(expected); + } + + [Theory] + [InlineData(1, "default:text")] + [InlineData(2, 42)] + public void Should_use_default_arg_having_2_types(int index, object expected) + { + var value = index switch + { + 1 => new TestUnion_struct_string_int("text"), + 2 => new TestUnion_struct_string_int(42), + _ => throw new Exception() + }; + + var calledActionOn = value.SwitchPartially(@default: v => (object)$"default:{v}", + int32: v => v); + + calledActionOn.Should().Be(expected); + } + } - var calledActionOn = value.SwitchPartially(context, - @default: (ctx, v) => + public class WithFuncAndContext + { + [Theory] + [InlineData(1, "text")] + [InlineData(2, 42)] + public void Should_pass_context_having_2_types(int index, object expected) + { + var value = index switch + { + 1 => new TestUnion_struct_string_int("text"), + 2 => new TestUnion_struct_string_int(42), + _ => throw new Exception() + }; + + var context = new object(); + + var calledActionOn = value.SwitchPartially(context, + @default: (ctx, v) => + { + ctx.Should().Be(context); + return (object)$"default:{v}"; + }, + @string: (ctx, v) => + { + ctx.Should().Be(context); + return v; + }, + int32: (ctx, v) => { ctx.Should().Be(context); - return (object)$"default:{v}"; - }, - int32: (ctx, v) => - { - ctx.Should().Be(context); - return v; - }, - boolean: (ctx, v) => - { - ctx.Should().Be(context); - return v; - }, - guid: (ctx, v) => - { - ctx.Should().Be(context); - return v; - }); + return v; + }); - calledActionOn.Should().Be(index == 4 ? new Guid((string)expected) : expected); - } + calledActionOn.Should().Be(expected); + } - [Theory] - [InlineData(1, "default:text")] - [InlineData(2, 42)] - [InlineData(3, true)] - [InlineData(4, "1F99EE8B-862C-4068-B5E1-015EA81AA470")] - [InlineData(5, 'A')] - public void Should_pass_context_to_default_having_5_types(int index, object expected) - { - var value = index switch + [Theory] + [InlineData(1, "default:text")] + [InlineData(2, 42)] + public void Should_pass_context_to_default_having_2_types(int index, object expected) { - 1 => new TestUnion_class_string_int_bool_guid_char("text"), - 2 => new TestUnion_class_string_int_bool_guid_char(42), - 3 => new TestUnion_class_string_int_bool_guid_char(true), - 4 => new TestUnion_class_string_int_bool_guid_char(new Guid("1F99EE8B-862C-4068-B5E1-015EA81AA470")), - 5 => new TestUnion_class_string_int_bool_guid_char('A'), - _ => throw new Exception() - }; - - var context = new object(); - - var calledActionOn = value.SwitchPartially(context, - @default: (ctx, v) => + var value = index switch + { + 1 => new TestUnion_struct_string_int("text"), + 2 => new TestUnion_struct_string_int(42), + _ => throw new Exception() + }; + + var context = new object(); + + var calledActionOn = value.SwitchPartially(context, + @default: (ctx, v) => + { + ctx.Should().Be(context); + return (object)$"default:{v}"; + }, + int32: (ctx, v) => { ctx.Should().Be(context); - return (object)$"default:{v}"; - }, - int32: (ctx, v) => - { - ctx.Should().Be(context); - return v; - }, - boolean: (ctx, v) => - { - ctx.Should().Be(context); - return v; - }, - guid: (ctx, v) => - { - ctx.Should().Be(context); - return v; - }, - @char: (ctx, v) => - { - ctx.Should().Be(context); - return v; - }); - - calledActionOn.Should().Be(index == 4 ? new Guid((string)expected) : expected); + return v; + }); + + calledActionOn.Should().Be(expected); + } } } } diff --git a/test/Thinktecture.Runtime.Extensions.Tests/UnionTests/ToString.cs b/test/Thinktecture.Runtime.Extensions.Tests/UnionTests/ToString.cs index 5c3eb25d..0cd2cfaf 100644 --- a/test/Thinktecture.Runtime.Extensions.Tests/UnionTests/ToString.cs +++ b/test/Thinktecture.Runtime.Extensions.Tests/UnionTests/ToString.cs @@ -10,6 +10,9 @@ public void Should_return_string_representation_of_the_inner_value_having_2_type { new TestUnion_class_string_int("text").ToString().Should().Be("text"); new TestUnion_class_string_int(42).ToString().Should().Be("42"); + + new TestUnion_struct_string_int("text").ToString().Should().Be("text"); + new TestUnion_struct_string_int(42).ToString().Should().Be("42"); } [Fact] diff --git a/test/Thinktecture.Runtime.Extensions.Tests/UnionTests/Value.cs b/test/Thinktecture.Runtime.Extensions.Tests/UnionTests/Value.cs index a204164f..5d137da6 100644 --- a/test/Thinktecture.Runtime.Extensions.Tests/UnionTests/Value.cs +++ b/test/Thinktecture.Runtime.Extensions.Tests/UnionTests/Value.cs @@ -20,6 +20,9 @@ public void Should_return_correct_value_having_2_types() new TestUnion_class_nullable_string_nullable_int("text").Value.Should().Be("text"); new TestUnion_class_nullable_string_nullable_int(1).Value.Should().Be(1); new TestUnion_class_nullable_string_nullable_int(nullableInt32: null).Value.Should().BeNull(); + + new TestUnion_struct_string_int("text").Value.Should().Be("text"); + new TestUnion_struct_string_int(1).Value.Should().Be(1); } [Fact]