| // Copyright (c) 2012, 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. |
| |
| import 'dart:async'; |
| import 'dart:convert'; |
| import 'dart:io'; |
| |
| import 'package:path/path.dart' as path; |
| import 'package:pub/src/io.dart'; |
| import 'package:test/test.dart'; |
| |
| import 'descriptor.dart' as d; |
| import 'test_pub.dart'; |
| |
| void main() { |
| group('listDir', () { |
| test('ignores hidden files by default', () { |
| expect(withTempDir((temp) { |
| writeTextFile(path.join(temp, 'file1.txt'), ''); |
| writeTextFile(path.join(temp, 'file2.txt'), ''); |
| writeTextFile(path.join(temp, '.file3.txt'), ''); |
| _createDir(path.join(temp, '.subdir')); |
| writeTextFile(path.join(temp, '.subdir', 'file3.txt'), ''); |
| |
| expect( |
| listDir(temp, recursive: true), |
| unorderedEquals( |
| [path.join(temp, 'file1.txt'), path.join(temp, 'file2.txt')])); |
| }), completes); |
| }); |
| |
| test('includes hidden files when told to', () { |
| expect(withTempDir((temp) { |
| writeTextFile(path.join(temp, 'file1.txt'), ''); |
| writeTextFile(path.join(temp, 'file2.txt'), ''); |
| writeTextFile(path.join(temp, '.file3.txt'), ''); |
| _createDir(path.join(temp, '.subdir')); |
| writeTextFile(path.join(temp, '.subdir', 'file3.txt'), ''); |
| |
| expect( |
| listDir(temp, recursive: true, includeHidden: true), |
| unorderedEquals([ |
| path.join(temp, 'file1.txt'), |
| path.join(temp, 'file2.txt'), |
| path.join(temp, '.file3.txt'), |
| path.join(temp, '.subdir'), |
| path.join(temp, '.subdir', 'file3.txt') |
| ])); |
| }), completes); |
| }); |
| |
| test("doesn't ignore hidden files above the directory being listed", () { |
| expect(withTempDir((temp) { |
| var dir = path.join(temp, '.foo', 'bar'); |
| ensureDir(dir); |
| writeTextFile(path.join(dir, 'file1.txt'), ''); |
| writeTextFile(path.join(dir, 'file2.txt'), ''); |
| writeTextFile(path.join(dir, 'file3.txt'), ''); |
| |
| expect( |
| listDir(dir, recursive: true), |
| unorderedEquals([ |
| path.join(dir, 'file1.txt'), |
| path.join(dir, 'file2.txt'), |
| path.join(dir, 'file3.txt') |
| ])); |
| }), completes); |
| }); |
| }); |
| |
| group('canonicalize', () { |
| test('resolves a non-link', () { |
| expect(_withCanonicalTempDir((temp) { |
| var filePath = path.join(temp, 'file'); |
| writeTextFile(filePath, ''); |
| expect(canonicalize(filePath), equals(filePath)); |
| }), completes); |
| }); |
| |
| test('resolves a non-existent file', () { |
| expect(_withCanonicalTempDir((temp) { |
| expect(canonicalize(path.join(temp, 'nothing')), |
| equals(path.join(temp, 'nothing'))); |
| }), completes); |
| }); |
| |
| test('resolves a symlink', () { |
| expect(_withCanonicalTempDir((temp) { |
| _createDir(path.join(temp, 'linked-dir')); |
| createSymlink(path.join(temp, 'linked-dir'), path.join(temp, 'dir')); |
| expect(canonicalize(path.join(temp, 'dir')), |
| equals(path.join(temp, 'linked-dir'))); |
| }), completes); |
| }); |
| |
| test('resolves a relative symlink', () { |
| expect(_withCanonicalTempDir((temp) { |
| _createDir(path.join(temp, 'linked-dir')); |
| createSymlink(path.join(temp, 'linked-dir'), path.join(temp, 'dir'), |
| relative: true); |
| expect(canonicalize(path.join(temp, 'dir')), |
| equals(path.join(temp, 'linked-dir'))); |
| }), completes); |
| }); |
| |
| test('resolves a single-level horizontally recursive symlink', () { |
| expect(_withCanonicalTempDir((temp) { |
| var linkPath = path.join(temp, 'foo'); |
| createSymlink(linkPath, linkPath); |
| expect(canonicalize(linkPath), equals(linkPath)); |
| }), completes); |
| }); |
| |
| test('resolves a multi-level horizontally recursive symlink', () { |
| expect(_withCanonicalTempDir((temp) { |
| var fooPath = path.join(temp, 'foo'); |
| var barPath = path.join(temp, 'bar'); |
| var bazPath = path.join(temp, 'baz'); |
| createSymlink(barPath, fooPath); |
| createSymlink(bazPath, barPath); |
| createSymlink(fooPath, bazPath); |
| expect(canonicalize(fooPath), equals(fooPath)); |
| expect(canonicalize(barPath), equals(barPath)); |
| expect(canonicalize(bazPath), equals(bazPath)); |
| |
| createSymlink(fooPath, path.join(temp, 'outer')); |
| expect(canonicalize(path.join(temp, 'outer')), equals(fooPath)); |
| }), completes); |
| }); |
| |
| test('resolves a broken symlink', () { |
| expect(_withCanonicalTempDir((temp) { |
| createSymlink(path.join(temp, 'nonexistent'), path.join(temp, 'foo')); |
| expect(canonicalize(path.join(temp, 'foo')), |
| equals(path.join(temp, 'nonexistent'))); |
| }), completes); |
| }); |
| |
| test('resolves multiple nested symlinks', () { |
| expect(_withCanonicalTempDir((temp) { |
| var dir1 = path.join(temp, 'dir1'); |
| var dir2 = path.join(temp, 'dir2'); |
| var subdir1 = path.join(dir1, 'subdir1'); |
| var subdir2 = path.join(dir2, 'subdir2'); |
| _createDir(dir2); |
| _createDir(subdir2); |
| createSymlink(dir2, dir1); |
| createSymlink(subdir2, subdir1); |
| expect(canonicalize(path.join(subdir1, 'file')), |
| equals(path.join(subdir2, 'file'))); |
| }), completes); |
| }); |
| |
| test('resolves a nested vertical symlink', () { |
| expect(_withCanonicalTempDir((temp) { |
| var dir1 = path.join(temp, 'dir1'); |
| var dir2 = path.join(temp, 'dir2'); |
| var subdir = path.join(dir1, 'subdir'); |
| _createDir(dir1); |
| _createDir(dir2); |
| createSymlink(dir2, subdir); |
| expect(canonicalize(path.join(subdir, 'file')), |
| equals(path.join(dir2, 'file'))); |
| }), completes); |
| }); |
| |
| test('resolves a vertically recursive symlink', () { |
| expect(_withCanonicalTempDir((temp) { |
| var dir = path.join(temp, 'dir'); |
| var subdir = path.join(dir, 'subdir'); |
| _createDir(dir); |
| createSymlink(dir, subdir); |
| expect( |
| canonicalize(path.join( |
| temp, 'dir', 'subdir', 'subdir', 'subdir', 'subdir', 'file')), |
| equals(path.join(dir, 'file'))); |
| }), completes); |
| }); |
| |
| test('resolves a symlink that links to a path that needs more resolving', |
| () { |
| expect(_withCanonicalTempDir((temp) { |
| var dir = path.join(temp, 'dir'); |
| var linkdir = path.join(temp, 'linkdir'); |
| var linkfile = path.join(dir, 'link'); |
| _createDir(dir); |
| createSymlink(dir, linkdir); |
| createSymlink(path.join(linkdir, 'file'), linkfile); |
| expect(canonicalize(linkfile), equals(path.join(dir, 'file'))); |
| }), completes); |
| }); |
| |
| test('resolves a pair of pathologically-recursive symlinks', () { |
| expect(_withCanonicalTempDir((temp) { |
| var foo = path.join(temp, 'foo'); |
| var subfoo = path.join(foo, 'subfoo'); |
| var bar = path.join(temp, 'bar'); |
| var subbar = path.join(bar, 'subbar'); |
| createSymlink(subbar, foo); |
| createSymlink(subfoo, bar); |
| expect(canonicalize(subfoo), |
| equals(path.join(subfoo, 'subbar', 'subfoo'))); |
| }), completes); |
| }); |
| }); |
| |
| testExistencePredicate('entryExists', entryExists, |
| forFile: true, |
| forFileSymlink: true, |
| forMultiLevelFileSymlink: true, |
| forDirectory: true, |
| forDirectorySymlink: true, |
| forMultiLevelDirectorySymlink: true, |
| forBrokenSymlink: true, |
| forMultiLevelBrokenSymlink: true); |
| |
| testExistencePredicate('linkExists', linkExists, |
| forFile: false, |
| forFileSymlink: true, |
| forMultiLevelFileSymlink: true, |
| forDirectory: false, |
| forDirectorySymlink: true, |
| forMultiLevelDirectorySymlink: true, |
| forBrokenSymlink: true, |
| forMultiLevelBrokenSymlink: true); |
| |
| testExistencePredicate('fileExists', fileExists, |
| forFile: true, |
| forFileSymlink: true, |
| forMultiLevelFileSymlink: true, |
| forDirectory: false, |
| forDirectorySymlink: false, |
| forMultiLevelDirectorySymlink: false, |
| forBrokenSymlink: false, |
| forMultiLevelBrokenSymlink: false); |
| |
| testExistencePredicate('dirExists', dirExists, |
| forFile: false, |
| forFileSymlink: false, |
| forMultiLevelFileSymlink: false, |
| forDirectory: true, |
| forDirectorySymlink: true, |
| forMultiLevelDirectorySymlink: true, |
| forBrokenSymlink: false, |
| forMultiLevelBrokenSymlink: false); |
| } |
| |
| void testExistencePredicate(String name, bool Function(String path) predicate, |
| {bool forFile, |
| bool forFileSymlink, |
| bool forMultiLevelFileSymlink, |
| bool forDirectory, |
| bool forDirectorySymlink, |
| bool forMultiLevelDirectorySymlink, |
| bool forBrokenSymlink, |
| bool forMultiLevelBrokenSymlink}) { |
| group(name, () { |
| test('returns $forFile for a file', () { |
| expect(withTempDir((temp) { |
| var file = path.join(temp, 'test.txt'); |
| writeTextFile(file, 'contents'); |
| expect(predicate(file), equals(forFile)); |
| }), completes); |
| }); |
| |
| test('returns $forDirectory for a directory', () { |
| expect(withTempDir((temp) { |
| var file = path.join(temp, 'dir'); |
| _createDir(file); |
| expect(predicate(file), equals(forDirectory)); |
| }), completes); |
| }); |
| |
| test('returns $forDirectorySymlink for a symlink to a directory', () { |
| expect(withTempDir((temp) { |
| var targetPath = path.join(temp, 'dir'); |
| var symlinkPath = path.join(temp, 'linkdir'); |
| _createDir(targetPath); |
| createSymlink(targetPath, symlinkPath); |
| expect(predicate(symlinkPath), equals(forDirectorySymlink)); |
| }), completes); |
| }); |
| |
| test( |
| 'returns $forMultiLevelDirectorySymlink for a multi-level symlink to ' |
| 'a directory', () { |
| expect(withTempDir((temp) { |
| var targetPath = path.join(temp, 'dir'); |
| var symlink1Path = path.join(temp, 'link1dir'); |
| var symlink2Path = path.join(temp, 'link2dir'); |
| _createDir(targetPath); |
| createSymlink(targetPath, symlink1Path); |
| createSymlink(symlink1Path, symlink2Path); |
| expect(predicate(symlink2Path), equals(forMultiLevelDirectorySymlink)); |
| }), completes); |
| }); |
| |
| test('returns $forBrokenSymlink for a broken symlink', () { |
| expect(withTempDir((temp) { |
| var targetPath = path.join(temp, 'dir'); |
| var symlinkPath = path.join(temp, 'linkdir'); |
| _createDir(targetPath); |
| createSymlink(targetPath, symlinkPath); |
| deleteEntry(targetPath); |
| expect(predicate(symlinkPath), equals(forBrokenSymlink)); |
| }), completes); |
| }); |
| |
| test('returns $forMultiLevelBrokenSymlink for a multi-level broken symlink', |
| () { |
| expect(withTempDir((temp) { |
| var targetPath = path.join(temp, 'dir'); |
| var symlink1Path = path.join(temp, 'link1dir'); |
| var symlink2Path = path.join(temp, 'link2dir'); |
| _createDir(targetPath); |
| createSymlink(targetPath, symlink1Path); |
| createSymlink(symlink1Path, symlink2Path); |
| deleteEntry(targetPath); |
| expect(predicate(symlink2Path), equals(forMultiLevelBrokenSymlink)); |
| }), completes); |
| }); |
| |
| // Windows doesn't support symlinking to files. |
| if (!Platform.isWindows) { |
| test('returns $forFileSymlink for a symlink to a file', () { |
| expect(withTempDir((temp) { |
| var targetPath = path.join(temp, 'test.txt'); |
| var symlinkPath = path.join(temp, 'link.txt'); |
| writeTextFile(targetPath, 'contents'); |
| createSymlink(targetPath, symlinkPath); |
| expect(predicate(symlinkPath), equals(forFileSymlink)); |
| }), completes); |
| }); |
| |
| test( |
| 'returns $forMultiLevelFileSymlink for a multi-level symlink to a ' |
| 'file', () { |
| expect(withTempDir((temp) { |
| var targetPath = path.join(temp, 'test.txt'); |
| var symlink1Path = path.join(temp, 'link1.txt'); |
| var symlink2Path = path.join(temp, 'link2.txt'); |
| writeTextFile(targetPath, 'contents'); |
| createSymlink(targetPath, symlink1Path); |
| createSymlink(symlink1Path, symlink2Path); |
| expect(predicate(symlink2Path), equals(forMultiLevelFileSymlink)); |
| }), completes); |
| }); |
| } |
| }); |
| group('extractTarGz', () { |
| test('decompresses simple archive', () async { |
| await withTempDir((tempDir) async { |
| await extractTarGz( |
| Stream.fromIterable( |
| [ |
| base64Decode( |
| 'H4sIAP2weF4AA+3S0QqCMBiG4V2KeAE1nfuF7maViNBqzDyQ6N4z6yCIogOtg97ncAz2wTvfuxCW' |
| 'alZ6UFqttIiUYpXObWlzM57fqcyIkcxoU2ZKZyYvtErsvLNuuvboYpKotqm7uPUv74XYeBf7Oh66' |
| '8I1dX+LH/qFbt6HaLHrnd9O/cQ0sxZv++UP/Qob+1srQX08/5dmf9z+le+erdJWOHyE9/3oPAAAA' |
| 'AAAAAAAAAAAAgM9dALkoaRMAKAAA') |
| ], |
| ), |
| tempDir); |
| |
| await d.dir(appPath, [ |
| d.rawPubspec({ |
| 'name': 'myapp', |
| }), |
| ]).validate(tempDir); |
| }); |
| }); |
| |
| test('throws on tar error', () async { |
| await withTempDir((tempDir) async { |
| await expectLater( |
| () async => await extractTarGz( |
| Stream.fromIterable( |
| [ |
| base64Decode( |
| // Correct Gzip of a faulty tar archive. |
| 'H4sICBKyeF4AA215YXBwLnRhcgDt0sEKgjAAh/GdewrxAWpzbkJvs0pEaDVmHiR699Q6BBJ00Dr0' |
| '/Y5jsD98850LYSMWJXuFkUJaITNTmEyPR09Caaut0lIXSkils1yKxCy76KFtLi4miWjqqo0H//Ze' |
| 'iLV3saviuQ3f2PUlfkwf2l0Tyv26c/44/xtDYJsP6a0trJn2z1765/3/UMbYvr+cf8rUn/e/pifn' |
| 'y3Sbjh8hvf16DwAAAAAAAAAAAAAAAIDPre4CU/3q/CcAAA==') |
| ], |
| ), |
| tempDir), |
| throwsA(isA<FileSystemException>())); |
| }); |
| }); |
| |
| test('throws on gzip error', () async { |
| await withTempDir((tempDir) async { |
| await expectLater( |
| () async => await extractTarGz( |
| Stream.fromIterable( |
| [ |
| [10, 20, 30] // Not a good gz stream. |
| ], |
| ), |
| tempDir), |
| throwsA(isA<FileSystemException>())); |
| }); |
| }); |
| }); |
| } |
| |
| /// Like [withTempDir], but canonicalizes the path before passing it to [fn]. |
| Future<T> _withCanonicalTempDir<T>(FutureOr<T> Function(String path) fn) => |
| withTempDir((temp) => fn(canonicalize(temp))); |
| |
| /// Creates a directory [dir]. |
| String _createDir(String dir) { |
| Directory(dir).createSync(); |
| return dir; |
| } |