/// <summary>
        /// Verifies that <paramref name="sources"/> produces the expected diagnostics.
        /// </summary>
        /// <param name="analyzer">The analyzer to apply.</param>
        /// <param name="sources">The code with error positions indicated.</param>
        /// <param name="compilationOptions">The <see cref="CSharpCompilationOptions"/> to use.</param>
        /// <param name="metadataReferences">The meta data metadataReferences to use when compiling.</param>
        /// <param name="expectedMessage">The expected message in the diagnostic produced by the analyzer.</param>
        /// <returns>The meta data from the run..</returns>
        public static async Task <DiagnosticsMetadata> DiagnosticsWithMetadataAsync(
            DiagnosticAnalyzer analyzer,
            DiagnosticsAndSources sources,
            CSharpCompilationOptions compilationOptions,
            IReadOnlyList <MetadataReference> metadataReferences,
            string expectedMessage = null)
        {
            if (sources.ExpectedDiagnostics.Count == 0)
            {
                throw AssertException.Create("Expected code to have at least one error position indicated with '↓'");
            }

            var data = await Analyze.GetDiagnosticsWithMetadataAsync(
                analyzer,
                sources.Code,
                compilationOptions,
                metadataReferences)
                       .ConfigureAwait(false);

            var expecteds = sources.ExpectedDiagnostics;
            var actuals   = data.Diagnostics
                            .SelectMany(x => x)
                            .ToArray();

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

                return(new DiagnosticsMetadata(
                           sources.Code,
                           sources.ExpectedDiagnostics,
                           data.Diagnostics,
                           data.Solution));
            }

            var error = StringBuilderPool.Borrow();

            error.AppendLine("Expected and actual diagnostics do not match.");
            var missingExpected = expecteds.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(sources.Code));
            }

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

            var missingActual = actuals.Except(expecteds);

            if (actuals.Length > 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.ToString(sources.Code));
            }

            throw AssertException.Create(StringBuilderPool.Return(error));
        }
Exemple #2
0
        private static void VerifyDiagnostics(DiagnosticsAndSources diagnosticsAndSources, IReadOnlyList <ImmutableArray <Diagnostic> > diagnostics, Solution solution, string?expectedMessage = null)
        {
            if (diagnosticsAndSources.ExpectedDiagnostics.Count == 0)
            {
                throw new AssertException("Expected code to have at least one error position indicated with '↓'");
            }

            var allDiagnostics = diagnostics.SelectMany(x => x).ToArray();

            if (allDiagnostics.Length == 0)
            {
                allDiagnostics = Analyze.GetDiagnostics(solution).SelectMany(x => x).ToArray();
            }

            if (Equals())
            {
                if (expectedMessage != null)
                {
                    foreach (var actual in allDiagnostics)
                    {
                        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 (allDiagnostics.Length == 1 &&
                diagnosticsAndSources.ExpectedDiagnostics.Count == 1 &&
                diagnosticsAndSources.ExpectedDiagnostics[0].Id == allDiagnostics[0].Id)
            {
                if (diagnosticsAndSources.ExpectedDiagnostics[0].PositionMatches(allDiagnostics[0]) &&
                    !diagnosticsAndSources.ExpectedDiagnostics[0].MessageMatches(allDiagnostics[0]))
                {
                    CodeAssert.AreEqual(diagnosticsAndSources.ExpectedDiagnostics[0].Message !, allDiagnostics[0].GetMessage(CultureInfo.InvariantCulture), "Expected and actual messages do not match.");
                }
            }

            error.AppendLine("Expected and actual diagnostics do not match.")
            .AppendLine("Expected:");
            foreach (var expected in diagnosticsAndSources.ExpectedDiagnostics.OrderBy(x => x.Span.StartLinePosition))
            {
                error.AppendLine(expected.ToString(diagnosticsAndSources.Code, "  "));
            }

            if (allDiagnostics.Length == 0)
            {
                error.AppendLine("Actual: <no diagnostics>");
            }
            else
            {
                error.AppendLine("Actual:");
                foreach (var diagnostic in allDiagnostics.OrderBy(x => x.Location.SourceSpan.Start))
                {
                    error.AppendLine(diagnostic.ToErrorString("  "));
                }
            }

            throw new AssertException(error.Return());

            bool Equals()
            {
                foreach (var diagnostic in allDiagnostics)
                {
                    if (diagnosticsAndSources.ExpectedDiagnostics.Any(e => e.Matches(diagnostic)))
                    {
                        continue;
                    }

                    return(false);
                }

                foreach (var expected in diagnosticsAndSources.ExpectedDiagnostics)
                {
                    if (allDiagnostics.Any(a => expected.Matches(a)))
                    {
                        continue;
                    }

                    return(false);
                }

                return(true);
            }
        }
        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());
        }