Skip to content

Commit d3d3536

Browse files
authored
xUnit TheoryData Migration Code Fix (#2464)
* Theory Data test * xUnit TheoryData Migration Code Fix
1 parent 1607824 commit d3d3536

File tree

2 files changed

+115
-0
lines changed

2 files changed

+115
-0
lines changed

TUnit.Analyzers.CodeFixers/XUnitMigrationCodeFixProvider.cs

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,10 +70,66 @@ private static async Task<Document> ConvertCodeAsync(Document document, Cancella
7070
updatedRoot = RemoveUsingDirectives(updatedRoot);
7171
UpdateSyntaxTrees(ref compilation, ref syntaxTree, updatedRoot);
7272

73+
updatedRoot = ConvertTheoryData(compilation, updatedRoot);
74+
UpdateSyntaxTrees(ref compilation, ref syntaxTree, updatedRoot);
75+
7376
// Apply all changes in one step
7477
return document.WithSyntaxRoot(updatedRoot);
7578
}
7679

80+
private static SyntaxNode ConvertTheoryData(Compilation compilation, SyntaxNode root)
81+
{
82+
var currentRoot = root;
83+
foreach (var objectCreationExpressionSyntax in currentRoot.DescendantNodes().OfType<BaseObjectCreationExpressionSyntax>())
84+
{
85+
var type = objectCreationExpressionSyntax switch
86+
{
87+
ObjectCreationExpressionSyntax explicitObjectCreationExpressionSyntax => explicitObjectCreationExpressionSyntax.Type,
88+
ImplicitObjectCreationExpressionSyntax implicitObjectCreationExpressionSyntax => SyntaxFactory.ParseTypeName(compilation.GetSemanticModel(implicitObjectCreationExpressionSyntax.SyntaxTree).GetTypeInfo(implicitObjectCreationExpressionSyntax).Type!.ToDisplayString()),
89+
_ => null
90+
};
91+
92+
if (type is not GenericNameSyntax genericNameSyntax ||
93+
genericNameSyntax.Identifier.Text != "TheoryData")
94+
{
95+
continue;
96+
}
97+
98+
var collectionItems = objectCreationExpressionSyntax.Initializer!
99+
.ChildNodes()
100+
.Select(x => x.DescendantNodesAndSelf().OfType<ExpressionSyntax>().First());
101+
102+
var arrayCreationExpressionSyntax = SyntaxFactory.ArrayCreationExpression(
103+
SyntaxFactory.ArrayType(genericNameSyntax.TypeArgumentList.Arguments[0],
104+
SyntaxFactory.SingletonList(
105+
SyntaxFactory.ArrayRankSpecifier(
106+
SyntaxFactory.SingletonSeparatedList<ExpressionSyntax>(
107+
SyntaxFactory.OmittedArraySizeExpression()
108+
)
109+
)
110+
)
111+
),
112+
SyntaxFactory.InitializerExpression(
113+
SyntaxKind.ArrayInitializerExpression,
114+
SyntaxFactory.SeparatedList(collectionItems)
115+
)
116+
).NormalizeWhitespace();
117+
118+
currentRoot = currentRoot.ReplaceNode(objectCreationExpressionSyntax, arrayCreationExpressionSyntax);
119+
}
120+
121+
foreach (var genericTheoryDataTypeSyntax in currentRoot.DescendantNodes().OfType<GenericNameSyntax>().Where(x => x.Identifier.Text == "TheoryData"))
122+
{
123+
var enumerableTypeSyntax = SyntaxFactory.GenericName(
124+
SyntaxFactory.Identifier("IEnumerable"),
125+
SyntaxFactory.TypeArgumentList(SyntaxFactory.SeparatedList(genericTheoryDataTypeSyntax.TypeArgumentList.Arguments)));
126+
127+
currentRoot = currentRoot.ReplaceNode(genericTheoryDataTypeSyntax, enumerableTypeSyntax);
128+
}
129+
130+
return currentRoot.NormalizeWhitespace();
131+
}
132+
77133
private static SyntaxNode UpdateInitializeDispose(Compilation compilation, SyntaxNode root)
78134
{
79135
// Always operate on the latest root

TUnit.Analyzers.Tests/XUnitMigrationAnalyzerTests.cs

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -501,4 +501,63 @@ public ValueTask DisposeAsync()
501501
"""
502502
);
503503
}
504+
505+
[Test]
506+
public async Task TheoryData_Is_Flagged()
507+
{
508+
await CodeFixer
509+
.VerifyAnalyzerAsync(
510+
"""
511+
{|#0:using System;
512+
using Xunit;
513+
514+
public class MyClass
515+
{
516+
public static readonly TheoryData<TimeSpan> Times = new()
517+
{
518+
TimeSpan.FromSeconds(1),
519+
TimeSpan.FromHours(1),
520+
TimeSpan.FromMilliseconds(10)
521+
};
522+
}|}
523+
""",
524+
Verifier.Diagnostic(Rules.XunitMigration).WithLocation(0)
525+
);
526+
}
527+
528+
[Test]
529+
public async Task TheoryData_Can_Be_Converted()
530+
{
531+
await CodeFixer
532+
.VerifyCodeFixAsync(
533+
"""
534+
{|#0:using TUnit.Core;
535+
using Xunit;
536+
537+
public class MyClass
538+
{
539+
public static readonly TheoryData<TimeSpan> Times = new()
540+
{
541+
TimeSpan.FromSeconds(1),
542+
TimeSpan.FromHours(1),
543+
TimeSpan.FromMilliseconds(10)
544+
};
545+
}|}
546+
""",
547+
Verifier.Diagnostic(Rules.XunitMigration).WithLocation(0),
548+
"""
549+
using TUnit.Core;
550+
551+
public class MyClass
552+
{
553+
public static readonly IEnumerable<TimeSpan> Times = new TimeSpan[]
554+
{
555+
TimeSpan.FromSeconds(1),
556+
TimeSpan.FromHours(1),
557+
TimeSpan.FromMilliseconds(10)
558+
};
559+
}
560+
"""
561+
);
562+
}
504563
}

0 commit comments

Comments
 (0)