Skip to content

Commit

Permalink
Update xUnit1041 to support [Collection(typeof(T))] and [Collection<T>]
Browse files Browse the repository at this point in the history
  • Loading branch information
bradwilson committed Jul 18, 2024
1 parent 9823e50 commit d0e74e4
Show file tree
Hide file tree
Showing 2 changed files with 61 additions and 38 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CSharp;
using Xunit;
using Verify = CSharpVerifier<Xunit.Analyzers.EnsureFixturesHaveASource>;

Expand Down Expand Up @@ -177,31 +178,42 @@ [Fact] public void TestMethod() {{ }}
await Verify.VerifyAnalyzer(source);
}

[Fact]
public async Task WithInheritedFixture_DoesNotTrigger()
[Theory]
[InlineData("[CollectionDefinition(nameof(TestCollection))]", "[Collection(nameof(TestCollection))]", true)]
[InlineData("", "[Collection(typeof(TestCollection))]", false)]
#if NETCOREAPP && ROSLYN_4_4_OR_GREATER // C# 11 is required for generic attributes
[InlineData("", "[Collection<TestCollection>]", false, LanguageVersion.CSharp11)]
#endif
public async Task WithInheritedFixture_DoesNotTrigger(
string collectionDefinition,
string collectionReference,
bool supportedByV2,
LanguageVersion? languageVersion = null)
{
var source = /* lang=c#-test */ """
var source = string.Format(/* lang=c#-test */ """
using Xunit;

public class Fixture { }
public class Fixture {{ }}

[CollectionDefinition("test")]
public class TestCollection : ICollectionFixture<Fixture> { }
{0}
public class TestCollection : ICollectionFixture<Fixture> {{ }}

public abstract class TestContext {
protected TestContext(Fixture fixture) { }
}
public abstract class TestContext {{
protected TestContext(Fixture fixture) {{ }}
}}

[Collection("test")]
public class TestClass : TestContext {
public TestClass(Fixture fixture) : base(fixture) { }
{1}
public class TestClass : TestContext {{
public TestClass(Fixture fixture) : base(fixture) {{ }}

[Fact]
public void TestMethod() { }
}
""";
public void TestMethod() {{ }}
}}
""", collectionDefinition, collectionReference);

await Verify.VerifyAnalyzer(source);
if (supportedByV2)
await Verify.VerifyAnalyzerV2(languageVersion ?? LanguageVersion.CSharp6, source);
await Verify.VerifyAnalyzerV3(languageVersion ?? LanguageVersion.CSharp6, source);
}

[Fact]
Expand Down
55 changes: 33 additions & 22 deletions src/xunit.analyzers/X1000/EnsureFixturesHaveASource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,14 +40,24 @@ public override void AnalyzeCompilation(
// Get the collection name from [Collection], if present
var collectionAttributeType = xunitContext.Core.CollectionAttributeType;
string? collectionDefinitionName = null;
object? collectionDefinition = null;
for (var type = namedType; type is not null && collectionDefinitionName is null; type = type.BaseType)
collectionDefinitionName =
for (var type = namedType; type is not null && collectionDefinition is null; type = type.BaseType)
{
var attribute =
type
.GetAttributes()
.FirstOrDefault(a => a.AttributeClass.IsAssignableFrom(collectionAttributeType))
?.ConstructorArguments.FirstOrDefault().Value?.ToString();
.FirstOrDefault(a => a.AttributeClass.IsAssignableFrom(collectionAttributeType));
if (attribute is null)
continue;
if (attribute.AttributeClass?.IsGenericType == true)
// Using [Collection<T>]
collectionDefinition = attribute.AttributeClass.TypeArguments.FirstOrDefault();
else
// Using [Collection("name"))] or [Collection(typeof(T))]
collectionDefinition = attribute.ConstructorArguments.FirstOrDefault().Value;
}
// Need to construct a full set of types we know can be resolved. Start with things
// like ITestOutputHelper and ITestContextAccessor (since they're injected by the framework)
Expand All @@ -67,8 +77,8 @@ public override void AnalyzeCompilation(
);
// Add types from IClassFixture<> and ICollectionFixture<> on the collection definition
var collectionFixtureTypes = ImmutableHashSet<INamedTypeSymbol>.Empty;
if (collectionDefinitionName is not null)
var matchingType = collectionDefinition as INamedTypeSymbol;
if (matchingType is null && collectionDefinition is string collectionDefinitionName)
{
var collectionDefinitionAttributeType = xunitContext.Core.CollectionDefinitionAttributeType;
Expand All @@ -79,24 +89,25 @@ bool MatchCollectionDefinition(INamedTypeSymbol symbol) =>
a.ConstructorArguments[0].Value?.ToString() == collectionDefinitionName
);
var matchingType = namedType.ContainingAssembly.FindNamedType(MatchCollectionDefinition);
if (matchingType is not null)
matchingType = namedType.ContainingAssembly.FindNamedType(MatchCollectionDefinition);
}
if (matchingType is not null)
{
var collectionFixtureType = xunitContext.Core.ICollectionFixtureType?.ConstructUnboundGenericType();
foreach (var @interface in matchingType.AllInterfaces.Where(i => i.IsGenericType))
{
var collectionFixtureType = xunitContext.Core.ICollectionFixtureType?.ConstructUnboundGenericType();
foreach (var @interface in matchingType.AllInterfaces.Where(i => i.IsGenericType))
var unboundGeneric = @interface.ConstructUnboundGenericType();
if (SymbolEqualityComparer.Default.Equals(classFixtureType, unboundGeneric)
|| SymbolEqualityComparer.Default.Equals(collectionFixtureType, unboundGeneric))
{
var unboundGeneric = @interface.ConstructUnboundGenericType();
if (SymbolEqualityComparer.Default.Equals(classFixtureType, unboundGeneric)
|| SymbolEqualityComparer.Default.Equals(collectionFixtureType, unboundGeneric))
var fixtureTypeSymbol = @interface.TypeArguments.First();
if (fixtureTypeSymbol is INamedTypeSymbol namedFixtureType)
{
var fixtureTypeSymbol = @interface.TypeArguments.First();
if (fixtureTypeSymbol is INamedTypeSymbol namedFixtureType)
{
if (xunitContext.HasV3References && namedFixtureType.IsGenericType && namedFixtureType.TypeArguments.Any(t => t is ITypeParameterSymbol))
namedFixtureType = namedFixtureType.ConstructedFrom;
validConstructorArgumentTypes.Add(namedFixtureType);
}
if (xunitContext.HasV3References && namedFixtureType.IsGenericType && namedFixtureType.TypeArguments.Any(t => t is ITypeParameterSymbol))
namedFixtureType = namedFixtureType.ConstructedFrom;
validConstructorArgumentTypes.Add(namedFixtureType);
}
}
}
Expand Down

0 comments on commit d0e74e4

Please sign in to comment.