Skip to content

Commit ee9aa75

Browse files
committed
test: improve language-server test resilience
primarily by increasing timeouts, ensuring testclient and testlanguageserver is disposed properly, and adding missing conditionawaiters. Likely still a testclient memory leak - see OmniSharp/csharp-language-server-protocol#1037
1 parent d524fc1 commit ee9aa75

File tree

10 files changed

+93
-24
lines changed

10 files changed

+93
-24
lines changed

src/language-server/Contextive.LanguageServer.Tests/ConfigurationTests.fs

+6-1
Original file line numberDiff line numberDiff line change
@@ -27,15 +27,20 @@ let tests =
2727

2828
let pathLoader () : obj = path
2929

30+
let loadingAwaiter = ConditionAwaiter.create ()
31+
3032
let config =
3133
[ Workspace.optionsBuilder <| Path.Combine("fixtures", "completion_tests")
32-
ConfigurationSection.contextivePathLoaderBuilder pathLoader ]
34+
ConfigurationSection.contextivePathLoaderBuilder pathLoader
35+
ServerLog.optionsBuilder loadingAwaiter ]
3336

3437
use! client = TestClient(config) |> init
3538

3639
path <- "two.yml"
3740
ConfigurationSection.didChangePath client path
3841

42+
do! ServerLog.waitForLogMessage loadingAwaiter "Loading contextive" |> Async.Ignore
43+
3944
let! labels = Completion.getCompletionLabels client
4045

4146
test <@ (labels, Fixtures.Two.expectedCompletionLabels) ||> Seq.compareWith compare = 0 @>

src/language-server/Contextive.LanguageServer.Tests/DefinitionsInitializationTests.fs

+5-5
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ let tests =
2525
[ Workspace.optionsBuilder ""
2626
ConfigurationSection.contextivePathBuilder pathValue ]
2727

28-
let! (client, _) = TestClient(config) |> initAndWaitForReply
28+
use! client = TestClient(config) |> init
2929

3030
let responseRouterReturns = client.SendRequest("contextive/initialize")
3131

@@ -59,7 +59,7 @@ let tests =
5959
ConfigurationSection.contextivePathBuilder pathValue
6060
showDocumentRequestHandlerBuilder <| handler showDocAwaiter showDocResponse ]
6161

62-
let! (client, _) = TestClient(config) |> initAndWaitForReply
62+
use! client = TestClient(config) |> init
6363

6464
let responseRouterReturns = client.SendRequest("contextive/initialize")
6565

@@ -70,7 +70,7 @@ let tests =
7070

7171
File.Delete(pathValue)
7272

73-
let! showDocMsg = ConditionAwaiter.waitForAny showDocAwaiter 5000
73+
let! showDocMsg = ConditionAwaiter.waitForAny showDocAwaiter
7474

7575
test <@ showDocMsg.Value.Uri.ToString().Contains(pathValue) @>
7676
}
@@ -90,7 +90,7 @@ let tests =
9090

9191
let existingContents = File.ReadAllText(fullPath)
9292

93-
let! (client, _) = TestClient(config) |> initAndWaitForReply
93+
use! client = TestClient(config) |> init
9494

9595
let responseRouterReturns = client.SendRequest("contextive/initialize")
9696

@@ -99,7 +99,7 @@ let tests =
9999
|> Async.AwaitTask
100100
|> Async.Ignore
101101

102-
let! showDocMsg = ConditionAwaiter.waitForAny showDocAwaiter 3000
102+
let! showDocMsg = ConditionAwaiter.waitForAny showDocAwaiter
103103

104104
let newContents = File.ReadAllText(fullPath)
105105

src/language-server/Contextive.LanguageServer.Tests/DefinitionsTests.fs

+1-1
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ let tests =
137137
let! definitions = getDefinitionsWithErrorHandler onErrorLoading configGetter workspaceFolder
138138
let! termsWhenInvalid = Definitions.find definitions "" id
139139

140-
let! errorMessage = ConditionAwaiter.waitForAny errorMessageAwaiter 500
140+
let! errorMessage = ConditionAwaiter.waitForAny errorMessageAwaiter
141141
test <@ errorMessage.Value = expectedErrorMessage @>
142142
test <@ Seq.length termsWhenInvalid = 0 @>
143143

src/language-server/Contextive.LanguageServer.Tests/Helpers/ConditionAwaiter.fs

+18-3
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ type Message<'T> =
1717
| WaitFor of WaitForCondition<'T>
1818
| Clear
1919

20+
[<Literal>]
21+
let private DEFAULT_TIMEOUT_MS = 5000
2022

2123
let create<'T> () =
2224
let awaiter =
@@ -55,16 +57,29 @@ let create<'T> () =
5557

5658
let received (awaiter: MailboxProcessor<Message<'T>>) msg = awaiter.Post(Received(msg))
5759

58-
let waitFor (awaiter: MailboxProcessor<Message<'T>>) condition timeout =
60+
/// Waits for a message that passes the condition to become true up to the nominated timeout.
61+
let waitForTimeout timeoutMs (awaiter: MailboxProcessor<Message<'T>>) condition =
5962
awaiter.PostAndTryAsyncReply(
6063
(fun rc ->
6164
WaitFor(
6265
{ ReplyChannel = rc
6366
Condition = condition }
6467
)),
65-
timeout
68+
timeoutMs
6669
)
6770

68-
let waitForAny (awaiter: MailboxProcessor<Message<'T>>) timeout = waitFor awaiter (fun _ -> true) timeout
71+
/// Waits for a message that passes the condition to become true up to the default timeout of 5s.
72+
/// If you want a custom timeout, use waitForTimeout.
73+
let waitFor (awaiter: MailboxProcessor<Message<'T>>) =
74+
waitForTimeout DEFAULT_TIMEOUT_MS awaiter
75+
76+
/// Waits for any message up to the nominated timeout.
77+
let waitForAnyTimeout timeoutMs (awaiter: MailboxProcessor<Message<'T>>) =
78+
waitForTimeout timeoutMs awaiter (fun _ -> true)
79+
80+
/// Waits for any message up to the default timeout of 5s.
81+
/// If you want a custom timeout, use waitForAnyTimeout.
82+
let waitForAny awaiter =
83+
waitForAnyTimeout DEFAULT_TIMEOUT_MS awaiter
6984

7085
let clear (awaiter: MailboxProcessor<Message<'T>>) timeout = awaiter.Post(Clear)

src/language-server/Contextive.LanguageServer.Tests/Helpers/ServerLog.fs

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ let waitForLogMessage logAwaiter (logMessage: string) =
1515
async {
1616
let logCondition = fun (m: string) -> m.Contains(logMessage)
1717

18-
return! ConditionAwaiter.waitFor logAwaiter logCondition 25000
18+
return! ConditionAwaiter.waitForTimeout 25000 logAwaiter logCondition
1919
}
2020

2121
let optionsBuilder logAwaiter (b: LanguageClientOptions) =

src/language-server/Contextive.LanguageServer.Tests/Helpers/TestClient.fs

+9-4
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,18 @@ type InitializationOptions =
1717
type private TestClient() =
1818
inherit LanguageServerTestBase(JsonRpcTestOptions())
1919

20-
override _.SetupServer() =
20+
member _.AddToDisposable languageServer = base.Disposable.Add(languageServer)
21+
22+
override this.SetupServer() =
2123
let clientPipe = Pipe()
2224
let serverPipe = Pipe()
2325

24-
setupAndStartLanguageServer (serverPipe.Reader.AsStream()) (clientPipe.Writer.AsStream())
25-
|> Async.Ignore
26-
|> Async.Start
26+
async {
27+
let! server = setupAndStartLanguageServer (serverPipe.Reader.AsStream()) (clientPipe.Writer.AsStream())
28+
// this.AddToDisposable server
29+
()
30+
}
31+
|> Async.StartImmediate
2732

2833
(clientPipe.Reader.AsStream(), serverPipe.Writer.AsStream())
2934

src/language-server/Contextive.LanguageServer.Tests/InitializationTests.fs

+4
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ let tests =
3131
ConfigurationSection.contextivePathBuilder pathValue ]
3232

3333
let! (client, reply) = TestClient(config) |> initAndWaitForReply
34+
use client = client
3435

3536
test <@ client.ClientSettings.Capabilities.Workspace.Configuration.IsSupported @>
3637

@@ -43,6 +44,7 @@ let tests =
4344
let config = [ ConfigurationSection.contextivePathBuilder $"/tmp/{pathValue}" ]
4445

4546
let! (client, reply) = TestClient(config) |> initAndWaitForReply
47+
use client = client
4648

4749
test <@ client.ClientSettings.Capabilities.Workspace.Configuration.IsSupported @>
4850

@@ -54,6 +56,7 @@ let tests =
5456
let config = [ ConfigurationSection.contextivePathBuilder pathValue ]
5557

5658
let! (client, reply) = TestClientWithCustomInitWait(config, Some pathValue) |> initAndWaitForReply
59+
use client = client
5760

5861
test <@ client.ClientSettings.Capabilities.Workspace.Configuration.IsSupported @>
5962

@@ -72,6 +75,7 @@ let tests =
7275
let defaultPath = ".contextive/definitions.yml"
7376

7477
let! (client, reply) = TestClient(config) |> initAndWaitForReply
78+
use client = client
7579

7680
test <@ (defaultArg reply "").Contains(defaultPath) @>
7781
}

src/language-server/Contextive.LanguageServer.Tests/Program.fs

+1
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,5 @@ open Contextive.LanguageServer.Program
66
[<EntryPoint>]
77
let main argv =
88
setupLogging
9+
VerifyTests.VerifierSettings.DisableRequireUniquePrefix()
910
runTestsInAssemblyWithCLIArgs [] argv

src/language-server/Contextive.LanguageServer.Tests/WatchedFilesTests.fs

+6-8
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,9 @@ let tests =
3939
ConfigurationSection.contextivePathBuilder $"one.yml"
4040
WatchedFiles.optionsBuilder registrationAwaiter ]
4141

42-
let! client = TestClient(config) |> init
42+
use! client = TestClient(config) |> init
4343

44-
let! registrationMsg = ConditionAwaiter.waitForAny registrationAwaiter 500
44+
let! registrationMsg = ConditionAwaiter.waitForAny registrationAwaiter
4545

4646
test <@ client.ClientSettings.Capabilities.Workspace.DidChangeWatchedFiles.IsSupported @>
4747

@@ -65,9 +65,9 @@ let tests =
6565
ConfigurationSection.contextivePathLoaderBuilder pathLoader
6666
WatchedFiles.optionsBuilder registrationAwaiter ]
6767

68-
let! client = TestClient(config) |> init
68+
use! client = TestClient(config) |> init
6969

70-
let! initialRegistrationMsg = ConditionAwaiter.waitForAny registrationAwaiter 500
70+
let! initialRegistrationMsg = ConditionAwaiter.waitForAny registrationAwaiter
7171

7272
ConditionAwaiter.clear registrationAwaiter 500
7373

@@ -81,7 +81,6 @@ let tests =
8181
match m with
8282
| Registered(_) -> true
8383
| _ -> false)
84-
500
8584

8685
let! unregistrationMsg =
8786
ConditionAwaiter.waitFor
@@ -90,7 +89,6 @@ let tests =
9089
match m with
9190
| Unregistered(_) -> true
9291
| _ -> false)
93-
500
9492

9593
match secondRegistrationMsg with
9694
| Some(Registered(_, opts)) -> test <@ opts.Watchers |> watcherIsForFile newDefinitionsFile @>
@@ -115,7 +113,7 @@ let tests =
115113
[ Workspace.optionsBuilder relativePath
116114
ConfigurationSection.contextivePathBuilder definitionsFile ]
117115

118-
let! client = TestClient(config) |> init
116+
use! client = TestClient(config) |> init
119117

120118
let definitionsFileUri =
121119
Path.Combine(Directory.GetCurrentDirectory(), relativePath, definitionsFile)
@@ -153,7 +151,7 @@ let tests =
153151
[ Workspace.optionsBuilder relativePath
154152
ConfigurationSection.contextivePathBuilder definitionsFile ]
155153

156-
let! client = TestClient(config) |> init
154+
use! client = TestClient(config) |> init
157155

158156
let definitionsFileUri =
159157
Path.Combine(Directory.GetCurrentDirectory(), relativePath, definitionsFile)

src/language-server/Contextive.LanguageServer.Tests/fixtures/completion_tests/three.yml

+42-1
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,45 @@ contexts:
55
- name: CombinedWord
66
- name: octopus
77
- name: combined
8-
- name: word
8+
- name: word
9+
- name: newterm
10+
- name: newterm
11+
- name: newterm
12+
- name: newterm
13+
- name: newterm
14+
- name: newterm
15+
- name: newterm
16+
- name: newterm
17+
- name: newterm
18+
- name: newterm
19+
- name: newterm
20+
- name: newterm
21+
- name: newterm
22+
- name: newterm
23+
- name: newterm
24+
- name: newterm
25+
- name: newterm
26+
- name: newterm
27+
- name: newterm
28+
- name: newterm
29+
- name: newterm
30+
- name: newterm
31+
- name: newterm
32+
- name: newterm
33+
- name: newterm
34+
- name: newterm
35+
- name: newterm
36+
- name: newterm
37+
- name: newterm
38+
- name: newterm
39+
- name: newterm
40+
- name: newterm
41+
- name: newterm
42+
- name: newterm
43+
- name: newterm
44+
- name: newterm
45+
- name: newterm
46+
- name: newterm
47+
- name: newterm
48+
- name: newterm
49+
- name: newterm

0 commit comments

Comments
 (0)