11using System ;
22using System . Collections . Generic ;
3+ using System . Linq ;
4+ using System . Threading ;
5+ using System . Threading . Tasks ;
36using Xunit ;
47using Xunit . Abstractions ;
58
@@ -22,6 +25,12 @@ public class ImplementsBlacklistedInterface : IBlacklistedInterface { }
2225 public interface IBlacklistedGenericInterface < T > { }
2326 public class ImplementsBlacklistedGenericInterface : IBlacklistedGenericInterface < string > { }
2427
28+ // Generic interface with blacklisted type argument testing
29+ public interface IGenericInterface < T > { }
30+ public class ImplementsGenericInterfaceWithBlacklisted : IGenericInterface < BlacklistedBaseClass > { }
31+ public class ImplementsGenericInterfaceWithDerived : IGenericInterface < DerivedFromBlacklisted > { }
32+ public class ImplementsGenericInterfaceWithNonBlacklisted : IGenericInterface < NonBlacklistedClass > { }
33+
2534 // Non-blacklisted types for negative tests
2635 public class NonBlacklistedClass { }
2736 public class AnotherNonBlacklistedClass { }
@@ -211,6 +220,51 @@ public void IsTypeBlacklisted_ImplementsIDisposable_WhenBlacklisted_ReturnsTrue(
211220 _output . WriteLine ( "MemoryStream (implements IDisposable) correctly detected as blacklisted" ) ;
212221 }
213222
223+ [ Fact ]
224+ public void IsTypeBlacklisted_ImplementsGenericInterfaceWithBlacklistedTypeArg_ReturnsTrue ( )
225+ {
226+ // Arrange
227+ var reflector = new Reflector ( ) ;
228+ reflector . Converters . BlacklistType ( typeof ( BlacklistedBaseClass ) ) ;
229+
230+ // Act - Class implements IGenericInterface<BlacklistedBaseClass>
231+ var result = reflector . Converters . IsTypeBlacklisted ( typeof ( ImplementsGenericInterfaceWithBlacklisted ) ) ;
232+
233+ // Assert
234+ Assert . True ( result ) ;
235+ _output . WriteLine ( "Type implementing generic interface with blacklisted type argument correctly returns true" ) ;
236+ }
237+
238+ [ Fact ]
239+ public void IsTypeBlacklisted_ImplementsGenericInterfaceWithDerivedBlacklistedTypeArg_ReturnsTrue ( )
240+ {
241+ // Arrange
242+ var reflector = new Reflector ( ) ;
243+ reflector . Converters . BlacklistType ( typeof ( BlacklistedBaseClass ) ) ;
244+
245+ // Act - Class implements IGenericInterface<DerivedFromBlacklisted> where DerivedFromBlacklisted extends BlacklistedBaseClass
246+ var result = reflector . Converters . IsTypeBlacklisted ( typeof ( ImplementsGenericInterfaceWithDerived ) ) ;
247+
248+ // Assert
249+ Assert . True ( result ) ;
250+ _output . WriteLine ( "Type implementing generic interface with derived blacklisted type argument correctly returns true" ) ;
251+ }
252+
253+ [ Fact ]
254+ public void IsTypeBlacklisted_ImplementsGenericInterfaceWithNonBlacklistedTypeArg_ReturnsFalse ( )
255+ {
256+ // Arrange
257+ var reflector = new Reflector ( ) ;
258+ reflector . Converters . BlacklistType ( typeof ( BlacklistedBaseClass ) ) ;
259+
260+ // Act - Class implements IGenericInterface<NonBlacklistedClass>
261+ var result = reflector . Converters . IsTypeBlacklisted ( typeof ( ImplementsGenericInterfaceWithNonBlacklisted ) ) ;
262+
263+ // Assert
264+ Assert . False ( result ) ;
265+ _output . WriteLine ( "Type implementing generic interface with non-blacklisted type argument correctly returns false" ) ;
266+ }
267+
214268 #endregion
215269
216270 #region Array Tests
@@ -757,5 +811,208 @@ public void IsTypeBlacklisted_SeparateReflectorInstances_IndependentBlacklists()
757811 }
758812
759813 #endregion
814+
815+ #region Inheritance with Generics Tests
816+
817+ public class GenericBase < T > { }
818+
819+ // Case 1: Extended from a class that has a generic argument which is blacklisted
820+ public class DerivedFromGenericWithBlacklistedArg : GenericBase < BlacklistedBaseClass > { }
821+
822+ // Case 3: Extended from a class that extends a class with blacklisted generic arg
823+ public class DeeplyDerived : DerivedFromGenericWithBlacklistedArg { }
824+
825+ [ Fact ]
826+ public void IsTypeBlacklisted_DerivedFromGenericWithBlacklistedArg_ReturnsTrue ( )
827+ {
828+ var reflector = new Reflector ( ) ;
829+ reflector . Converters . BlacklistType ( typeof ( BlacklistedBaseClass ) ) ;
830+
831+ Assert . True ( reflector . Converters . IsTypeBlacklisted ( typeof ( DerivedFromGenericWithBlacklistedArg ) ) ,
832+ "DerivedFromGenericWithBlacklistedArg should be blacklisted because it inherits from GenericBase<BlacklistedBaseClass>" ) ;
833+ }
834+
835+ [ Fact ]
836+ public void IsTypeBlacklisted_DeeplyDerivedFromGenericWithBlacklistedArg_ReturnsTrue ( )
837+ {
838+ var reflector = new Reflector ( ) ;
839+ reflector . Converters . BlacklistType ( typeof ( BlacklistedBaseClass ) ) ;
840+
841+ Assert . True ( reflector . Converters . IsTypeBlacklisted ( typeof ( DeeplyDerived ) ) ,
842+ "DeeplyDerived should be blacklisted because it inherits from DerivedFromGenericWithBlacklistedArg" ) ;
843+ }
844+
845+ #endregion
846+
847+ #region Concurrency and Cache Tests
848+
849+ [ Fact ]
850+ public async Task IsTypeBlacklisted_ConcurrentAccess_NoExceptionsAndCorrectResults ( )
851+ {
852+ // Arrange
853+ var reflector = new Reflector ( ) ;
854+ var exceptions = new System . Collections . Concurrent . ConcurrentBag < Exception > ( ) ;
855+ var results = new System . Collections . Concurrent . ConcurrentBag < bool > ( ) ;
856+ var barrier = new Barrier ( 4 ) ;
857+
858+ // Act - Run concurrent reads and writes
859+ var tasks = new Task [ 4 ] ;
860+
861+ // Task 0: Adds types to blacklist
862+ tasks [ 0 ] = Task . Run ( ( ) =>
863+ {
864+ try
865+ {
866+ barrier . SignalAndWait ( ) ;
867+ for ( int i = 0 ; i < 100 ; i ++ )
868+ {
869+ reflector . Converters . BlacklistType ( typeof ( BlacklistedBaseClass ) ) ;
870+ reflector . Converters . BlacklistType ( typeof ( IBlacklistedInterface ) ) ;
871+ }
872+ }
873+ catch ( Exception ex ) { exceptions . Add ( ex ) ; }
874+ } ) ;
875+
876+ // Tasks 1-3: Read from blacklist concurrently
877+ for ( int t = 1 ; t < 4 ; t ++ )
878+ {
879+ tasks [ t ] = Task . Run ( ( ) =>
880+ {
881+ try
882+ {
883+ barrier . SignalAndWait ( ) ;
884+ for ( int i = 0 ; i < 100 ; i ++ )
885+ {
886+ results . Add ( reflector . Converters . IsTypeBlacklisted ( typeof ( DerivedFromBlacklisted ) ) ) ;
887+ results . Add ( reflector . Converters . IsTypeBlacklisted ( typeof ( NonBlacklistedClass ) ) ) ;
888+ results . Add ( reflector . Converters . IsTypeBlacklisted ( typeof ( List < BlacklistedBaseClass > ) ) ) ;
889+ }
890+ }
891+ catch ( Exception ex ) { exceptions . Add ( ex ) ; }
892+ } ) ;
893+ }
894+
895+ await Task . WhenAll ( tasks ) ;
896+
897+ // Assert
898+ Assert . Empty ( exceptions ) ;
899+ _output . WriteLine ( $ "Completed { results . Count } concurrent blacklist checks without exceptions") ;
900+ }
901+
902+ [ Fact ]
903+ public async Task IsTypeBlacklisted_ConcurrentBlacklistModification_RetriesAndReturnsCorrectResult ( )
904+ {
905+ // Arrange
906+ var reflector = new Reflector ( ) ;
907+ var correctResultsCount = 0 ;
908+ var barrier = new Barrier ( 2 ) ;
909+
910+ // Act - One thread modifies blacklist while another reads
911+ var writerTask = Task . Run ( ( ) =>
912+ {
913+ barrier . SignalAndWait ( ) ;
914+ for ( int i = 0 ; i < 50 ; i ++ )
915+ {
916+ reflector . Converters . BlacklistType ( typeof ( BlacklistedBaseClass ) ) ;
917+ Thread . Sleep ( 1 ) ; // Small delay to interleave
918+ reflector . Converters . RemoveBlacklistedType ( typeof ( BlacklistedBaseClass ) ) ;
919+ }
920+ // End with type blacklisted
921+ reflector . Converters . BlacklistType ( typeof ( BlacklistedBaseClass ) ) ;
922+ } ) ;
923+
924+ var readerTask = Task . Run ( ( ) =>
925+ {
926+ barrier . SignalAndWait ( ) ;
927+ for ( int i = 0 ; i < 100 ; i ++ )
928+ {
929+ // Result may vary during concurrent modification, but should never throw
930+ var result = reflector . Converters . IsTypeBlacklisted ( typeof ( DerivedFromBlacklisted ) ) ;
931+ if ( result ) Interlocked . Increment ( ref correctResultsCount ) ;
932+ }
933+ } ) ;
934+
935+ await Task . WhenAll ( writerTask , readerTask ) ;
936+
937+ // Final state should be consistent
938+ Assert . True ( reflector . Converters . IsTypeBlacklisted ( typeof ( DerivedFromBlacklisted ) ) ,
939+ "After writer finishes with BlacklistType, derived types should be blacklisted" ) ;
940+ _output . WriteLine ( $ "Reader got 'true' result { correctResultsCount } times during concurrent modification") ;
941+ }
942+
943+ // Helper types for cache size test - generate many unique types
944+ public class CacheTestType1 { }
945+ public class CacheTestType2 { }
946+ public class CacheTestType3 { }
947+ public class CacheTestType4 { }
948+ public class CacheTestType5 { }
949+
950+ [ Fact ]
951+ public void IsTypeBlacklisted_CacheSizeLimit_ClearsWhenExceeded ( )
952+ {
953+ // Arrange
954+ var reflector = new Reflector ( ) ;
955+ reflector . Converters . BlacklistType ( typeof ( BlacklistedBaseClass ) ) ;
956+
957+ // Get all types from the current assembly to fill the cache
958+ var types = typeof ( IsTypeBlacklistedTests ) . Assembly . GetTypes ( )
959+ . Where ( t => t != null )
960+ . Take ( 1100 ) // More than MaxBlacklistCacheSize (1000)
961+ . ToList ( ) ;
962+
963+ // Act - Query many types to fill the cache beyond its limit
964+ foreach ( var type in types )
965+ {
966+ try
967+ {
968+ reflector . Converters . IsTypeBlacklisted ( type ) ;
969+ }
970+ catch
971+ {
972+ // Some types may throw during reflection, ignore
973+ }
974+ }
975+
976+ // Query again - should still work correctly after cache was cleared
977+ var result1 = reflector . Converters . IsTypeBlacklisted ( typeof ( BlacklistedBaseClass ) ) ;
978+ var result2 = reflector . Converters . IsTypeBlacklisted ( typeof ( DerivedFromBlacklisted ) ) ;
979+ var result3 = reflector . Converters . IsTypeBlacklisted ( typeof ( NonBlacklistedClass ) ) ;
980+
981+ // Assert - Results should still be correct after cache overflow
982+ Assert . True ( result1 , "Exact blacklisted type should return true" ) ;
983+ Assert . True ( result2 , "Derived from blacklisted type should return true" ) ;
984+ Assert . False ( result3 , "Non-blacklisted type should return false" ) ;
985+
986+ _output . WriteLine ( $ "Queried { types . Count } types, cache correctly handles overflow") ;
987+ }
988+
989+ [ Fact ]
990+ public void IsTypeBlacklisted_CacheInvalidation_ReturnsCorrectResultAfterBlacklistChange ( )
991+ {
992+ // Arrange
993+ var reflector = new Reflector ( ) ;
994+
995+ // Prime the cache with a "false" result
996+ var initialResult = reflector . Converters . IsTypeBlacklisted ( typeof ( NonBlacklistedClass ) ) ;
997+ Assert . False ( initialResult ) ;
998+
999+ // Act - Blacklist the type
1000+ reflector . Converters . BlacklistType ( typeof ( NonBlacklistedClass ) ) ;
1001+
1002+ // Assert - Cache should be invalidated, new result should be correct
1003+ var afterBlacklistResult = reflector . Converters . IsTypeBlacklisted ( typeof ( NonBlacklistedClass ) ) ;
1004+ Assert . True ( afterBlacklistResult , "After blacklisting, type should be detected as blacklisted" ) ;
1005+
1006+ // Act - Remove from blacklist
1007+ reflector . Converters . RemoveBlacklistedType ( typeof ( NonBlacklistedClass ) ) ;
1008+
1009+ // Assert - Cache should be invalidated again
1010+ var afterRemoveResult = reflector . Converters . IsTypeBlacklisted ( typeof ( NonBlacklistedClass ) ) ;
1011+ Assert . False ( afterRemoveResult , "After removing from blacklist, type should not be detected as blacklisted" ) ;
1012+
1013+ _output . WriteLine ( "Cache correctly invalidated after blacklist modifications" ) ;
1014+ }
1015+
1016+ #endregion
7601017 }
7611018}
0 commit comments