diff --git a/ReflectorNet.Tests/src/ReflectorTests/IsTypeBlacklistedTests.cs b/ReflectorNet.Tests/src/ReflectorTests/IsTypeBlacklistedTests.cs index 05bbd917..a4871671 100644 --- a/ReflectorNet.Tests/src/ReflectorTests/IsTypeBlacklistedTests.cs +++ b/ReflectorNet.Tests/src/ReflectorTests/IsTypeBlacklistedTests.cs @@ -515,6 +515,178 @@ public void IsTypeBlacklisted_GenericWithInterfaceImplementor_ReturnsTrue() #endregion + #region Generic Type Definition Blacklisting Tests + + [Fact] + public void IsTypeBlacklisted_ClosedGenericBlacklisted_OtherClosedTypesNotAffected() + { + // Arrange + var reflector = new Reflector(); + reflector.Converters.BlacklistType(typeof(List)); // Only blacklist List + + // Act & Assert + Assert.True(reflector.Converters.IsTypeBlacklisted(typeof(List))); // Exact match - blacklisted + Assert.False(reflector.Converters.IsTypeBlacklisted(typeof(List))); // Different closed type - NOT blacklisted + Assert.False(reflector.Converters.IsTypeBlacklisted(typeof(List))); // Different closed type - NOT blacklisted + + _output.WriteLine("Blacklisting List does not affect List or List"); + } + + [Fact] + public void IsTypeBlacklisted_GenericTypeDefinitionBlacklisted_AllClosedTypesBlacklisted() + { + // Arrange + var reflector = new Reflector(); + reflector.Converters.BlacklistType(typeof(List<>)); // Blacklist the generic type definition + + // Act & Assert + Assert.True(reflector.Converters.IsTypeBlacklisted(typeof(List))); + Assert.True(reflector.Converters.IsTypeBlacklisted(typeof(List))); + Assert.True(reflector.Converters.IsTypeBlacklisted(typeof(List))); + Assert.True(reflector.Converters.IsTypeBlacklisted(typeof(List))); + + _output.WriteLine("Blacklisting List<> blacklists all List types"); + } + + [Fact] + public void IsTypeBlacklisted_GenericTypeDefinitionBlacklisted_OtherGenericFamiliesNotAffected() + { + // Arrange + var reflector = new Reflector(); + reflector.Converters.BlacklistType(typeof(List<>)); // Only blacklist List<> + + // Act & Assert + Assert.True(reflector.Converters.IsTypeBlacklisted(typeof(List))); // List<> family - blacklisted + Assert.False(reflector.Converters.IsTypeBlacklisted(typeof(Dictionary))); // Different family - NOT blacklisted + Assert.False(reflector.Converters.IsTypeBlacklisted(typeof(HashSet))); // Different family - NOT blacklisted + + _output.WriteLine("Blacklisting List<> does not affect Dictionary<,> or HashSet<>"); + } + + [Fact] + public void IsTypeBlacklisted_DictionaryDefinitionBlacklisted_AllClosedTypesBlacklisted() + { + // Arrange + var reflector = new Reflector(); + reflector.Converters.BlacklistType(typeof(Dictionary<,>)); // Blacklist Dictionary<,> + + // Act & Assert + Assert.True(reflector.Converters.IsTypeBlacklisted(typeof(Dictionary))); + Assert.True(reflector.Converters.IsTypeBlacklisted(typeof(Dictionary))); + Assert.True(reflector.Converters.IsTypeBlacklisted(typeof(Dictionary))); + + _output.WriteLine("Blacklisting Dictionary<,> blacklists all Dictionary types"); + } + + [Fact] + public void IsTypeBlacklisted_NestedGenericWithBlacklistedDefinition_ReturnsTrue() + { + // Arrange + var reflector = new Reflector(); + reflector.Converters.BlacklistType(typeof(List<>)); // Blacklist List<> + + // Act - Dictionary contains List as a type argument + var result = reflector.Converters.IsTypeBlacklisted(typeof(Dictionary>)); + + // Assert + Assert.True(result); + _output.WriteLine("Dictionary> is blacklisted when List<> is blacklisted"); + } + + [Fact] + public void IsTypeBlacklisted_GenericWrapperDefinitionBlacklisted_CustomClassBlacklisted() + { + // Arrange + var reflector = new Reflector(); + reflector.Converters.BlacklistType(typeof(GenericWrapper<>)); // Blacklist our custom GenericWrapper<> + + // Act & Assert + Assert.True(reflector.Converters.IsTypeBlacklisted(typeof(GenericWrapper))); + Assert.True(reflector.Converters.IsTypeBlacklisted(typeof(GenericWrapper))); + Assert.True(reflector.Converters.IsTypeBlacklisted(typeof(GenericWrapper))); + + _output.WriteLine("Blacklisting GenericWrapper<> blacklists all GenericWrapper types"); + } + + [Fact] + public void IsTypeBlacklisted_MultiGenericWrapperDefinitionBlacklisted_AllClosedTypesBlacklisted() + { + // Arrange + var reflector = new Reflector(); + reflector.Converters.BlacklistType(typeof(MultiGenericWrapper<,>)); // Blacklist MultiGenericWrapper<,> + + // Act & Assert + Assert.True(reflector.Converters.IsTypeBlacklisted(typeof(MultiGenericWrapper))); + Assert.True(reflector.Converters.IsTypeBlacklisted(typeof(MultiGenericWrapper))); + Assert.True(reflector.Converters.IsTypeBlacklisted(typeof(MultiGenericWrapper))); + + _output.WriteLine("Blacklisting MultiGenericWrapper<,> blacklists all MultiGenericWrapper types"); + } + + [Fact] + public void IsTypeBlacklisted_GenericTypeDefinitionItself_ReturnsTrue() + { + // Arrange + var reflector = new Reflector(); + reflector.Converters.BlacklistType(typeof(List<>)); + + // Act - Check if the open generic type itself is blacklisted + var result = reflector.Converters.IsTypeBlacklisted(typeof(List<>)); + + // Assert + Assert.True(result); + _output.WriteLine("The generic type definition List<> itself is blacklisted"); + } + + [Fact] + public void IsTypeBlacklisted_ArrayOfBlacklistedGenericDefinition_ReturnsTrue() + { + // Arrange + var reflector = new Reflector(); + reflector.Converters.BlacklistType(typeof(List<>)); + + // Act - Array of List where List<> is blacklisted + var result = reflector.Converters.IsTypeBlacklisted(typeof(List[])); + + // Assert + Assert.True(result); + _output.WriteLine("List[] is blacklisted when List<> is blacklisted"); + } + +#if NET5_0_OR_GREATER + [Fact] + public void IsTypeBlacklisted_SpanDefinitionBlacklisted_AllSpanTypesBlacklisted() + { + // Arrange + var reflector = new Reflector(); + reflector.Converters.BlacklistType(typeof(Span<>)); // Blacklist Span<> + + // Act & Assert + Assert.True(reflector.Converters.IsTypeBlacklisted(typeof(Span))); + Assert.True(reflector.Converters.IsTypeBlacklisted(typeof(Span))); + Assert.True(reflector.Converters.IsTypeBlacklisted(typeof(Span))); + + _output.WriteLine("Blacklisting Span<> blacklists all Span types"); + } + + [Fact] + public void IsTypeBlacklisted_ReadOnlySpanDefinitionBlacklisted_AllReadOnlySpanTypesBlacklisted() + { + // Arrange + var reflector = new Reflector(); + reflector.Converters.BlacklistType(typeof(ReadOnlySpan<>)); // Blacklist ReadOnlySpan<> + + // Act & Assert + Assert.True(reflector.Converters.IsTypeBlacklisted(typeof(ReadOnlySpan))); + Assert.True(reflector.Converters.IsTypeBlacklisted(typeof(ReadOnlySpan))); + Assert.True(reflector.Converters.IsTypeBlacklisted(typeof(ReadOnlySpan))); + + _output.WriteLine("Blacklisting ReadOnlySpan<> blacklists all ReadOnlySpan types"); + } +#endif + + #endregion + #region Nested Generic and Array Combinations [Fact] diff --git a/ReflectorNet/src/Reflector/Reflector.Registry.cs b/ReflectorNet/src/Reflector/Reflector.Registry.cs index ad008c37..5e8ef54b 100644 --- a/ReflectorNet/src/Reflector/Reflector.Registry.cs +++ b/ReflectorNet/src/Reflector/Reflector.Registry.cs @@ -260,9 +260,18 @@ private bool IsTypeBlacklistedInternal(Type? type, HashSet visited, Concur return true; } - // Check if it's a generic type and any type argument is blacklisted + // Check if it's a generic type if (type.IsGenericType) { + // Check if the generic type definition is blacklisted (e.g., List<> blacklisted means List is also blacklisted) + if (!type.IsGenericTypeDefinition) + { + var genericDefinition = type.GetGenericTypeDefinition(); + if (_blacklistedTypes.ContainsKey(genericDefinition)) + return true; + } + + // Check if any type argument is blacklisted var genericArgs = type.GetGenericArguments(); for (int i = 0; i < genericArgs.Length; i++) {