Skip to content

Commit ced08a2

Browse files
Add foundation for debug tests - stdio and fixures
1 parent fc25e18 commit ced08a2

File tree

7 files changed

+174
-88
lines changed

7 files changed

+174
-88
lines changed

src/PowerShellEditorServices.Engine/Hosting/EditorServicesHost.cs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,10 @@
1616
using System.Security.Principal;
1717
using System.Threading;
1818
using System.Threading.Tasks;
19+
using Microsoft.Extensions.DependencyInjection;
1920
using Microsoft.Extensions.Logging;
2021
using Microsoft.PowerShell.EditorServices.Engine.Server;
22+
using Microsoft.PowerShell.EditorServices.Engine.Services;
2123
using Microsoft.PowerShell.EditorServices.Utility;
2224
using Serilog;
2325

@@ -328,6 +330,36 @@ public void StartDebugService(
328330

329331
break;
330332

333+
case EditorServiceTransportType.Stdio:
334+
_debugServer = new PsesDebugServer(
335+
_factory,
336+
Console.OpenStandardInput(),
337+
Console.OpenStandardOutput());
338+
339+
Task.Run(async () =>
340+
{
341+
_logger.LogInformation("Starting debug server");
342+
343+
IServiceProvider serviceProvider = useExistingSession
344+
? _languageServer.LanguageServer.Services
345+
: new ServiceCollection().AddSingleton<PowerShellContextService>(
346+
(provider) => PowerShellContextService.Create(
347+
_factory,
348+
provider.GetService<OmniSharp.Extensions.LanguageServer.Protocol.Server.ILanguageServer>(),
349+
profilePaths,
350+
_featureFlags,
351+
_enableConsoleRepl,
352+
_internalHost,
353+
_hostDetails,
354+
_additionalModules))
355+
.BuildServiceProvider();
356+
357+
await _debugServer.StartAsync(serviceProvider);
358+
_logger.LogInformation(
359+
$"Debug service started, type = {config.TransportType}, endpoint = {config.Endpoint}");
360+
});
361+
break;
362+
331363
default:
332364
throw new NotSupportedException("not supported");
333365
}

src/PowerShellEditorServices.Engine/Server/PsesLanguageServer.cs

Lines changed: 8 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -68,9 +68,15 @@ public async Task StartAsync()
6868
.AddSingleton<ConfigurationService>()
6969
.AddSingleton<PowerShellContextService>(
7070
(provider) =>
71-
GetFullyInitializedPowerShellContext(
71+
PowerShellContextService.Create(
72+
LoggerFactory,
7273
provider.GetService<OmniSharp.Extensions.LanguageServer.Protocol.Server.ILanguageServer>(),
73-
_profilePaths))
74+
_profilePaths,
75+
_featureFlags,
76+
_enableConsoleRepl,
77+
_internalHost,
78+
_hostDetails,
79+
_additionalModules))
7480
.AddSingleton<TemplateService>()
7581
.AddSingleton<EditorOperationsService>()
7682
.AddSingleton<ExtensionService>(
@@ -158,57 +164,6 @@ public async Task WaitForShutdown()
158164
await LanguageServer.WaitForExit;
159165
}
160166

161-
private PowerShellContextService GetFullyInitializedPowerShellContext(
162-
OmniSharp.Extensions.LanguageServer.Protocol.Server.ILanguageServer languageServer,
163-
ProfilePaths profilePaths)
164-
{
165-
var logger = LoggerFactory.CreateLogger<PowerShellContextService>();
166-
167-
// PSReadLine can only be used when -EnableConsoleRepl is specified otherwise
168-
// issues arise when redirecting stdio.
169-
var powerShellContext = new PowerShellContextService(
170-
logger,
171-
languageServer,
172-
_featureFlags.Contains("PSReadLine") && _enableConsoleRepl);
173-
174-
EditorServicesPSHostUserInterface hostUserInterface =
175-
_enableConsoleRepl
176-
? (EditorServicesPSHostUserInterface)new TerminalPSHostUserInterface(powerShellContext, logger, _internalHost)
177-
: new ProtocolPSHostUserInterface(languageServer, powerShellContext, logger);
178-
179-
EditorServicesPSHost psHost =
180-
new EditorServicesPSHost(
181-
powerShellContext,
182-
_hostDetails,
183-
hostUserInterface,
184-
logger);
185-
186-
Runspace initialRunspace = PowerShellContextService.CreateRunspace(psHost);
187-
powerShellContext.Initialize(profilePaths, initialRunspace, true, hostUserInterface);
188-
189-
powerShellContext.ImportCommandsModuleAsync(
190-
Path.Combine(
191-
Path.GetDirectoryName(this.GetType().GetTypeInfo().Assembly.Location),
192-
@"..\Commands"));
193-
194-
// TODO: This can be moved to the point after the $psEditor object
195-
// gets initialized when that is done earlier than LanguageServer.Initialize
196-
foreach (string module in this._additionalModules)
197-
{
198-
var command =
199-
new PSCommand()
200-
.AddCommand("Microsoft.PowerShell.Core\\Import-Module")
201-
.AddParameter("Name", module);
202-
203-
powerShellContext.ExecuteCommandAsync<PSObject>(
204-
command,
205-
sendOutputToHost: false,
206-
sendErrorToHost: true);
207-
}
208-
209-
return powerShellContext;
210-
}
211-
212167
protected abstract (Stream input, Stream output) GetInputOutputStreams();
213168
}
214169
}

src/PowerShellEditorServices.Engine/Services/PowerShellContext/PowerShellContextService.cs

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,64 @@ public PowerShellContextService(
161161
ExecutionStatusChanged += PowerShellContext_ExecutionStatusChangedAsync;
162162
}
163163

164+
public static PowerShellContextService Create(
165+
ILoggerFactory factory,
166+
OmniSharp.Extensions.LanguageServer.Protocol.Server.ILanguageServer languageServer,
167+
ProfilePaths profilePaths,
168+
HashSet<string> featureFlags,
169+
bool enableConsoleRepl,
170+
PSHost internalHost,
171+
HostDetails hostDetails,
172+
string[] additionalModules
173+
)
174+
{
175+
var logger = factory.CreateLogger<PowerShellContextService>();
176+
177+
// PSReadLine can only be used when -EnableConsoleRepl is specified otherwise
178+
// issues arise when redirecting stdio.
179+
var powerShellContext = new PowerShellContextService(
180+
logger,
181+
languageServer,
182+
featureFlags.Contains("PSReadLine") && enableConsoleRepl);
183+
184+
EditorServicesPSHostUserInterface hostUserInterface =
185+
enableConsoleRepl
186+
? (EditorServicesPSHostUserInterface)new TerminalPSHostUserInterface(powerShellContext, logger, internalHost)
187+
: new ProtocolPSHostUserInterface(languageServer, powerShellContext, logger);
188+
189+
EditorServicesPSHost psHost =
190+
new EditorServicesPSHost(
191+
powerShellContext,
192+
hostDetails,
193+
hostUserInterface,
194+
logger);
195+
196+
Runspace initialRunspace = PowerShellContextService.CreateRunspace(psHost);
197+
powerShellContext.Initialize(profilePaths, initialRunspace, true, hostUserInterface);
198+
199+
powerShellContext.ImportCommandsModuleAsync(
200+
Path.Combine(
201+
Path.GetDirectoryName(typeof(PowerShellContextService).GetTypeInfo().Assembly.Location),
202+
@"..\Commands"));
203+
204+
// TODO: This can be moved to the point after the $psEditor object
205+
// gets initialized when that is done earlier than LanguageServer.Initialize
206+
foreach (string module in additionalModules)
207+
{
208+
var command =
209+
new PSCommand()
210+
.AddCommand("Microsoft.PowerShell.Core\\Import-Module")
211+
.AddParameter("Name", module);
212+
213+
powerShellContext.ExecuteCommandAsync<PSObject>(
214+
command,
215+
sendOutputToHost: false,
216+
sendErrorToHost: true);
217+
}
218+
219+
return powerShellContext;
220+
}
221+
164222
/// <summary>
165223
///
166224
/// </summary>
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
using System.Collections.Generic;
2+
using System.IO;
3+
using System.Threading.Tasks;
4+
using Microsoft.Extensions.Logging;
5+
using Newtonsoft.Json.Linq;
6+
using OmniSharp.Extensions.LanguageServer.Client;
7+
using OmniSharp.Extensions.LanguageServer.Client.Processes;
8+
using OmniSharp.Extensions.LanguageServer.Protocol.Models;
9+
10+
namespace PowerShellEditorServices.Test.E2E
11+
{
12+
public class LSPTestsFixture : TestsFixture
13+
{
14+
public override bool IsDebugAdapterTests => false;
15+
16+
public LanguageClient LanguageClient { get; private set; }
17+
public List<Diagnostic> Diagnostics { get; set; }
18+
19+
public async override Task CustomInitializeAsync(
20+
ILoggerFactory factory,
21+
StdioServerProcess process)
22+
{
23+
LanguageClient = new LanguageClient(factory, process);
24+
25+
DirectoryInfo testdir =
26+
Directory.CreateDirectory(Path.Combine(s_binDir, Path.GetRandomFileName()));
27+
28+
await LanguageClient.Initialize(testdir.FullName);
29+
30+
// Make sure Script Analysis is enabled because we'll need it in the tests.
31+
LanguageClient.Workspace.DidChangeConfiguration(JObject.Parse(@"
32+
{
33+
""PowerShell"": {
34+
""ScriptAnalysis"": {
35+
""Enable"": true
36+
}
37+
}
38+
}
39+
"));
40+
41+
Diagnostics = new List<Diagnostic>();
42+
LanguageClient.TextDocument.OnPublishDiagnostics((uri, diagnostics) =>
43+
{
44+
Diagnostics.AddRange(diagnostics);
45+
});
46+
}
47+
48+
public override async Task DisposeAsync()
49+
{
50+
await LanguageClient.Shutdown();
51+
await _psesProcess.Stop();
52+
LanguageClient?.Dispose();
53+
}
54+
}
55+
}

test/PowerShellEditorServices.Test.E2E/LanguageServerProtocolMessageTests.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121

2222
namespace PowerShellEditorServices.Test.E2E
2323
{
24-
public class LanguageServerProtocolMessageTests : IClassFixture<TestsFixture>, IDisposable
24+
public class LanguageServerProtocolMessageTests : IClassFixture<LSPTestsFixture>, IDisposable
2525
{
2626
private readonly static string s_binDir =
2727
Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
@@ -33,7 +33,7 @@ public class LanguageServerProtocolMessageTests : IClassFixture<TestsFixture>, I
3333
private readonly string PwshExe;
3434
private readonly ITestOutputHelper _output;
3535

36-
public LanguageServerProtocolMessageTests(ITestOutputHelper output, TestsFixture data)
36+
public LanguageServerProtocolMessageTests(ITestOutputHelper output, LSPTestsFixture data)
3737
{
3838
Diagnostics = new List<Diagnostic>();
3939
LanguageClient = data.LanguageClient;

test/PowerShellEditorServices.Test.E2E/PowerShellEditorServices.Test.E2E.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
<PackageReference Include="Microsoft.Extensions.Logging" Version="3.0.0-preview8.19405.4" />
1111
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.0.1" />
1212
<PackageReference Include="Newtonsoft.Json" Version="12.0.2" />
13-
<PackageReference Include="OmniSharp.Extensions.LanguageClient" Version="0.13.2-*" />
13+
<PackageReference Include="OmniSharp.Extensions.LanguageClient" Version="1.0.0" />
1414
<PackageReference Include="xunit" Version="2.4.0" />
1515
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.0" />
1616
</ItemGroup>

test/PowerShellEditorServices.Test.E2E/TestsFixture.cs

Lines changed: 18 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@
1313

1414
namespace PowerShellEditorServices.Test.E2E
1515
{
16-
public class TestsFixture : IAsyncLifetime
16+
public abstract class TestsFixture : IAsyncLifetime
1717
{
18-
private readonly static string s_binDir =
18+
protected readonly static string s_binDir =
1919
Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
2020

2121
private readonly static string s_bundledModulePath = new FileInfo(Path.Combine(
@@ -38,11 +38,11 @@ public class TestsFixture : IAsyncLifetime
3838
const string s_hostVersion = "1.0.0";
3939
readonly static string[] s_additionalModules = { "PowerShellEditorServices.VSCode" };
4040

41-
private StdioServerProcess _psesProcess;
41+
protected StdioServerProcess _psesProcess;
4242

4343
public static string PwshExe { get; } = Environment.GetEnvironmentVariable("PWSH_EXE_NAME") ?? "pwsh";
44-
public LanguageClient LanguageClient { get; private set; }
45-
public List<Diagnostic> Diagnostics { get; set; }
44+
45+
public virtual bool IsDebugAdapterTests { get; set; }
4646

4747
public async Task InitializeAsync()
4848
{
@@ -56,7 +56,8 @@ public async Task InitializeAsync()
5656
processStartInfo.ArgumentList.Add("-NoProfile");
5757
processStartInfo.ArgumentList.Add("-EncodedCommand");
5858

59-
string[] args = {
59+
List<string> args = new List<string>
60+
{
6061
Path.Combine(s_bundledModulePath, "PowerShellEditorServices", "Start-EditorServices.ps1"),
6162
"-LogPath", s_logPath,
6263
"-LogLevel", s_logLevel,
@@ -70,6 +71,11 @@ public async Task InitializeAsync()
7071
"-Stdio"
7172
};
7273

74+
if (IsDebugAdapterTests)
75+
{
76+
args.Add("-DebugServiceOnly");
77+
}
78+
7379
string base64Str = Convert.ToBase64String(
7480
System.Text.Encoding.Unicode.GetBytes(string.Join(' ', args)));
7581

@@ -78,36 +84,16 @@ public async Task InitializeAsync()
7884
_psesProcess = new StdioServerProcess(factory, processStartInfo);
7985
await _psesProcess.Start();
8086

81-
LanguageClient = new LanguageClient(factory, _psesProcess);
82-
83-
DirectoryInfo testdir =
84-
Directory.CreateDirectory(Path.Combine(s_binDir, Path.GetRandomFileName()));
85-
86-
await LanguageClient.Initialize(testdir.FullName);
87-
88-
// Make sure Script Analysis is enabled because we'll need it in the tests.
89-
LanguageClient.Workspace.DidChangeConfiguration(JObject.Parse(@"
90-
{
91-
""PowerShell"": {
92-
""ScriptAnalysis"": {
93-
""Enable"": true
87+
await CustomInitializeAsync(factory, _psesProcess);
9488
}
95-
}
96-
}
97-
"));
9889

99-
Diagnostics = new List<Diagnostic>();
100-
LanguageClient.TextDocument.OnPublishDiagnostics((uri, diagnostics) =>
101-
{
102-
Diagnostics.AddRange(diagnostics);
103-
});
104-
}
105-
106-
public async Task DisposeAsync()
90+
public virtual async Task DisposeAsync()
10791
{
108-
await LanguageClient.Shutdown();
10992
await _psesProcess.Stop();
110-
LanguageClient?.Dispose();
11193
}
94+
95+
public abstract Task CustomInitializeAsync(
96+
ILoggerFactory factory,
97+
StdioServerProcess process);
11298
}
11399
}

0 commit comments

Comments
 (0)