Split helpers out of LSP base test into a mixin

This splits the helper functions out of the abstract base test, which will allow them to be reused in integration tests that have their own transport (real stdio to a process).

Change-Id: I7777dc873582b4bacd91d998fed740ada47bd2b0
Reviewed-on: https://dart-review.googlesource.com/c/90062
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
diff --git a/pkg/analysis_server/test/lsp/initialization_test.dart b/pkg/analysis_server/test/lsp/initialization_test.dart
index 7afb2a5..edbad6e 100644
--- a/pkg/analysis_server/test/lsp/initialization_test.dart
+++ b/pkg/analysis_server/test/lsp/initialization_test.dart
@@ -66,7 +66,7 @@
   test_uninitialized_dropsNotifications() async {
     final notification =
         makeNotification(new Method.fromJson('randomNotification'), null);
-    final nextNotification = channel.errorNotificationsFromServer.first;
+    final nextNotification = errorNotificationsFromServer.first;
     channel.sendNotificationToServer(notification);
 
     // Wait up to 1sec to ensure no error/log notifications were sent back.
diff --git a/pkg/analysis_server/test/lsp/server_abstract.dart b/pkg/analysis_server/test/lsp/server_abstract.dart
index b4d1f9f..2fd13dc 100644
--- a/pkg/analysis_server/test/lsp/server_abstract.dart
+++ b/pkg/analysis_server/test/lsp/server_abstract.dart
@@ -30,8 +30,8 @@
 
 final beginningOfDocument = new Range(new Position(0, 0), new Position(0, 0));
 
-abstract class AbstractLspAnalysisServerTest
-    with ResourceProviderMixin, ClientCapabilitiesHelperMixin {
+mixin LspAnalysisServerTestMixin
+    implements ResourceProviderMixin, ClientCapabilitiesHelperMixin {
   static const positionMarker = '^';
   static const rangeMarkerStart = '[[';
   static const rangeMarkerEnd = ']]';
@@ -39,13 +39,46 @@
   static final allMarkersPattern =
       new RegExp(allMarkers.map(RegExp.escape).join('|'));
 
-  MockLspServerChannel channel;
-  LspAnalysisServer server;
-
   int _id = 0;
   String projectFolderPath, mainFilePath;
   Uri projectFolderUri, mainFileUri;
 
+  Stream<Message> get serverToClient;
+
+  /**
+   * A stream of [NotificationMessage]s from the server that may be errors.
+   */
+  Stream<NotificationMessage> get errorNotificationsFromServer {
+    return notificationsFromServer.where(_isErrorNotification);
+  }
+
+  /**
+   * A stream of [NotificationMessage]s from the server.
+   */
+  Stream<NotificationMessage> get notificationsFromServer {
+    return serverToClient
+        .where((m) => m is NotificationMessage)
+        .cast<NotificationMessage>();
+  }
+
+  /// Checks whether a notification is likely an error from the server (for
+  /// example a window/showMessage). This is useful for tests that want to
+  /// ensure no errors come from the server in response to notifications (which
+  /// don't have their own responses).
+  bool _isErrorNotification(NotificationMessage notification) {
+    return notification.method == Method.window_logMessage ||
+        notification.method == Method.window_showMessage;
+  }
+
+  /**
+   * A stream of [RequestMessage]s from the server.
+   */
+  Stream<RequestMessage> get requestsFromServer {
+    return serverToClient
+        .where((m) => m is RequestMessage)
+        .cast<RequestMessage>();
+  }
+
   void applyChanges(
     Map<String, String> fileContents,
     Map<String, List<TextEdit>> changes,
@@ -158,8 +191,7 @@
         changes,
       ),
     );
-    channel.sendNotificationToServer(notification);
-    await pumpEventQueue();
+    sendNotificationToServer(notification);
   }
 
   Future changeWorkspaceFolders({List<Uri> add, List<Uri> remove}) async {
@@ -172,8 +204,7 @@
         ),
       ),
     );
-    channel.sendNotificationToServer(notification);
-    await pumpEventQueue();
+    sendNotificationToServer(notification);
   }
 
   Future closeFile(Uri uri) async {
@@ -182,8 +213,7 @@
       new DidCloseTextDocumentParams(
           new TextDocumentIdentifier(uri.toString())),
     );
-    channel.sendNotificationToServer(notification);
-    await pumpEventQueue();
+    sendNotificationToServer(notification);
   }
 
   Future<Object> executeCommand(Command command) async {
@@ -240,7 +270,7 @@
     FutureOr<void> f(), {
     Duration timeout = const Duration(seconds: 5),
   }) async {
-    final firstError = channel.errorNotificationsFromServer.first;
+    final firstError = errorNotificationsFromServer.first;
     await f();
 
     final notificationFromServer = await firstError.timeout(timeout);
@@ -256,7 +286,7 @@
     Duration timeout = const Duration(seconds: 5),
   }) async {
     final firstRequest =
-        channel.requestsFromServer.firstWhere((n) => n.method == method);
+        requestsFromServer.firstWhere((n) => n.method == method);
     await f();
 
     final requestFromServer = await firstRequest.timeout(timeout);
@@ -268,7 +298,7 @@
   /// Sends a request to the server and unwraps the result. Throws if the
   /// response was not successful or returned an error.
   Future<T> expectSuccessfulResponseTo<T>(RequestMessage request) async {
-    final resp = await channel.sendRequestToServer(request);
+    final resp = await sendRequestToServer(request);
     if (resp.error != null) {
       throw resp.error;
     } else {
@@ -491,12 +521,12 @@
             ),
             null,
             workspaceFolders?.map(toWorkspaceFolder)?.toList()));
-    final response = await channel.sendRequestToServer(request);
+    final response = await sendRequestToServer(request);
     expect(response.id, equals(request.id));
 
     if (response.error == null) {
       final notification = makeNotification(Method.initialized, null);
-      channel.sendNotificationToServer(notification);
+      sendNotificationToServer(notification);
       await pumpEventQueue();
     }
 
@@ -530,7 +560,7 @@
       new DidOpenTextDocumentParams(new TextDocumentItem(
           uri.toString(), dartLanguageId, version, content)),
     );
-    channel.sendNotificationToServer(notification);
+    sendNotificationToServer(notification);
     await pumpEventQueue();
   }
 
@@ -621,7 +651,7 @@
     String newName,
   ) {
     final request = makeRenameRequest(version, uri, pos, newName);
-    return channel.sendRequestToServer(request);
+    return sendRequestToServer(request);
   }
 
   Future replaceFile(int newVersion, Uri uri, String content) {
@@ -635,10 +665,67 @@
   /// Sends [responseParams] to the server as a successful response to
   /// a server-initiated [request].
   void respondTo<T>(RequestMessage request, T responseParams) {
-    channel.sendResponseToServer(
+    sendResponseToServer(
         new ResponseMessage(request.id, responseParams, null, jsonRpcVersion));
   }
 
+  FutureOr<void> sendNotificationToServer(NotificationMessage notification);
+
+  Future<ResponseMessage> sendRequestToServer(RequestMessage request);
+
+  void sendResponseToServer(ResponseMessage response);
+
+  WorkspaceFolder toWorkspaceFolder(Uri uri) {
+    return WorkspaceFolder(uri.toString(), path.basename(uri.toFilePath()));
+  }
+
+  Future<List<Diagnostic>> waitForDiagnostics(Uri uri) async {
+    PublishDiagnosticsParams diagnosticParams;
+    await serverToClient.firstWhere((message) {
+      if (message is NotificationMessage &&
+          message.method == Method.textDocument_publishDiagnostics) {
+        diagnosticParams = message.params;
+
+        return diagnosticParams.uri == uri.toString();
+      }
+      return false;
+    });
+    return diagnosticParams.diagnostics;
+  }
+
+  /// Removes markers like `[[` and `]]` and `^` that are used for marking
+  /// positions/ranges in strings to avoid hard-coding positions in tests.
+  String withoutMarkers(String contents) =>
+      contents.replaceAll(allMarkersPattern, '');
+
+  /// Removes range markers from strings to give accurate position offsets.
+  String withoutRangeMarkers(String contents) =>
+      contents.replaceAll(rangeMarkerStart, '').replaceAll(rangeMarkerEnd, '');
+}
+
+abstract class AbstractLspAnalysisServerTest
+    with
+        ResourceProviderMixin,
+        ClientCapabilitiesHelperMixin,
+        LspAnalysisServerTestMixin {
+  MockLspServerChannel channel;
+  LspAnalysisServer server;
+
+  Stream<Message> get serverToClient => channel.serverToClient;
+
+  Future sendNotificationToServer(NotificationMessage notification) async {
+    channel.sendNotificationToServer(notification);
+    await pumpEventQueue();
+  }
+
+  Future<ResponseMessage> sendRequestToServer(RequestMessage request) {
+    return channel.sendRequestToServer(request);
+  }
+
+  void sendResponseToServer(ResponseMessage response) {
+    channel.sendResponseToServer(response);
+  }
+
   void setUp() {
     channel = new MockLspServerChannel(debugPrintCommunication);
     // Create an SDK in the mock file system.
@@ -665,33 +752,6 @@
     channel.close();
     await server.shutdown();
   }
-
-  WorkspaceFolder toWorkspaceFolder(Uri uri) {
-    return WorkspaceFolder(uri.toString(), path.basename(uri.toFilePath()));
-  }
-
-  Future<List<Diagnostic>> waitForDiagnostics(Uri uri) async {
-    PublishDiagnosticsParams diagnosticParams;
-    await channel.serverToClient.firstWhere((message) {
-      if (message is NotificationMessage &&
-          message.method == Method.textDocument_publishDiagnostics) {
-        diagnosticParams = message.params;
-
-        return diagnosticParams.uri == uri.toString();
-      }
-      return false;
-    });
-    return diagnosticParams.diagnostics;
-  }
-
-  /// Removes markers like `[[` and `]]` and `^` that are used for marking
-  /// positions/ranges in strings to avoid hard-coding positions in tests.
-  String withoutMarkers(String contents) =>
-      contents.replaceAll(allMarkersPattern, '');
-
-  /// Removes range markers from strings to give accurate position offsets.
-  String withoutRangeMarkers(String contents) =>
-      contents.replaceAll(rangeMarkerStart, '').replaceAll(rangeMarkerEnd, '');
 }
 
 mixin ClientCapabilitiesHelperMixin {
diff --git a/pkg/analysis_server/test/lsp/server_test.dart b/pkg/analysis_server/test/lsp/server_test.dart
index 3be7d04..8f2525c 100644
--- a/pkg/analysis_server/test/lsp/server_test.dart
+++ b/pkg/analysis_server/test/lsp/server_test.dart
@@ -100,7 +100,7 @@
     await initialize();
     final notification =
         makeNotification(new Method.fromJson(r'$/randomNotification'), null);
-    final firstError = channel.errorNotificationsFromServer.first;
+    final firstError = errorNotificationsFromServer.first;
     channel.sendNotificationToServer(notification);
 
     // Wait up to 1sec to ensure no error/log notifications were sent back.
diff --git a/pkg/analysis_server/test/mocks.dart b/pkg/analysis_server/test/mocks.dart
index 4ee6655..367e52e 100644
--- a/pkg/analysis_server/test/mocks.dart
+++ b/pkg/analysis_server/test/mocks.dart
@@ -65,31 +65,6 @@
     return _closed.future;
   }
 
-  /**
-   * A stream of [NotificationMessage]s from the server that may be errors.
-   */
-  Stream<lsp.NotificationMessage> get errorNotificationsFromServer {
-    return notificationsFromServer.where(_isErrorNotification);
-  }
-
-  /**
-   * A stream of [NotificationMessage]s from the server.
-   */
-  Stream<lsp.NotificationMessage> get notificationsFromServer {
-    return _serverToClient.stream
-        .where((m) => m is lsp.NotificationMessage)
-        .cast<lsp.NotificationMessage>();
-  }
-
-  /**
-   * A stream of [RequestMessage]s from the server.
-   */
-  Stream<lsp.RequestMessage> get requestsFromServer {
-    return _serverToClient.stream
-        .where((m) => m is lsp.RequestMessage)
-        .cast<lsp.RequestMessage>();
-  }
-
   Stream<lsp.Message> get serverToClient => _serverToClient.stream;
 
   @override
@@ -224,15 +199,6 @@
       lsp.ToJsonable message, T Function(Map<String, dynamic>) constructor) {
     return constructor(jsonDecode(jsonEncode(message.toJson())));
   }
-
-  /// Checks whether a notification is likely an error from the server (for
-  /// example a window/showMessage). This is useful for tests that want to
-  /// ensure no errors come from the server in response to notifications (which
-  /// don't have their own responses).
-  bool _isErrorNotification(lsp.NotificationMessage notification) {
-    return notification.method == Method.window_logMessage ||
-        notification.method == Method.window_showMessage;
-  }
 }
 
 /**