File: Diagnostics\DiagnosticTaggerWrapper.cs
Web Access
Project: src\src\EditorFeatures\TestUtilities\Microsoft.CodeAnalysis.EditorFeatures.Test.Utilities.csproj (Microsoft.CodeAnalysis.EditorFeatures.Test.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.
 
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Editor.InlineDiagnostics;
using Microsoft.CodeAnalysis.Editor.Shared.Utilities;
using Microsoft.CodeAnalysis.Shared.TestHooks;
using Microsoft.CodeAnalysis.SolutionCrawler;
using Microsoft.CodeAnalysis.Test.Utilities;
using Microsoft.VisualStudio.Text.Tagging;
using Roslyn.Test.Utilities;
 
namespace Microsoft.CodeAnalysis.Editor.UnitTests.Diagnostics;
 
internal sealed class DiagnosticTaggerWrapper<TProvider, TTag>
    where TProvider : AbstractDiagnosticsTaggerProvider<TTag>
    where TTag : ITag
{
    private readonly EditorTestWorkspace _workspace;
    private readonly IThreadingContext _threadingContext;
    private readonly IAsynchronousOperationListenerProvider _listenerProvider;
 
    private AbstractDiagnosticsTaggerProvider<TTag>? _taggerProvider;
 
    public DiagnosticTaggerWrapper(
        EditorTestWorkspace workspace,
        IReadOnlyDictionary<string, ImmutableArray<DiagnosticAnalyzer>>? analyzerMap = null,
        bool createTaggerProvider = true)
    {
        _threadingContext = workspace.GetService<IThreadingContext>();
        _listenerProvider = workspace.GetService<IAsynchronousOperationListenerProvider>();
 
        var analyzerReference = new TestAnalyzerReferenceByLanguage(analyzerMap ?? DiagnosticExtensions.GetCompilerDiagnosticAnalyzersMap());
        workspace.TryApplyChanges(workspace.CurrentSolution.WithAnalyzerReferences([analyzerReference]));
 
        // Change the background analysis scope to OpenFiles instead of ActiveFile (default),
        // so that every diagnostic tagger test does not need to mark test files as "active" file.
        workspace.GlobalOptions.SetGlobalOption(SolutionCrawlerOptionsStorage.BackgroundAnalysisScopeOption, LanguageNames.CSharp, BackgroundAnalysisScope.OpenFiles);
        workspace.GlobalOptions.SetGlobalOption(SolutionCrawlerOptionsStorage.BackgroundAnalysisScopeOption, LanguageNames.VisualBasic, BackgroundAnalysisScope.OpenFiles);
 
        _workspace = workspace;
 
        if (createTaggerProvider)
        {
            _ = TaggerProvider;
        }
    }
 
    public AbstractDiagnosticsTaggerProvider<TTag> TaggerProvider
    {
        get
        {
            if (_taggerProvider == null)
            {
                WpfTestRunner.RequireWpfFact($"{nameof(DiagnosticTaggerWrapper<TProvider, TTag>)}.{nameof(TaggerProvider)} creates asynchronous taggers");
 
                if (typeof(TProvider) == typeof(InlineDiagnosticsTaggerProvider))
                {
                    _taggerProvider = (AbstractDiagnosticsTaggerProvider<TTag>)(object)_workspace.ExportProvider.GetExportedValues<ITaggerProvider>()
                        .OfType<TProvider>()
                        .Single();
                }
                else
                {
                    throw new InvalidOperationException();
                }
            }
 
            return _taggerProvider;
        }
    }
 
    public async Task WaitForTags()
    {
        await _listenerProvider.WaitAllDispatcherOperationAndTasksAsync(
            _workspace,
            FeatureAttribute.Workspace,
            FeatureAttribute.SolutionCrawlerLegacy,
            FeatureAttribute.DiagnosticService,
            FeatureAttribute.ErrorSquiggles,
            FeatureAttribute.Classification);
    }
}