diff --git a/packages/flutter_tools/lib/src/commands/analyze_continuously.dart b/packages/flutter_tools/lib/src/commands/analyze_continuously.dart index b791ae2b3d..705ed33793 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 @@ class AnalyzeContinuously extends AnalyzeBase { logger.printStatus(terminal.clearScreen(), newline: false); // Remove errors for deleted files, sort, and print errors. - final List errors = []; + final List sortedErrors = []; + final List pathsToRemove = []; analysisErrors.forEach((String path, List 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((AnalysisError error) => error.toLegacyString())); + dumpErrors(sortedErrors.map((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 8a0620e4dc..35f63cafcd 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 @@ void main() { }); testUsingContext('Can run AnalysisService with customized cache location', () async { - final Completer completer = Completer(); final StreamController> stdin = StreamController>(); final FakeProcessManager processManager = FakeProcessManager.list( [ @@ -188,7 +188,6 @@ void main() { '--sdk', 'HostArtifact.engineDartSdkPath', ], - completer: completer, stdin: IOSink(stdin.sink), ), ]); @@ -212,6 +211,11 @@ void main() { }); 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 completer = Completer(); final StreamController> stdin = StreamController>(); final FakeProcessManager processManager = FakeProcessManager.list( @@ -226,8 +230,12 @@ void main() { '--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 @@ void main() { 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(['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 completer = Completer(); + final StreamController> stdin = StreamController>(); + final FakeProcessManager processManager = FakeProcessManager.list( + [ + FakeCommand( + command: const [ + '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(['analyze', '--watch'])); - await stdin.stream.first; + await FakeAsync().run((FakeAsync time) async { + final TestFlutterCommandRunner commandRunner = TestFlutterCommandRunner(); + commandRunner.addCommand(command); + unawaited(commandRunner.run(['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); }); }