Beispiel #1
0
        /// <summary>
        /// For testing a <see cref="CodeRefactoringProvider"/>.
        /// </summary>
        /// <param name="refactoring">The <see cref="CodeRefactoringProvider"/>.</param>
        /// <param name="before">The code to analyze with <paramref name="refactoring"/>. Position is provided by <paramref name="span"/>.</param>
        /// <param name="span">A <see cref="TextSpan"/> indicating the position.</param>
        /// <param name="after">The expected code produced by <paramref name="refactoring"/>.</param>
        /// <param name="title">The title of the refactoring to apply.</param>
        public static void Refactoring(CodeRefactoringProvider refactoring, string before, TextSpan span, string after, string title)
        {
#pragma warning disable CS0618 // Suppress until removed. Will be replaced with Metadatareferences.FromAttributes()
            var refactored = Refactor.Apply(refactoring, before, span, title, MetadataReferences);
#pragma warning restore CS0618 // Suppress until removed. Will be replaced with Metadatareferences.FromAttributes()
            CodeAssert.AreEqual(after, refactored);
        }
Beispiel #2
0
        /// <summary>
        /// For testing a <see cref="CodeRefactoringProvider"/>.
        /// </summary>
        /// <param name="refactoring">The <see cref="CodeRefactoringProvider"/>.</param>
        /// <param name="codeWithPositionIndicated">The code to refactor with position indicated with ↓.</param>
        /// <param name="title">The title of the refactoring to apply.</param>
        /// <param name="fixedCode">The expected code produced by the refactoring.</param>
        public static void Refactoring(CodeRefactoringProvider refactoring, string codeWithPositionIndicated, string title, string fixedCode)
        {
            var position   = GetPosition(codeWithPositionIndicated, out var testCode);
            var refactored = Refactor.Apply(refactoring, testCode, position, title, MetadataReferences);

            CodeAssert.AreEqual(refactored, fixedCode);
        }
Beispiel #3
0
        /// <summary>
        /// For testing a <see cref="CodeRefactoringProvider"/>.
        /// </summary>
        /// <param name="refactoring">The <see cref="CodeRefactoringProvider"/>.</param>
        /// <param name="before">The code to analyze with <paramref name="refactoring"/>. Indicate position with ↓ (alt + 25).</param>
        /// <param name="after">The expected code produced by <paramref name="refactoring"/>.</param>
        public static void Refactoring(CodeRefactoringProvider refactoring, string before, string after)
        {
            var position = GetPosition(before, out var testCode);

#pragma warning disable CS0618 // Suppress until removed. Will be replaced with Metadatareferences.FromAttributes()
            var refactored = Refactor.Apply(refactoring, testCode, position, MetadataReferences);
#pragma warning restore CS0618 // Suppress until removed. Will be replaced with Metadatareferences.FromAttributes()
            CodeAssert.AreEqual(after, refactored);
        }
Beispiel #4
0
        private static async Task AreEqualAsync(IReadOnlyList <string> expected, Solution actual, string messageHeader)
        {
            var index = 0;

            foreach (var project in actual.Projects)
            {
                for (var i = 0; i < project.DocumentIds.Count; i++)
                {
                    var fixedSource = await CodeReader.GetStringFromDocumentAsync(project.GetDocument(project.DocumentIds[i]), Formatter.Annotation, CancellationToken.None).ConfigureAwait(false);

                    CodeAssert.AreEqual(expected[index], fixedSource, messageHeader);
                    index++;
                }
            }
        }
Beispiel #5
0
        /// <summary>
        /// Verifies that
        /// 1. <paramref name="diagnosticsAndSources"/> produces the expected diagnostics when analyzed.
        /// 2. The code fix fixes the code.
        /// </summary>
        /// <param name="analyzer">The analyzer to run on the code..</param>
        /// <param name="codeFix">The code fix to apply.</param>
        /// <param name="diagnosticsAndSources">The code and expected diagnostics.</param>
        /// <param name="fixedCode">The expected code produced by the code fix.</param>
        /// <param name="fixTitle">The title of the fix to apply if more than one.</param>
        /// <param name="compilationOptions">The <see cref="CSharpCompilationOptions"/> to use.</param>
        /// <param name="metadataReferences">The meta data metadataReferences to add to the compilation.</param>
        /// <param name="allowCompilationErrors">If compilation errors are accepted in the fixed code.</param>
        /// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
        public static async Task CodeFixAsync(DiagnosticAnalyzer analyzer, CodeFixProvider codeFix, DiagnosticsAndSources diagnosticsAndSources, string fixedCode, string fixTitle, CSharpCompilationOptions compilationOptions, IReadOnlyList <MetadataReference> metadataReferences, AllowCompilationErrors allowCompilationErrors)
        {
            AssertCodeFixCanFixDiagnosticsFromAnalyzer(analyzer, codeFix);
            var data = await DiagnosticsWithMetadataAsync(analyzer, diagnosticsAndSources, compilationOptions, metadataReferences).ConfigureAwait(false);

            var fixableDiagnostics = data.ActualDiagnostics.SelectMany(x => x)
                                     .Where(x => codeFix.FixableDiagnosticIds.Contains(x.Id))
                                     .ToArray();

            if (fixableDiagnostics.Length == 0)
            {
                var message = $"Code analyzed with {analyzer} did not generate any diagnostics fixable by {codeFix}.{Environment.NewLine}" +
                              $"The analyzed code contained the following diagnostics: {{{string.Join(", ", data.ExpectedDiagnostics.Select(d => d.Id))}}}{Environment.NewLine}" +
                              $"The code fix supports the following diagnostics: {{{string.Join(", ", codeFix.FixableDiagnosticIds)}}}";
                throw AssertException.Create(message);
            }

            if (fixableDiagnostics.Length > 1)
            {
                var message = $"Code analyzed with {analyzer} generated more than one diagnostic fixable by {codeFix}.{Environment.NewLine}" +
                              $"The analyzed code contained the following diagnostics: {{{string.Join(", ", data.ExpectedDiagnostics.Select(d => d.Id))}}}{Environment.NewLine}" +
                              $"The code fix supports the following diagnostics: {{{string.Join(", ", codeFix.FixableDiagnosticIds)}}}{Environment.NewLine}" +
                              $"Maybe you meant to call AnalyzerAssert.FixAll?";
                throw AssertException.Create(message);
            }

            var diagnostic    = fixableDiagnostics.Single();
            var fixedSolution = await Fix.ApplyAsync(data.Solution, codeFix, diagnostic, fixTitle, CancellationToken.None).ConfigureAwait(false);

            if (ReferenceEquals(data.Solution, fixedSolution))
            {
                throw AssertException.Create($"{codeFix} did not change any document.");
            }

            var fixedSource = await CodeReader.GetStringFromDocumentAsync(
                fixedSolution.GetDocument(data.Solution.GetDocument(diagnostic.Location.SourceTree).Id),
                Formatter.Annotation,
                CancellationToken.None)
                              .ConfigureAwait(false);

            CodeAssert.AreEqual(fixedCode, fixedSource);

            if (allowCompilationErrors == AllowCompilationErrors.No)
            {
                await AssertNoCompilerErrorsAsync(codeFix, fixedSolution).ConfigureAwait(false);
            }
        }
        private static async Task VerifyFixAsync(Solution sln, IReadOnlyList <ImmutableArray <Diagnostic> > diagnostics, DiagnosticAnalyzer analyzer, CodeFixProvider fix, string fixedCode, string fixTitle = null, AllowCompilationErrors allowCompilationErrors = AllowCompilationErrors.No)
        {
            var fixableDiagnostics = diagnostics.SelectMany(x => x)
                                     .Where(x => fix.FixableDiagnosticIds.Contains(x.Id))
                                     .ToArray();

            if (fixableDiagnostics.Length == 0)
            {
                var message = $"Code analyzed with {analyzer} did not generate any diagnostics fixable by {fix}.{Environment.NewLine}" +
                              $"The analyzed code contained the following diagnostics: {{{string.Join(", ", diagnostics.SelectMany(x => x).Select(d => d.Id))}}}{Environment.NewLine}" +
                              $"The code fix supports the following diagnostics: {{{string.Join(", ", fix.FixableDiagnosticIds)}}}";
                throw new AssertException(message);
            }

            if (fixableDiagnostics.Length > 1)
            {
                var message = $"Code analyzed with {analyzer} generated more than one diagnostic fixable by {fix}.{Environment.NewLine}" +
                              $"The analyzed code contained the following diagnostics: {{{string.Join(", ", diagnostics.SelectMany(x => x).Select(d => d.Id))}}}{Environment.NewLine}" +
                              $"The code fix supports the following diagnostics: {{{string.Join(", ", fix.FixableDiagnosticIds)}}}{Environment.NewLine}" +
                              $"Maybe you meant to call AnalyzerAssert.FixAll?";
                throw new AssertException(message);
            }

            var diagnostic    = fixableDiagnostics.Single();
            var fixedSolution = await Fix.ApplyAsync(sln, fix, diagnostic, fixTitle).ConfigureAwait(false);

            if (ReferenceEquals(sln, fixedSolution))
            {
                throw new AssertException($"{fix} did not change any document.");
            }

            var fixedSource = await CodeReader.GetStringFromDocumentAsync(
                fixedSolution.GetDocument(sln.GetDocument(diagnostic.Location.SourceTree).Id),
                Formatter.Annotation,
                CancellationToken.None)
                              .ConfigureAwait(false);

            CodeAssert.AreEqual(fixedCode, fixedSource);

            if (allowCompilationErrors == AllowCompilationErrors.No)
            {
                await VerifyNoCompilerErrorsAsync(fix, fixedSolution).ConfigureAwait(false);
            }
        }
Beispiel #7
0
        private static async Task AreEqualAsync(IReadOnlyList <string> expected, Solution actual, string?messageHeader)
        {
            var actualCount = actual.Projects.SelectMany(x => x.Documents).Count();

            if (expected.Count != actualCount)
            {
                throw new AssertException($"Expected {expected.Count} documents the fixed solution has {actualCount} documents.");
            }

            foreach (var project in actual.Projects)
            {
                foreach (var document in project.Documents)
                {
                    var fixedSource = await CodeReader.GetStringFromDocumentAsync(document, CancellationToken.None).ConfigureAwait(false);

                    CodeAssert.AreEqual(FindExpected(fixedSource), fixedSource, messageHeader);
                }
            }

            string FindExpected(string fixedSource)
            {
                var fixedNamespace = CodeReader.Namespace(fixedSource);
                var fixedFileName  = CodeReader.FileName(fixedSource);
                var match          = expected.FirstOrDefault(x => x == fixedSource);

                if (match != null)
                {
                    return(match);
                }

                foreach (var candidate in expected)
                {
                    if (CodeReader.Namespace(candidate) == fixedNamespace &&
                        CodeReader.FileName(candidate) == fixedFileName)
                    {
                        return(candidate);
                    }
                }

                throw new AssertException($"The fixed solution contains a document {fixedFileName} in namespace {fixedNamespace} that is not in the expected documents.");
            }
        }
Beispiel #8
0
 /// <summary>
 /// Serializes the syntax tree and compares the strings.
 /// This can be useful when having trouble getting whitespace right.
 /// </summary>
 /// <typeparam name="T">The node type.</typeparam>
 /// <param name="expected">The expected shape of the AST.</param>
 /// <param name="actual">The actual node.</param>
 public static void Ast <T>(T expected, T actual)
     where T : SyntaxNode
 {
     CodeAssert.AreEqual(SyntaxFactoryWriter.Serialize(expected), SyntaxFactoryWriter.Serialize(actual));
 }
Beispiel #9
0
        /// <summary>
        /// For testing a <see cref="CodeRefactoringProvider"/>.
        /// </summary>
        /// <param name="refactoring">The <see cref="CodeRefactoringProvider"/>.</param>
        /// <param name="code">The code to refactor.</param>
        /// <param name="span">The position.</param>
        /// <param name="title">The title of the refactoring to apply.</param>
        /// <param name="fixedCode">The expected code produced by the refactoring.</param>
        public static void Refactoring(CodeRefactoringProvider refactoring, string code, TextSpan span, string title, string fixedCode)
        {
            var refactored = Refactor.Apply(refactoring, code, span, title, MetadataReferences);

            CodeAssert.AreEqual(refactored, fixedCode);
        }
Beispiel #10
0
 /// <summary>
 /// Serializes the syntax tree and compares the strings.
 /// This can be useful when having trouble getting whitespace right.
 /// </summary>
 /// <typeparam name="T">The node type.</typeparam>
 /// <param name="expected">The expected shape of the AST.</param>
 /// <param name="actual">The actual node.</param>
 /// <param name="settings"><see cref="AstWriterSettings"/>.</param>
 public static void Ast <T>(T expected, T actual, AstWriterSettings settings = null)
     where T : SyntaxNode
 {
     CodeAssert.AreEqual(AstWriter.Serialize(expected, settings), AstWriter.Serialize(actual, settings));
 }
        private static void VerifyDiagnostics(DiagnosticsAndSources diagnosticsAndSources, IReadOnlyList <Diagnostic> actuals, string expectedMessage = null)
        {
            if (diagnosticsAndSources.ExpectedDiagnostics.Count == 0)
            {
                throw new AssertException("Expected code to have at least one error position indicated with '↓'");
            }

            if (diagnosticsAndSources.ExpectedDiagnostics.SetEquals(actuals))
            {
                if (expectedMessage != null)
                {
                    foreach (var actual in actuals)
                    {
                        var actualMessage = actual.GetMessage(CultureInfo.InvariantCulture);
                        TextAssert.AreEqual(expectedMessage, actualMessage, $"Expected and actual diagnostic message for the diagnostic {actual} does not match");
                    }
                }

                return;
            }

            var error = StringBuilderPool.Borrow();

            if (actuals.Count == 1 &&
                diagnosticsAndSources.ExpectedDiagnostics.Count == 1 &&
                diagnosticsAndSources.ExpectedDiagnostics[0].Id == actuals[0].Id)
            {
                if (diagnosticsAndSources.ExpectedDiagnostics[0].PositionMatches(actuals[0]) &&
                    !diagnosticsAndSources.ExpectedDiagnostics[0].MessageMatches(actuals[0]))
                {
                    CodeAssert.AreEqual(diagnosticsAndSources.ExpectedDiagnostics[0].Message, actuals[0].GetMessage(CultureInfo.InvariantCulture), "Expected and actual messages do not match.");
                }
            }

            error.AppendLine("Expected and actual diagnostics do not match.");
            var missingExpected = diagnosticsAndSources.ExpectedDiagnostics.Except(actuals);

            for (var i = 0; i < missingExpected.Count; i++)
            {
                if (i == 0)
                {
                    error.Append("Expected:\r\n");
                }

                var expected = missingExpected[i];
                error.AppendLine(expected.ToString(diagnosticsAndSources.Code));
            }

            if (actuals.Count == 0)
            {
                error.AppendLine("Actual: <no errors>");
            }

            var missingActual = actuals.Except(diagnosticsAndSources.ExpectedDiagnostics);

            if (actuals.Count > 0 && missingActual.Count == 0)
            {
                error.AppendLine("Actual: <missing>");
            }

            for (var i = 0; i < missingActual.Count; i++)
            {
                if (i == 0)
                {
                    error.Append("Actual:\r\n");
                }

                var actual = missingActual[i];
                error.AppendLine(actual.ToErrorString());
            }

            throw new AssertException(error.Return());
        }