-
Notifications
You must be signed in to change notification settings - Fork 190
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add tests for the cohost linked editing range endpoint (#10596)
Part of #9519 Finally some cohosting tests! Some people would say that I'm being lazy in adding tests for the simplest endpoint we have. Those people have a point. The test infra here almost entirely avoids ServiceHub, and certainly avoids the Roslyn solution sync mechanism, but it _does_ use our real services and service factories, including the OOP services' separate MEF composition, so the services themselves are partying on a real Roslyn (test) solution and are using real implementations of all of their dependencies. Things not covered by this test infra yet: * OOP initialization * Adding generated C# files to the Roslyn solution (IDynamicFile does this in real life) * Dealing with Html documents in any way Future PRs to add tests for more endpoints should add these as needed.
- Loading branch information
Showing
12 changed files
with
445 additions
and
6 deletions.
There are no files selected for viewing
20 changes: 20 additions & 0 deletions
20
src/Razor/src/Microsoft.CodeAnalysis.Remote.Razor/IBrokeredServiceInterceptor.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
// Copyright (c) .NET Foundation. All rights reserved. | ||
// Licensed under the MIT license. See License.txt in the project root for license information. | ||
|
||
using System; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
using Microsoft.CodeAnalysis.ExternalAccess.Razor; | ||
using Microsoft.CodeAnalysis.ExternalAccess.Razor.Api; | ||
|
||
namespace Microsoft.CodeAnalysis.Remote.Razor; | ||
|
||
/// <summary> | ||
/// An abstraction to avoid calling the static <see cref="RazorBrokeredServiceImplementation"/> helper defined in Roslyn | ||
/// </summary> | ||
internal interface IBrokeredServiceInterceptor | ||
{ | ||
ValueTask RunServiceAsync(Func<CancellationToken, ValueTask> implementation, CancellationToken cancellationToken); | ||
|
||
ValueTask<T> RunServiceAsync<T>(RazorPinnedSolutionInfoWrapper solutionInfo, Func<Solution, ValueTask<T>> implementation, CancellationToken cancellationToken); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
176 changes: 176 additions & 0 deletions
176
...Microsoft.VisualStudio.LanguageServices.Razor.Test/Cohost/CohostLinkedEditingRangeTest.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,176 @@ | ||
// Copyright (c) .NET Foundation. All rights reserved. | ||
// Licensed under the MIT license. See License.txt in the project root for license information. | ||
|
||
using System.Collections.Immutable; | ||
using System.Threading.Tasks; | ||
using Microsoft.CodeAnalysis.ExternalAccess.Razor; | ||
using Microsoft.CodeAnalysis.Razor.LinkedEditingRange; | ||
using Microsoft.CodeAnalysis.Razor.Workspaces; | ||
using Microsoft.CodeAnalysis.Testing; | ||
using Microsoft.CodeAnalysis.Text; | ||
using Microsoft.VisualStudio.LanguageServer.Protocol; | ||
using Microsoft.VisualStudio.Razor.LanguageClient.Cohost; | ||
using Xunit; | ||
using Xunit.Abstractions; | ||
|
||
namespace Microsoft.VisualStudio.LanguageServices.Razor.Test.Cohost; | ||
|
||
public class CohostLinkedEditingRangeTest(ITestOutputHelper testOutputHelper) : CohostTestBase(testOutputHelper) | ||
{ | ||
[Theory] | ||
[InlineData("$$div")] | ||
[InlineData("di$$v")] | ||
[InlineData("div$$")] | ||
public async Task Html_StartTag(string startTagAndCursorLocation) | ||
{ | ||
var input = $""" | ||
This is a Razor document. | ||
|
||
<[|{startTagAndCursorLocation}|]> | ||
Here is some content. | ||
</[|div|]> | ||
|
||
The end. | ||
"""; | ||
|
||
await VerifyLinkedEditingRangeAsync(input); | ||
} | ||
|
||
[Theory] | ||
[InlineData("$$div")] | ||
[InlineData("di$$v")] | ||
[InlineData("div$$")] | ||
public async Task Html_EndTag(string endTagAndCursorLocation) | ||
{ | ||
var input = $""" | ||
This is a Razor document. | ||
|
||
<[|div|]> | ||
Here is some content. | ||
</[|{endTagAndCursorLocation}|]> | ||
|
||
The end. | ||
"""; | ||
|
||
await VerifyLinkedEditingRangeAsync(input); | ||
} | ||
|
||
[Fact] | ||
public async Task Html_EndTag_BeforeSlash() | ||
{ | ||
var input = $""" | ||
This is a Razor document. | ||
|
||
<div> | ||
Here is some content. | ||
<$$/div> | ||
|
||
The end. | ||
"""; | ||
|
||
await VerifyLinkedEditingRangeAsync(input); | ||
} | ||
|
||
[Fact] | ||
public async Task Html_NotATag() | ||
{ | ||
var input = $""" | ||
This is a $$Razor document. | ||
|
||
<div> | ||
Here is some content. | ||
</div> | ||
|
||
The end. | ||
"""; | ||
|
||
await VerifyLinkedEditingRangeAsync(input); | ||
} | ||
|
||
[Fact] | ||
public async Task Html_NestedTags_Outer() | ||
{ | ||
var input = $""" | ||
This is a Razor document. | ||
|
||
<[|d$$iv|]> | ||
<div> | ||
Here is some content. | ||
</div> | ||
</[|div|]> | ||
|
||
The end. | ||
"""; | ||
|
||
await VerifyLinkedEditingRangeAsync(input); | ||
} | ||
|
||
[Fact] | ||
public async Task Html_NestedTags_Inner() | ||
{ | ||
var input = $""" | ||
This is a Razor document. | ||
|
||
<div> | ||
<[|d$$iv|]> | ||
Here is some content. | ||
</[|div|]> | ||
</div> | ||
|
||
The end. | ||
"""; | ||
|
||
await VerifyLinkedEditingRangeAsync(input); | ||
} | ||
|
||
[Fact] | ||
public async Task Html_SelfClosingTag() | ||
{ | ||
var input = $""" | ||
This is a Razor document. | ||
|
||
<b$$r /> | ||
Here is some content. | ||
|
||
The end. | ||
"""; | ||
|
||
await VerifyLinkedEditingRangeAsync(input); | ||
} | ||
|
||
private async Task VerifyLinkedEditingRangeAsync(string input) | ||
{ | ||
TestFileMarkupParser.GetPositionAndSpans(input, out input, out int cursorPosition, out ImmutableArray<TextSpan> spans); | ||
var document = CreateRazorDocument(input); | ||
var sourceText = await document.GetTextAsync(DisposalToken); | ||
sourceText.GetLineAndOffset(cursorPosition, out var lineIndex, out var characterIndex); | ||
|
||
var endpoint = new CohostLinkedEditingRangeEndpoint(RemoteServiceProvider, LoggerFactory); | ||
|
||
var request = new LinkedEditingRangeParams() | ||
{ | ||
TextDocument = new TextDocumentIdentifier() | ||
{ | ||
Uri = document.CreateUri() | ||
}, | ||
Position = new Position() | ||
{ | ||
Line = lineIndex, | ||
Character = characterIndex | ||
} | ||
}; | ||
|
||
var result = await endpoint.GetTestAccessor().HandleRequestAsync(request, document, DisposalToken); | ||
|
||
if (spans.Length == 0) | ||
{ | ||
Assert.Null(result); | ||
return; | ||
} | ||
|
||
Assert.NotNull(result); | ||
Assert.Equal(LinkedEditingRangeHelper.WordPattern, result.WordPattern); | ||
Assert.Equal(spans[0], result.Ranges[0].ToTextSpan(sourceText)); | ||
Assert.Equal(spans[1], result.Ranges[1].ToTextSpan(sourceText)); | ||
} | ||
} |
53 changes: 53 additions & 0 deletions
53
src/Razor/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/Cohost/CohostTestBase.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
// Copyright (c) .NET Foundation. All rights reserved. | ||
// Licensed under the MIT license. See License.txt in the project root for license information. | ||
|
||
using System.IO; | ||
using System.Threading.Tasks; | ||
using Microsoft.AspNetCore.Razor; | ||
using Microsoft.AspNetCore.Razor.Test.Common; | ||
using Microsoft.AspNetCore.Razor.Test.Common.Workspaces; | ||
using Microsoft.CodeAnalysis; | ||
using Microsoft.CodeAnalysis.Razor.Remote; | ||
using Microsoft.CodeAnalysis.Text; | ||
using Xunit.Abstractions; | ||
|
||
namespace Microsoft.VisualStudio.LanguageServices.Razor.Test.Cohost; | ||
|
||
public abstract class CohostTestBase(ITestOutputHelper testOutputHelper) : WorkspaceTestBase(testOutputHelper) | ||
{ | ||
private IRemoteServiceProvider? _remoteServiceProvider; | ||
|
||
private protected IRemoteServiceProvider RemoteServiceProvider => _remoteServiceProvider.AssumeNotNull(); | ||
|
||
protected override Task InitializeAsync() | ||
{ | ||
_remoteServiceProvider = new ShortCircuitingRemoteServiceProvider(TestOutputHelper); | ||
|
||
return base.InitializeAsync(); | ||
} | ||
|
||
protected TextDocument CreateRazorDocument(string contents) | ||
{ | ||
var projectFilePath = TestProjectData.SomeProject.FilePath; | ||
var documentFilePath = TestProjectData.SomeProjectComponentFile1.FilePath; | ||
var projectName = Path.GetFileNameWithoutExtension(projectFilePath); | ||
var projectId = ProjectId.CreateNewId(debugName: projectName); | ||
var documentId = DocumentId.CreateNewId(projectId, debugName: documentFilePath); | ||
|
||
var solution = Workspace.CurrentSolution.AddProject(ProjectInfo.Create( | ||
projectId, | ||
VersionStamp.Create(), | ||
name: projectName, | ||
assemblyName: projectName, | ||
LanguageNames.CSharp, | ||
documentFilePath)); | ||
|
||
solution = solution.AddAdditionalDocument( | ||
documentId, | ||
documentFilePath, | ||
SourceText.From(contents), | ||
filePath: documentFilePath); | ||
|
||
return solution.GetAdditionalDocument(documentId).AssumeNotNull(); | ||
} | ||
} |
39 changes: 39 additions & 0 deletions
39
...st/Microsoft.VisualStudio.LanguageServices.Razor.Test/Cohost/InterceptingServiceBroker.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
// Copyright (c) .NET Foundation. All rights reserved. | ||
// Licensed under the MIT license. See License.txt in the project root for license information. | ||
|
||
using System; | ||
using System.IO.Pipelines; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
using Microsoft.CodeAnalysis; | ||
using Microsoft.CodeAnalysis.ExternalAccess.Razor; | ||
using Microsoft.CodeAnalysis.Remote.Razor; | ||
using Microsoft.ServiceHub.Framework; | ||
|
||
namespace Microsoft.VisualStudio.LanguageServices.Razor.Test.Cohost; | ||
|
||
internal class InterceptingServiceBroker(Solution solution) : IServiceBroker, IBrokeredServiceInterceptor | ||
{ | ||
public event EventHandler<BrokeredServicesChangedEventArgs>? AvailabilityChanged { add { } remove { } } | ||
|
||
public ValueTask<IDuplexPipe?> GetPipeAsync(ServiceMoniker serviceMoniker, ServiceActivationOptions options = default, CancellationToken cancellationToken = default) | ||
{ | ||
throw new NotImplementedException(); | ||
} | ||
|
||
public ValueTask<T?> GetProxyAsync<T>(ServiceRpcDescriptor serviceDescriptor, ServiceActivationOptions options = default, CancellationToken cancellationToken = default) | ||
where T : class | ||
{ | ||
throw new NotImplementedException(); | ||
} | ||
|
||
public ValueTask RunServiceAsync(Func<CancellationToken, ValueTask> implementation, CancellationToken cancellationToken) | ||
{ | ||
return implementation(cancellationToken); | ||
} | ||
|
||
public ValueTask<T> RunServiceAsync<T>(RazorPinnedSolutionInfoWrapper solutionInfo, Func<Solution, ValueTask<T>> implementation, CancellationToken cancellationToken) | ||
{ | ||
return implementation(solution); | ||
} | ||
} |
Oops, something went wrong.