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

[flutter_tools] check if stream is open before sending message in ios device (#99947)

parent 2386fd90
......@@ -647,14 +647,25 @@ class IOSDeviceLogReader extends DeviceLogReader {
// Logging from the dart code has no prefixing metadata.
final RegExp _debuggerLoggingRegex = RegExp(r'^\S* \S* \S*\[[0-9:]*] (.*)');
late final StreamController<String> _linesController = StreamController<String>.broadcast(
@visibleForTesting
late final StreamController<String> linesController = StreamController<String>.broadcast(
onListen: _listenToSysLog,
onCancel: dispose,
);
// Sometimes (race condition?) we try to send a log after the controller has
// been closed. See https://github.com/flutter/flutter/issues/99021 for more
// context.
void _addToLinesController(String message) {
if (!linesController.isClosed) {
linesController.add(message);
}
}
final List<StreamSubscription<void>> _loggingSubscriptions = <StreamSubscription<void>>[];
@override
Stream<String> get logLines => _linesController.stream;
Stream<String> get logLines => linesController.stream;
@override
FlutterVmService? get connectedVMService => _connectedVMService;
......@@ -694,7 +705,7 @@ class IOSDeviceLogReader extends DeviceLogReader {
}
final String message = processVmServiceMessage(event);
if (message.isNotEmpty) {
_linesController.add(message);
_addToLinesController(message);
}
}
......@@ -717,9 +728,9 @@ class IOSDeviceLogReader extends DeviceLogReader {
}
// Add the debugger logs to the controller created on initialization.
_loggingSubscriptions.add(debugger.logLines.listen(
(String line) => _linesController.add(_debuggerLineHandler(line)),
onError: _linesController.addError,
onDone: _linesController.close,
(String line) => _addToLinesController(_debuggerLineHandler(line)),
onError: linesController.addError,
onDone: linesController.close,
cancelOnError: true,
));
}
......@@ -737,8 +748,8 @@ class IOSDeviceLogReader extends DeviceLogReader {
process.stdout.transform<String>(utf8.decoder).transform<String>(const LineSplitter()).listen(_newSyslogLineHandler());
process.stderr.transform<String>(utf8.decoder).transform<String>(const LineSplitter()).listen(_newSyslogLineHandler());
process.exitCode.whenComplete(() {
if (_linesController.hasListener) {
_linesController.close();
if (linesController.hasListener) {
linesController.close();
}
});
assert(idevicesyslogProcess == null);
......@@ -761,7 +772,7 @@ class IOSDeviceLogReader extends DeviceLogReader {
return (String line) {
if (printing) {
if (!_anyLineRegex.hasMatch(line)) {
_linesController.add(decodeSyslog(line));
_addToLinesController(decodeSyslog(line));
return;
}
......@@ -773,7 +784,7 @@ class IOSDeviceLogReader extends DeviceLogReader {
if (match != null) {
final String logLine = line.substring(match.end);
// Only display the log line after the initial device and executable information.
_linesController.add(decodeSyslog(logLine));
_addToLinesController(decodeSyslog(logLine));
printing = true;
}
......
......@@ -383,7 +383,7 @@ class IOSDeployDebugger {
// To avoid all lines being double spaced, if the last line from the
// debugger was not an empty line, skip this empty line.
// This will still cause "legit" logged newlines to be doubled...
} else {
} else if (!_debuggerOutput.isClosed) {
_debuggerOutput.add(line);
}
lastLineFromDebugger = line;
......@@ -413,11 +413,15 @@ class IOSDeployDebugger {
} on ProcessException catch (exception, stackTrace) {
_logger.printTrace('ios-deploy failed: $exception');
_debuggerState = _IOSDeployDebuggerState.detached;
_debuggerOutput.addError(exception, stackTrace);
if (!_debuggerOutput.isClosed) {
_debuggerOutput.addError(exception, stackTrace);
}
} on ArgumentError catch (exception, stackTrace) {
_logger.printTrace('ios-deploy failed: $exception');
_debuggerState = _IOSDeployDebuggerState.detached;
_debuggerOutput.addError(exception, stackTrace);
if (!_debuggerOutput.isClosed) {
_debuggerOutput.addError(exception, stackTrace);
}
}
// Wait until the debugger attaches, or the attempt fails.
return debuggerCompleter.future;
......
......@@ -7,6 +7,7 @@
import 'dart:async';
import 'package:flutter_tools/src/artifacts.dart';
import 'package:flutter_tools/src/base/async_guard.dart';
import 'package:flutter_tools/src/base/logger.dart';
import 'package:flutter_tools/src/cache.dart';
import 'package:flutter_tools/src/convert.dart';
......@@ -309,6 +310,45 @@ Runner(libsystem_asl.dylib)[297] <Notice>: libMobileGestalt
logReader.dispose();
expect(iosDeployDebugger.detached, true);
});
testWithoutContext('Does not throw if debuggerStream set after logReader closed', () async {
final Stream<String> debuggingLogs = Stream<String>.fromIterable(<String>[
'2020-09-15 19:15:10.931434-0700 Runner[541:226276] Did finish launching.',
'2020-09-15 19:15:10.931434-0700 Runner[541:226276] [Category] Did finish launching from logging category.',
'stderr from dart',
'',
]);
final IOSDeviceLogReader logReader = IOSDeviceLogReader.test(
iMobileDevice: IMobileDevice(
artifacts: artifacts,
processManager: processManager,
cache: fakeCache,
logger: logger,
),
useSyslog: false,
);
Object exception;
StackTrace trace;
await asyncGuard(
() async {
await logReader.linesController.close();
final FakeIOSDeployDebugger iosDeployDebugger = FakeIOSDeployDebugger();
iosDeployDebugger.logLines = debuggingLogs;
logReader.debuggerStream = iosDeployDebugger;
await logReader.logLines.drain<void>();
},
onError: (Object err, StackTrace stackTrace) {
exception = err;
trace = stackTrace;
}
);
expect(
exception,
isNull,
reason: trace.toString(),
);
});
});
}
......
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