File: ExternalAccess\UnitTesting\SolutionCrawler\UnitTestingWorkCoordinator.UnitTestingWorkItem.cs
Web Access
Project: ..\..\..\src\Features\Core\Portable\Microsoft.CodeAnalysis.Features.csproj (Microsoft.CodeAnalysis.Features)
// 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.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Linq;
using Microsoft.CodeAnalysis.ExternalAccess.UnitTesting.Api;
using Microsoft.CodeAnalysis.Shared.TestHooks;
using Roslyn.Utilities;
 
namespace Microsoft.CodeAnalysis.ExternalAccess.UnitTesting.SolutionCrawler
{
    internal partial class UnitTestingSolutionCrawlerRegistrationService
    {
        internal partial class UnitTestingWorkCoordinator
        {
            // this is internal only type
            private readonly struct UnitTestingWorkItem
            {
                // project related workitem
                public readonly ProjectId ProjectId;
 
                // document related workitem
                public readonly DocumentId? DocumentId;
                public readonly string Language;
                public readonly UnitTestingInvocationReasons InvocationReasons;
                public readonly bool IsLowPriority;
 
                // extra info
                public readonly SyntaxPath? ActiveMember;
 
                /// <summary>
                /// Non-empty if this work item is intended to be executed only for specific incremental analyzer(s).
                /// Otherwise, the work item is applicable to all relevant incremental analyzers.
                /// </summary>
                public readonly ImmutableHashSet<IUnitTestingIncrementalAnalyzer> SpecificAnalyzers;
 
                /// <summary>
                /// Gets all the applicable analyzers to execute for this work item.
                /// If this work item has any specific analyzer(s), then returns the intersection of <see cref="SpecificAnalyzers"/>
                /// and the given <paramref name="allAnalyzers"/>.
                /// Otherwise, returns <paramref name="allAnalyzers"/>.
                /// </summary>
                public IEnumerable<IUnitTestingIncrementalAnalyzer> GetApplicableAnalyzers(ImmutableArray<IUnitTestingIncrementalAnalyzer> allAnalyzers)
                    => SpecificAnalyzers?.Count > 0 ? SpecificAnalyzers.Where(allAnalyzers.Contains) : allAnalyzers;
 
                // retry
                public readonly bool IsRetry;
 
                // common
                public readonly IAsyncToken AsyncToken;
 
#if false // Not used in unit testing crawling
                public bool MustRefresh
                {
                    get
                    {
                        // in current design, we need to re-run all incremental analyzer on document open and close
                        // so that incremental analyzer who only cares about opened document can have a chance to clean up
                        // its state.
                        return InvocationReasons.Contains(UnitTestingPredefinedInvocationReasons.DocumentOpened) ||
                               InvocationReasons.Contains(UnitTestingPredefinedInvocationReasons.DocumentClosed);
                    }
                }
#endif
 
                private UnitTestingWorkItem(
                    DocumentId? documentId,
                    ProjectId projectId,
                    string language,
                    UnitTestingInvocationReasons invocationReasons,
                    bool isLowPriority,
                    SyntaxPath? activeMember,
                    ImmutableHashSet<IUnitTestingIncrementalAnalyzer> specificAnalyzers,
                    bool retry,
                    IAsyncToken asyncToken)
                {
                    Debug.Assert(documentId == null || documentId.ProjectId == projectId);
 
                    DocumentId = documentId;
                    ProjectId = projectId;
                    Language = language;
                    InvocationReasons = invocationReasons;
                    IsLowPriority = isLowPriority;
 
                    ActiveMember = activeMember;
                    SpecificAnalyzers = specificAnalyzers;
 
                    IsRetry = retry;
 
                    AsyncToken = asyncToken;
                }
 
                public UnitTestingWorkItem(DocumentId documentId, string language, UnitTestingInvocationReasons invocationReasons, bool isLowPriority, SyntaxPath? activeMember, IAsyncToken asyncToken)
                    : this(documentId, documentId.ProjectId, language, invocationReasons, isLowPriority, activeMember, ImmutableHashSet.Create<IUnitTestingIncrementalAnalyzer>(), retry: false, asyncToken)
                {
                }
 
                public UnitTestingWorkItem(DocumentId documentId, string language, UnitTestingInvocationReasons invocationReasons, bool isLowPriority, IUnitTestingIncrementalAnalyzer? analyzer, IAsyncToken asyncToken)
                    : this(documentId, documentId.ProjectId, language, invocationReasons, isLowPriority, activeMember: null,
                           analyzer == null ? ImmutableHashSet.Create<IUnitTestingIncrementalAnalyzer>() : ImmutableHashSet.Create(analyzer),
                           retry: false, asyncToken)
                {
                }
 
                public object Key => DocumentId ?? (object)ProjectId;
 
                private ImmutableHashSet<IUnitTestingIncrementalAnalyzer> Union(ImmutableHashSet<IUnitTestingIncrementalAnalyzer> analyzers)
                {
                    if (analyzers.IsEmpty)
                    {
                        return SpecificAnalyzers;
                    }
 
                    if (SpecificAnalyzers.IsEmpty)
                    {
                        return analyzers;
                    }
 
                    return SpecificAnalyzers.Union(analyzers);
                }
 
                public UnitTestingWorkItem Retry(IAsyncToken asyncToken)
                {
                    return new UnitTestingWorkItem(
                        DocumentId, ProjectId, Language, InvocationReasons, IsLowPriority, ActiveMember, SpecificAnalyzers,
                        retry: true, asyncToken: asyncToken);
                }
 
                public UnitTestingWorkItem With(
                    UnitTestingInvocationReasons invocationReasons, SyntaxPath? currentMember,
                    ImmutableHashSet<IUnitTestingIncrementalAnalyzer> analyzers, bool retry, IAsyncToken asyncToken)
                {
                    // dispose old one
                    AsyncToken.Dispose();
 
                    // create new work item
                    return new UnitTestingWorkItem(
                        DocumentId, ProjectId, Language,
                        InvocationReasons.With(invocationReasons),
                        IsLowPriority,
                        ActiveMember == currentMember ? currentMember : null,
                        Union(analyzers), IsRetry || retry,
                        asyncToken);
                }
 
                public UnitTestingWorkItem WithAsyncToken(IAsyncToken asyncToken)
                {
                    return new UnitTestingWorkItem(
                        DocumentId, ProjectId, Language, InvocationReasons, IsLowPriority, ActiveMember, SpecificAnalyzers,
                        retry: false, asyncToken: asyncToken);
                }
 
                public UnitTestingWorkItem ToProjectWorkItem(IAsyncToken asyncToken)
                {
                    RoslynDebug.Assert(DocumentId != null);
 
                    // create new work item that represents work per project
                    return new UnitTestingWorkItem(
                        documentId: null,
                        DocumentId.ProjectId,
                        Language,
                        InvocationReasons,
                        IsLowPriority,
                        ActiveMember,
                        SpecificAnalyzers,
                        IsRetry,
                        asyncToken);
                }
 
                public UnitTestingWorkItem With(ImmutableHashSet<IUnitTestingIncrementalAnalyzer> specificAnalyzers, IAsyncToken asyncToken)
                {
                    return new UnitTestingWorkItem(DocumentId, ProjectId, Language, InvocationReasons,
                        IsLowPriority, ActiveMember, specificAnalyzers, IsRetry, asyncToken);
                }
 
                public override string ToString()
                    => $"{DocumentId?.ToString() ?? ProjectId.ToString()}, ({InvocationReasons}), LowPriority:{IsLowPriority}, ActiveMember:{ActiveMember != null}, Retry:{IsRetry}, ({string.Join("|", SpecificAnalyzers.Select(a => a.GetType().Name))})";
            }
        }
    }
}