File: Interactive\Commands\ResetInteractiveTests.cs
Web Access
Project: src\src\VisualStudio\CSharp\Test\Microsoft.VisualStudio.LanguageServices.CSharp.UnitTests.csproj (Microsoft.VisualStudio.LanguageServices.CSharp.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.
 
#nullable disable
 
extern alias InteractiveHost;
 
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Test.Utilities;
using Microsoft.VisualStudio.Text.Editor.OptionsExtensionMethods;
using Roslyn.Test.Utilities;
using Xunit;
using InteractiveHost::Microsoft.CodeAnalysis.Interactive;
using Microsoft.CodeAnalysis.Editor.UnitTests;
using Microsoft.VisualStudio.InteractiveWindow;
using Microsoft.VisualStudio.Utilities;
using Microsoft.CodeAnalysis.Options;
 
namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Interactive.Commands
{
    [UseExportProvider]
    public class ResetInteractiveTests
    {
        private const string WorkspaceXmlStr =
@"<Workspace>
    <Project Language=""Visual Basic"" AssemblyName=""ResetInteractiveVisualBasicSubproject"" CommonReferences=""true"">
        <Document FilePath=""VisualBasicDocument""></Document>
    </Project>
    <Project Language=""C#"" AssemblyName=""ResetInteractiveTestsAssembly"" CommonReferences=""true"">
        <ProjectReference>ResetInteractiveVisualBasicSubproject</ProjectReference>
        <Document FilePath=""ResetInteractiveTestsDocument"">
namespace ResetInteractiveTestsDocument
{
    class TestClass
    {
    }
}</Document>
    </Project>
</Workspace>";
 
        [WpfFact]
        [Trait(Traits.Feature, Traits.Features.Interactive)]
        public async Task TestResetREPLWithProjectContext()
        {
            using var workspace = TestWorkspace.Create(WorkspaceXmlStr, composition: EditorTestCompositions.InteractiveWindow);
 
            var project = workspace.CurrentSolution.Projects.FirstOrDefault(p => p.AssemblyName == "ResetInteractiveTestsAssembly");
            var document = project.Documents.FirstOrDefault(d => d.FilePath == "ResetInteractiveTestsDocument");
            var replReferenceCommands = GetProjectReferences(workspace, project).Select(r => CreateReplReferenceCommand(r));
 
            Assert.True(replReferenceCommands.Any(rc => rc.EndsWith(@"ResetInteractiveTestsAssembly.dll""")));
            Assert.True(replReferenceCommands.Any(rc => rc.EndsWith(@"ResetInteractiveVisualBasicSubproject.dll""")));
 
            var expectedReferences = replReferenceCommands.ToList();
            var expectedUsings = new List<string> { @"using ""System"";", @"using ""ResetInteractiveTestsDocument"";" };
            await AssertResetInteractiveAsync(workspace, project, buildSucceeds: true, expectedReferences: expectedReferences, expectedUsings: expectedUsings);
 
            // Test that no submissions are executed if the build fails.
            await AssertResetInteractiveAsync(workspace, project, buildSucceeds: false, expectedReferences: []);
        }
 
        private async Task AssertResetInteractiveAsync(
            TestWorkspace workspace,
            Project project,
            bool buildSucceeds,
            List<string> expectedReferences = null,
            List<string> expectedUsings = null)
        {
            expectedReferences ??= [];
            expectedUsings ??= [];
 
            var testHost = new InteractiveWindowTestHost(workspace.ExportProvider.GetExportedValue<IInteractiveWindowFactoryService>());
            var executedSubmissionCalls = new List<string>();
 
            void executeSubmission(object _, string code) => executedSubmissionCalls.Add(code);
            testHost.Evaluator.OnExecute += executeSubmission;
 
            var uiThreadOperationExecutor = workspace.GetService<IUIThreadOperationExecutor>();
            var editorOptionsService = workspace.GetService<EditorOptionsService>();
            var editorOptions = editorOptionsService.Factory.GetOptions(testHost.Window.CurrentLanguageBuffer);
            var newLineCharacter = editorOptions.GetNewLineCharacter();
 
            var resetInteractive = new TestResetInteractive(
                uiThreadOperationExecutor,
                editorOptionsService,
                CreateReplReferenceCommand,
                CreateImport,
                buildSucceeds: buildSucceeds)
            {
                References = ImmutableArray.CreateRange(GetProjectReferences(workspace, project)),
                ReferenceSearchPaths = ImmutableArray.Create("rsp1", "rsp2"),
                SourceSearchPaths = ImmutableArray.Create("ssp1", "ssp2"),
                ProjectNamespaces = ImmutableArray.Create("System", "ResetInteractiveTestsDocument", "VisualBasicResetInteractiveTestsDocument"),
                NamespacesToImport = ImmutableArray.Create("System", "ResetInteractiveTestsDocument"),
                ProjectDirectory = "pj",
                Platform = InteractiveHostPlatform.Desktop64,
            };
 
            await resetInteractive.ExecuteAsync(testHost.Window, "Interactive C#");
 
            // Validate that the project was rebuilt.
            Assert.Equal(1, resetInteractive.BuildProjectCount);
            Assert.Equal(0, resetInteractive.CancelBuildProjectCount);
 
            if (buildSucceeds)
            {
                Assert.Equal(InteractiveHostPlatform.Desktop64, testHost.Evaluator.ResetOptions.Platform);
            }
            else
            {
                Assert.Null(testHost.Evaluator.ResetOptions);
            }
 
            var expectedSubmissions = new List<string>();
            if (expectedReferences.Any())
            {
                expectedSubmissions.AddRange(expectedReferences.Select(r => r + newLineCharacter));
            }
 
            if (expectedUsings.Any())
            {
                expectedSubmissions.Add(string.Join(newLineCharacter, expectedUsings) + newLineCharacter);
            }
 
            AssertEx.Equal(expectedSubmissions, executedSubmissionCalls);
 
            testHost.Evaluator.OnExecute -= executeSubmission;
        }
 
        /// <summary>
        /// Simulates getting all project references.
        /// </summary>
        /// <param name="workspace">Workspace with the solution.</param>
        /// <param name="project">A project that should be built.</param>
        /// <returns>A list of paths that should be referenced.</returns>
        private static IEnumerable<string> GetProjectReferences(TestWorkspace workspace, Project project)
        {
            var metadataReferences = project.MetadataReferences.Select(r => r.Display);
            var projectReferences = project.ProjectReferences.SelectMany(p => GetProjectReferences(
                workspace,
                workspace.CurrentSolution.GetProject(p.ProjectId)));
            var outputReference = new string[] { project.OutputFilePath };
 
            return metadataReferences.Union(projectReferences).Concat(outputReference);
        }
 
        private string CreateReplReferenceCommand(string referenceName)
        {
            return $@"#r ""{referenceName}""";
        }
 
        private string CreateImport(string importName)
        {
            return $@"using ""{importName}"";";
        }
    }
}