Unverified Commit 6fddb7ee authored by Zachary Anderson's avatar Zachary Anderson Committed by GitHub

[flutter_tools] Handle full device when creating a temp directory (#53691)

parent 3a0d8377
...@@ -8,8 +8,8 @@ import 'dart:io' as io show Directory, File, Link; ...@@ -8,8 +8,8 @@ import 'dart:io' as io show Directory, File, Link;
import 'package:file/file.dart'; import 'package:file/file.dart';
import 'package:meta/meta.dart'; import 'package:meta/meta.dart';
import 'package:path/path.dart' as p; // ignore: package_path_import import 'package:path/path.dart' as p; // ignore: package_path_import
import 'package:platform/platform.dart';
import '../globals.dart' as globals;
import 'common.dart' show throwToolExit; import 'common.dart' show throwToolExit;
// The Flutter tool hits file system errors that only the end-user can address. // The Flutter tool hits file system errors that only the end-user can address.
...@@ -30,13 +30,40 @@ import 'common.dart' show throwToolExit; ...@@ -30,13 +30,40 @@ import 'common.dart' show throwToolExit;
/// example, the tool should gernerally be able to continue executing even if it /// example, the tool should gernerally be able to continue executing even if it
/// fails to delete a file. /// fails to delete a file.
class ErrorHandlingFileSystem extends ForwardingFileSystem { class ErrorHandlingFileSystem extends ForwardingFileSystem {
ErrorHandlingFileSystem(FileSystem delegate) : super(delegate); ErrorHandlingFileSystem({
@required FileSystem delegate,
@required Platform platform,
}) :
assert(delegate != null),
assert(platform != null),
_platform = platform,
super(delegate);
@visibleForTesting @visibleForTesting
FileSystem get fileSystem => delegate; FileSystem get fileSystem => delegate;
final Platform _platform;
@override
File file(dynamic path) => ErrorHandlingFile(
platform: _platform,
fileSystem: delegate,
delegate: delegate.file(path),
);
@override
Directory directory(dynamic path) => ErrorHandlingDirectory(
platform: _platform,
fileSystem: delegate,
delegate: delegate.directory(path),
);
@override @override
File file(dynamic path) => ErrorHandlingFile(delegate, delegate.file(path)); Link link(dynamic path) => ErrorHandlingLink(
platform: _platform,
fileSystem: delegate,
delegate: delegate.link(path),
);
// Caching the path context here and clearing when the currentDirectory setter // Caching the path context here and clearing when the currentDirectory setter
// is updated works since the flutter tool restricts usage of dart:io directly // is updated works since the flutter tool restricts usage of dart:io directly
...@@ -60,7 +87,15 @@ class ErrorHandlingFileSystem extends ForwardingFileSystem { ...@@ -60,7 +87,15 @@ class ErrorHandlingFileSystem extends ForwardingFileSystem {
class ErrorHandlingFile class ErrorHandlingFile
extends ForwardingFileSystemEntity<File, io.File> extends ForwardingFileSystemEntity<File, io.File>
with ForwardingFile { with ForwardingFile {
ErrorHandlingFile(this.fileSystem, this.delegate); ErrorHandlingFile({
@required Platform platform,
@required this.fileSystem,
@required this.delegate,
}) :
assert(platform != null),
assert(fileSystem != null),
assert(delegate != null),
_platform = platform;
@override @override
final io.File delegate; final io.File delegate;
...@@ -68,17 +103,28 @@ class ErrorHandlingFile ...@@ -68,17 +103,28 @@ class ErrorHandlingFile
@override @override
final FileSystem fileSystem; final FileSystem fileSystem;
final Platform _platform;
@override @override
File wrapFile(io.File delegate) => File wrapFile(io.File delegate) => ErrorHandlingFile(
ErrorHandlingFile(fileSystem, delegate); platform: _platform,
fileSystem: fileSystem,
delegate: delegate,
);
@override @override
Directory wrapDirectory(io.Directory delegate) => Directory wrapDirectory(io.Directory delegate) => ErrorHandlingDirectory(
ErrorHandlingDirectory(fileSystem, delegate); platform: _platform,
fileSystem: fileSystem,
delegate: delegate,
);
@override @override
Link wrapLink(io.Link delegate) => Link wrapLink(io.Link delegate) => ErrorHandlingLink(
ErrorHandlingLink(fileSystem, delegate); platform: _platform,
fileSystem: fileSystem,
delegate: delegate,
);
@override @override
Future<File> writeAsBytes( Future<File> writeAsBytes(
...@@ -92,6 +138,7 @@ class ErrorHandlingFile ...@@ -92,6 +138,7 @@ class ErrorHandlingFile
mode: mode, mode: mode,
flush: flush, flush: flush,
)), )),
platform: _platform,
failureMessage: 'Flutter failed to write to a file at "${delegate.path}"', failureMessage: 'Flutter failed to write to a file at "${delegate.path}"',
); );
} }
...@@ -104,6 +151,7 @@ class ErrorHandlingFile ...@@ -104,6 +151,7 @@ class ErrorHandlingFile
}) { }) {
_runSync<void>( _runSync<void>(
() => delegate.writeAsBytesSync(bytes, mode: mode, flush: flush), () => delegate.writeAsBytesSync(bytes, mode: mode, flush: flush),
platform: _platform,
failureMessage: 'Flutter failed to write to a file at "${delegate.path}"', failureMessage: 'Flutter failed to write to a file at "${delegate.path}"',
); );
} }
...@@ -122,6 +170,7 @@ class ErrorHandlingFile ...@@ -122,6 +170,7 @@ class ErrorHandlingFile
encoding: encoding, encoding: encoding,
flush: flush, flush: flush,
)), )),
platform: _platform,
failureMessage: 'Flutter failed to write to a file at "${delegate.path}"', failureMessage: 'Flutter failed to write to a file at "${delegate.path}"',
); );
} }
...@@ -140,69 +189,27 @@ class ErrorHandlingFile ...@@ -140,69 +189,27 @@ class ErrorHandlingFile
encoding: encoding, encoding: encoding,
flush: flush, flush: flush,
), ),
platform: _platform,
failureMessage: 'Flutter failed to write to a file at "${delegate.path}"', failureMessage: 'Flutter failed to write to a file at "${delegate.path}"',
); );
} }
@override @override
String toString() => delegate.toString(); String toString() => delegate.toString();
Future<T> _run<T>(Future<T> Function() op, { String failureMessage }) async {
try {
return await op();
} on FileSystemException catch (e) {
if (globals.platform.isWindows) {
_handleWindowsException(e, failureMessage);
}
rethrow;
}
}
T _runSync<T>(T Function() op, { String failureMessage }) {
try {
return op();
} on FileSystemException catch (e) {
if (globals.platform.isWindows) {
_handleWindowsException(e, failureMessage);
}
rethrow;
}
}
void _handleWindowsException(FileSystemException e, String message) {
// From:
// https://docs.microsoft.com/en-us/windows/win32/debug/system-error-codes
const int kDeviceFull = 112;
const int kUserMappedSectionOpened = 1224;
final int errorCode = e.osError?.errorCode ?? 0;
// Catch errors and bail when:
switch (errorCode) {
case kDeviceFull:
throwToolExit(
'$message. The target device is full.'
'\n$e\n'
'Free up space and try again.',
);
break;
case kUserMappedSectionOpened:
throwToolExit(
'$message. The file is being used by another program.'
'\n$e\n'
'Do you have an antivirus program running? '
'Try disabling your antivirus program and try again.',
);
break;
default:
// Caller must rethrow the exception.
break;
}
}
} }
class ErrorHandlingDirectory class ErrorHandlingDirectory
extends ForwardingFileSystemEntity<Directory, io.Directory> extends ForwardingFileSystemEntity<Directory, io.Directory>
with ForwardingDirectory<Directory> { with ForwardingDirectory<Directory> {
ErrorHandlingDirectory(this.fileSystem, this.delegate); ErrorHandlingDirectory({
@required Platform platform,
@required this.fileSystem,
@required this.delegate,
}) :
assert(platform != null),
assert(fileSystem != null),
assert(delegate != null),
_platform = platform;
@override @override
final io.Directory delegate; final io.Directory delegate;
...@@ -210,17 +217,28 @@ class ErrorHandlingDirectory ...@@ -210,17 +217,28 @@ class ErrorHandlingDirectory
@override @override
final FileSystem fileSystem; final FileSystem fileSystem;
final Platform _platform;
@override @override
File wrapFile(io.File delegate) => File wrapFile(io.File delegate) => ErrorHandlingFile(
ErrorHandlingFile(fileSystem, delegate); platform: _platform,
fileSystem: fileSystem,
delegate: delegate,
);
@override @override
Directory wrapDirectory(io.Directory delegate) => Directory wrapDirectory(io.Directory delegate) => ErrorHandlingDirectory(
ErrorHandlingDirectory(fileSystem, delegate); platform: _platform,
fileSystem: fileSystem,
delegate: delegate,
);
@override @override
Link wrapLink(io.Link delegate) => Link wrapLink(io.Link delegate) => ErrorHandlingLink(
ErrorHandlingLink(fileSystem, delegate); platform: _platform,
fileSystem: fileSystem,
delegate: delegate,
);
// For the childEntity methods, we first obtain an instance of the entity // For the childEntity methods, we first obtain an instance of the entity
// from the underlying file system, then invoke childEntity() on it, then // from the underlying file system, then invoke childEntity() on it, then
...@@ -237,6 +255,26 @@ class ErrorHandlingDirectory ...@@ -237,6 +255,26 @@ class ErrorHandlingDirectory
Link childLink(String basename) => Link childLink(String basename) =>
wrapLink(fileSystem.directory(delegate).childLink(basename)); wrapLink(fileSystem.directory(delegate).childLink(basename));
@override
Future<Directory> createTemp([String prefix]) {
return _run<Directory>(
() async => wrap(await delegate.createTemp(prefix)),
platform: _platform,
failureMessage:
'Flutter failed to create a temporary directory with prefix "$prefix"',
);
}
@override
Directory createTempSync([String prefix]) {
return _runSync<Directory>(
() => wrap(delegate.createTempSync(prefix)),
platform: _platform,
failureMessage:
'Flutter failed to create a temporary directory with prefix "$prefix"',
);
}
@override @override
String toString() => delegate.toString(); String toString() => delegate.toString();
} }
...@@ -244,7 +282,15 @@ class ErrorHandlingDirectory ...@@ -244,7 +282,15 @@ class ErrorHandlingDirectory
class ErrorHandlingLink class ErrorHandlingLink
extends ForwardingFileSystemEntity<Link, io.Link> extends ForwardingFileSystemEntity<Link, io.Link>
with ForwardingLink { with ForwardingLink {
ErrorHandlingLink(this.fileSystem, this.delegate); ErrorHandlingLink({
@required Platform platform,
@required this.fileSystem,
@required this.delegate,
}) :
assert(platform != null),
assert(fileSystem != null),
assert(delegate != null),
_platform = platform;
@override @override
final io.Link delegate; final io.Link delegate;
...@@ -252,18 +298,113 @@ class ErrorHandlingLink ...@@ -252,18 +298,113 @@ class ErrorHandlingLink
@override @override
final FileSystem fileSystem; final FileSystem fileSystem;
final Platform _platform;
@override @override
File wrapFile(io.File delegate) => File wrapFile(io.File delegate) => ErrorHandlingFile(
ErrorHandlingFile(fileSystem, delegate); platform: _platform,
fileSystem: fileSystem,
delegate: delegate,
);
@override @override
Directory wrapDirectory(io.Directory delegate) => Directory wrapDirectory(io.Directory delegate) => ErrorHandlingDirectory(
ErrorHandlingDirectory(fileSystem, delegate); platform: _platform,
fileSystem: fileSystem,
delegate: delegate,
);
@override @override
Link wrapLink(io.Link delegate) => Link wrapLink(io.Link delegate) => ErrorHandlingLink(
ErrorHandlingLink(fileSystem, delegate); platform: _platform,
fileSystem: fileSystem,
delegate: delegate,
);
@override @override
String toString() => delegate.toString(); String toString() => delegate.toString();
} }
Future<T> _run<T>(Future<T> Function() op, {
@required Platform platform,
String failureMessage,
}) async {
assert(platform != null);
try {
return await op();
} on FileSystemException catch (e) {
if (platform.isWindows) {
_handleWindowsException(e, failureMessage);
} else if (platform.isLinux) {
_handleLinuxException(e, failureMessage);
}
rethrow;
}
}
T _runSync<T>(T Function() op, {
@required Platform platform,
String failureMessage,
}) {
assert(platform != null);
try {
return op();
} on FileSystemException catch (e) {
if (platform.isWindows) {
_handleWindowsException(e, failureMessage);
} else if (platform.isLinux) {
_handleLinuxException(e, failureMessage);
}
rethrow;
}
}
void _handleLinuxException(FileSystemException e, String message) {
// 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
const int enospc = 28;
final int errorCode = e.osError?.errorCode ?? 0;
// Catch errors and bail when:
switch (errorCode) {
case enospc:
throwToolExit(
'$message. The target device is full.'
'\n$e\n'
'Free up space and try again.',
);
break;
default:
// Caller must rethrow the exception.
break;
}
}
void _handleWindowsException(FileSystemException e, String message) {
// From:
// https://docs.microsoft.com/en-us/windows/win32/debug/system-error-codes
const int kDeviceFull = 112;
const int kUserMappedSectionOpened = 1224;
final int errorCode = e.osError?.errorCode ?? 0;
// Catch errors and bail when:
switch (errorCode) {
case kDeviceFull:
throwToolExit(
'$message. The target device is full.'
'\n$e\n'
'Free up space and try again.',
);
break;
case kUserMappedSectionOpened:
throwToolExit(
'$message. The file is being used by another program.'
'\n$e\n'
'Do you have an antivirus program running? '
'Try disabling your antivirus program and try again.',
);
break;
default:
// Caller must rethrow the exception.
break;
}
}
...@@ -52,7 +52,8 @@ const FileSystem _kLocalFs = LocalFileSystem(); ...@@ -52,7 +52,8 @@ const FileSystem _kLocalFs = LocalFileSystem();
/// By default it uses local disk-based implementation. Override this in tests /// By default it uses local disk-based implementation. Override this in tests
/// with [MemoryFileSystem]. /// with [MemoryFileSystem].
FileSystem get fs => ErrorHandlingFileSystem( FileSystem get fs => ErrorHandlingFileSystem(
context.get<FileSystem>() ?? _kLocalFs, delegate: context.get<FileSystem>() ?? _kLocalFs,
platform: platform,
); );
final FileSystemUtils _defaultFileSystemUtils = FileSystemUtils( final FileSystemUtils _defaultFileSystemUtils = FileSystemUtils(
......
...@@ -10,8 +10,6 @@ import 'package:platform/platform.dart'; ...@@ -10,8 +10,6 @@ import 'package:platform/platform.dart';
import 'package:path/path.dart' as path; // ignore: package_path_import import 'package:path/path.dart' as path; // ignore: package_path_import
import '../../src/common.dart'; import '../../src/common.dart';
import '../../src/context.dart';
import '../../src/testbed.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 {}
...@@ -23,93 +21,200 @@ final Platform windowsPlatform = FakePlatform( ...@@ -23,93 +21,200 @@ final Platform windowsPlatform = FakePlatform(
environment: <String, String>{} environment: <String, String>{}
); );
final Platform linuxPlatform = FakePlatform(
operatingSystem: 'linux',
environment: <String, String>{}
);
void setupWriteMocks({
FileSystem mockFileSystem,
ErrorHandlingFileSystem fs,
int errorCode,
}) {
final MockFile mockFile = MockFile();
when(mockFileSystem.file(any)).thenReturn(mockFile);
when(mockFile.writeAsBytes(
any,
mode: anyNamed('mode'),
flush: anyNamed('flush'),
)).thenAnswer((_) async {
throw FileSystemException('', '', OSError('', errorCode));
});
when(mockFile.writeAsString(
any,
mode: anyNamed('mode'),
encoding: anyNamed('encoding'),
flush: anyNamed('flush'),
)).thenAnswer((_) async {
throw FileSystemException('', '', OSError('', errorCode));
});
when(mockFile.writeAsBytesSync(
any,
mode: anyNamed('mode'),
flush: anyNamed('flush'),
)).thenThrow(FileSystemException('', '', OSError('', errorCode)));
when(mockFile.writeAsStringSync(
any,
mode: anyNamed('mode'),
encoding: anyNamed('encoding'),
flush: anyNamed('flush'),
)).thenThrow(FileSystemException('', '', OSError('', errorCode)));
}
void setupCreateTempMocks({
FileSystem mockFileSystem,
ErrorHandlingFileSystem fs,
int errorCode,
}) {
final MockDirectory mockDirectory = MockDirectory();
when(mockFileSystem.directory(any)).thenReturn(mockDirectory);
when(mockDirectory.createTemp(any)).thenAnswer((_) async {
throw FileSystemException('', '', OSError('', errorCode));
});
when(mockDirectory.createTempSync(any))
.thenThrow(FileSystemException('', '', OSError('', errorCode)));
}
void main() { void main() {
group('throws ToolExit on Windows', () { group('throws ToolExit on Windows', () {
const int kDeviceFull = 112; const int kDeviceFull = 112;
const int kUserMappedSectionOpened = 1224; const int kUserMappedSectionOpened = 1224;
Testbed testbed;
MockFileSystem mockFileSystem; MockFileSystem mockFileSystem;
ErrorHandlingFileSystem fs; ErrorHandlingFileSystem fs;
setUp(() { setUp(() {
mockFileSystem = MockFileSystem(); mockFileSystem = MockFileSystem();
fs = ErrorHandlingFileSystem(mockFileSystem); fs = ErrorHandlingFileSystem(
delegate: mockFileSystem,
platform: windowsPlatform,
);
when(mockFileSystem.path).thenReturn(MockPathContext()); when(mockFileSystem.path).thenReturn(MockPathContext());
testbed = Testbed(overrides: <Type, Generator>{
Platform: () => windowsPlatform,
});
}); });
void writeTests({ testWithoutContext('when writing to a full device', () async {
String testName, setupWriteMocks(
int errorCode, mockFileSystem: mockFileSystem,
String expectedMessage, fs: fs,
}) { errorCode: kDeviceFull,
test(testName, () => testbed.run(() async { );
final MockFile mockFile = MockFile();
when(mockFileSystem.file(any)).thenReturn(mockFile); final File file = fs.file('file');
when(mockFile.writeAsBytes(
any, const String expectedMessage = 'The target device is full';
mode: anyNamed('mode'), expect(() async => await file.writeAsBytes(<int>[0]),
flush: anyNamed('flush'), throwsToolExit(message: expectedMessage));
)).thenAnswer((_) async { expect(() async => await file.writeAsString(''),
throw FileSystemException('', '', OSError('', errorCode)); throwsToolExit(message: expectedMessage));
}); expect(() => file.writeAsBytesSync(<int>[0]),
when(mockFile.writeAsString( throwsToolExit(message: expectedMessage));
any, expect(() => file.writeAsStringSync(''),
mode: anyNamed('mode'), throwsToolExit(message: expectedMessage));
encoding: anyNamed('encoding'), });
flush: anyNamed('flush'),
)).thenAnswer((_) async { testWithoutContext('when the file is being used by another program', () async {
throw FileSystemException('', '', OSError('', errorCode)); setupWriteMocks(
}); mockFileSystem: mockFileSystem,
when(mockFile.writeAsBytesSync( fs: fs,
any, errorCode: kUserMappedSectionOpened,
mode: anyNamed('mode'), );
flush: anyNamed('flush'),
)).thenThrow(FileSystemException('', '', OSError('', errorCode))); final File file = fs.file('file');
when(mockFile.writeAsStringSync(
any, const String expectedMessage = 'The file is being used by another program';
mode: anyNamed('mode'), expect(() async => await file.writeAsBytes(<int>[0]),
encoding: anyNamed('encoding'), throwsToolExit(message: expectedMessage));
flush: anyNamed('flush'), expect(() async => await file.writeAsString(''),
)).thenThrow(FileSystemException('', '', OSError('', errorCode))); throwsToolExit(message: expectedMessage));
expect(() => file.writeAsBytesSync(<int>[0]),
final File file = fs.file('file'); throwsToolExit(message: expectedMessage));
expect(() => file.writeAsStringSync(''),
expect(() async => await file.writeAsBytes(<int>[0]), throwsToolExit(message: expectedMessage));
throwsToolExit(message: expectedMessage)); });
expect(() async => await file.writeAsString(''),
throwsToolExit(message: expectedMessage)); testWithoutContext('when creating a temporary dir on a full device', () async {
expect(() => file.writeAsBytesSync(<int>[0]), setupCreateTempMocks(
throwsToolExit(message: expectedMessage)); mockFileSystem: mockFileSystem,
expect(() => file.writeAsStringSync(''), fs: fs,
throwsToolExit(message: expectedMessage)); errorCode: kDeviceFull,
})); );
}
final Directory directory = fs.directory('directory');
writeTests(
testName: 'when writing to a full device', const String expectedMessage = 'The target device is full';
errorCode: kDeviceFull, expect(() async => await directory.createTemp('prefix'),
expectedMessage: 'The target device is full', throwsToolExit(message: expectedMessage));
); expect(() => directory.createTempSync('prefix'),
writeTests( throwsToolExit(message: expectedMessage));
testName: 'when the file is being used by another program', });
errorCode: kUserMappedSectionOpened,
expectedMessage: 'The file is being used by another program',
);
}); });
test('Caches path context correctly', () { group('throws ToolExit on Linux', () {
const int enospc= 28;
MockFileSystem mockFileSystem;
ErrorHandlingFileSystem fs;
setUp(() {
mockFileSystem = MockFileSystem();
fs = ErrorHandlingFileSystem(
delegate: mockFileSystem,
platform: linuxPlatform,
);
when(mockFileSystem.path).thenReturn(MockPathContext());
});
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 {
setupCreateTempMocks(
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));
});
});
testWithoutContext('Caches path context correctly', () {
final MockFileSystem mockFileSystem = MockFileSystem(); final MockFileSystem mockFileSystem = MockFileSystem();
final FileSystem fs = ErrorHandlingFileSystem(mockFileSystem); final FileSystem fs = ErrorHandlingFileSystem(
delegate: mockFileSystem,
platform: const LocalPlatform(),
);
expect(identical(fs.path, fs.path), true); expect(identical(fs.path, fs.path), true);
}); });
test('Clears cache when CWD changes', () { testWithoutContext('Clears cache when CWD changes', () {
final MockFileSystem mockFileSystem = MockFileSystem(); final MockFileSystem mockFileSystem = MockFileSystem();
final FileSystem fs = ErrorHandlingFileSystem(mockFileSystem); final FileSystem fs = ErrorHandlingFileSystem(
delegate: mockFileSystem,
platform: const LocalPlatform(),
);
final Object firstPath = fs.path; final Object firstPath = fs.path;
...@@ -119,8 +224,11 @@ void main() { ...@@ -119,8 +224,11 @@ void main() {
expect(identical(firstPath, fs.path), false); expect(identical(firstPath, fs.path), false);
}); });
test('Throws type error if Directory type is set to curentDirectory with LocalFileSystem', () { testWithoutContext('Throws type error if Directory type is set to curentDirectory with LocalFileSystem', () {
final FileSystem fs = ErrorHandlingFileSystem(const LocalFileSystem()); final FileSystem fs = ErrorHandlingFileSystem(
delegate: const LocalFileSystem(),
platform: const LocalPlatform(),
);
final MockDirectory directory = MockDirectory(); final MockDirectory directory = MockDirectory();
when(directory.path).thenReturn('path'); when(directory.path).thenReturn('path');
...@@ -128,17 +236,23 @@ void main() { ...@@ -128,17 +236,23 @@ void main() {
}); });
group('toString() gives toString() of delegate', () { group('toString() gives toString() of delegate', () {
test('ErrorHandlingFileSystem', () { testWithoutContext('ErrorHandlingFileSystem', () {
final MockFileSystem mockFileSystem = MockFileSystem(); final MockFileSystem mockFileSystem = MockFileSystem();
final FileSystem fs = ErrorHandlingFileSystem(mockFileSystem); final FileSystem fs = ErrorHandlingFileSystem(
delegate: mockFileSystem,
platform: const LocalPlatform(),
);
expect(mockFileSystem.toString(), isNotNull); expect(mockFileSystem.toString(), isNotNull);
expect(fs.toString(), equals(mockFileSystem.toString())); expect(fs.toString(), equals(mockFileSystem.toString()));
}); });
test('ErrorHandlingFile', () { testWithoutContext('ErrorHandlingFile', () {
final MockFileSystem mockFileSystem = MockFileSystem(); final MockFileSystem mockFileSystem = MockFileSystem();
final FileSystem fs = ErrorHandlingFileSystem(mockFileSystem); final FileSystem fs = ErrorHandlingFileSystem(
delegate: mockFileSystem,
platform: const LocalPlatform(),
);
final MockFile mockFile = MockFile(); final MockFile mockFile = MockFile();
when(mockFileSystem.file(any)).thenReturn(mockFile); when(mockFileSystem.file(any)).thenReturn(mockFile);
......
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