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

[flutter_tools] handle FileSystemException trying to delete temp directory...

[flutter_tools] handle FileSystemException trying to delete temp directory from core_devices.dart (#140415)

Fixes https://github.com/flutter/flutter/issues/140416, the top crasher on stable/3.16.4
parent ea6017de
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
import 'package:meta/meta.dart'; import 'package:meta/meta.dart';
import 'package:process/process.dart'; import 'package:process/process.dart';
import '../base/error_handling_io.dart';
import '../base/file_system.dart'; import '../base/file_system.dart';
import '../base/io.dart'; import '../base/io.dart';
import '../base/logger.dart'; import '../base/logger.dart';
...@@ -101,7 +102,7 @@ class IOSCoreDeviceControl { ...@@ -101,7 +102,7 @@ class IOSCoreDeviceControl {
_logger.printError('Error executing devicectl: $err'); _logger.printError('Error executing devicectl: $err');
return <Object?>[]; return <Object?>[];
} finally { } finally {
tempDirectory.deleteSync(recursive: true); ErrorHandlingFileSystem.deleteIfExists(tempDirectory, recursive: true);
} }
} }
......
...@@ -111,7 +111,7 @@ void main() { ...@@ -111,7 +111,7 @@ void main() {
exceptionHandler = FileExceptionHandler(); exceptionHandler = FileExceptionHandler();
}); });
testWithoutContext('bypasses error handling when withAllowedFailure is used', () { testWithoutContext('bypasses error handling when noExitOnFailure is used', () {
final ErrorHandlingFileSystem fileSystem = ErrorHandlingFileSystem( final ErrorHandlingFileSystem fileSystem = ErrorHandlingFileSystem(
delegate: MemoryFileSystem.test(opHandle: exceptionHandler.opHandle), delegate: MemoryFileSystem.test(opHandle: exceptionHandler.opHandle),
platform: windowsPlatform, platform: windowsPlatform,
...@@ -123,9 +123,9 @@ void main() { ...@@ -123,9 +123,9 @@ void main() {
FileSystemOp.write, FileSystemOp.write,
FileSystemException('', file.path, const OSError('', kUserPermissionDenied)), FileSystemException('', file.path, const OSError('', kUserPermissionDenied)),
); );
final Matcher throwsNonToolExit = throwsA(isNot(isA<ToolExit>()));
expect(() => ErrorHandlingFileSystem.noExitOnFailure( expect(() => ErrorHandlingFileSystem.noExitOnFailure(
() => file.writeAsStringSync('')), throwsException); () => file.writeAsStringSync('')), throwsNonToolExit);
// nesting does not unconditionally re-enable errors. // nesting does not unconditionally re-enable errors.
expect(() { expect(() {
...@@ -133,7 +133,7 @@ void main() { ...@@ -133,7 +133,7 @@ void main() {
ErrorHandlingFileSystem.noExitOnFailure(() { }); ErrorHandlingFileSystem.noExitOnFailure(() { });
file.writeAsStringSync(''); file.writeAsStringSync('');
}); });
}, throwsException); }, throwsNonToolExit);
// Check that state does not leak. // Check that state does not leak.
expect(() => file.writeAsStringSync(''), throwsToolExit()); expect(() => file.writeAsStringSync(''), throwsToolExit());
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
import 'package:file/memory.dart'; import 'package:file/memory.dart';
import 'package:file_testing/file_testing.dart'; import 'package:file_testing/file_testing.dart';
import 'package:flutter_tools/src/base/file_system.dart'; import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/base/io.dart';
import 'package:flutter_tools/src/base/logger.dart'; import 'package:flutter_tools/src/base/logger.dart';
import 'package:flutter_tools/src/base/version.dart'; import 'package:flutter_tools/src/base/version.dart';
import 'package:flutter_tools/src/ios/core_devices.dart'; import 'package:flutter_tools/src/ios/core_devices.dart';
...@@ -35,7 +36,7 @@ void main() { ...@@ -35,7 +36,7 @@ void main() {
version: Version(14, 0, 0), version: Version(14, 0, 0),
); );
xcode = Xcode.test( xcode = Xcode.test(
processManager: FakeProcessManager.any(), processManager: fakeProcessManager,
xcodeProjectInterpreter: xcodeProjectInterpreter, xcodeProjectInterpreter: xcodeProjectInterpreter,
); );
deviceControl = IOSCoreDeviceControl( deviceControl = IOSCoreDeviceControl(
...@@ -86,6 +87,7 @@ void main() { ...@@ -86,6 +87,7 @@ void main() {
setUp(() { setUp(() {
logger = BufferLogger.test(); logger = BufferLogger.test();
fakeProcessManager = FakeProcessManager.empty(); fakeProcessManager = FakeProcessManager.empty();
// TODO(fujino): make this FakeProcessManager.empty()
xcode = Xcode.test(processManager: FakeProcessManager.any()); xcode = Xcode.test(processManager: FakeProcessManager.any());
deviceControl = IOSCoreDeviceControl( deviceControl = IOSCoreDeviceControl(
logger: logger, logger: logger,
...@@ -1322,6 +1324,37 @@ invalid JSON ...@@ -1322,6 +1324,37 @@ invalid JSON
}); });
group('list devices', () { group('list devices', () {
testWithoutContext('Handles FileSystemException deleting temp directory', () async {
final Directory tempDir = fileSystem.systemTempDirectory
.childDirectory('core_devices.rand0');
final File tempFile = tempDir.childFile('core_device_list.json');
final List<String> args = <String>[
'xcrun',
'devicectl',
'list',
'devices',
'--timeout',
'5',
'--json-output',
tempFile.path,
];
fakeProcessManager.addCommand(FakeCommand(
command: args,
onRun: () {
// Simulate that this command threw and simulataneously the OS
// deleted the temp directory
expect(tempFile, exists);
tempDir.deleteSync(recursive: true);
expect(tempFile, isNot(exists));
throw ProcessException(args.first, args.sublist(1));
},
));
await deviceControl.getCoreDevices();
expect(logger.errorText, contains('Error executing devicectl: ProcessException'));
expect(fakeProcessManager, hasNoRemainingExpectations);
});
testWithoutContext('No devices', () async { testWithoutContext('No devices', () async {
const String deviceControlOutput = ''' const String deviceControlOutput = '''
{ {
......
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