Example #1
0
        /// <summary>
        /// Verifies that refactoring will be applied correctly using specified <typeparamref name="TRefactoringProvider"/>.
        /// </summary>
        /// <param name="state"></param>
        /// <param name="expected"></param>
        /// <param name="options"></param>
        /// <param name="cancellationToken"></param>
        public async Task VerifyRefactoringAsync(
            RefactoringTestState state,
            ExpectedTestState expected,
            TestOptions options = null,
            CancellationToken cancellationToken = default)
        {
            if (state.Spans.IsEmpty)
            {
                Fail("Span on which a refactoring should be invoked was not found.");
            }

            options ??= Options;

            TRefactoringProvider refactoringProvider = Activator.CreateInstance <TRefactoringProvider>();

            foreach (TextSpan span in state.Spans)
            {
                cancellationToken.ThrowIfCancellationRequested();

                using (Workspace workspace = new AdhocWorkspace())
                {
                    (Document document, ImmutableArray <ExpectedDocument> expectedDocuments) = CreateDocument(workspace.CurrentSolution, state.Source, state.AdditionalFiles, options);

                    SemanticModel semanticModel = await document.GetSemanticModelAsync(cancellationToken);

                    ImmutableArray <Diagnostic> compilerDiagnostics = semanticModel.GetDiagnostics(cancellationToken: cancellationToken);

                    VerifyCompilerDiagnostics(compilerDiagnostics, options);

                    CodeAction        action           = null;
                    List <CodeAction> candidateActions = null;

                    var context = new CodeRefactoringContext(
                        document,
                        span,
                        a =>
                    {
                        if (state.EquivalenceKey == null ||
                            string.Equals(a.EquivalenceKey, state.EquivalenceKey, StringComparison.Ordinal))
                        {
                            if (action != null)
                            {
                                Fail($"Multiple refactorings registered by '{refactoringProvider.GetType().Name}'.", new CodeAction[] { action, a });
                            }

                            action = a;
                        }
                        else
                        {
                            (candidateActions ??= new List <CodeAction>()).Add(a);
                        }
                    },
Example #2
0
        internal async Task VerifyExpectedDocument(
            ExpectedTestState expected,
            Document document,
            CancellationToken cancellationToken)
        {
            SyntaxNode root = await document.GetSyntaxRootAsync(simplify : true, format : true, cancellationToken);

            string actual = root.ToFullString();

            Assert.Equal(expected.Source, actual);

            if (!expected.Annotations.IsEmpty ||
                !expected.AlwaysVerifyAnnotations.IsEmpty)
            {
                VerifyAnnotations(expected, root, actual);
            }
        }
Example #3
0
        private void VerifyAnnotations(
            ExpectedTestState expected,
            SyntaxNode root,
            string source)
        {
            foreach (KeyValuePair <string, ImmutableArray <TextSpan> > kvp in expected.AnnotationsByKind)
            {
                string kind = GetAnnotationKind(kvp.Key);
                ImmutableArray <TextSpan> spans = kvp.Value;

                VerifyAnnotations(root, source, kind, spans);
            }

            foreach (string kind in expected.AlwaysVerifyAnnotations)
            {
                if (!expected.AnnotationsByKind.ContainsKey(kind))
                {
                    ImmutableArray <TextSpan> spans = expected.AnnotationsByKind.GetValueOrDefault(kind, ImmutableArray <TextSpan> .Empty);

                    VerifyAnnotations(root, source, kind, spans);
                }
            }
        /// <summary>
        /// Verifies that specified source will produce compiler diagnostic.
        /// </summary>
        /// <param name="data"></param>
        /// <param name="expected"></param>
        /// <param name="options"></param>
        /// <param name="cancellationToken"></param>
        public async Task VerifyFixAsync(
            CompilerDiagnosticFixTestData data,
            ExpectedTestState expected,
            TestOptions options = null,
            CancellationToken cancellationToken = default)
        {
            if (data == null)
            {
                throw new ArgumentNullException(nameof(data));
            }

            if (expected == null)
            {
                throw new ArgumentNullException(nameof(expected));
            }

            cancellationToken.ThrowIfCancellationRequested();

            options ??= Options;

            TFixProvider fixProvider = Activator.CreateInstance <TFixProvider>();

            VerifyFixableDiagnostics(fixProvider, data.DiagnosticId);

            using (Workspace workspace = new AdhocWorkspace())
            {
                (Document document, ImmutableArray <ExpectedDocument> expectedDocuments) = CreateDocument(workspace.CurrentSolution, data.Source, data.AdditionalFiles, options);

                Project project = document.Project;

                document = project.GetDocument(document.Id);

                ImmutableArray <Diagnostic> previousDiagnostics = ImmutableArray <Diagnostic> .Empty;

                var fixRegistered = false;

                while (true)
                {
                    cancellationToken.ThrowIfCancellationRequested();

                    Compilation compilation = await document.Project.GetCompilationAsync(cancellationToken);

                    ImmutableArray <Diagnostic> diagnostics = compilation.GetDiagnostics(cancellationToken: cancellationToken);

                    int length = diagnostics.Length;

                    if (length == 0)
                    {
                        if (!fixRegistered)
                        {
                            Fail("No compiler diagnostic found.");
                        }

                        break;
                    }

                    if (previousDiagnostics.Any())
                    {
                        VerifyNoNewCompilerDiagnostics(previousDiagnostics, diagnostics, options);
                    }

                    if (DiagnosticDeepEqualityComparer.Equals(diagnostics, previousDiagnostics))
                    {
                        Fail("Same diagnostics returned before and after the fix was applied.", diagnostics);
                    }

                    Diagnostic diagnostic = FindDiagnosticToFix(diagnostics);

                    if (diagnostic == null)
                    {
                        if (!fixRegistered)
                        {
                            Fail($"No compiler diagnostic with ID '{data.DiagnosticId}' found.", diagnostics);
                        }

                        break;
                    }

                    CodeAction        action           = null;
                    List <CodeAction> candidateActions = null;

                    var context = new CodeFixContext(
                        document,
                        diagnostic,
                        (a, d) =>
                    {
                        if ((data.EquivalenceKey == null ||
                             string.Equals(data.EquivalenceKey, a.EquivalenceKey, StringComparison.Ordinal)) &&
                            d.Contains(diagnostic))
                        {
                            if (action != null)
                            {
                                Fail($"Multiple fixes registered by '{fixProvider.GetType().Name}'.", new CodeAction[] { action, a });
                            }

                            action = a;
                        }
                        else
                        {
                            (candidateActions ??= new List <CodeAction>()).Add(a);
                        }
                    },
        /// <summary>
        /// Verifies that specified source will produce compiler diagnostic.
        /// </summary>
        /// <param name="state"></param>
        /// <param name="expected"></param>
        /// <param name="options"></param>
        /// <param name="cancellationToken"></param>
        public async Task VerifyFixAsync(
            CompilerDiagnosticFixTestState state,
            ExpectedTestState expected,
            TestOptions options = null,
            CancellationToken cancellationToken = default)
        {
            cancellationToken.ThrowIfCancellationRequested();

            options ??= Options;

            TFixProvider fixProvider = Activator.CreateInstance <TFixProvider>();

            VerifyFixableDiagnostics(fixProvider, state.DiagnosticId);

            using (Workspace workspace = new AdhocWorkspace())
            {
                (Document document, ImmutableArray <ExpectedDocument> expectedDocuments) = CreateDocument(workspace.CurrentSolution, state.Source, state.AdditionalFiles, options);

                Project project = document.Project;

                document = project.GetDocument(document.Id);

                ImmutableArray <Diagnostic> previousDiagnostics = ImmutableArray <Diagnostic> .Empty;

                var fixRegistered = false;

                while (true)
                {
                    cancellationToken.ThrowIfCancellationRequested();

                    Compilation compilation = await document.Project.GetCompilationAsync(cancellationToken);

                    ImmutableArray <Diagnostic> diagnostics = compilation.GetDiagnostics(cancellationToken: cancellationToken);

                    int length = diagnostics.Length;

                    if (length == 0)
                    {
                        break;
                    }

                    if (previousDiagnostics.Any())
                    {
                        VerifyNoNewCompilerDiagnostics(previousDiagnostics, diagnostics, options);
                    }

                    if (DiagnosticDeepEqualityComparer.Equals(diagnostics, previousDiagnostics))
                    {
                        Assert.True(false, "Same diagnostics returned before and after the fix was applied." + diagnostics.ToDebugString());
                    }

                    Diagnostic diagnostic = FindDiagnosticToFix(diagnostics);

                    if (diagnostic == null)
                    {
                        break;
                    }

                    CodeAction action = null;

                    var context = new CodeFixContext(
                        document,
                        diagnostic,
                        (a, d) =>
                    {
                        if ((state.EquivalenceKey == null ||
                             string.Equals(state.EquivalenceKey, a.EquivalenceKey, StringComparison.Ordinal)) &&
                            d.Contains(diagnostic))
                        {
                            if (action != null)
                            {
                                Assert.True(false, $"Multiple fixes registered by '{fixProvider.GetType().Name}'.");
                            }

                            action = a;
                        }
                    },
                        cancellationToken);

                    await fixProvider.RegisterCodeFixesAsync(context);

                    if (action == null)
                    {
                        break;
                    }

                    fixRegistered = true;

                    document = await VerifyAndApplyCodeActionAsync(document, action, expected.CodeActionTitle);

                    previousDiagnostics = diagnostics;
                }

                Assert.True(fixRegistered, "No code fix has been registered.");

                await VerifyExpectedDocument(expected, document, cancellationToken);

                if (expectedDocuments.Any())
                {
                    await VerifyAdditionalDocumentsAsync(document.Project, expectedDocuments, cancellationToken);
                }
            }

            Diagnostic FindDiagnosticToFix(ImmutableArray <Diagnostic> diagnostics)
            {
                Diagnostic match = null;

                foreach (Diagnostic diagnostic in diagnostics)
                {
                    if (string.Equals(diagnostic.Id, state.DiagnosticId, StringComparison.Ordinal))
                    {
                        if (match == null ||
                            diagnostic.Location.SourceSpan.Start > match.Location.SourceSpan.Start)
                        {
                            match = diagnostic;
                        }
                    }
                }

                return(match);
            }
        }
        /// <summary>
        /// Verifies that refactoring will be applied correctly using specified <typeparamref name="TRefactoringProvider"/>.
        /// </summary>
        /// <param name="state"></param>
        /// <param name="expected"></param>
        /// <param name="options"></param>
        /// <param name="cancellationToken"></param>
        public async Task VerifyRefactoringAsync(
            RefactoringTestState state,
            ExpectedTestState expected,
            TestOptions options = null,
            CancellationToken cancellationToken = default)
        {
            if (state.Spans.IsEmpty)
            {
                Assert.True(false, "Span on which a refactoring should be invoked was not found.");
            }

            options ??= Options;

            TRefactoringProvider refactoringProvider = Activator.CreateInstance <TRefactoringProvider>();

            foreach (TextSpan span in state.Spans)
            {
                cancellationToken.ThrowIfCancellationRequested();

                using (Workspace workspace = new AdhocWorkspace())
                {
                    (Document document, ImmutableArray <ExpectedDocument> expectedDocuments) = CreateDocument(workspace.CurrentSolution, state.Source, state.AdditionalFiles, options);

                    SemanticModel semanticModel = await document.GetSemanticModelAsync(cancellationToken);

                    ImmutableArray <Diagnostic> compilerDiagnostics = semanticModel.GetDiagnostics(cancellationToken: cancellationToken);

                    VerifyCompilerDiagnostics(compilerDiagnostics, options);

                    CodeAction action = null;

                    var context = new CodeRefactoringContext(
                        document,
                        span,
                        a =>
                    {
                        if (state.EquivalenceKey == null ||
                            string.Equals(a.EquivalenceKey, state.EquivalenceKey, StringComparison.Ordinal))
                        {
                            if (action != null)
                            {
                                Assert.True(false, "Multiple fixes available.");
                            }

                            action = a;
                        }
                    },
                        cancellationToken);

                    await refactoringProvider.ComputeRefactoringsAsync(context);

                    Assert.True(action != null, "No code refactoring has been registered.");

                    document = await VerifyAndApplyCodeActionAsync(document, action, expected.CodeActionTitle);

                    semanticModel = await document.GetSemanticModelAsync(cancellationToken);

                    ImmutableArray <Diagnostic> newCompilerDiagnostics = semanticModel.GetDiagnostics(cancellationToken: cancellationToken);

                    VerifyCompilerDiagnostics(newCompilerDiagnostics, options);
                    VerifyNoNewCompilerDiagnostics(compilerDiagnostics, newCompilerDiagnostics, options);

                    await VerifyExpectedDocument(expected, document, cancellationToken);
                }
            }
        }