Unverified Commit b0810bc9 authored by Christopher Fujino's avatar Christopher Fujino Committed by GitHub

Fix analysis throwing string (#91435)

parent 5883a662
......@@ -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)}',
),
);
}
}));
......
......@@ -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);
......
......@@ -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) {
......
......@@ -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);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment