/// <summary>
        /// General verifier for code fixes.
        /// Creates a <see cref="Document"/> from the source string, then gets <see cref="Diagnostic"/>s on it and
        /// applies the relevant code fixes. Then gets the string after the code fix is applied and compares it with the
        /// expected result.
        /// <note type="note">
        /// <para>If any code fix causes new diagnostics to show up, the test fails unless
        /// <paramref name="allowNewCompilerDiagnostics"/> is set to <see langword="true"/>.</para>
        /// </note>
        /// </summary>
        /// <param name="language">The language the source classes are in. Values may be taken from the
        /// <see cref="LanguageNames"/> class.</param>
        /// <param name="analyzer">The analyzer to be applied to the source code.</param>
        /// <param name="codeFixProvider">The code fix to be applied to the code wherever the relevant
        /// <see cref="Diagnostic"/> is found.</param>
        /// <param name="oldSource">A class in the form of a string before the code fix was applied to it.</param>
        /// <param name="newSource">A class in the form of a string after the code fix was applied to it.</param>
        /// <param name="codeFixIndex">Index determining which code fix to apply if there are multiple.</param>
        /// <param name="allowNewCompilerDiagnostics">A value indicating whether or not the test will fail if the code
        /// fix introduces other warnings after being applied.</param>
        /// <param name="cancellationToken">The <see cref="CancellationToken"/> that the task will observe.</param>
        private async Task VerifyFixAsync(string language, DiagnosticAnalyzer analyzer, CodeFixProvider codeFixProvider, string oldSource, string newSource, int?codeFixIndex, bool allowNewCompilerDiagnostics, CancellationToken cancellationToken)
        {
            var document            = CreateDocument(oldSource, language);
            var analyzerDiagnostics = await GetSortedDiagnosticsFromDocumentsAsync(analyzer, new[] { document }, cancellationToken);

            var compilerDiagnostics = await GetCompilerDiagnosticsAsync(document, cancellationToken);

            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);
                await codeFixProvider.ComputeFixesAsync(context).ConfigureAwait(false);

                if (!actions.Any())
                {
                    break;
                }

                if (codeFixIndex != null)
                {
                    document = await ApplyFixAsync(document, actions.ElementAt((int)codeFixIndex), cancellationToken).ConfigureAwait(false);

                    break;
                }

                document = await ApplyFixAsync(document, actions.ElementAt(0), cancellationToken).ConfigureAwait(false);

                analyzerDiagnostics = await GetSortedDiagnosticsFromDocumentsAsync(analyzer, new[] { document }, cancellationToken);

                var newCompilerDiagnostics = GetNewDiagnostics(compilerDiagnostics, await GetCompilerDiagnosticsAsync(document, cancellationToken).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 = await Formatter.FormatAsync(document, Formatter.Annotation, cancellationToken : cancellationToken);

                    newCompilerDiagnostics = GetNewDiagnostics(compilerDiagnostics, await GetCompilerDiagnosticsAsync(document, cancellationToken).ConfigureAwait(false));

                    Assert.IsTrue(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 GetStringFromDocumentAsync(document, cancellationToken).ConfigureAwait(false);

            Assert.AreEqual(newSource, actual);
        }
Exemplo n.º 2
0
        /// <summary>
        /// General verifier for a diagnostics that should not have fix registred.
        /// Creates a Document from the source string, then gets diagnostics on it and verify if it has no fix registred.
        /// It will fail the test if it has any fix registred to it
        /// </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="source">A class in the form of a string before the CodeFix was applied to it</param>
        private async Task VerifyHasNoFixAsync(string language, DiagnosticAnalyzer analyzer, CodeFixProvider codeFixProvider, string source)
        {
            var document            = TestHelpers.CreateDocument(source, language);
            var analyzerDiagnostics = await GetSortedDiagnosticsFromDocumentsAsync(analyzer, new[] { document });

            foreach (var analyzerDiagnostic in analyzerDiagnostics)
            {
                var actions = new List <CodeAction>();
                var context = new CodeFixContext(document, analyzerDiagnostic, (a, d) => actions.Add(a), CancellationToken.None);
                await codeFixProvider.ComputeFixesAsync(context);

                Assert.False(actions.Any(), $"Should not have a code fix registered for diagnostic '{analyzerDiagnostic.Id}'");
            }
        }
Exemplo n.º 3
0
        /// <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            = TestHelpers.CreateDocument(oldSource, language);
            var analyzerDiagnostics = await GetSortedDiagnosticsFromDocumentsAsync(analyzer, new[] { document });

            var compilerDiagnostics = await GetCompilerDiagnosticsAsync(document);

            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.ComputeFixesAsync(context);

                if (!actions.Any())
                {
                    break;
                }

                if (codeFixIndex != null)
                {
                    document = await ApplyFixAsync(document, actions.ElementAt((int)codeFixIndex));

                    break;
                }

                document = await ApplyFixAsync(document, actions.ElementAt(0));

                analyzerDiagnostics = await GetSortedDiagnosticsFromDocumentsAsync(analyzer, new[] { document });

                var newCompilerDiagnostics = GetNewDiagnostics(compilerDiagnostics, await GetCompilerDiagnosticsAsync(document));

                //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(), Formatter.Annotation, document.Project.Solution.Workspace));
                    newCompilerDiagnostics = GetNewDiagnostics(compilerDiagnostics, await GetCompilerDiagnosticsAsync(document));

                    Assert.True(false, "Fix introduced new compiler diagnostics:\r\n\{string.Join(" \ r \ n ", newCompilerDiagnostics.Select(d => d.ToString()))}\r\n\r\nNew document:\r\n\{(await document.GetSyntaxRootAsync()).ToFullString()}\r\n");
                }

                //check if there are analyzer diagnostics left after the code fix
                if (!analyzerDiagnostics.Any())
                {
                    break;
                }
            }
        protected static async Task <Solution> ApplyFixAsync(CodeFixProvider codeFixProvider, Document document, Diagnostic diagnostic)
        {
            var actions = new List <CodeAction>();
            var context = new CodeFixContext(document, diagnostic, (a, d) => actions.Add(a), CancellationToken.None);
            await codeFixProvider.ComputeFixesAsync(context);

            if (actions.Count > 1)
            {
                Assert.Fail("Expected only a single code action, but found {0} code actions. Use another overload of this method.", actions.Count);
            }

            var operations = await actions.Single().GetOperationsAsync(CancellationToken.None);

            return(operations.OfType <ApplyChangesOperation>().Single().ChangedSolution);
        }