Skip to content

Commit 35a52b2

Browse files
authored
Merge pull request #64 from IvanMurzak/update/optimize-blacklist
update: Optimized type search by name
2 parents 9c4c006 + d716d47 commit 35a52b2

15 files changed

Lines changed: 3067 additions & 1044 deletions
Lines changed: 309 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,309 @@
1+
/*
2+
* ReflectorNet
3+
* Author: Ivan Murzak (https://github.com/IvanMurzak)
4+
* Copyright (c) 2025 Ivan Murzak
5+
* Licensed under the Apache License, Version 2.0. See LICENSE file in the project root for full license information.
6+
*/
7+
8+
using System;
9+
using System.Collections.Generic;
10+
using System.Linq;
11+
using com.IvanMurzak.ReflectorNet.Tests.TypeUtilsTests;
12+
using com.IvanMurzak.ReflectorNet.Utils;
13+
using Xunit;
14+
using Xunit.Abstractions;
15+
16+
namespace com.IvanMurzak.ReflectorNet.Tests.ReflectorTests
17+
{
18+
/// <summary>
19+
/// Tests for Reflector.Registry.BlacklistTypesInAssembly method.
20+
/// Focuses on verifying that complex types are correctly blacklisted when batch-registered via assembly scan.
21+
/// </summary>
22+
public class BlacklistTypesInAssemblyTests : BaseTest
23+
{
24+
public BlacklistTypesInAssemblyTests(ITestOutputHelper output) : base(output) { }
25+
26+
[Fact]
27+
public void BlacklistTypesInAssembly_ComplexOuterAssemblyTypes_AreBlacklisted()
28+
{
29+
// Arrange
30+
var reflector = new Reflector();
31+
var registry = reflector.Converters;
32+
33+
// We target the loaded assembly "ReflectorNet.Tests.OuterAssembly"
34+
// Note: The assembly name differs from the namespace prefix "com.IvanMurzak..."
35+
var assemblyPrefix = "ReflectorNet.Tests.OuterAssembly";
36+
37+
// Force load the assembly to ensure it's available for scanning
38+
var dummy = typeof(com.IvanMurzak.ReflectorNet.OuterAssembly.Model.OuterSimpleClass);
39+
40+
// Collect all type names defined in the OuterAssembly test data
41+
var typesToBlacklist = new HashSet<string>();
42+
43+
// Add simple and generic types from OuterAssembly
44+
foreach (var kvp in GetTypeIdTests.OuterAssemblyTypes)
45+
{
46+
typesToBlacklist.Add(kvp.Key);
47+
48+
// For generic types, also blacklist the open generic definition.
49+
// This ensures that any constructed generic of this type (even with non-blacklisted args)
50+
// is considered blacklisted.
51+
if (kvp.Value.IsGenericType && !kvp.Value.IsGenericTypeDefinition)
52+
{
53+
var genericDef = kvp.Value.GetGenericTypeDefinition();
54+
// FullName might be null for some types, but should be fine for these class definitions
55+
if (genericDef.FullName != null)
56+
{
57+
typesToBlacklist.Add(genericDef.FullName);
58+
}
59+
}
60+
}
61+
62+
// Add array types from OuterAssembly
63+
foreach (var typeId in GetTypeIdTests.OuterAssemblyArrayTypes.Keys)
64+
{
65+
typesToBlacklist.Add(typeId);
66+
}
67+
68+
_output.WriteLine($"Targeting assembly prefix: {assemblyPrefix}");
69+
_output.WriteLine($"Blacklisting {typesToBlacklist.Count} unique type identifiers via BlacklistTypesInAssembly...");
70+
71+
// Act
72+
// Attempt to blacklist all these types by finding them in OuterAssembly
73+
var changed = registry.BlacklistTypesInAssembly(assemblyPrefix, typesToBlacklist.ToArray());
74+
75+
Assert.True(changed, "BlacklistTypesInAssembly should return true as types should have been added.");
76+
77+
// Assert
78+
79+
// 1. Verify the types we explicitly asked to blacklist are indeed blacklisted
80+
VerifySetIsBlacklisted(registry, GetTypeIdTests.OuterAssemblyTypes, "OuterAssemblyTypes (Direct Targets)");
81+
VerifySetIsBlacklisted(registry, GetTypeIdTests.OuterAssemblyArrayTypes, "OuterAssemblyArrayTypes (Direct Targets)");
82+
83+
// 2. Verify derived complex types are also blacklisted.
84+
VerifySetIsBlacklisted(registry, GetTypeIdTests.ComplexCombinedTypes, "ComplexCombinedTypes (Implicitly Blacklisted)");
85+
}
86+
87+
[Fact]
88+
public void BlacklistTypesInAssembly_BuiltInTypes_AreBlacklisted()
89+
{
90+
// Arrange
91+
var reflector = new Reflector();
92+
var registry = reflector.Converters;
93+
94+
// Act & Assert
95+
BlacklistAndVerifyByTypeId(registry, GetTypeIdTests.BuiltInTypes, "BuiltInTypes");
96+
}
97+
98+
[Fact]
99+
public void BlacklistTypesInAssembly_BuiltInArrayTypes_AreBlacklisted()
100+
{
101+
// Arrange
102+
var reflector = new Reflector();
103+
var registry = reflector.Converters;
104+
105+
// Act & Assert
106+
BlacklistAndVerifyByTypeId(registry, GetTypeIdTests.BuiltInArrayTypes, "BuiltInArrayTypes");
107+
}
108+
109+
[Fact]
110+
public void BlacklistTypesInAssembly_BuiltInGenericTypes_AreBlacklisted()
111+
{
112+
// Arrange
113+
var reflector = new Reflector();
114+
var registry = reflector.Converters;
115+
116+
// Act & Assert
117+
BlacklistAndVerifyByTypeId(registry, GetTypeIdTests.BuiltInGenericTypes, "BuiltInGenericTypes");
118+
}
119+
120+
[Fact]
121+
public void BlacklistTypesInAssembly_NestedGenericTypes_AreBlacklisted()
122+
{
123+
// Arrange
124+
var reflector = new Reflector();
125+
var registry = reflector.Converters;
126+
127+
// Act & Assert
128+
BlacklistAndVerifyByTypeId(registry, GetTypeIdTests.NestedGenericTypes, "NestedGenericTypes");
129+
}
130+
131+
[Fact]
132+
public void BlacklistTypesInAssembly_ThisAssemblyTypes_AreBlacklisted()
133+
{
134+
// Arrange
135+
var reflector = new Reflector();
136+
var registry = reflector.Converters;
137+
138+
// Act & Assert
139+
BlacklistAndVerifyByTypeId(registry, GetTypeIdTests.ThisAssemblyTypes, "ThisAssemblyTypes");
140+
}
141+
142+
[Fact]
143+
public void BlacklistTypesInAssembly_ReflectorNetTypes_AreBlacklisted()
144+
{
145+
// Arrange
146+
var reflector = new Reflector();
147+
var registry = reflector.Converters;
148+
149+
// Act & Assert
150+
BlacklistAndVerifyByTypeId(registry, GetTypeIdTests.ReflectorNetTypes, "ReflectorNetTypes");
151+
}
152+
153+
[Fact]
154+
public void BlacklistTypesInAssembly_OuterAssemblyTypes_AreBlacklisted()
155+
{
156+
// Arrange
157+
var reflector = new Reflector();
158+
var registry = reflector.Converters;
159+
160+
// Force load the assembly to ensure it's available for scanning
161+
var dummy = typeof(com.IvanMurzak.ReflectorNet.OuterAssembly.Model.OuterSimpleClass);
162+
163+
// Act & Assert
164+
BlacklistAndVerifyByTypeId(registry, GetTypeIdTests.OuterAssemblyTypes, "OuterAssemblyTypes");
165+
}
166+
167+
[Fact]
168+
public void BlacklistTypesInAssembly_OuterAssemblyArrayTypes_AreBlacklisted()
169+
{
170+
// Arrange
171+
var reflector = new Reflector();
172+
var registry = reflector.Converters;
173+
174+
// Force load the assembly to ensure it's available for scanning
175+
var dummy = typeof(com.IvanMurzak.ReflectorNet.OuterAssembly.Model.OuterSimpleClass);
176+
177+
// Act & Assert
178+
BlacklistAndVerifyByTypeId(registry, GetTypeIdTests.OuterAssemblyArrayTypes, "OuterAssemblyArrayTypes");
179+
}
180+
181+
[Fact]
182+
public void BlacklistTypesInAssembly_ComplexCombinedTypes_AreBlacklisted()
183+
{
184+
// Arrange
185+
var reflector = new Reflector();
186+
var registry = reflector.Converters;
187+
188+
// Force load the assembly to ensure it's available for scanning
189+
var dummy = typeof(com.IvanMurzak.ReflectorNet.OuterAssembly.Model.OuterSimpleClass);
190+
191+
// Act & Assert
192+
BlacklistAndVerifyByTypeId(registry, GetTypeIdTests.ComplexCombinedTypes, "ComplexCombinedTypes");
193+
}
194+
195+
/// <summary>
196+
/// Blacklists types using their TypeUtils.GetTypeId() string representation with exact assembly matching,
197+
/// then verifies they are correctly blacklisted.
198+
/// </summary>
199+
private void BlacklistAndVerifyByTypeId(Reflector.Registry registry, Dictionary<string, Type> types, string dictionaryName)
200+
{
201+
_output.WriteLine($"### Testing {dictionaryName} ({types.Count} types)\n");
202+
203+
// Group types by their root assembly to blacklist efficiently
204+
var typesByAssembly = new Dictionary<string, List<(string typeId, Type type)>>();
205+
206+
foreach (var kvp in types)
207+
{
208+
var type = kvp.Value;
209+
var typeId = TypeUtils.GetTypeId(type);
210+
211+
// Get the assembly name from the type itself
212+
// For arrays, get element type's assembly; for generics, get the generic definition's assembly
213+
var rootType = GetRootType(type);
214+
var assemblyName = rootType.Assembly.GetName().Name;
215+
216+
if (string.IsNullOrEmpty(assemblyName))
217+
continue;
218+
219+
if (!typesByAssembly.TryGetValue(assemblyName, out var list))
220+
{
221+
list = new List<(string, Type)>();
222+
typesByAssembly[assemblyName] = list;
223+
}
224+
list.Add((typeId, type));
225+
}
226+
227+
// Blacklist types grouped by assembly
228+
var totalBlacklisted = 0;
229+
foreach (var kvp in typesByAssembly)
230+
{
231+
var assemblyName = kvp.Key;
232+
var typeList = kvp.Value;
233+
var typeIds = typeList.Select(t => t.typeId).ToArray();
234+
235+
_output.WriteLine($" Assembly: {assemblyName}");
236+
foreach (var (typeId, type) in typeList)
237+
{
238+
_output.WriteLine($" - {typeId}");
239+
}
240+
241+
var changed = registry.BlacklistTypesInAssembly(assemblyName, typeIds);
242+
if (changed)
243+
totalBlacklisted += typeIds.Length;
244+
}
245+
246+
_output.WriteLine($"\n Blacklisted {totalBlacklisted} types across {typesByAssembly.Count} assemblies.\n");
247+
248+
// Verify all types are blacklisted
249+
VerifySetIsBlacklisted(registry, types, dictionaryName);
250+
}
251+
252+
/// <summary>
253+
/// Gets the root type for assembly resolution.
254+
/// For arrays, returns the element type.
255+
/// For generics, returns the generic type definition.
256+
/// </summary>
257+
private static Type GetRootType(Type type)
258+
{
259+
// Handle arrays - get the element type
260+
if (type.IsArray)
261+
{
262+
var elementType = type.GetElementType();
263+
return elementType != null ? GetRootType(elementType) : type;
264+
}
265+
266+
// Handle constructed generics - get the definition
267+
if (type.IsGenericType && !type.IsGenericTypeDefinition)
268+
{
269+
return type.GetGenericTypeDefinition();
270+
}
271+
272+
return type;
273+
}
274+
275+
private void VerifySetIsBlacklisted(Reflector.Registry registry, Dictionary<string, Type> types, string dictionaryName)
276+
{
277+
_output.WriteLine($"\n### Verifying {dictionaryName} ({types.Count} types)");
278+
279+
var failedTypes = new List<string>();
280+
var passedCount = 0;
281+
282+
foreach (var kvp in types)
283+
{
284+
var typeId = kvp.Key;
285+
var type = kvp.Value;
286+
287+
// Primary check: IsTypeBlacklisted
288+
var isBlacklisted = registry.IsTypeBlacklisted(type);
289+
290+
if (isBlacklisted)
291+
{
292+
passedCount++;
293+
}
294+
else
295+
{
296+
failedTypes.Add(typeId);
297+
_output.WriteLine($" [FAIL] {typeId} is NOT blacklisted.");
298+
}
299+
}
300+
301+
_output.WriteLine($" [SUMMARY] {passedCount}/{types.Count} passed.");
302+
303+
if (failedTypes.Count > 0)
304+
{
305+
Assert.Fail($"Failed to blacklist {failedTypes.Count} types in {dictionaryName}. First failure: {failedTypes[0]}");
306+
}
307+
}
308+
}
309+
}

0 commit comments

Comments
 (0)