From e4f18e3f1bc6f85215ed0da944c68e523a707ed6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?/=E1=90=A0=CB=B5-=20=E2=A9=8A=20-=CB=B5=E3=83=9E=20?= =?UTF-8?q?=E1=B6=BB=20=F0=9D=97=93=20=F0=90=B0=81?= Date: Wed, 11 Dec 2024 08:15:34 +0100 Subject: [PATCH] fix: fix TypeCache parsing behavior and related unit tests. (#238) --- src/Silverback.Core/Util/TypesCache.cs | 26 ++---- .../Util/TypesCacheTests.TestData.cs | 87 +++++++++++++++++++ .../Util/TypesCacheTests.cs | 16 ++-- 3 files changed, 100 insertions(+), 29 deletions(-) create mode 100644 tests/Silverback.Core.Tests/Util/TypesCacheTests.TestData.cs diff --git a/src/Silverback.Core/Util/TypesCache.cs b/src/Silverback.Core/Util/TypesCache.cs index c4955cee3..fd68850eb 100644 --- a/src/Silverback.Core/Util/TypesCache.cs +++ b/src/Silverback.Core/Util/TypesCache.cs @@ -1,9 +1,10 @@ -// Copyright (c) 2020 Sergio Aquilini +// Copyright (c) 2020 Sergio Aquilini // This code is licensed under MIT license (see LICENSE file for details) using System; using System.Collections.Concurrent; using System.Diagnostics.CodeAnalysis; +using System.Text.RegularExpressions; namespace Silverback.Util { @@ -30,29 +31,14 @@ internal static class TypesCache return type; } - internal static string CleanAssemblyQualifiedName(string typeAssemblyQualifiedName) + private static string CleanAssemblyQualifiedName(string typeAssemblyQualifiedName) { - if (string.IsNullOrEmpty(typeAssemblyQualifiedName)) + if (string.IsNullOrWhiteSpace(typeAssemblyQualifiedName)) return typeAssemblyQualifiedName; - int endGenericType = typeAssemblyQualifiedName.LastIndexOf(']'); - if (endGenericType == -1) - { - string[] split = typeAssemblyQualifiedName.Split(',', 3, StringSplitOptions.RemoveEmptyEntries); - return split.Length >= 2 ? $"{split[0].Trim()}, {split[1].Trim()}" : typeAssemblyQualifiedName; - } - - int startGenericType = typeAssemblyQualifiedName.IndexOf('[', StringComparison.InvariantCulture); - if (startGenericType == -1) - return typeAssemblyQualifiedName; - - string type = typeAssemblyQualifiedName[..startGenericType].Trim(); - if (endGenericType + 1 >= typeAssemblyQualifiedName.Length) - return type; + var cleanAssemblyQualifiedName = Regex.Replace(typeAssemblyQualifiedName, @", (Version=\d+\.\d+\.\d+\.\d+|Culture=\w+|PublicKeyToken=\w+)", string.Empty); - string next = typeAssemblyQualifiedName[(endGenericType + 1)..]; - string assemblyName = next.Split(",", 2, StringSplitOptions.RemoveEmptyEntries)[0].Trim(); - return $"{type}, {assemblyName}"; + return cleanAssemblyQualifiedName; } [SuppressMessage("", "CA1031", Justification = "Can catch all, the operation is retried")] diff --git a/tests/Silverback.Core.Tests/Util/TypesCacheTests.TestData.cs b/tests/Silverback.Core.Tests/Util/TypesCacheTests.TestData.cs new file mode 100644 index 000000000..adc0f5cda --- /dev/null +++ b/tests/Silverback.Core.Tests/Util/TypesCacheTests.TestData.cs @@ -0,0 +1,87 @@ +// Copyright (c) 2020 Sergio Aquilini +// This code is licensed under MIT license (see LICENSE file for details) + +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; + +namespace Silverback.Tests.Core.Util +{ + public partial class TypesCacheTests + { + public static IEnumerable AssemblyQualifiedNameType_ReturnType() + { + // Shortened qualified name with no types + yield return new object[] + { + "Silverback.Tests.Core.Util.TypesCacheTests+GenericTypeTest`2, Silverback.Core.Tests", + new AssemblyQualifiedGenericTypeResult( + typeof(GenericTypeTest<,>)), + }; + + // Shortened qualified name with types [shortened qualified name],[shortened qualified name, assembly] + yield return new object[] + { + "Silverback.Tests.Core.Util.TypesCacheTests+GenericTypeTest`2[[System.Int64],[Silverback.Tests.Core.Util.TypesCacheTests, Silverback.Core.Tests]], Silverback.Core.Tests", + new AssemblyQualifiedGenericTypeResult( + typeof(GenericTypeTest)), + }; + + // Shortened qualified name with types [full qualified name],[shortened qualified name] + yield return new object[] + { + "Silverback.Tests.Core.Util.TypesCacheTests+GenericTypeTest`2[[System.Int64, System.Private.CoreLib, Version=1.2.3.4, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],[Silverback.Tests.Core.Util.TypesCacheTests, Silverback.Core.Tests]], Silverback.Core.Tests", + new AssemblyQualifiedGenericTypeResult( + typeof(GenericTypeTest)), + }; + + // Shortened qualified name with types [full qualified name],[full qualified name] + yield return new object[] + { + "Silverback.Tests.Core.Util.TypesCacheTests+GenericTypeTest`2[[System.Int64, System.Private.CoreLib, Version=1.2.3.4, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],[Silverback.Tests.Core.Util.TypesCacheTests, Silverback.Core.Tests, Version=1.2.3.4, Culture=neutral, PublicKeyToken=null]], Silverback.Core.Tests", + new AssemblyQualifiedGenericTypeResult( + typeof(GenericTypeTest)), + }; + + // Shortened qualified name with types [shortened qualified name],[full qualified name] + yield return new object[] + { + "Silverback.Tests.Core.Util.TypesCacheTests+GenericTypeTest`2[[System.Int64, System.Private.CoreLib],[Silverback.Tests.Core.Util.TypesCacheTests, Silverback.Core.Tests, Version=1.2.3.4, Culture=neutral, PublicKeyToken=null]], Silverback.Core.Tests", + new AssemblyQualifiedGenericTypeResult( + typeof(GenericTypeTest)), + }; + + // Full qualified name with full qualified types. + yield return new object[] + { + "Silverback.Tests.Core.Util.TypesCacheTests+GenericTypeTest`2[[System.Int64, System.Private.CoreLib, Version=1.2.3.4, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],[Silverback.Tests.Core.Util.TypesCacheTests, Silverback.Core.Tests, Version=1.2.3.4, Culture=neutral, PublicKeyToken=null]], Silverback.Core.Tests, Version=1.2.3.4, Culture=neutral, PublicKeyToken=null", + new AssemblyQualifiedGenericTypeResult( + typeof(GenericTypeTest)), + }; + + // Full qualified name - no culture, with type [full qualified name - no version] + yield return new object[] + { + "System.Collections.Generic.List`1[[System.Char, System.Private.CoreLib, Culture=neutral]], System.Private.CoreLib, Version=5.0.0.0, PublicKeyToken=7cec85d7bea7798e", + new AssemblyQualifiedGenericTypeResult( + typeof(List)), + }; + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1034:Nested types should not be visible", Justification = "Record belongs to unit test data.")] + public record AssemblyQualifiedGenericTypeResult(Type MatchingType) + { + } + + [SuppressMessage("StyleCop.CSharp.DocumentationRules", "CA1812: Avoid uninstantiated internal classes", Justification = "Class loaded via tests.")] + private class GenericTypeTest + where T2 : class + { + [SuppressMessage("Major Code Smell", "S1144:Unused private types or members should be removed", Justification = "Generic type assignment.")] + public T1? P1 { get; } + + [SuppressMessage("Major Code Smell", "S1144:Unused private types or members should be removed", Justification = "Generic type assignment.")] + public T2? P2 { get; set; } + } + } +} diff --git a/tests/Silverback.Core.Tests/Util/TypesCacheTests.cs b/tests/Silverback.Core.Tests/Util/TypesCacheTests.cs index c0985ea69..b562cd1b4 100644 --- a/tests/Silverback.Core.Tests/Util/TypesCacheTests.cs +++ b/tests/Silverback.Core.Tests/Util/TypesCacheTests.cs @@ -9,7 +9,7 @@ namespace Silverback.Tests.Core.Util { - public class TypesCacheTests + public partial class TypesCacheTests { [Fact] public void GetType_ExistingType_TypeReturned() @@ -64,16 +64,14 @@ public void GetType_IncompleteTypeName_TypeReturned() } [Theory] - [InlineData("Silverback.Tests.Core.TestTypes.Messages.TestEventOne2", "Silverback.Tests.Core.TestTypes.Messages.TestEventOne2")] - [InlineData("Silverback.Tests.Core.TestTypes.Messages.TestEventOne2, Silverback.Core.Tests", "Silverback.Tests.Core.TestTypes.Messages.TestEventOne2, Silverback.Core.Tests")] - [InlineData("Silverback.Tests.Core.TestTypes.Messages.TestEventOne2, Silverback.Core.Tests, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null", "Silverback.Tests.Core.TestTypes.Messages.TestEventOne2, Silverback.Core.Tests")] - [InlineData("Silverback.Tests.Core.Util.TypesCacheTests+MyMessage`2[[System.Int32, System.Private.CoreLib, Version=5.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],[System.Int64, System.Private.CoreLib, Version=5.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]], Silverback.Core.Tests, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null", "Silverback.Tests.Core.Util.TypesCacheTests+MyMessage`2, Silverback.Core.Tests")] - [InlineData("Silverback.Tests.Core.Util.TypesCacheTests+MyMessage`2[[System.Int32, System.Private.CoreLib, Version=5.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],[System.Int64, System.Private.CoreLib, Version=5.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]]", "Silverback.Tests.Core.Util.TypesCacheTests+MyMessage`2")] - public void GetType_GenericType_TypeReturned(string typeAssemblyQualifiedName, string expected) + [MemberData(nameof(AssemblyQualifiedNameType_ReturnType))] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Performance", "CA1825:Avoid zero-length array allocations", Justification = "Unit test member data.")] + public void GetType_GenericType_TypeReturned(string typeAssemblyQualifiedName, AssemblyQualifiedGenericTypeResult expectedResult) { - string cleanedName = TypesCache.CleanAssemblyQualifiedName(typeAssemblyQualifiedName); + var type = TypesCache.GetType(typeAssemblyQualifiedName); - cleanedName.Should().Be(expected); + type.Should().NotBeNull(); + expectedResult.MatchingType.IsAssignableFrom(type).Should().BeTrue(); } } }