// Copyright (c) 2017, 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') import 'dart:async'; import 'dart:convert'; import 'dart:io' as io; import 'package:file/file.dart'; import 'package:file_testing/file_testing.dart'; import 'package:path/path.dart' as p; import 'package:test/test.dart'; import 'package:test/test.dart' as testpkg show group, setUp, tearDown, test; import 'utils.dart'; /// Callback used in [runCommonTests] to produce the root folder in which all /// file system entities will be created. typedef RootPathGenerator = String Function(); /// Callback used in [runCommonTests] to create the file system under test. /// It must return either a [FileSystem] or a [Future] that completes with a /// [FileSystem]. typedef FileSystemGenerator = dynamic Function(); /// A function to run before tests (passed to [setUp]) or after tests /// (passed to [tearDown]). typedef SetUpTearDown = dynamic Function(); /// Runs a suite of tests common to all file system implementations. All file /// system implementations should run *at least* these tests to ensure /// compliance with file system API. /// /// If [root] is specified, its return value will be used as the root folder /// in which all file system entities will be created. If not specified, the /// tests will attempt to create entities in the file system root. /// /// [skip] may be used to skip certain tests (or entire groups of tests) in /// this suite (to be used, for instance, if a file system implementation is /// not yet fully complete). The format of each entry in the list is: /// `$group1Description > $group2Description > ... > $testDescription`. /// Entries may use regular expression syntax. /// /// If [replay] is specified, each test (and its setup callbacks) will run /// twice - once as a "setup" pass with the file system returned by /// [createFileSystem], and again as the "test" pass with the file system /// returned by [replay]. This is intended for use with `ReplayFileSystem`, /// where in order for the file system to behave as expected, a recording of /// the invocation(s) must first be made. void runCommonTests( FileSystemGenerator createFileSystem, { RootPathGenerator? root, List skip = const [], FileSystemGenerator? replay, }) { RootPathGenerator? rootfn = root; group('common', () { late FileSystemGenerator createFs; late List setUps; late List tearDowns; late FileSystem fs; late String root; List stack = []; void skipIfNecessary(String description, void Function() callback) { stack.add(description); bool matchesCurrentFrame(String input) => RegExp('^$input\$').hasMatch(stack.join(' > ')); if (skip.where(matchesCurrentFrame).isEmpty) { callback(); } stack.removeLast(); } testpkg.setUp(() async { createFs = createFileSystem; setUps = []; tearDowns = []; }); void setUp(FutureOr Function() callback) { testpkg.setUp(replay == null ? callback : () => setUps.add(callback)); } void tearDown(FutureOr Function() callback) { if (replay == null) { testpkg.tearDown(callback); } else { testpkg.setUp(() => tearDowns.insert(0, callback)); } } void group(String description, void Function() body) => skipIfNecessary(description, () => testpkg.group(description, body)); void test(String description, FutureOr Function() body, {dynamic skip}) => skipIfNecessary(description, () { if (replay == null) { testpkg.test(description, body, skip: skip); } else { group('rerun', () { testpkg.setUp(() async { await Future.forEach(setUps, (SetUpTearDown setUp) => setUp()); await body(); for (SetUpTearDown tearDown in tearDowns) { await tearDown(); } createFs = replay; await Future.forEach(setUps, (SetUpTearDown setUp) => setUp()); }); testpkg.test(description, body, skip: skip); testpkg.tearDown(() async { for (SetUpTearDown tearDown in tearDowns) { await tearDown(); } }); }); } }); /// Returns [path] prefixed by the [root] namespace. /// This is only intended for absolute paths. String ns(String path) { p.Context posix = p.Context(style: p.Style.posix); List parts = posix.split(path); parts[0] = root; path = fs.path.joinAll(parts); String rootPrefix = fs.path.rootPrefix(path); assert(rootPrefix.isNotEmpty); String result = root == rootPrefix ? path : (path == rootPrefix ? root : fs.path.join(root, fs.path.joinAll(parts.sublist(1)))); return result; } setUp(() async { root = rootfn != null ? rootfn() : '/'; fs = await createFs() as FileSystem; assert(fs.path.isAbsolute(root)); assert(!root.endsWith(fs.path.separator) || fs.path.rootPrefix(root) == root); }); group('FileSystem', () { group('directory', () { test('allowsStringArgument', () { expect(fs.directory(ns('/foo')), isDirectory); }); test('allowsUriArgument', () { expect(fs.directory(Uri.parse('file:///')), isDirectory); }); test('succeedsWithUriArgument', () { fs.directory(ns('/foo')).createSync(); Uri uri = fs.path.toUri(ns('/foo')); expect(fs.directory(uri), exists); }); test('allowsDirectoryArgument', () { expect(fs.directory(io.Directory(ns('/foo'))), isDirectory); }); test('disallowsOtherArgumentType', () { expect(() => fs.directory(123), throwsArgumentError); }); // Fails due to // https://github.com/google/file.dart/issues/112 test('considersBothSlashesEquivalent', () { fs.directory(r'foo\bar_dir').createSync(recursive: true); expect(fs.directory(r'foo/bar_dir'), exists); }, skip: 'Fails due to https://github.com/google/file.dart/issues/112'); }); group('file', () { test('allowsStringArgument', () { expect(fs.file(ns('/foo')), isFile); }); test('allowsUriArgument', () { expect(fs.file(Uri.parse('file:///')), isFile); }); test('succeedsWithUriArgument', () { fs.file(ns('/foo')).createSync(); Uri uri = fs.path.toUri(ns('/foo')); expect(fs.file(uri), exists); }); test('allowsDirectoryArgument', () { expect(fs.file(io.File(ns('/foo'))), isFile); }); test('disallowsOtherArgumentType', () { expect(() => fs.file(123), throwsArgumentError); }); // Fails due to // https://github.com/google/file.dart/issues/112 test('considersBothSlashesEquivalent', () { fs.file(r'foo\bar_file').createSync(recursive: true); expect(fs.file(r'foo/bar_file'), exists); }, skip: 'Fails due to https://github.com/google/file.dart/issues/112'); }); group('link', () { test('allowsStringArgument', () { expect(fs.link(ns('/foo')), isLink); }); test('allowsUriArgument', () { expect(fs.link(Uri.parse('file:///')), isLink); }); test('succeedsWithUriArgument', () { fs.file(ns('/foo')).createSync(); fs.link(ns('/bar')).createSync(ns('/foo')); Uri uri = fs.path.toUri(ns('/bar')); expect(fs.link(uri), exists); }); test('allowsDirectoryArgument', () { expect(fs.link(io.File(ns('/foo'))), isLink); }); test('disallowsOtherArgumentType', () { expect(() => fs.link(123), throwsArgumentError); }); }); group('path', () { test('hasCorrectCurrentWorkingDirectory', () { expect(fs.path.current, fs.currentDirectory.path); }); test('separatorIsAmongExpectedValues', () { expect(fs.path.separator, anyOf('/', r'\')); }); }); group('systemTempDirectory', () { test('existsAsDirectory', () { Directory tmp = fs.systemTempDirectory; expect(tmp, isDirectory); expect(tmp, exists); }); }); group('currentDirectory', () { test('defaultsToRoot', () { expect(fs.currentDirectory.path, root); }); test('throwsIfSetToNonExistentPath', () { expectFileSystemException(ErrorCodes.ENOENT, () { fs.currentDirectory = ns('/foo'); }); }); test('throwsIfHasNonExistentPathInComplexChain', () { fs.directory(ns('/foo')).createSync(); expectFileSystemException(ErrorCodes.ENOENT, () { fs.currentDirectory = ns('/bar/../foo'); }); }); test('succeedsIfSetToValidStringPath', () { fs.directory(ns('/foo')).createSync(); fs.currentDirectory = ns('/foo'); expect(fs.currentDirectory.path, ns('/foo')); }); test('succeedsIfSetToValidDirectory', () { fs.directory(ns('/foo')).createSync(); fs.currentDirectory = io.Directory(ns('/foo')); expect(fs.currentDirectory.path, ns('/foo')); }); test('throwsIfArgumentIsNotStringOrDirectory', () { expect(() { fs.currentDirectory = 123; }, throwsArgumentError); }); test('succeedsIfSetToRelativePath', () { fs.directory(ns('/foo/bar')).createSync(recursive: true); fs.currentDirectory = 'foo'; expect(fs.currentDirectory.path, ns('/foo')); fs.currentDirectory = 'bar'; expect(fs.currentDirectory.path, ns('/foo/bar')); }); test('succeedsIfSetToAbsolutePathWhenCwdIsNotRoot', () { fs.directory(ns('/foo/bar')).createSync(recursive: true); fs.directory(ns('/baz/qux')).createSync(recursive: true); fs.currentDirectory = ns('/foo/bar'); expect(fs.currentDirectory.path, ns('/foo/bar')); fs.currentDirectory = fs.directory(ns('/baz/qux')); expect(fs.currentDirectory.path, ns('/baz/qux')); }); test('succeedsIfSetToParentDirectory', () { fs.directory(ns('/foo')).createSync(); fs.currentDirectory = 'foo'; expect(fs.currentDirectory.path, ns('/foo')); fs.currentDirectory = '..'; expect(fs.currentDirectory.path, ns('/')); }); test('staysAtRootIfSetToParentOfRoot', () { fs.currentDirectory = List.filled(20, '..').join(fs.path.separator); String cwd = fs.currentDirectory.path; expect(cwd, fs.path.rootPrefix(cwd)); }); test('removesTrailingSlashIfSet', () { fs.directory(ns('/foo')).createSync(); fs.currentDirectory = ns('/foo/'); expect(fs.currentDirectory.path, ns('/foo')); }); test('throwsIfSetToFilePathSegmentAtTail', () { fs.file(ns('/foo')).createSync(); expectFileSystemException(ErrorCodes.ENOTDIR, () { fs.currentDirectory = ns('/foo'); }); }); test('throwsIfSetToFilePathSegmentViaTraversal', () { fs.file(ns('/foo')).createSync(); expectFileSystemException(ErrorCodes.ENOTDIR, () { fs.currentDirectory = ns('/foo/bar/baz'); }); }); test('resolvesLinksIfEncountered', () { fs.link(ns('/foo/bar/baz')).createSync(ns('/qux'), recursive: true); fs.directory(ns('/qux')).createSync(); fs.directory(ns('/quux')).createSync(); fs.currentDirectory = ns('/foo/bar/baz/../quux/'); expect(fs.currentDirectory.path, ns('/quux')); }); test('succeedsIfSetToDirectoryLinkAtTail', () { fs.directory(ns('/foo')).createSync(); fs.link(ns('/bar')).createSync(ns('/foo')); fs.currentDirectory = ns('/bar'); expect(fs.currentDirectory.path, ns('/foo')); }); test('throwsIfSetToLinkLoop', () { fs.link(ns('/foo')).createSync(ns('/bar')); fs.link(ns('/bar')).createSync(ns('/foo')); expectFileSystemException( anyOf(ErrorCodes.EMLINK, ErrorCodes.ELOOP), () { fs.currentDirectory = ns('/foo'); }, ); }); }); group('stat', () { test('isNotFoundForEmptyPath', () { FileStat stat = fs.statSync(''); expect(stat.type, FileSystemEntityType.notFound); }); test('isNotFoundForPathToNonExistentEntityAtTail', () { FileStat stat = fs.statSync(ns('/foo')); expect(stat.type, FileSystemEntityType.notFound); }); test('isNotFoundForPathToNonExistentEntityInTraversal', () { FileStat stat = fs.statSync(ns('/foo/bar')); expect(stat.type, FileSystemEntityType.notFound); }); test('isDirectoryForDirectory', () { fs.directory(ns('/foo')).createSync(); FileStat stat = fs.statSync(ns('/foo')); expect(stat.type, FileSystemEntityType.directory); }); test('isFileForFile', () { fs.file(ns('/foo')).createSync(); FileStat stat = fs.statSync(ns('/foo')); expect(stat.type, FileSystemEntityType.file); }); test('isFileForLinkToFile', () { fs.file(ns('/foo')).createSync(); fs.link(ns('/bar')).createSync(ns('/foo')); FileStat stat = fs.statSync(ns('/bar')); expect(stat.type, FileSystemEntityType.file); }); test('isNotFoundForLinkWithCircularReference', () { fs.link(ns('/foo')).createSync(ns('/bar')); fs.link(ns('/bar')).createSync(ns('/baz')); fs.link(ns('/baz')).createSync(ns('/foo')); FileStat stat = fs.statSync(ns('/foo')); expect(stat.type, FileSystemEntityType.notFound); }); }); group('identical', () { test('isTrueForIdenticalPathsToExistentFile', () { fs.file(ns('/foo')).createSync(); expect(fs.identicalSync(ns('/foo'), ns('/foo')), true); }); test('isFalseForDifferentPathsToDifferentFiles', () { fs.file(ns('/foo')).createSync(); fs.file(ns('/bar')).createSync(); expect(fs.identicalSync(ns('/foo'), ns('/bar')), false); }); test('isTrueForDifferentPathsToSameFileViaLinkInTraversal', () { fs.file(ns('/foo/file')).createSync(recursive: true); fs.link(ns('/bar')).createSync(ns('/foo')); expect(fs.identicalSync(ns('/foo/file'), ns('/bar/file')), true); }); test('isFalseForDifferentPathsToSameFileViaLinkAtTail', () { fs.file(ns('/foo')).createSync(); fs.link(ns('/bar')).createSync(ns('/foo')); expect(fs.identicalSync(ns('/foo'), ns('/bar')), false); }); test('throwsForDifferentPathsToNonExistentEntities', () { expectFileSystemException(ErrorCodes.ENOENT, () { fs.identicalSync(ns('/foo'), ns('/bar')); }); }); test('throwsForDifferentPathsToOneFileOneNonExistentEntity', () { fs.file(ns('/foo')).createSync(); expectFileSystemException(ErrorCodes.ENOENT, () { fs.identicalSync(ns('/foo'), ns('/bar')); }); }); }); group('type', () { test('isFileForFile', () { fs.file(ns('/foo')).createSync(); FileSystemEntityType type = fs.typeSync(ns('/foo')); expect(type, FileSystemEntityType.file); }); test('isDirectoryForDirectory', () { fs.directory(ns('/foo')).createSync(); FileSystemEntityType type = fs.typeSync(ns('/foo')); expect(type, FileSystemEntityType.directory); }); test('isDirectoryForAncestorOfRoot', () { FileSystemEntityType type = fs .typeSync(List.filled(20, '..').join(fs.path.separator)); expect(type, FileSystemEntityType.directory); }); test('isFileForLinkToFileAndFollowLinksTrue', () { fs.file(ns('/foo')).createSync(); fs.link(ns('/bar')).createSync(ns('/foo')); FileSystemEntityType type = fs.typeSync(ns('/bar')); expect(type, FileSystemEntityType.file); }); test('isLinkForLinkToFileAndFollowLinksFalse', () { fs.file(ns('/foo')).createSync(); fs.link(ns('/bar')).createSync(ns('/foo')); FileSystemEntityType type = fs.typeSync(ns('/bar'), followLinks: false); expect(type, FileSystemEntityType.link); }); test('isNotFoundForLinkWithCircularReferenceAndFollowLinksTrue', () { fs.link(ns('/foo')).createSync(ns('/bar')); fs.link(ns('/bar')).createSync(ns('/baz')); fs.link(ns('/baz')).createSync(ns('/foo')); FileSystemEntityType type = fs.typeSync(ns('/foo')); expect(type, FileSystemEntityType.notFound); }); test('isNotFoundForNoEntityAtTail', () { FileSystemEntityType type = fs.typeSync(ns('/foo')); expect(type, FileSystemEntityType.notFound); }); test('isNotFoundForNoDirectoryInTraversal', () { FileSystemEntityType type = fs.typeSync(ns('/foo/bar/baz')); expect(type, FileSystemEntityType.notFound); }); }); group('isFile/isDirectory/isLink', () { late String filePath; late String directoryPath; late String fileLinkPath; late String directoryLinkPath; setUp(() { filePath = ns('/file'); directoryPath = ns('/directory'); fileLinkPath = ns('/file-link'); directoryLinkPath = ns('/directory-link'); fs.file(filePath).createSync(); fs.directory(directoryPath).createSync(); fs.link(fileLinkPath).createSync(filePath); fs.link(directoryLinkPath).createSync(directoryPath); }); test('isFile', () { expect(fs.isFileSync(filePath), true); expect(fs.isFileSync(directoryPath), false); expect(fs.isFileSync(fileLinkPath), true); expect(fs.isFileSync(directoryLinkPath), false); }); test('isDirectory', () { expect(fs.isDirectorySync(filePath), false); expect(fs.isDirectorySync(directoryPath), true); expect(fs.isDirectorySync(fileLinkPath), false); expect(fs.isDirectorySync(directoryLinkPath), true); }); test('isLink', () { expect(fs.isLinkSync(filePath), false); expect(fs.isLinkSync(directoryPath), false); expect(fs.isLinkSync(fileLinkPath), true); expect(fs.isLinkSync(directoryLinkPath), true); }); }); }); group('Directory', () { test('uri', () { expect(fs.directory(ns('/foo')).uri, fs.path.toUri('${ns('/foo')}/')); expect(fs.directory('foo').uri.toString(), 'foo/'); }); group('exists', () { test('falseIfNotExists', () { expect(fs.directory(ns('/foo')), isNot(exists)); expect(fs.directory('foo'), isNot(exists)); expect(fs.directory(ns('/foo/bar')), isNot(exists)); }); test('trueIfExistsAsDirectory', () { fs.directory(ns('/foo')).createSync(); expect(fs.directory(ns('/foo')), exists); expect(fs.directory('foo'), exists); }); test('falseIfExistsAsFile', () { fs.file(ns('/foo')).createSync(); expect(fs.directory(ns('/foo')), isNot(exists)); expect(fs.directory('foo'), isNot(exists)); }); test('trueIfExistsAsLinkToDirectory', () { fs.directory(ns('/foo')).createSync(); fs.link(ns('/bar')).createSync(ns('/foo')); expect(fs.directory(ns('/bar')), exists); expect(fs.directory('bar'), exists); }); test('falseIfExistsAsLinkToFile', () { fs.file(ns('/foo')).createSync(); fs.link(ns('/bar')).createSync(ns('/foo')); expect(fs.directory(ns('/bar')), isNot(exists)); expect(fs.directory('bar'), isNot(exists)); }); test('falseIfNotFoundSegmentExistsThenIsBackedOut', () { fs.directory(ns('/foo')).createSync(); expect(fs.directory(ns('/bar/../foo')), isNot(exists)); }); }); group('create', () { test('returnsCovariantType', () async { expect(await fs.directory(ns('/foo')).create(), isDirectory); }); test('succeedsIfAlreadyExistsAsDirectory', () { fs.directory(ns('/foo')).createSync(); fs.directory(ns('/foo')).createSync(); }); test('throwsIfAlreadyExistsAsFile', () { fs.file(ns('/foo')).createSync(); expectFileSystemException(ErrorCodes.ENOTDIR, () { fs.directory(ns('/foo')).createSync(); }); }); test('succeedsIfAlreadyExistsAsLinkToDirectory', () { fs.directory(ns('/foo')).createSync(); fs.link(ns('/bar')).createSync(ns('/foo')); fs.directory(ns('/bar')).createSync(); }); test('throwsIfAlreadyExistsAsLinkToFile', () { fs.file(ns('/foo')).createSync(); fs.link(ns('/bar')).createSync(ns('/foo')); // TODO(tvolkert): Change this to just be 'Not a directory' // once Dart 1.22 is stable. expectFileSystemException( anyOf(ErrorCodes.EEXIST, ErrorCodes.ENOTDIR), () { fs.directory(ns('/bar')).createSync(); }, ); }); test('throwsIfAlreadyExistsAsLinkToNotFoundAtTail', () { fs.link(ns('/foo')).createSync(ns('/bar')); expectFileSystemException(ErrorCodes.ENOENT, () { fs.directory(ns('/foo')).createSync(); }); }); test('throwsIfAlreadyExistsAsLinkToNotFoundViaTraversal', () { fs.link(ns('/foo')).createSync(ns('/bar/baz')); expectFileSystemException(ErrorCodes.ENOENT, () { fs.directory(ns('/foo')).createSync(); }); }); test('throwsIfAlreadyExistsAsLinkToNotFoundInDifferentDirectory', () { fs.directory(ns('/foo')).createSync(); fs.directory(ns('/bar')).createSync(); fs.link(ns('/bar/baz')).createSync(ns('/foo/qux')); expectFileSystemException(ErrorCodes.ENOENT, () { fs.directory(ns('/bar/baz')).createSync(); }); }); test('succeedsIfTailDoesntExist', () { expect(fs.directory(ns('/')), exists); fs.directory(ns('/foo')).createSync(); expect(fs.directory(ns('/foo')), exists); }); test('throwsIfAncestorDoesntExistRecursiveFalse', () { expectFileSystemException(ErrorCodes.ENOENT, () { fs.directory(ns('/foo/bar')).createSync(); }); }); test('succeedsIfAncestorDoesntExistRecursiveTrue', () { fs.directory(ns('/foo/bar')).createSync(recursive: true); expect(fs.directory(ns('/foo')), exists); expect(fs.directory(ns('/foo/bar')), exists); }); }); group('rename', () { test('returnsCovariantType', () async { Directory src() => fs.directory(ns('/foo'))..createSync(); expect(src().renameSync(ns('/bar')), isDirectory); expect(await src().rename(ns('/baz')), isDirectory); }); test('succeedsIfDestinationDoesntExist', () { Directory src = fs.directory(ns('/foo'))..createSync(); Directory dest = src.renameSync(ns('/bar')); expect(dest.path, ns('/bar')); expect(dest, exists); }); test( 'succeedsIfDestinationIsEmptyDirectory', () { fs.directory(ns('/bar')).createSync(); Directory src = fs.directory(ns('/foo'))..createSync(); Directory dest = src.renameSync(ns('/bar')); expect(src, isNot(exists)); expect(dest, exists); }, // See https://github.com/google/file.dart/issues/197. skip: io.Platform.isWindows, ); test('throwsIfDestinationIsFile', () { fs.file(ns('/bar')).createSync(); Directory src = fs.directory(ns('/foo'))..createSync(); expectFileSystemException(ErrorCodes.ENOTDIR, () { src.renameSync(ns('/bar')); }); }); test('throwsIfDestinationParentFolderDoesntExist', () { Directory src = fs.directory(ns('/foo'))..createSync(); expectFileSystemException(ErrorCodes.ENOENT, () { src.renameSync(ns('/bar/baz')); }); }); test('throwsIfDestinationIsNonEmptyDirectory', () { fs.file(ns('/bar/baz')).createSync(recursive: true); Directory src = fs.directory(ns('/foo'))..createSync(); // The error will be 'Directory not empty' on OS X, but it will be // 'File exists' on Linux. expectFileSystemException( anyOf(ErrorCodes.ENOTEMPTY, ErrorCodes.EEXIST), () { src.renameSync(ns('/bar')); }, ); }); test('throwsIfSourceDoesntExist', () { expectFileSystemException(ErrorCodes.ENOENT, () { fs.directory(ns('/foo')).renameSync(ns('/bar')); }); }); test('throwsIfSourceIsFile', () { fs.file(ns('/foo')).createSync(); expectFileSystemException(ErrorCodes.ENOTDIR, () { fs.directory(ns('/foo')).renameSync(ns('/bar')); }); }); test('succeedsIfSourceIsLinkToDirectory', () { fs.directory(ns('/foo')).createSync(); fs.link(ns('/bar')).createSync(ns('/foo')); fs.directory(ns('/bar')).renameSync(ns('/baz')); expect(fs.typeSync(ns('/foo'), followLinks: false), FileSystemEntityType.directory); expect(fs.typeSync(ns('/bar')), FileSystemEntityType.notFound); expect(fs.typeSync(ns('/baz'), followLinks: false), FileSystemEntityType.link); expect(fs.link(ns('/baz')).targetSync(), ns('/foo')); }); test('throwsIfDestinationIsLinkToNotFound', () { Directory src = fs.directory(ns('/foo'))..createSync(); fs.link(ns('/bar')).createSync(ns('/baz')); expectFileSystemException(ErrorCodes.ENOTDIR, () { src.renameSync(ns('/bar')); }); }); test('throwsIfDestinationIsLinkToEmptyDirectory', () { Directory src = fs.directory(ns('/foo'))..createSync(); fs.directory(ns('/bar')).createSync(); fs.link(ns('/baz')).createSync(ns('/bar')); expectFileSystemException(ErrorCodes.ENOTDIR, () { src.renameSync(ns('/baz')); }); }); test('succeedsIfDestinationIsInDifferentDirectory', () { Directory src = fs.directory(ns('/foo'))..createSync(); fs.directory(ns('/bar')).createSync(); src.renameSync(ns('/bar/baz')); expect(fs.typeSync(ns('/foo')), FileSystemEntityType.notFound); expect(fs.typeSync(ns('/bar/baz')), FileSystemEntityType.directory); }); test('succeedsIfSourceIsLinkToDifferentDirectory', () { fs.directory(ns('/foo/subfoo')).createSync(recursive: true); fs.directory(ns('/bar/subbar')).createSync(recursive: true); fs.directory(ns('/baz/subbaz')).createSync(recursive: true); fs.link(ns('/foo/subfoo/lnk')).createSync(ns('/bar/subbar')); fs.directory(ns('/foo/subfoo/lnk')).renameSync(ns('/baz/subbaz/dst')); expect(fs.typeSync(ns('/foo/subfoo/lnk')), FileSystemEntityType.notFound); expect(fs.typeSync(ns('/baz/subbaz/dst'), followLinks: false), FileSystemEntityType.link); expect(fs.typeSync(ns('/baz/subbaz/dst'), followLinks: true), FileSystemEntityType.directory); }); }); group('delete', () { test('returnsCovariantType', () async { Directory dir = fs.directory(ns('/foo'))..createSync(); expect(await dir.delete(), isDirectory); }); test('succeedsIfEmptyDirectoryExistsAndRecursiveFalse', () { Directory dir = fs.directory(ns('/foo'))..createSync(); dir.deleteSync(); expect(dir, isNot(exists)); }); test('succeedsIfEmptyDirectoryExistsAndRecursiveTrue', () { Directory dir = fs.directory(ns('/foo'))..createSync(); dir.deleteSync(recursive: true); expect(dir, isNot(exists)); }); test('throwsIfNonEmptyDirectoryExistsAndRecursiveFalse', () { Directory dir = fs.directory(ns('/foo'))..createSync(); fs.file(ns('/foo/bar')).createSync(); expectFileSystemException(ErrorCodes.ENOTEMPTY, () { dir.deleteSync(); }); }); test('succeedsIfNonEmptyDirectoryExistsAndRecursiveTrue', () { Directory dir = fs.directory(ns('/foo'))..createSync(); fs.file(ns('/foo/bar')).createSync(); dir.deleteSync(recursive: true); expect(fs.directory(ns('/foo')), isNot(exists)); expect(fs.file(ns('/foo/bar')), isNot(exists)); }); test('throwsIfDirectoryDoesntExistAndRecursiveFalse', () { expectFileSystemException(ErrorCodes.ENOENT, () { fs.directory(ns('/foo')).deleteSync(); }); }); test('throwsIfDirectoryDoesntExistAndRecursiveTrue', () { expectFileSystemException(ErrorCodes.ENOENT, () { fs.directory(ns('/foo')).deleteSync(recursive: true); }); }); test('succeedsIfPathReferencesFileAndRecursiveTrue', () { fs.file(ns('/foo')).createSync(); fs.directory(ns('/foo')).deleteSync(recursive: true); expect(fs.typeSync(ns('/foo')), FileSystemEntityType.notFound); }); test('throwsIfPathReferencesFileAndRecursiveFalse', () { fs.file(ns('/foo')).createSync(); expectFileSystemException(ErrorCodes.ENOTDIR, () { fs.directory(ns('/foo')).deleteSync(); }); }); test('succeedsIfPathReferencesLinkToDirectoryAndRecursiveTrue', () { fs.directory(ns('/foo')).createSync(); fs.link(ns('/bar')).createSync(ns('/foo')); fs.directory(ns('/bar')).deleteSync(recursive: true); expect(fs.typeSync(ns('/foo'), followLinks: false), FileSystemEntityType.directory); expect(fs.typeSync(ns('/bar'), followLinks: false), FileSystemEntityType.notFound); }); test('succeedsIfPathReferencesLinkToDirectoryAndRecursiveFalse', () { fs.directory(ns('/foo')).createSync(); fs.link(ns('/bar')).createSync(ns('/foo')); fs.directory(ns('/bar')).deleteSync(); expect(fs.typeSync(ns('/foo'), followLinks: false), FileSystemEntityType.directory); expect(fs.typeSync(ns('/bar'), followLinks: false), FileSystemEntityType.notFound); }); test('succeedsIfExistsAsLinkToDirectoryInDifferentDirectory', () { fs.directory(ns('/foo/bar')).createSync(recursive: true); fs.link(ns('/baz/qux')).createSync(ns('/foo/bar'), recursive: true); fs.directory(ns('/baz/qux')).deleteSync(); expect(fs.typeSync(ns('/foo/bar'), followLinks: false), FileSystemEntityType.directory); expect(fs.typeSync(ns('/baz/qux'), followLinks: false), FileSystemEntityType.notFound); }); test('succeedsIfPathReferencesLinkToFileAndRecursiveTrue', () { fs.file(ns('/foo')).createSync(); fs.link(ns('/bar')).createSync(ns('/foo')); fs.directory(ns('/bar')).deleteSync(recursive: true); expect(fs.typeSync(ns('/foo'), followLinks: false), FileSystemEntityType.file); expect(fs.typeSync(ns('/bar'), followLinks: false), FileSystemEntityType.notFound); }); test('throwsIfPathReferencesLinkToFileAndRecursiveFalse', () { fs.file(ns('/foo')).createSync(); fs.link(ns('/bar')).createSync(ns('/foo')); expectFileSystemException(ErrorCodes.ENOTDIR, () { fs.directory(ns('/bar')).deleteSync(); }); }); test('throwsIfPathReferencesLinkToNotFoundAndRecursiveFalse', () { fs.link(ns('/foo')).createSync(ns('/bar')); expectFileSystemException(ErrorCodes.ENOTDIR, () { fs.directory(ns('/foo')).deleteSync(); }); }); }); group('resolveSymbolicLinks', () { test('succeedsForRootDirectory', () { expect(fs.directory(ns('/')).resolveSymbolicLinksSync(), ns('/')); }); test('throwsIfPathIsEmpty', () { expectFileSystemException(ErrorCodes.ENOENT, () { fs.directory('').resolveSymbolicLinksSync(); }); }); test('throwsIfLoopInLinkChain', () { fs.link(ns('/foo')).createSync(ns('/bar')); fs.link(ns('/bar')).createSync(ns('/baz')); fs.link(ns('/baz')).createSync(ns('/foo')); expectFileSystemException( anyOf(ErrorCodes.EMLINK, ErrorCodes.ELOOP), () { fs.directory(ns('/foo')).resolveSymbolicLinksSync(); }, ); }); test('throwsIfPathNotFoundInTraversal', () { expectFileSystemException(ErrorCodes.ENOENT, () { fs.directory(ns('/foo/bar')).resolveSymbolicLinksSync(); }); }); test('throwsIfPathNotFoundAtTail', () { expectFileSystemException(ErrorCodes.ENOENT, () { fs.directory(ns('/foo')).resolveSymbolicLinksSync(); }); }); test('throwsIfPathNotFoundInMiddleThenBackedOut', () { fs.directory(ns('/foo/bar')).createSync(recursive: true); expectFileSystemException(ErrorCodes.ENOENT, () { fs.directory(ns('/foo/baz/../bar')).resolveSymbolicLinksSync(); }); }); test('resolvesRelativePathToCurrentDirectory', () { fs.directory(ns('/foo/bar')).createSync(recursive: true); fs.link(ns('/foo/baz')).createSync(ns('/foo/bar')); fs.currentDirectory = ns('/foo'); expect( fs.directory('baz').resolveSymbolicLinksSync(), ns('/foo/bar')); }); test('resolvesAbsolutePathsAbsolutely', () { fs.directory(ns('/foo/bar')).createSync(recursive: true); fs.currentDirectory = ns('/foo'); expect(fs.directory(ns('/foo/bar')).resolveSymbolicLinksSync(), ns('/foo/bar')); }); test('handlesRelativeLinks', () { fs.directory(ns('/foo/bar/baz')).createSync(recursive: true); fs.link(ns('/foo/qux')).createSync(fs.path.join('bar', 'baz')); expect( fs.directory(ns('/foo/qux')).resolveSymbolicLinksSync(), ns('/foo/bar/baz'), ); expect( fs.directory(fs.path.join('foo', 'qux')).resolveSymbolicLinksSync(), ns('/foo/bar/baz'), ); }); test('handlesAbsoluteLinks', () { fs.directory(ns('/foo')).createSync(); fs.directory(ns('/bar/baz/qux')).createSync(recursive: true); fs.link(ns('/foo/quux')).createSync(ns('/bar/baz/qux')); expect(fs.directory(ns('/foo/quux')).resolveSymbolicLinksSync(), ns('/bar/baz/qux')); }); test('handlesLinksWhoseTargetsHaveNestedLinks', () { fs.directory(ns('/foo')).createSync(); fs.link(ns('/foo/quuz')).createSync(ns('/bar')); fs.link(ns('/foo/grault')).createSync(ns('/baz/quux')); fs.directory(ns('/bar')).createSync(); fs.link(ns('/bar/qux')).createSync(ns('/baz')); fs.link(ns('/bar/garply')).createSync(ns('/foo')); fs.directory(ns('/baz')).createSync(); fs.link(ns('/baz/quux')).createSync(ns('/bar/garply/quuz')); expect(fs.directory(ns('/foo/grault/qux')).resolveSymbolicLinksSync(), ns('/baz')); }); test('handlesParentAndThisFolderReferences', () { fs.directory(ns('/foo/bar/baz')).createSync(recursive: true); fs.link(ns('/foo/bar/baz/qux')).createSync(fs.path.join('..', '..')); String resolved = fs .directory(ns('/foo/./bar/baz/../baz/qux/bar')) .resolveSymbolicLinksSync(); expect(resolved, ns('/foo/bar')); }); test('handlesBackToBackSlashesInPath', () { fs.directory(ns('/foo/bar/baz')).createSync(recursive: true); expect(fs.directory(ns('//foo/bar///baz')).resolveSymbolicLinksSync(), ns('/foo/bar/baz')); }); test('handlesComplexPathWithMultipleLinks', () { fs .link(ns('/foo/bar/baz')) .createSync(fs.path.join('..', '..', 'qux'), recursive: true); fs.link(ns('/qux')).createSync('quux'); fs.link(ns('/quux/quuz')).createSync(ns('/foo'), recursive: true); String resolved = fs .directory(ns('/foo//bar/./baz/quuz/bar/..///bar/baz/')) .resolveSymbolicLinksSync(); expect(resolved, ns('/quux')); }); }); group('absolute', () { test('returnsCovariantType', () { expect(fs.directory('foo').absolute, isDirectory); }); test('returnsSamePathIfAlreadyAbsolute', () { expect(fs.directory(ns('/foo')).absolute.path, ns('/foo')); }); test('succeedsForRelativePaths', () { expect(fs.directory('foo').absolute.path, ns('/foo')); }); }); group('parent', () { late String root; setUp(() { root = fs.path.style.name == 'windows' ? r'C:\' : '/'; }); test('returnsCovariantType', () { expect(fs.directory(root).parent, isDirectory); }); test('returnsRootForRoot', () { expect(fs.directory(root).parent.path, root); }); test('succeedsForNonRoot', () { expect(fs.directory(ns('/foo/bar')).parent.path, ns('/foo')); }); }); group('createTemp', () { test('returnsCovariantType', () { expect(fs.directory(ns('/')).createTempSync(), isDirectory); }); test('throwsIfDirectoryDoesntExist', () { expectFileSystemException(ErrorCodes.ENOENT, () { fs.directory(ns('/foo')).createTempSync(); }); }); test('resolvesNameCollisions', () { fs.directory(ns('/foo/bar')).createSync(recursive: true); Directory tmp = fs.directory(ns('/foo')).createTempSync('bar'); expect(tmp.path, allOf(isNot(ns('/foo/bar')), startsWith(ns('/foo/bar')))); }); test('succeedsWithoutPrefix', () { Directory dir = fs.directory(ns('/foo'))..createSync(); expect(dir.createTempSync().path, startsWith(ns('/foo/'))); }); test('succeedsWithPrefix', () { Directory dir = fs.directory(ns('/foo'))..createSync(); expect(dir.createTempSync('bar').path, startsWith(ns('/foo/bar'))); }); test('succeedsWithNestedPathPrefixThatExists', () { fs.directory(ns('/foo/bar')).createSync(recursive: true); Directory tmp = fs.directory(ns('/foo')).createTempSync('bar/baz'); expect(tmp.path, startsWith(ns('/foo/bar/baz'))); }); test('throwsWithNestedPathPrefixThatDoesntExist', () { Directory dir = fs.directory(ns('/foo'))..createSync(); expectFileSystemException(ErrorCodes.ENOENT, () { dir.createTempSync('bar/baz'); }); }); }); group('list', () { late Directory dir; setUp(() { dir = fs.currentDirectory = fs.directory(ns('/foo'))..createSync(); fs.file('bar').createSync(); fs.file(fs.path.join('baz', 'qux')).createSync(recursive: true); fs.link('quux').createSync(fs.path.join('baz', 'qux')); fs .link(fs.path.join('baz', 'quuz')) .createSync(fs.path.join('..', 'quux')); fs.link(fs.path.join('baz', 'grault')).createSync('.'); fs.currentDirectory = ns('/'); }); test('returnsCovariantType', () async { void expectIsFileSystemEntity(dynamic entity) { expect(entity, isFileSystemEntity); } dir.listSync().forEach(expectIsFileSystemEntity); (await dir.list().toList()).forEach(expectIsFileSystemEntity); }); test('returnsEmptyListForEmptyDirectory', () { Directory empty = fs.directory(ns('/bar'))..createSync(); expect(empty.listSync(), isEmpty); }); test('throwsIfDirectoryDoesntExist', () { expectFileSystemException(ErrorCodes.ENOENT, () { fs.directory(ns('/bar')).listSync(); }); }); test('returnsLinkObjectsIfFollowLinksFalse', () { List list = dir.listSync(followLinks: false); expect(list, hasLength(3)); expect(list, contains(allOf(isFile, hasPath(ns('/foo/bar'))))); expect(list, contains(allOf(isDirectory, hasPath(ns('/foo/baz'))))); expect(list, contains(allOf(isLink, hasPath(ns('/foo/quux'))))); }); test('followsLinksIfFollowLinksTrue', () { List list = dir.listSync(); expect(list, hasLength(3)); expect(list, contains(allOf(isFile, hasPath(ns('/foo/bar'))))); expect(list, contains(allOf(isDirectory, hasPath(ns('/foo/baz'))))); expect(list, contains(allOf(isFile, hasPath(ns('/foo/quux'))))); }); test('returnsLinkObjectsForRecursiveLinkIfFollowLinksTrue', () { expect( dir.listSync(recursive: true), allOf( hasLength(9), allOf( contains(allOf(isFile, hasPath(ns('/foo/bar')))), contains(allOf(isFile, hasPath(ns('/foo/quux')))), contains(allOf(isFile, hasPath(ns('/foo/baz/qux')))), contains(allOf(isFile, hasPath(ns('/foo/baz/quuz')))), contains(allOf(isFile, hasPath(ns('/foo/baz/grault/qux')))), contains(allOf(isFile, hasPath(ns('/foo/baz/grault/quuz')))), ), allOf( contains(allOf(isDirectory, hasPath(ns('/foo/baz')))), contains(allOf(isDirectory, hasPath(ns('/foo/baz/grault')))), ), contains(allOf(isLink, hasPath(ns('/foo/baz/grault/grault')))), ), ); }); test('recurseIntoDirectoriesIfRecursiveTrueFollowLinksFalse', () { expect( dir.listSync(recursive: true, followLinks: false), allOf( hasLength(6), contains(allOf(isFile, hasPath(ns('/foo/bar')))), contains(allOf(isFile, hasPath(ns('/foo/baz/qux')))), contains(allOf(isLink, hasPath(ns('/foo/quux')))), contains(allOf(isLink, hasPath(ns('/foo/baz/quuz')))), contains(allOf(isLink, hasPath(ns('/foo/baz/grault')))), contains(allOf(isDirectory, hasPath(ns('/foo/baz')))), ), ); }); test('childEntriesNotNormalized', () { dir = fs.directory(ns('/bar/baz'))..createSync(recursive: true); fs.file(ns('/bar/baz/qux')).createSync(); List list = fs.directory(ns('/bar//../bar/./baz')).listSync(); expect(list, hasLength(1)); expect(list[0], allOf(isFile, hasPath(ns('/bar//../bar/./baz/qux')))); }); test('symlinksToNotFoundAlwaysReturnedAsLinks', () { dir = fs.directory(ns('/bar'))..createSync(); fs.link(ns('/bar/baz')).createSync('qux'); for (bool followLinks in const [true, false]) { List list = dir.listSync(followLinks: followLinks); expect(list, hasLength(1)); expect(list[0], allOf(isLink, hasPath(ns('/bar/baz')))); } }); }); test('childEntities', () { Directory dir = fs.directory(ns('/foo'))..createSync(); dir.childDirectory('bar').createSync(); dir.childFile('baz').createSync(); dir.childLink('qux').createSync('bar'); expect(fs.directory(ns('/foo/bar')), exists); expect(fs.file(ns('/foo/baz')), exists); expect(fs.link(ns('/foo/qux')), exists); }); }); group('File', () { test('uri', () { expect(fs.file(ns('/foo')).uri, fs.path.toUri(ns('/foo'))); expect(fs.file('foo').uri.toString(), 'foo'); }); group('create', () { test('returnsCovariantType', () async { expect(await fs.file(ns('/foo')).create(), isFile); }); test('succeedsIfTailDoesntAlreadyExist', () { fs.file(ns('/foo')).createSync(); expect(fs.file(ns('/foo')), exists); }); test('succeedsIfAlreadyExistsAsFile', () { fs.file(ns('/foo')).createSync(); fs.file(ns('/foo')).createSync(); expect(fs.file(ns('/foo')), exists); }); test('throwsIfAncestorDoesntExistRecursiveFalse', () { expectFileSystemException(ErrorCodes.ENOENT, () { fs.file(ns('/foo/bar')).createSync(); }); }); test('succeedsIfAncestorDoesntExistRecursiveTrue', () { fs.file(ns('/foo/bar')).createSync(recursive: true); expect(fs.file(ns('/foo/bar')), exists); }); test('throwsIfAlreadyExistsAsDirectory', () { fs.directory(ns('/foo')).createSync(); expectFileSystemException(ErrorCodes.EISDIR, () { fs.file(ns('/foo')).createSync(); }); }); test('throwsIfAlreadyExistsAsLinkToDirectory', () { fs.directory(ns('/foo')).createSync(); fs.link(ns('/bar')).createSync(ns('/foo')); expectFileSystemException(ErrorCodes.EISDIR, () { fs.file(ns('/bar')).createSync(); }); }); test('succeedsIfAlreadyExistsAsLinkToFile', () { fs.file(ns('/foo')).createSync(); fs.link(ns('/bar')).createSync(ns('/foo')); fs.file(ns('/bar')).createSync(); expect(fs.file(ns('/bar')), exists); }); test('succeedsIfAlreadyExistsAsLinkToNotFoundAtTail', () { fs.link(ns('/foo')).createSync(ns('/bar')); fs.file(ns('/foo')).createSync(); expect(fs.typeSync(ns('/foo'), followLinks: false), FileSystemEntityType.link); expect(fs.typeSync(ns('/bar'), followLinks: false), FileSystemEntityType.file); }); test('throwsIfAlreadyExistsAsLinkToNotFoundViaTraversal', () { fs.link(ns('/foo')).createSync(ns('/bar/baz')); expectFileSystemException(ErrorCodes.ENOENT, () { fs.file(ns('/foo')).createSync(); }); expectFileSystemException(ErrorCodes.ENOENT, () { fs.file(ns('/foo')).createSync(recursive: true); }); }); /* test('throwsIfPathSegmentIsLinkToNotFoundAndRecursiveTrue', () { fs.link(ns('/foo')).createSync(ns('/bar')); expectFileSystemException(ErrorCodes.ENOENT, () { fs.file(ns('/foo/baz')).createSync(recursive: true); }); }); */ test('succeedsIfAlreadyExistsAsLinkToNotFoundInDifferentDirectory', () { fs.directory(ns('/foo')).createSync(); fs.directory(ns('/bar')).createSync(); fs.link(ns('/bar/baz')).createSync(ns('/foo/qux')); fs.file(ns('/bar/baz')).createSync(); expect(fs.typeSync(ns('/bar/baz'), followLinks: false), FileSystemEntityType.link); expect(fs.typeSync(ns('/foo/qux'), followLinks: false), FileSystemEntityType.file); }); }); group('rename', () { test('returnsCovariantType', () async { File f() => fs.file(ns('/foo'))..createSync(); expect(await f().rename(ns('/bar')), isFile); expect(f().renameSync(ns('/baz')), isFile); }); test('succeedsIfDestinationDoesntExistAtTail', () { File src = fs.file(ns('/foo'))..createSync(); File dest = src.renameSync(ns('/bar')); expect(fs.file(ns('/foo')), isNot(exists)); expect(fs.file(ns('/bar')), exists); expect(dest.path, ns('/bar')); }); test('throwsIfDestinationDoesntExistViaTraversal', () { File f = fs.file(ns('/foo'))..createSync(); expectFileSystemException(ErrorCodes.ENOENT, () { f.renameSync(ns('/bar/baz')); }); }); test('succeedsIfDestinationExistsAsFile', () { File f = fs.file(ns('/foo'))..createSync(); fs.file(ns('/bar')).createSync(); f.renameSync(ns('/bar')); expect(fs.file(ns('/foo')), isNot(exists)); expect(fs.file(ns('/bar')), exists); }); test('throwsIfDestinationExistsAsDirectory', () { File f = fs.file(ns('/foo'))..createSync(); fs.directory(ns('/bar')).createSync(); expectFileSystemException(ErrorCodes.EISDIR, () { f.renameSync(ns('/bar')); }); }); test('succeedsIfDestinationExistsAsLinkToFile', () { File f = fs.file(ns('/foo'))..createSync(); fs.file(ns('/bar')).createSync(); fs.link(ns('/baz')).createSync(ns('/bar')); f.renameSync(ns('/baz')); expect(fs.typeSync(ns('/foo')), FileSystemEntityType.notFound); expect(fs.typeSync(ns('/bar'), followLinks: false), FileSystemEntityType.file); expect(fs.typeSync(ns('/baz'), followLinks: false), FileSystemEntityType.file); }); test('throwsIfDestinationExistsAsLinkToDirectory', () { File f = fs.file(ns('/foo'))..createSync(); fs.directory(ns('/bar')).createSync(); fs.link(ns('/baz')).createSync(ns('/bar')); expectFileSystemException(ErrorCodes.EISDIR, () { f.renameSync(ns('/baz')); }); }); test('succeedsIfDestinationExistsAsLinkToNotFound', () { File f = fs.file(ns('/foo'))..createSync(); fs.link(ns('/bar')).createSync(ns('/baz')); f.renameSync(ns('/bar')); expect(fs.typeSync(ns('/foo')), FileSystemEntityType.notFound); expect(fs.typeSync(ns('/bar'), followLinks: false), FileSystemEntityType.file); }); test('throwsIfSourceDoesntExist', () { expectFileSystemException(ErrorCodes.ENOENT, () { fs.file(ns('/foo')).renameSync(ns('/bar')); }); }); test('throwsIfSourceExistsAsDirectory', () { fs.directory(ns('/foo')).createSync(); expectFileSystemException(ErrorCodes.EISDIR, () { fs.file(ns('/foo')).renameSync(ns('/bar')); }); }); test('succeedsIfSourceExistsAsLinkToFile', () { fs.file(ns('/foo')).createSync(); fs.link(ns('/bar')).createSync(ns('/foo')); fs.file(ns('/bar')).renameSync(ns('/baz')); expect(fs.typeSync(ns('/bar')), FileSystemEntityType.notFound); expect(fs.typeSync(ns('/baz'), followLinks: false), FileSystemEntityType.link); expect(fs.typeSync(ns('/baz'), followLinks: true), FileSystemEntityType.file); }); test('throwsIfSourceExistsAsLinkToDirectory', () { fs.directory(ns('/foo')).createSync(); fs.link(ns('/bar')).createSync(ns('/foo')); expectFileSystemException(ErrorCodes.EISDIR, () { fs.file(ns('/bar')).renameSync(ns('/baz')); }); }); test('throwsIfSourceExistsAsLinkToNotFound', () { fs.link(ns('/foo')).createSync(ns('/bar')); expectFileSystemException(ErrorCodes.ENOENT, () { fs.file(ns('/foo')).renameSync(ns('/baz')); }); }); }); group('copy', () { test('returnsCovariantType', () async { File f() => fs.file(ns('/foo'))..createSync(); expect(await f().copy(ns('/bar')), isFile); expect(f().copySync(ns('/baz')), isFile); }); test('succeedsIfDestinationDoesntExistAtTail', () { File f = fs.file(ns('/foo')) ..createSync() ..writeAsStringSync('foo'); f.copySync(ns('/bar')); expect(fs.file(ns('/foo')), exists); expect(fs.file(ns('/bar')), exists); expect(fs.file(ns('/foo')).readAsStringSync(), 'foo'); }); test('throwsIfDestinationDoesntExistViaTraversal', () { File f = fs.file(ns('/foo'))..createSync(); expectFileSystemException(ErrorCodes.ENOENT, () { f.copySync(ns('/bar/baz')); }); }); test('succeedsIfDestinationExistsAsFile', () { File f = fs.file(ns('/foo')) ..createSync() ..writeAsStringSync('foo'); fs.file(ns('/bar')) ..createSync() ..writeAsStringSync('bar'); f.copySync(ns('/bar')); expect(fs.file(ns('/foo')), exists); expect(fs.file(ns('/bar')), exists); expect(fs.file(ns('/foo')).readAsStringSync(), 'foo'); expect(fs.file(ns('/bar')).readAsStringSync(), 'foo'); }); test('throwsIfDestinationExistsAsDirectory', () { File f = fs.file(ns('/foo'))..createSync(); fs.directory(ns('/bar')).createSync(); expectFileSystemException(ErrorCodes.EISDIR, () { f.copySync(ns('/bar')); }); }); test('succeedsIfDestinationExistsAsLinkToFile', () { File f = fs.file(ns('/foo')) ..createSync() ..writeAsStringSync('foo'); fs.file(ns('/bar')) ..createSync() ..writeAsStringSync('bar'); fs.link(ns('/baz')).createSync(ns('/bar')); f.copySync(ns('/baz')); expect(fs.typeSync(ns('/foo'), followLinks: false), FileSystemEntityType.file); expect(fs.typeSync(ns('/bar'), followLinks: false), FileSystemEntityType.file); expect(fs.typeSync(ns('/baz'), followLinks: false), FileSystemEntityType.link); expect(fs.file(ns('/foo')).readAsStringSync(), 'foo'); expect(fs.file(ns('/bar')).readAsStringSync(), 'foo'); }, skip: io.Platform.isWindows /* No links on Windows */); test('throwsIfDestinationExistsAsLinkToDirectory', () { File f = fs.file(ns('/foo'))..createSync(); fs.directory(ns('/bar')).createSync(); fs.link(ns('/baz')).createSync(ns('/bar')); expectFileSystemException(ErrorCodes.EISDIR, () { f.copySync(ns('/baz')); }); }); test('throwsIfSourceDoesntExist', () { expectFileSystemException(ErrorCodes.ENOENT, () { fs.file(ns('/foo')).copySync(ns('/bar')); }); }); test('throwsIfSourceExistsAsDirectory', () { fs.directory(ns('/foo')).createSync(); expectFileSystemException(ErrorCodes.EISDIR, () { fs.file(ns('/foo')).copySync(ns('/bar')); }); }); test('succeedsIfSourceExistsAsLinkToFile', () { fs.file(ns('/foo')) ..createSync() ..writeAsStringSync('foo'); fs.link(ns('/bar')).createSync(ns('/foo')); fs.file(ns('/bar')).copySync(ns('/baz')); expect(fs.typeSync(ns('/foo'), followLinks: false), FileSystemEntityType.file); expect(fs.typeSync(ns('/bar'), followLinks: false), FileSystemEntityType.link); expect(fs.typeSync(ns('/baz'), followLinks: false), FileSystemEntityType.file); expect(fs.file(ns('/foo')).readAsStringSync(), 'foo'); expect(fs.file(ns('/baz')).readAsStringSync(), 'foo'); }); test('succeedsIfDestinationIsInDifferentDirectoryThanSource', () { File f = fs.file(ns('/foo/bar')) ..createSync(recursive: true) ..writeAsStringSync('foo'); fs.directory(ns('/baz')).createSync(); f.copySync(ns('/baz/qux')); expect(fs.file(ns('/foo/bar')), exists); expect(fs.file(ns('/baz/qux')), exists); expect(fs.file(ns('/foo/bar')).readAsStringSync(), 'foo'); expect(fs.file(ns('/baz/qux')).readAsStringSync(), 'foo'); }); test('succeedsIfSourceIsLinkToFileInDifferentDirectory', () { fs.file(ns('/foo/bar')) ..createSync(recursive: true) ..writeAsStringSync('foo'); fs.link(ns('/baz/qux')).createSync(ns('/foo/bar'), recursive: true); fs.file(ns('/baz/qux')).copySync(ns('/baz/quux')); expect(fs.typeSync(ns('/foo/bar'), followLinks: false), FileSystemEntityType.file); expect(fs.typeSync(ns('/baz/qux'), followLinks: false), FileSystemEntityType.link); expect(fs.typeSync(ns('/baz/quux'), followLinks: false), FileSystemEntityType.file); expect(fs.file(ns('/foo/bar')).readAsStringSync(), 'foo'); expect(fs.file(ns('/baz/quux')).readAsStringSync(), 'foo'); }); test('succeedsIfDestinationIsLinkToFileInDifferentDirectory', () { fs.file(ns('/foo/bar')) ..createSync(recursive: true) ..writeAsStringSync('bar'); fs.file(ns('/baz/qux')) ..createSync(recursive: true) ..writeAsStringSync('qux'); fs.link(ns('/baz/quux')).createSync(ns('/foo/bar')); fs.file(ns('/baz/qux')).copySync(ns('/baz/quux')); expect(fs.typeSync(ns('/foo/bar'), followLinks: false), FileSystemEntityType.file); expect(fs.typeSync(ns('/baz/qux'), followLinks: false), FileSystemEntityType.file); expect(fs.typeSync(ns('/baz/quux'), followLinks: false), FileSystemEntityType.link); expect(fs.file(ns('/foo/bar')).readAsStringSync(), 'qux'); expect(fs.file(ns('/baz/qux')).readAsStringSync(), 'qux'); }, skip: io.Platform.isWindows /* No links on Windows */); }); group('length', () { test('throwsIfDoesntExist', () { expectFileSystemException(ErrorCodes.ENOENT, () { fs.file(ns('/foo')).lengthSync(); }); }); test('throwsIfExistsAsDirectory', () { fs.directory(ns('/foo')).createSync(); expectFileSystemException(ErrorCodes.EISDIR, () { fs.file(ns('/foo')).lengthSync(); }); }); test('returnsZeroForNewlyCreatedFile', () { File f = fs.file(ns('/foo'))..createSync(); expect(f.lengthSync(), 0); }); test('writeNBytesReturnsLengthN', () { File f = fs.file(ns('/foo'))..createSync(); f.writeAsBytesSync([1, 2, 3, 4], flush: true); expect(f.lengthSync(), 4); }); test('succeedsIfExistsAsLinkToFile', () { fs.file(ns('/foo')).createSync(); fs.link(ns('/bar')).createSync(ns('/foo')); expect(fs.file(ns('/bar')).lengthSync(), 0); }); }); group('absolute', () { test('returnsSamePathIfAlreadyAbsolute', () { expect(fs.file(ns('/foo')).absolute.path, ns('/foo')); }); test('succeedsForRelativePaths', () { expect(fs.file('foo').absolute.path, ns('/foo')); }); }); group('lastAccessed', () { test('isNowForNewlyCreatedFile', () { DateTime before = downstairs(); File f = fs.file(ns('/foo'))..createSync(); DateTime after = ceil(); DateTime accessed = f.lastAccessedSync(); expect(accessed, isSameOrAfter(before)); expect(accessed, isSameOrBefore(after)); }); test('throwsIfDoesntExist', () { expectFileSystemException(ErrorCodes.ENOENT, () { fs.file(ns('/foo')).lastAccessedSync(); }); }); test('throwsIfExistsAsDirectory', () { fs.directory(ns('/foo')).createSync(); expectFileSystemException(ErrorCodes.EISDIR, () { fs.file(ns('/foo')).lastAccessedSync(); }); }); test('succeedsIfExistsAsLinkToFile', () { DateTime before = downstairs(); fs.file(ns('/foo')).createSync(); fs.link(ns('/bar')).createSync(ns('/foo')); DateTime after = ceil(); DateTime accessed = fs.file(ns('/bar')).lastAccessedSync(); expect(accessed, isSameOrAfter(before)); expect(accessed, isSameOrBefore(after)); }); }); group('setLastAccessed', () { final DateTime time = DateTime(1999); test('throwsIfDoesntExist', () { expectFileSystemException(ErrorCodes.ENOENT, () { fs.file(ns('/foo')).setLastAccessedSync(time); }); }); test('throwsIfExistsAsDirectory', () { fs.directory(ns('/foo')).createSync(); expectFileSystemException(ErrorCodes.EISDIR, () { fs.file(ns('/foo')).setLastAccessedSync(time); }); }); test('succeedsIfExistsAsFile', () { File f = fs.file(ns('/foo'))..createSync(); f.setLastAccessedSync(time); expect(fs.file(ns('/foo')).lastAccessedSync(), time); }); test('succeedsIfExistsAsLinkToFile', () { File f = fs.file(ns('/foo'))..createSync(); fs.link(ns('/bar')).createSync(ns('/foo')); f.setLastAccessedSync(time); expect(fs.file(ns('/bar')).lastAccessedSync(), time); }); }); group('lastModified', () { test('isNowForNewlyCreatedFile', () { DateTime before = downstairs(); File f = fs.file(ns('/foo'))..createSync(); DateTime after = ceil(); DateTime modified = f.lastModifiedSync(); expect(modified, isSameOrAfter(before)); expect(modified, isSameOrBefore(after)); }); test('throwsIfDoesntExist', () { expectFileSystemException(ErrorCodes.ENOENT, () { fs.file(ns('/foo')).lastModifiedSync(); }); }); test('throwsIfExistsAsDirectory', () { fs.directory(ns('/foo')).createSync(); expectFileSystemException(ErrorCodes.EISDIR, () { fs.file(ns('/foo')).lastModifiedSync(); }); }); test('succeedsIfExistsAsLinkToFile', () { DateTime before = downstairs(); fs.file(ns('/foo')).createSync(); fs.link(ns('/bar')).createSync(ns('/foo')); DateTime after = ceil(); DateTime modified = fs.file(ns('/bar')).lastModifiedSync(); expect(modified, isSameOrAfter(before)); expect(modified, isSameOrBefore(after)); }); }); group('setLastModified', () { final DateTime time = DateTime(1999); test('throwsIfDoesntExist', () { expectFileSystemException(ErrorCodes.ENOENT, () { fs.file(ns('/foo')).setLastModifiedSync(time); }); }); test('throwsIfExistsAsDirectory', () { fs.directory(ns('/foo')).createSync(); expectFileSystemException(ErrorCodes.EISDIR, () { fs.file(ns('/foo')).setLastModifiedSync(time); }); }); test('succeedsIfExistsAsFile', () { File f = fs.file(ns('/foo'))..createSync(); f.setLastModifiedSync(time); expect(fs.file(ns('/foo')).lastModifiedSync(), time); }); test('succeedsIfExistsAsLinkToFile', () { File f = fs.file(ns('/foo'))..createSync(); fs.link(ns('/bar')).createSync(ns('/foo')); f.setLastModifiedSync(time); expect(fs.file(ns('/bar')).lastModifiedSync(), time); }); }); group('open', () { void testIfDoesntExistAtTail(FileMode mode) { if (mode == FileMode.read) { test('throwsIfDoesntExistAtTail', () { expectFileSystemException(ErrorCodes.ENOENT, () { fs.file(ns('/bar')).openSync(mode: mode); }); }); } else { test('createsFileIfDoesntExistAtTail', () { RandomAccessFile raf = fs.file(ns('/bar')).openSync(mode: mode); raf.closeSync(); expect(fs.file(ns('/bar')), exists); }); } } void testThrowsIfDoesntExistViaTraversal(FileMode mode) { test('throwsIfDoesntExistViaTraversal', () { expectFileSystemException(ErrorCodes.ENOENT, () { fs.file(ns('/bar/baz')).openSync(mode: mode); }); }); } void testRandomAccessFileOperations(FileMode mode) { group('RandomAccessFile', () { late File f; late RandomAccessFile raf; setUp(() { f = fs.file(ns('/foo'))..createSync(); f.writeAsStringSync('pre-existing content\n', flush: true); raf = f.openSync(mode: mode); }); tearDown(() { try { raf.closeSync(); } on FileSystemException { // Ignore; a test may have already closed it. } }); test('succeedsIfClosedAfterClosed', () { raf.closeSync(); expectFileSystemException(null, () { raf.closeSync(); }); }); test('throwsIfReadAfterClose', () { raf.closeSync(); expectFileSystemException(null, () { raf.readByteSync(); }); }); test('throwsIfWriteAfterClose', () { raf.closeSync(); expectFileSystemException(null, () { raf.writeByteSync(0xBAD); }); }); test('throwsIfTruncateAfterClose', () { raf.closeSync(); expectFileSystemException(null, () { raf.truncateSync(0); }); }); if (mode == FileMode.write || mode == FileMode.writeOnly) { test('lengthIsResetToZeroIfOpened', () { expect(raf.lengthSync(), equals(0)); }); test('throwsIfAsyncUnawaited', () async { try { final Future future = raf.flush(); expectFileSystemException(null, () => raf.flush()); expectFileSystemException(null, () => raf.flushSync()); await expectLater(future, completes); raf.flushSync(); } finally { raf.closeSync(); } }); } else { test('lengthIsNotModifiedIfOpened', () { expect(raf.lengthSync(), isNot(equals(0))); }); } if (mode == FileMode.writeOnly || mode == FileMode.writeOnlyAppend) { test('throwsIfReadByte', () { expectFileSystemException(ErrorCodes.EBADF, () { raf.readByteSync(); }); }); test('throwsIfRead', () { expectFileSystemException(ErrorCodes.EBADF, () { raf.readSync(2); }); }); test('throwsIfReadInto', () { expectFileSystemException(ErrorCodes.EBADF, () { raf.readIntoSync(List.filled(5, 0)); }); }); } else { group('read', () { setUp(() { if (mode == FileMode.write) { // Write data back that we truncated when opening the file. raf.writeStringSync('pre-existing content\n'); } // Reset the position to zero so we can read the content. raf.setPositionSync(0); }); test('readByte', () { expect(utf8.decode([raf.readByteSync()]), 'p'); }); test('read', () { List bytes = raf.readSync(1024); expect(bytes.length, 21); expect(utf8.decode(bytes), 'pre-existing content\n'); }); test('readIntoWithBufferLargerThanContent', () { List buffer = List.filled(1024, 0); int numRead = raf.readIntoSync(buffer); expect(numRead, 21); expect(utf8.decode(buffer.sublist(0, 21)), 'pre-existing content\n'); }); test('readIntoWithBufferSmallerThanContent', () { List buffer = List.filled(10, 0); int numRead = raf.readIntoSync(buffer); expect(numRead, 10); expect(utf8.decode(buffer), 'pre-existi'); }); test('readIntoWithStart', () { List buffer = List.filled(10, 0); int numRead = raf.readIntoSync(buffer, 2); expect(numRead, 8); expect(utf8.decode(buffer.sublist(2)), 'pre-exis'); }); test('readIntoWithStartAndEnd', () { List buffer = List.filled(10, 0); int numRead = raf.readIntoSync(buffer, 2, 5); expect(numRead, 3); expect(utf8.decode(buffer.sublist(2, 5)), 'pre'); }); test('openReadHandleDoesNotChange', () { final String initial = utf8.decode(raf.readSync(4)); expect(initial, 'pre-'); final File newFile = f.renameSync(ns('/bar')); String rest = utf8.decode(raf.readSync(1024)); expect(rest, 'existing content\n'); assert(newFile.path != f.path); expect(f, isNot(exists)); expect(newFile, exists); // [RandomAccessFile.path] always returns the original path. expect(raf.path, f.path); }); }); } if (mode == FileMode.read) { test('throwsIfWriteByte', () { expectFileSystemException(ErrorCodes.EBADF, () { raf.writeByteSync(0xBAD); }); }); test('throwsIfWriteFrom', () { expectFileSystemException(ErrorCodes.EBADF, () { raf.writeFromSync([1, 2, 3, 4]); }); }); test('throwsIfWriteString', () { expectFileSystemException(ErrorCodes.EBADF, () { raf.writeStringSync('This should throw.'); }); }); } else { test('lengthGrowsAsDataIsWritten', () { int lengthBefore = f.lengthSync(); raf.writeByteSync(0xFACE); expect(raf.lengthSync(), lengthBefore + 1); }); test('flush', () { int lengthBefore = f.lengthSync(); raf.writeByteSync(0xFACE); raf.flushSync(); expect(f.lengthSync(), lengthBefore + 1); }); test('writeByte', () { raf.writeByteSync(utf8.encode('A').first); raf.flushSync(); if (mode == FileMode.write || mode == FileMode.writeOnly) { expect(f.readAsStringSync(), 'A'); } else { expect(f.readAsStringSync(), 'pre-existing content\nA'); } }); test('writeFrom', () { raf.writeFromSync(utf8.encode('Hello world')); raf.flushSync(); if (mode == FileMode.write || mode == FileMode.writeOnly) { expect(f.readAsStringSync(), 'Hello world'); } else { expect(f.readAsStringSync(), 'pre-existing content\nHello world'); } }); test('writeFromWithStart', () { raf.writeFromSync(utf8.encode('Hello world'), 2); raf.flushSync(); if (mode == FileMode.write || mode == FileMode.writeOnly) { expect(f.readAsStringSync(), 'llo world'); } else { expect( f.readAsStringSync(), 'pre-existing content\nllo world'); } }); test('writeFromWithStartAndEnd', () { raf.writeFromSync(utf8.encode('Hello world'), 2, 5); raf.flushSync(); if (mode == FileMode.write || mode == FileMode.writeOnly) { expect(f.readAsStringSync(), 'llo'); } else { expect(f.readAsStringSync(), 'pre-existing content\nllo'); } }); test('writeString', () { raf.writeStringSync('Hello world'); raf.flushSync(); if (mode == FileMode.write || mode == FileMode.writeOnly) { expect(f.readAsStringSync(), 'Hello world'); } else { expect(f.readAsStringSync(), 'pre-existing content\nHello world'); } }); test('openWriteHandleDoesNotChange', () { raf.writeStringSync('Hello '); final File newFile = f.renameSync(ns('/bar')); raf.writeStringSync('world'); final String contents = newFile.readAsStringSync(); if (mode == FileMode.write || mode == FileMode.writeOnly) { expect(contents, 'Hello world'); } else { expect(contents, 'pre-existing content\nHello world'); } assert(newFile.path != f.path); expect(f, isNot(exists)); expect(newFile, exists); // [RandomAccessFile.path] always returns the original path. expect(raf.path, f.path); }); } if (mode == FileMode.append || mode == FileMode.writeOnlyAppend) { test('positionInitializedToEndOfFile', () { expect(raf.positionSync(), 21); }); } else { test('positionInitializedToZero', () { expect(raf.positionSync(), 0); }); } group('position', () { setUp(() { if (mode == FileMode.write || mode == FileMode.writeOnly) { // Write data back that we truncated when opening the file. raf.writeStringSync('pre-existing content\n'); } }); if (mode != FileMode.writeOnly && mode != FileMode.writeOnlyAppend) { test('growsAfterRead', () { raf.setPositionSync(0); raf.readSync(10); expect(raf.positionSync(), 10); }); test('affectsRead', () { raf.setPositionSync(5); expect(utf8.decode(raf.readSync(5)), 'xisti'); }); } if (mode == FileMode.read) { test('succeedsIfSetPastEndOfFile', () { raf.setPositionSync(32); expect(raf.positionSync(), 32); }); } else { test('growsAfterWrite', () { int positionBefore = raf.positionSync(); raf.writeStringSync('Hello world'); expect(raf.positionSync(), positionBefore + 11); }); test('affectsWrite', () { raf.setPositionSync(5); raf.writeStringSync('-yo-'); raf.flushSync(); expect(f.readAsStringSync(), 'pre-e-yo-ing content\n'); }); test('succeedsIfSetAndWrittenPastEndOfFile', () { raf.setPositionSync(32); expect(raf.positionSync(), 32); raf.writeStringSync('here'); raf.flushSync(); List bytes = f.readAsBytesSync(); expect(bytes.length, 36); expect(utf8.decode(bytes.sublist(0, 21)), 'pre-existing content\n'); expect(utf8.decode(bytes.sublist(32, 36)), 'here'); expect(bytes.sublist(21, 32), everyElement(0)); }); } test('throwsIfSetToNegativeNumber', () { expectFileSystemException(ErrorCodes.EINVAL, () { raf.setPositionSync(-12); }); }); }); if (mode == FileMode.read) { test('throwsIfTruncate', () { expectFileSystemException(ErrorCodes.EINVAL, () { raf.truncateSync(5); }); }); } else { group('truncate', () { setUp(() { if (mode == FileMode.write || mode == FileMode.writeOnly) { // Write data back that we truncated when opening the file. raf.writeStringSync('pre-existing content\n'); } }); test('succeedsIfSetWithinRangeOfContent', () { raf.truncateSync(5); raf.flushSync(); expect(f.lengthSync(), 5); expect(f.readAsStringSync(), 'pre-e'); }); test('succeedsIfSetToZero', () { raf.truncateSync(0); raf.flushSync(); expect(f.lengthSync(), 0); expect(f.readAsStringSync(), isEmpty); }); test('throwsIfSetToNegativeNumber', () { expectFileSystemException(ErrorCodes.EINVAL, () { raf.truncateSync(-2); }); }); test('extendsFileIfSetPastEndOfFile', () { raf.truncateSync(32); raf.flushSync(); List bytes = f.readAsBytesSync(); expect(bytes.length, 32); expect(utf8.decode(bytes.sublist(0, 21)), 'pre-existing content\n'); expect(bytes.sublist(21, 32), everyElement(0)); }); }); } }); } void testOpenWithMode(FileMode mode) { testIfDoesntExistAtTail(mode); testThrowsIfDoesntExistViaTraversal(mode); testRandomAccessFileOperations(mode); } group('READ', () => testOpenWithMode(FileMode.read)); group('WRITE', () => testOpenWithMode(FileMode.write)); group('APPEND', () => testOpenWithMode(FileMode.append)); group('WRITE_ONLY', () => testOpenWithMode(FileMode.writeOnly)); group('WRITE_ONLY_APPEND', () => testOpenWithMode(FileMode.writeOnlyAppend)); }); group('openRead', () { test('throwsIfDoesntExist', () { Stream> stream = fs.file(ns('/foo')).openRead(); expect(stream.drain(), throwsFileSystemException(ErrorCodes.ENOENT)); }); test('succeedsIfExistsAsFile', () async { File f = fs.file(ns('/foo'))..createSync(); f.writeAsStringSync('Hello world', flush: true); Stream> stream = f.openRead(); List> data = await stream.toList(); expect(data, hasLength(1)); expect(utf8.decode(data[0]), 'Hello world'); }); test('throwsIfExistsAsDirectory', () { fs.directory(ns('/foo')).createSync(); Stream> stream = fs.file(ns('/foo')).openRead(); expect(stream.drain(), throwsFileSystemException(ErrorCodes.EISDIR)); }); test('succeedsIfExistsAsLinkToFile', () async { File f = fs.file(ns('/foo'))..createSync(); fs.link(ns('/bar')).createSync(ns('/foo')); f.writeAsStringSync('Hello world', flush: true); Stream> stream = fs.file(ns('/bar')).openRead(); List> data = await stream.toList(); expect(data, hasLength(1)); expect(utf8.decode(data[0]), 'Hello world'); }); test('respectsStartAndEndParameters', () async { File f = fs.file(ns('/foo'))..createSync(); f.writeAsStringSync('Hello world', flush: true); Stream> stream = f.openRead(2); List> data = await stream.toList(); expect(data, hasLength(1)); expect(utf8.decode(data[0]), 'llo world'); stream = f.openRead(2, 5); data = await stream.toList(); expect(data, hasLength(1)); expect(utf8.decode(data[0]), 'llo'); }); test('throwsIfStartParameterIsNegative', () async { File f = fs.file(ns('/foo'))..createSync(); Stream> stream = f.openRead(-2); expect(stream.drain(), throwsRangeError); }); test('stopsAtEndOfFileIfEndParameterIsPastEndOfFile', () async { File f = fs.file(ns('/foo'))..createSync(); f.writeAsStringSync('Hello world', flush: true); Stream> stream = f.openRead(2, 1024); List> data = await stream.toList(); expect(data, hasLength(1)); expect(utf8.decode(data[0]), 'llo world'); }); test('providesSingleSubscriptionStream', () async { File f = fs.file(ns('/foo'))..createSync(); f.writeAsStringSync('Hello world', flush: true); Stream> stream = f.openRead(); expect(stream.isBroadcast, isFalse); await stream.drain(); }); test('openReadHandleDoesNotChange', () async { // Ideally, `data` should be large enough so that its contents are // split across multiple chunks in the [Stream]. However, there // doesn't seem to be a good way to determine the chunk size used by // [io.File]. final List data = List.generate( 1024 * 256, (int index) => index & 0xFF, growable: false, ); final File f = fs.file(ns('/foo'))..createSync(); f.writeAsBytesSync(data, flush: true); final Stream> stream = f.openRead(); File? newFile; List? initialChunk; final List remainingChunks = []; await for (List chunk in stream) { if (initialChunk == null) { initialChunk = chunk; assert(initialChunk.isNotEmpty); expect(initialChunk, data.getRange(0, initialChunk.length)); newFile = f.renameSync(ns('/bar')); } else { remainingChunks.addAll(chunk); } } expect( remainingChunks, data.getRange(initialChunk!.length, data.length), ); assert(newFile?.path != f.path); expect(f, isNot(exists)); expect(newFile, exists); }); test('openReadCompatibleWithUtf8Decoder', () async { const content = 'Hello world!'; File file = fs.file(ns('/foo')) ..createSync() ..writeAsStringSync(content); expect( await file .openRead() .transform(utf8.decoder) .transform(const LineSplitter()) .first, content, ); }); }); group('openWrite', () { test('createsFileIfDoesntExist', () async { await fs.file(ns('/foo')).openWrite().close(); expect(fs.file(ns('/foo')), exists); }); test('throwsIfExistsAsDirectory', () { fs.directory(ns('/foo')).createSync(); expect(fs.file(ns('/foo')).openWrite().close(), throwsFileSystemException(ErrorCodes.EISDIR)); }); test('throwsIfExistsAsLinkToDirectory', () { fs.directory(ns('/foo')).createSync(); fs.link(ns('/bar')).createSync(ns('/foo')); expect(fs.file(ns('/bar')).openWrite().close(), throwsFileSystemException(ErrorCodes.EISDIR)); }); test('throwsIfModeIsRead', () { expect(() => fs.file(ns('/foo')).openWrite(mode: FileMode.read), throwsArgumentError); }); test('succeedsIfExistsAsEmptyFile', () async { File f = fs.file(ns('/foo'))..createSync(); IOSink sink = f.openWrite(); sink.write('Hello world'); await sink.flush(); await sink.close(); expect(f.readAsStringSync(), 'Hello world'); }); test('succeedsIfExistsAsLinkToFile', () async { fs.file(ns('/foo')).createSync(); fs.link(ns('/bar')).createSync(ns('/foo')); IOSink sink = fs.file(ns('/bar')).openWrite(); sink.write('Hello world'); await sink.flush(); await sink.close(); expect(fs.file(ns('/foo')).readAsStringSync(), 'Hello world'); }); test('overwritesContentInWriteMode', () async { File f = fs.file(ns('/foo'))..createSync(); f.writeAsStringSync('Hello'); IOSink sink = f.openWrite(); sink.write('Goodbye'); await sink.flush(); await sink.close(); expect(fs.file(ns('/foo')).readAsStringSync(), 'Goodbye'); }); test('appendsContentInAppendMode', () async { File f = fs.file(ns('/foo'))..createSync(); f.writeAsStringSync('Hello'); IOSink sink = f.openWrite(mode: FileMode.append); sink.write('Goodbye'); await sink.flush(); await sink.close(); expect(fs.file(ns('/foo')).readAsStringSync(), 'HelloGoodbye'); }); test('openWriteHandleDoesNotChange', () async { File f = fs.file(ns('/foo'))..createSync(); IOSink sink = f.openWrite(); sink.write('Hello'); await sink.flush(); final File newFile = f.renameSync(ns('/bar')); sink.write('Goodbye'); await sink.flush(); await sink.close(); expect(newFile.readAsStringSync(), 'HelloGoodbye'); assert(newFile.path != f.path); expect(f, isNot(exists)); expect(newFile, exists); }); group('ioSink', () { late File f; late IOSink sink; late bool isSinkClosed; Future closeSink() { Future future = sink.close(); isSinkClosed = true; return future; } setUp(() { f = fs.file(ns('/foo')); sink = f.openWrite(); isSinkClosed = false; }); tearDown(() async { if (!isSinkClosed) { await closeSink(); } }); test('throwsIfAddError', () async { sink.addError(ArgumentError()); expect(sink.done, throwsArgumentError); isSinkClosed = true; }); test('allowsChangingEncoding', () async { sink.encoding = latin1; sink.write('ÿ'); sink.encoding = utf8; sink.write('ÿ'); await sink.flush(); expect(await f.readAsBytes(), [255, 195, 191]); }); test('succeedsIfAddRawData', () async { sink.add([1, 2, 3, 4]); await sink.flush(); expect(await f.readAsBytes(), [1, 2, 3, 4]); }); test('succeedsIfWrite', () async { sink.write('Hello world'); await sink.flush(); expect(await f.readAsString(), 'Hello world'); }); test('succeedsIfWriteAll', () async { sink.writeAll(['foo', 'bar', 'baz'], ' '); await sink.flush(); expect(await f.readAsString(), 'foo bar baz'); }); test('succeedsIfWriteCharCode', () async { sink.writeCharCode(35); await sink.flush(); expect(await f.readAsString(), '#'); }); test('succeedsIfWriteln', () async { sink.writeln('Hello world'); await sink.flush(); expect(await f.readAsString(), 'Hello world\n'); }); test('ignoresDataWrittenAfterClose', () async { sink.write('Before close'); await closeSink(); expect(() => sink.write('After close'), throwsStateError); expect(await f.readAsString(), 'Before close'); }); test('ignoresCloseAfterAlreadyClosed', () async { sink.write('Hello world'); Future f1 = closeSink(); Future f2 = closeSink(); await Future.wait(>[f1, f2]); }); test('returnsAccurateDoneFuture', () async { bool done = false; // ignore: unawaited_futures sink.done.then((dynamic _) => done = true); expect(done, isFalse); sink.write('foo'); expect(done, isFalse); await sink.close(); expect(done, isTrue); }); group('addStream', () { late StreamController> controller; late bool isControllerClosed; Future closeController() { Future future = controller.close(); isControllerClosed = true; return future; } setUp(() { controller = StreamController>(); isControllerClosed = false; sink.addStream(controller.stream); }); tearDown(() async { if (!isControllerClosed) { await closeController(); } }); test('succeedsIfStreamProducesData', () async { controller.add([1, 2, 3, 4, 5]); await closeController(); await sink.flush(); expect(await f.readAsBytes(), [1, 2, 3, 4, 5]); }); test('blocksCallToAddWhileStreamIsActive', () { expect(() => sink.add([1, 2, 3]), throwsStateError); }); test('blocksCallToWriteWhileStreamIsActive', () { expect(() => sink.write('foo'), throwsStateError); }); test('blocksCallToWriteAllWhileStreamIsActive', () { expect(() => sink.writeAll(['a', 'b']), throwsStateError); }); test('blocksCallToWriteCharCodeWhileStreamIsActive', () { expect(() => sink.writeCharCode(35), throwsStateError); }); test('blocksCallToWritelnWhileStreamIsActive', () { expect(() => sink.writeln('foo'), throwsStateError); }); test('blocksCallToFlushWhileStreamIsActive', () { expect(() => sink.flush(), throwsStateError); }); }); }); }); group('readAsBytes', () { test('throwsIfDoesntExist', () { expectFileSystemException(ErrorCodes.ENOENT, () { fs.file(ns('/foo')).readAsBytesSync(); }); }); test('throwsIfExistsAsDirectory', () { fs.directory(ns('/foo')).createSync(); expectFileSystemException(ErrorCodes.EISDIR, () { fs.file(ns('/foo')).readAsBytesSync(); }); }); test('throwsIfExistsAsLinkToDirectory', () { fs.directory(ns('/foo')).createSync(); fs.link(ns('/bar')).createSync(ns('/foo')); expectFileSystemException(ErrorCodes.EISDIR, () { fs.file(ns('/bar')).readAsBytesSync(); }); }); test('succeedsIfExistsAsFile', () { File f = fs.file(ns('/foo'))..createSync(); f.writeAsBytesSync([1, 2, 3, 4]); expect(f.readAsBytesSync(), [1, 2, 3, 4]); }); test('succeedsIfExistsAsLinkToFile', () { fs.file(ns('/foo')).createSync(); fs.link(ns('/bar')).createSync(ns('/foo')); fs.file(ns('/foo')).writeAsBytesSync([1, 2, 3, 4]); expect(fs.file(ns('/bar')).readAsBytesSync(), [1, 2, 3, 4]); }); test('returnsEmptyListForZeroByteFile', () { File f = fs.file(ns('/foo'))..createSync(); expect(f.readAsBytesSync(), isEmpty); }); test('returns a copy, not a view, of the file content', () { File f = fs.file(ns('/foo'))..createSync(); f.writeAsBytesSync([1, 2, 3, 4]); List result = f.readAsBytesSync(); expect(result, [1, 2, 3, 4]); result[0] = 10; expect(f.readAsBytesSync(), [1, 2, 3, 4]); }); }); group('readAsString', () { test('throwsIfDoesntExist', () { expectFileSystemException(ErrorCodes.ENOENT, () { fs.file(ns('/foo')).readAsStringSync(); }); }); test('throwsIfExistsAsDirectory', () { fs.directory(ns('/foo')).createSync(); expectFileSystemException(ErrorCodes.EISDIR, () { fs.file(ns('/foo')).readAsStringSync(); }); }); test('throwsIfExistsAsLinkToDirectory', () { fs.directory(ns('/foo')).createSync(); fs.link(ns('/bar')).createSync(ns('/foo')); expectFileSystemException(ErrorCodes.EISDIR, () { fs.file(ns('/bar')).readAsStringSync(); }); }); test('succeedsIfExistsAsFile', () { File f = fs.file(ns('/foo'))..createSync(); f.writeAsStringSync('Hello world'); expect(f.readAsStringSync(), 'Hello world'); }); test('succeedsIfExistsAsLinkToFile', () { fs.file(ns('/foo')).createSync(); fs.link(ns('/bar')).createSync(ns('/foo')); fs.file(ns('/foo')).writeAsStringSync('Hello world'); expect(fs.file(ns('/bar')).readAsStringSync(), 'Hello world'); }); test('returnsEmptyStringForZeroByteFile', () { File f = fs.file(ns('/foo'))..createSync(); expect(f.readAsStringSync(), isEmpty); }); }); group('readAsLines', () { const String testString = 'Hello world\nHow are you?\nI am fine'; final List expectedLines = [ 'Hello world', 'How are you?', 'I am fine', ]; test('throwsIfDoesntExist', () { expectFileSystemException(ErrorCodes.ENOENT, () { fs.file(ns('/foo')).readAsLinesSync(); }); }); test('throwsIfExistsAsDirectory', () { fs.directory(ns('/foo')).createSync(); expectFileSystemException(ErrorCodes.EISDIR, () { fs.file(ns('/foo')).readAsLinesSync(); }); }); test('throwsIfExistsAsLinkToDirectory', () { fs.directory(ns('/foo')).createSync(); fs.link(ns('/bar')).createSync(ns('/foo')); expectFileSystemException(ErrorCodes.EISDIR, () { fs.file(ns('/bar')).readAsLinesSync(); }); }); test('succeedsIfExistsAsFile', () { File f = fs.file(ns('/foo'))..createSync(); f.writeAsStringSync(testString); expect(f.readAsLinesSync(), expectedLines); }); test('succeedsIfExistsAsLinkToFile', () { File f = fs.file(ns('/foo'))..createSync(); fs.link(ns('/bar')).createSync(ns('/foo')); f.writeAsStringSync(testString); expect(f.readAsLinesSync(), expectedLines); }); test('returnsEmptyListForZeroByteFile', () { File f = fs.file(ns('/foo'))..createSync(); expect(f.readAsLinesSync(), isEmpty); }); test('isTrailingNewlineAgnostic', () { File f = fs.file(ns('/foo'))..createSync(); f.writeAsStringSync('$testString\n'); expect(f.readAsLinesSync(), expectedLines); f.writeAsStringSync('\n'); expect(f.readAsLinesSync(), ['']); f.writeAsStringSync('\n\n'); expect(f.readAsLinesSync(), ['', '']); }); }); group('writeAsBytes', () { test('returnsCovariantType', () async { expect(await fs.file(ns('/foo')).writeAsBytes([]), isFile); }); test('createsFileIfDoesntExist', () { File f = fs.file(ns('/foo')); expect(f, isNot(exists)); f.writeAsBytesSync([1, 2, 3, 4]); expect(f, exists); }); test('throwsIfExistsAsDirectory', () { fs.directory(ns('/foo')).createSync(); expectFileSystemException(ErrorCodes.EISDIR, () { fs.file(ns('/foo')).writeAsBytesSync([1, 2, 3, 4]); }); }); test('throwsIfExistsAsLinkToDirectory', () { fs.directory(ns('/foo')).createSync(); fs.link(ns('/bar')).createSync(ns('/foo')); expectFileSystemException(ErrorCodes.EISDIR, () { fs.file(ns('/foo')).writeAsBytesSync([1, 2, 3, 4]); }); }); test('succeedsIfExistsAsLinkToFile', () { File f = fs.file(ns('/foo'))..createSync(); fs.link(ns('/bar')).createSync(ns('/foo')); fs.file(ns('/bar')).writeAsBytesSync([1, 2, 3, 4]); expect(f.readAsBytesSync(), [1, 2, 3, 4]); }); test('throwsIfFileModeRead', () { File f = fs.file(ns('/foo'))..createSync(); expectFileSystemException(ErrorCodes.EBADF, () { f.writeAsBytesSync([1], mode: FileMode.read); }); }); test('overwritesContentIfFileModeWrite', () { File f = fs.file(ns('/foo'))..createSync(); f.writeAsBytesSync([1, 2]); expect(f.readAsBytesSync(), [1, 2]); f.writeAsBytesSync([3, 4]); expect(f.readAsBytesSync(), [3, 4]); }); test('appendsContentIfFileModeAppend', () { File f = fs.file(ns('/foo'))..createSync(); f.writeAsBytesSync([1, 2], mode: FileMode.append); expect(f.readAsBytesSync(), [1, 2]); f.writeAsBytesSync([3, 4], mode: FileMode.append); expect(f.readAsBytesSync(), [1, 2, 3, 4]); }); test('acceptsEmptyBytesList', () { File f = fs.file(ns('/foo'))..createSync(); f.writeAsBytesSync([]); expect(f.readAsBytesSync(), []); }); test('updatesLastModifiedTime', () async { File f = fs.file(ns('/foo'))..createSync(); DateTime before = f.statSync().modified; await Future.delayed(const Duration(seconds: 2)); f.writeAsBytesSync([1, 2, 3]); DateTime after = f.statSync().modified; expect(after, isAfter(before)); }); }); group('writeAsString', () { test('returnsCovariantType', () async { expect(await fs.file(ns('/foo')).writeAsString('foo'), isFile); }); test('createsFileIfDoesntExist', () { File f = fs.file(ns('/foo')); expect(f, isNot(exists)); f.writeAsStringSync('Hello world'); expect(f, exists); }); test('throwsIfExistsAsDirectory', () { fs.directory(ns('/foo')).createSync(); expectFileSystemException(ErrorCodes.EISDIR, () { fs.file(ns('/foo')).writeAsStringSync('Hello world'); }); }); test('throwsIfExistsAsLinkToDirectory', () { fs.directory(ns('/foo')).createSync(); fs.link(ns('/bar')).createSync(ns('/foo')); expectFileSystemException(ErrorCodes.EISDIR, () { fs.file(ns('/foo')).writeAsStringSync('Hello world'); }); }); test('succeedsIfExistsAsLinkToFile', () { File f = fs.file(ns('/foo'))..createSync(); fs.link(ns('/bar')).createSync(ns('/foo')); fs.file(ns('/bar')).writeAsStringSync('Hello world'); expect(f.readAsStringSync(), 'Hello world'); }); test('throwsIfFileModeRead', () { File f = fs.file(ns('/foo'))..createSync(); expectFileSystemException(ErrorCodes.EBADF, () { f.writeAsStringSync('Hello world', mode: FileMode.read); }); }); test('overwritesContentIfFileModeWrite', () { File f = fs.file(ns('/foo'))..createSync(); f.writeAsStringSync('Hello world'); expect(f.readAsStringSync(), 'Hello world'); f.writeAsStringSync('Goodbye cruel world'); expect(f.readAsStringSync(), 'Goodbye cruel world'); }); test('appendsContentIfFileModeAppend', () { File f = fs.file(ns('/foo'))..createSync(); f.writeAsStringSync('Hello', mode: FileMode.append); expect(f.readAsStringSync(), 'Hello'); f.writeAsStringSync('Goodbye', mode: FileMode.append); expect(f.readAsStringSync(), 'HelloGoodbye'); }); test('acceptsEmptyString', () { File f = fs.file(ns('/foo'))..createSync(); f.writeAsStringSync(''); expect(f.readAsStringSync(), isEmpty); }); }); group('exists', () { test('trueIfExists', () { fs.file(ns('/foo')).createSync(); expect(fs.file(ns('/foo')), exists); }); test('falseIfDoesntExistAtTail', () { expect(fs.file(ns('/foo')), isNot(exists)); }); test('falseIfDoesntExistViaTraversal', () { expect(fs.file(ns('/foo/bar')), isNot(exists)); }); test('falseIfExistsAsDirectory', () { fs.directory(ns('/foo')).createSync(); expect(fs.file(ns('/foo')), isNot(exists)); }); test('falseIfExistsAsLinkToDirectory', () { fs.directory(ns('/foo')).createSync(); fs.link(ns('/bar')).createSync(ns('/foo')); expect(fs.file(ns('/bar')), isNot(exists)); }); test('trueIfExistsAsLinkToFile', () { fs.file(ns('/foo')).createSync(); fs.link(ns('/bar')).createSync(ns('/foo')); expect(fs.file(ns('/bar')), exists); }); test('falseIfNotFoundSegmentExistsThenIsBackedOut', () { fs.file(ns('/foo/bar')).createSync(recursive: true); expect(fs.directory(ns('/baz/../foo/bar')), isNot(exists)); }); }); group('stat', () { test('isNotFoundIfDoesntExistAtTail', () { FileStat stat = fs.file(ns('/foo')).statSync(); expect(stat.type, FileSystemEntityType.notFound); }); test('isNotFoundIfDoesntExistViaTraversal', () { FileStat stat = fs.file(ns('/foo/bar')).statSync(); expect(stat.type, FileSystemEntityType.notFound); }); test('isDirectoryIfExistsAsDirectory', () { fs.directory(ns('/foo')).createSync(); FileStat stat = fs.file(ns('/foo')).statSync(); expect(stat.type, FileSystemEntityType.directory); }); test('isFileIfExistsAsFile', () { fs.file(ns('/foo')).createSync(); FileStat stat = fs.file(ns('/foo')).statSync(); expect(stat.type, FileSystemEntityType.file); }); test('isFileIfExistsAsLinkToFile', () { fs.file(ns('/foo')).createSync(); fs.link(ns('/bar')).createSync(ns('/foo')); FileStat stat = fs.file(ns('/bar')).statSync(); expect(stat.type, FileSystemEntityType.file); }); }); group('delete', () { test('returnsCovariantType', () async { File f = fs.file(ns('/foo'))..createSync(); expect(await f.delete(), isFile); }); test('succeedsIfExistsAsFile', () { fs.file(ns('/foo')).createSync(); expect(fs.file(ns('/foo')), exists); fs.file(ns('/foo')).deleteSync(); expect(fs.file(ns('/foo')), isNot(exists)); }); test('throwsIfDoesntExistAndRecursiveFalse', () { expectFileSystemException(ErrorCodes.ENOENT, () { fs.file(ns('/foo')).deleteSync(); }); }); test('throwsIfDoesntExistAndRecursiveTrue', () { expectFileSystemException(ErrorCodes.ENOENT, () { fs.file(ns('/foo')).deleteSync(recursive: true); }); }); test('succeedsIfExistsAsDirectoryAndRecursiveTrue', () { fs.directory(ns('/foo')).createSync(); fs.file(ns('/foo')).deleteSync(recursive: true); expect(fs.typeSync(ns('/foo')), FileSystemEntityType.notFound); }); test('throwsIfExistsAsDirectoryAndRecursiveFalse', () { fs.directory(ns('/foo')).createSync(); expectFileSystemException(ErrorCodes.EISDIR, () { fs.file(ns('/foo')).deleteSync(); }); }); test('succeedsIfExistsAsLinkToFileAndRecursiveTrue', () { fs.file(ns('/foo')).createSync(); fs.link(ns('/bar')).createSync(ns('/foo')); expect(fs.file(ns('/bar')), exists); fs.file(ns('/bar')).deleteSync(recursive: true); expect(fs.file(ns('/bar')), isNot(exists)); }); test('succeedsIfExistsAsLinkToFileAndRecursiveFalse', () { fs.file(ns('/foo')).createSync(); fs.link(ns('/bar')).createSync(ns('/foo')); expect(fs.file(ns('/bar')), exists); fs.file(ns('/bar')).deleteSync(); expect(fs.file(ns('/bar')), isNot(exists)); }); test('succeedsIfExistsAsLinkToDirectoryAndRecursiveTrue', () { fs.directory(ns('/foo')).createSync(); fs.link(ns('/bar')).createSync(ns('/foo')); expect(fs.typeSync(ns('/bar')), FileSystemEntityType.directory); fs.file(ns('/bar')).deleteSync(recursive: true); expect(fs.typeSync(ns('/foo')), FileSystemEntityType.directory); expect(fs.typeSync(ns('/bar')), FileSystemEntityType.notFound); }); test('throwsIfExistsAsLinkToDirectoryAndRecursiveFalse', () { fs.directory(ns('/foo')).createSync(); fs.link(ns('/bar')).createSync(ns('/foo')); expectFileSystemException(ErrorCodes.EISDIR, () { fs.file(ns('/bar')).deleteSync(); }); }); }); }); group('Link', () { group('uri', () { test('whenTargetIsDirectory', () { fs.directory(ns('/foo')).createSync(); Link l = fs.link(ns('/bar'))..createSync(ns('/foo')); expect(l.uri, fs.path.toUri(ns('/bar'))); expect(fs.link('bar').uri.toString(), 'bar'); }); test('whenTargetIsFile', () { fs.file(ns('/foo')).createSync(); Link l = fs.link(ns('/bar'))..createSync(ns('/foo')); expect(l.uri, fs.path.toUri(ns('/bar'))); expect(fs.link('bar').uri.toString(), 'bar'); }); test('whenLinkDoesntExist', () { expect(fs.link(ns('/foo')).uri, fs.path.toUri(ns('/foo'))); expect(fs.link('foo').uri.toString(), 'foo'); }); }); group('exists', () { test('isFalseIfLinkDoesntExistAtTail', () { expect(fs.link(ns('/foo')), isNot(exists)); }); test('isFalseIfLinkDoesntExistViaTraversal', () { expect(fs.link(ns('/foo/bar')), isNot(exists)); }); test('isFalseIfPathReferencesFile', () { fs.file(ns('/foo')).createSync(); expect(fs.link(ns('/foo')), isNot(exists)); }); test('isFalseIfPathReferencesDirectory', () { fs.directory(ns('/foo')).createSync(); expect(fs.link(ns('/foo')), isNot(exists)); }); test('isTrueIfTargetIsNotFound', () { Link l = fs.link(ns('/foo'))..createSync(ns('/bar')); expect(l, exists); }); test('isTrueIfTargetIsFile', () { Link l = fs.link(ns('/foo'))..createSync(ns('/bar')); fs.file(ns('/bar')).createSync(); expect(l, exists); }); test('isTrueIfTargetIsDirectory', () { Link l = fs.link(ns('/foo'))..createSync(ns('/bar')); fs.directory(ns('/bar')).createSync(); expect(l, exists); }); test('isTrueIfTargetIsLinkLoop', () { Link l = fs.link(ns('/foo'))..createSync(ns('/bar')); fs.link(ns('/bar')).createSync(ns('/foo')); expect(l, exists); }); }); group('stat', () { test('isNotFoundIfLinkDoesntExistAtTail', () { expect(fs.link(ns('/foo')).statSync().type, FileSystemEntityType.notFound); }); test('isNotFoundIfLinkDoesntExistViaTraversal', () { expect(fs.link(ns('/foo/bar')).statSync().type, FileSystemEntityType.notFound); }); test('isFileIfPathReferencesFile', () { fs.file(ns('/foo')).createSync(); expect( fs.link(ns('/foo')).statSync().type, FileSystemEntityType.file); }); test('isDirectoryIfPathReferencesDirectory', () { fs.directory(ns('/foo')).createSync(); expect(fs.link(ns('/foo')).statSync().type, FileSystemEntityType.directory); }); test('isNotFoundIfTargetNotFoundAtTail', () { Link l = fs.link(ns('/foo'))..createSync(ns('/bar')); expect(l.statSync().type, FileSystemEntityType.notFound); }); test('isNotFoundIfTargetNotFoundViaTraversal', () { Link l = fs.link(ns('/foo'))..createSync(ns('/bar/baz')); expect(l.statSync().type, FileSystemEntityType.notFound); }); test('isNotFoundIfTargetIsLinkLoop', () { Link l = fs.link(ns('/foo'))..createSync(ns('/bar')); fs.link(ns('/bar')).createSync(ns('/foo')); expect(l.statSync().type, FileSystemEntityType.notFound); }); test('isFileIfTargetIsFile', () { Link l = fs.link(ns('/foo'))..createSync(ns('/bar')); fs.file(ns('/bar')).createSync(); expect(l.statSync().type, FileSystemEntityType.file); }); test('isDirectoryIfTargetIsDirectory', () { Link l = fs.link(ns('/foo'))..createSync(ns('/bar')); fs.directory(ns('/bar')).createSync(); expect(l.statSync().type, FileSystemEntityType.directory); }); }); group('delete', () { test('returnsCovariantType', () async { Link link = fs.link(ns('/foo'))..createSync(ns('/bar')); expect(await link.delete(), isLink); }); test('throwsIfLinkDoesntExistAtTail', () { expectFileSystemException(ErrorCodes.ENOENT, () { fs.link(ns('/foo')).deleteSync(); }); }); test('throwsIfLinkDoesntExistViaTraversal', () { expectFileSystemException(ErrorCodes.ENOENT, () { fs.link(ns('/foo/bar')).deleteSync(); }); }); test('throwsIfPathReferencesFileAndRecursiveFalse', () { fs.file(ns('/foo')).createSync(); expectFileSystemException(ErrorCodes.EINVAL, () { fs.link(ns('/foo')).deleteSync(); }); }); test('succeedsIfPathReferencesFileAndRecursiveTrue', () { fs.file(ns('/foo')).createSync(); fs.link(ns('/foo')).deleteSync(recursive: true); expect(fs.typeSync(ns('/foo'), followLinks: false), FileSystemEntityType.notFound); }); test('throwsIfPathReferencesDirectoryAndRecursiveFalse', () { fs.directory(ns('/foo')).createSync(); // TODO(tvolkert): Change this to just be 'Is a directory' // once Dart 1.22 is stable. expectFileSystemException( anyOf(ErrorCodes.EINVAL, ErrorCodes.EISDIR), () { fs.link(ns('/foo')).deleteSync(); }, ); }); test('succeedsIfPathReferencesDirectoryAndRecursiveTrue', () { fs.directory(ns('/foo')).createSync(); fs.link(ns('/foo')).deleteSync(recursive: true); expect(fs.typeSync(ns('/foo'), followLinks: false), FileSystemEntityType.notFound); }); test('unlinksIfTargetIsFileAndRecursiveFalse', () { Link l = fs.link(ns('/foo'))..createSync(ns('/bar')); fs.file(ns('/bar')).createSync(); l.deleteSync(); expect(fs.typeSync(ns('/foo'), followLinks: false), FileSystemEntityType.notFound); expect(fs.typeSync(ns('/bar'), followLinks: false), FileSystemEntityType.file); }); test('unlinksIfTargetIsFileAndRecursiveTrue', () { Link l = fs.link(ns('/foo'))..createSync(ns('/bar')); fs.file(ns('/bar')).createSync(); l.deleteSync(recursive: true); expect(fs.typeSync(ns('/foo'), followLinks: false), FileSystemEntityType.notFound); expect(fs.typeSync(ns('/bar'), followLinks: false), FileSystemEntityType.file); }); test('unlinksIfTargetIsDirectoryAndRecursiveFalse', () { Link l = fs.link(ns('/foo'))..createSync(ns('/bar')); fs.directory(ns('/bar')).createSync(); l.deleteSync(); expect(fs.typeSync(ns('/foo'), followLinks: false), FileSystemEntityType.notFound); expect(fs.typeSync(ns('/bar'), followLinks: false), FileSystemEntityType.directory); }); test('unlinksIfTargetIsDirectoryAndRecursiveTrue', () { Link l = fs.link(ns('/foo'))..createSync(ns('/bar')); fs.directory(ns('/bar')).createSync(); l.deleteSync(recursive: true); expect(fs.typeSync(ns('/foo'), followLinks: false), FileSystemEntityType.notFound); expect(fs.typeSync(ns('/bar'), followLinks: false), FileSystemEntityType.directory); }); test('unlinksIfTargetIsLinkLoop', () { Link l = fs.link(ns('/foo'))..createSync(ns('/bar')); fs.link(ns('/bar')).createSync(ns('/foo')); l.deleteSync(); expect(fs.typeSync(ns('/foo'), followLinks: false), FileSystemEntityType.notFound); expect(fs.typeSync(ns('/bar'), followLinks: false), FileSystemEntityType.link); }); }); group('parent', () { test('returnsCovariantType', () { expect(fs.link(ns('/foo')).parent, isDirectory); }); test('succeedsIfLinkDoesntExist', () { expect(fs.link(ns('/foo')).parent.path, ns('/')); }); test('ignoresLinkTarget', () { Link l = fs.link(ns('/foo/bar')) ..createSync(ns('/baz/qux'), recursive: true); expect(l.parent.path, ns('/foo')); }); }); group('create', () { test('returnsCovariantType', () async { expect(await fs.link(ns('/foo')).create(ns('/bar')), isLink); }); test('succeedsIfLinkDoesntExistAtTail', () { Link l = fs.link(ns('/foo'))..createSync(ns('/bar')); expect(fs.typeSync(ns('/foo'), followLinks: false), FileSystemEntityType.link); expect(l.targetSync(), ns('/bar')); }); test('throwsIfLinkDoesntExistViaTraversalAndRecursiveFalse', () { expectFileSystemException(ErrorCodes.ENOENT, () { fs.link(ns('/foo/bar')).createSync('baz'); }); }); test('succeedsIfLinkDoesntExistViaTraversalAndRecursiveTrue', () { Link l = fs.link(ns('/foo/bar'))..createSync('baz', recursive: true); expect(fs.typeSync(ns('/foo'), followLinks: false), FileSystemEntityType.directory); expect(fs.typeSync(ns('/foo/bar'), followLinks: false), FileSystemEntityType.link); expect(l.targetSync(), 'baz'); }); test('throwsIfAlreadyExistsAsFile', () { fs.file(ns('/foo')).createSync(); expectFileSystemException(ErrorCodes.EEXIST, () { fs.link(ns('/foo')).createSync(ns('/bar')); }); }); test('throwsIfAlreadyExistsAsDirectory', () { fs.directory(ns('/foo')).createSync(); expectFileSystemException(ErrorCodes.EEXIST, () { fs.link(ns('/foo')).createSync(ns('/bar')); }); }); test('throwsIfAlreadyExistsWithSameTarget', () { fs.link(ns('/foo')).createSync(ns('/bar')); expectFileSystemException(ErrorCodes.EEXIST, () { fs.link(ns('/foo')).createSync(ns('/bar')); }); }); test('throwsIfAlreadyExistsWithDifferentTarget', () { fs.link(ns('/foo')).createSync(ns('/bar')); expectFileSystemException(ErrorCodes.EEXIST, () { fs.link(ns('/foo')).createSync(ns('/baz')); }); }); }); group('update', () { test('returnsCovariantType', () async { Link l = fs.link(ns('/foo'))..createSync(ns('/bar')); expect(await l.update(ns('/baz')), isLink); }); test('throwsIfLinkDoesntExistAtTail', () { expectFileSystemException(ErrorCodes.ENOENT, () { fs.link(ns('/foo')).updateSync(ns('/bar')); }); }); test('throwsIfLinkDoesntExistViaTraversal', () { expectFileSystemException(ErrorCodes.ENOENT, () { fs.link(ns('/foo/bar')).updateSync(ns('/baz')); }); }); test('throwsIfPathReferencesFile', () { fs.file(ns('/foo')).createSync(); expectFileSystemException(ErrorCodes.EINVAL, () { fs.link(ns('/foo')).updateSync(ns('/bar')); }); }); test('throwsIfPathReferencesDirectory', () { fs.directory(ns('/foo')).createSync(); // TODO(tvolkert): Change this to just be 'Is a directory' // once Dart 1.22 is stable. expectFileSystemException( anyOf(ErrorCodes.EINVAL, ErrorCodes.EISDIR), () { fs.link(ns('/foo')).updateSync(ns('/bar')); }, ); }); test('succeedsIfNewTargetSameAsOldTarget', () { fs.link(ns('/foo')).createSync(ns('/bar')); fs.link(ns('/foo')).updateSync(ns('/bar')); expect(fs.typeSync(ns('/foo'), followLinks: false), FileSystemEntityType.link); expect(fs.link(ns('/foo')).targetSync(), ns('/bar')); }); test('succeedsIfNewTargetDifferentFromOldTarget', () { fs.link(ns('/foo')).createSync(ns('/bar')); fs.link(ns('/foo')).updateSync(ns('/baz')); expect(fs.typeSync(ns('/foo'), followLinks: false), FileSystemEntityType.link); expect(fs.link(ns('/foo')).targetSync(), ns('/baz')); }); }); group('absolute', () { test('returnsCovariantType', () { expect(fs.link('foo').absolute, isLink); }); test('returnsSamePathIfAlreadyAbsolute', () { expect(fs.link(ns('/foo')).absolute.path, ns('/foo')); }); test('succeedsForRelativePaths', () { expect(fs.link('foo').absolute.path, ns('/foo')); }); }); group('target', () { test('throwsIfLinkDoesntExistAtTail', () { expectFileSystemException(ErrorCodes.ENOENT, () { fs.link(ns('/foo')).targetSync(); }); }); test('throwsIfLinkDoesntExistViaTraversal', () { expectFileSystemException(ErrorCodes.ENOENT, () { fs.link(ns('/foo/bar')).targetSync(); }); }); test('throwsIfPathReferencesFile', () { fs.file(ns('/foo')).createSync(); expectFileSystemException(ErrorCodes.ENOENT, () { fs.link(ns('/foo')).targetSync(); }); }); test('throwsIfPathReferencesDirectory', () { fs.directory(ns('/foo')).createSync(); expectFileSystemException(ErrorCodes.ENOENT, () { fs.link(ns('/foo')).targetSync(); }); }); test('succeedsIfTargetIsNotFound', () { Link l = fs.link(ns('/foo'))..createSync(ns('/bar')); expect(l.targetSync(), ns('/bar')); }); test('succeedsIfTargetIsFile', () { Link l = fs.link(ns('/foo'))..createSync(ns('/bar')); fs.file(ns('/bar')).createSync(); expect(l.targetSync(), ns('/bar')); }); test('succeedsIfTargetIsDirectory', () { Link l = fs.link(ns('/foo'))..createSync(ns('/bar')); fs.directory(ns('/bar')).createSync(); expect(l.targetSync(), ns('/bar')); }); test('succeedsIfTargetIsLinkLoop', () { Link l = fs.link(ns('/foo'))..createSync(ns('/bar')); fs.link(ns('/bar')).createSync(ns('/foo')); expect(l.targetSync(), ns('/bar')); }); }); group('rename', () { test('returnsCovariantType', () async { Link l() => fs.link(ns('/foo'))..createSync(ns('/bar')); expect(l().renameSync(ns('/bar')), isLink); expect(await l().rename(ns('/bar')), isLink); }); test('throwsIfSourceDoesntExistAtTail', () { expectFileSystemException(ErrorCodes.ENOENT, () { fs.link(ns('/foo')).renameSync(ns('/bar')); }); }); test('throwsIfSourceDoesntExistViaTraversal', () { expectFileSystemException(ErrorCodes.ENOENT, () { fs.link(ns('/foo/bar')).renameSync(ns('/bar')); }); }); test('throwsIfSourceIsFile', () { fs.file(ns('/foo')).createSync(); expectFileSystemException(ErrorCodes.EINVAL, () { fs.link(ns('/foo')).renameSync(ns('/bar')); }); }); test('throwsIfSourceIsDirectory', () { fs.directory(ns('/foo')).createSync(); expectFileSystemException(ErrorCodes.EISDIR, () { fs.link(ns('/foo')).renameSync(ns('/bar')); }); }); test('succeedsIfSourceIsLinkToFile', () { Link l = fs.link(ns('/foo'))..createSync(ns('/bar')); fs.file(ns('/bar')).createSync(); Link renamed = l.renameSync(ns('/baz')); expect(renamed.path, ns('/baz')); expect(fs.typeSync(ns('/foo'), followLinks: false), FileSystemEntityType.notFound); expect(fs.typeSync(ns('/bar'), followLinks: false), FileSystemEntityType.file); expect(fs.typeSync(ns('/baz'), followLinks: false), FileSystemEntityType.link); expect(fs.link(ns('/baz')).targetSync(), ns('/bar')); }); test('succeedsIfSourceIsLinkToNotFound', () { Link l = fs.link(ns('/foo'))..createSync(ns('/bar')); Link renamed = l.renameSync(ns('/baz')); expect(renamed.path, ns('/baz')); expect(fs.typeSync(ns('/foo'), followLinks: false), FileSystemEntityType.notFound); expect(fs.typeSync(ns('/baz'), followLinks: false), FileSystemEntityType.link); expect(fs.link(ns('/baz')).targetSync(), ns('/bar')); }); test('succeedsIfSourceIsLinkToDirectory', () { Link l = fs.link(ns('/foo'))..createSync(ns('/bar')); fs.directory(ns('/bar')).createSync(); Link renamed = l.renameSync(ns('/baz')); expect(renamed.path, ns('/baz')); expect(fs.typeSync(ns('/foo'), followLinks: false), FileSystemEntityType.notFound); expect(fs.typeSync(ns('/bar'), followLinks: false), FileSystemEntityType.directory); expect(fs.typeSync(ns('/baz'), followLinks: false), FileSystemEntityType.link); expect(fs.link(ns('/baz')).targetSync(), ns('/bar')); }); test('succeedsIfSourceIsLinkLoop', () { Link l = fs.link(ns('/foo'))..createSync(ns('/bar')); fs.link(ns('/bar')).createSync(ns('/foo')); Link renamed = l.renameSync(ns('/baz')); expect(renamed.path, ns('/baz')); expect(fs.typeSync(ns('/foo'), followLinks: false), FileSystemEntityType.notFound); expect(fs.typeSync(ns('/bar'), followLinks: false), FileSystemEntityType.link); expect(fs.typeSync(ns('/baz'), followLinks: false), FileSystemEntityType.link); expect(fs.link(ns('/baz')).targetSync(), ns('/bar')); }); test('succeedsIfDestinationDoesntExistAtTail', () { Link l = fs.link(ns('/foo'))..createSync(ns('/bar')); Link renamed = l.renameSync(ns('/baz')); expect(renamed.path, ns('/baz')); expect(fs.link(ns('/foo')), isNot(exists)); expect(fs.link(ns('/baz')), exists); }); test('throwsIfDestinationDoesntExistViaTraversal', () { Link l = fs.link(ns('/foo'))..createSync(ns('/bar')); expectFileSystemException(ErrorCodes.ENOENT, () { l.renameSync(ns('/baz/qux')); }); }); test('throwsIfDestinationExistsAsFile', () { Link l = fs.link(ns('/foo'))..createSync(ns('/bar')); fs.file(ns('/baz')).createSync(); expectFileSystemException(ErrorCodes.EINVAL, () { l.renameSync(ns('/baz')); }); }); test('throwsIfDestinationExistsAsDirectory', () { Link l = fs.link(ns('/foo'))..createSync(ns('/bar')); fs.directory(ns('/baz')).createSync(); expectFileSystemException(ErrorCodes.EINVAL, () { l.renameSync(ns('/baz')); }); }); test('succeedsIfDestinationExistsAsLinkToFile', () { Link l = fs.link(ns('/foo'))..createSync(ns('/bar')); fs.file(ns('/baz')).createSync(); fs.link(ns('/qux')).createSync(ns('/baz')); l.renameSync(ns('/qux')); expect(fs.typeSync(ns('/foo')), FileSystemEntityType.notFound); expect(fs.typeSync(ns('/baz'), followLinks: false), FileSystemEntityType.file); expect(fs.typeSync(ns('/qux'), followLinks: false), FileSystemEntityType.link); expect(fs.link(ns('/qux')).targetSync(), ns('/bar')); }); test('throwsIfDestinationExistsAsLinkToDirectory', () { Link l = fs.link(ns('/foo'))..createSync(ns('/bar')); fs.directory(ns('/baz')).createSync(); fs.link(ns('/qux')).createSync(ns('/baz')); l.renameSync(ns('/qux')); expect(fs.typeSync(ns('/foo')), FileSystemEntityType.notFound); expect(fs.typeSync(ns('/baz'), followLinks: false), FileSystemEntityType.directory); expect(fs.typeSync(ns('/qux'), followLinks: false), FileSystemEntityType.link); expect(fs.link(ns('/qux')).targetSync(), ns('/bar')); }); test('succeedsIfDestinationExistsAsLinkToNotFound', () { Link l = fs.link(ns('/foo'))..createSync(ns('/bar')); fs.link(ns('/baz')).createSync(ns('/qux')); l.renameSync(ns('/baz')); expect(fs.typeSync(ns('/foo')), FileSystemEntityType.notFound); expect(fs.typeSync(ns('/baz'), followLinks: false), FileSystemEntityType.link); expect(fs.link(ns('/baz')).targetSync(), ns('/bar')); }); }); }); }); }