-
Notifications
You must be signed in to change notification settings - Fork 759
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support pipe & socket flags for language server startup #5852
Merged
Merged
Changes from all commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
6f7f575
Support pipe & socket flags for language server startup
anthony-c-martin f4fbee7
Merge remote-tracking branch 'origin/main' into ant/pipes
anthony-c-martin 447c442
Address PR comments, add tests
anthony-c-martin 96503d7
Fix socket test
anthony-c-martin File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
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
256 changes: 256 additions & 0 deletions
256
src/Bicep.LangServer.IntegrationTests/InputOutputTests.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,256 @@ | ||
// Copyright (c) Microsoft Corporation. | ||
// Licensed under the MIT License. | ||
|
||
using System; | ||
using System.Collections.Generic; | ||
using System.Diagnostics; | ||
using System.Diagnostics.CodeAnalysis; | ||
using System.IO; | ||
using System.IO.Pipes; | ||
using System.Net; | ||
using System.Net.Sockets; | ||
using System.Text.RegularExpressions; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
using Bicep.Core.Extensions; | ||
using Bicep.Core.FileSystem; | ||
using Bicep.Core.Navigation; | ||
using Bicep.Core.Parsing; | ||
using Bicep.Core.Samples; | ||
using Bicep.Core.Semantics; | ||
using Bicep.Core.Syntax; | ||
using Bicep.Core.Syntax.Visitors; | ||
using Bicep.Core.Text; | ||
using Bicep.Core.TypeSystem.Az; | ||
using Bicep.Core.UnitTests; | ||
using Bicep.Core.UnitTests.Assertions; | ||
using Bicep.Core.UnitTests.Utils; | ||
using Bicep.Core.Workspaces; | ||
using Bicep.LangServer.IntegrationTests.Assertions; | ||
using Bicep.LangServer.IntegrationTests.Extensions; | ||
using Bicep.LangServer.IntegrationTests.Helpers; | ||
using FluentAssertions; | ||
using FluentAssertions.Execution; | ||
using Microsoft.VisualStudio.TestTools.UnitTesting; | ||
using OmniSharp.Extensions.LanguageServer.Client; | ||
using OmniSharp.Extensions.LanguageServer.Protocol; | ||
using OmniSharp.Extensions.LanguageServer.Protocol.Client; | ||
using OmniSharp.Extensions.LanguageServer.Protocol.Document; | ||
using OmniSharp.Extensions.LanguageServer.Protocol.Models; | ||
using OmniSharp.Extensions.LanguageServer.Protocol.Window; | ||
using SymbolKind = Bicep.Core.Semantics.SymbolKind; | ||
|
||
namespace Bicep.LangServer.IntegrationTests | ||
{ | ||
[TestClass] | ||
public class InputOutputTests | ||
{ | ||
[NotNull] | ||
public TestContext? TestContext { get; set; } | ||
|
||
private CancellationToken GetCancellationTokenWithTimeout(TimeSpan timeSpan) | ||
=> CancellationTokenSource.CreateLinkedTokenSource( | ||
new CancellationTokenSource(timeSpan).Token, | ||
TestContext.CancellationTokenSource.Token).Token; | ||
|
||
private static Process StartServerProcessWithConsoleIO() | ||
{ | ||
var exePath = typeof(LanguageServer.Program).Assembly.Location; | ||
|
||
var process = new Process | ||
{ | ||
StartInfo = new ProcessStartInfo | ||
{ | ||
FileName = "dotnet", | ||
Arguments = exePath, | ||
UseShellExecute = false, | ||
RedirectStandardError = true, | ||
RedirectStandardOutput = true, | ||
RedirectStandardInput = true, | ||
}, | ||
}; | ||
|
||
process.Start(); | ||
|
||
return process; | ||
} | ||
|
||
private static Process StartServerProcessWithNamedPipeIo(string pipeName) | ||
{ | ||
var exePath = typeof(LanguageServer.Program).Assembly.Location; | ||
|
||
var process = new Process | ||
{ | ||
StartInfo = new ProcessStartInfo | ||
{ | ||
FileName = "dotnet", | ||
Arguments = $"{exePath} --pipe {pipeName}", | ||
UseShellExecute = false, | ||
RedirectStandardError = true, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
RedirectStandardOutput = true, | ||
RedirectStandardInput = true, | ||
}, | ||
}; | ||
|
||
process.Start(); | ||
|
||
return process; | ||
} | ||
|
||
private static Process StartServerProcessWithSocketIo(int port) | ||
{ | ||
var exePath = typeof(LanguageServer.Program).Assembly.Location; | ||
|
||
var process = new Process | ||
{ | ||
StartInfo = new ProcessStartInfo | ||
{ | ||
FileName = "dotnet", | ||
Arguments = $"{exePath} --socket {port}", | ||
UseShellExecute = false, | ||
RedirectStandardError = true, | ||
RedirectStandardOutput = true, | ||
RedirectStandardInput = true, | ||
}, | ||
}; | ||
|
||
process.Start(); | ||
|
||
return process; | ||
} | ||
|
||
private async Task<ILanguageClient> InitializeLanguageClient(Stream inputStream, Stream outputStream, MultipleMessageListener<PublishDiagnosticsParams> publishDiagnosticsListener, CancellationToken cancellationToken) | ||
{ | ||
var client = LanguageClient.PreInit(options => | ||
{ | ||
options | ||
.WithInput(inputStream) | ||
.WithOutput(outputStream) | ||
.OnInitialize((client, request, cancellationToken) => { TestContext.WriteLine("Language client initializing."); return Task.CompletedTask; }) | ||
.OnInitialized((client, request, response, cancellationToken) => { TestContext.WriteLine("Language client initialized."); return Task.CompletedTask; }) | ||
.OnStarted((client, cancellationToken) => { TestContext.WriteLine("Language client started."); return Task.CompletedTask; }) | ||
.OnLogTrace(@params => TestContext.WriteLine($"TRACE: {@params.Message} VERBOSE: {@params.Verbose}")) | ||
.OnLogMessage(@params => TestContext.WriteLine($"{@params.Type}: {@params.Message}")) | ||
.OnPublishDiagnostics(x => publishDiagnosticsListener.AddMessage(x)); | ||
}); | ||
|
||
await client.Initialize(cancellationToken); | ||
|
||
return client; | ||
} | ||
|
||
[TestMethod] | ||
public async Task ServerProcess_e2e_test_with_console_io() | ||
{ | ||
var cancellationToken = GetCancellationTokenWithTimeout(TimeSpan.FromSeconds(60)); | ||
var publishDiagsListener = new MultipleMessageListener<PublishDiagnosticsParams>(); | ||
var documentUri = DocumentUri.From("/template.bicep"); | ||
var bicepFile = @" | ||
#disable-next-line no-unused-params | ||
param foo string = 123 // trigger a type error | ||
"; | ||
|
||
using var process = StartServerProcessWithConsoleIO(); | ||
try | ||
{ | ||
var input = process.StandardOutput.BaseStream; | ||
var output = process.StandardInput.BaseStream; | ||
|
||
using var client = await InitializeLanguageClient(input, output, publishDiagsListener, cancellationToken); | ||
|
||
client.DidOpenTextDocument(TextDocumentParamHelper.CreateDidOpenDocumentParams(documentUri, bicepFile, 0)); | ||
var publishDiagsResult = await publishDiagsListener.WaitNext(); | ||
|
||
publishDiagsResult.Diagnostics.Should().SatisfyRespectively( | ||
d => | ||
{ | ||
d.Range.Should().HaveRange((2, 19), (2, 22)); | ||
d.Should().HaveCodeAndSeverity("BCP027", DiagnosticSeverity.Error); | ||
}); | ||
} | ||
finally | ||
{ | ||
process.Kill(entireProcessTree: true); | ||
process.Dispose(); | ||
} | ||
} | ||
|
||
[TestMethod] | ||
public async Task ServerProcess_e2e_test_with_named_pipes_io() | ||
{ | ||
var cancellationToken = GetCancellationTokenWithTimeout(TimeSpan.FromSeconds(60)); | ||
var publishDiagsListener = new MultipleMessageListener<PublishDiagnosticsParams>(); | ||
var documentUri = DocumentUri.From("/template.bicep"); | ||
var bicepFile = @" | ||
#disable-next-line no-unused-params | ||
param foo string = 123 // trigger a type error | ||
"; | ||
|
||
var pipeName = Guid.NewGuid().ToString(); | ||
using var pipeStream = new NamedPipeServerStream(pipeName, PipeDirection.InOut, NamedPipeServerStream.MaxAllowedServerInstances, PipeTransmissionMode.Byte, PipeOptions.Asynchronous); | ||
using var process = StartServerProcessWithNamedPipeIo(pipeName); | ||
try | ||
{ | ||
await pipeStream.WaitForConnectionAsync(cancellationToken); | ||
|
||
using var client = await InitializeLanguageClient(pipeStream, pipeStream, publishDiagsListener, cancellationToken); | ||
|
||
client.DidOpenTextDocument(TextDocumentParamHelper.CreateDidOpenDocumentParams(documentUri, bicepFile, 0)); | ||
var publishDiagsResult = await publishDiagsListener.WaitNext(); | ||
|
||
publishDiagsResult.Diagnostics.Should().SatisfyRespectively( | ||
d => | ||
{ | ||
d.Range.Should().HaveRange((2, 19), (2, 22)); | ||
d.Should().HaveCodeAndSeverity("BCP027", DiagnosticSeverity.Error); | ||
}); | ||
} | ||
finally | ||
{ | ||
process.Kill(entireProcessTree: true); | ||
process.Dispose(); | ||
} | ||
} | ||
|
||
[TestMethod] | ||
public async Task ServerProcess_e2e_test_with_socket_io() | ||
{ | ||
var cancellationToken = GetCancellationTokenWithTimeout(TimeSpan.FromSeconds(60)); | ||
var publishDiagsListener = new MultipleMessageListener<PublishDiagnosticsParams>(); | ||
var documentUri = DocumentUri.From("/template.bicep"); | ||
var bicepFile = @" | ||
#disable-next-line no-unused-params | ||
param foo string = 123 // trigger a type error | ||
"; | ||
|
||
var tcpListener = new TcpListener(IPAddress.Loopback, 0); | ||
tcpListener.Start(); | ||
var tcpPort = (tcpListener.LocalEndpoint as IPEndPoint)!.Port; | ||
|
||
using var process = StartServerProcessWithSocketIo(tcpPort); | ||
|
||
using var tcpClient = await tcpListener.AcceptTcpClientAsync(cancellationToken); | ||
var tcpStream = tcpClient.GetStream(); | ||
|
||
try | ||
{ | ||
using var client = await InitializeLanguageClient(tcpStream, tcpStream, publishDiagsListener, cancellationToken); | ||
|
||
client.DidOpenTextDocument(TextDocumentParamHelper.CreateDidOpenDocumentParams(documentUri, bicepFile, 0)); | ||
var publishDiagsResult = await publishDiagsListener.WaitNext(); | ||
|
||
publishDiagsResult.Diagnostics.Should().SatisfyRespectively( | ||
d => | ||
{ | ||
d.Range.Should().HaveRange((2, 19), (2, 22)); | ||
d.Should().HaveCodeAndSeverity("BCP027", DiagnosticSeverity.Error); | ||
}); | ||
} | ||
finally | ||
{ | ||
process.Kill(entireProcessTree: true); | ||
process.Dispose(); | ||
} | ||
} | ||
} | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All 3 versions of this differ only in the arguments. Worth refactoring?