Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Sign in
Toggle navigation
F
Front-End
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
abdullh.alsoleman
Front-End
Commits
c1c12aa3
Unverified
Commit
c1c12aa3
authored
May 11, 2021
by
Jenn Magder
Committed by
GitHub
May 11, 2021
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add posix permission chown suggestion to io error handling (#81942)
parent
0581c05c
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
357 additions
and
92 deletions
+357
-92
error_handling_io.dart
packages/flutter_tools/lib/src/base/error_handling_io.dart
+46
-11
error_handling_io_test.dart
...tools/test/general.shard/base/error_handling_io_test.dart
+310
-79
config_test.dart
packages/flutter_tools/test/general.shard/config_test.dart
+1
-2
No files found.
packages/flutter_tools/lib/src/base/error_handling_io.dart
View file @
c1c12aa3
...
@@ -212,6 +212,7 @@ class ErrorHandlingFile
...
@@ -212,6 +212,7 @@ class ErrorHandlingFile
)),
)),
platform:
_platform
,
platform:
_platform
,
failureMessage:
'Flutter failed to write to a file at "
${delegate.path}
"'
,
failureMessage:
'Flutter failed to write to a file at "
${delegate.path}
"'
,
posixPermissionSuggestion:
_posixPermissionSuggestion
(<
String
>[
delegate
.
path
]),
);
);
}
}
...
@@ -221,6 +222,7 @@ class ErrorHandlingFile
...
@@ -221,6 +222,7 @@ class ErrorHandlingFile
()
=>
delegate
.
readAsStringSync
(),
()
=>
delegate
.
readAsStringSync
(),
platform:
_platform
,
platform:
_platform
,
failureMessage:
'Flutter failed to read a file at "
${delegate.path}
"'
,
failureMessage:
'Flutter failed to read a file at "
${delegate.path}
"'
,
posixPermissionSuggestion:
_posixPermissionSuggestion
(<
String
>[
delegate
.
path
]),
);
);
}
}
...
@@ -234,6 +236,7 @@ class ErrorHandlingFile
...
@@ -234,6 +236,7 @@ class ErrorHandlingFile
()
=>
delegate
.
writeAsBytesSync
(
bytes
,
mode:
mode
,
flush:
flush
),
()
=>
delegate
.
writeAsBytesSync
(
bytes
,
mode:
mode
,
flush:
flush
),
platform:
_platform
,
platform:
_platform
,
failureMessage:
'Flutter failed to write to a file at "
${delegate.path}
"'
,
failureMessage:
'Flutter failed to write to a file at "
${delegate.path}
"'
,
posixPermissionSuggestion:
_posixPermissionSuggestion
(<
String
>[
delegate
.
path
]),
);
);
}
}
...
@@ -253,6 +256,7 @@ class ErrorHandlingFile
...
@@ -253,6 +256,7 @@ class ErrorHandlingFile
)),
)),
platform:
_platform
,
platform:
_platform
,
failureMessage:
'Flutter failed to write to a file at "
${delegate.path}
"'
,
failureMessage:
'Flutter failed to write to a file at "
${delegate.path}
"'
,
posixPermissionSuggestion:
_posixPermissionSuggestion
(<
String
>[
delegate
.
path
]),
);
);
}
}
...
@@ -272,6 +276,7 @@ class ErrorHandlingFile
...
@@ -272,6 +276,7 @@ class ErrorHandlingFile
),
),
platform:
_platform
,
platform:
_platform
,
failureMessage:
'Flutter failed to write to a file at "
${delegate.path}
"'
,
failureMessage:
'Flutter failed to write to a file at "
${delegate.path}
"'
,
posixPermissionSuggestion:
_posixPermissionSuggestion
(<
String
>[
delegate
.
path
]),
);
);
}
}
...
@@ -283,6 +288,7 @@ class ErrorHandlingFile
...
@@ -283,6 +288,7 @@ class ErrorHandlingFile
),
),
platform:
_platform
,
platform:
_platform
,
failureMessage:
'Flutter failed to create file at "
${delegate.path}
"'
,
failureMessage:
'Flutter failed to create file at "
${delegate.path}
"'
,
posixPermissionSuggestion:
recursive
?
null
:
_posixPermissionSuggestion
(<
String
>[
delegate
.
parent
.
path
]),
);
);
}
}
...
@@ -294,6 +300,7 @@ class ErrorHandlingFile
...
@@ -294,6 +300,7 @@ class ErrorHandlingFile
),
),
platform:
_platform
,
platform:
_platform
,
failureMessage:
'Flutter failed to open a file at "
${delegate.path}
"'
,
failureMessage:
'Flutter failed to open a file at "
${delegate.path}
"'
,
posixPermissionSuggestion:
_posixPermissionSuggestion
(<
String
>[
delegate
.
path
]),
);
);
}
}
...
@@ -307,7 +314,8 @@ class ErrorHandlingFile
...
@@ -307,7 +314,8 @@ class ErrorHandlingFile
_runSync
<
void
>(
_runSync
<
void
>(
()
=>
delegate
.
openSync
(
mode:
FileMode
.
read
).
closeSync
(),
()
=>
delegate
.
openSync
(
mode:
FileMode
.
read
).
closeSync
(),
platform:
_platform
,
platform:
_platform
,
failureMessage:
'Flutter failed to copy
$path
to
$newPath
due to source location error'
failureMessage:
'Flutter failed to copy
$path
to
$newPath
due to source location error'
,
posixPermissionSuggestion:
_posixPermissionSuggestion
(<
String
>[
path
]),
);
);
// Next check if the destination file can be written. If not, bail through
// Next check if the destination file can be written. If not, bail through
// error handling.
// error handling.
...
@@ -347,11 +355,17 @@ class ErrorHandlingFile
...
@@ -347,11 +355,17 @@ class ErrorHandlingFile
source
?.
closeSync
();
source
?.
closeSync
();
sink
?.
closeSync
();
sink
?.
closeSync
();
}
}
},
platform:
_platform
,
failureMessage:
'Flutter failed to copy
$path
to
$newPath
due to unknown error'
);
},
platform:
_platform
,
failureMessage:
'Flutter failed to copy
$path
to
$newPath
due to unknown error'
,
posixPermissionSuggestion:
_posixPermissionSuggestion
(<
String
>[
path
,
resultFile
.
parent
.
path
]),
);
// The original copy failed, but the manual copy worked.
// The original copy failed, but the manual copy worked.
return
wrapFile
(
resultFile
);
return
wrapFile
(
resultFile
);
}
}
String
_posixPermissionSuggestion
(
List
<
String
>
paths
)
=>
'Try running:
\n
'
' sudo chown -R
\$
(whoami)
${paths.map(fileSystem.path.absolute).join(' ')}
'
;
@override
@override
String
toString
()
=>
delegate
.
toString
();
String
toString
()
=>
delegate
.
toString
();
}
}
...
@@ -420,6 +434,7 @@ class ErrorHandlingDirectory
...
@@ -420,6 +434,7 @@ class ErrorHandlingDirectory
platform:
_platform
,
platform:
_platform
,
failureMessage:
failureMessage:
'Flutter failed to create a directory at "
${delegate.path}
"'
,
'Flutter failed to create a directory at "
${delegate.path}
"'
,
posixPermissionSuggestion:
recursive
?
null
:
_posixPermissionSuggestion
(
delegate
.
parent
.
path
),
);
);
}
}
...
@@ -450,6 +465,7 @@ class ErrorHandlingDirectory
...
@@ -450,6 +465,7 @@ class ErrorHandlingDirectory
platform:
_platform
,
platform:
_platform
,
failureMessage:
failureMessage:
'Flutter failed to create a directory at "
${delegate.path}
"'
,
'Flutter failed to create a directory at "
${delegate.path}
"'
,
posixPermissionSuggestion:
recursive
?
null
:
_posixPermissionSuggestion
(
delegate
.
parent
.
path
),
);
);
}
}
...
@@ -460,6 +476,7 @@ class ErrorHandlingDirectory
...
@@ -460,6 +476,7 @@ class ErrorHandlingDirectory
platform:
_platform
,
platform:
_platform
,
failureMessage:
failureMessage:
'Flutter failed to delete a directory at "
${delegate.path}
"'
,
'Flutter failed to delete a directory at "
${delegate.path}
"'
,
posixPermissionSuggestion:
recursive
?
null
:
_posixPermissionSuggestion
(
delegate
.
path
),
);
);
}
}
...
@@ -470,6 +487,7 @@ class ErrorHandlingDirectory
...
@@ -470,6 +487,7 @@ class ErrorHandlingDirectory
platform:
_platform
,
platform:
_platform
,
failureMessage:
failureMessage:
'Flutter failed to delete a directory at "
${delegate.path}
"'
,
'Flutter failed to delete a directory at "
${delegate.path}
"'
,
posixPermissionSuggestion:
recursive
?
null
:
_posixPermissionSuggestion
(
delegate
.
path
),
);
);
}
}
...
@@ -480,9 +498,13 @@ class ErrorHandlingDirectory
...
@@ -480,9 +498,13 @@ class ErrorHandlingDirectory
platform:
_platform
,
platform:
_platform
,
failureMessage:
failureMessage:
'Flutter failed to check for directory existence at "
${delegate.path}
"'
,
'Flutter failed to check for directory existence at "
${delegate.path}
"'
,
posixPermissionSuggestion:
_posixPermissionSuggestion
(
delegate
.
parent
.
path
),
);
);
}
}
String
_posixPermissionSuggestion
(
String
path
)
=>
'Try running:
\n
'
' sudo chown -R
\$
(whoami)
${fileSystem.path.absolute(path)}
'
;
@override
@override
String
toString
()
=>
delegate
.
toString
();
String
toString
()
=>
delegate
.
toString
();
}
}
...
@@ -538,6 +560,7 @@ const String _kNoExecutableFound = 'The Flutter tool could not locate an executa
...
@@ -538,6 +560,7 @@ const String _kNoExecutableFound = 'The Flutter tool could not locate an executa
Future
<
T
>
_run
<
T
>(
Future
<
T
>
Function
()
op
,
{
Future
<
T
>
_run
<
T
>(
Future
<
T
>
Function
()
op
,
{
required
Platform
platform
,
required
Platform
platform
,
String
?
failureMessage
,
String
?
failureMessage
,
String
?
posixPermissionSuggestion
,
})
async
{
})
async
{
assert
(
platform
!=
null
);
assert
(
platform
!=
null
);
try
{
try
{
...
@@ -551,14 +574,14 @@ Future<T> _run<T>(Future<T> Function() op, {
...
@@ -551,14 +574,14 @@ Future<T> _run<T>(Future<T> Function() op, {
if
(
platform
.
isWindows
)
{
if
(
platform
.
isWindows
)
{
_handleWindowsException
(
e
,
failureMessage
,
e
.
osError
?.
errorCode
??
0
);
_handleWindowsException
(
e
,
failureMessage
,
e
.
osError
?.
errorCode
??
0
);
}
else
if
(
platform
.
isLinux
||
platform
.
isMacOS
)
{
}
else
if
(
platform
.
isLinux
||
platform
.
isMacOS
)
{
_handlePosixException
(
e
,
failureMessage
,
e
.
osError
?.
errorCode
??
0
);
_handlePosixException
(
e
,
failureMessage
,
e
.
osError
?.
errorCode
??
0
,
posixPermissionSuggestion
);
}
}
rethrow
;
rethrow
;
}
on
io
.
ProcessException
catch
(
e
)
{
}
on
io
.
ProcessException
catch
(
e
)
{
if
(
platform
.
isWindows
)
{
if
(
platform
.
isWindows
)
{
_handleWindowsException
(
e
,
failureMessage
,
e
.
errorCode
);
_handleWindowsException
(
e
,
failureMessage
,
e
.
errorCode
);
}
else
if
(
platform
.
isLinux
||
platform
.
isMacOS
)
{
}
else
if
(
platform
.
isLinux
||
platform
.
isMacOS
)
{
_handlePosixException
(
e
,
failureMessage
,
e
.
errorCode
);
_handlePosixException
(
e
,
failureMessage
,
e
.
errorCode
,
posixPermissionSuggestion
);
}
}
rethrow
;
rethrow
;
}
}
...
@@ -567,6 +590,7 @@ Future<T> _run<T>(Future<T> Function() op, {
...
@@ -567,6 +590,7 @@ Future<T> _run<T>(Future<T> Function() op, {
T
_runSync
<
T
>(
T
Function
()
op
,
{
T
_runSync
<
T
>(
T
Function
()
op
,
{
required
Platform
platform
,
required
Platform
platform
,
String
?
failureMessage
,
String
?
failureMessage
,
String
?
posixPermissionSuggestion
,
})
{
})
{
assert
(
platform
!=
null
);
assert
(
platform
!=
null
);
try
{
try
{
...
@@ -580,14 +604,14 @@ T _runSync<T>(T Function() op, {
...
@@ -580,14 +604,14 @@ T _runSync<T>(T Function() op, {
if
(
platform
.
isWindows
)
{
if
(
platform
.
isWindows
)
{
_handleWindowsException
(
e
,
failureMessage
,
e
.
osError
?.
errorCode
??
0
);
_handleWindowsException
(
e
,
failureMessage
,
e
.
osError
?.
errorCode
??
0
);
}
else
if
(
platform
.
isLinux
||
platform
.
isMacOS
)
{
}
else
if
(
platform
.
isLinux
||
platform
.
isMacOS
)
{
_handlePosixException
(
e
,
failureMessage
,
e
.
osError
?.
errorCode
??
0
);
_handlePosixException
(
e
,
failureMessage
,
e
.
osError
?.
errorCode
??
0
,
posixPermissionSuggestion
);
}
}
rethrow
;
rethrow
;
}
on
io
.
ProcessException
catch
(
e
)
{
}
on
io
.
ProcessException
catch
(
e
)
{
if
(
platform
.
isWindows
)
{
if
(
platform
.
isWindows
)
{
_handleWindowsException
(
e
,
failureMessage
,
e
.
errorCode
);
_handleWindowsException
(
e
,
failureMessage
,
e
.
errorCode
);
}
else
if
(
platform
.
isLinux
||
platform
.
isMacOS
)
{
}
else
if
(
platform
.
isLinux
||
platform
.
isMacOS
)
{
_handlePosixException
(
e
,
failureMessage
,
e
.
errorCode
);
_handlePosixException
(
e
,
failureMessage
,
e
.
errorCode
,
posixPermissionSuggestion
);
}
}
rethrow
;
rethrow
;
}
}
...
@@ -617,6 +641,9 @@ class ErrorHandlingProcessManager extends ProcessManager {
...
@@ -617,6 +641,9 @@ class ErrorHandlingProcessManager extends ProcessManager {
return
_runSync
(
return
_runSync
(
()
=>
_delegate
.
canRun
(
executable
,
workingDirectory:
workingDirectory
),
()
=>
_delegate
.
canRun
(
executable
,
workingDirectory:
workingDirectory
),
platform:
_platform
,
platform:
_platform
,
failureMessage:
'Flutter failed to run "
$executable
"'
,
posixPermissionSuggestion:
'Try running:
\n
'
' sudo chown -R
\$
(whoami)
$executable
&& chmod u+rx
$executable
'
,
);
);
}
}
...
@@ -695,7 +722,7 @@ class ErrorHandlingProcessManager extends ProcessManager {
...
@@ -695,7 +722,7 @@ class ErrorHandlingProcessManager extends ProcessManager {
}
}
}
}
void
_handlePosixException
(
Exception
e
,
String
?
message
,
int
errorCode
)
{
void
_handlePosixException
(
Exception
e
,
String
?
message
,
int
errorCode
,
String
?
posixPermissionSuggestion
)
{
// From:
// 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.h
// https://github.com/torvalds/linux/blob/master/include/uapi/asm-generic/errno-base.h
// https://github.com/torvalds/linux/blob/master/include/uapi/asm-generic/errno-base.h
...
@@ -714,10 +741,18 @@ void _handlePosixException(Exception e, String? message, int errorCode) {
...
@@ -714,10 +741,18 @@ void _handlePosixException(Exception e, String? message, int errorCode) {
break
;
break
;
case
eperm:
case
eperm:
case
eacces:
case
eacces:
errorMessage
=
final
StringBuffer
errorBuffer
=
StringBuffer
();
'
$message
. The flutter tool cannot access the file or directory.
\n
'
if
(
message
!=
null
&&
message
.
isNotEmpty
)
{
'Please ensure that the SDK and/or project is installed in a location '
errorBuffer
.
writeln
(
'
$message
.'
);
'that has read/write permissions for the current user.'
;
}
else
{
errorBuffer
.
writeln
(
'The flutter tool cannot access the file or directory.'
);
}
errorBuffer
.
writeln
(
'Please ensure that the SDK and/or project is installed in a location '
'that has read/write permissions for the current user.'
);
if
(
posixPermissionSuggestion
!=
null
&&
posixPermissionSuggestion
.
isNotEmpty
)
{
errorBuffer
.
writeln
(
posixPermissionSuggestion
);
}
errorMessage
=
errorBuffer
.
toString
();
break
;
break
;
default
:
default
:
// Caller must rethrow the exception.
// Caller must rethrow the exception.
...
...
packages/flutter_tools/test/general.shard/base/error_handling_io_test.dart
View file @
c1c12aa3
...
@@ -15,7 +15,6 @@ import 'package:flutter_tools/src/base/file_system.dart';
...
@@ -15,7 +15,6 @@ import 'package:flutter_tools/src/base/file_system.dart';
import
'package:flutter_tools/src/base/io.dart'
;
import
'package:flutter_tools/src/base/io.dart'
;
import
'package:flutter_tools/src/base/platform.dart'
;
import
'package:flutter_tools/src/base/platform.dart'
;
import
'package:mockito/mockito.dart'
;
import
'package:mockito/mockito.dart'
;
import
'package:path/path.dart'
as
path
;
// flutter_ignore: package_path_import
import
'package:process/process.dart'
;
import
'package:process/process.dart'
;
import
'../../src/common.dart'
;
import
'../../src/common.dart'
;
...
@@ -23,7 +22,6 @@ import '../../src/fake_process_manager.dart';
...
@@ -23,7 +22,6 @@ import '../../src/fake_process_manager.dart';
class
MockFile
extends
Mock
implements
File
{}
class
MockFile
extends
Mock
implements
File
{}
class
MockFileSystem
extends
Mock
implements
FileSystem
{}
class
MockFileSystem
extends
Mock
implements
FileSystem
{}
class
MockPathContext
extends
Mock
implements
path
.
Context
{}
class
MockDirectory
extends
Mock
implements
Directory
{}
class
MockDirectory
extends
Mock
implements
Directory
{}
class
MockRandomAccessFile
extends
Mock
implements
RandomAccessFile
{}
class
MockRandomAccessFile
extends
Mock
implements
RandomAccessFile
{}
...
@@ -48,7 +46,11 @@ void setupWriteMocks({
...
@@ -48,7 +46,11 @@ void setupWriteMocks({
int
errorCode
,
int
errorCode
,
})
{
})
{
final
MockFile
mockFile
=
MockFile
();
final
MockFile
mockFile
=
MockFile
();
final
MockDirectory
mockParentDirectory
=
MockDirectory
();
when
(
mockFileSystem
.
file
(
any
)).
thenReturn
(
mockFile
);
when
(
mockFileSystem
.
file
(
any
)).
thenReturn
(
mockFile
);
when
(
mockFile
.
path
).
thenReturn
(
'parent/file'
);
when
(
mockFile
.
parent
).
thenReturn
(
mockParentDirectory
);
when
(
mockParentDirectory
.
path
).
thenReturn
(
'parent'
);
when
(
mockFile
.
writeAsBytes
(
when
(
mockFile
.
writeAsBytes
(
any
,
any
,
mode:
anyNamed
(
'mode'
),
mode:
anyNamed
(
'mode'
),
...
@@ -88,7 +90,11 @@ void setupReadMocks({
...
@@ -88,7 +90,11 @@ void setupReadMocks({
int
errorCode
,
int
errorCode
,
})
{
})
{
final
MockFile
mockFile
=
MockFile
();
final
MockFile
mockFile
=
MockFile
();
final
MockDirectory
mockParentDirectory
=
MockDirectory
();
when
(
mockFileSystem
.
file
(
any
)).
thenReturn
(
mockFile
);
when
(
mockFileSystem
.
file
(
any
)).
thenReturn
(
mockFile
);
when
(
mockFile
.
path
).
thenReturn
(
'parent/file'
);
when
(
mockFile
.
parent
).
thenReturn
(
mockParentDirectory
);
when
(
mockParentDirectory
.
path
).
thenReturn
(
'parent'
);
when
(
mockFileSystem
.
currentDirectory
).
thenThrow
(
FileSystemException
(
''
,
''
,
OSError
(
''
,
errorCode
)));
when
(
mockFileSystem
.
currentDirectory
).
thenThrow
(
FileSystemException
(
''
,
''
,
OSError
(
''
,
errorCode
)));
when
(
mockFile
.
readAsStringSync
(
when
(
mockFile
.
readAsStringSync
(
encoding:
anyNamed
(
'encoding'
),
encoding:
anyNamed
(
'encoding'
),
...
@@ -101,7 +107,12 @@ void setupDirectoryMocks({
...
@@ -101,7 +107,12 @@ void setupDirectoryMocks({
int
errorCode
,
int
errorCode
,
})
{
})
{
final
MockDirectory
mockDirectory
=
MockDirectory
();
final
MockDirectory
mockDirectory
=
MockDirectory
();
final
MockDirectory
mockParentDirectory
=
MockDirectory
();
when
(
mockDirectory
.
parent
).
thenReturn
(
mockParentDirectory
);
when
(
mockFileSystem
.
directory
(
any
)).
thenReturn
(
mockDirectory
);
when
(
mockFileSystem
.
directory
(
any
)).
thenReturn
(
mockDirectory
);
when
(
mockDirectory
.
path
).
thenReturn
(
'parent/directory'
);
when
(
mockDirectory
.
parent
).
thenReturn
(
mockParentDirectory
);
when
(
mockParentDirectory
.
path
).
thenReturn
(
'parent'
);
when
(
mockDirectory
.
createTemp
(
any
)).
thenAnswer
((
_
)
async
{
when
(
mockDirectory
.
createTemp
(
any
)).
thenAnswer
((
_
)
async
{
throw
FileSystemException
(
''
,
''
,
OSError
(
''
,
errorCode
));
throw
FileSystemException
(
''
,
''
,
OSError
(
''
,
errorCode
));
});
});
...
@@ -188,7 +199,8 @@ void main() {
...
@@ -188,7 +199,8 @@ void main() {
delegate:
mockFileSystem
,
delegate:
mockFileSystem
,
platform:
windowsPlatform
,
platform:
windowsPlatform
,
);
);
when
(
mockFileSystem
.
path
).
thenReturn
(
MockPathContext
());
// For fs.path.absolute usage.
when
(
mockFileSystem
.
path
).
thenReturn
(
MemoryFileSystem
.
test
().
path
);
});
});
testWithoutContext
(
'bypasses error handling when withAllowedFailure is used'
,
()
{
testWithoutContext
(
'bypasses error handling when withAllowedFailure is used'
,
()
{
...
@@ -372,6 +384,7 @@ void main() {
...
@@ -372,6 +384,7 @@ void main() {
MockFileSystem
mockFileSystem
;
MockFileSystem
mockFileSystem
;
ErrorHandlingFileSystem
fs
;
ErrorHandlingFileSystem
fs
;
FileExceptionHandler
exceptionHandler
;
setUp
(()
{
setUp
(()
{
mockFileSystem
=
MockFileSystem
();
mockFileSystem
=
MockFileSystem
();
...
@@ -379,51 +392,113 @@ void main() {
...
@@ -379,51 +392,113 @@ void main() {
delegate:
mockFileSystem
,
delegate:
mockFileSystem
,
platform:
linuxPlatform
,
platform:
linuxPlatform
,
);
);
when
(
mockFileSystem
.
path
).
thenReturn
(
MockPathContext
());
// For fs.path.absolute usage.
when
(
mockFileSystem
.
path
).
thenReturn
(
MemoryFileSystem
.
test
().
path
);
exceptionHandler
=
FileExceptionHandler
();
});
});
testWithoutContext
(
'when access is denied'
,
()
async
{
testWithoutContext
(
'when access is denied'
,
()
async
{
setupWriteMocks
(
final
ErrorHandlingFileSystem
fileSystem
=
ErrorHandlingFileSystem
(
mockFileSystem:
mockFileSystem
,
delegate:
MemoryFileSystem
.
test
(
opHandle:
exceptionHandler
.
opHandle
),
fs:
fs
,
platform:
linuxPlatform
,
errorCode:
eacces
,
);
);
final
Directory
directory
=
fileSystem
.
directory
(
'dir'
)..
createSync
();
final
File
file
=
directory
.
childFile
(
'file'
);
final
File
file
=
fs
.
file
(
'file'
);
exceptionHandler
.
addError
(
file
,
const
String
expectedMessage
=
'The flutter tool cannot access the file or directory'
;
FileSystemOp
.
create
,
expect
(()
async
=>
file
.
writeAsBytes
(<
int
>[
0
]),
FileSystemException
(
''
,
file
.
path
,
const
OSError
(
''
,
eacces
)),
throwsToolExit
(
message:
expectedMessage
));
);
expect
(()
async
=>
file
.
writeAsString
(
''
),
exceptionHandler
.
addError
(
throwsToolExit
(
message:
expectedMessage
));
file
,
expect
(()
=>
file
.
writeAsBytesSync
(<
int
>[
0
]),
FileSystemOp
.
write
,
throwsToolExit
(
message:
expectedMessage
));
FileSystemException
(
''
,
file
.
path
,
const
OSError
(
''
,
eacces
)),
expect
(()
=>
file
.
writeAsStringSync
(
''
),
);
throwsToolExit
(
message:
expectedMessage
));
exceptionHandler
.
addError
(
expect
(()
=>
file
.
openSync
(),
file
,
throwsToolExit
(
message:
expectedMessage
));
FileSystemOp
.
read
,
expect
(()
=>
file
.
createSync
(),
FileSystemException
(
''
,
file
.
path
,
const
OSError
(
''
,
eacces
)),
throwsToolExit
(
message:
expectedMessage
));
);
exceptionHandler
.
addError
(
file
,
FileSystemOp
.
delete
,
FileSystemException
(
''
,
file
.
path
,
const
OSError
(
''
,
eacces
)),
);
const
String
writeMessage
=
'Flutter failed to write to a file at "dir/file".
\n
'
'Please ensure that the SDK and/or project is installed in a location that has read/write permissions for the current user.
\n
'
'Try running:
\n
'
r' sudo chown -R $(whoami) /dir/file'
;
expect
(()
async
=>
file
.
writeAsBytes
(<
int
>[
0
]),
throwsToolExit
(
message:
writeMessage
));
expect
(()
async
=>
file
.
writeAsString
(
''
),
throwsToolExit
(
message:
writeMessage
));
expect
(()
=>
file
.
writeAsBytesSync
(<
int
>[
0
]),
throwsToolExit
(
message:
writeMessage
));
expect
(()
=>
file
.
writeAsStringSync
(
''
),
throwsToolExit
(
message:
writeMessage
));
const
String
createMessage
=
'Flutter failed to create file at "dir/file".
\n
'
'Please ensure that the SDK and/or project is installed in a location that has read/write permissions for the current user.
\n
'
'Try running:
\n
'
r' sudo chown -R $(whoami) /dir'
;
expect
(()
=>
file
.
createSync
(),
throwsToolExit
(
message:
createMessage
));
// Recursive does not contain the "sudo chown" suggestion.
expect
(()
async
=>
file
.
createSync
(
recursive:
true
),
throwsA
(
isA
<
ToolExit
>().
having
((
ToolExit
e
)
=>
e
.
message
,
'message'
,
isNot
(
contains
(
'sudo chown'
)))));
const
String
readMessage
=
'Flutter failed to read a file at "dir/file".
\n
'
'Please ensure that the SDK and/or project is installed in a location that has read/write permissions for the current user.
\n
'
'Try running:
\n
'
r' sudo chown -R $(whoami) /dir/file'
;
expect
(()
=>
file
.
readAsStringSync
(),
throwsToolExit
(
message:
readMessage
));
});
});
testWithoutContext
(
'when access is denied for directories'
,
()
async
{
testWithoutContext
(
'when access is denied for directories'
,
()
async
{
setupDirectoryMocks
(
final
ErrorHandlingFileSystem
fileSystem
=
ErrorHandlingFileSystem
(
mockFileSystem:
mockFileSystem
,
delegate:
MemoryFileSystem
.
test
(
opHandle:
exceptionHandler
.
opHandle
),
fs:
fs
,
platform:
linuxPlatform
,
errorCode:
eperm
,
);
);
final
Directory
parent
=
fileSystem
.
directory
(
'parent'
)..
createSync
();
final
Directory
directory
=
parent
.
childDirectory
(
'childDir'
);
final
Directory
directory
=
fs
.
directory
(
'file'
);
exceptionHandler
.
addError
(
directory
,
FileSystemOp
.
create
,
FileSystemException
(
''
,
directory
.
path
,
const
OSError
(
''
,
eperm
)),
);
exceptionHandler
.
addError
(
directory
,
FileSystemOp
.
delete
,
FileSystemException
(
''
,
directory
.
path
,
const
OSError
(
''
,
eperm
)),
);
const
String
expectedMessage
=
'The flutter tool cannot access the file or directory'
;
const
String
createMessage
=
'Flutter failed to create a directory at "parent/childDir".
\n
'
'Please ensure that the SDK and/or project is installed in a location that has read/write permissions for the current user.
\n
'
'Try running:
\n
'
r' sudo chown -R $(whoami) /parent'
;
expect
(()
async
=>
directory
.
create
(),
expect
(()
async
=>
directory
.
create
(),
throwsToolExit
(
message:
expectedMessage
));
throwsToolExit
(
message:
createMessage
));
expect
(()
async
=>
directory
.
delete
(),
throwsToolExit
(
message:
expectedMessage
));
expect
(()
=>
directory
.
createSync
(),
expect
(()
=>
directory
.
createSync
(),
throwsToolExit
(
message:
expectedMessage
));
throwsToolExit
(
message:
createMessage
));
// Recursive does not contain the "sudo chown" suggestion.
expect
(()
async
=>
directory
.
createSync
(
recursive:
true
),
throwsA
(
isA
<
ToolExit
>().
having
((
ToolExit
e
)
=>
e
.
message
,
'message'
,
isNot
(
contains
(
'sudo chown'
)))));
const
String
deleteMessage
=
'Flutter failed to delete a directory at "parent/childDir".
\n
'
'Please ensure that the SDK and/or project is installed in a location that has read/write permissions for the current user.
\n
'
'Try running:
\n
'
r' sudo chown -R $(whoami) /parent'
;
expect
(()
=>
directory
.
deleteSync
(),
expect
(()
=>
directory
.
deleteSync
(),
throwsToolExit
(
message:
expectedMessage
));
throwsToolExit
(
message:
deleteMessage
));
expect
(()
async
=>
directory
.
delete
(),
throwsToolExit
(
message:
deleteMessage
));
// Recursive does not contain the "sudo chown" suggestion.
expect
(()
async
=>
directory
.
deleteSync
(
recursive:
true
),
throwsA
(
isA
<
ToolExit
>().
having
((
ToolExit
e
)
=>
e
.
message
,
'message'
,
isNot
(
contains
(
'sudo chown'
)))));
});
});
testWithoutContext
(
'when writing to a full device'
,
()
async
{
testWithoutContext
(
'when writing to a full device'
,
()
async
{
...
@@ -496,6 +571,7 @@ void main() {
...
@@ -496,6 +571,7 @@ void main() {
const
int
eacces
=
13
;
const
int
eacces
=
13
;
MockFileSystem
mockFileSystem
;
MockFileSystem
mockFileSystem
;
ErrorHandlingFileSystem
fs
;
ErrorHandlingFileSystem
fs
;
FileExceptionHandler
exceptionHandler
;
setUp
(()
{
setUp
(()
{
mockFileSystem
=
MockFileSystem
();
mockFileSystem
=
MockFileSystem
();
...
@@ -503,49 +579,113 @@ void main() {
...
@@ -503,49 +579,113 @@ void main() {
delegate:
mockFileSystem
,
delegate:
mockFileSystem
,
platform:
macOSPlatform
,
platform:
macOSPlatform
,
);
);
when
(
mockFileSystem
.
path
).
thenReturn
(
MockPathContext
());
// For fs.path.absolute usage.
when
(
mockFileSystem
.
path
).
thenReturn
(
MemoryFileSystem
.
test
().
path
);
exceptionHandler
=
FileExceptionHandler
();
});
});
testWithoutContext
(
'when access is denied'
,
()
async
{
testWithoutContext
(
'when access is denied'
,
()
async
{
setupWriteMocks
(
final
ErrorHandlingFileSystem
fileSystem
=
ErrorHandlingFileSystem
(
mockFileSystem:
mockFileSystem
,
delegate:
MemoryFileSystem
.
test
(
opHandle:
exceptionHandler
.
opHandle
),
fs:
fs
,
platform:
macOSPlatform
,
errorCode:
eacces
,
);
);
final
Directory
directory
=
fileSystem
.
directory
(
'dir'
)..
createSync
();
final
File
file
=
directory
.
childFile
(
'file'
);
final
File
file
=
fs
.
file
(
'file'
);
exceptionHandler
.
addError
(
file
,
const
String
expectedMessage
=
'The flutter tool cannot access the file'
;
FileSystemOp
.
create
,
expect
(()
async
=>
file
.
writeAsBytes
(<
int
>[
0
]),
FileSystemException
(
''
,
file
.
path
,
const
OSError
(
''
,
eacces
)),
throwsToolExit
(
message:
expectedMessage
));
);
expect
(()
async
=>
file
.
writeAsString
(
''
),
exceptionHandler
.
addError
(
throwsToolExit
(
message:
expectedMessage
));
file
,
expect
(()
=>
file
.
writeAsBytesSync
(<
int
>[
0
]),
FileSystemOp
.
write
,
throwsToolExit
(
message:
expectedMessage
));
FileSystemException
(
''
,
file
.
path
,
const
OSError
(
''
,
eacces
)),
expect
(()
=>
file
.
writeAsStringSync
(
''
),
);
throwsToolExit
(
message:
expectedMessage
));
exceptionHandler
.
addError
(
expect
(()
=>
file
.
openSync
(),
file
,
throwsToolExit
(
message:
expectedMessage
));
FileSystemOp
.
read
,
FileSystemException
(
''
,
file
.
path
,
const
OSError
(
''
,
eacces
)),
);
exceptionHandler
.
addError
(
file
,
FileSystemOp
.
delete
,
FileSystemException
(
''
,
file
.
path
,
const
OSError
(
''
,
eacces
)),
);
const
String
writeMessage
=
'Flutter failed to write to a file at "dir/file".
\n
'
'Please ensure that the SDK and/or project is installed in a location that has read/write permissions for the current user.
\n
'
'Try running:
\n
'
r' sudo chown -R $(whoami) /dir/file'
;
expect
(()
async
=>
file
.
writeAsBytes
(<
int
>[
0
]),
throwsToolExit
(
message:
writeMessage
));
expect
(()
async
=>
file
.
writeAsString
(
''
),
throwsToolExit
(
message:
writeMessage
));
expect
(()
=>
file
.
writeAsBytesSync
(<
int
>[
0
]),
throwsToolExit
(
message:
writeMessage
));
expect
(()
=>
file
.
writeAsStringSync
(
''
),
throwsToolExit
(
message:
writeMessage
));
const
String
createMessage
=
'Flutter failed to create file at "dir/file".
\n
'
'Please ensure that the SDK and/or project is installed in a location that has read/write permissions for the current user.
\n
'
'Try running:
\n
'
r' sudo chown -R $(whoami) /dir'
;
expect
(()
=>
file
.
createSync
(),
throwsToolExit
(
message:
createMessage
));
// Recursive does not contain the "sudo chown" suggestion.
expect
(()
async
=>
file
.
createSync
(
recursive:
true
),
throwsA
(
isA
<
ToolExit
>().
having
((
ToolExit
e
)
=>
e
.
message
,
'message'
,
isNot
(
contains
(
'sudo chown'
)))));
const
String
readMessage
=
'Flutter failed to read a file at "dir/file".
\n
'
'Please ensure that the SDK and/or project is installed in a location that has read/write permissions for the current user.
\n
'
'Try running:
\n
'
r' sudo chown -R $(whoami) /dir/file'
;
expect
(()
=>
file
.
readAsStringSync
(),
throwsToolExit
(
message:
readMessage
));
});
});
testWithoutContext
(
'when access is denied for directories'
,
()
async
{
testWithoutContext
(
'when access is denied for directories'
,
()
async
{
setupDirectoryMocks
(
final
ErrorHandlingFileSystem
fileSystem
=
ErrorHandlingFileSystem
(
mockFileSystem:
mockFileSystem
,
delegate:
MemoryFileSystem
.
test
(
opHandle:
exceptionHandler
.
opHandle
),
fs:
fs
,
platform:
macOSPlatform
,
errorCode:
eperm
,
);
);
final
Directory
parent
=
fileSystem
.
directory
(
'parent'
)..
createSync
();
final
Directory
directory
=
parent
.
childDirectory
(
'childDir'
);
final
Directory
directory
=
fs
.
directory
(
'file'
);
exceptionHandler
.
addError
(
directory
,
FileSystemOp
.
create
,
FileSystemException
(
''
,
directory
.
path
,
const
OSError
(
''
,
eperm
)),
);
exceptionHandler
.
addError
(
directory
,
FileSystemOp
.
delete
,
FileSystemException
(
''
,
directory
.
path
,
const
OSError
(
''
,
eperm
)),
);
const
String
expectedMessage
=
'The flutter tool cannot access the file or directory'
;
const
String
createMessage
=
'Flutter failed to create a directory at "parent/childDir".
\n
'
'Please ensure that the SDK and/or project is installed in a location that has read/write permissions for the current user.
\n
'
'Try running:
\n
'
r' sudo chown -R $(whoami) /parent'
;
expect
(()
async
=>
directory
.
create
(),
expect
(()
async
=>
directory
.
create
(),
throwsToolExit
(
message:
expectedMessage
));
throwsToolExit
(
message:
createMessage
));
expect
(()
async
=>
directory
.
delete
(),
expect
(()
=>
directory
.
createSync
(),
throwsToolExit
(
message:
createMessage
));
throwsToolExit
(
message:
expectedMessage
));
expect
(()
=>
directory
.
createSync
(),
// Recursive does not contain the "sudo chown" suggestion.
throwsToolExit
(
message:
expectedMessage
));
expect
(()
async
=>
directory
.
createSync
(
recursive:
true
),
throwsA
(
isA
<
ToolExit
>().
having
((
ToolExit
e
)
=>
e
.
message
,
'message'
,
isNot
(
contains
(
'sudo chown'
)))));
const
String
deleteMessage
=
'Flutter failed to delete a directory at "parent/childDir".
\n
'
'Please ensure that the SDK and/or project is installed in a location that has read/write permissions for the current user.
\n
'
'Try running:
\n
'
r' sudo chown -R $(whoami) /parent'
;
expect
(()
=>
directory
.
deleteSync
(),
expect
(()
=>
directory
.
deleteSync
(),
throwsToolExit
(
message:
expectedMessage
));
throwsToolExit
(
message:
deleteMessage
));
expect
(()
async
=>
directory
.
delete
(),
throwsToolExit
(
message:
deleteMessage
));
// Recursive does not contain the "sudo chown" suggestion.
expect
(()
async
=>
directory
.
deleteSync
(
recursive:
true
),
throwsA
(
isA
<
ToolExit
>().
having
((
ToolExit
e
)
=>
e
.
message
,
'message'
,
isNot
(
contains
(
'sudo chown'
)))));
});
});
testWithoutContext
(
'when writing to a full device'
,
()
async
{
testWithoutContext
(
'when writing to a full device'
,
()
async
{
...
@@ -635,7 +775,8 @@ void main() {
...
@@ -635,7 +775,8 @@ void main() {
final
Object
firstPath
=
fs
.
path
;
final
Object
firstPath
=
fs
.
path
;
fs
.
currentDirectory
=
null
;
fs
.
currentDirectory
=
null
;
when
(
mockFileSystem
.
path
).
thenReturn
(
MockPathContext
());
// For fs.path.absolute usage.
when
(
mockFileSystem
.
path
).
thenReturn
(
MemoryFileSystem
.
test
().
path
);
expect
(
identical
(
firstPath
,
fs
.
path
),
false
);
expect
(
identical
(
firstPath
,
fs
.
path
),
false
);
});
});
...
@@ -689,7 +830,7 @@ void main() {
...
@@ -689,7 +830,7 @@ void main() {
const
int
kUserMappedSectionOpened
=
1224
;
const
int
kUserMappedSectionOpened
=
1224
;
const
int
kUserPermissionDenied
=
5
;
const
int
kUserPermissionDenied
=
5
;
test
(
'when PackageProcess throws an exception containg non-executable bits'
,
()
{
test
WithoutContext
(
'when PackageProcess throws an exception containg non-executable bits'
,
()
{
final
FakeProcessManager
fakeProcessManager
=
FakeProcessManager
.
list
(<
FakeCommand
>[
final
FakeProcessManager
fakeProcessManager
=
FakeProcessManager
.
list
(<
FakeCommand
>[
const
FakeCommand
(
command:
<
String
>[
'foo'
],
exception:
ProcessPackageExecutableNotFoundException
(
''
,
candidates:
<
String
>[
'not-empty'
])),
const
FakeCommand
(
command:
<
String
>[
'foo'
],
exception:
ProcessPackageExecutableNotFoundException
(
''
,
candidates:
<
String
>[
'not-empty'
])),
const
FakeCommand
(
command:
<
String
>[
'foo'
],
exception:
ProcessPackageExecutableNotFoundException
(
''
,
candidates:
<
String
>[
'not-empty'
])),
const
FakeCommand
(
command:
<
String
>[
'foo'
],
exception:
ProcessPackageExecutableNotFoundException
(
''
,
candidates:
<
String
>[
'not-empty'
])),
...
@@ -708,7 +849,7 @@ void main() {
...
@@ -708,7 +849,7 @@ void main() {
throwsToolExit
(
message:
expectedMessage
));
throwsToolExit
(
message:
expectedMessage
));
});
});
test
(
'when PackageProcess throws an exception without containing non-executable bits'
,
()
{
test
WithoutContext
(
'when PackageProcess throws an exception without containing non-executable bits'
,
()
{
final
FakeProcessManager
fakeProcessManager
=
FakeProcessManager
.
list
(<
FakeCommand
>[
final
FakeProcessManager
fakeProcessManager
=
FakeProcessManager
.
list
(<
FakeCommand
>[
const
FakeCommand
(
command:
<
String
>[
'foo'
],
exception:
ProcessPackageExecutableNotFoundException
(
''
,
candidates:
<
String
>[])),
const
FakeCommand
(
command:
<
String
>[
'foo'
],
exception:
ProcessPackageExecutableNotFoundException
(
''
,
candidates:
<
String
>[])),
const
FakeCommand
(
command:
<
String
>[
'foo'
],
exception:
ProcessPackageExecutableNotFoundException
(
''
,
candidates:
<
String
>[])),
const
FakeCommand
(
command:
<
String
>[
'foo'
],
exception:
ProcessPackageExecutableNotFoundException
(
''
,
candidates:
<
String
>[])),
...
@@ -725,7 +866,7 @@ void main() {
...
@@ -725,7 +866,7 @@ void main() {
expect
(()
async
=>
processManager
.
runSync
(<
String
>[
'foo'
]),
throwsProcessException
());
expect
(()
async
=>
processManager
.
runSync
(<
String
>[
'foo'
]),
throwsProcessException
());
});
});
test
(
'when the device is full'
,
()
{
test
WithoutContext
(
'when the device is full'
,
()
{
final
FakeProcessManager
fakeProcessManager
=
FakeProcessManager
.
list
(<
FakeCommand
>[
final
FakeProcessManager
fakeProcessManager
=
FakeProcessManager
.
list
(<
FakeCommand
>[
const
FakeCommand
(
command:
<
String
>[
'foo'
],
exception:
ProcessException
(
''
,
<
String
>[],
''
,
kDeviceFull
)),
const
FakeCommand
(
command:
<
String
>[
'foo'
],
exception:
ProcessException
(
''
,
<
String
>[],
''
,
kDeviceFull
)),
const
FakeCommand
(
command:
<
String
>[
'foo'
],
exception:
ProcessException
(
''
,
<
String
>[],
''
,
kDeviceFull
)),
const
FakeCommand
(
command:
<
String
>[
'foo'
],
exception:
ProcessException
(
''
,
<
String
>[],
''
,
kDeviceFull
)),
...
@@ -747,7 +888,7 @@ void main() {
...
@@ -747,7 +888,7 @@ void main() {
throwsToolExit
(
message:
expectedMessage
));
throwsToolExit
(
message:
expectedMessage
));
});
});
test
(
'when the file is being used by another program'
,
()
{
test
WithoutContext
(
'when the file is being used by another program'
,
()
{
final
FakeProcessManager
fakeProcessManager
=
FakeProcessManager
.
list
(<
FakeCommand
>[
final
FakeProcessManager
fakeProcessManager
=
FakeProcessManager
.
list
(<
FakeCommand
>[
const
FakeCommand
(
command:
<
String
>[
'foo'
],
exception:
ProcessException
(
''
,
<
String
>[],
''
,
kUserMappedSectionOpened
)),
const
FakeCommand
(
command:
<
String
>[
'foo'
],
exception:
ProcessException
(
''
,
<
String
>[],
''
,
kUserMappedSectionOpened
)),
const
FakeCommand
(
command:
<
String
>[
'foo'
],
exception:
ProcessException
(
''
,
<
String
>[],
''
,
kUserMappedSectionOpened
)),
const
FakeCommand
(
command:
<
String
>[
'foo'
],
exception:
ProcessException
(
''
,
<
String
>[],
''
,
kUserMappedSectionOpened
)),
...
@@ -768,7 +909,7 @@ void main() {
...
@@ -768,7 +909,7 @@ void main() {
throwsToolExit
(
message:
expectedMessage
));
throwsToolExit
(
message:
expectedMessage
));
});
});
test
(
'when permissions are denied'
,
()
{
test
WithoutContext
(
'when permissions are denied'
,
()
{
final
FakeProcessManager
fakeProcessManager
=
FakeProcessManager
.
list
(<
FakeCommand
>[
final
FakeProcessManager
fakeProcessManager
=
FakeProcessManager
.
list
(<
FakeCommand
>[
const
FakeCommand
(
command:
<
String
>[
'foo'
],
exception:
ProcessException
(
''
,
<
String
>[],
''
,
kUserPermissionDenied
)),
const
FakeCommand
(
command:
<
String
>[
'foo'
],
exception:
ProcessException
(
''
,
<
String
>[],
''
,
kUserPermissionDenied
)),
const
FakeCommand
(
command:
<
String
>[
'foo'
],
exception:
ProcessException
(
''
,
<
String
>[],
''
,
kUserPermissionDenied
)),
const
FakeCommand
(
command:
<
String
>[
'foo'
],
exception:
ProcessException
(
''
,
<
String
>[],
''
,
kUserPermissionDenied
)),
...
@@ -788,13 +929,25 @@ void main() {
...
@@ -788,13 +929,25 @@ void main() {
expect
(()
=>
processManager
.
runSync
(<
String
>[
'foo'
]),
expect
(()
=>
processManager
.
runSync
(<
String
>[
'foo'
]),
throwsToolExit
(
message:
expectedMessage
));
throwsToolExit
(
message:
expectedMessage
));
});
});
testWithoutContext
(
'when cannot run executable'
,
()
{
final
ThrowingFakeProcessManager
throwingFakeProcessManager
=
ThrowingFakeProcessManager
(
const
ProcessException
(
''
,
<
String
>[],
''
,
kUserPermissionDenied
));
final
ProcessManager
processManager
=
ErrorHandlingProcessManager
(
delegate:
throwingFakeProcessManager
,
platform:
windowsPlatform
,
);
const
String
expectedMessage
=
r'Flutter failed to run "C:\path\to\dart". The flutter tool cannot access the file or directory.'
;
expect
(()
async
=>
processManager
.
canRun
(
r'C:\path\to\dart'
),
throwsToolExit
(
message:
expectedMessage
));
});
});
});
group
(
'ProcessManager on linux throws tool exit'
,
()
{
group
(
'ProcessManager on linux throws tool exit'
,
()
{
const
int
enospc
=
28
;
const
int
enospc
=
28
;
const
int
eacces
=
13
;
const
int
eacces
=
13
;
test
(
'when writing to a full device'
,
()
{
test
WithoutContext
(
'when writing to a full device'
,
()
{
final
FakeProcessManager
fakeProcessManager
=
FakeProcessManager
.
list
(<
FakeCommand
>[
final
FakeProcessManager
fakeProcessManager
=
FakeProcessManager
.
list
(<
FakeCommand
>[
const
FakeCommand
(
command:
<
String
>[
'foo'
],
exception:
ProcessException
(
''
,
<
String
>[],
''
,
enospc
)),
const
FakeCommand
(
command:
<
String
>[
'foo'
],
exception:
ProcessException
(
''
,
<
String
>[],
''
,
enospc
)),
const
FakeCommand
(
command:
<
String
>[
'foo'
],
exception:
ProcessException
(
''
,
<
String
>[],
''
,
enospc
)),
const
FakeCommand
(
command:
<
String
>[
'foo'
],
exception:
ProcessException
(
''
,
<
String
>[],
''
,
enospc
)),
...
@@ -815,7 +968,7 @@ void main() {
...
@@ -815,7 +968,7 @@ void main() {
throwsToolExit
(
message:
expectedMessage
));
throwsToolExit
(
message:
expectedMessage
));
});
});
test
(
'when permissions are denied'
,
()
{
test
WithoutContext
(
'when permissions are denied'
,
()
{
final
FakeProcessManager
fakeProcessManager
=
FakeProcessManager
.
list
(<
FakeCommand
>[
final
FakeProcessManager
fakeProcessManager
=
FakeProcessManager
.
list
(<
FakeCommand
>[
const
FakeCommand
(
command:
<
String
>[
'foo'
],
exception:
ProcessException
(
''
,
<
String
>[],
''
,
eacces
)),
const
FakeCommand
(
command:
<
String
>[
'foo'
],
exception:
ProcessException
(
''
,
<
String
>[],
''
,
eacces
)),
const
FakeCommand
(
command:
<
String
>[
'foo'
],
exception:
ProcessException
(
''
,
<
String
>[],
''
,
eacces
)),
const
FakeCommand
(
command:
<
String
>[
'foo'
],
exception:
ProcessException
(
''
,
<
String
>[],
''
,
eacces
)),
...
@@ -835,13 +988,29 @@ void main() {
...
@@ -835,13 +988,29 @@ void main() {
expect
(()
=>
processManager
.
runSync
(<
String
>[
'foo'
]),
expect
(()
=>
processManager
.
runSync
(<
String
>[
'foo'
]),
throwsToolExit
(
message:
expectedMessage
));
throwsToolExit
(
message:
expectedMessage
));
});
});
testWithoutContext
(
'when cannot run executable'
,
()
{
final
ThrowingFakeProcessManager
throwingFakeProcessManager
=
ThrowingFakeProcessManager
(
const
ProcessException
(
''
,
<
String
>[],
''
,
eacces
));
final
ProcessManager
processManager
=
ErrorHandlingProcessManager
(
delegate:
throwingFakeProcessManager
,
platform:
linuxPlatform
,
);
const
String
expectedMessage
=
'Flutter failed to run "/path/to/dart".
\n
'
'Please ensure that the SDK and/or project is installed in a location that has read/write permissions for the current user.
\n
'
'Try running:
\n
'
r' sudo chown -R $(whoami) /path/to/dart && chmod u+rx /path/to/dart'
;
expect
(()
async
=>
processManager
.
canRun
(
'/path/to/dart'
),
throwsToolExit
(
message:
expectedMessage
));
});
});
});
group
(
'ProcessManager on macOS throws tool exit'
,
()
{
group
(
'ProcessManager on macOS throws tool exit'
,
()
{
const
int
enospc
=
28
;
const
int
enospc
=
28
;
const
int
eacces
=
13
;
const
int
eacces
=
13
;
test
(
'when writing to a full device'
,
()
{
test
WithoutContext
(
'when writing to a full device'
,
()
{
final
FakeProcessManager
fakeProcessManager
=
FakeProcessManager
.
list
(<
FakeCommand
>[
final
FakeProcessManager
fakeProcessManager
=
FakeProcessManager
.
list
(<
FakeCommand
>[
const
FakeCommand
(
command:
<
String
>[
'foo'
],
exception:
ProcessException
(
''
,
<
String
>[],
''
,
enospc
)),
const
FakeCommand
(
command:
<
String
>[
'foo'
],
exception:
ProcessException
(
''
,
<
String
>[],
''
,
enospc
)),
const
FakeCommand
(
command:
<
String
>[
'foo'
],
exception:
ProcessException
(
''
,
<
String
>[],
''
,
enospc
)),
const
FakeCommand
(
command:
<
String
>[
'foo'
],
exception:
ProcessException
(
''
,
<
String
>[],
''
,
enospc
)),
...
@@ -862,7 +1031,7 @@ void main() {
...
@@ -862,7 +1031,7 @@ void main() {
throwsToolExit
(
message:
expectedMessage
));
throwsToolExit
(
message:
expectedMessage
));
});
});
test
(
'when permissions are denied'
,
()
{
test
WithoutContext
(
'when permissions are denied'
,
()
{
final
FakeProcessManager
fakeProcessManager
=
FakeProcessManager
.
list
(<
FakeCommand
>[
final
FakeProcessManager
fakeProcessManager
=
FakeProcessManager
.
list
(<
FakeCommand
>[
const
FakeCommand
(
command:
<
String
>[
'foo'
],
exception:
ProcessException
(
''
,
<
String
>[],
''
,
eacces
)),
const
FakeCommand
(
command:
<
String
>[
'foo'
],
exception:
ProcessException
(
''
,
<
String
>[],
''
,
eacces
)),
const
FakeCommand
(
command:
<
String
>[
'foo'
],
exception:
ProcessException
(
''
,
<
String
>[],
''
,
eacces
)),
const
FakeCommand
(
command:
<
String
>[
'foo'
],
exception:
ProcessException
(
''
,
<
String
>[],
''
,
eacces
)),
...
@@ -870,7 +1039,7 @@ void main() {
...
@@ -870,7 +1039,7 @@ void main() {
]);
]);
final
ProcessManager
processManager
=
ErrorHandlingProcessManager
(
final
ProcessManager
processManager
=
ErrorHandlingProcessManager
(
delegate:
fakeProcessManager
,
delegate:
fakeProcessManager
,
platform:
linux
Platform
,
platform:
macOS
Platform
,
);
);
const
String
expectedMessage
=
'The flutter tool cannot access the file'
;
const
String
expectedMessage
=
'The flutter tool cannot access the file'
;
...
@@ -882,6 +1051,22 @@ void main() {
...
@@ -882,6 +1051,22 @@ void main() {
expect
(()
=>
processManager
.
runSync
(<
String
>[
'foo'
]),
expect
(()
=>
processManager
.
runSync
(<
String
>[
'foo'
]),
throwsToolExit
(
message:
expectedMessage
));
throwsToolExit
(
message:
expectedMessage
));
});
});
testWithoutContext
(
'when cannot run executable'
,
()
{
final
ThrowingFakeProcessManager
throwingFakeProcessManager
=
ThrowingFakeProcessManager
(
const
ProcessException
(
''
,
<
String
>[],
''
,
eacces
));
final
ProcessManager
processManager
=
ErrorHandlingProcessManager
(
delegate:
throwingFakeProcessManager
,
platform:
macOSPlatform
,
);
const
String
expectedMessage
=
'Flutter failed to run "/path/to/dart".
\n
'
'Please ensure that the SDK and/or project is installed in a location that has read/write permissions for the current user.
\n
'
'Try running:
\n
'
r' sudo chown -R $(whoami) /path/to/dart && chmod u+rx /path/to/dart'
;
expect
(()
async
=>
processManager
.
canRun
(
'/path/to/dart'
),
throwsToolExit
(
message:
expectedMessage
));
});
});
});
testWithoutContext
(
'ErrorHandlingProcessManager delegates killPid correctly'
,
()
async
{
testWithoutContext
(
'ErrorHandlingProcessManager delegates killPid correctly'
,
()
async
{
...
@@ -910,21 +1095,32 @@ void main() {
...
@@ -910,21 +1095,32 @@ void main() {
delegate:
mockFileSystem
,
delegate:
mockFileSystem
,
platform:
linuxPlatform
,
platform:
linuxPlatform
,
);
);
when
(
mockFileSystem
.
path
).
thenReturn
(
MockPathContext
());
// For fs.path.absolute usage.
when
(
mockFileSystem
.
path
).
thenReturn
(
MemoryFileSystem
.
test
().
path
);
});
});
testWithoutContext
(
'copySync handles error if openSync on source file fails'
,
()
{
testWithoutContext
(
'copySync handles error if openSync on source file fails'
,
()
{
final
MockFile
source
=
MockFile
();
final
MockFile
source
=
MockFile
();
when
(
source
.
path
).
thenReturn
(
'source'
);
when
(
source
.
openSync
(
mode:
anyNamed
(
'mode'
)))
when
(
source
.
openSync
(
mode:
anyNamed
(
'mode'
)))
.
thenThrow
(
const
FileSystemException
(
''
,
''
,
OSError
(
''
,
eaccess
)));
.
thenThrow
(
const
FileSystemException
(
''
,
''
,
OSError
(
''
,
eaccess
)));
when
(
mockFileSystem
.
file
(
'source'
)).
thenReturn
(
source
);
when
(
mockFileSystem
.
file
(
'source'
)).
thenReturn
(
source
);
expect
(()
=>
fileSystem
.
file
(
'source'
).
copySync
(
'dest'
),
throwsToolExit
());
const
String
expectedMessage
=
'Flutter failed to copy source to dest due to source location error.
\n
'
'Please ensure that the SDK and/or project is installed in a location that has read/write permissions for the current user.
\n
'
'Try running:
\n
'
r' sudo chown -R $(whoami) /source'
;
expect
(()
=>
fileSystem
.
file
(
'source'
).
copySync
(
'dest'
),
throwsToolExit
(
message:
expectedMessage
));
});
});
testWithoutContext
(
'copySync handles error if createSync on destination file fails'
,
()
{
testWithoutContext
(
'copySync handles error if createSync on destination file fails'
,
()
{
final
MockFile
source
=
MockFile
();
final
MockFile
source
=
MockFile
();
when
(
source
.
path
).
thenReturn
(
'source'
);
final
MockDirectory
parent
=
MockDirectory
();
when
(
parent
.
path
).
thenReturn
(
'destParent'
);
final
MockFile
dest
=
MockFile
();
final
MockFile
dest
=
MockFile
();
when
(
dest
.
parent
).
thenReturn
(
parent
);
when
(
source
.
openSync
(
mode:
anyNamed
(
'mode'
)))
when
(
source
.
openSync
(
mode:
anyNamed
(
'mode'
)))
.
thenReturn
(
MockRandomAccessFile
());
.
thenReturn
(
MockRandomAccessFile
());
when
(
dest
.
createSync
(
recursive:
anyNamed
(
'recursive'
)))
when
(
dest
.
createSync
(
recursive:
anyNamed
(
'recursive'
)))
...
@@ -932,13 +1128,20 @@ void main() {
...
@@ -932,13 +1128,20 @@ void main() {
when
(
mockFileSystem
.
file
(
'source'
)).
thenReturn
(
source
);
when
(
mockFileSystem
.
file
(
'source'
)).
thenReturn
(
source
);
when
(
mockFileSystem
.
file
(
'dest'
)).
thenReturn
(
dest
);
when
(
mockFileSystem
.
file
(
'dest'
)).
thenReturn
(
dest
);
expect
(()
=>
fileSystem
.
file
(
'source'
).
copySync
(
'dest'
),
throwsToolExit
());
const
String
expectedMessage
=
'Flutter failed to copy source to dest due to destination location error.
\n
'
'Please ensure that the SDK and/or project is installed in a location that has read/write permissions for the current user.'
;
expect
(()
=>
fileSystem
.
file
(
'source'
).
copySync
(
'dest'
),
throwsToolExit
(
message:
expectedMessage
));
});
});
// dart:io is able to clobber read-only files.
// dart:io is able to clobber read-only files.
testWithoutContext
(
'copySync will copySync even if the destination is not writable'
,
()
{
testWithoutContext
(
'copySync will copySync even if the destination is not writable'
,
()
{
final
MockFile
source
=
MockFile
();
final
MockFile
source
=
MockFile
();
when
(
source
.
path
).
thenReturn
(
'source'
);
final
MockDirectory
parent
=
MockDirectory
();
when
(
parent
.
path
).
thenReturn
(
'destParent'
);
final
MockFile
dest
=
MockFile
();
final
MockFile
dest
=
MockFile
();
when
(
dest
.
parent
).
thenReturn
(
parent
);
when
(
source
.
copySync
(
any
)).
thenReturn
(
dest
);
when
(
source
.
copySync
(
any
)).
thenReturn
(
dest
);
when
(
mockFileSystem
.
file
(
'source'
)).
thenReturn
(
source
);
when
(
mockFileSystem
.
file
(
'source'
)).
thenReturn
(
source
);
...
@@ -955,7 +1158,11 @@ void main() {
...
@@ -955,7 +1158,11 @@ void main() {
testWithoutContext
(
'copySync will copySync if there are no exceptions'
,
()
{
testWithoutContext
(
'copySync will copySync if there are no exceptions'
,
()
{
final
MockFile
source
=
MockFile
();
final
MockFile
source
=
MockFile
();
when
(
source
.
path
).
thenReturn
(
'source'
);
final
MockDirectory
parent
=
MockDirectory
();
when
(
parent
.
path
).
thenReturn
(
'destParent'
);
final
MockFile
dest
=
MockFile
();
final
MockFile
dest
=
MockFile
();
when
(
dest
.
parent
).
thenReturn
(
parent
);
when
(
source
.
copySync
(
any
)).
thenReturn
(
dest
);
when
(
source
.
copySync
(
any
)).
thenReturn
(
dest
);
when
(
mockFileSystem
.
file
(
'source'
)).
thenReturn
(
source
);
when
(
mockFileSystem
.
file
(
'source'
)).
thenReturn
(
source
);
...
@@ -973,7 +1180,11 @@ void main() {
...
@@ -973,7 +1180,11 @@ void main() {
testWithoutContext
(
'copySync can directly copy bytes if both files can be opened but copySync fails'
,
()
{
testWithoutContext
(
'copySync can directly copy bytes if both files can be opened but copySync fails'
,
()
{
final
MemoryFileSystem
memoryFileSystem
=
MemoryFileSystem
.
test
();
final
MemoryFileSystem
memoryFileSystem
=
MemoryFileSystem
.
test
();
final
MockFile
source
=
MockFile
();
final
MockFile
source
=
MockFile
();
when
(
source
.
path
).
thenReturn
(
'source'
);
final
MockDirectory
parent
=
MockDirectory
();
when
(
parent
.
path
).
thenReturn
(
'destParent'
);
final
MockFile
dest
=
MockFile
();
final
MockFile
dest
=
MockFile
();
when
(
dest
.
parent
).
thenReturn
(
parent
);
final
List
<
int
>
expectedBytes
=
List
<
int
>.
generate
(
64
*
1024
+
3
,
(
int
i
)
=>
i
.
isEven
?
0
:
1
);
final
List
<
int
>
expectedBytes
=
List
<
int
>.
generate
(
64
*
1024
+
3
,
(
int
i
)
=>
i
.
isEven
?
0
:
1
);
final
File
memorySource
=
memoryFileSystem
.
file
(
'source'
)
final
File
memorySource
=
memoryFileSystem
.
file
(
'source'
)
..
writeAsBytesSync
(
expectedBytes
);
..
writeAsBytesSync
(
expectedBytes
);
...
@@ -997,7 +1208,11 @@ void main() {
...
@@ -997,7 +1208,11 @@ void main() {
testWithoutContext
(
'copySync deletes the result file if the fallback fails'
,
()
{
testWithoutContext
(
'copySync deletes the result file if the fallback fails'
,
()
{
final
MemoryFileSystem
memoryFileSystem
=
MemoryFileSystem
.
test
();
final
MemoryFileSystem
memoryFileSystem
=
MemoryFileSystem
.
test
();
final
MockFile
source
=
MockFile
();
final
MockFile
source
=
MockFile
();
when
(
source
.
path
).
thenReturn
(
'source'
);
final
MockDirectory
parent
=
MockDirectory
();
when
(
parent
.
path
).
thenReturn
(
'destParent'
);
final
MockFile
dest
=
MockFile
();
final
MockFile
dest
=
MockFile
();
when
(
dest
.
parent
).
thenReturn
(
parent
);
final
File
memorySource
=
memoryFileSystem
.
file
(
'source'
)
final
File
memorySource
=
memoryFileSystem
.
file
(
'source'
)
..
createSync
();
..
createSync
();
final
File
memoryDest
=
memoryFileSystem
.
file
(
'dest'
)
final
File
memoryDest
=
memoryFileSystem
.
file
(
'dest'
)
...
@@ -1020,7 +1235,12 @@ void main() {
...
@@ -1020,7 +1235,12 @@ void main() {
when
(
mockFileSystem
.
file
(
'source'
)).
thenReturn
(
source
);
when
(
mockFileSystem
.
file
(
'source'
)).
thenReturn
(
source
);
when
(
mockFileSystem
.
file
(
'dest'
)).
thenReturn
(
dest
);
when
(
mockFileSystem
.
file
(
'dest'
)).
thenReturn
(
dest
);
expect
(()
=>
fileSystem
.
file
(
'source'
).
copySync
(
'dest'
),
throwsToolExit
());
const
String
expectedMessage
=
'Flutter failed to copy source to dest due to unknown error.
\n
'
'Please ensure that the SDK and/or project is installed in a location that has read/write permissions for the current user.
\n
'
'Try running:
\n
'
r' sudo chown -R $(whoami) /source /destParent'
;
expect
(()
=>
fileSystem
.
file
(
'source'
).
copySync
(
'dest'
),
throwsToolExit
(
message:
expectedMessage
));
verify
(
dest
.
deleteSync
(
recursive:
true
)).
called
(
1
);
verify
(
dest
.
deleteSync
(
recursive:
true
)).
called
(
1
);
});
});
...
@@ -1036,3 +1256,14 @@ class FakeSignalProcessManager extends Fake implements ProcessManager {
...
@@ -1036,3 +1256,14 @@ class FakeSignalProcessManager extends Fake implements ProcessManager {
return
true
;
return
true
;
}
}
}
}
class
ThrowingFakeProcessManager
extends
Fake
implements
ProcessManager
{
ThrowingFakeProcessManager
(
Exception
exception
)
:
_exception
=
exception
;
final
Exception
_exception
;
@override
bool
canRun
(
dynamic
executable
,
{
String
workingDirectory
})
{
throw
_exception
;
}
}
packages/flutter_tools/test/general.shard/config_test.dart
View file @
c1c12aa3
...
@@ -104,8 +104,7 @@ void main() {
...
@@ -104,8 +104,7 @@ void main() {
config
=
Config
.
createForTesting
(
file
,
bufferLogger
);
config
=
Config
.
createForTesting
(
file
,
bufferLogger
);
expect
(
bufferLogger
.
errorText
,
contains
(
'Could not read preferences in testfile'
));
expect
(
bufferLogger
.
errorText
,
contains
(
'Could not read preferences in testfile'
));
// Also contains original error message:
expect
(
bufferLogger
.
errorText
,
contains
(
r'sudo chown -R $(whoami) /testfile'
));
expect
(
bufferLogger
.
errorText
,
contains
(
'The flutter tool cannot access the file or directory'
));
});
});
testWithoutContext
(
'Config in home dir is used if it exists'
,
()
{
testWithoutContext
(
'Config in home dir is used if it exists'
,
()
{
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment