Fix analysis throwing string (#91435)

This commit is contained in:
Christopher Fujino 2021-10-29 17:02:05 -07:00 committed by GitHub
parent 5883a6628d
commit b0810bc939
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 114 additions and 3 deletions

View File

@ -125,7 +125,12 @@ class AnalyzeOnce extends AnalyzeBase {
// Completing the future in the callback can't fail.
unawaited(server.onExit.then<void>((int exitCode) {
if (!analysisCompleter.isCompleted) {
analysisCompleter.completeError('analysis server exited: $exitCode');
analysisCompleter.completeError(
// Include the last 20 lines of server output in exception message
Exception(
'analysis server exited with code $exitCode and output:\n${server.getLogs(20)}',
),
);
}
}));

View File

@ -79,7 +79,7 @@ class AnalysisServer {
final Stream<String> errorStream = _process!.stderr
.transform<String>(utf8.decoder)
.transform<String>(const LineSplitter());
errorStream.listen(_logger.printError);
errorStream.listen(_handleError);
final Stream<String> inStream = _process!.stdout
.transform<String>(utf8.decoder)
@ -94,6 +94,28 @@ class AnalysisServer {
<String, dynamic>{'included': directories, 'excluded': <String>[]});
}
final List<String> _logs = <String>[];
/// Aggregated STDOUT and STDERR logs from the server.
///
/// This can be surfaced to the user if the server crashes. If [tail] is null,
/// returns all logs, else only the last [tail] lines.
String getLogs([int? tail]) {
if (tail == null) {
return _logs.join('\n');
}
// Since List doesn't implement a .tail() method, we reverse it then use
// .take()
final Iterable<String> reversedLogs = _logs.reversed;
final List<String> firstTailLogs = reversedLogs.take(tail).toList();
return firstTailLogs.reversed.join('\n');
}
void _handleError(String message) {
_logs.add('[stderr] $message');
_logger.printError(message);
}
bool get didServerErrorOccur => _didServerErrorOccur;
Stream<bool> get onAnalyzing => _analyzingController.stream;
@ -113,6 +135,7 @@ class AnalysisServer {
}
void _handleServerResponse(String line) {
_logs.add('[stdout] $line');
_logger.printTrace('<== $line');
final dynamic response = json.decode(line);

View File

@ -312,7 +312,6 @@ class FlutterCommandRunner extends CommandRunner<void> {
return <String>[];
}
final List<String> projectPaths = globals.fs.directory(rootPath)
.listSync(followLinks: false)
.expand((FileSystemEntity entity) {

View File

@ -4,15 +4,25 @@
// @dart = 2.8
import 'package:args/command_runner.dart';
import 'package:file/file.dart';
import 'package:file/memory.dart';
import 'package:flutter_tools/src/artifacts.dart';
import 'package:flutter_tools/src/base/logger.dart';
import 'package:flutter_tools/src/base/platform.dart';
import 'package:flutter_tools/src/base/terminal.dart';
import 'package:flutter_tools/src/cache.dart';
import 'package:flutter_tools/src/commands/analyze.dart';
import 'package:flutter_tools/src/commands/analyze_base.dart';
import 'package:flutter_tools/src/dart/analysis.dart';
import '../../src/common.dart';
import '../../src/context.dart';
import '../../src/fake_process_manager.dart';
import '../../src/test_flutter_command_runner.dart';
const String _kFlutterRoot = '/data/flutter';
const int SIGABRT = -6;
void main() {
testWithoutContext('analyze generate correct errors message', () async {
@ -35,6 +45,80 @@ void main() {
);
});
group('analyze command', () {
FileSystem fileSystem;
Platform platform;
BufferLogger logger;
FakeProcessManager processManager;
Terminal terminal;
AnalyzeCommand command;
CommandRunner<void> runner;
setUpAll(() {
Cache.disableLocking();
});
setUp(() {
fileSystem = MemoryFileSystem.test();
platform = FakePlatform();
logger = BufferLogger.test();
processManager = FakeProcessManager.empty();
terminal = Terminal.test();
command = AnalyzeCommand(
artifacts: Artifacts.test(),
fileSystem: fileSystem,
logger: logger,
platform: platform,
processManager: processManager,
terminal: terminal,
);
runner = createTestCommandRunner(command);
// Setup repo roots
const String homePath = '/home/user/flutter';
Cache.flutterRoot = homePath;
for (final String dir in <String>['dev', 'examples', 'packages']) {
fileSystem.directory(homePath).childDirectory(dir).createSync(recursive: true);
}
});
testUsingContext('SIGABRT throws Exception', () async {
const String stderr = 'Something bad happened!';
processManager.addCommands(
<FakeCommand>[
const FakeCommand(
// artifact paths are from Artifacts.test() and stable
command: <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',
],
exitCode: SIGABRT,
stderr: stderr,
),
],
);
await expectLater(
runner.run(<String>['analyze']),
throwsA(
isA<Exception>().having(
(Exception e) => e.toString(),
'description',
contains('analysis server exited with code $SIGABRT and output:\n[stderr] $stderr'),
),
),
);
},
overrides: <Type, Generator>{
FileSystem: () => fileSystem,
ProcessManager: () => processManager,
});
});
testWithoutContext('analyze inRepo', () {
final FileSystem fileSystem = MemoryFileSystem.test();
fileSystem.directory(_kFlutterRoot).createSync(recursive: true);