diff --git a/src/MSTest.Analyzer/MT1001CodeFixProvider.cs b/src/MSTest.Analyzer/MT1001CodeFixProvider.cs index 20c81e1..d984c1e 100644 --- a/src/MSTest.Analyzer/MT1001CodeFixProvider.cs +++ b/src/MSTest.Analyzer/MT1001CodeFixProvider.cs @@ -56,10 +56,18 @@ public sealed override Task RegisterCodeFixesAsync(CodeFixContext context) private static SyntaxNode AddTestUsingDirective(SyntaxNode root) { var compilationUnit = root.AncestorsAndSelf().OfType().First(); - var updated = compilationUnit.AddUsings( - SyntaxFactory.UsingDirective(SyntaxFactory.ParseName(MSTestConstants.Namespace))); + var updatedCompilationUnit = compilationUnit; - return root.ReplaceNode(compilationUnit, updated); + var existing = compilationUnit.Usings + .FirstOrDefault(x => x.GetText().ToString().Contains(MSTestConstants.Namespace)); + + if (existing == null) + { + updatedCompilationUnit = compilationUnit.AddUsings( + SyntaxFactory.UsingDirective(SyntaxFactory.ParseName(MSTestConstants.Namespace))); + } + + return root.ReplaceNode(compilationUnit, updatedCompilationUnit); } private static async Task AddTestClassAttributeAsync(Document document, Diagnostic diagnostic, CancellationToken cancellationToken) @@ -72,13 +80,14 @@ private static async Task AddTestClassAttributeAsync(Document document .OfType() .First(); + var leadingTrivia = node.GetLeadingTrivia(); var attributes = node.AttributeLists.Add( SyntaxFactory.AttributeList( SyntaxFactory.SingletonSeparatedList( SyntaxFactory.Attribute( SyntaxFactory.IdentifierName(MSTestConstants.TestClass.Replace("Attribute", string.Empty))))) .WithTrailingTrivia(SyntaxFactory.Whitespace("\r\n"))); - root = root.ReplaceNode(node, node.WithAttributeLists(attributes)); + root = root.ReplaceNode(node, node.WithoutLeadingTrivia().WithAttributeLists(attributes).WithLeadingTrivia(leadingTrivia)); // Add the using directive root = AddTestUsingDirective(root); diff --git a/src/MSTest.Analyzer/MT1003CodeFixProvider.cs b/src/MSTest.Analyzer/MT1003CodeFixProvider.cs index 717a875..4cb69c0 100644 --- a/src/MSTest.Analyzer/MT1003CodeFixProvider.cs +++ b/src/MSTest.Analyzer/MT1003CodeFixProvider.cs @@ -56,10 +56,18 @@ public sealed override Task RegisterCodeFixesAsync(CodeFixContext context) private static SyntaxNode AddTestUsingDirective(SyntaxNode root) { var compilationUnit = root.AncestorsAndSelf().OfType().First(); - var updated = compilationUnit.AddUsings( - SyntaxFactory.UsingDirective(SyntaxFactory.ParseName(MSTestConstants.Namespace))); + var updatedCompilationUnit = compilationUnit; - return root.ReplaceNode(compilationUnit, updated); + var existing = compilationUnit.Usings + .FirstOrDefault(x => x.GetText().ToString().Contains(MSTestConstants.Namespace)); + + if (existing == null) + { + updatedCompilationUnit = compilationUnit.AddUsings( + SyntaxFactory.UsingDirective(SyntaxFactory.ParseName(MSTestConstants.Namespace))); + } + + return root.ReplaceNode(compilationUnit, updatedCompilationUnit); } private static async Task AddTestMethodAttributeAsync(Document document, Diagnostic diagnostic, CancellationToken cancellationToken) @@ -72,13 +80,14 @@ private static async Task AddTestMethodAttributeAsync(Document documen .OfType() .First(); + var leadingTrivia = node.GetLeadingTrivia(); var attributes = node.AttributeLists.Add( SyntaxFactory.AttributeList( SyntaxFactory.SingletonSeparatedList( SyntaxFactory.Attribute( SyntaxFactory.IdentifierName(MSTestConstants.TestMethod.Replace("Attribute", string.Empty))))) .WithTrailingTrivia(SyntaxFactory.Whitespace("\r\n"))); - root = root.ReplaceNode(node, node.WithAttributeLists(attributes)); + root = root.ReplaceNode(node, node.WithoutLeadingTrivia().WithAttributeLists(attributes).WithLeadingTrivia(leadingTrivia)); // Add the using directive root = AddTestUsingDirective(root); diff --git a/test/MSTest.Analyzer.Tests/MT1001UnitTest.cs b/test/MSTest.Analyzer.Tests/MT1001UnitTest.cs index c99f08d..15e97c9 100644 --- a/test/MSTest.Analyzer.Tests/MT1001UnitTest.cs +++ b/test/MSTest.Analyzer.Tests/MT1001UnitTest.cs @@ -107,6 +107,52 @@ public class TypeName this.VerifyCSharpFix(test, fixtest); } + /// + /// Tests reformatting is correct with documentation blocks + /// + [TestMethod] + public void ReformatsCorrectlyWithDocumentation() + { + var test = @" +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace TestPackage +{ + /// + /// Tests the class. + /// + public class TypeName + { + } +}"; + + var expected = new DiagnosticResult + { + Id = "MT1001", + Message = "The class 'TypeName' should be marked with the [TestClass] attribute", + Severity = DiagnosticSeverity.Warning, + Location = new DiagnosticResultLocation("Test0.cs", 9, 18) + }; + + this.VerifyCSharpDiagnostic(test, expected); + + var fixtest = @" +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace TestPackage +{ + /// + /// Tests the class. + /// + [TestClass] + public class TypeName + { + } +}"; + + this.VerifyCSharpFix(test, fixtest); + } + /// /// Tests with an invalid structure. /// diff --git a/test/MSTest.Analyzer.Tests/MT1003UnitTest.cs b/test/MSTest.Analyzer.Tests/MT1003UnitTest.cs index 3e80a36..58f293c 100644 --- a/test/MSTest.Analyzer.Tests/MT1003UnitTest.cs +++ b/test/MSTest.Analyzer.Tests/MT1003UnitTest.cs @@ -99,6 +99,66 @@ public void MethodName() this.VerifyCSharpFix(test, fixtest); } + /// + /// Tests reformatting is correct with documentation blocks + /// + [TestMethod] + public void ReformatsCorrectlyWithDocumentation() + { + var test = @" +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace MyTest +{ + /// + /// Tests the class. + /// + [TestClass] + public class MyClassTest + { + /// + /// Tests the constructor. + /// + public void Constructor() + { + } + } +}"; + + var expected = new DiagnosticResult + { + Id = "MT1003", + Message = "The method 'MyClassTest.Constructor' is public and should be marked with one of the test attribute", + Severity = DiagnosticSeverity.Warning, + Location = new DiagnosticResultLocation("Test0.cs", 15, 21) + }; + + this.VerifyCSharpDiagnostic(test, expected); + + var fixtest = @" +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace MyTest +{ + /// + /// Tests the class. + /// + [TestClass] + public class MyClassTest + { + /// + /// Tests the constructor. + /// + [TestMethod] + public void Constructor() + { + } + } +}"; + + this.VerifyCSharpFix(test, fixtest); + } + /// /// Creates a new instance of the CSharp diagnostic analyzer begin tested. ///