Skip to content

Commit d85ab6d

Browse files
bwilkersonCommit Queue
authored andcommitted
Refactor the MessageSchedulerTestView for more flexibility
There are no functional changes, just changes to the way the logging functionality is implemented. The motivation for the changes is to allow future CLs to explore ways of changing the expectations so that async handling of messages won't produce flaky tests. All references to the 'messageLog' outside the class have been replaced by higher-level methods with a semantic meaning. The class has been split into an interface and an implementation, which allows the test-specific aspects to be in the `test` directory. Some additional code cleanup was also done. Change-Id: Ie515f71153a96fb6e92eb8e2eb05f4b5e064bbd7 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/421965 Commit-Queue: Brian Wilkerson <[email protected]> Reviewed-by: Phil Quitslund <[email protected]>
1 parent 147c919 commit d85ab6d

File tree

9 files changed

+135
-69
lines changed

9 files changed

+135
-69
lines changed

pkg/analysis_server/lib/src/analysis_server.dart

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -283,16 +283,14 @@ abstract class AnalysisServer {
283283
DartFixPromptManager? dartFixPromptManager,
284284
this.providedByteStore,
285285
PluginManager? pluginManager,
286-
bool retainDataForTesting = false,
286+
MessageSchedulerListener? messageSchedulerListener,
287287
}) : resourceProvider = OverlayResourceProvider(baseResourceProvider),
288288
pubApi = PubApi(
289289
instrumentationService,
290290
httpClient,
291291
Platform.environment['PUB_HOSTED_URL'],
292292
),
293-
messageScheduler = MessageScheduler(
294-
testView: retainDataForTesting ? MessageSchedulerTestView() : null,
295-
) {
293+
messageScheduler = MessageScheduler(testView: messageSchedulerListener) {
296294
messageScheduler.setServer(this);
297295
// Set the default URI converter. This uses the resource providers path
298296
// context (unlike the initialized value) which allows tests to override it.

pkg/analysis_server/lib/src/legacy_analysis_server.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -392,7 +392,7 @@ class LegacyAnalysisServer extends AnalysisServer {
392392
super.dartFixPromptManager,
393393
super.providedByteStore,
394394
super.pluginManager,
395-
super.retainDataForTesting,
395+
super.messageSchedulerListener,
396396
}) : lspClientConfiguration = lsp.LspClientConfiguration(
397397
baseResourceProvider.pathContext,
398398
),

pkg/analysis_server/lib/src/lsp/lsp_analysis_server.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ class LspAnalysisServer extends AnalysisServer {
162162
this.detachableFileSystemManager,
163163
super.enableBlazeWatcher,
164164
super.dartFixPromptManager,
165-
super.retainDataForTesting,
165+
super.messageSchedulerListener,
166166
}) : lspClientConfiguration = LspClientConfiguration(
167167
baseResourceProvider.pathContext,
168168
),

pkg/analysis_server/lib/src/scheduler/message_scheduler.dart

Lines changed: 50 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -23,15 +23,15 @@ import 'package:meta/meta.dart';
2323
/// [AnalysisServer].
2424
///
2525
/// Clients include IDE's (LSP and Legacy protocol), DTD, the Diagnostic server
26-
/// and file wathcers. The [MessageScheduler] acts as a hub for all incoming
26+
/// and file watchers. The [MessageScheduler] acts as a hub for all incoming
2727
/// messages and forwards the messages to the appropriate handlers.
2828
final class MessageScheduler {
2929
/// A flag to allow disabling overlapping message handlers.
3030
static bool allowOverlappingHandlers = true;
3131

3232
/// A view into the [MessageScheduler] used for testing.
3333
@visibleForTesting
34-
MessageSchedulerTestView? testView;
34+
MessageSchedulerListener? testView;
3535

3636
/// The [AnalysisServer] associated with the scheduler.
3737
// TODO(brianwilkerson): Make this field private.
@@ -60,9 +60,7 @@ final class MessageScheduler {
6060
/// atomically and it was decided that it was cleaner for the scheduler to
6161
/// have the nullable reference to the server rather than the other way
6262
/// around.
63-
MessageScheduler({this.testView}) {
64-
testView?.messageScheduler = this;
65-
}
63+
MessageScheduler({this.testView});
6664

6765
/// Add the [message] to the end of the pending messages queue.
6866
///
@@ -87,7 +85,7 @@ final class MessageScheduler {
8785
/// - The incoming [legacy.ANALYSIS_REQUEST_UPDATE_CONTENT] message cancels
8886
/// any rename files request that is in progress.
8987
void add(ScheduledMessage message) {
90-
testView?.logAddMessage(message);
88+
testView?.addPendingMessage(message);
9189
if (message is LegacyMessage) {
9290
var request = message.request;
9391
var method = request.method;
@@ -109,9 +107,7 @@ final class MessageScheduler {
109107
code: lsp.ErrorCodes.ContentModified.toJson(),
110108
reason: 'File content was modified',
111109
);
112-
testView?.messageLog.add(
113-
'Canceled active request ${activeRequest.method}',
114-
);
110+
testView?.cancelActiveMessage(activeMessage);
115111
}
116112
}
117113
}
@@ -157,9 +153,7 @@ final class MessageScheduler {
157153
var message = activeMessage.message as lsp.RequestMessage;
158154
if (message.method == incomingMsgMethod) {
159155
activeMessage.cancellationToken?.cancel(reason: reason);
160-
testView?.messageLog.add(
161-
'Canceled active request ${message.method}',
162-
);
156+
testView?.cancelActiveMessage(activeMessage);
163157
}
164158
}
165159
}
@@ -168,9 +162,7 @@ final class MessageScheduler {
168162
var message = pendingMessage.message as lsp.RequestMessage;
169163
if (message.method == msg.method) {
170164
pendingMessage.cancellationToken?.cancel(reason: reason);
171-
testView?.messageLog.add(
172-
'Canceled pending request ${msg.method}',
173-
);
165+
testView?.cancelPendingMessage(pendingMessage);
174166
}
175167
}
176168
}
@@ -186,18 +178,18 @@ final class MessageScheduler {
186178
/// Dispatch the first message in the queue to be executed.
187179
void processMessages() async {
188180
_isProcessing = true;
189-
testView?.messageLog.add('Entering process messages loop');
181+
testView?.startProcessingMessages();
190182
try {
191183
while (_pendingMessages.isNotEmpty) {
192184
var currentMessage = _pendingMessages.removeFirst();
193185
_activeMessages.addLast(currentMessage);
186+
testView?.addActiveMessage(currentMessage);
194187
completer = Completer<void>();
195188
unawaited(
196189
completer.future.then((_) {
197190
_activeMessages.remove(currentMessage);
198191
}),
199192
);
200-
testView?.logHandleMessage(currentMessage);
201193
switch (currentMessage) {
202194
case LspMessage():
203195
var lspMessage = currentMessage.message;
@@ -242,9 +234,7 @@ final class MessageScheduler {
242234
// TODO(pq): if not awaited, consider adding a `then` so we can track
243235
// when the future completes. But note that we may see some flakiness in
244236
// tests as message handling gets non-deterministically interleaved.
245-
testView?.messageLog.add(
246-
' Complete ${currentMessage.runtimeType}: ${currentMessage.toString()}',
247-
);
237+
testView?.messageCompleted(currentMessage);
248238
}
249239
} catch (error, stackTrace) {
250240
server.instrumentationService.logException(
@@ -254,7 +244,7 @@ final class MessageScheduler {
254244
);
255245
}
256246
_isProcessing = false;
257-
testView?.messageLog.add('Exit process messages loop');
247+
testView?.endProcessingMessages();
258248
}
259249

260250
/// Set the [AnalysisServer].
@@ -358,7 +348,7 @@ final class MessageScheduler {
358348
var request = activeMessage.message as lsp.RequestMessage;
359349
if (request.id == params.id) {
360350
activeMessage.cancellationToken?.cancel();
361-
testView?.messageLog.add('Canceled active request ${request.method}');
351+
testView?.cancelActiveMessage(activeMessage);
362352
return;
363353
}
364354
}
@@ -368,9 +358,7 @@ final class MessageScheduler {
368358
var request = pendingMessage.message as lsp.RequestMessage;
369359
if (request.id == params.id) {
370360
pendingMessage.cancellationToken?.cancel();
371-
testView?.messageLog.add(
372-
'Canceled pending request ${request.method}',
373-
);
361+
testView?.cancelPendingMessage(pendingMessage);
374362
return;
375363
}
376364
}
@@ -403,7 +391,10 @@ final class MessageScheduler {
403391
return path != null ? Uri.file(path) : null;
404392
}
405393

406-
void checkAndCancelRefactor(LspMessage lspMessage) {
394+
void checkAndCancelRefactor(
395+
LspMessage lspMessage, {
396+
required bool isActive,
397+
}) {
407398
var request = lspMessage.message as lsp.RequestMessage;
408399
var execParams = _getCommandParams(request);
409400
if (execParams != null &&
@@ -414,14 +405,16 @@ final class MessageScheduler {
414405
lspMessage.cancellationToken?.cancel(
415406
code: lsp.ErrorCodes.ContentModified.toJson(),
416407
);
417-
testView?.messageLog.add(
418-
'Canceled in progress request ${request.method}',
419-
);
408+
if (isActive) {
409+
testView?.cancelActiveMessage(lspMessage);
410+
} else {
411+
testView?.cancelPendingMessage(lspMessage);
412+
}
420413
}
421414
}
422415
}
423416

424-
void checkAndCancelRename(LspMessage lspMessage) {
417+
void checkAndCancelRename(LspMessage lspMessage, {required bool isActive}) {
425418
var request = lspMessage.message as lsp.RequestMessage;
426419
var renameParams = _getRenameParams(request);
427420
if (renameParams != null) {
@@ -430,9 +423,11 @@ final class MessageScheduler {
430423
lspMessage.cancellationToken?.cancel(
431424
code: lsp.ErrorCodes.ContentModified.toJson(),
432425
);
433-
testView?.messageLog.add(
434-
'Canceled in progress request ${request.method}',
435-
);
426+
if (isActive) {
427+
testView?.cancelActiveMessage(lspMessage);
428+
} else {
429+
testView?.cancelPendingMessage(lspMessage);
430+
}
436431
}
437432
}
438433
}
@@ -441,37 +436,44 @@ final class MessageScheduler {
441436
if (activeMessage is LspMessage && activeMessage.isRequest) {
442437
var request = activeMessage.message as lsp.RequestMessage;
443438
if (request.method == lsp.Method.workspace_executeCommand) {
444-
checkAndCancelRefactor(activeMessage);
439+
checkAndCancelRefactor(activeMessage, isActive: true);
445440
} else if (request.method == lsp.Method.textDocument_rename) {
446-
checkAndCancelRename(activeMessage);
441+
checkAndCancelRename(activeMessage, isActive: true);
447442
}
448443
}
449444
}
450445
for (var pendingMessage in _pendingMessages) {
451446
if (pendingMessage is LspMessage && pendingMessage.isRequest) {
452447
var request = pendingMessage.message as lsp.RequestMessage;
453448
if (request.method == lsp.Method.workspace_executeCommand) {
454-
checkAndCancelRefactor(pendingMessage);
449+
checkAndCancelRefactor(pendingMessage, isActive: false);
455450
} else if (request.method == lsp.Method.textDocument_rename) {
456-
checkAndCancelRename(pendingMessage);
451+
checkAndCancelRename(pendingMessage, isActive: false);
457452
}
458453
}
459454
}
460455
}
461456
}
462457

463-
class MessageSchedulerTestView {
464-
late final MessageScheduler messageScheduler;
458+
abstract class MessageSchedulerListener {
459+
/// Report that the [message] was added to the active message queue.
460+
void addActiveMessage(ScheduledMessage message);
465461

466-
List<String> messageLog = <String>[];
462+
/// Report that the [message] was added to the pending message queue.
463+
void addPendingMessage(ScheduledMessage message);
467464

468-
void logAddMessage(ScheduledMessage message) {
469-
messageLog.add(
470-
'Incoming ${message is LspMessage ? message.message.runtimeType : message.runtimeType}: ${message.toString()}',
471-
);
472-
}
465+
/// Report that an active [message] was cancelled.
466+
void cancelActiveMessage(ScheduledMessage message);
473467

474-
void logHandleMessage(ScheduledMessage message) {
475-
messageLog.add(' Start ${message.runtimeType}: ${message.toString()}');
476-
}
468+
/// Report that a pending [message] was cancelled.
469+
void cancelPendingMessage(ScheduledMessage message);
470+
471+
/// Report that the loop that processes messages has stopped running.
472+
void endProcessingMessages();
473+
474+
/// Report that the [message] has been completed.
475+
void messageCompleted(ScheduledMessage message);
476+
477+
/// Report that the loop that processes messages has started to run.
478+
void startProcessingMessages();
477479
}

pkg/analysis_server/test/analysis_server_base.dart

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import 'package:unified_analytics/unified_analytics.dart';
2828
import 'constants.dart';
2929
import 'mocks.dart';
3030
import 'support/configuration_files.dart';
31+
import 'utils/message_scheduler_test_view.dart';
3132

3233
// TODO(scheglov): This is duplicate with pkg/linter/test/rule_test_support.dart.
3334
// Keep them as consistent with each other as they are today. Ultimately combine
@@ -96,6 +97,7 @@ abstract class ContextResolutionTest with ResourceProviderMixin {
9697

9798
final TestPluginManager pluginManager = TestPluginManager();
9899
late final MockServerChannel serverChannel;
100+
MessageSchedulerTestView? testView;
99101
late final LegacyAnalysisServer server;
100102

101103
DartFixPromptManager? dartFixPromptManager;
@@ -198,6 +200,7 @@ abstract class ContextResolutionTest with ResourceProviderMixin {
198200

199201
serverChannel.notifications.listen(processNotification);
200202

203+
testView = retainDataForTesting ? MessageSchedulerTestView() : null;
201204
server = LegacyAnalysisServer(
202205
serverChannel,
203206
resourceProvider,
@@ -209,7 +212,7 @@ abstract class ContextResolutionTest with ResourceProviderMixin {
209212
dartFixPromptManager: dartFixPromptManager,
210213
providedByteStore: _byteStore,
211214
pluginManager: pluginManager,
212-
retainDataForTesting: retainDataForTesting,
215+
messageSchedulerListener: testView,
213216
);
214217

215218
server.completionState.budgetDuration = const Duration(seconds: 30);

pkg/analysis_server/test/integration/server/message_scheduler_test.dart

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import 'package:test_reflective_loader/test_reflective_loader.dart';
1616

1717
import '../../analysis_server_base.dart';
1818
import '../../lsp/code_actions_refactor_test.dart';
19+
import '../../utils/message_scheduler_test_view.dart';
1920
import '../../utils/test_code_extensions.dart';
2021

2122
void main() {
@@ -25,8 +26,8 @@ void main() {
2526
});
2627
}
2728

28-
void _assertLogContents(MessageScheduler messageScheduler, String expected) {
29-
var actual = _getLogContents(messageScheduler.testView!.messageLog);
29+
void _assertLogContents(MessageSchedulerTestView testView, String expected) {
30+
var actual = _getLogContents(testView.messageLog);
3031
if (actual != expected) {
3132
print('-------- Actual --------');
3233
print('$actual------------------------');
@@ -58,7 +59,7 @@ class LegacyServerMessageSchedulerTest extends PubPackageAnalysisServerTest {
5859
Future<void> test_initialize() async {
5960
await setRoots(included: [workspaceRootPath], excluded: []);
6061
await waitForTasksFinished();
61-
_assertLogContents(messageScheduler, r'''
62+
_assertLogContents(testView!, r'''
6263
Incoming LegacyMessage: analysis.setAnalysisRoots
6364
Entering process messages loop
6465
Start LegacyMessage: analysis.setAnalysisRoots
@@ -78,7 +79,7 @@ Exit process messages loop
7879
futures.add(handleSuccessfulRequest(request));
7980
await Future.wait(futures);
8081
await waitForTasksFinished();
81-
_assertLogContents(messageScheduler, r'''
82+
_assertLogContents(testView!, r'''
8283
Incoming LegacyMessage: analysis.setAnalysisRoots
8384
Entering process messages loop
8485
Start LegacyMessage: analysis.setAnalysisRoots
@@ -148,7 +149,7 @@ void f() {
148149
PerformRefactorCommandHandler.delayAfterResolveForTests = null;
149150
}
150151

151-
_assertLogContents(messageScheduler, r'''
152+
_assertLogContents(testView!, r'''
152153
Incoming RequestMessage: initialize
153154
Entering process messages loop
154155
Start LspMessage: initialize
@@ -208,7 +209,7 @@ class B {
208209
await Future.wait(futures);
209210
await pumpEventQueue(times: 5000);
210211

211-
_assertLogContents(messageScheduler, r'''
212+
_assertLogContents(testView!, r'''
212213
Incoming RequestMessage: initialize
213214
Entering process messages loop
214215
Start LspMessage: initialize
@@ -245,7 +246,7 @@ Exit process messages loop
245246
await initialize();
246247
await initialAnalysis;
247248
await pumpEventQueue(times: 5000);
248-
_assertLogContents(messageScheduler, r'''
249+
_assertLogContents(testView!, r'''
249250
Incoming RequestMessage: initialize
250251
Entering process messages loop
251252
Start LspMessage: initialize
@@ -275,7 +276,7 @@ void main() {
275276
await Future.wait(futures);
276277
await pumpEventQueue(times: 5000);
277278

278-
_assertLogContents(messageScheduler, r'''
279+
_assertLogContents(testView!, r'''
279280
Incoming RequestMessage: initialize
280281
Entering process messages loop
281282
Start LspMessage: initialize
@@ -326,7 +327,7 @@ void f() {
326327
await executeCommand(codeAction.command!);
327328
await pumpEventQueue(times: 5000);
328329

329-
_assertLogContents(messageScheduler, r'''
330+
_assertLogContents(testView!, r'''
330331
Incoming RequestMessage: initialize
331332
Entering process messages loop
332333
Start LspMessage: initialize

0 commit comments

Comments
 (0)