private async Task VerifyAsync(string test) { // Arrange var expectedLocation = new DiagnosticLocation("Test.cs", 9, 9); var expectedFix = @" using Microsoft.AspNetCore.Mvc; [ApiController] public class PetController : ControllerBase { public IActionResult GetPetId() { return Ok(); } }"; var project = CreateProject(test); // Act & Assert var actualDiagnostics = await GetDiagnosticAsync(project); AssertDiagnostic(expectedLocation, actualDiagnostics); var actualFix = await ApplyCodeFixAsync(project, actualDiagnostics); Assert.Equal(expectedFix, actualFix, ignoreLineEndingDifferences: true); }
public static TestSource Read(string rawSource) { var testInput = new TestSource(); var lines = rawSource.Split(new[] { "\n", "\r\n" }, StringSplitOptions.None); for (var i = 0; i < lines.Length; i++) { var line = lines[i]; while (true) { var markerStartIndex = line.IndexOf(MarkerStart, StringComparison.Ordinal); if (markerStartIndex == -1) { break; } var markerEndIndex = line.IndexOf(MarkerEnd, markerStartIndex, StringComparison.Ordinal); var markerName = line.Substring(markerStartIndex + 2, markerEndIndex - markerStartIndex - 2); var markerLocation = new DiagnosticLocation(i + 1, markerStartIndex + 1); if (testInput.DefaultMarkerLocation == null) { testInput.DefaultMarkerLocation = markerLocation; } testInput.MarkerLocations.Add(markerName, markerLocation); line = line.Substring(0, markerStartIndex) + line.Substring(markerEndIndex + MarkerEnd.Length); } lines[i] = line; } testInput.Source = string.Join(Environment.NewLine, lines); return(testInput); }
public async Task VerifyCodeFixWillNotRemoveTriviaAsync() { var testCode = @" class TestClass { public void TestMethod() { /* do nothing */ ; } } "; var fixedCode = @" class TestClass { public void TestMethod() { /* do nothing */ } } "; var expected = new DiagnosticLocation(6, 42); await VerifyDiagnosticsAsync(testCode, expected); await VerifyDiagnosticsAsync(fixedCode, EmptyDiagnosticResults); await VerifyCodeFixAsync(testCode, fixedCode); }
public static Diagnostic ToDiagnostic(this DiagnosticLocation location) { var tags = new List <DiagnosticTag>(); foreach (var tag in location?.Tags ?? Array.Empty <string>()) { if (tag == "Unnecessary") { tags.Add(DiagnosticTag.Unnecessary); } if (tag == "Deprecated") { tags.Add(DiagnosticTag.Deprecated); } } return(new Diagnostic() { // We don't have a code at the moment // Code = quickFix., Message = !string.IsNullOrWhiteSpace(location.Text) ? location.Text : location.Id, Range = location.ToRange(), Severity = ToDiagnosticSeverity(location.LogLevel), Code = location.Id, // TODO: We need to forward this type though if we add something like Vb Support Source = "csharp", Tags = tags, }); }
public async Task DiagnosticsAndCodeFixes_WhenApiControllerActionDoesNotHaveAttribute() { // Arrange var expectedLocation = new DiagnosticLocation("Test.cs", 8, 16); var test = @" using Microsoft.AspNetCore.Mvc; [ApiController] [Route] public class PetController : Controller { public int GetPetId() => 0; }"; var expectedFix = @" using Microsoft.AspNetCore.Mvc; [ApiController] [Route] public class PetController : Controller { [HttpGet] public int GetPetId() => 0; }"; var project = CreateProject(test); // Act & Assert var actualDiagnostics = await GetDiagnosticAsync(project); AssertDiagnostic(expectedLocation, actualDiagnostics); var actualFix = await ApplyCodeFixAsync(project, actualDiagnostics); Assert.Equal(expectedFix, actualFix, ignoreLineEndingDifferences: true); }
public async Task Pubternal_type_in_nested_namespace_field___warning_GF0001() { string library = @" namespace gfoidl.Internal.Services { public class Bar { public void Do() { } } }"; TestSource code = TestSource.Read(@" using gfoidl.Internal.Services; namespace MyProgram { internal class Worker { private /*MM*/Bar _bar = new Bar(); } }"); Diagnostic[] diagnostics = await this.GetDiagnosticsWithProjectReference(code.Source, library); DiagnosticLocation expected = code.DefaultMarkerLocation; Assert.Multiple(() => { Assert.AreEqual(1, diagnostics.Length); Diagnostic diagnostic = diagnostics[0]; Assert.AreEqual("GF0001", diagnostic.Id); Assert.IsTrue(diagnostic.Location.IsInSource); Assert.That(diagnostic.Location, Is.EqualTo(expected)); }); }
public async Task CodeFix_ProducesFullyQualifiedNamespaces() { // Arrange var location = new DiagnosticLocation("Test.cs", 6, 18); var test = @" using Microsoft.AspNetCore.Mvc; public class HomeController : Controller { public async void Index() => await Response.Body.FlushAsync(); }"; var expectedFix = @" using Microsoft.AspNetCore.Mvc; public class HomeController : Controller { public async System.Threading.Tasks.Task Index() => await Response.Body.FlushAsync(); }"; var project = CreateProject(test); // Act & Assert var actualDiagnostics = await GetDiagnosticAsync(project); AssertDiagnostic(location, actualDiagnostics); var actualFix = await ApplyCodeFixAsync(project, actualDiagnostics); Assert.Equal(expectedFix, actualFix, ignoreLineEndingDifferences: true); }
public async Task DiagnosticsAreReturned_WhenActionMethodIsExpressionBodied() { // Arrange var location = new DiagnosticLocation("Test.cs", 7, 18); var test = @" using Microsoft.AspNetCore.Mvc; using System.Threading.Tasks; public class HomeController : Controller { public async void Index() => await Response.Body.FlushAsync(); }"; var expectedFix = @" using Microsoft.AspNetCore.Mvc; using System.Threading.Tasks; public class HomeController : Controller { public async Task Index() => await Response.Body.FlushAsync(); }"; var project = CreateProject(test); // Act & Assert var actualDiagnostics = await GetDiagnosticAsync(project); AssertDiagnostic(location, actualDiagnostics); var actualFix = await ApplyCodeFixAsync(project, actualDiagnostics); Assert.Equal(expectedFix, actualFix, ignoreLineEndingDifferences: true); }
public static void DiagnosticLocation(DiagnosticLocation expected, Location actual) { var actualSpan = actual.GetLineSpan(); var actualLinePosition = actualSpan.StartLinePosition; // Only check line position if there is an actual line in the real diagnostic if (actualLinePosition.Line > 0) { if (actualLinePosition.Line + 1 != expected.Line) { throw new DiagnosticLocationAssertException( expected, actual, $"Expected diagnostic to be on line \"{expected.Line}\" was actually on line \"{actualLinePosition.Line + 1}\""); } } // Only check column position if there is an actual column position in the real diagnostic if (actualLinePosition.Character > 0) { if (actualLinePosition.Character + 1 != expected.Column) { throw new DiagnosticLocationAssertException( expected, actual, $"Expected diagnostic to start at column \"{expected.Column}\" was actually on column \"{actualLinePosition.Character + 1}\""); } } }
public DiagnosticLocationAssertException( DiagnosticLocation expected, Location actual, string message) : base(expected, actual) { Message = message; }
public async Task TestMemberAsync(string declaration) { var testCode = declaration + ";"; var fixedCode = declaration; var expected = new DiagnosticLocation(1, declaration.Length + 1); await VerifyDiagnosticsAsync(testCode, expected); await VerifyDiagnosticsAsync(fixedCode, EmptyDiagnosticResults); await VerifyCodeFixAsync(testCode, fixedCode); }
public async Task AG0030_WhenDynamicInInterface_ShowWarning() { var code = @" interface TestInterface { dynamic TestMethod1(); } " ; var expected = new DiagnosticLocation(3, 6); await VerifyDiagnosticsAsync(code, expected); }
public bool Equals(IncrementalStubGenerationContext?other) { return(other is not null && StubEnvironment.AreCompilationSettingsEqual(Environment, other.Environment) && SignatureContext.Equals(other.SignatureContext) && ContainingSyntaxContext.Equals(other.ContainingSyntaxContext) && StubMethodSyntaxTemplate.Equals(other.StubMethodSyntaxTemplate) && JSExportData.Equals(other.JSExportData) && DiagnosticLocation.Equals(DiagnosticLocation) && GeneratorFactoryKey.Equals(other.GeneratorFactoryKey) && Diagnostics.SequenceEqual(other.Diagnostics)); }
public async Task DiagnosticsAndCodeFixes_WhenModelStateIsInElseIf() { // Arrange var expectedLocation = new DiagnosticLocation("Test.cs", 13, 9); var test = @" using Microsoft.AspNetCore.Mvc; [ApiController] public class PetController : ControllerBase { public IActionResult GetPetId() { if (User == null) { return Unauthorized(); } else if (!ModelState.IsValid) { return BadRequest(ModelState); } return Ok(); } }"; var expectedFix = @" using Microsoft.AspNetCore.Mvc; [ApiController] public class PetController : ControllerBase { public IActionResult GetPetId() { if (User == null) { return Unauthorized(); } return Ok(); } }"; var project = CreateProject(test); // Act & Assert var actualDiagnostics = await GetDiagnosticAsync(project); AssertDiagnostic(expectedLocation, actualDiagnostics); var actualFix = await ApplyCodeFixAsync(project, actualDiagnostics); Assert.Equal(expectedFix, actualFix, ignoreLineEndingDifferences: true); }
private void AssertDiagnostic(DiagnosticLocation expectedLocation, Diagnostic[] actualDiagnostics) { // Assert Assert.Collection( actualDiagnostics, diagnostic => { Assert.Equal(DiagnosticDescriptor.Id, diagnostic.Id); Assert.Same(DiagnosticDescriptor, diagnostic.Descriptor); AnalyzerAssert.DiagnosticLocation(expectedLocation, diagnostic.Location); }); }
public async Task Issue_1_used_to_hide_pubternals___warning_GF0001() { string library = @" namespace gfoidl.Internal { public abstract class Base { private static readonly Bar s_bar = new Bar(); public static Bar Default => s_bar; public abstract void Do(); } public sealed class Bar : Base { public override void Do() { } } }"; TestSource code = TestSource.Read(@" using gfoidl.Internal; namespace MyProgram { internal class Worker { public void Work() { /*M0*/Base./*M1*/Default.Do(); } } }", "M0", "M1"); Diagnostic[] diagnostics = await this.GetDiagnosticsWithProjectReference(code.Source, library); DiagnosticLocation expectedM0 = code.MarkerLocations["M0"]; DiagnosticLocation expectedM1 = code.MarkerLocations["M1"]; Assert.Multiple(() => { Assert.AreEqual(2, diagnostics.Length); Diagnostic diagnostic = diagnostics[0]; Assert.AreEqual("GF0001", diagnostic.Id); Assert.IsTrue(diagnostic.Location.IsInSource); Assert.That(diagnostic.Location, Is.EqualTo(expectedM0)); diagnostic = diagnostics[1]; Assert.AreEqual("GF0001", diagnostic.Id); Assert.IsTrue(diagnostic.Location.IsInSource); Assert.That(diagnostic.Location, Is.EqualTo(expectedM1)); }); }
public bool Equals(IncrementalStubGenerationContext?other) { return(other is not null && StubEnvironment.AreCompilationSettingsEqual(Environment, other.Environment) && SignatureContext.Equals(other.SignatureContext) && ContainingSyntaxContext.Equals(other.ContainingSyntaxContext) && StubMethodSyntaxTemplate.Equals(other.StubMethodSyntaxTemplate) && LibraryImportData.Equals(other.LibraryImportData) && DiagnosticLocation.Equals(DiagnosticLocation) && ForwardedAttributes.SequenceEqual(other.ForwardedAttributes, (IEqualityComparer <AttributeSyntax>)SyntaxEquivalentComparer.Instance) && GeneratorFactoryKey.Equals(other.GeneratorFactoryKey) && Diagnostics.SequenceEqual(other.Diagnostics)); }
public static Diagnostic ToDiagnostic(this DiagnosticLocation location) { return(new Diagnostic() { // We don't have a code at the moment // Code = quickFix., Message = location.Text, Range = location.ToRange(), Severity = ToDiagnosticSeverity(location.LogLevel), // TODO: We need to forward this type though if we add something like Vb Support Source = "csharp", }); }
public async Task AG0030_WhenMethodReturnsDynamic_ShowWarning() { var code = @" class TestClass { public dynamic TestMethod2() { return 1; } } " ; var expected = new DiagnosticLocation(3, 6); await VerifyDiagnosticsAsync(code, expected); }
public async Task AG0030_WhenDynamicVariableDeclared_ShowWarning() { var code = @" class TestClass { public void TestMethod1() { dynamic instance = 1; } } " ; var expected = new DiagnosticLocation(4, 7); await VerifyDiagnosticsAsync(code, expected); }
public async Task AG0030_WhenClassHasDynamicAsOneOfManyGenericParameters_ShouldShowWarning() { var code = @" class TestClass<T1, T2, T3> { public void TestMethod1() { var test = new TestClass<bool, dynamic, int>(); } } " ; var expected = new DiagnosticLocation(4, 23); await VerifyDiagnosticsAsync(code, expected); }
public async Task AG0030_WhenMethodHasDynamicAsGenericParameter_ShouldShowWarning() { var code = @" class TestClass { public void GenericMethod<T>() { } public void TestMethod1() { GenericMethod<dynamic>(); } } " ; var expected = new DiagnosticLocation(6, 8); await VerifyDiagnosticsAsync(code, expected); }
public async Task AG0030_WhenMethodHasDynamicAsOneOfManyGenericParameters_ShouldShowWarning() { var code = @" class TestClass { public void GenericMethod<T1, T2, T3, T4>() { } public void TestMethod1() { GenericMethod<bool, long, dynamic, int>(); } } " ; var expected = new DiagnosticLocation(6, 8); await VerifyDiagnosticsAsync(code, expected); }
public async Task DiagnosticsAreReturned_WhenActionReturnsAsyncIActionResult() { // Arrange var expectedLocation = new DiagnosticLocation("Test.cs", 8, 18); var test = @" using Microsoft.AspNetCore.Mvc; using System.Threading.Tasks; [ApiController] public class PetController: ControllerBase { public async Task<IActionResult> GetPet() { await Task.Delay(0); return Ok(new Pet()); } } public class Pet {}"; var expectedFix = @" using Microsoft.AspNetCore.Mvc; using System.Threading.Tasks; [ApiController] public class PetController: ControllerBase { public async Task<ActionResult<Pet>> GetPet() { await Task.Delay(0); return Ok(new Pet()); } } public class Pet {}"; var project = CreateProject(test); // Act & Assert var actualDiagnostics = await GetDiagnosticAsync(project); AssertDiagnostic(expectedLocation, actualDiagnostics); var actualFix = await ApplyCodeFixAsync(project, actualDiagnostics); Assert.Equal(expectedFix, actualFix, ignoreLineEndingDifferences: true); }
protected Project CreateProjectFromFile([CallerMemberName] string fileName = "") { var solutionDirectory = TestPathUtilities.GetSolutionRootDirectory("Mvc"); var projectDirectory = Path.Combine(solutionDirectory, "test", GetType().Assembly.GetName().Name); var filePath = Path.Combine(projectDirectory, "TestFiles", fileName + ".cs"); if (!File.Exists(filePath)) { throw new FileNotFoundException($"TestFile {fileName} could not be found at {filePath}.", filePath); } const string MarkerStart = "/*MM"; const string MarkerEnd = "*/"; var lines = File.ReadAllLines(filePath); for (var i = 0; i < lines.Length; i++) { var line = lines[i]; var markerStartIndex = line.IndexOf(MarkerStart, StringComparison.Ordinal); if (markerStartIndex != -1) { var markerEndIndex = line.IndexOf(MarkerEnd, markerStartIndex, StringComparison.Ordinal); var markerName = line.Substring(markerStartIndex + 2, markerEndIndex - markerStartIndex - 2); var resultLocation = new DiagnosticLocation(i + 1, markerStartIndex + 1);; if (DefaultMarkerLocation == null) { DefaultMarkerLocation = resultLocation; } MarkerLocations[markerName] = resultLocation; line = line.Substring(0, markerStartIndex) + line.Substring(markerEndIndex + MarkerEnd.Length); } lines[i] = line; } var inputSource = string.Join(Environment.NewLine, lines); return(CreateProject(inputSource)); }
public async Task DiagnosticsAreReturned_WhenActionsReturnIActionResult() { // Arrange var expectedLocation = new DiagnosticLocation("Test.cs", 9, 12); var test = @" using Microsoft.AspNetCore.Mvc; public class Pet {} [ApiController] public class PetController: ControllerBase { public IActionResult GetPet() { return Ok(new Pet()); } }"; var expectedFix = @" using Microsoft.AspNetCore.Mvc; public class Pet {} [ApiController] public class PetController: ControllerBase { public ActionResult<Pet> GetPet() { return Ok(new Pet()); } }"; var project = CreateProject(test); // Act var actualDiagnostics = await GetDiagnosticAsync(project); AssertDiagnostic(expectedLocation, actualDiagnostics); var actualFix = await ApplyCodeFixAsync(project, actualDiagnostics); Assert.Equal(expectedFix, actualFix, ignoreLineEndingDifferences: true); }
/// <summary> /// Helper method to VerifyDiagnosticResult that checks the location of a diagnostic and compares it with the location in the expected DiagnosticResult. /// </summary> /// <param name="analyzer">The analyzer that was being run on the sources</param> /// <param name="diagnostic">The diagnostic that was found in the code</param> /// <param name="actual">The Location of the Diagnostic found in the code</param> /// <param name="expected">The DiagnosticResultLocation that should have been found</param> private static void VerifyDiagnosticLocation(DiagnosticAnalyzer analyzer, Diagnostic diagnostic, Location actual, DiagnosticLocation expected) { FileLinePositionSpan actualSpan = actual.GetLineSpan(); Assert.True(actualSpan.Path == expected.Span.Path || (actualSpan.Path != null && actualSpan.Path.Contains("Test0.") && expected.Span.Path.Contains("Test.")), string.Format(CultureInfo.InvariantCulture, "Expected diagnostic to be in file \"{0}\" was actually in file \"{1}\"\r\n\r\nDiagnostic:\r\n {2}\r\n", expected.Span.Path, actualSpan.Path, FormatDiagnostics(analyzer, diagnostic))); Microsoft.CodeAnalysis.Text.LinePosition actualLinePosition = actualSpan.StartLinePosition; // Only check line position if there is an actual line in the real diagnostic if (expected.Span.StartLinePosition.Line > 0) { if (actualLinePosition.Line + 1 != expected.Span.StartLinePosition.Line) { Assert.True(false, string.Format(CultureInfo.InvariantCulture, "Expected diagnostic to be on line \"{0}\" was actually on line \"{1}\"\r\n\r\nDiagnostic:\r\n {2}\r\n", expected.Span.StartLinePosition.Line, actualLinePosition.Line + 1, FormatDiagnostics(analyzer, diagnostic))); } } // Only check column position if there is an actual column position in the real diagnostic if (actualLinePosition.Character > 0) { if (actualLinePosition.Character + 1 != expected.Span.StartLinePosition.Character) { Assert.True(false, string.Format(CultureInfo.InvariantCulture, "Expected diagnostic to start at column \"{0}\" was actually at column \"{1}\"\r\n\r\nDiagnostic:\r\n {2}\r\n", expected.Span.StartLinePosition.Character, actualLinePosition.Character + 1, FormatDiagnostics(analyzer, diagnostic))); } } }
/// <summary> /// Helper method to VerifyDiagnosticResult that checks the location of a diagnostic and compares it with the location in the expected DiagnosticResult. /// </summary> /// <param name="analyzer">The analyzer that was being run on the sources</param> /// <param name="diagnostic">The diagnostic that was found in the code</param> /// <param name="actual">The Location of the Diagnostic found in the code</param> /// <param name="expected">The DiagnosticResultLocation that should have been found</param> private static void VerifyDiagnosticLocation(DiagnosticAnalyzer analyzer, Diagnostic diagnostic, Location actual, DiagnosticLocation expected) { var actualSpan = actual.GetLineSpan(); Assert.True(actualSpan.Path == expected.Span.Path || (actualSpan.Path != null && actualSpan.Path.Contains("Test0.") && expected.Span.Path.Contains("Test.")), $"Expected diagnostic to be in file \"{expected.Span.Path}\" was actually in file \"{actualSpan.Path}\"\r\n\r\nDiagnostic:\r\n {FormatDiagnostics(analyzer, diagnostic)}\r\n"); var actualLinePosition = actualSpan.StartLinePosition; // Only check line position if there is an actual line in the real diagnostic if (actualLinePosition.Line > 0) { if (actualLinePosition.Line != expected.Span.StartLinePosition.Line) { Assert.True(false, $"Expected diagnostic to be on line \"{expected.Span.StartLinePosition.Line + 1}\" was actually on line \"{actualLinePosition.Line + 1}\"\r\n\r\nDiagnostic:\r\n {FormatDiagnostics(analyzer, diagnostic)}\r\n"); } } // Only check column position if there is an actual column position in the real diagnostic if (actualLinePosition.Character > 0) { if (actualLinePosition.Character != expected.Span.StartLinePosition.Character) { Assert.True(false, $"Expected diagnostic to start at column \"{expected.Span.StartLinePosition.Character + 1}\" was actually at column \"{actualLinePosition.Character + 1}\"\r\n\r\nDiagnostic:\r\n {FormatDiagnostics(analyzer, diagnostic)}\r\n"); } } }
/// <param name="isAssertEnabled">Indicates that unit test assertions are enabled for non-matches.</param> /// <returns>True if actual matches expected, false otherwise.</returns> private static bool VerifyDiagnosticLocation(DiagnosticAnalyzer analyzer, Diagnostic diagnostic, Location actual, DiagnosticLocation expected, bool isAssertEnabled) { FileLinePositionSpan actualSpan = actual.GetLineSpan(); if (isAssertEnabled) { Assert.True(actualSpan.Path == expected.Span.Path || (actualSpan.Path != null && actualSpan.Path.Contains("Test0.") && expected.Span.Path.Contains("Test.")), string.Format("Expected diagnostic to be in file \"{0}\" was actually in file \"{1}\"\r\n\r\nDiagnostic:\r\n {2}\r\n", expected.Span.Path, actualSpan.Path, FormatDiagnostics(analyzer, diagnostic))); } else if (!(actualSpan.Path == expected.Span.Path || (actualSpan.Path != null && actualSpan.Path.Contains("Test0.") && expected.Span.Path.Contains("Test.")))) { return(false); } Microsoft.CodeAnalysis.Text.LinePosition actualLinePosition = actualSpan.StartLinePosition; // Only check line position if there is an actual line in the real diagnostic if (expected.Span.StartLinePosition.Line > 0) { if (actualLinePosition.Line != expected.Span.StartLinePosition.Line) { if (isAssertEnabled) { Assert.True(false, string.Format("Expected diagnostic to be on line \"{0}\" was actually on line \"{1}\"\r\n\r\nDiagnostic:\r\n {2}\r\n", expected.Span.StartLinePosition.Line + 1, actualLinePosition.Line + 1, FormatDiagnostics(analyzer, diagnostic))); } else { return(false); } } } // Only check column position if there is an actual column position in the real diagnostic if (expected.Span.StartLinePosition.Character > 0) { if (actualLinePosition.Character != expected.Span.StartLinePosition.Character) { if (isAssertEnabled) { Assert.True(false, string.Format("Expected diagnostic to start at column \"{0}\" was actually at column \"{1}\"\r\n\r\nDiagnostic:\r\n {2}\r\n", expected.Span.StartLinePosition.Character + 1, actualLinePosition.Character + 1, FormatDiagnostics(analyzer, diagnostic))); } else { return(false); } } } // Matches. return(true); }