File: InProcess\OutputInProcess.cs
Web Access
Project: src\src\Razor\src\Razor\test\Microsoft.VisualStudio.Razor.IntegrationTests\Microsoft.VisualStudio.Razor.IntegrationTests.csproj (Microsoft.VisualStudio.Razor.IntegrationTests)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Razor.Logging;
using Microsoft.CodeAnalysis.Razor.Workspaces.Settings;
using Microsoft.VisualStudio.Razor.IntegrationTests.Extensions;
using Microsoft.VisualStudio.Razor.IntegrationTests.Logging;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.Shell.Interop;
using Microsoft.VisualStudio.TextManager.Interop;
using Xunit.Abstractions;
 
namespace Microsoft.VisualStudio.Extensibility.Testing;
 
[TestService]
internal partial class OutputInProcess
{
    private const string RazorPaneName = "Razor Logger Output";
 
    private IntegrationTestOutputLoggerProvider? _testLoggerProvider;
 
    public async Task<ILogger> SetupIntegrationTestLoggerAsync(ITestOutputHelper testOutputHelper, CancellationToken cancellationToken)
    {
        // Make sure we log as much as possible for integration tests
        var clientSettingsManager = await TestServices.Shell.GetComponentModelServiceAsync<IClientSettingsManager>(cancellationToken);
        clientSettingsManager.Update(clientSettingsManager.GetClientSettings().AdvancedSettings with { LogLevel = LogLevel.Trace });
 
        var loggerFactory = await TestServices.Shell.GetComponentModelServiceAsync<ILoggerFactory>(cancellationToken);
 
        // We can't remove logging providers, so we just keep track of ours so we can make sure it points to the right test output helper
        if (_testLoggerProvider is null)
        {
            _testLoggerProvider = new IntegrationTestOutputLoggerProvider(testOutputHelper);
            loggerFactory.AddLoggerProvider(_testLoggerProvider);
        }
        else
        {
            _testLoggerProvider.SetOutput(testOutputHelper);
        }
 
        return loggerFactory.GetOrCreateLogger(GetType().Name);
    }
 
    public void ClearIntegrationTestLogger()
    {
        _testLoggerProvider?.SetOutput(null);
    }
 
    public async Task<bool> HasErrorsAsync(CancellationToken cancellationToken)
    {
        var content = await GetRazorOutputPaneContentAsync(cancellationToken);
 
        return content is null || content.Contains("Error");
    }
 
    /// <summary>
    /// This method returns the current content of the "Razor Language Server Client" output pane.
    /// </summary>
    /// <param name="cancellationToken">A cancellation token.</param>
    /// <returns>The contents of the RLSC output pane.</returns>
    public async Task<string?> GetRazorOutputPaneContentAsync(CancellationToken cancellationToken)
    {
        var outputPaneTextView = await GetOutputPaneTextViewAsync(RazorPaneName, cancellationToken);
 
        if (outputPaneTextView is null)
        {
            return null;
        }
 
        return await outputPaneTextView.GetContentAsync(JoinableTaskFactory, cancellationToken);
    }
 
    private async Task<IVsTextView?> GetOutputPaneTextViewAsync(string paneName, CancellationToken cancellationToken)
    {
        var sVSOutputWindow = await TestServices.Shell.GetRequiredGlobalServiceAsync<SVsOutputWindow, IVsOutputWindow>(cancellationToken);
        var extensibleObject = await TestServices.Shell.GetRequiredGlobalServiceAsync<SVsOutputWindow, IVsExtensibleObject>(cancellationToken);
 
        // The null propName gives use the OutputWindow object
        ErrorHandler.ThrowOnFailure(extensibleObject.GetAutomationObject(pszPropName: null, out var outputWindowObj));
        var outputWindow = (EnvDTE.OutputWindow)outputWindowObj;
 
        // This is a public entry point to COutputWindow::GetPaneByName
        EnvDTE.OutputWindowPane? pane = null;
        try
        {
            pane = outputWindow.OutputWindowPanes.Item(paneName);
        }
        catch (ArgumentException)
        {
            return null;
        }
 
        var textView = OutputWindowPaneToIVsTextView(pane, sVSOutputWindow);
 
        return textView;
 
        static IVsTextView OutputWindowPaneToIVsTextView(EnvDTE.OutputWindowPane outputWindowPane, IVsOutputWindow sVsOutputWindow)
        {
            var guid = Guid.Parse(outputWindowPane.Guid);
            ErrorHandler.ThrowOnFailure(sVsOutputWindow.GetPane(guid, out var result));
 
            if (result is not IVsTextView textView)
            {
                throw new InvalidOperationException($"{nameof(IVsOutputWindowPane)} should implement {nameof(IVsTextView)}");
            }
 
            return textView;
        }
    }
}