[analysis_server] Switch LSP tests to use workspaceAnalysisComplete() instead of progress/status notifications

Progress/status notifications for analysis are not a reliably indicator of analysis being complete. They might not be sent if there is no work, or may have multiple bursts in case of plugins. The new `dart/workspace/analysis/complete` request was added as a reliably signal that all pending work is complete.

This change updates all tests that used the progress/status notifications to know analysis had completed (except tests that were specifically testing the notifications), removing concepts like "initialAnalysis" which could be unsound with plugins (or debouncing of status notifications).

I also replaced some `await pumpEventQueue();` which followed calls to the server with `await workspaceAnalysisComplete();` because it should also be more reliable. However `pumpEventQueue` remains when it's after synchronous modifications to the resource provider, since otherwise the server might not have started processing the watch event before we called it.

Change-Id: I342890c82e18848b9bffc00876141216fbbcd5c7
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/526482
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Reviewed-by: Samuel Rawlins <srawlins@google.com>
diff --git a/pkg/analysis_server/benchmark/perf/memory_tests.dart b/pkg/analysis_server/benchmark/perf/memory_tests.dart
index 0005e5f..e799695 100644
--- a/pkg/analysis_server/benchmark/perf/memory_tests.dart
+++ b/pkg/analysis_server/benchmark/perf/memory_tests.dart
@@ -145,7 +145,7 @@
   int _fileVersion = 1;
 
   @override
-  Future<void> get analysisFinished => _test.initialAnalysis;
+  Future<void> get analysisFinished => _test.workspaceAnalysisComplete();
 
   @override
   Future<void> closeFile(String filePath) {
diff --git a/pkg/analysis_server/integration_test/lsp_server/analyzer_status_test.dart b/pkg/analysis_server/integration_test/lsp_server/analyzer_status_test.dart
index 5f63c17..e82b51eb 100644
--- a/pkg/analysis_server/integration_test/lsp_server/analyzer_status_test.dart
+++ b/pkg/analysis_server/integration_test/lsp_server/analyzer_status_test.dart
@@ -20,7 +20,7 @@
     newFile(mainFilePath, initialContents);
 
     await initialize();
-    await initialAnalysis;
+    await workspaceAnalysisComplete();
 
     // Set up futures to wait for the new events.
     var startNotification = waitForAnalysisStart();
diff --git a/pkg/analysis_server/integration_test/server/message_scheduler_test.dart b/pkg/analysis_server/integration_test/server/message_scheduler_test.dart
index 056a43b..0e09acc 100644
--- a/pkg/analysis_server/integration_test/server/message_scheduler_test.dart
+++ b/pkg/analysis_server/integration_test/server/message_scheduler_test.dart
@@ -240,8 +240,7 @@
 
   Future<void> test_initialize() async {
     await initialize();
-    await initialAnalysis;
-    await pumpEventQueue(times: 5000);
+    await workspaceAnalysisComplete();
     _assertLogContents(testView!, r'''
 Incoming RequestMessage: initialize:0
 Entering process messages loop
@@ -253,6 +252,10 @@
   Start LspMessage: initialized
 Exit process messages loop
   Complete LspMessage: initialized
+Incoming RequestMessage: dart/workspace/analysis/complete:1
+Entering process messages loop
+  Start LspMessage: dart/workspace/analysis/complete:1
+Exit process messages loop
 ''');
   }
 
diff --git a/pkg/analysis_server/test/lsp/analyzer_status_test.dart b/pkg/analysis_server/test/lsp/analyzer_status_test.dart
index 64d0591..f48c3bc4 100644
--- a/pkg/analysis_server/test/lsp/analyzer_status_test.dart
+++ b/pkg/analysis_server/test/lsp/analyzer_status_test.dart
@@ -46,7 +46,7 @@
     newFile(mainFilePath, initialContents);
 
     await initialize();
-    await initialAnalysis;
+    await workspaceAnalysisComplete();
 
     // Set up futures to wait for the new events.
     var startNotification = waitForAnalysisStart();
diff --git a/pkg/analysis_server/test/lsp/cancel_request_test.dart b/pkg/analysis_server/test/lsp/cancel_request_test.dart
index 0bf6441..79c294a 100644
--- a/pkg/analysis_server/test/lsp/cancel_request_test.dart
+++ b/pkg/analysis_server/test/lsp/cancel_request_test.dart
@@ -28,7 +28,7 @@
 
     await initialize();
     await openFile(mainFileUri, code.code);
-    await initialAnalysis;
+    await workspaceAnalysisComplete();
 
     // Create a completion request that we'll cancel.
     var completionRequest = makeRequest(
diff --git a/pkg/analysis_server/test/lsp/completion_dart_test.dart b/pkg/analysis_server/test/lsp/completion_dart_test.dart
index 994f754..c0293ac 100644
--- a/pkg/analysis_server/test/lsp/completion_dart_test.dart
+++ b/pkg/analysis_server/test/lsp/completion_dart_test.dart
@@ -109,7 +109,7 @@
   Future<void> initializeServer() async {
     await initialize();
     await openFile(mainFileUri, code.code);
-    await initialAnalysis;
+    await workspaceAnalysisComplete();
   }
 
   Future<void> test_supported() async {
@@ -183,7 +183,7 @@
   Future<void> initializeServer() async {
     await initialize();
     await openFile(mainFileUri, code.code);
-    await initialAnalysis;
+    await workspaceAnalysisComplete();
   }
 
   Future<void> test_abstract_class() async {
@@ -1559,7 +1559,7 @@
     await provideConfig(initialize, {'documentation': ?preference});
 
     await openFile(mainFileUri, code.code);
-    await initialAnalysis;
+    await workspaceAnalysisComplete();
     var res = await getCompletion(mainFileUri, code.position.position);
     var completion = res.singleWhere((c) => c.label == 'A');
     var resolved = await resolveCompletion(completion); // Resolve for docs
@@ -1600,7 +1600,7 @@
     await provideConfig(initialize, {'documentation': ?preference});
 
     await openFile(mainFileUri, code.code);
-    await initialAnalysis;
+    await workspaceAnalysisComplete();
     var res = await getCompletion(mainFileUri, code.position.position);
     var completion = res.singleWhere((c) => c.label == 'InOtherFile');
 
@@ -1716,7 +1716,7 @@
     await initialize();
 
     await openFile(mainFileUri, code.code);
-    await initialAnalysis;
+    await workspaceAnalysisComplete();
     var res = await getCompletion(mainFileUri, code.position.position);
 
     var completion = res.where((c) => c.label == 'MyClass').single;
@@ -2226,7 +2226,7 @@
     await provideConfig(initialize, {'completeFunctionCalls': true});
 
     await openFile(mainFileUri, code.code);
-    await initialAnalysis;
+    await workspaceAnalysisComplete();
     var res = await getCompletion(mainFileUri, code.position.position);
     var item = res.singleWhere((c) => c.label == 'myFunction(…)');
     // Ensure the snippet comes through in the expected format with the expected
@@ -2275,7 +2275,7 @@
     await provideConfig(initialize, {'completeFunctionCalls': true});
 
     await openFile(mainFileUri, code.code);
-    await initialAnalysis;
+    await workspaceAnalysisComplete();
     var res = await getCompletion(mainFileUri, code.position.position);
 
     var completion = res.singleWhere(
@@ -2585,7 +2585,7 @@
       // after the expectations are set up above, because otherwise if the
       // exceptions occur too quickly, they will be unhandled (whereas the
       // expectations attach error handlers to them).
-      await pumpEventQueue(times: 50000);
+      await pumpEventQueue(times: 5000);
       completer.complete();
       await Future.wait(expectationFutures);
     } finally {
@@ -3150,7 +3150,7 @@
 
     await initialize();
     await openFile(mainFileUri, code.code);
-    await initialAnalysis;
+    await workspaceAnalysisComplete();
     var res = await getCompletionList(mainFileUri, code.position.position);
 
     // Expect everything (hashCode etc. will take it over 500).
@@ -3180,7 +3180,7 @@
 
     await provideConfig(initialize, {'maxCompletionItems': 200});
     await openFile(mainFileUri, code.code);
-    await initialAnalysis;
+    await workspaceAnalysisComplete();
     var res = await getCompletionList(mainFileUri, code.position.position);
 
     // Should be capped at 200 and marked as incomplete.
@@ -3318,7 +3318,7 @@
 
     await provideConfig(initialize, {'maxCompletionItems': 10});
     await openFile(mainFileUri, code.code);
-    await initialAnalysis;
+    await workspaceAnalysisComplete();
     var res = await getCompletionList(mainFileUri, code.position.position);
 
     expect(res.items, hasLength(10));
@@ -3352,7 +3352,7 @@
     setCompletionItemSnippetSupport();
     await provideConfig(initialize, {'maxCompletionItems': 10});
     await openFile(mainFileUri, code.code);
-    await initialAnalysis;
+    await workspaceAnalysisComplete();
     var res = await getCompletionList(mainFileUri, code.position.position);
 
     // Should be capped at 10 and marked as incomplete.
@@ -3707,7 +3707,7 @@
     await initialize();
 
     await openFile(mainFileUri, code.code);
-    await initialAnalysis;
+    await workspaceAnalysisComplete();
     var res = await getCompletion(mainFileUri, code.position.position);
 
     var completion = res.singleWhere((c) => c.label.startsWith('foo'));
@@ -3971,7 +3971,7 @@
     await initialize();
 
     await openFile(mainFileUri, code.code);
-    await initialAnalysis;
+    await workspaceAnalysisComplete();
     var res = await getCompletion(mainFileUri, code.position.position);
 
     // Find the completion for the class in the other file.
@@ -4086,7 +4086,7 @@
     await initialize();
 
     await openFile(mainFileUri, code.code);
-    await initialAnalysis;
+    await workspaceAnalysisComplete();
     var res = await getCompletion(mainFileUri, code.position.position);
     var completions = res.where((c) => c.label == 'MyExportedClass').toList();
     expect(completions, hasLength(1));
@@ -4117,7 +4117,7 @@
     await initialize();
 
     await openFile(mainFileUri, code.code);
-    await initialAnalysis;
+    await workspaceAnalysisComplete();
     var res = await getCompletion(mainFileUri, code.position.position);
 
     var completions = res.where((c) => c.label == 'MyExportedClass').toList();
@@ -4148,7 +4148,7 @@
     await initialize();
 
     await openFile(mainFileUri, code.code);
-    await initialAnalysis;
+    await workspaceAnalysisComplete();
     var res = await getCompletion(mainFileUri, code.position.position);
 
     var completions = res.where((c) => c.label == 'MyDuplicatedClass').toList();
@@ -4187,7 +4187,7 @@
     await initialize();
 
     await openFile(mainFileUri, code.code);
-    await initialAnalysis;
+    await workspaceAnalysisComplete();
     var res = await getCompletion(mainFileUri, code.position.position);
 
     var enumCompletions = res
@@ -4268,7 +4268,7 @@
     await initialize();
 
     await openFile(mainFileUri, code.code);
-    await initialAnalysis;
+    await workspaceAnalysisComplete();
     var res = await getCompletion(mainFileUri, code.position.position);
 
     var completions = res
@@ -4305,7 +4305,7 @@
     await initialize();
 
     await openFile(mainFileUri, code.code);
-    await initialAnalysis;
+    await workspaceAnalysisComplete();
     var res = await getCompletion(mainFileUri, code.position.position);
 
     // Expect only a single entry for the 'empty' extension member.
@@ -4360,7 +4360,7 @@
     await initialize();
 
     await openFile(mainFileUri, code.code);
-    await initialAnalysis;
+    await workspaceAnalysisComplete();
     var res = await getCompletion(mainFileUri, code.position.position);
 
     var completions = res.where((c) => c.label == 'MyExportedClass').toList();
@@ -4442,7 +4442,7 @@
     await initialize();
 
     await openFile(mainFileUri, code.code);
-    await initialAnalysis;
+    await workspaceAnalysisComplete();
     var res = await getCompletion(mainFileUri, code.position.position);
 
     var completions = res.where((c) => c.label == 'MyExportedClass').toList();
@@ -4475,7 +4475,7 @@
     await initialize();
 
     await openFile(mainFileUri, code.code);
-    await initialAnalysis;
+    await workspaceAnalysisComplete();
     var res = await getCompletion(mainFileUri, code.position.position);
 
     // Find the completion for the class in the other file.
@@ -4573,7 +4573,7 @@
     await initialize();
 
     await openFile(mainFileUri, code.code);
-    await initialAnalysis;
+    await workspaceAnalysisComplete();
     var res = await getCompletion(mainFileUri, code.position.position);
 
     var completion = res.singleWhere((c) => c.label == 'InOtherFile');
@@ -4629,7 +4629,7 @@
       },
     );
     await openFile(mainFileUri, code.code);
-    await initialAnalysis;
+    await workspaceAnalysisComplete();
     var res = await getCompletionList(mainFileUri, code.position.position);
 
     // Ensure we flagged that we returned everything.
@@ -4656,7 +4656,7 @@
       },
     );
     await openFile(mainFileUri, code.code);
-    await initialAnalysis;
+    await workspaceAnalysisComplete();
     var res = await getCompletionList(mainFileUri, code.position.position);
 
     // Ensure we flagged that we did not return everything.
@@ -4781,7 +4781,7 @@
     content = 'MyOtherClass^';
     await initialize();
     await openFile(mainFileUri, code.code);
-    await initialAnalysis;
+    await workspaceAnalysisComplete();
 
     // Start with a blank file.
     newFile(otherFilePath, '');
@@ -4816,7 +4816,7 @@
     await initialize();
 
     await openFile(mainFileUri, code.code);
-    await initialAnalysis;
+    await workspaceAnalysisComplete();
     var res = await getCompletion(mainFileUri, code.position.position);
 
     // Find the completion for the class in the other file.
@@ -4890,7 +4890,7 @@
     await initialize();
 
     await openFile(mainFileUri, code.code);
-    await initialAnalysis;
+    await workspaceAnalysisComplete();
     var res = await getCompletion(mainFileUri, code.position.position);
 
     var completion = res.singleWhere(
@@ -5031,7 +5031,7 @@
     );
 
     await openFile(mainFileUri, code.code);
-    await initialAnalysis;
+    await workspaceAnalysisComplete();
     var res = await getCompletion(mainFileUri, code.position.position);
 
     // Ensure the item doesn't appear in the results (because we might not
@@ -5056,7 +5056,7 @@
     await initialize();
 
     await openFile(mainFileUri, code.code);
-    await initialAnalysis;
+    await workspaceAnalysisComplete();
     var res = await getCompletion(mainFileUri, code.position.position);
 
     // Ensure the item doesn't appear in the results (because we might not
@@ -5106,7 +5106,7 @@
   ) async {
     await initialize();
     await openFile(fileUri, code.code);
-    await initialAnalysis;
+    await workspaceAnalysisComplete();
     var res = await getCompletion(fileUri, code.position.position);
 
     var completion = res.singleWhere((c) => c.label == completionLabel);
@@ -5222,7 +5222,7 @@
 
     await initialize();
     await openFile(mainFileUri, code.code);
-    await initialAnalysis;
+    await workspaceAnalysisComplete();
 
     // Use a Completer to control when the completion handler starts computing.
     var completer = Completer<void>();
diff --git a/pkg/analysis_server/test/lsp/diagnostic_test.dart b/pkg/analysis_server/test/lsp/diagnostic_test.dart
index 9024b686..eabbb01 100644
--- a/pkg/analysis_server/test/lsp/diagnostic_test.dart
+++ b/pkg/analysis_server/test/lsp/diagnostic_test.dart
@@ -728,7 +728,7 @@
     newFile(newFilePath, '');
 
     // Allow server to catch up.
-    await waitForAnalysisComplete();
+    await workspaceAnalysisComplete();
 
     // Expect unused_import, not uri_does_not_exist.
     expect(diagnostics[mainFileUri]!.single.code, 'unused_import');
@@ -858,13 +858,11 @@
 
     await provideConfig(initialize, {});
     await openFile(mainFileUri, contents);
-    await initialAnalysis;
+    await workspaceAnalysisComplete();
     expect(diagnostics[mainFileUri], isNull);
 
-    await Future.wait([
-      updateConfig({'showTodos': true}),
-      waitForAnalysisComplete(),
-    ]);
+    await updateConfig({'showTodos': true});
+    await workspaceAnalysisComplete();
     expect(diagnostics[mainFileUri], hasLength(1));
   }
 
@@ -883,7 +881,7 @@
       // either.
       'showTodos': ['TODO', 'fixme'],
     });
-    await initialAnalysis;
+    await workspaceAnalysisComplete();
 
     var initialDiagnostics = diagnostics[mainFileUri]!;
     expect(initialDiagnostics, hasLength(2));
diff --git a/pkg/analysis_server/test/lsp/document_changes_test.dart b/pkg/analysis_server/test/lsp/document_changes_test.dart
index ec8b76b..f1a7239 100644
--- a/pkg/analysis_server/test/lsp/document_changes_test.dart
+++ b/pkg/analysis_server/test/lsp/document_changes_test.dart
@@ -138,10 +138,10 @@
     newFile(mainFilePath, content);
 
     await initialize();
-    await initialAnalysis;
+    await workspaceAnalysisComplete();
 
     await openFile(mainFileUri, newContent);
-    await pumpEventQueue(times: 50000);
+    await workspaceAnalysisComplete();
 
     // Expect diagnostics, because changing the content will have triggered
     // analysis.
@@ -153,10 +153,10 @@
     newFile(mainFilePath, content);
 
     await initialize();
-    await initialAnalysis;
+    await workspaceAnalysisComplete();
 
     await openFile(mainFileUri, content);
-    await pumpEventQueue(times: 5000);
+    await workspaceAnalysisComplete();
 
     // Expect no diagnostics because the file didn't actually change content
     // when the overlay was created, so it should not have triggered analysis.
@@ -210,7 +210,7 @@
       ],
       openFile(mainFileUri, content),
     ]);
-    await pumpEventQueue(times: 50000);
+    await workspaceAnalysisComplete();
     expect(server.resourceProvider.hasOverlay(mainFilePath), isTrue);
   }
 
@@ -232,19 +232,19 @@
 
     // Expect diagnostics after opening the file with the same contents.
     await openFile(mainFileUri, content);
-    await pumpEventQueue(times: 5000);
+    await workspaceAnalysisComplete();
     expect(diagnostics[mainFileUri], isNotEmpty);
 
     // Expect diagnostics after deleting the file because the overlay is still
     // active.
     deleteFile(mainFilePath);
-    await pumpEventQueue(times: 5000);
+    await workspaceAnalysisComplete();
     expect(diagnostics[mainFileUri], isNotEmpty);
 
     // Expect diagnostics to be removed after we close the file (which removes
     // the overlay).
     await closeFile(mainFileUri);
-    await pumpEventQueue(times: 5000);
+    await workspaceAnalysisComplete();
     expect(diagnostics[mainFileUri], isEmpty);
   }
 
@@ -268,19 +268,19 @@
 
     // Expect diagnostics after opening the file with the same contents.
     await openFile(mainFileUri, content);
-    await pumpEventQueue(times: 5000);
+    await workspaceAnalysisComplete();
     expect(diagnostics[mainFileUri], isNotEmpty);
 
     // Expect diagnostics after deleting the file because the overlay is still
     // active.
     deleteFile(mainFilePath);
-    await pumpEventQueue(times: 5000);
+    await workspaceAnalysisComplete();
     expect(diagnostics[mainFileUri], isNotEmpty);
 
     // Expect diagnostics remain after re-creating the file (the overlay is still
     // active).
     newFile(mainFilePath, content);
-    await pumpEventQueue(times: 5000);
+    await workspaceAnalysisComplete();
     expect(diagnostics[mainFileUri], isNotEmpty);
 
     // Expect diagnostics remain after we close the file because the file still
@@ -331,7 +331,7 @@
     newFile(binMainFilePath, binMainContent);
 
     await initialize();
-    await initialAnalysis;
+    await workspaceAnalysisComplete();
 
     // Expect diagnostics because 'foo.dart' doesn't exist.
     expect(diagnostics[binMainFileUri], isNotEmpty);
@@ -339,10 +339,8 @@
     // Create the file and _immediately_ open it, so the file exists when the
     // overlay is created, even though the watcher event has not been processed.
     newFile(fooFilePath, fooContent);
-    await Future.wait([
-      openFile(fooUri, fooContent),
-      waitForAnalysisComplete(),
-    ]);
+    await openFile(fooUri, fooContent);
+    await workspaceAnalysisComplete();
 
     // Expect the diagnostics have gone.
     expect(diagnostics[binMainFileUri], isEmpty);
diff --git a/pkg/analysis_server/test/lsp/hover_test.dart b/pkg/analysis_server/test/lsp/hover_test.dart
index a6040a3..828b91e 100644
--- a/pkg/analysis_server/test/lsp/hover_test.dart
+++ b/pkg/analysis_server/test/lsp/hover_test.dart
@@ -37,7 +37,7 @@
 
     await provideConfig(initialize, {'documentation': ?preference});
     await openFile(mainFileUri, code.code);
-    await initialAnalysis;
+    await workspaceAnalysisComplete();
     var hover = await getHover(mainFileUri, code.position.position);
     var hoverContents = _getStringContents(hover!);
 
@@ -61,7 +61,7 @@
 
     await initialize();
     await openFile(mainFileUri, code.code);
-    await initialAnalysis;
+    await workspaceAnalysisComplete();
     var hover = await getHover(mainFileUri, code.position.position);
     expect(hover, isNotNull);
     expect(hover!.range, equals(code.range.range));
@@ -78,14 +78,18 @@
   }) async {
     var code = TestCode.parse(content);
 
-    var initialAnalysis = waitForAnalysis ? waitForAnalysisComplete() : null;
     await initialize();
     if (withOpenFile) {
       await openFile(mainFileUri, code.code);
     } else {
       newFile(mainFilePath, code.code);
+      if (waitForAnalysis) {
+        await pumpEventQueue(times: 5000); // Allow server to see watch event.
+      }
     }
-    await initialAnalysis;
+    if (waitForAnalysis) {
+      await workspaceAnalysisComplete();
+    }
     var hover = await getHover(mainFileUri, code.position.position);
     expect(hover, isNull);
   }
@@ -96,7 +100,7 @@
 
     await initialize();
     await openFile(mainFileUri, code.code);
-    await initialAnalysis;
+    await workspaceAnalysisComplete();
     var hover = await getHover(mainFileUri, code.position.position);
     expect(hover, isNotNull);
     expect(hover!.range, equals(code.range.range));
@@ -117,14 +121,18 @@
 
     var code = TestCode.parse(content);
 
-    var initialAnalysis = waitForAnalysis ? waitForAnalysisComplete() : null;
     await initialize();
     if (withOpenFile) {
       await openFile(fileUri, code.code);
     } else {
       newFile(mainFilePath, code.code);
+      if (waitForAnalysis) {
+        await pumpEventQueue(times: 5000); // Allow server to see watch event.
+      }
     }
-    await initialAnalysis;
+    if (waitForAnalysis) {
+      await workspaceAnalysisComplete();
+    }
     var hover = await getHover(fileUri, code.position.position);
     expect(hover, isNotNull);
     expect(hover!.range, equals(code.range.range));
diff --git a/pkg/analysis_server/test/lsp/import_test.dart b/pkg/analysis_server/test/lsp/import_test.dart
index 63b8526..427abbd 100644
--- a/pkg/analysis_server/test/lsp/import_test.dart
+++ b/pkg/analysis_server/test/lsp/import_test.dart
@@ -615,7 +615,7 @@
 
     newFile(fromUri(fileUri ?? mainFileUri), code.code);
     await initialize();
-    await initialAnalysis;
+    await workspaceAnalysisComplete();
     var res = await getImports(fileUri ?? mainFileUri, code.position.position);
 
     expect(res ?? [], expectedLocations);
diff --git a/pkg/analysis_server/test/lsp/inline_value_test.dart b/pkg/analysis_server/test/lsp/inline_value_test.dart
index b6c275e..cc8069d 100644
--- a/pkg/analysis_server/test/lsp/inline_value_test.dart
+++ b/pkg/analysis_server/test/lsp/inline_value_test.dart
@@ -534,7 +534,7 @@
         'experimentalInlineValuesProperties': true,
     });
     await openFile(mainFileUri, code.code);
-    await initialAnalysis;
+    await workspaceAnalysisComplete();
 
     var actualValues = await getInlineValues(
       mainFileUri,
diff --git a/pkg/analysis_server/test/lsp/migrate_test.dart b/pkg/analysis_server/test/lsp/migrate_test.dart
index 787bb19..45cca73 100644
--- a/pkg/analysis_server/test/lsp/migrate_test.dart
+++ b/pkg/analysis_server/test/lsp/migrate_test.dart
@@ -1561,7 +1561,7 @@
     String? expectedEdit,
     bool apply = false,
   }) async {
-    await initialAnalysis;
+    await workspaceAnalysisComplete();
     var request = makeRequest(
       CustomMethods.migrate,
       DartMigrateParams(
diff --git a/pkg/analysis_server/test/lsp/outline_test.dart b/pkg/analysis_server/test/lsp/outline_test.dart
index 921101f..95709e8 100644
--- a/pkg/analysis_server/test/lsp/outline_test.dart
+++ b/pkg/analysis_server/test/lsp/outline_test.dart
@@ -251,15 +251,12 @@
       waitForOutline(mainFileUri).then((outline) => mainOutline = outline),
     );
 
-    await Future.wait([
-      initialize(initializationOptions: {'outline': true}),
-      waitForAnalysisComplete(),
-    ]);
-    await pumpEventQueue(times: 5000);
+    await initialize(initializationOptions: {'outline': true});
+    await workspaceAnalysisComplete();
     expect(mainOutline, isNull); // Shouldn't be sent yet, file is not open.
 
     await openFile(mainFileUri, content);
-    await pumpEventQueue(times: 5000);
+    await workspaceAnalysisComplete();
     expect(mainOutline, isNotNull); // Should have been sent now.
   }
 }
diff --git a/pkg/analysis_server/test/lsp/reanalyze_test.dart b/pkg/analysis_server/test/lsp/reanalyze_test.dart
index d02e69b..1eeea09 100644
--- a/pkg/analysis_server/test/lsp/reanalyze_test.dart
+++ b/pkg/analysis_server/test/lsp/reanalyze_test.dart
@@ -15,12 +15,21 @@
 
 @reflectiveTest
 class ReanalyzeTest extends AbstractLspAnalysisServerTest {
+  @override
+  void setUp() {
+    super.setUp();
+
+    // We use progress notifications to verify analysis happened after executing
+    // the command.
+    setWorkDoneProgressSupport();
+  }
+
   Future<void> test_reanalyze() async {
     const initialContents = 'int a = 1;';
     newFile(mainFilePath, initialContents);
 
     await initialize();
-    await initialAnalysis;
+    await workspaceAnalysisComplete();
 
     // Set up futures to wait for the new events.
     var startNotification = waitForAnalysisStart();
diff --git a/pkg/analysis_server/test/lsp/rename_test.dart b/pkg/analysis_server/test/lsp/rename_test.dart
index d6df260..98ca7c4 100644
--- a/pkg/analysis_server/test/lsp/rename_test.dart
+++ b/pkg/analysis_server/test/lsp/rename_test.dart
@@ -1629,7 +1629,7 @@
     var code = TestCode.parse(content);
     await initialize(workspaceFolders: workspaceFolders);
     await openFile(fileUri, code.code, version: documentVersion);
-    await initialAnalysis;
+    await workspaceAnalysisComplete();
 
     var result = await rename(
       fileUri,
diff --git a/pkg/analysis_server/test/lsp/server_abstract.dart b/pkg/analysis_server/test/lsp/server_abstract.dart
index 763a482..ba20214 100644
--- a/pkg/analysis_server/test/lsp/server_abstract.dart
+++ b/pkg/analysis_server/test/lsp/server_abstract.dart
@@ -908,26 +908,13 @@
   /// server.
   bool failTestOnErrorDiagnostic = true;
 
-  /// A completer for [initialAnalysis].
-  final Completer<void> _initialAnalysisCompleter = Completer<void>();
-
-  /// A completer for [currentAnalysis].
-  Completer<void> _currentAnalysisCompleter = Completer<void>()..complete();
-
   /// [analysisOptionsPath] as a 'file:///' [Uri].
   Uri get analysisOptionsUri => pathContext.toUri(analysisOptionsPath);
 
-  /// A [Future] that completes when the current analysis completes (or is
-  /// already completed if no analysis is in progress).
-  Future<void> get currentAnalysis => _currentAnalysisCompleter.future;
-
   /// The experimental capabilities returned from the server during initialization.
   Map<String, Object?> get experimentalServerCapabilities =>
       serverCapabilities.experimental as Map<String, Object?>? ?? {};
 
-  /// A [Future] that completes with the first analysis after initialization.
-  Future<void> get initialAnalysis => _initialAnalysisCompleter.future;
-
   bool get initialized => _clientCapabilities != null;
 
   /// The URI for an augmentation for [mainFileUri].
@@ -1146,16 +1133,6 @@
     notificationsFromServer.listen((notification) async {
       if (notification.method == Method.progress) {
         await _handleProgress(notification);
-      } else if (notification.method == CustomMethods.analyzerStatus) {
-        var params = AnalyzerStatusParams.fromJson(
-          notification.params as Map<String, Object?>,
-        );
-
-        if (params.isAnalyzing) {
-          _handleAnalysisBegin();
-        } else {
-          _handleAnalysisEnd();
-        }
       }
     });
 
@@ -1608,19 +1585,6 @@
     return outlineParams.outline;
   }
 
-  void _handleAnalysisBegin() {
-    assert(_currentAnalysisCompleter.isCompleted);
-    _currentAnalysisCompleter = Completer<void>();
-  }
-
-  void _handleAnalysisEnd() {
-    if (!_initialAnalysisCompleter.isCompleted) {
-      _initialAnalysisCompleter.complete();
-    }
-    assert(!_currentAnalysisCompleter.isCompleted);
-    _currentAnalysisCompleter.complete();
-  }
-
   Future<void> _handleProgress(NotificationMessage request) async {
     var params = ProgressParams.fromJson(
       request.params as Map<String, Object?>,
@@ -1636,15 +1600,6 @@
     if (WorkDoneProgressEnd.canParse(params.value, nullLspJsonReporter)) {
       _validProgressTokens.remove(params.token);
     }
-
-    if (params.token == analyzingProgressToken) {
-      if (WorkDoneProgressBegin.canParse(params.value, nullLspJsonReporter)) {
-        _handleAnalysisBegin();
-      }
-      if (WorkDoneProgressEnd.canParse(params.value, nullLspJsonReporter)) {
-        _handleAnalysisEnd();
-      }
-    }
   }
 
   Future<void> _handleWorkDoneProgressCreate(RequestMessage request) async {
@@ -1683,7 +1638,7 @@
   @override
   Future<void> initializeServer() async {
     await initialize();
-    await currentAnalysis;
+    await workspaceAnalysisComplete();
   }
 }
 
diff --git a/pkg/analysis_server/test/lsp/server_test.dart b/pkg/analysis_server/test/lsp/server_test.dart
index e4ca003..e941045 100644
--- a/pkg/analysis_server/test/lsp/server_test.dart
+++ b/pkg/analysis_server/test/lsp/server_test.dart
@@ -39,21 +39,19 @@
 
   Future<void> test_trigger_afterInitialAnalysis() async {
     await initialize();
-    await initialAnalysis;
-    await pumpEventQueue(times: 5000);
+    await workspaceAnalysisComplete();
     expect(promptManager.checksTriggered, 1);
   }
 
   Future<void> test_trigger_afterPackageConfigChange() async {
     await initialize();
-    await initialAnalysis;
-    await pumpEventQueue(times: 5000);
+    await workspaceAnalysisComplete();
     expect(promptManager.checksTriggered, 1);
 
     // Expect that writing package config attempts to trigger another check.
     writeTestPackageConfig();
-    await waitForAnalysisComplete();
-    await pumpEventQueue(times: 5000);
+    await pumpEventQueue(times: 5000); // Allow server to pick up watcher event.
+    await workspaceAnalysisComplete();
     expect(promptManager.checksTriggered, 2);
   }
 }
diff --git a/pkg/analysis_server/test/lsp/signature_help_test.dart b/pkg/analysis_server/test/lsp/signature_help_test.dart
index a1184f3..711c1ef 100644
--- a/pkg/analysis_server/test/lsp/signature_help_test.dart
+++ b/pkg/analysis_server/test/lsp/signature_help_test.dart
@@ -52,7 +52,7 @@
     if (state == _FileState.open) {
       await openFile(mainFileUri, code.code);
     }
-    await initialAnalysis;
+    await workspaceAnalysisComplete();
 
     var res = (await getSignatureHelp(
       mainFileUri,
@@ -125,7 +125,7 @@
     var code = TestCode.parse(content);
     await provideConfig(initialize, {'documentation': ?preference});
     await openFile(mainFileUri, code.code);
-    await initialAnalysis;
+    await workspaceAnalysisComplete();
     var signatureHelp = await getSignatureHelp(
       mainFileUri,
       code.position.position,
diff --git a/pkg/analysis_server/test/lsp/temporary_overlay_operation_test.dart b/pkg/analysis_server/test/lsp/temporary_overlay_operation_test.dart
index b8c1984..c900c47 100644
--- a/pkg/analysis_server/test/lsp/temporary_overlay_operation_test.dart
+++ b/pkg/analysis_server/test/lsp/temporary_overlay_operation_test.dart
@@ -32,7 +32,7 @@
   Future<void> test_noIntermediateAnalysisResults() async {
     newFile(mainFilePath, '');
     await initialize();
-    await initialAnalysis;
+    await workspaceAnalysisComplete();
 
     // Modify the overlays to have invalid code, which will then be reverted.
     // At no point should diagnostics or closing labels be transmitted for the
@@ -48,7 +48,7 @@
 
   Future<void> test_pausesRequestQueue() async {
     await initialize();
-    await initialAnalysis;
+    await workspaceAnalysisComplete();
     await openFile(mainFileUri, '// ORIGINAL');
 
     await _TestTemporaryOverlayOperation(server, () async {
@@ -62,7 +62,7 @@
     }).doWork();
 
     // Ensure we processed the update afterwards.
-    await pumpEventQueue(times: 5000);
+    await workspaceAnalysisComplete();
     expectFsStateContent(mainFilePath, '// CHANGED');
     expectOverlayContent(mainFilePath, '// CHANGED');
   }
@@ -70,7 +70,7 @@
   Future<void> test_pausesWatcherEvents() async {
     var mainFile = newFile(mainFilePath, '// ORIGINAL');
     await initialize();
-    await initialAnalysis;
+    await workspaceAnalysisComplete();
 
     await _TestTemporaryOverlayOperation(server, () async {
       // Modify the file to trigger watcher events
@@ -89,7 +89,7 @@
   Future<void> test_restoresOverlays() async {
     newFile(mainFilePath, '// DISK');
     await initialize();
-    await initialAnalysis;
+    await workspaceAnalysisComplete();
     await openFile(mainFileUri, '// ORIGINAL OVERLAY');
 
     late _TestTemporaryOverlayOperation operation;
@@ -101,14 +101,14 @@
     });
     await operation.doWork();
 
-    await pumpEventQueue(times: 5000);
+    await workspaceAnalysisComplete();
     expectOverlayContent(mainFilePath, '// ORIGINAL OVERLAY');
   }
 
   Future<void> test_temporarilyRemovesAddedFiles() async {
     newFile(mainFilePath, '');
     await initialize();
-    await initialAnalysis;
+    await workspaceAnalysisComplete();
 
     expect(server.driverMap.values.single.addedFiles, isNotEmpty);
 
diff --git a/pkg/analysis_server/test/lsp/workspace_analysis_complete_test.dart b/pkg/analysis_server/test/lsp/workspace_analysis_complete_test.dart
index 8be941a..0b197ff 100644
--- a/pkg/analysis_server/test/lsp/workspace_analysis_complete_test.dart
+++ b/pkg/analysis_server/test/lsp/workspace_analysis_complete_test.dart
@@ -35,6 +35,15 @@
     return super.sendRequestToServer(request);
   }
 
+  @override
+  void setUp() {
+    super.setUp();
+
+    // Enable progress notifications because we want to check the analysis
+    // occurs prior to this request completing.
+    setWorkDoneProgressSupport();
+  }
+
   Future<void> test_advertisedCapability() async {
     await initialize();
 
@@ -96,9 +105,10 @@
       r'Response to initialize',
       r'Response to dart/workspace/analysis/complete',
       // When adding a new workspace folder that triggers analysis.
-      r'$/analyzerStatus notification',
+      'window/workDoneProgress/create request',
+      r'$/progress notification',
       r'textDocument/publishDiagnostics notification',
-      r'$/analyzerStatus notification',
+      r'$/progress notification',
       r'Response to dart/workspace/analysis/complete',
     ]);
   }
@@ -168,9 +178,10 @@
 
     expect(messages, [
       r'Response to initialize',
-      r'$/analyzerStatus notification',
+      'window/workDoneProgress/create request',
+      r'$/progress notification',
       r'textDocument/publishDiagnostics notification',
-      r'$/analyzerStatus notification',
+      r'$/progress notification',
       r'Response to dart/workspace/analysis/complete',
     ]);
   }
@@ -190,8 +201,9 @@
       // Initial empty workspace.
       r'Response to initialize',
       // Analysis of empty workspace.
-      r'$/analyzerStatus notification',
-      r'$/analyzerStatus notification',
+      'window/workDoneProgress/create request',
+      r'$/progress notification',
+      r'$/progress notification',
       r'Response to dart/workspace/analysis/complete',
     ]);
   }
diff --git a/pkg/analysis_server/test/lsp_over_legacy/abstract_lsp_over_legacy.dart b/pkg/analysis_server/test/lsp_over_legacy/abstract_lsp_over_legacy.dart
index 5bbd151..9cf4cba 100644
--- a/pkg/analysis_server/test/lsp_over_legacy/abstract_lsp_over_legacy.dart
+++ b/pkg/analysis_server/test/lsp_over_legacy/abstract_lsp_over_legacy.dart
@@ -402,9 +402,6 @@
   final Set<String> _priorityFiles = {};
 
   @override
-  Future<void> get currentAnalysis => waitForTasksFinished();
-
-  @override
   Future<void> closeFile(Uri uri) async {
     // closeFile should both remove the overlay and remove from priority files,
     // since that's equivalent of what the LSP document handlers do and shared
@@ -461,6 +458,9 @@
     await addOverlay(fromUri(uri), content);
   }
 
+  @override
+  Future<void> workspaceAnalysisComplete() => waitForTasksFinished();
+
   void _updatePriorityFiles() {
     setPriorityFiles(_priorityFiles.map(getFile).toList());
   }
diff --git a/pkg/analysis_server/test/shared/shared_edit_argument_tests.dart b/pkg/analysis_server/test/shared/shared_edit_argument_tests.dart
index 23b0e7b..b938535 100644
--- a/pkg/analysis_server/test/shared/shared_edit_argument_tests.dart
+++ b/pkg/analysis_server/test/shared/shared_edit_argument_tests.dart
@@ -940,7 +940,7 @@
     if (open) {
       await openFile(testFileUri, code.code);
     }
-    await currentAnalysis;
+    await workspaceAnalysisComplete();
     var verifier = await executeForEdits(
       () => editArgument(testFileUri, code.position.position, edit),
     );
@@ -976,7 +976,7 @@
     code = TestCode.parse(content);
     createFile(testFilePath, code.code);
     await initializeServer();
-    await currentAnalysis;
+    await workspaceAnalysisComplete();
 
     await expectLater(
       editArgument(testFileUri, code.position.position, edit),
diff --git a/pkg/analysis_server/test/shared/shared_editable_arguments_tests.dart b/pkg/analysis_server/test/shared/shared_editable_arguments_tests.dart
index c8939ce..57ccdb1 100644
--- a/pkg/analysis_server/test/shared/shared_editable_arguments_tests.dart
+++ b/pkg/analysis_server/test/shared/shared_editable_arguments_tests.dart
@@ -33,7 +33,7 @@
     createFile(testFilePath, code.code);
     await initializeServer();
     await open(testFileUri, code.code);
-    await currentAnalysis;
+    await workspaceAnalysisComplete();
     return await getEditableArguments(testFileUri, code.position.position);
   }
 
@@ -809,7 +809,7 @@
     createFile(textFilePath, content);
     await initializeServer();
     await openFile(textFileUri, content);
-    await currentAnalysis;
+    await workspaceAnalysisComplete();
     var result = await getEditableArguments(
       textFileUri,
       Position(line: 0, character: 0),
diff --git a/pkg/analysis_server/test/shared/shared_test_interface.dart b/pkg/analysis_server/test/shared/shared_test_interface.dart
index 72636c5..d19986f 100644
--- a/pkg/analysis_server/test/shared/shared_test_interface.dart
+++ b/pkg/analysis_server/test/shared/shared_test_interface.dart
@@ -15,11 +15,6 @@
 /// notification from [openFile] whereas when using the legacy server (even for
 /// LSP-over-Legacy tests) will send an `analysis.updateContent` request.
 abstract interface class SharedTestInterface {
-  /// A future that completes when the current analysis completes.
-  ///
-  /// If there is no analysis in progress, completes immediately.
-  Future<void> get currentAnalysis;
-
   /// Sets whether the test should fail if error diagnostics are generated.
   ///
   /// This is used to avoid accidentally including invalid code in tests but can
@@ -76,6 +71,11 @@
   /// Converts [filePath] to a file:/// URI.
   Uri toUri(String filePath);
 
+  /// Returns when current analysis completes.
+  ///
+  /// If there is no analysis in progress, completes immediately.
+  Future<void> workspaceAnalysisComplete();
+
   /// Writes a package_config.json for the package under test (considered
   /// 'package:test').
   void writeTestPackageConfig({