Skip to content

Commit 3b6cd9c

Browse files
committed
Fix #107: Host tests should have wait timeouts
This change adds a standard 5 second timeout to language server and debug adapter tests which wait for events to be returned from the server. This will prevent these tests from hanging on the CI server and also will indicate which events are not being raised as expected.
1 parent 6516a6a commit 3b6cd9c

File tree

4 files changed

+108
-116
lines changed

4 files changed

+108
-116
lines changed

test/PowerShellEditorServices.Test.Host/DebugAdapterTests.cs

Lines changed: 2 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,15 @@
55

66
using Microsoft.PowerShell.EditorServices.Protocol.Client;
77
using Microsoft.PowerShell.EditorServices.Protocol.DebugAdapter;
8-
using Microsoft.PowerShell.EditorServices.Protocol.MessageProtocol;
98
using Microsoft.PowerShell.EditorServices.Protocol.MessageProtocol.Channel;
109
using System;
1110
using System.IO;
1211
using System.Threading.Tasks;
1312
using Xunit;
14-
using Xunit.Abstractions;
15-
using Xunit.Sdk;
1613

1714
namespace Microsoft.PowerShell.EditorServices.Test.Host
1815
{
19-
public class DebugAdapterTests : IAsyncLifetime
16+
public class DebugAdapterTests : ServerTestsBase, IAsyncLifetime
2017
{
2118
private DebugAdapterClient debugAdapterClient;
2219
private string DebugScriptPath =
@@ -33,6 +30,7 @@ public Task InitializeAsync()
3330

3431
System.Console.WriteLine(" Output log at path: {0}", testLogPath);
3532

33+
this.protocolClient =
3634
this.debugAdapterClient =
3735
new DebugAdapterClient(
3836
new StdioClientChannel(
@@ -103,44 +101,6 @@ private Task LaunchScript(string scriptPath)
103101
{
104102
return this.debugAdapterClient.LaunchScript(scriptPath);
105103
}
106-
107-
private Task<TResult> SendRequest<TParams, TResult>(
108-
RequestType<TParams, TResult> requestType,
109-
TParams requestParams)
110-
{
111-
return
112-
this.debugAdapterClient.SendRequest(
113-
requestType,
114-
requestParams);
115-
}
116-
117-
private Task SendEvent<TParams>(EventType<TParams> eventType, TParams eventParams)
118-
{
119-
return
120-
this.debugAdapterClient.SendEvent(
121-
eventType,
122-
eventParams);
123-
}
124-
125-
private Task<TParams> WaitForEvent<TParams>(EventType<TParams> eventType)
126-
{
127-
TaskCompletionSource<TParams> eventTask = new TaskCompletionSource<TParams>();
128-
129-
this.debugAdapterClient.SetEventHandler(
130-
eventType,
131-
(p, ctx) =>
132-
{
133-
if (!eventTask.Task.IsCompleted)
134-
{
135-
eventTask.SetResult(p);
136-
}
137-
138-
return Task.FromResult(true);
139-
},
140-
true); // Override any existing handler
141-
142-
return eventTask.Task;
143-
}
144104
}
145105
}
146106

test/PowerShellEditorServices.Test.Host/LanguageServerTests.cs

Lines changed: 2 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -6,26 +6,20 @@
66
using Microsoft.PowerShell.EditorServices.Protocol.Client;
77
using Microsoft.PowerShell.EditorServices.Protocol.DebugAdapter;
88
using Microsoft.PowerShell.EditorServices.Protocol.LanguageServer;
9-
using Microsoft.PowerShell.EditorServices.Protocol.MessageProtocol;
109
using Microsoft.PowerShell.EditorServices.Protocol.MessageProtocol.Channel;
1110
using Microsoft.PowerShell.EditorServices.Protocol.Messages;
12-
using Nito.AsyncEx;
1311
using System;
14-
using System.Collections.Concurrent;
1512
using System.IO;
1613
using System.Linq;
1714
using System.Threading.Tasks;
1815
using Xunit;
1916

2017
namespace Microsoft.PowerShell.EditorServices.Test.Host
2118
{
22-
public class LanguageServerTests : IAsyncLifetime
19+
public class LanguageServerTests : ServerTestsBase, IAsyncLifetime
2320
{
2421
private LanguageServiceClient languageServiceClient;
2522

26-
private ConcurrentDictionary<string, AsyncProducerConsumerQueue<object>> eventQueuePerType =
27-
new ConcurrentDictionary<string, AsyncProducerConsumerQueue<object>>();
28-
2923
public Task InitializeAsync()
3024
{
3125
string testLogPath =
@@ -37,6 +31,7 @@ public Task InitializeAsync()
3731

3832
System.Console.WriteLine(" Output log at path: {0}", testLogPath);
3933

34+
this.protocolClient =
4035
this.languageServiceClient =
4136
new LanguageServiceClient(
4237
new StdioClientChannel(
@@ -503,24 +498,6 @@ await this.SendEvent(
503498
Assert.Equal("0\r\n", choiceOutput.Output);
504499
}
505500

506-
private Task<TResult> SendRequest<TParams, TResult>(
507-
RequestType<TParams, TResult> requestType,
508-
TParams requestParams)
509-
{
510-
return
511-
this.languageServiceClient.SendRequest(
512-
requestType,
513-
requestParams);
514-
}
515-
516-
private Task SendEvent<TParams>(EventType<TParams> eventType, TParams eventParams)
517-
{
518-
return
519-
this.languageServiceClient.SendEvent(
520-
eventType,
521-
eventParams);
522-
}
523-
524501
private async Task SendOpenFileEvent(string filePath, bool waitForDiagnostics = true)
525502
{
526503
string fileContents = string.Join(Environment.NewLine, File.ReadAllLines(filePath));
@@ -549,54 +526,5 @@ await this.SendEvent(
549526
await diagnosticWaitTask;
550527
}
551528
}
552-
553-
private void QueueEventsForType<TParams>(EventType<TParams> eventType)
554-
{
555-
var eventQueue =
556-
this.eventQueuePerType.AddOrUpdate(
557-
eventType.MethodName,
558-
new AsyncProducerConsumerQueue<object>(),
559-
(key, queue) => queue);
560-
561-
this.languageServiceClient.SetEventHandler(
562-
eventType,
563-
(p, ctx) =>
564-
{
565-
return eventQueue.EnqueueAsync(p);
566-
});
567-
}
568-
569-
private Task<TParams> WaitForEvent<TParams>(EventType<TParams> eventType)
570-
{
571-
// Use the event queue if one has been registered
572-
AsyncProducerConsumerQueue<object> eventQueue = null;
573-
if (this.eventQueuePerType.TryGetValue(eventType.MethodName, out eventQueue))
574-
{
575-
return
576-
eventQueue
577-
.DequeueAsync()
578-
.ContinueWith<TParams>(
579-
task => (TParams)task.Result);
580-
}
581-
else
582-
{
583-
TaskCompletionSource<TParams> eventTask = new TaskCompletionSource<TParams>();
584-
585-
this.languageServiceClient.SetEventHandler(
586-
eventType,
587-
(p, ctx) =>
588-
{
589-
if (!eventTask.Task.IsCompleted)
590-
{
591-
eventTask.SetResult(p);
592-
}
593-
594-
return Task.FromResult(true);
595-
},
596-
true); // Override any existing handler
597-
598-
return eventTask.Task;
599-
}
600-
}
601529
}
602530
}

test/PowerShellEditorServices.Test.Host/PowerShellEditorServices.Test.Host.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@
8080
<Compile Include="DebugAdapterTests.cs" />
8181
<Compile Include="Properties\AssemblyInfo.cs" />
8282
<Compile Include="LanguageServerTests.cs" />
83+
<Compile Include="ServerTestsBase.cs" />
8384
</ItemGroup>
8485
<ItemGroup>
8586
<None Include="App.config" />
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
using Microsoft.PowerShell.EditorServices.Protocol.Client;
2+
using Microsoft.PowerShell.EditorServices.Protocol.MessageProtocol;
3+
using Nito.AsyncEx;
4+
using System;
5+
using System.Collections.Concurrent;
6+
using System.Threading.Tasks;
7+
8+
namespace Microsoft.PowerShell.EditorServices.Test.Host
9+
{
10+
public class ServerTestsBase
11+
{
12+
protected ProtocolClient protocolClient;
13+
14+
private ConcurrentDictionary<string, AsyncProducerConsumerQueue<object>> eventQueuePerType =
15+
new ConcurrentDictionary<string, AsyncProducerConsumerQueue<object>>();
16+
17+
protected Task<TResult> SendRequest<TParams, TResult>(
18+
RequestType<TParams, TResult> requestType,
19+
TParams requestParams)
20+
{
21+
return
22+
this.protocolClient.SendRequest(
23+
requestType,
24+
requestParams);
25+
}
26+
27+
protected Task SendEvent<TParams>(EventType<TParams> eventType, TParams eventParams)
28+
{
29+
return
30+
this.protocolClient.SendEvent(
31+
eventType,
32+
eventParams);
33+
}
34+
35+
protected void QueueEventsForType<TParams>(EventType<TParams> eventType)
36+
{
37+
var eventQueue =
38+
this.eventQueuePerType.AddOrUpdate(
39+
eventType.MethodName,
40+
new AsyncProducerConsumerQueue<object>(),
41+
(key, queue) => queue);
42+
43+
this.protocolClient.SetEventHandler(
44+
eventType,
45+
(p, ctx) =>
46+
{
47+
return eventQueue.EnqueueAsync(p);
48+
});
49+
}
50+
51+
protected async Task<TParams> WaitForEvent<TParams>(
52+
EventType<TParams> eventType,
53+
int timeoutMilliseconds = 5000)
54+
{
55+
Task<TParams> eventTask = null;
56+
57+
// Use the event queue if one has been registered
58+
AsyncProducerConsumerQueue<object> eventQueue = null;
59+
if (this.eventQueuePerType.TryGetValue(eventType.MethodName, out eventQueue))
60+
{
61+
eventTask =
62+
eventQueue
63+
.DequeueAsync()
64+
.ContinueWith<TParams>(
65+
task => (TParams)task.Result);
66+
}
67+
else
68+
{
69+
TaskCompletionSource<TParams> eventTaskSource = new TaskCompletionSource<TParams>();
70+
71+
this.protocolClient.SetEventHandler(
72+
eventType,
73+
(p, ctx) =>
74+
{
75+
if (!eventTaskSource.Task.IsCompleted)
76+
{
77+
eventTaskSource.SetResult(p);
78+
}
79+
80+
return Task.FromResult(true);
81+
},
82+
true); // Override any existing handler
83+
84+
eventTask = eventTaskSource.Task;
85+
}
86+
87+
await
88+
Task.WhenAny(
89+
eventTask,
90+
Task.Delay(timeoutMilliseconds));
91+
92+
if (!eventTask.IsCompleted)
93+
{
94+
throw new TimeoutException(
95+
string.Format(
96+
"Timed out waiting for '{0}' event!",
97+
eventType.MethodName));
98+
}
99+
100+
return await eventTask;
101+
}
102+
}
103+
}

0 commit comments

Comments
 (0)