File: LspEditorFeatureDetectorTest.cs
Web Access
Project: src\src\Razor\src\Razor\test\Microsoft.VisualStudio.LanguageServices.Razor.UnitTests\Microsoft.VisualStudio.LanguageServices.Razor.UnitTests.csproj (Microsoft.VisualStudio.LanguageServices.Razor.UnitTests)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using Microsoft.AspNetCore.Razor;
using Microsoft.AspNetCore.Razor.Test.Common;
using Microsoft.AspNetCore.Razor.Test.Common.VisualStudio;
using Microsoft.CodeAnalysis.Razor.Logging;
using Microsoft.VisualStudio.Razor.Logging;
using Microsoft.VisualStudio.Shell.Interop;
using Moq;
using Xunit;
using Xunit.Abstractions;
 
namespace Microsoft.VisualStudio.Razor;
 
public class LspEditorFeatureDetectorTest(ITestOutputHelper testOutput) : ToolingTestBase(testOutput)
{
    public static TheoryData<bool, bool> IsLspEditorEnabledAndSupportedTestData { get; } = new()
    {
        // hasDotNetCoreCSharpCapability, expectedResult
        { false, false }, // .Net Framework project - always non-LSP
        { true, true },  // .Net Core project
    };
 
    [UITheory]
    [MemberData(nameof(IsLspEditorEnabledAndSupportedTestData))]
    public void IsLspEditorEnabledAndSupported(
        bool hasDotNetCoreCSharpCapability,
        bool expectedResult)
    {
        // Arrange
        var featureDetector = CreateLspEditorFeatureDetector(hasDotNetCoreCSharpCapability);
 
        // Act
        var result = featureDetector.IsLspEditorSupported(@"c:\TestProject\TestFile.cshtml");
 
        // Assert
        Assert.Equal(expectedResult, result);
    }
 
    public static TheoryData<bool, bool, bool, bool> IsRemoteClientTestData { get; } = new()
    {
        // isLiveShareHostActive, isLiveShareGuestActive, cloudEnvironmentConnectedActive, expectedResult
        { false, false, false, false },
        { true, false, false, false },
        { false, true, false, true },
        { false, false, true, true },
        { true, false, true, true },
        { true, true, true, true }
    };
 
    [UITheory]
    [MemberData(nameof(IsRemoteClientTestData))]
    public void IsRemoteClient(bool liveShareHostActive, bool liveShareGuestActive, bool cloudEnvironmentConnectedActive, bool expectedResult)
    {
        // Arrange
        var uiContextService = CreateUIContextService(liveShareHostActive, liveShareGuestActive, cloudEnvironmentConnectedActive);
        var featureDetector = CreateLspEditorFeatureDetector(uiContextService);
 
        // Act
        var result = featureDetector.IsRemoteClient();
 
        // Assert
        Assert.Equal(expectedResult, result);
    }
 
    public static TheoryData<bool, bool, bool, bool> IsLiveShareHostTestData { get; } = new()
    {
        // isLiveShareHostActive, isLiveShareGuestActive, cloudEnvironmentConnectedActive, expectedResult
        { false, false, false, false },
        { true, false, false, true },
        { false, true, false, false },
        { false, false, true, false },
        { true, false, true, true },
        { true, true, true, true }
    };
 
    [UITheory]
    [MemberData(nameof(IsLiveShareHostTestData))]
    public void IsLiveShareHost(bool liveShareHostActive, bool liveShareGuestActive, bool cloudEnvironmentConnectedActive, bool expectedResult)
    {
        // Arrange
        var uiContextService = CreateUIContextService(liveShareHostActive, liveShareGuestActive, cloudEnvironmentConnectedActive);
        var featureDetector = CreateLspEditorFeatureDetector(uiContextService);
 
        // Act
        var result = featureDetector.IsLiveShareHost();
 
        // Assert
        Assert.Equal(expectedResult, result);
    }
 
    private ILspEditorFeatureDetector CreateLspEditorFeatureDetector(IUIContextService uiContextService)
        => CreateLspEditorFeatureDetector(uiContextService, hasDotNetCoreCSharpCapability: true);
 
    private ILspEditorFeatureDetector CreateLspEditorFeatureDetector(
        bool hasDotNetCoreCSharpCapability = true)
    {
        return CreateLspEditorFeatureDetector(CreateUIContextService(), hasDotNetCoreCSharpCapability);
    }
 
    private ILspEditorFeatureDetector CreateLspEditorFeatureDetector(
        IUIContextService uiContextService,
        bool hasDotNetCoreCSharpCapability)
    {
        uiContextService ??= CreateUIContextService();
 
        var featureDetector = new LspEditorFeatureDetector(
            uiContextService,
            CreateProjectCapabilityResolver(hasDotNetCoreCSharpCapability),
            CreateRazorActivityLog());
 
        AddDisposable(featureDetector);
 
        return featureDetector;
    }
 
    private static IUIContextService CreateUIContextService(
        bool liveShareHostActive = false,
        bool liveShareGuestActive = false,
        bool cloudEnvironmentConnectedActive = false)
    {
        var mock = new StrictMock<IUIContextService>();
 
        mock.Setup(x => x.IsActive(Guids.LiveShareHostUIContextGuid))
            .Returns(liveShareHostActive);
 
        mock.Setup(x => x.IsActive(Guids.LiveShareGuestUIContextGuid))
            .Returns(liveShareGuestActive);
 
        mock.Setup(x => x.IsActive(VSConstants.UICONTEXT.CloudEnvironmentConnected_guid))
            .Returns(cloudEnvironmentConnectedActive);
 
        return mock.Object;
    }
 
    private static IProjectCapabilityResolver CreateProjectCapabilityResolver(bool hasDotNetCoreCSharpCapability)
    {
        var projectCapabilityResolverMock = new StrictMock<IProjectCapabilityResolver>();
 
        projectCapabilityResolverMock
            .Setup(x => x.CheckCapability(WellKnownProjectCapabilities.DotNetCoreCSharp, It.IsAny<string>()))
            .Returns(new CapabilityCheckResult(IsInProject: true, HasCapability: hasDotNetCoreCSharpCapability));
 
        return projectCapabilityResolverMock.Object;
    }
 
    private RazorActivityLog CreateRazorActivityLog()
    {
        var vsActivityLogMock = new StrictMock<IVsActivityLog>();
        vsActivityLogMock
            .Setup(x => x.LogEntry(It.IsAny<uint>(), "Razor", It.IsAny<string>()))
            .Callback((uint entryType, string source, string description) =>
            {
                switch ((__ACTIVITYLOG_ENTRYTYPE)entryType)
                {
                    case __ACTIVITYLOG_ENTRYTYPE.ALE_ERROR:
                        Logger.LogError($"Error:{description}");
                        break;
 
                    case __ACTIVITYLOG_ENTRYTYPE.ALE_WARNING:
                        Logger.LogError($"Warning:{description}");
                        break;
 
                    case __ACTIVITYLOG_ENTRYTYPE.ALE_INFORMATION:
                        Logger.LogError($"Info:{description}");
                        break;
 
                    default:
                        Assumed.Unreachable();
                        break;
                }
            })
            .Returns(VSConstants.S_OK);
 
        var serviceProvider = VsMocks.CreateAsyncServiceProvider(b =>
            b.AddService<SVsActivityLog, IVsActivityLog>(vsActivityLogMock.Object));
 
        var activityLog = new RazorActivityLog(serviceProvider);
 
        AddDisposable(activityLog);
 
        return activityLog;
    }
}