[flutter_releases] Flutter beta 2.10.0-0.3.pre Framework Cherrypicks (#97259)

* Fix analyze --watch command iterator (#96264)

* 'add branch flutter-2.8-candidate.16 to enabled_branches in .ci.yaml'

* 'Update Engine revision to 5ac30ef0c70b76c5d5b5465b0ad4f08d5433684f for beta release 2.10.0-0.3.pre'

* remove unnecessary ref to branch candidate

Co-authored-by: Jenn Magder <magder@google.com>
Co-authored-by: Kevin Chisholm <kevinjchisholm@google.com>
Co-authored-by: godofredoc <godofredoc@google.com>
diff --git a/bin/internal/engine.version b/bin/internal/engine.version
index 9c1fbfe..8fe8bb8 100644
--- a/bin/internal/engine.version
+++ b/bin/internal/engine.version
@@ -1 +1 @@
-74b74b10ea6764e373ca2bcb420fdb4a6f6b5918
+5ac30ef0c70b76c5d5b5465b0ad4f08d5433684f
diff --git a/packages/flutter_tools/lib/src/commands/analyze_continuously.dart b/packages/flutter_tools/lib/src/commands/analyze_continuously.dart
index b791ae2..705ed33 100644
--- a/packages/flutter_tools/lib/src/commands/analyze_continuously.dart
+++ b/packages/flutter_tools/lib/src/commands/analyze_continuously.dart
@@ -110,27 +110,29 @@
       logger.printStatus(terminal.clearScreen(), newline: false);
 
       // Remove errors for deleted files, sort, and print errors.
-      final List<AnalysisError> errors = <AnalysisError>[];
+      final List<AnalysisError> sortedErrors = <AnalysisError>[];
+      final List<String> pathsToRemove = <String>[];
       analysisErrors.forEach((String path, List<AnalysisError> errors) {
         if (fileSystem.isFileSync(path)) {
-          errors.addAll(errors);
+          sortedErrors.addAll(errors);
         } else {
-          analysisErrors.remove(path);
+          pathsToRemove.add(path);
         }
       });
+      analysisErrors.removeWhere((String path, _) => pathsToRemove.contains(path));
 
-      errors.sort();
+      sortedErrors.sort();
 
-      for (final AnalysisError error in errors) {
+      for (final AnalysisError error in sortedErrors) {
         logger.printStatus(error.toString());
         if (error.code != null) {
           logger.printTrace('error code: ${error.code}');
         }
       }
 
-      dumpErrors(errors.map<String>((AnalysisError error) => error.toLegacyString()));
+      dumpErrors(sortedErrors.map<String>((AnalysisError error) => error.toLegacyString()));
 
-      final int issueCount = errors.length;
+      final int issueCount = sortedErrors.length;
       final int issueDiff = issueCount - lastErrorCount;
       lastErrorCount = issueCount;
       final String seconds = (analysisTimer.elapsedMilliseconds / 1000.0).toStringAsFixed(2);
diff --git a/packages/flutter_tools/test/commands.shard/hermetic/analyze_continuously_test.dart b/packages/flutter_tools/test/commands.shard/hermetic/analyze_continuously_test.dart
index 8a0620e..35f63ca 100644
--- a/packages/flutter_tools/test/commands.shard/hermetic/analyze_continuously_test.dart
+++ b/packages/flutter_tools/test/commands.shard/hermetic/analyze_continuously_test.dart
@@ -6,6 +6,7 @@
 
 import 'dart:async';
 
+import 'package:fake_async/fake_async.dart';
 import 'package:file/memory.dart';
 import 'package:flutter_tools/src/artifacts.dart';
 import 'package:flutter_tools/src/base/common.dart';
@@ -174,7 +175,6 @@
   });
 
   testUsingContext('Can run AnalysisService with customized cache location', () async {
-    final Completer<void> completer = Completer<void>();
     final StreamController<List<int>> stdin = StreamController<List<int>>();
     final FakeProcessManager processManager = FakeProcessManager.list(
       <FakeCommand>[
@@ -188,7 +188,6 @@
             '--sdk',
             'HostArtifact.engineDartSdkPath',
           ],
-          completer: completer,
           stdin: IOSink(stdin.sink),
         ),
       ]);
@@ -212,6 +211,11 @@
   });
 
   testUsingContext('Can run AnalysisService with customized cache location --watch', () async {
+    final MemoryFileSystem fileSystem = MemoryFileSystem.test();
+    fileSystem.directory('directoryA').childFile('foo').createSync(recursive: true);
+
+    final BufferLogger logger = BufferLogger.test();
+
     final Completer<void> completer = Completer<void>();
     final StreamController<List<int>> stdin = StreamController<List<int>>();
     final FakeProcessManager processManager = FakeProcessManager.list(
@@ -226,8 +230,12 @@
             '--sdk',
             'HostArtifact.engineDartSdkPath',
           ],
-          completer: completer,
           stdin: IOSink(stdin.sink),
+          stdout: '''
+{"event":"server.status","params":{"analysis":{"isAnalyzing":true}}}
+{"event":"analysis.errors","params":{"file":"/directoryA/foo","errors":[{"type":"TestError","message":"It's an error.","severity":"warning","code":"500","location":{"file":"/directoryA/foo","startLine": 100,"startColumn":5,"offset":0}}]}}
+{"event":"server.status","params":{"analysis":{"isAnalyzing":false}}}
+'''
         ),
       ]);
 
@@ -235,17 +243,78 @@
     final AnalyzeCommand command = AnalyzeCommand(
       terminal: Terminal.test(),
       artifacts: artifacts,
-      logger: BufferLogger.test(),
+      logger: logger,
+      platform: FakePlatform(),
+      fileSystem: fileSystem,
+      processManager: processManager,
+    );
+
+    await FakeAsync().run((FakeAsync time) async {
+      final TestFlutterCommandRunner commandRunner = TestFlutterCommandRunner();
+      commandRunner.addCommand(command);
+      unawaited(commandRunner.run(<String>['analyze', '--watch']));
+
+      while (!logger.statusText.contains('analyzed 1 file')) {
+        time.flushMicrotasks();
+      }
+      completer.complete();
+      return completer.future;
+    });
+    expect(logger.statusText, contains("warning • It's an error • directoryA/foo:100:5 • 500"));
+    expect(logger.statusText, contains('1 issue found. (1 new)'));
+    expect(logger.errorText, isEmpty);
+    expect(processManager, hasNoRemainingExpectations);
+  });
+
+  testUsingContext('AnalysisService --watch skips errors from non-files', () async {
+    final BufferLogger logger = BufferLogger.test();
+    final Completer<void> completer = Completer<void>();
+    final StreamController<List<int>> stdin = StreamController<List<int>>();
+    final FakeProcessManager processManager = FakeProcessManager.list(
+        <FakeCommand>[
+          FakeCommand(
+              command: const <String>[
+                'HostArtifact.engineDartSdkPath/bin/dart',
+                '--disable-dart-dev',
+                'HostArtifact.engineDartSdkPath/bin/snapshots/analysis_server.dart.snapshot',
+                '--disable-server-feature-completion',
+                '--disable-server-feature-search',
+                '--sdk',
+                'HostArtifact.engineDartSdkPath',
+              ],
+              stdin: IOSink(stdin.sink),
+              stdout: '''
+{"event":"server.status","params":{"analysis":{"isAnalyzing":true}}}
+{"event":"analysis.errors","params":{"file":"/directoryA/bar","errors":[{"type":"TestError","message":"It's an error.","severity":"warning","code":"500","location":{"file":"/directoryA/bar","startLine":100,"startColumn":5,"offset":0}}]}}
+{"event":"server.status","params":{"analysis":{"isAnalyzing":false}}}
+'''
+          ),
+        ]);
+
+    final Artifacts artifacts = Artifacts.test();
+    final AnalyzeCommand command = AnalyzeCommand(
+      terminal: Terminal.test(),
+      artifacts: artifacts,
+      logger: logger,
       platform: FakePlatform(),
       fileSystem: MemoryFileSystem.test(),
       processManager: processManager,
     );
 
-    final TestFlutterCommandRunner commandRunner = TestFlutterCommandRunner();
-    commandRunner.addCommand(command);
-    unawaited(commandRunner.run(<String>['analyze', '--watch']));
-    await stdin.stream.first;
+    await FakeAsync().run((FakeAsync time) async {
+      final TestFlutterCommandRunner commandRunner = TestFlutterCommandRunner();
+      commandRunner.addCommand(command);
+      unawaited(commandRunner.run(<String>['analyze', '--watch']));
 
+      while (!logger.statusText.contains('analyzed 1 file')) {
+        time.flushMicrotasks();
+      }
+      completer.complete();
+      return completer.future;
+    });
+
+    expect(logger.statusText, contains('No issues found!'));
+    expect(logger.errorText, isEmpty);
     expect(processManager, hasNoRemainingExpectations);
   });
 }