diff --git a/pkgs/io_file/constants.json b/pkgs/io_file/constants.json index 3635513c..2f05dec5 100644 --- a/pkgs/io_file/constants.json +++ b/pkgs/io_file/constants.json @@ -48,5 +48,8 @@ "": [ "AT_FDCWD", "AT_REMOVEDIR" + ], + "": [ + "PATH_MAX" ] } \ No newline at end of file diff --git a/pkgs/io_file/dart_test.yaml b/pkgs/io_file/dart_test.yaml new file mode 100644 index 00000000..f11f1a15 --- /dev/null +++ b/pkgs/io_file/dart_test.yaml @@ -0,0 +1,3 @@ +concurrency: 1 +reporter: compact +test-randomize-ordering-seed: random diff --git a/pkgs/io_file/lib/src/constant_bindings.g.dart b/pkgs/io_file/lib/src/constant_bindings.g.dart index bed9de56..d209cf44 100644 --- a/pkgs/io_file/lib/src/constant_bindings.g.dart +++ b/pkgs/io_file/lib/src/constant_bindings.g.dart @@ -49,6 +49,9 @@ external int get_AT_FDCWD(); @ffi.Native(symbol: 'libc_shim_get_AT_REMOVEDIR') external int get_AT_REMOVEDIR(); +@ffi.Native(symbol: 'libc_shim_get_PATH_MAX') +external int get_PATH_MAX(); + @ffi.Native(symbol: 'libc_shim_get_S_IEXEC') external int get_S_IEXEC(); diff --git a/pkgs/io_file/lib/src/constants.g.dart b/pkgs/io_file/lib/src/constants.g.dart index 0c31f77a..dba1e09d 100644 --- a/pkgs/io_file/lib/src/constants.g.dart +++ b/pkgs/io_file/lib/src/constants.g.dart @@ -131,6 +131,15 @@ int get AT_REMOVEDIR { } } +int get PATH_MAX { + final v = get_PATH_MAX(); + if (v == libc_shim_UNDEFINED) { + throw UnsupportedError('PATH_MAX'); + } else { + return v; + } +} + int get S_IEXEC { final v = get_S_IEXEC(); if (v == libc_shim_UNDEFINED) { diff --git a/pkgs/io_file/lib/src/file_system.dart b/pkgs/io_file/lib/src/file_system.dart index eb815410..a5612754 100644 --- a/pkgs/io_file/lib/src/file_system.dart +++ b/pkgs/io_file/lib/src/file_system.dart @@ -86,6 +86,24 @@ base class FileSystem { throw UnsupportedError('createTemporaryDirectory'); } + /// The current + /// [working directory](https://en.wikipedia.org/wiki/Working_directory) of + /// the Dart process. + /// + /// Setting the value of this field will change the working directory for + /// *all* isolates. + /// + /// On Windows, unless + /// [long paths are enabled](https://learn.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation), + /// the maximum length of the [currentDirectory] path is 260 characters. + String get currentDirectory { + throw UnsupportedError('currentDirectory'); + } + + set currentDirectory(String path) { + throw UnsupportedError('currentDirectory'); + } + /// Deletes the directory at the given path. /// /// If `path` is a directory but the directory is not empty, then diff --git a/pkgs/io_file/lib/src/libc_bindings.g.dart b/pkgs/io_file/lib/src/libc_bindings.g.dart index ecfcc5e4..d0fc37bd 100644 --- a/pkgs/io_file/lib/src/libc_bindings.g.dart +++ b/pkgs/io_file/lib/src/libc_bindings.g.dart @@ -78,9 +78,17 @@ external ffi.Pointer mkdtemp(ffi.Pointer template); external ffi.Pointer strerror(int errnum); /// +@ffi.Native)>(symbol: 'libc_shim_chdir') +external int chdir(ffi.Pointer path); + @ffi.Native(symbol: 'libc_shim_close') external int close(int fd); +@ffi.Native Function(ffi.Pointer, ffi.Int64)>( + symbol: 'libc_shim_getcwd', +) +external ffi.Pointer getcwd(ffi.Pointer buf, int size); + @ffi.Native, ffi.Int)>( symbol: 'libc_shim_unlinkat', ) diff --git a/pkgs/io_file/lib/src/vm_posix_file_system.dart b/pkgs/io_file/lib/src/vm_posix_file_system.dart index 826c12da..bb1dbe9c 100644 --- a/pkgs/io_file/lib/src/vm_posix_file_system.dart +++ b/pkgs/io_file/lib/src/vm_posix_file_system.dart @@ -21,7 +21,7 @@ const _defaultMode = 438; // => 0666 => rw-rw-rw- /// The default `mode` to use when creating a directory. const _defaultDirectoryMode = 511; // => 0777 => rwxrwxrwx -Exception _getError(int err, String message, String path) { +Exception _getError(int err, String message, [String? path]) { //TODO(brianquinlan): In the long-term, do we need to avoid exceptions that // are part of `dart:io`? Can we move those exceptions into a different // namespace? @@ -30,12 +30,16 @@ Exception _getError(int err, String message, String path) { err, ); - if (err == libc.EPERM || err == libc.EACCES) { - return io.PathAccessException(path, osError, message); - } else if (err == libc.EEXIST) { - return io.PathExistsException(path, osError, message); - } else if (err == libc.ENOENT) { - return io.PathNotFoundException(path, osError, message); + if (path != null) { + if (err == libc.EPERM || err == libc.EACCES) { + return io.PathAccessException(path, osError, message); + } else if (err == libc.EEXIST) { + return io.PathExistsException(path, osError, message); + } else if (err == libc.ENOENT) { + return io.PathNotFoundException(path, osError, message); + } else { + return io.FileSystemException(message, path, osError); + } } else { return io.FileSystemException(message, path, osError); } @@ -95,6 +99,24 @@ base class PosixFileSystem extends FileSystem { } }); + @override + set currentDirectory(String path) => ffi.using((arena) { + if (libc.chdir(path.toNativeUtf8(allocator: arena).cast()) == -1) { + final errno = libc.errno; + throw _getError(errno, 'chdir failed', path); + } + }); + + @override + String get currentDirectory => ffi.using((arena) { + final buffer = arena(libc.PATH_MAX); + if (libc.getcwd(buffer, libc.PATH_MAX) == nullptr) { + final errno = libc.errno; + throw _getError(errno, 'getcwd failed', null); + } + return buffer.cast().toDartString(); + }); + @override String createTemporaryDirectory({String? parent, String? prefix}) => ffi.using((arena) { diff --git a/pkgs/io_file/lib/src/vm_windows_file_system.dart b/pkgs/io_file/lib/src/vm_windows_file_system.dart index 8dcfe9a8..101064d5 100644 --- a/pkgs/io_file/lib/src/vm_windows_file_system.dart +++ b/pkgs/io_file/lib/src/vm_windows_file_system.dart @@ -23,6 +23,33 @@ void _primeGetLastError() { win32.GetLastError(); } +Pointer _apiPath(String path, Allocator a) => + (r'\\?\' + p.canonicalize(path)).toNativeUtf16(allocator: a); + +/* +Pointer _apiPath(String path, Allocator a) { + var length = 256; + var utf16Path = path.toNativeUtf16(allocator: a); + do { + final buffer = win32.wsalloc(length); + try { + final result = win32.GetFullPathName(utf16Path, length, buffer, nullptr); + if (result == 0) { + final errorCode = win32.GetLastError(); + throw _getError(errorCode, 'GetFullPathName failed', path); + } + if (result < length) { + win32.PathAllocCanonicalize() + } else { + length = result; + } + } finally { + win32.free(buffer); + } + } while (true); +} +*/ + String _formatMessage(int errorCode) { final buffer = win32.wsalloc(1024); try { @@ -130,8 +157,7 @@ base class WindowsFileSystem extends FileSystem { void createDirectory(String path) => using((arena) { _primeGetLastError(); - if (win32.CreateDirectory(path.toNativeUtf16(allocator: arena), nullptr) == - win32.FALSE) { + if (win32.CreateDirectory(_apiPath(path, arena), nullptr) == win32.FALSE) { final errorCode = win32.GetLastError(); throw _getError(errorCode, 'create directory failed', path); } @@ -148,6 +174,44 @@ base class WindowsFileSystem extends FileSystem { return path; } + @override + set currentDirectory(String path) => using((arena) { + // XXX + // SetCurrentDirectory does not actually support paths larger than MAX_PATH, + // this limitation is due to the size of the internal buffer used for + // storing + // current directory. In Windows 10, version 1607, changes have been made + // to the OS to lift MAX_PATH limitations from file and directory management + // APIs, but both application and OS need to opt-in into new behavior. + // See https://learn.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation?tabs=registry#enable-long-paths-in-windows-10-version-1607-and-later + + _primeGetLastError(); + if (win32.SetCurrentDirectory(path.toNativeUtf16()) == win32.FALSE) { + final errorCode = win32.GetLastError(); + throw _getError(errorCode, 'SetCurrentDirectory failed', path); + } + }); + + @override + String get currentDirectory => using((arena) { + _primeGetLastError(); + + var length = 256; + do { + final buffer = win32.wsalloc(length); + try { + final result = win32.GetCurrentDirectory(length, buffer); + if (result < length) { + return buffer.toDartString(); + } else { + length = result; + } + } finally { + win32.free(buffer); + } + } while (true); + }); + @override void removeDirectory(String path) => using((arena) { _primeGetLastError(); @@ -247,7 +311,7 @@ base class WindowsFileSystem extends FileSystem { _primeGetLastError(); final f = win32.CreateFile( - path.toNativeUtf16(), + _apiPath(path, arena), win32.GENERIC_READ | win32.FILE_SHARE_READ, win32.FILE_SHARE_READ | win32.FILE_SHARE_WRITE, nullptr, @@ -383,7 +447,7 @@ base class WindowsFileSystem extends FileSystem { }; final f = win32.CreateFile( - path.toNativeUtf16(allocator: arena), + _apiPath(path, arena), mode == WriteMode.appendExisting ? win32.FILE_APPEND_DATA : win32.FILE_GENERIC_WRITE, diff --git a/pkgs/io_file/pubspec.yaml b/pkgs/io_file/pubspec.yaml index d5ed591d..b8a29f7b 100644 --- a/pkgs/io_file/pubspec.yaml +++ b/pkgs/io_file/pubspec.yaml @@ -14,7 +14,7 @@ dependencies: logging: ^1.3.0 native_toolchain_c: ^0.16.0 path: ^1.9.1 - win32: ^5.11.0 + win32: ^5.13.0 dev_dependencies: args: ^2.7.0 diff --git a/pkgs/io_file/src/constants.g.c b/pkgs/io_file/src/constants.g.c index 07f52f40..7eb9de23 100644 --- a/pkgs/io_file/src/constants.g.c +++ b/pkgs/io_file/src/constants.g.c @@ -7,6 +7,7 @@ #include #include +#include #include int64_t libc_shim_get_EACCES(void) { #ifdef EACCES @@ -106,6 +107,13 @@ int64_t libc_shim_get_AT_REMOVEDIR(void) { #endif return libc_shim_UNDEFINED; } +int64_t libc_shim_get_PATH_MAX(void) { +#ifdef PATH_MAX + assert(PATH_MAX != libc_shim_UNDEFINED); + return PATH_MAX; +#endif + return libc_shim_UNDEFINED; +} int64_t libc_shim_get_S_IEXEC(void) { #ifdef S_IEXEC assert(S_IEXEC != libc_shim_UNDEFINED); diff --git a/pkgs/io_file/src/constants.g.h b/pkgs/io_file/src/constants.g.h index 2d724b4c..780c0c7b 100644 --- a/pkgs/io_file/src/constants.g.h +++ b/pkgs/io_file/src/constants.g.h @@ -38,6 +38,8 @@ int64_t libc_shim_get_AT_FDCWD(void); __attribute__((visibility("default"))) __attribute__((used)) int64_t libc_shim_get_AT_REMOVEDIR(void); __attribute__((visibility("default"))) __attribute__((used)) +int64_t libc_shim_get_PATH_MAX(void); +__attribute__((visibility("default"))) __attribute__((used)) int64_t libc_shim_get_S_IEXEC(void); __attribute__((visibility("default"))) __attribute__((used)) int64_t libc_shim_get_S_IFBLK(void); diff --git a/pkgs/io_file/src/libc_shim.c b/pkgs/io_file/src/libc_shim.c index ac86db45..41eaf6a5 100644 --- a/pkgs/io_file/src/libc_shim.c +++ b/pkgs/io_file/src/libc_shim.c @@ -142,8 +142,12 @@ char *libc_shim_strerror(int errnum) { return strerror(errnum); } // +int libc_shim_chdir(const char *path) { return chdir(path); } + int libc_shim_close(int fd) { return close(fd); } +char *libc_shim_getcwd(char *buf, int64_t size) { return getcwd(buf, size); } + int libc_shim_unlinkat(int dirfd, const char *pathname, int flags) { return unlinkat(dirfd, pathname, flags); } diff --git a/pkgs/io_file/src/libc_shim.h b/pkgs/io_file/src/libc_shim.h index 0ebe26b5..a2a89924 100644 --- a/pkgs/io_file/src/libc_shim.h +++ b/pkgs/io_file/src/libc_shim.h @@ -106,6 +106,8 @@ LIBC_SHIM_EXPORT char *libc_shim_strerror(int errnum); // +LIBC_SHIM_EXPORT int libc_shim_chdir(const char *path); LIBC_SHIM_EXPORT int libc_shim_close(int fd); +LIBC_SHIM_EXPORT char *libc_shim_getcwd(char *buf, int64_t size); LIBC_SHIM_EXPORT int libc_shim_unlinkat(int dirfd, const char *pathname, int flags); diff --git a/pkgs/io_file/test/create_directory_test.dart b/pkgs/io_file/test/create_directory_test.dart index fb0f8672..cff3e883 100644 --- a/pkgs/io_file/test/create_directory_test.dart +++ b/pkgs/io_file/test/create_directory_test.dart @@ -8,6 +8,7 @@ library; import 'dart:io'; import 'package:io_file/io_file.dart'; +import 'package:path/path.dart' as p; import 'package:test/test.dart'; import 'package:win32/win32.dart' as win32; @@ -17,12 +18,18 @@ import 'test_utils.dart'; void main() { group('createDirectory', () { late String tmp; + late String cwd; - setUp(() => tmp = createTemp('createDirectory')); - - tearDown(() => deleteTemp(tmp)); + setUp(() { + tmp = createTemp('createDirectory'); + cwd = fileSystem.currentDirectory; + fileSystem.currentDirectory = tmp; + }); - //TODO(brianquinlan): test with a very long path. + tearDown(() { + fileSystem.currentDirectory = cwd; + deleteTemp(tmp); + }); test('success', () { final path = '$tmp/dir'; @@ -31,6 +38,75 @@ void main() { expect(FileSystemEntity.isDirectorySync(path), isTrue); }); + test('absolute path, long directory name', () { + // On Windows: + // When using an API to create a directory, the specified path cannot be + // so long that you cannot append an 8.3 file name (that is, the directory + // name cannot exceed MAX_PATH minus 12). + final dirname = ''.padLeft( + Platform.isWindows ? win32.MAX_PATH - 12 : 255, + 'd', + ); + final path = p.join(tmp, dirname); + + fileSystem.createDirectory(path); + expect(FileSystemEntity.isDirectorySync(path), isTrue); + }); + + test('absolute path, too long directory name', () { + final path = p.join(tmp, ''.padRight(256, 'd')); + + expect( + () => fileSystem.createDirectory(path), + throwsA( + isA() + .having((e) => e.message, 'message', 'create directory failed') + .having((e) => e.path, 'path', path) + .having( + (e) => e.osError?.errorCode, + 'errorCode', + Platform.isWindows + ? win32.ERROR_INVALID_NAME + : errors.enametoolong, + ), + ), + ); + }); + + test('relative path, long directory name', () { + // On Windows: + // When using an API to create a directory, the specified path cannot be + // so long that you cannot append an 8.3 file name (that is, the directory + // name cannot exceed MAX_PATH minus 12). + final path = ''.padLeft( + Platform.isWindows ? win32.MAX_PATH - 12 : 255, + 'd', + ); + fileSystem.createDirectory(path); + + expect(FileSystemEntity.isDirectorySync(path), isTrue); + }); + + test('relative path, too long directory name', () { + final path = ''.padRight(256, 'd'); + + expect( + () => fileSystem.createDirectory(path), + throwsA( + isA() + .having((e) => e.message, 'message', 'create directory failed') + .having((e) => e.path, 'path', path) + .having( + (e) => e.osError?.errorCode, + 'errorCode', + Platform.isWindows + ? win32.ERROR_INVALID_NAME + : errors.enametoolong, + ), + ), + ); + }); + test('create in non-existent directory', () { final path = '$tmp/foo/dir'; @@ -39,6 +115,7 @@ void main() { throwsA( isA() .having((e) => e.message, 'message', 'create directory failed') + .having((e) => e.path, 'path', path) .having( (e) => e.osError?.errorCode, 'errorCode', @@ -57,6 +134,41 @@ void main() { throwsA( isA() .having((e) => e.message, 'message', 'create directory failed') + .having((e) => e.path, 'path', path) + .having( + (e) => e.osError?.errorCode, + 'errorCode', + Platform.isWindows ? win32.ERROR_ALREADY_EXISTS : errors.eexist, + ), + ), + ); + }); + + test('create "."', () { + const path = '.'; + expect( + () => fileSystem.createDirectory(path), + throwsA( + isA() + .having((e) => e.message, 'message', 'create directory failed') + .having((e) => e.path, 'path', path) + .having( + (e) => e.osError?.errorCode, + 'errorCode', + Platform.isWindows ? win32.ERROR_ALREADY_EXISTS : errors.eexist, + ), + ), + ); + }); + + test('create ".."', () { + const path = '..'; + expect( + () => fileSystem.createDirectory(path), + throwsA( + isA() + .having((e) => e.message, 'message', 'create directory failed') + .having((e) => e.path, 'path', path) .having( (e) => e.osError?.errorCode, 'errorCode', @@ -75,6 +187,7 @@ void main() { throwsA( isA() .having((e) => e.message, 'message', 'create directory failed') + .having((e) => e.path, 'path', path) .having( (e) => e.osError?.errorCode, 'errorCode', diff --git a/pkgs/io_file/test/create_temporary_directory_test.dart b/pkgs/io_file/test/create_temporary_directory_test.dart index 1df8ef1e..031b190f 100644 --- a/pkgs/io_file/test/create_temporary_directory_test.dart +++ b/pkgs/io_file/test/create_temporary_directory_test.dart @@ -18,12 +18,18 @@ import 'test_utils.dart'; void main() { group('createTemporaryDirectory', () { late String tmp; + late String cwd; - setUp(() => tmp = createTemp('createTemporaryDirectory')); - - tearDown(() => deleteTemp(tmp)); + setUp(() { + tmp = createTemp('createTemporaryDirectory'); + cwd = fileSystem.currentDirectory; + fileSystem.currentDirectory = tmp; + }); - //TODO(brianquinlan): test with a very long path. + tearDown(() { + fileSystem.currentDirectory = cwd; + deleteTemp(tmp); + }); test('no arguments', () { final tmp1 = fileSystem.createTemporaryDirectory(); diff --git a/pkgs/io_file/test/current_directory_test.dart b/pkgs/io_file/test/current_directory_test.dart new file mode 100644 index 00000000..34f0d53f --- /dev/null +++ b/pkgs/io_file/test/current_directory_test.dart @@ -0,0 +1,96 @@ +// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +@TestOn('vm') +library; + +import 'dart:io'; + +import 'package:io_file/io_file.dart'; +import 'package:path/path.dart' as p; +import 'package:test/test.dart'; +import 'package:win32/win32.dart' as win32; + +import 'test_utils.dart'; + +void main() { + group('currentDirectory', () { + late String tmp; + late String cwd; + + setUp(() { + tmp = createTemp('currentDirectory'); + cwd = fileSystem.currentDirectory; + fileSystem.currentDirectory = tmp; + }); + + tearDown(() { + fileSystem.currentDirectory = cwd; + deleteTemp(tmp); + }); + + //TODO(brianquinlan): test with a very long path. + + test('absolute path', () { + final path = '$tmp/dir'; + Directory(path).createSync(recursive: true); + + fileSystem.currentDirectory = path; + + expect( + fileSystem.same(fileSystem.currentDirectory, path), + isTrue, + reason: + '${fileSystem.currentDirectory} is a diffent directory than' + '$path', + ); + expect( + p.isAbsolute(fileSystem.currentDirectory), + isTrue, + reason: '${fileSystem.currentDirectory} is not absolute', + ); + }); + + test('absolute path, too long path', () { + // On Windows, limited to MAX_PATH (260) characters. + final path = p.join(tmp, ''.padLeft(200, 'a'), ''.padLeft(200, 'b')); + Directory(path).createSync(recursive: true); + final oldCurrentDirectory = fileSystem.currentDirectory; + + expect( + () => fileSystem.currentDirectory = path, + throwsA( + isA() + .having((e) => e.path, 'path', path) + .having( + (e) => e.osError?.errorCode, + 'errorCode', + win32.ERROR_FILENAME_EXCED_RANGE, + ), + ), + ); + expect(fileSystem.currentDirectory, oldCurrentDirectory); + }, skip: !Platform.isWindows); + + test('relative path', () { + final path = '$tmp/dir'; + Directory(path).createSync(recursive: true); + + fileSystem.currentDirectory = 'dir'; + + expect( + fileSystem.same(fileSystem.currentDirectory, path), + isTrue, + reason: + '${fileSystem.currentDirectory} is a diffent directory than ' + '$path', + ); + expect( + p.isAbsolute(fileSystem.currentDirectory), + isTrue, + reason: '${fileSystem.currentDirectory} is not absolute', + ); + }); + }); +} diff --git a/pkgs/io_file/test/errors.dart b/pkgs/io_file/test/errors.dart index 77a630f6..0c70a3eb 100644 --- a/pkgs/io_file/test/errors.dart +++ b/pkgs/io_file/test/errors.dart @@ -9,6 +9,9 @@ int get eexist => Platform.isMacOS ? DarwinErrors.eexist : LinuxErrors.eexist; int get eisdir => Platform.isMacOS ? DarwinErrors.eisdir : LinuxErrors.eisdir; +int get enametoolong => + Platform.isMacOS ? DarwinErrors.enametoolong : LinuxErrors.enametoolong; + int get enoent => Platform.isMacOS ? DarwinErrors.enoent : LinuxErrors.enoent; int get enotdir => diff --git a/pkgs/io_file/test/read_as_bytes_test.dart b/pkgs/io_file/test/read_as_bytes_test.dart index 858efe35..b65798c3 100644 --- a/pkgs/io_file/test/read_as_bytes_test.dart +++ b/pkgs/io_file/test/read_as_bytes_test.dart @@ -10,6 +10,7 @@ import 'dart:typed_data'; import 'package:io_file/io_file.dart'; import 'package:io_file/src/internal_constants.dart'; +import 'package:path/path.dart' as p; import 'package:test/test.dart'; import 'package:win32/win32.dart' as win32; @@ -22,10 +23,18 @@ void main() { group('readAsBytes', () { late String tmp; + late String cwd; - setUp(() => tmp = createTemp('readAsBytes')); + setUp(() { + tmp = createTemp('readAsBytes'); + cwd = fileSystem.currentDirectory; + fileSystem.currentDirectory = tmp; + }); - tearDown(() => deleteTemp(tmp)); + tearDown(() { + fileSystem.currentDirectory = cwd; + deleteTemp(tmp); + }); test('non-existant file', () { expect( @@ -136,6 +145,16 @@ void main() { } }); group('regular files', () { + test('absolute path, long file name', () { + final data = randomUint8List(20); + final subdir = p.join(tmp, ''.padRight(255, 'f')); + final path = '$subdir/file'; + Directory(subdir).createSync(); + + File(path).writeAsBytesSync(data); + expect(fileSystem.readAsBytes(path), data); + }); + for (var i = 0; i <= 1024; ++i) { test('Read small file: $i bytes', () { final data = randomUint8List(i); diff --git a/pkgs/io_file/test/remove_directory_test.dart b/pkgs/io_file/test/remove_directory_test.dart index 7be6ba5b..431e6c22 100644 --- a/pkgs/io_file/test/remove_directory_test.dart +++ b/pkgs/io_file/test/remove_directory_test.dart @@ -17,10 +17,18 @@ import 'test_utils.dart'; void main() { group('removeDirectory', () { late String tmp; + late String cwd; - setUp(() => tmp = createTemp('removeDirectory')); + setUp(() { + tmp = createTemp('removeDirectory'); + cwd = fileSystem.currentDirectory; + fileSystem.currentDirectory = tmp; + }); - tearDown(() => deleteTemp(tmp)); + tearDown(() { + fileSystem.currentDirectory = cwd; + deleteTemp(tmp); + }); //TODO(brianquinlan): test with a very long path. diff --git a/pkgs/io_file/test/remove_directory_tree_test.dart b/pkgs/io_file/test/remove_directory_tree_test.dart index 370a3f98..7f34c8a4 100644 --- a/pkgs/io_file/test/remove_directory_tree_test.dart +++ b/pkgs/io_file/test/remove_directory_tree_test.dart @@ -17,10 +17,18 @@ import 'test_utils.dart'; void main() { group('removeDirectoryTree', () { late String tmp; + late String cwd; - setUp(() => tmp = createTemp('removeDirectoryTree')); + setUp(() { + tmp = createTemp('removeDirectoryTree'); + cwd = fileSystem.currentDirectory; + fileSystem.currentDirectory = tmp; + }); - tearDown(() => deleteTemp(tmp)); + tearDown(() { + fileSystem.currentDirectory = cwd; + deleteTemp(tmp); + }); //TODO(brianquinlan): test with a very long path. diff --git a/pkgs/io_file/test/rename_test.dart b/pkgs/io_file/test/rename_test.dart index 4deef9b6..8d22f4e0 100644 --- a/pkgs/io_file/test/rename_test.dart +++ b/pkgs/io_file/test/rename_test.dart @@ -13,12 +13,20 @@ import 'package:test/test.dart'; import 'test_utils.dart'; void main() { - group('move', () { + group('rename', () { late String tmp; + late String cwd; - setUp(() => tmp = createTemp('move')); + setUp(() { + tmp = createTemp('rename'); + cwd = fileSystem.currentDirectory; + fileSystem.currentDirectory = tmp; + }); - tearDown(() => deleteTemp(tmp)); + tearDown(() { + fileSystem.currentDirectory = cwd; + deleteTemp(tmp); + }); //TODO(brianquinlan): test with a very long path. diff --git a/pkgs/io_file/test/same_test.dart b/pkgs/io_file/test/same_test.dart index 9f296b81..d31b8ee7 100644 --- a/pkgs/io_file/test/same_test.dart +++ b/pkgs/io_file/test/same_test.dart @@ -18,20 +18,19 @@ import 'test_utils.dart'; void main() { group('same', () { late String tmp; - late Directory cwd; + late String cwd; setUp(() { tmp = createTemp('same'); - cwd = Directory.current; + cwd = fileSystem.currentDirectory; + fileSystem.currentDirectory = tmp; }); tearDown(() { - Directory.current = cwd; + fileSystem.currentDirectory = cwd; deleteTemp(tmp); }); - //TODO(brianquinlan): test with a very long path. - test('path1 does not exist', () { final path1 = '$tmp/file1'; final path2 = '$tmp/file2'; @@ -157,7 +156,7 @@ void main() { }); test('same file, absolute and relative paths', () { - Directory.current = tmp; + fileSystem.currentDirectory = tmp; final path1 = '$tmp/file1'; const path2 = 'file1'; File(path1).writeAsStringSync('Hello World'); @@ -216,7 +215,6 @@ void main() { }); test('same directory, absolute and relative paths', () { - Directory.current = tmp; final path1 = '$tmp/dir1'; const path2 = 'dir1'; Directory(path1).createSync(); diff --git a/pkgs/io_file/test/write_as_bytes_test.dart b/pkgs/io_file/test/write_as_bytes_test.dart index ce479db2..844483c7 100644 --- a/pkgs/io_file/test/write_as_bytes_test.dart +++ b/pkgs/io_file/test/write_as_bytes_test.dart @@ -9,6 +9,7 @@ import 'dart:io'; import 'dart:typed_data'; import 'package:io_file/io_file.dart'; +import 'package:path/path.dart' as p; import 'package:test/test.dart'; import 'package:win32/win32.dart' as win32; @@ -16,14 +17,20 @@ import 'errors.dart' as errors; import 'test_utils.dart'; void main() { - //TODO(brianquinlan): test with a very long path. - group('writeAsBytes', () { late String tmp; + late String cwd; - setUp(() => tmp = createTemp('writeAsBytes')); + setUp(() { + tmp = createTemp('writeAsBytes'); + cwd = fileSystem.currentDirectory; + fileSystem.currentDirectory = tmp; + }); - tearDown(() => deleteTemp(tmp)); + tearDown(() { + fileSystem.currentDirectory = cwd; + deleteTemp(tmp); + }); test('directory', () { expect( @@ -204,6 +211,16 @@ void main() { }); }); + test('absolute path, long file name', () { + final data = randomUint8List(20); + final subdir = p.join(tmp, ''.padRight(255, 'f')); + final path = '$subdir/file'; + Directory(subdir).createSync(); + + fileSystem.writeAsBytes(path, data, WriteMode.truncateExisting); + expect(File(path).readAsBytesSync(), data); + }); + group('regular files', () { for (var i = 0; i <= 1024; ++i) { test('Write small file: $i bytes', () { diff --git a/pkgs/io_file/test/write_as_string_test.dart b/pkgs/io_file/test/write_as_string_test.dart index c6b5a12a..c3c3cc0e 100644 --- a/pkgs/io_file/test/write_as_string_test.dart +++ b/pkgs/io_file/test/write_as_string_test.dart @@ -10,6 +10,7 @@ import 'dart:io'; import 'dart:typed_data'; import 'package:io_file/io_file.dart'; +import 'package:path/path.dart' as p; import 'package:test/test.dart'; import 'package:win32/win32.dart' as win32; @@ -21,10 +22,18 @@ void main() { group('writeAsString', () { late String tmp; + late String cwd; - setUp(() => tmp = createTemp('writeAsString')); + setUp(() { + tmp = createTemp('writeAsString'); + cwd = fileSystem.currentDirectory; + fileSystem.currentDirectory = tmp; + }); - tearDown(() => deleteTemp(tmp)); + tearDown(() { + fileSystem.currentDirectory = cwd; + deleteTemp(tmp); + }); test('directory', () { expect( @@ -204,6 +213,15 @@ void main() { }); }); + test('absolute path, long file name', () { + final subdir = p.join(tmp, ''.padRight(255, 'f')); + final path = '$subdir/file'; + Directory(subdir).createSync(); + + fileSystem.writeAsString(path, 'Hello World!'); + expect(File(path).readAsStringSync(), 'Hello World!'); + }); + group('encoding', () { test('non-ascii', () { final path = '$tmp/file';