Unverified Commit 9248fda4 authored by Jonah Williams's avatar Jonah Williams Committed by GitHub

[flutter_tools] add EACCES to list of immediate exit tool conditions (#65125)

Similar to the permission denied error on Windows, this is not resolvable by the tool.
parent 48f1a0a8
......@@ -357,15 +357,15 @@ Future<T> _run<T>(Future<T> Function() op, {
} on FileSystemException catch (e) {
if (platform.isWindows) {
_handleWindowsException(e, failureMessage, e.osError?.errorCode ?? 0);
} else if (platform.isLinux) {
_handleLinuxException(e, failureMessage, e.osError?.errorCode ?? 0);
} else if (platform.isLinux || platform.isMacOS) {
_handlePosixException(e, failureMessage, e.osError?.errorCode ?? 0);
}
rethrow;
} on io.ProcessException catch (e) {
if (platform.isWindows) {
_handleWindowsException(e, failureMessage, e.errorCode ?? 0);
} else if (platform.isLinux) {
_handleLinuxException(e, failureMessage, e.errorCode ?? 0);
} else if (platform.isLinux || platform.isMacOS) {
_handlePosixException(e, failureMessage, e.errorCode ?? 0);
}
rethrow;
}
......@@ -381,15 +381,15 @@ T _runSync<T>(T Function() op, {
} on FileSystemException catch (e) {
if (platform.isWindows) {
_handleWindowsException(e, failureMessage, e.osError?.errorCode ?? 0);
} else if (platform.isLinux) {
_handleLinuxException(e, failureMessage, e.osError?.errorCode ?? 0);
} else if (platform.isLinux || platform.isMacOS) {
_handlePosixException(e, failureMessage, e.osError?.errorCode ?? 0);
}
rethrow;
} on io.ProcessException catch (e) {
if (platform.isWindows) {
_handleWindowsException(e, failureMessage, e.errorCode ?? 0);
} else if (platform.isLinux) {
_handleLinuxException(e, failureMessage, e.errorCode ?? 0);
} else if (platform.isLinux || platform.isMacOS) {
_handlePosixException(e, failureMessage, e.errorCode ?? 0);
}
rethrow;
}
......@@ -490,11 +490,13 @@ class ErrorHandlingProcessManager extends ProcessManager {
}
}
void _handleLinuxException(Exception e, String message, int errorCode) {
void _handlePosixException(Exception e, String message, int errorCode) {
// From:
// 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 enospc = 28;
const int eacces = 13;
// Catch errors and bail when:
switch (errorCode) {
case enospc:
......@@ -504,6 +506,13 @@ void _handleLinuxException(Exception e, String message, int errorCode) {
'Free up space and try again.',
);
break;
case eacces:
throwToolExit(
'$message. The flutter tool cannot access the file.\n'
'Please ensure that the SDK and/or project is installed in a location '
'that has read/write permissions for the current user.'
);
break;
default:
// Caller must rethrow the exception.
break;
......
......@@ -28,6 +28,11 @@ final Platform linuxPlatform = FakePlatform(
environment: <String, String>{}
);
final Platform macOSPlatform = FakePlatform(
operatingSystem: 'macos',
environment: <String, String>{}
);
void setupWriteMocks({
FileSystem mockFileSystem,
ErrorHandlingFileSystem fs,
......@@ -194,6 +199,7 @@ void main() {
group('throws ToolExit on Linux', () {
const int enospc = 28;
const int eacces = 13;
MockFileSystem mockFileSystem;
ErrorHandlingFileSystem fs;
......@@ -206,6 +212,103 @@ void main() {
when(mockFileSystem.path).thenReturn(MockPathContext());
});
testWithoutContext('when access is denied', () async {
setupWriteMocks(
mockFileSystem: mockFileSystem,
fs: fs,
errorCode: eacces,
);
final File file = fs.file('file');
const String expectedMessage = 'The flutter tool cannot access the file';
expect(() async => await file.writeAsBytes(<int>[0]),
throwsToolExit(message: expectedMessage));
expect(() async => await file.writeAsString(''),
throwsToolExit(message: expectedMessage));
expect(() => file.writeAsBytesSync(<int>[0]),
throwsToolExit(message: expectedMessage));
expect(() => file.writeAsStringSync(''),
throwsToolExit(message: expectedMessage));
expect(() => file.openSync(),
throwsToolExit(message: expectedMessage));
});
testWithoutContext('when writing to a full device', () async {
setupWriteMocks(
mockFileSystem: mockFileSystem,
fs: fs,
errorCode: enospc,
);
final File file = fs.file('file');
const String expectedMessage = 'The target device is full';
expect(() async => await file.writeAsBytes(<int>[0]),
throwsToolExit(message: expectedMessage));
expect(() async => await file.writeAsString(''),
throwsToolExit(message: expectedMessage));
expect(() => file.writeAsBytesSync(<int>[0]),
throwsToolExit(message: expectedMessage));
expect(() => file.writeAsStringSync(''),
throwsToolExit(message: expectedMessage));
});
testWithoutContext('when creating a temporary dir on a full device', () async {
setupDirectoryMocks(
mockFileSystem: mockFileSystem,
fs: fs,
errorCode: enospc,
);
final Directory directory = fs.directory('directory');
const String expectedMessage = 'The target device is full';
expect(() async => await directory.createTemp('prefix'),
throwsToolExit(message: expectedMessage));
expect(() => directory.createTempSync('prefix'),
throwsToolExit(message: expectedMessage));
});
});
group('throws ToolExit on macOS', () {
const int enospc = 28;
const int eacces = 13;
MockFileSystem mockFileSystem;
ErrorHandlingFileSystem fs;
setUp(() {
mockFileSystem = MockFileSystem();
fs = ErrorHandlingFileSystem(
delegate: mockFileSystem,
platform: macOSPlatform,
);
when(mockFileSystem.path).thenReturn(MockPathContext());
});
testWithoutContext('when access is denied', () async {
setupWriteMocks(
mockFileSystem: mockFileSystem,
fs: fs,
errorCode: eacces,
);
final File file = fs.file('file');
const String expectedMessage = 'The flutter tool cannot access the file';
expect(() async => await file.writeAsBytes(<int>[0]),
throwsToolExit(message: expectedMessage));
expect(() async => await file.writeAsString(''),
throwsToolExit(message: expectedMessage));
expect(() => file.writeAsBytesSync(<int>[0]),
throwsToolExit(message: expectedMessage));
expect(() => file.writeAsStringSync(''),
throwsToolExit(message: expectedMessage));
expect(() => file.openSync(),
throwsToolExit(message: expectedMessage));
});
testWithoutContext('when writing to a full device', () async {
setupWriteMocks(
mockFileSystem: mockFileSystem,
......@@ -365,6 +468,7 @@ void main() {
group('ProcessManager on linux throws tool exit', () {
const int enospc = 28;
const int eacces = 13;
test('when writing to a full device', () {
final MockProcessManager mockProcessManager = MockProcessManager();
......@@ -386,6 +490,74 @@ void main() {
expect(() => processManager.runSync(<String>['foo']),
throwsToolExit(message: expectedMessage));
});
test('when permissions are denied', () {
final MockProcessManager mockProcessManager = MockProcessManager();
final ProcessManager processManager = ErrorHandlingProcessManager(
delegate: mockProcessManager,
platform: linuxPlatform,
);
setupProcessManagerMocks(mockProcessManager, eacces);
const String expectedMessage = 'The flutter tool cannot access the file';
expect(() => processManager.canRun('foo'),
throwsToolExit(message: expectedMessage));
expect(() => processManager.killPid(1),
throwsToolExit(message: expectedMessage));
expect(() async => await processManager.start(<String>['foo']),
throwsToolExit(message: expectedMessage));
expect(() async => await processManager.run(<String>['foo']),
throwsToolExit(message: expectedMessage));
expect(() => processManager.runSync(<String>['foo']),
throwsToolExit(message: expectedMessage));
});
});
group('ProcessManager on macOS throws tool exit', () {
const int enospc = 28;
const int eacces = 13;
test('when writing to a full device', () {
final MockProcessManager mockProcessManager = MockProcessManager();
final ProcessManager processManager = ErrorHandlingProcessManager(
delegate: mockProcessManager,
platform: macOSPlatform,
);
setupProcessManagerMocks(mockProcessManager, enospc);
const String expectedMessage = 'The target device is full';
expect(() => processManager.canRun('foo'),
throwsToolExit(message: expectedMessage));
expect(() => processManager.killPid(1),
throwsToolExit(message: expectedMessage));
expect(() async => await processManager.start(<String>['foo']),
throwsToolExit(message: expectedMessage));
expect(() async => await processManager.run(<String>['foo']),
throwsToolExit(message: expectedMessage));
expect(() => processManager.runSync(<String>['foo']),
throwsToolExit(message: expectedMessage));
});
test('when permissions are denied', () {
final MockProcessManager mockProcessManager = MockProcessManager();
final ProcessManager processManager = ErrorHandlingProcessManager(
delegate: mockProcessManager,
platform: linuxPlatform,
);
setupProcessManagerMocks(mockProcessManager, eacces);
const String expectedMessage = 'The flutter tool cannot access the file';
expect(() => processManager.canRun('foo'),
throwsToolExit(message: expectedMessage));
expect(() => processManager.killPid(1),
throwsToolExit(message: expectedMessage));
expect(() async => await processManager.start(<String>['foo']),
throwsToolExit(message: expectedMessage));
expect(() async => await processManager.run(<String>['foo']),
throwsToolExit(message: expectedMessage));
expect(() => processManager.runSync(<String>['foo']),
throwsToolExit(message: expectedMessage));
});
});
}
......
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