/// <summary> /// General verifier for codefixes. /// Creates a Document from the source string, then gets diagnostics on it and applies the relevant codefixes. /// Then gets the string after the codefix is applied and compares it with the expected result. /// Note: If any codefix causes new diagnostics to show up, the test fails unless allowNewCompilerDiagnostics is set to true. /// </summary> /// <param name="language">The language the source code is in</param> /// <param name="analyzer">The analyzer to be applied to the source code</param> /// <param name="codeFixProvider">The codefix to be applied to the code wherever the relevant Diagnostic is found</param> /// <param name="oldSource">A class in the form of a string before the CodeFix was applied to it</param> /// <param name="newSource">A class in the form of a string after the CodeFix was applied to it</param> /// <param name="codeFixIndex">Index determining which codefix to apply if there are multiple</param> /// <param name="allowNewCompilerDiagnostics">A bool controlling whether or not the test will fail if the CodeFix introduces other warnings after being applied</param> private async Task VerifyFixAsync(string language, DiagnosticAnalyzer analyzer, CodeFixProvider codeFixProvider, string oldSource, string newSource, int?codeFixIndex, bool allowNewCompilerDiagnostics) { var document = VerificationHelper.CreateDocument(oldSource, language); var analyzerDiagnostics = await GetSortedDiagnosticsFromDocumentsAsync(analyzer, new[] { document }).ConfigureAwait(false); var compilerDiagnostics = await VerificationHelper.GetCompilerDiagnosticsAsync(document).ConfigureAwait(false); var attempts = analyzerDiagnostics.Length; for (int i = 0; i < attempts; ++i) { var actions = new List <CodeAction>(); var context = new CodeFixContext(document, analyzerDiagnostics[0], (a, d) => actions.Add(a), CancellationToken.None); await codeFixProvider.RegisterCodeFixesAsync(context).ConfigureAwait(false); if (!actions.Any()) { break; } if (codeFixIndex != null) { document = await VerificationHelper.ApplyCodeActionAsync(document, actions.ElementAt((int)codeFixIndex)).ConfigureAwait(false); break; } document = await VerificationHelper.ApplyCodeActionAsync(document, actions.ElementAt(0)).ConfigureAwait(false); analyzerDiagnostics = await GetSortedDiagnosticsFromDocumentsAsync(analyzer, new[] { document }).ConfigureAwait(false); var newCompilerDiagnostics = VerificationHelper.GetNewDiagnostics(compilerDiagnostics, await VerificationHelper.GetCompilerDiagnosticsAsync(document).ConfigureAwait(false)); //check if applying the code fix introduced any new compiler diagnostics if (!allowNewCompilerDiagnostics && newCompilerDiagnostics.Any()) { // Format and get the compiler diagnostics again so that the locations make sense in the output document = document.WithSyntaxRoot(Formatter.Format(await document.GetSyntaxRootAsync().ConfigureAwait(false), Formatter.Annotation, document.Project.Solution.Workspace)); newCompilerDiagnostics = VerificationHelper.GetNewDiagnostics(compilerDiagnostics, await VerificationHelper.GetCompilerDiagnosticsAsync(document).ConfigureAwait(false)); Assert.True(false, string.Format("Fix introduced new compiler diagnostics:\r\n{0}\r\n\r\nNew document:\r\n{1}\r\n", string.Join("\r\n", newCompilerDiagnostics.Select(d => d.ToString())), (await document.GetSyntaxRootAsync().ConfigureAwait(false)).ToFullString())); } //check if there are analyzer diagnostics left after the code fix if (!analyzerDiagnostics.Any()) { break; } } //after applying all of the code fixes, compare the resulting string to the inputted one var actual = await VerificationHelper.GetStringFromDocumentAsync(document).ConfigureAwait(false); Assert.Equal(newSource, actual); }
/// <summary> /// General verifier for refactorings. /// Creates a Document from the source string, then applies the relevant refactorings. /// Then gets the string after the refactoring is applied and compares it with the expected result. /// Note: If any refactoring causes new compiler diagnostics to show up, the test fails unless allowNewCompilerDiagnostics is set to true. /// </summary> /// <param name="language">The language the source code is in</param> /// <param name="codeRefactoringProvider">The refactoring to be applied to the code</param> /// <param name="oldSource">A class in the form of a string before the refactoring was applied to it</param> /// <param name="newSource">A class in the form of a string after the refactoring was applied to it</param> /// <param name="nodeToRefactor">A function that finds selected node for refactoring accepting the root node as an argument</param> /// <param name="codeRefactoringIndex">Index determining which refactoring to apply if there are multiple</param> /// <param name="allowNewCompilerDiagnostics">A bool controlling whether or not the test will fail if the refactoring introduces other warnings after being applied</param> private async Task VerifyRefactoringAsync(string language, CodeRefactoringProvider codeRefactoringProvider, string oldSource, string newSource, Func <SyntaxNode, SyntaxNode> nodeToRefactor, int?codeRefactoringIndex, bool allowNewCompilerDiagnostics) { var document = VerificationHelper.CreateDocument(oldSource, language); var compilerDiagnostics = (await VerificationHelper.GetCompilerDiagnosticsAsync(document).ConfigureAwait(false)).ToArray(); var actions = new List <CodeAction>(); var root = await document.GetSyntaxRootAsync().ConfigureAwait(false); var node = nodeToRefactor(root); var context = new CodeRefactoringContext(document, node.FullSpan, a => actions.Add(a), CancellationToken.None); await codeRefactoringProvider.ComputeRefactoringsAsync(context).ConfigureAwait(false); if (actions.Count > 0) { document = await VerificationHelper.ApplyCodeActionAsync(document, codeRefactoringIndex != null ?actions[(int)codeRefactoringIndex] : actions[0]).ConfigureAwait(false); var newCompilerDiagnostics = VerificationHelper.GetNewDiagnostics(compilerDiagnostics, await VerificationHelper.GetCompilerDiagnosticsAsync(document).ConfigureAwait(false)); //check if applying the code fix introduced any new compiler diagnostics if (!allowNewCompilerDiagnostics && newCompilerDiagnostics.Any()) { // Format and get the compiler diagnostics again so that the locations make sense in the output document = document.WithSyntaxRoot(Formatter.Format(await document.GetSyntaxRootAsync().ConfigureAwait(false), Formatter.Annotation, document.Project.Solution.Workspace)); newCompilerDiagnostics = VerificationHelper.GetNewDiagnostics(compilerDiagnostics, await VerificationHelper.GetCompilerDiagnosticsAsync(document).ConfigureAwait(false)); var newSyntaxRoot = await document.GetSyntaxRootAsync().ConfigureAwait(false); Assert.True(false, string.Format("Refactoring introduced new compiler diagnostics:\r\n{0}\r\n\r\nNew document:\r\n{1}\r\n", string.Join("\r\n", newCompilerDiagnostics.Select(d => d.ToString())), newSyntaxRoot.ToFullString())); } } //after applying all of the refactorings, compare the resulting string to the inputted one var actual = await VerificationHelper.GetStringFromDocumentAsync(document).ConfigureAwait(false); Assert.Equal(newSource, actual); }