File: Diagnostics\AbstractSuppressionAllCodeTests.cs
Web Access
Project: ..\..\..\src\EditorFeatures\DiagnosticsTestUtilities\Microsoft.CodeAnalysis.EditorFeatures.DiagnosticsTests.Utilities.csproj (Microsoft.CodeAnalysis.EditorFeatures.DiagnosticsTests.Utilities)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
#nullable disable
 
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.CodeFixes.Suppression;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces;
using Microsoft.CodeAnalysis.Shared.Utilities;
using Microsoft.CodeAnalysis.Simplification;
using Microsoft.CodeAnalysis.Test.Utilities;
using Microsoft.CodeAnalysis.UnitTests.Diagnostics;
using Roslyn.Utilities;
using Xunit;
 
namespace Microsoft.CodeAnalysis.Editor.UnitTests.Diagnostics
{
    [UseExportProvider]
    public abstract class AbstractSuppressionAllCodeTests : IEqualityComparer<Diagnostic>
    {
        protected abstract TestWorkspace CreateWorkspaceFromFile(string definition, ParseOptions parseOptions);
 
        internal abstract Tuple<Analyzer, IConfigurationFixProvider> CreateDiagnosticProviderAndFixer(Workspace workspace);
 
        protected Task TestPragmaAsync(string code, ParseOptions options, Func<string, bool> verifier)
        {
            var set = new HashSet<ValueTuple<SyntaxToken, SyntaxToken>>();
            return TestPragmaOrAttributeAsync(code, options, pragma: true, digInto: n => true, verifier: verifier, fixChecker: c =>
            {
                var fix = (AbstractSuppressionCodeFixProvider.PragmaWarningCodeAction)c;
                var tuple = ValueTuple.Create(fix.StartToken_TestOnly, fix.EndToken_TestOnly);
                if (set.Contains(tuple))
                {
                    return true;
                }
 
                set.Add(tuple);
                return false;
            });
        }
 
        protected Task TestSuppressionWithAttributeAsync(string code, ParseOptions options, Func<SyntaxNode, bool> digInto, Func<string, bool> verifier)
        {
            var set = new HashSet<ISymbol>();
            return TestPragmaOrAttributeAsync(code, options, pragma: false, digInto: digInto, verifier: verifier, fixChecker: c =>
            {
                var fix = (AbstractSuppressionCodeFixProvider.GlobalSuppressMessageCodeAction)c;
                if (set.Contains(fix.TargetSymbol_TestOnly))
                {
                    return true;
                }
 
                set.Add(fix.TargetSymbol_TestOnly);
                return false;
            });
        }
 
        protected async Task TestPragmaOrAttributeAsync(
            string code, ParseOptions options, bool pragma, Func<SyntaxNode, bool> digInto, Func<string, bool> verifier, Func<CodeAction, bool> fixChecker)
        {
            using (var workspace = CreateWorkspaceFromFile(code, options))
            {
                var (analyzer, fixer) = CreateDiagnosticProviderAndFixer(workspace);
 
                var analyzerReference = new AnalyzerImageReference(ImmutableArray.Create<DiagnosticAnalyzer>(analyzer));
                workspace.TryApplyChanges(workspace.CurrentSolution.WithAnalyzerReferences(new[] { analyzerReference }));
 
                var document = workspace.CurrentSolution.Projects.Single().Documents.Single();
                var root = document.GetSyntaxRootAsync().GetAwaiter().GetResult();
                var existingDiagnostics = root.GetDiagnostics().ToArray();
 
                var descendants = root.DescendantNodesAndSelf(digInto).ToImmutableArray();
                analyzer.AllNodes = descendants;
                var diagnostics = await DiagnosticProviderTestUtilities.GetAllDiagnosticsAsync(workspace, document, root.FullSpan);
 
                foreach (var diagnostic in diagnostics)
                {
                    if (!fixer.IsFixableDiagnostic(diagnostic))
                    {
                        continue;
                    }
 
                    var fixes = fixer.GetFixesAsync(document, diagnostic.Location.SourceSpan, SpecializedCollections.SingletonEnumerable(diagnostic), CodeActionOptions.DefaultProvider, CancellationToken.None).GetAwaiter().GetResult();
                    if (fixes == null || fixes.Count() <= 0)
                    {
                        continue;
                    }
 
                    var fix = GetFix(fixes.Select(f => f.Action), pragma);
                    if (fix == null)
                    {
                        continue;
                    }
 
                    // already same fix has been tested
                    if (fixChecker(fix))
                    {
                        continue;
                    }
 
                    var operations = fix.GetOperationsAsync(
                        document.Project.Solution, new ProgressTracker(), CancellationToken.None).GetAwaiter().GetResult();
 
                    var applyChangesOperation = operations.OfType<ApplyChangesOperation>().Single();
                    var newDocument = applyChangesOperation.ChangedSolution.Projects.Single().Documents.Single();
                    var newTree = newDocument.GetSyntaxTreeAsync().GetAwaiter().GetResult();
 
                    var newText = newTree.GetText().ToString();
                    Assert.True(verifier(newText));
 
                    var newDiagnostics = newTree.GetDiagnostics();
                    Assert.Equal(0, existingDiagnostics.Except(newDiagnostics, this).Count());
                }
            }
        }
 
        private static CodeAction GetFix(IEnumerable<CodeAction> fixes, bool pragma)
        {
            if (pragma)
            {
                return fixes.FirstOrDefault(f => f is AbstractSuppressionCodeFixProvider.PragmaWarningCodeAction);
            }
 
            return fixes.OfType<AbstractSuppressionCodeFixProvider.GlobalSuppressMessageCodeAction>().FirstOrDefault();
        }
 
        public bool Equals(Diagnostic x, Diagnostic y)
            => x.Id == y.Id && x.Descriptor.Category == y.Descriptor.Category;
 
        public int GetHashCode(Diagnostic obj)
            => Hash.Combine(obj.Id, obj.Descriptor.Category.GetHashCode());
 
        internal class Analyzer : DiagnosticAnalyzer, IBuiltInAnalyzer
        {
            private readonly DiagnosticDescriptor _descriptor =
                new DiagnosticDescriptor("TestId", "Test", "Test", "Test", DiagnosticSeverity.Warning, isEnabledByDefault: true);
 
            public CodeActionRequestPriority RequestPriority => CodeActionRequestPriority.Normal;
 
            public bool OpenFileOnly(SimplifierOptions options) => false;
 
            public ImmutableArray<SyntaxNode> AllNodes { get; set; }
 
            public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics
            {
                get
                {
                    return ImmutableArray.Create(_descriptor);
                }
            }
 
            public DiagnosticAnalyzerCategory GetAnalyzerCategory()
                => DiagnosticAnalyzerCategory.SyntaxTreeWithoutSemanticsAnalysis;
 
            public override void Initialize(AnalysisContext analysisContext)
            {
                analysisContext.RegisterSyntaxTreeAction(
                    context =>
                    {
                        foreach (var node in AllNodes)
                        {
                            context.ReportDiagnostic(Diagnostic.Create(_descriptor, node.GetLocation()));
                        }
                    });
            }
        }
    }
}