File: Preview\PreviewWorkspaceTests.cs
Web Access
Project: src\src\EditorFeatures\Test\Microsoft.CodeAnalysis.EditorFeatures.UnitTests.csproj (Microsoft.CodeAnalysis.EditorFeatures.UnitTests)
// 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.Immutable;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Editor.Shared.Preview;
using Microsoft.CodeAnalysis.Host;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Storage;
using Microsoft.CodeAnalysis.Test.Utilities;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Test.Utilities;
using Roslyn.Utilities;
using Xunit;
 
namespace Microsoft.CodeAnalysis.Editor.UnitTests.Preview;
 
[UseExportProvider]
[Trait(Traits.Editor, Traits.Editors.Preview), Trait(Traits.Feature, Traits.Features.Tagging)]
public class PreviewWorkspaceTests
{
    [Fact]
    public void TestPreviewCreationDefault()
    {
        using var previewWorkspace = new PreviewWorkspace();
        Assert.NotNull(previewWorkspace.CurrentSolution);
    }
 
    [Fact]
    public void TestPreviewCreationWithExplicitHostServices()
    {
        var hostServices = FeaturesTestCompositions.Features.GetHostServices();
        using var previewWorkspace = new PreviewWorkspace(hostServices);
        Assert.NotNull(previewWorkspace.CurrentSolution);
    }
 
    [Fact]
    public void TestPreviewCreationWithSolution()
    {
        using var custom = new AdhocWorkspace();
        using var previewWorkspace = new PreviewWorkspace(custom.CurrentSolution);
        Assert.NotNull(previewWorkspace.CurrentSolution);
    }
 
    [Fact]
    public void TestPreviewAddRemoveProject()
    {
        using var previewWorkspace = new PreviewWorkspace();
        var solution = previewWorkspace.CurrentSolution;
        var project = solution.AddProject("project", "project.dll", LanguageNames.CSharp);
        Assert.True(previewWorkspace.TryApplyChanges(project.Solution));
 
        var newSolution = previewWorkspace.CurrentSolution.RemoveProject(project.Id);
        Assert.True(previewWorkspace.TryApplyChanges(newSolution));
 
        Assert.Equal(0, previewWorkspace.CurrentSolution.ProjectIds.Count);
    }
 
    [Fact]
    public void TestPreviewProjectChanges()
    {
        using var previewWorkspace = new PreviewWorkspace();
        var solution = previewWorkspace.CurrentSolution;
        var project = solution.AddProject("project", "project.dll", LanguageNames.CSharp);
        Assert.True(previewWorkspace.TryApplyChanges(project.Solution));
 
        var addedSolution = previewWorkspace.CurrentSolution.Projects.First()
                                            .AddMetadataReference(NetFramework.mscorlib)
                                            .AddDocument("document", "").Project.Solution;
        Assert.True(previewWorkspace.TryApplyChanges(addedSolution));
        Assert.Equal(1, previewWorkspace.CurrentSolution.Projects.First().MetadataReferences.Count);
        Assert.Equal(1, previewWorkspace.CurrentSolution.Projects.First().DocumentIds.Count);
 
        var text = "class C {}";
        var changedSolution = previewWorkspace.CurrentSolution.Projects.First().Documents.First().WithText(SourceText.From(text)).Project.Solution;
        Assert.True(previewWorkspace.TryApplyChanges(changedSolution));
        Assert.Equal(previewWorkspace.CurrentSolution.Projects.First().Documents.First().GetTextAsync().Result.ToString(), text);
 
        var removedSolution = previewWorkspace.CurrentSolution.Projects.First()
                                            .RemoveMetadataReference(previewWorkspace.CurrentSolution.Projects.First().MetadataReferences[0])
                                            .RemoveDocument(previewWorkspace.CurrentSolution.Projects.First().DocumentIds[0]).Solution;
 
        Assert.True(previewWorkspace.TryApplyChanges(removedSolution));
        Assert.Equal(0, previewWorkspace.CurrentSolution.Projects.First().MetadataReferences.Count);
        Assert.Equal(0, previewWorkspace.CurrentSolution.Projects.First().DocumentIds.Count);
    }
 
    [WpfFact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/923121")]
    public void TestPreviewOpenCloseFile()
    {
        using var previewWorkspace = new PreviewWorkspace();
        var solution = previewWorkspace.CurrentSolution;
        var project = solution.AddProject("project", "project.dll", LanguageNames.CSharp);
        var document = project.AddDocument("document", "");
        var sourceTextContainer = SourceText.From("Text").Container;
 
        Assert.True(previewWorkspace.TryApplyChanges(document.Project.Solution));
 
        previewWorkspace.OpenDocument(document.Id, sourceTextContainer);
        Assert.Equal(1, previewWorkspace.GetOpenDocumentIds().Count());
        Assert.True(previewWorkspace.IsDocumentOpen(document.Id));
 
        previewWorkspace.CloseDocument(document.Id);
        Assert.Equal(0, previewWorkspace.GetOpenDocumentIds().Count());
        Assert.False(previewWorkspace.IsDocumentOpen(document.Id));
    }
 
    [Fact]
    public async Task TestPreviewServices()
    {
        using var previewWorkspace = new PreviewWorkspace(EditorTestCompositions.EditorFeatures.GetHostServices());
        var persistentService = previewWorkspace.Services.SolutionServices.GetPersistentStorageService();
 
        var storage = await persistentService.GetStorageAsync(SolutionKey.ToSolutionKey(previewWorkspace.CurrentSolution), CancellationToken.None);
        Assert.IsType<NoOpPersistentStorage>(storage);
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/28639")]
    public void TestPreviewWorkspaceDoesNotLeakSolution()
    {
        // Verify that analyzer execution doesn't leak solution instances from the preview workspace.
 
        var previewWorkspace = new PreviewWorkspace();
        Assert.NotNull(previewWorkspace.CurrentSolution);
        var solutionObjectReference = ObjectReference.CreateFromFactory(
            static previewWorkspace =>
            {
                var project = previewWorkspace.CurrentSolution.AddProject("project", "project.dll", LanguageNames.CSharp);
                Assert.True(previewWorkspace.TryApplyChanges(project.Solution));
                return previewWorkspace.CurrentSolution;
            },
            previewWorkspace);
 
        var analyzers = ImmutableArray.Create<DiagnosticAnalyzer>(new CommonDiagnosticAnalyzers.NotConfigurableDiagnosticAnalyzer());
        ExecuteAnalyzers(previewWorkspace, analyzers);
 
        previewWorkspace.Dispose();
        solutionObjectReference.AssertReleased();
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/pull/67142")]
    public void TestPreviewWorkspaceDoesNotLeakItself()
    {
        var composition = EditorTestCompositions.EditorFeatures;
        var exportProvider = composition.ExportProviderFactory.CreateExportProvider();
        var previewWorkspaceReference = ObjectReference.CreateFromFactory(
            static composition => new PreviewWorkspace(composition.GetHostServices()),
            composition);
 
        // Verify the GC can reclaim member for a workspace which has not been disposed.
        previewWorkspaceReference.AssertReleased();
 
        // Keep the export provider alive longer than the workspace to further ensure that the workspace is not GC
        // rooted within the export provider instance.
        GC.KeepAlive(exportProvider);
    }
 
    private static void ExecuteAnalyzers(PreviewWorkspace previewWorkspace, ImmutableArray<DiagnosticAnalyzer> analyzers)
    {
        var analyzerOptions = new AnalyzerOptions(additionalFiles: ImmutableArray<AdditionalText>.Empty);
        var project = previewWorkspace.CurrentSolution.Projects.Single();
        var compilationWithAnalyzersOptions = new CompilationWithAnalyzersOptions(analyzerOptions, onAnalyzerException: null, concurrentAnalysis: false, logAnalyzerExecutionTime: false);
        var compilation = project.GetRequiredCompilationAsync(CancellationToken.None).Result;
        var compilationWithAnalyzers = new CompilationWithAnalyzers(compilation, analyzers, compilationWithAnalyzersOptions);
        var result = compilationWithAnalyzers.GetAnalysisResultAsync(CancellationToken.None).Result;
        Assert.Equal(1, result.CompilationDiagnostics.Count);
    }
}