Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
using System.Collections.Generic;
using TryAtSoftware.Extensions.Collections;

[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
Copy link

Copilot AI Jan 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While allowing multiple CleanUtility attributes on the same class is technically supported by the registration code, there's a critical constraint that needs to be documented: each attribute on the same class must have a unique combination of Category and Name. The utility ID is computed as "c:{Category}|n:{Name}" (see ICleanUtilityDescriptor interface), and duplicate IDs will cause a runtime exception during registration (see CleanTestAssemblyData constructor line 33: "Two clean utilities with the same identifier cannot co-exist."). Consider adding this constraint to the attribute's documentation or implementing validation to provide a clearer error message when duplicate combinations are detected.

Copilot uses AI. Check for mistakes.
Copy link

Copilot AI Jan 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This feature enhancement lacks test coverage. While the existing code can handle multiple attributes through iteration, there are no tests validating this new capability. Consider adding tests that cover: (1) successfully registering a class with multiple CleanUtility attributes with different category/name combinations, (2) verifying that duplicate category/name combinations on the same class are properly rejected, and (3) ensuring that utilities registered through multiple attributes are correctly available for test discovery and execution.

Copilot uses AI. Check for mistakes.
public class CleanUtilityAttribute : Attribute
{
public string Name { get; }
Expand Down
6 changes: 3 additions & 3 deletions TryAtSoftware.CleanTests.Core/XUnit/CleanTestFramework.cs
Original file line number Diff line number Diff line change
Expand Up @@ -67,16 +67,16 @@ private static void RegisterUtilitiesFromAssembly(IAssemblyInfo assemblyInfo, Li
{
if (type.IsAbstract) continue;

var initializationUtilityAttributes = type.GetCustomAttributes(typeof(CleanUtilityAttribute)).ToArray();
if (initializationUtilityAttributes.Length == 0) continue;
var cleanUtilityAttributes = type.GetCustomAttributes(typeof(CleanUtilityAttribute)).ToArray();
Copy link

Copilot AI Jan 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The variable has been renamed from 'initializationUtilityAttributes' to 'cleanUtilityAttributes' for consistency with the attribute name. However, this is only a partial renaming - the variable 'initializationUtility' at line 86 still uses the old naming convention. Consider renaming it to 'cleanUtility' or 'cleanUtilityDescriptor' for consistency throughout the method.

Copilot uses AI. Check for mistakes.
if (cleanUtilityAttributes.Length == 0) continue;

var decoratedType = new DecoratedType(type);
var externalDemands = decoratedType.ExtractDemands<ExternalDemandsAttribute>();
var internalDemands = decoratedType.ExtractDemands<InternalDemandsAttribute>();
var outerDemands = decoratedType.ExtractDemands<OuterDemandsAttribute>();
var requirements = ExtractRequirements(type);

foreach (var utilityAttribute in initializationUtilityAttributes.OrEmptyIfNull().IgnoreNullValues())
foreach (var utilityAttribute in cleanUtilityAttributes.OrEmptyIfNull().IgnoreNullValues())
{
var categoryArgument = utilityAttribute.GetNamedArgument<string>(nameof(CleanUtilityAttribute.Category));
var nameArgument = utilityAttribute.GetNamedArgument<string>(nameof(CleanUtilityAttribute.Name));
Expand Down
Loading