From 2c6f862bf83fff5e1d7b0d71a80304f488da36ad Mon Sep 17 00:00:00 2001 From: Jonah Williams Date: Mon, 21 Sep 2020 11:32:07 -0700 Subject: [PATCH] [flutter_tools] add EPERM to set of immediate exit errors (#66159) --- .../lib/src/base/error_handling_io.dart | 34 +++++++++++- .../base/error_handling_io_test.dart | 52 ++++++++++++++++++- 2 files changed, 84 insertions(+), 2 deletions(-) diff --git a/packages/flutter_tools/lib/src/base/error_handling_io.dart b/packages/flutter_tools/lib/src/base/error_handling_io.dart index e4c1d8628b..88a798c50a 100644 --- a/packages/flutter_tools/lib/src/base/error_handling_io.dart +++ b/packages/flutter_tools/lib/src/base/error_handling_io.dart @@ -297,6 +297,36 @@ class ErrorHandlingDirectory ); } + @override + Future create({bool recursive = false}) { + return _run( + () async => wrap(await delegate.create(recursive: recursive)), + platform: _platform, + failureMessage: + 'Flutter failed to create a directory at "${delegate.path}"', + ); + } + + @override + Future delete({bool recursive = false}) { + return _run( + () async => wrap(fileSystem.directory((await delegate.delete(recursive: recursive)).path)), + platform: _platform, + failureMessage: + 'Flutter failed to delete a directory at "${delegate.path}"', + ); + } + + @override + void deleteSync({bool recursive = false}) { + return _runSync( + () => delegate.deleteSync(recursive: recursive), + platform: _platform, + failureMessage: + 'Flutter failed to delete a directory at "${delegate.path}"', + ); + } + @override String toString() => delegate.toString(); } @@ -495,6 +525,7 @@ void _handlePosixException(Exception e, String message, int errorCode) { // https://github.com/torvalds/linux/blob/master/include/uapi/asm-generic/errno.h // https://github.com/torvalds/linux/blob/master/include/uapi/asm-generic/errno-base.h // https://github.com/apple/darwin-xnu/blob/master/bsd/dev/dtrace/scripts/errno.d + const int eperm = 1; const int enospc = 28; const int eacces = 13; // Catch errors and bail when: @@ -506,9 +537,10 @@ void _handlePosixException(Exception e, String message, int errorCode) { 'Free up space and try again.', ); break; + case eperm: case eacces: throwToolExit( - '$message. The flutter tool cannot access the file.\n' + '$message. The flutter tool cannot access the file or directory.\n' 'Please ensure that the SDK and/or project is installed in a location ' 'that has read/write permissions for the current user.' ); diff --git a/packages/flutter_tools/test/general.shard/base/error_handling_io_test.dart b/packages/flutter_tools/test/general.shard/base/error_handling_io_test.dart index 9d6f1b0758..05b93b0396 100644 --- a/packages/flutter_tools/test/general.shard/base/error_handling_io_test.dart +++ b/packages/flutter_tools/test/general.shard/base/error_handling_io_test.dart @@ -85,6 +85,14 @@ void setupDirectoryMocks({ .thenThrow(FileSystemException('', '', OSError('', errorCode))); when(mockDirectory.createSync(recursive: anyNamed('recursive'))) .thenThrow(FileSystemException('', '', OSError('', errorCode))); + when(mockDirectory.create()) + .thenThrow(FileSystemException('', '', OSError('', errorCode))); + when(mockDirectory.createSync()) + .thenThrow(FileSystemException('', '', OSError('', errorCode))); + when(mockDirectory.delete()) + .thenThrow(FileSystemException('', '', OSError('', errorCode))); + when(mockDirectory.deleteSync()) + .thenThrow(FileSystemException('', '', OSError('', errorCode))); } void main() { @@ -198,6 +206,7 @@ void main() { }); group('throws ToolExit on Linux', () { + const int eperm = 1; const int enospc = 28; const int eacces = 13; MockFileSystem mockFileSystem; @@ -221,7 +230,7 @@ void main() { final File file = fs.file('file'); - const String expectedMessage = 'The flutter tool cannot access the file'; + const String expectedMessage = 'The flutter tool cannot access the file or directory'; expect(() async => await file.writeAsBytes([0]), throwsToolExit(message: expectedMessage)); expect(() async => await file.writeAsString(''), @@ -234,6 +243,26 @@ void main() { throwsToolExit(message: expectedMessage)); }); + testWithoutContext('when access is denied for directories', () async { + setupDirectoryMocks( + mockFileSystem: mockFileSystem, + fs: fs, + errorCode: eperm, + ); + + final Directory directory = fs.directory('file'); + + const String expectedMessage = 'The flutter tool cannot access the file or directory'; + expect(() async => await directory.create(), + throwsToolExit(message: expectedMessage)); + expect(() async => await directory.delete(), + throwsToolExit(message: expectedMessage)); + expect(() => directory.createSync(), + throwsToolExit(message: expectedMessage)); + expect(() => directory.deleteSync(), + throwsToolExit(message: expectedMessage)); + }); + testWithoutContext('when writing to a full device', () async { setupWriteMocks( mockFileSystem: mockFileSystem, @@ -273,6 +302,7 @@ void main() { group('throws ToolExit on macOS', () { + const int eperm = 1; const int enospc = 28; const int eacces = 13; MockFileSystem mockFileSystem; @@ -309,6 +339,26 @@ void main() { throwsToolExit(message: expectedMessage)); }); + testWithoutContext('when access is denied for directories', () async { + setupDirectoryMocks( + mockFileSystem: mockFileSystem, + fs: fs, + errorCode: eperm, + ); + + final Directory directory = fs.directory('file'); + + const String expectedMessage = 'The flutter tool cannot access the file or directory'; + expect(() async => await directory.create(), + throwsToolExit(message: expectedMessage)); + expect(() async => await directory.delete(), + throwsToolExit(message: expectedMessage)); + expect(() => directory.createSync(), + throwsToolExit(message: expectedMessage)); + expect(() => directory.deleteSync(), + throwsToolExit(message: expectedMessage)); + }); + testWithoutContext('when writing to a full device', () async { setupWriteMocks( mockFileSystem: mockFileSystem,