blob: 37ccd11be891fd3d19f9de8eb9d493e9a7cd15fb [file] [log] [blame]
// Copyright (c) 2013, 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.
// @dart = 2.9
import "dart:async";
import "dart:io";
import "package:async_helper/async_helper.dart";
import "package:expect/expect.dart";
import "package:path/path.dart";
// Test the dart:io Link class.
class FutureExpect {
static Future isTrue(Future<bool> result) =>
result.then((value) => Expect.isTrue(value));
static Future isFalse(Future<bool> result) =>
result.then((value) => Expect.isFalse(value));
static Future equals(expected, Future result) =>
result.then((value) => Expect.equals(expected, value));
static Future listEquals(expected, Future result) =>
result.then((value) => Expect.listEquals(expected, value));
static Future throws(Future result) => result.then((value) {
throw new ExpectException(
"FutureExpect.throws received $value instead of an exception");
}, onError: (_) => null);
}
Future testCreate() {
return Directory.systemTemp.createTemp('dart_link_async').then((baseDir) {
if (isRelative(baseDir.path)) {
Expect.fail(
'Link tests expect absolute paths to system temporary directories. '
'A relative path in TMPDIR gives relative paths to them.');
}
String base = baseDir.path;
String link = join(base, 'link');
String target = join(base, 'target');
return new Directory(target)
.create()
.then((_) => new Link(link).create(target))
.then((_) => FutureExpect.equals(
FileSystemEntityType.directory, FileSystemEntity.type(link)))
.then((_) => FutureExpect.equals(
FileSystemEntityType.directory, FileSystemEntity.type(target)))
.then((_) => FutureExpect.equals(FileSystemEntityType.link,
FileSystemEntity.type(link, followLinks: false)))
.then((_) => FutureExpect.equals(FileSystemEntityType.directory,
FileSystemEntity.type(target, followLinks: false)))
.then((_) => FutureExpect.isTrue(FileSystemEntity.isLink(link)))
.then((_) => FutureExpect.isFalse(FileSystemEntity.isLink(target)))
.then((_) => FutureExpect.isTrue(new Directory(link).exists()))
.then((_) => FutureExpect.isTrue(new Directory(target).exists()))
.then((_) => FutureExpect.isTrue(new Link(link).exists()))
.then((_) => FutureExpect.isFalse(new Link(target).exists()))
.then((_) => FutureExpect.equals(target, new Link(link).target()))
.then((_) => FutureExpect.throws(new Link(target).target()))
.then((_) {
String createdThroughLink = join(base, 'link', 'createdThroughLink');
String createdDirectly = join(base, 'target', 'createdDirectly');
String createdFile = join(base, 'link', 'createdFile');
return new Directory(createdThroughLink)
.create()
.then((_) => new Directory(createdDirectly).create())
.then((_) => new File(createdFile).create())
.then((_) =>
FutureExpect.isTrue(new Directory(createdThroughLink).exists()))
.then((_) =>
FutureExpect.isTrue(new Directory(createdDirectly).exists()))
.then((_) => FutureExpect.isTrue(
new Directory(join(base, 'link', 'createdDirectly')).exists()))
.then((_) => FutureExpect.isTrue(
new Directory(join(base, 'target', 'createdThroughLink'))
.exists()))
.then((_) => FutureExpect.equals(FileSystemEntityType.directory,
FileSystemEntity.type(createdThroughLink, followLinks: false)))
.then((_) => FutureExpect.equals(FileSystemEntityType.directory,
FileSystemEntity.type(createdDirectly, followLinks: false)))
// Test FileSystemEntity.identical on files, directories, and links,
// reached by different paths.
.then((_) => FutureExpect.isTrue(FileSystemEntity.identical(createdDirectly, createdDirectly)))
.then((_) => FutureExpect.isFalse(FileSystemEntity.identical(createdDirectly, createdThroughLink)))
.then((_) => FutureExpect.isTrue(FileSystemEntity.identical(createdDirectly, join(base, 'link', 'createdDirectly'))))
.then((_) => FutureExpect.isTrue(FileSystemEntity.identical(createdThroughLink, join(base, 'target', 'createdThroughLink'))))
.then((_) => FutureExpect.isFalse(FileSystemEntity.identical(target, link)))
.then((_) => FutureExpect.isTrue(FileSystemEntity.identical(link, link)))
.then((_) => FutureExpect.isTrue(FileSystemEntity.identical(target, target)))
.then((_) => new Link(link).target())
.then((linkTarget) => FutureExpect.isTrue(FileSystemEntity.identical(target, linkTarget)))
.then((_) => new File(".").resolveSymbolicLinks())
.then((fullCurrentDir) => FutureExpect.isTrue(FileSystemEntity.identical(".", fullCurrentDir)))
.then((_) => FutureExpect.isTrue(FileSystemEntity.identical(createdFile, createdFile)))
.then((_) => FutureExpect.isFalse(FileSystemEntity.identical(createdFile, createdDirectly)))
.then((_) => FutureExpect.isTrue(FileSystemEntity.identical(createdFile, join(base, 'link', 'createdFile'))))
.then((_) => FutureExpect.throws(FileSystemEntity.identical(createdFile, join(base, 'link', 'does_not_exist'))))
.then((_) => testDirectoryListing(base, baseDir))
.then((_) => new Directory(target).delete(recursive: true))
.then((_) {
var futures = <Future>[];
for (bool recursive in [true, false]) {
for (bool followLinks in [true, false]) {
var result = baseDir.listSync(
recursive: recursive, followLinks: followLinks);
Expect.equals(1, result.length);
Expect.isTrue(result[0] is Link);
futures.add(FutureExpect.isTrue(baseDir
.list(recursive: recursive, followLinks: followLinks)
.single
.then((element) => element is Link)));
}
}
return Future.wait(futures);
}).then((_) => baseDir.delete(recursive: true));
});
});
}
Future testCreateLoopingLink(_) {
return Directory.systemTemp
.createTemp('dart_link_async')
.then((dir) => dir.path)
.then((String base) => new Directory(join(base, 'a', 'b', 'c'))
.create(recursive: true)
.then((_) => new Link(join(base, 'a', 'b', 'c', 'd'))
.create(join(base, 'a', 'b')))
.then((_) =>
new Link(join(base, 'a', 'b', 'c', 'e')).create(join(base, 'a')))
.then((_) => new Directory(join(base, 'a'))
.list(recursive: true, followLinks: false)
.last)
// This directory listing must terminate, even though it contains loops.
.then((_) => new Directory(join(base, 'a'))
.list(recursive: true, followLinks: true)
.last)
// This directory listing must terminate, even though it contains loops.
.then((_) => new Directory(join(base, 'a', 'b', 'c'))
.list(recursive: true, followLinks: true)
.last)
.then((_) => new Directory(base).delete(recursive: true))
.then((_) => FutureExpect.isFalse(new Directory(base).exists())));
}
Future testRename(_) {
Future testRename(String base, String target) {
Link link1;
Link link2;
return new Link(join(base, 'c')).create(target).then((link) {
link1 = link;
Expect.isTrue(link1.existsSync());
return link1.rename(join(base, 'd'));
}).then((link) {
link2 = link;
Expect.isFalse(link1.existsSync());
Expect.isTrue(link2.existsSync());
return link2.delete();
}).then((_) => Expect.isFalse(link2.existsSync()));
}
Future testUpdate(String base, String target1, String target2) {
Link link1;
return new Link(join(base, 'c')).create(target1).then((link) {
link1 = link;
Expect.isTrue(link1.existsSync());
return link1.update(target2);
}).then((Link link) {
Expect.isTrue(link1.existsSync());
Expect.isTrue(link.existsSync());
return FutureExpect
.equals(target2, link.target())
.then((_) => FutureExpect.equals(target2, link1.target()))
.then((_) => link.delete());
}).then((_) => Expect.isFalse(link1.existsSync()));
}
return Directory.systemTemp.createTemp('dart_link_async').then((baseDir) {
String base = baseDir.path;
var targetsFutures = <Future>[];
targetsFutures.add(new Directory(join(base, 'a')).create());
if (Platform.isWindows) {
// Currently only links to directories are supported on Windows.
targetsFutures.add(new Directory(join(base, 'b')).create());
} else {
targetsFutures.add(new File(join(base, 'b')).create());
}
return Future.wait(targetsFutures).then((targets) {
return testRename(base, targets[0].path)
.then((_) => testRename(base, targets[1].path))
.then((_) => testUpdate(base, targets[0].path, targets[1].path))
.then((_) => baseDir.delete(recursive: true));
});
});
}
Future testDirectoryListing(String base, Directory baseDir) {
Map makeExpected(bool recursive, bool followLinks) {
Map expected = new Map();
expected['target'] = 'Directory';
expected['link'] = followLinks ? 'Directory' : 'Link';
if (recursive) {
expected[join('target', 'createdDirectly')] = 'Directory';
expected[join('target', 'createdThroughLink')] = 'Directory';
expected[join('target', 'createdFile')] = 'File';
if (followLinks) {
expected[join('link', 'createdDirectly')] = 'Directory';
expected[join('link', 'createdThroughLink')] = 'Directory';
expected[join('link', 'createdFile')] = 'File';
}
}
return expected;
}
void checkEntity(FileSystemEntity x, Map expected) {
String ending = relative(x.path, from: base);
Expect.isNotNull(expected[ending]);
Expect.isTrue(x.toString().startsWith(expected[ending]));
expected[ending] = 'Found';
}
var futures = <Future>[];
for (bool recursive in [true, false]) {
for (bool followLinks in [true, false]) {
Map expected = makeExpected(recursive, followLinks);
for (var x
in baseDir.listSync(recursive: recursive, followLinks: followLinks)) {
checkEntity(x, expected);
}
for (var v in expected.values) {
Expect.equals('Found', v);
}
expected = makeExpected(recursive, followLinks);
futures.add(baseDir
.list(recursive: recursive, followLinks: followLinks)
.forEach((entity) => checkEntity(entity, expected))
.then((_) {
for (var v in expected.values) {
Expect.equals('Found', v);
}
}));
}
}
return Future.wait(futures);
}
Future checkExists(String filePath) =>
new File(filePath).exists().then(Expect.isTrue);
Future testRelativeLinks(_) {
return Directory.systemTemp
.createTemp('dart_link_async')
.then((tempDirectory) {
String temp = tempDirectory.path;
String oldWorkingDirectory = Directory.current.path;
// Make directories and files to test links.
return new Directory(join(temp, 'dir1', 'dir2'))
.create(recursive: true)
.then((_) => new File(join(temp, 'dir1', 'file1')).create())
.then((_) => new File(join(temp, 'dir1', 'dir2', 'file2')).create())
// Make links whose path and/or target is given by a relative path.
.then((_) => new Link(join(temp, 'dir1', 'link1_2')).create('dir2'))
.then((_) => Directory.current = temp)
.then((_) => new Link('link0_2').create(join('dir1', 'dir2')))
.then((_) => new Link(join('dir1', 'link1_0')).create('..'))
.then((_) => Directory.current = 'dir1')
.then((_) => new Link(join('..', 'link0_1')).create('dir1'))
.then(
(_) => new Link(join('dir2', 'link2_1')).create(join(temp, 'dir1')))
.then((_) => new Link(join(temp, 'dir1', 'dir2', 'link2_0'))
.create(join('..', '..')))
// Test that the links go to the right targets.
.then((_) => checkExists(join('..', 'link0_1', 'file1')))
.then((_) => checkExists(join('..', 'link0_2', 'file2')))
.then((_) => checkExists(join('link1_0', 'dir1', 'file1')))
.then((_) => checkExists(join('link1_2', 'file2')))
.then((_) => checkExists(join('dir2', 'link2_0', 'dir1', 'file1')))
.then((_) => checkExists(join('dir2', 'link2_1', 'file1')))
// Clean up
.whenComplete(() => Directory.current = oldWorkingDirectory)
.whenComplete(() => tempDirectory.delete(recursive: true));
});
}
main() {
asyncStart();
testCreate()
.then(testCreateLoopingLink)
.then(testRename)
.then(testRelativeLinks)
.then((_) => asyncEnd());
}