|
// 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.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Test.Utilities;
using Roslyn.LanguageServer.Protocol;
using Roslyn.Test.Utilities;
using Xunit;
using Xunit.Abstractions;
using LSP = Roslyn.LanguageServer.Protocol;
namespace Microsoft.CodeAnalysis.LanguageServer.UnitTests.ProjectContext;
public sealed class GetTextDocumentWithContextHandlerTests : AbstractLanguageServerProtocolTests
{
public GetTextDocumentWithContextHandlerTests(ITestOutputHelper testOutputHelper) : base(testOutputHelper)
{
}
[Theory, CombinatorialData]
public async Task SingleDocumentReturnsSingleContext(bool mutatingLspWorkspace)
{
var workspaceXml =
"""
<Workspace>
<Project Language="C#" CommonReferences="true" AssemblyName="CSProj">
<Document FilePath = "C:\C.cs">{|caret:|}</Document>
</Project>
</Workspace>
""";
await using var testLspServer = await CreateXmlTestLspServerAsync(workspaceXml, mutatingLspWorkspace);
var documentUri = testLspServer.GetLocations("caret").Single().DocumentUri;
var result = await RunGetProjectContext(testLspServer, documentUri);
Assert.NotNull(result);
Assert.Equal(0, result!.DefaultIndex);
var context = Assert.Single(result.ProjectContexts);
Assert.Equal(ProtocolConversions.ProjectIdToProjectContextId(testLspServer.GetCurrentSolution().ProjectIds.Single()), context.Id);
Assert.Equal(LSP.VSProjectKind.CSharp, context.Kind);
Assert.Equal("CSProj", context.Label);
}
[Theory, CombinatorialData]
public async Task MultipleDocumentsReturnsMultipleContexts(bool mutatingLspWorkspace)
{
var workspaceXml =
"""
<Workspace>
<Project Language="C#" CommonReferences="true" AssemblyName="CSProj1">
<Document FilePath="C:\C.cs">{|caret:|}</Document>
</Project>
<Project Language="C#" CommonReferences="true" AssemblyName="CSProj2">
<Document IsLinkFile="true" LinkFilePath="C:\C.cs" LinkAssemblyName="CSProj1">{|caret:|}</Document>
</Project>
</Workspace>
""";
await using var testLspServer = await CreateXmlTestLspServerAsync(workspaceXml, mutatingLspWorkspace);
var documentUri = testLspServer.GetLocations("caret").Single().DocumentUri;
var result = await RunGetProjectContext(testLspServer, documentUri);
Assert.NotNull(result);
Assert.Collection(result!.ProjectContexts.OrderBy(c => c.Label),
c => Assert.Equal("CSProj1", c.Label),
c => Assert.Equal("CSProj2", c.Label));
}
[Theory, CombinatorialData]
public async Task SwitchingContextsChangesDefaultContext(bool mutatingLspWorkspace)
{
var workspaceXml =
"""
<Workspace>
<Project Language="C#" CommonReferences="true" AssemblyName="CSProj1">
<Document FilePath="C:\C.cs">{|caret:|}</Document>
</Project>
<Project Language="C#" CommonReferences="true" AssemblyName="CSProj2">
<Document IsLinkFile="true" LinkFilePath="C:\C.cs" LinkAssemblyName="CSProj1"></Document>
</Project>
</Workspace>
""";
await using var testLspServer = await CreateXmlTestLspServerAsync(workspaceXml, mutatingLspWorkspace);
// Ensure all the linked documents are open so we can change contexts
var document = testLspServer.TestWorkspace.Documents.First();
await testLspServer.OpenDocumentInWorkspaceAsync(document.Id, openAllLinkedDocuments: true);
var documentUri = testLspServer.GetLocations("caret").Single().DocumentUri;
foreach (var project in testLspServer.GetCurrentSolution().Projects)
{
testLspServer.TestWorkspace.SetDocumentContext(project.DocumentIds.Single());
var result = await RunGetProjectContext(testLspServer, documentUri);
Assert.Equal(ProtocolConversions.ProjectIdToProjectContextId(project.Id), result!.ProjectContexts[result.DefaultIndex].Id);
Assert.Equal(project.Name, result!.ProjectContexts[result.DefaultIndex].Label);
}
}
[Theory, CombinatorialData]
public async Task ProjectContextListKeysShouldMatchForTheSameSetOfProjects(bool mutatingLspWorkspace)
{
var workspaceXml =
"""
<Workspace>
<Project Language="C#" CommonReferences="true" AssemblyName="CSProj1">
<Document FilePath="C:\B.cs"></Document>
<Document FilePath="C:\C.cs"></Document>
</Project>
<Project Language="C#" CommonReferences="true" AssemblyName="CSProj2">
<Document IsLinkFile="true" LinkFilePath="C:\B.cs" LinkAssemblyName="CSProj1"></Document>
<Document IsLinkFile="true" LinkFilePath="C:\C.cs" LinkAssemblyName="CSProj1"></Document>
</Project>
</Workspace>
""";
await using var testLspServer = await CreateXmlTestLspServerAsync(workspaceXml, mutatingLspWorkspace);
// Ensure all the documents are open
var project1 = testLspServer.TestWorkspace.Projects.First(p => p.Name == "CSProj1");
foreach (var document in project1.Documents)
{
await testLspServer.OpenDocumentInWorkspaceAsync(document.Id, openAllLinkedDocuments: true);
}
var results = await Task.WhenAll(project1.Documents.Select(document => RunGetProjectContext(testLspServer, document)));
var keys = results.Select(r => r?.Key).ToArray();
Assert.Equal(keys.Length, keys.OfType<string>().Count());
Assert.Single(keys.Distinct());
}
[Theory, CombinatorialData]
public async Task ProjectContextListKeysShouldDifferForDifferentSetOfProjects(bool mutatingLspWorkspace)
{
var workspaceXml =
"""
<Workspace>
<Project Language="C#" CommonReferences="true" AssemblyName="CSProj1">
<Document FilePath="C:\B.cs"></Document>
</Project>
<Project Language="C#" CommonReferences="true" AssemblyName="CSProj2">
<Document FilePath="C:\C.cs"></Document>
</Project>
</Workspace>
""";
await using var testLspServer = await CreateXmlTestLspServerAsync(workspaceXml, mutatingLspWorkspace);
// Ensure all the documents are open
foreach (var project in testLspServer.TestWorkspace.Projects)
{
var document = project.Documents.First();
await testLspServer.OpenDocumentInWorkspaceAsync(document.Id, openAllLinkedDocuments: true);
}
var results = await Task.WhenAll(testLspServer.TestWorkspace.Projects.Select(project => RunGetProjectContext(testLspServer, project.Documents.First())));
var keys = results.Select(r => r?.Key).ToArray();
Assert.Equal(keys.Length, keys.OfType<string>().Count());
Assert.Distinct(keys);
}
internal static async Task<LSP.VSProjectContextList?> RunGetProjectContext(TestLspServer testLspServer, TestHostDocument document)
{
var documentUri = ProtocolConversions.CreateAbsoluteDocumentUri(document.FilePath!);
return await testLspServer.ExecuteRequestAsync<LSP.VSGetProjectContextsParams, LSP.VSProjectContextList?>(LSP.VSMethods.GetProjectContextsName,
CreateGetProjectContextParams(documentUri), cancellationToken: CancellationToken.None);
}
internal static async Task<LSP.VSProjectContextList?> RunGetProjectContext(TestLspServer testLspServer, DocumentUri uri)
{
return await testLspServer.ExecuteRequestAsync<LSP.VSGetProjectContextsParams, LSP.VSProjectContextList?>(LSP.VSMethods.GetProjectContextsName,
CreateGetProjectContextParams(uri), cancellationToken: CancellationToken.None);
}
private static LSP.VSGetProjectContextsParams CreateGetProjectContextParams(DocumentUri uri)
=> new()
{
TextDocument = new LSP.TextDocumentItem { DocumentUri = uri }
};
}
|