[dartdev] Fix flakey LSP server test

This test was written to assume that the stdout stream would deliver LSP headers and body in separate "packets", but wasn't guaranteed to be the case. The failure at https://dart-ci.appspot.com/log/pkg-linux-release/unittest-asserts-release-linux/19779/pkg/dartdev/test/commands/language_server_test shows that both header+body arrived together which caused the test to fail.

These changes buffer the output until the complete message is available and then return the body of the message.

Change-Id: I5a5ec49fdca667ae351e602ca2d589fd2ab06ba3
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/251548
Commit-Queue: Brian Wilkerson <brianwilkerson@google.com>
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
This commit is contained in:
Danny Tuppeny 2022-07-15 17:44:30 +00:00 committed by Commit Bot
parent 2e86c1f018
commit 027618092a

View file

@ -2,6 +2,7 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'dart:async';
import 'dart:convert';
import 'dart:io';
@ -31,9 +32,6 @@ void defineLanguageServerTests() {
process = await project.start(args);
final Stream<String> inStream =
process!.stdout.transform<String>(utf8.decoder);
// Send an LSP init.
final String message = jsonEncode({
'jsonrpc': '2.0',
@ -51,12 +49,10 @@ void defineLanguageServerTests() {
process!.stdin.write('\r\n');
process!.stdin.write(message);
List<String> responses = await inStream.take(2).toList();
expect(responses, hasLength(2));
// Expect
final response = await _readLspMessage(process!.stdout);
expect(responses[0], startsWith('Content-Length: '));
final json = jsonDecode(responses[1]);
final json = jsonDecode(response);
expect(json['id'], 1);
expect(json['result'], isNotNull);
final result = json['result'];
@ -99,3 +95,42 @@ void defineLanguageServerTests() {
process = null;
});
}
/// Reads the first LSP message from [stream].
Future<String> _readLspMessage(Stream<List<int>> stream) {
// Headers are complete if there are 2x '\r\n\'. The '\r' is part of the LSP
// spec for headers and included on all platforms, not just Windows.
const lspHeaderBodySeperator = '\r\n\r\n';
final contentLengthRegExp = RegExp(r'Content-Length: (\d+)\r\n');
final completer = Completer<String>();
final buffer = StringBuffer();
late final StreamSubscription<String> inSubscription;
inSubscription = stream.transform<String>(utf8.decoder).listen(
(data) {
// Collect the output into the buffer.
buffer.write(data);
// Check whether the buffer has a complete message.
final bufferString = buffer.toString();
// To know if we have a complete message, we need to check we have the
// headers, extract the content-length, then check we have that many
// bytes in the body.
if (bufferString.contains(lspHeaderBodySeperator)) {
final parts = bufferString.split(lspHeaderBodySeperator);
final headers = parts[0];
final body = parts[1];
final length =
int.parse(contentLengthRegExp.firstMatch(headers)!.group(1)!);
// Check if we're already had the full payload.
if (body.length >= length) {
completer.complete(body.substring(0, length));
inSubscription.cancel();
}
}
},
);
return completer.future;
}