Skip to content
This repository was archived by the owner on Jul 12, 2022. It is now read-only.

Commit 7e08433

Browse files
committed
Add transformation to convert MSTest Assert calls to xUnit
1 parent 6612669 commit 7e08433

File tree

1 file changed

+95
-7
lines changed

1 file changed

+95
-7
lines changed

src/Microsoft.DotNet.CodeFormatting/Rules/UsesXunitForTests.cs

Lines changed: 95 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
using System.ComponentModel.Composition;
1111
using Microsoft.CodeAnalysis.CSharp;
1212
using Microsoft.CodeAnalysis.CodeGeneration;
13+
using System.Runtime.Serialization;
1314

1415
namespace Microsoft.DotNet.CodeFormatting.Rules
1516
{
@@ -70,6 +71,7 @@ public async Task<Document> ProcessAsync(Document document, CancellationToken ca
7071
TransformationTracker transformationTracker = new TransformationTracker();
7172
RemoveTestClassAttributes(root, semanticModel, transformationTracker);
7273
ChangeTestMethodAttributesToFact(root, semanticModel, transformationTracker);
74+
ChangeAssertCalls(root, semanticModel, transformationTracker);
7375
root = transformationTracker.TransformRoot(root);
7476

7577

@@ -121,7 +123,7 @@ private void RemoveTestClassAttributes(CompilationUnitSyntax root, SemanticModel
121123
nodesToRemove.AddRange(attributesToRemove);
122124
}
123125

124-
transformationTracker.AddTransformation(nodesToRemove, (transformationRoot, rewrittenNodes) =>
126+
transformationTracker.AddTransformation(nodesToRemove, (transformationRoot, rewrittenNodes, originalNodeMap) =>
125127
{
126128
foreach (AttributeSyntax rewrittenNode in rewrittenNodes)
127129
{
@@ -157,14 +159,82 @@ private void ChangeTestMethodAttributesToFact(CompilationUnitSyntax root, Semant
157159
}
158160
}
159161

160-
transformationTracker.AddTransformation(nodesToReplace, (transformationRoot, rewrittenNodes) =>
162+
transformationTracker.AddTransformation(nodesToReplace, (transformationRoot, rewrittenNodes, originalNodeMap) =>
161163
{
162164
return transformationRoot.ReplaceNodes(rewrittenNodes, (originalNode, rewrittenNode) =>
163165
{
164166
return ((AttributeSyntax)rewrittenNode).WithName(SyntaxFactory.ParseName("Fact")).NormalizeWhitespace();
165167
});
166168
});
167-
169+
}
170+
171+
private void ChangeAssertCalls(CompilationUnitSyntax root, SemanticModel semanticModel, TransformationTracker transformationTracker)
172+
{
173+
Dictionary<string, string> assertMethodsToRename = new Dictionary<string, string>()
174+
{
175+
{ "AreEqual", "Equal" },
176+
{ "AreNotEqual", "NotEqual" },
177+
{ "IsNull", "Null" },
178+
{ "IsNotNull", "NotNull" },
179+
{ "AreSame", "Same" },
180+
{ "AreNotSame", "NotSame" },
181+
{ "IsTrue", "True" },
182+
{ "IsFalse", "False" },
183+
{ "IsInstanceOfType", "IsAssignableFrom" },
184+
185+
};
186+
187+
Dictionary<SimpleNameSyntax, string> nameReplacementsForNodes = new Dictionary<SimpleNameSyntax, string>();
188+
List<InvocationExpressionSyntax> methodCallsToReverseArguments = new List<InvocationExpressionSyntax>();
189+
190+
foreach (var methodCallSyntax in root.DescendantNodes().OfType<MemberAccessExpressionSyntax>())
191+
{
192+
var expressionSyntax = methodCallSyntax.Expression;
193+
var expressionTypeInfo = semanticModel.GetTypeInfo(expressionSyntax);
194+
if (expressionTypeInfo.Type != null)
195+
{
196+
string expressionDocID = expressionTypeInfo.Type.GetDocumentationCommentId();
197+
if (expressionDocID == "T:Microsoft.VisualStudio.TestTools.UnitTesting.Assert")
198+
{
199+
string newMethodName;
200+
if (assertMethodsToRename.TryGetValue(methodCallSyntax.Name.Identifier.Text, out newMethodName))
201+
{
202+
nameReplacementsForNodes.Add(methodCallSyntax.Name, newMethodName);
203+
204+
if (newMethodName == "IsAssignableFrom" && methodCallSyntax.Parent is InvocationExpressionSyntax)
205+
{
206+
// Parameter order is reversed between MSTest Assert.IsInstanceOfType and xUnit Assert.IsAssignableFrom
207+
methodCallsToReverseArguments.Add((InvocationExpressionSyntax)methodCallSyntax.Parent);
208+
}
209+
}
210+
}
211+
}
212+
}
213+
214+
if (nameReplacementsForNodes.Any())
215+
{
216+
transformationTracker.AddTransformation(nameReplacementsForNodes.Keys, (transformationRoot, rewrittenNodes, originalNodeMap) =>
217+
{
218+
return transformationRoot.ReplaceNodes(rewrittenNodes, (originalNode, rewrittenNode) =>
219+
{
220+
var realOriginalNode = (SimpleNameSyntax)originalNodeMap[originalNode];
221+
string newName = nameReplacementsForNodes[realOriginalNode];
222+
return SyntaxFactory.ParseName(newName);
223+
});
224+
});
225+
226+
transformationTracker.AddTransformation(methodCallsToReverseArguments, (transformationRoot, rewrittenNodes, originalNodeMap) =>
227+
{
228+
return transformationRoot.ReplaceNodes(rewrittenNodes, (originalNode, rewrittenNode) =>
229+
{
230+
var invocationExpression = (InvocationExpressionSyntax)rewrittenNode;
231+
var oldArguments = invocationExpression.ArgumentList.Arguments;
232+
var newArguments = new SeparatedSyntaxList<ArgumentSyntax>().AddRange(new[] { oldArguments[1], oldArguments[0] });
233+
234+
return invocationExpression.WithArgumentList(invocationExpression.ArgumentList.WithArguments(newArguments));
235+
});
236+
});
237+
}
168238
}
169239

170240
private static SyntaxTriviaList RemoveCompilerDirectives(SyntaxTriviaList stl)
@@ -186,10 +256,11 @@ private static SyntaxTriviaList RemoveCompilerDirectives(SyntaxTriviaList stl)
186256

187257
class TransformationTracker
188258
{
189-
Dictionary<SyntaxAnnotation, Func<CompilationUnitSyntax, IEnumerable<SyntaxNode>, CompilationUnitSyntax>> _annotationToTransformation = new Dictionary<SyntaxAnnotation, Func<CompilationUnitSyntax, IEnumerable<SyntaxNode>, CompilationUnitSyntax>>();
259+
Dictionary<SyntaxAnnotation, Func<CompilationUnitSyntax, IEnumerable<SyntaxNode>, Dictionary<SyntaxNode, SyntaxNode>, CompilationUnitSyntax>> _annotationToTransformation = new Dictionary<SyntaxAnnotation, Func<CompilationUnitSyntax, IEnumerable<SyntaxNode>, Dictionary<SyntaxNode, SyntaxNode>, CompilationUnitSyntax>>();
190260
Dictionary<SyntaxNode, List<SyntaxAnnotation>> _nodeToAnnotations = new Dictionary<SyntaxNode, List<SyntaxAnnotation>>();
261+
Dictionary<SyntaxAnnotation, SyntaxNode> _originalNodeLookup = new Dictionary<SyntaxAnnotation, SyntaxNode>();
191262

192-
public void AddTransformation(IEnumerable<SyntaxNode> nodesToTransform, Func<CompilationUnitSyntax, IEnumerable<SyntaxNode>, CompilationUnitSyntax> transformerFunc)
263+
public void AddTransformation(IEnumerable<SyntaxNode> nodesToTransform, Func<CompilationUnitSyntax, IEnumerable<SyntaxNode>, Dictionary<SyntaxNode, SyntaxNode>, CompilationUnitSyntax> transformerFunc)
193264
{
194265
var annotation = new SyntaxAnnotation();
195266
_annotationToTransformation[annotation] = transformerFunc;
@@ -203,22 +274,39 @@ public void AddTransformation(IEnumerable<SyntaxNode> nodesToTransform, Func<Com
203274
_nodeToAnnotations[node] = annotationsForNode;
204275
}
205276
annotationsForNode.Add(annotation);
277+
278+
var originalNodeAnnotation = new SyntaxAnnotation();
279+
_originalNodeLookup[originalNodeAnnotation] = node;
280+
annotationsForNode.Add(originalNodeAnnotation);
206281
}
207282
}
208283

209284
public CompilationUnitSyntax TransformRoot(CompilationUnitSyntax root)
210285
{
211286
root = root.ReplaceNodes(_nodeToAnnotations.Keys, (originalNode, rewrittenNode) =>
212287
{
213-
return rewrittenNode.WithAdditionalAnnotations(_nodeToAnnotations[originalNode]);
288+
var ret = rewrittenNode.WithAdditionalAnnotations(_nodeToAnnotations[originalNode]);
289+
290+
return ret;
214291
});
215292

216293
foreach (var kvp in _annotationToTransformation)
217294
{
295+
Dictionary<SyntaxNode, SyntaxNode> originalNodeMap = new Dictionary<SyntaxNode, SyntaxNode>();
296+
foreach (var originalNodeKvp in _originalNodeLookup)
297+
{
298+
var annotatedNodes = root.GetAnnotatedNodes(originalNodeKvp.Key).ToList();
299+
SyntaxNode annotatedNode = annotatedNodes.SingleOrDefault();
300+
if (annotatedNode != null)
301+
{
302+
originalNodeMap[annotatedNode] = originalNodeKvp.Value;
303+
}
304+
}
305+
218306
var syntaxAnnotation = kvp.Key;
219307
var transformation = kvp.Value;
220308
var nodesToTransform = root.GetAnnotatedNodes(syntaxAnnotation);
221-
root = transformation(root, nodesToTransform);
309+
root = transformation(root, nodesToTransform, originalNodeMap);
222310
}
223311

224312
return root;

0 commit comments

Comments
 (0)