Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 26 additions & 14 deletions TUnit.Core/StaticPropertyReflectionInitializer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,15 +60,26 @@ public static async Task InitializeStaticPropertiesForType(Type type)
return;
}

// Get all static properties with data source attributes
var staticProperties = type.GetProperties(BindingFlags.Public | BindingFlags.Static)
.Where(p => p.CanWrite && HasDataSourceAttribute(p));
var staticProperties = type.GetProperties(BindingFlags.Public | BindingFlags.Static);

foreach (var property in staticProperties)
{
if (!property.CanWrite)
{
continue;
}

// Single-pass lookup avoids materialising the attribute array twice
// (once to filter, once to fetch) and skips properties with no data source.
var dataSourceAttr = GetFirstDataSourceAttribute(property);
if (dataSourceAttr is null)
{
continue;
}

try
{
await InitializeStaticProperty(type, property);
await InitializeStaticProperty(property, dataSourceAttr);
}
catch (Exception ex)
{
Expand All @@ -78,23 +89,24 @@ public static async Task InitializeStaticPropertiesForType(Type type)
}
}

private static bool HasDataSourceAttribute(PropertyInfo property)
private static IDataSourceAttribute? GetFirstDataSourceAttribute(PropertyInfo property)
{
return property.GetCustomAttributes()
.Any(attr => attr is IDataSourceAttribute);
foreach (var attribute in property.GetCustomAttributes())
{
if (attribute is IDataSourceAttribute dataSource)
{
return dataSource;
}
}

return null;
}

#if NET8_0_OR_GREATER
[RequiresUnreferencedCode("Data source initialization may require dynamic code generation")]
#endif
private static async Task InitializeStaticProperty(Type type, PropertyInfo property)
private static async Task InitializeStaticProperty(PropertyInfo property, IDataSourceAttribute dataSourceAttr)
{
if (property.GetCustomAttributes()
.FirstOrDefault(attr => attr is IDataSourceAttribute) is not IDataSourceAttribute dataSourceAttr)
{
return;
}

// Create metadata for the data source
var metadata = new DataGeneratorMetadata
{
Expand Down
26 changes: 18 additions & 8 deletions TUnit.Engine/Discovery/ReflectionInstanceFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -62,19 +62,16 @@ private static async Task InjectPropertiesAsync(object instance, Type type)
continue;
}

// Look for data source attributes on the property
var dataSourceAttrs = property.GetCustomAttributes()
.OfType<IDataSourceAttribute>()
.ToArray();
// Look for the first data source attribute on the property.
// Single-pass avoids allocating the OfType iterator + array for the
// common case of properties without a data source attribute.
var dataSource = GetFirstDataSourceAttribute(property);

if (dataSourceAttrs.Length == 0)
if (dataSource is null)
{
continue;
}

// Try to get data from the first data source
var dataSource = dataSourceAttrs[0];

try
{
var metadata = CreatePropertyMetadata(property, type, dataSource);
Expand Down Expand Up @@ -116,6 +113,19 @@ private static async Task InjectPropertiesAsync(object instance, Type type)
}
}

private static IDataSourceAttribute? GetFirstDataSourceAttribute(PropertyInfo property)
{
foreach (var attribute in property.GetCustomAttributes())
{
if (attribute is IDataSourceAttribute dataSource)
{
return dataSource;
}
}

return null;
}

private static DataGeneratorMetadata CreatePropertyMetadata(PropertyInfo property, Type containingType, IDataSourceAttribute dataSource)
{
var propertyMetadata = new PropertyMetadata
Expand Down
Loading