Version 0.4.3.5
svn merge -c 20575 https://dart.googlecode.com/svn/branches/bleeding_edge/dart dart
svn merge -c 20576 https://dart.googlecode.com/svn/branches/bleeding_edge/dart dart
svn merge -c 20577 https://dart.googlecode.com/svn/branches/bleeding_edge/dart dart
Review URL: https://codereview.chromium.org//13132002
git-svn-id: http://dart.googlecode.com/svn/trunk@20602 260f80e4-7a28-3924-810f-c04153c831b5
diff --git a/runtime/vm/class_finalizer.cc b/runtime/vm/class_finalizer.cc
index 400f1fa..d4173cc 100644
--- a/runtime/vm/class_finalizer.cc
+++ b/runtime/vm/class_finalizer.cc
@@ -627,7 +627,8 @@
const intptr_t offset = cls.NumTypeArguments() - num_type_params;
AbstractType& type_arg = AbstractType::Handle();
AbstractType& cls_type_param = AbstractType::Handle();
- AbstractType& bound = AbstractType::Handle();
+ AbstractType& declared_bound = AbstractType::Handle();
+ AbstractType& instantiated_bound = AbstractType::Handle();
const TypeArguments& cls_type_params =
TypeArguments::Handle(cls.type_parameters());
ASSERT((cls_type_params.IsNull() && (num_type_params == 0)) ||
@@ -640,33 +641,41 @@
cls_type_param = cls_type_params.TypeAt(i);
const TypeParameter& type_param = TypeParameter::Cast(cls_type_param);
ASSERT(type_param.IsFinalized());
- bound = type_param.bound();
- if (!bound.IsObjectType() && !bound.IsDynamicType()) {
+ declared_bound = type_param.bound();
+ if (!declared_bound.IsObjectType() && !declared_bound.IsDynamicType()) {
Error& malformed_error = Error::Handle();
// Note that the bound may be malformed, in which case the bound check
// will return an error and the bound check will be postponed to run time.
// Note also that the bound may still be unfinalized.
- if (!bound.IsFinalized()) {
- ASSERT(bound.IsBeingFinalized());
+ if (!declared_bound.IsFinalized()) {
+ ASSERT(declared_bound.IsBeingFinalized());
// The bound refers to type parameters, creating a cycle; postpone
// bound check to run time, when the bound will be finalized.
// TODO(regis): Do we need to instantiate an uninstantiated bound here?
- type_arg = BoundedType::New(type_arg, bound, type_param);
+ type_arg = BoundedType::New(type_arg, declared_bound, type_param);
arguments.SetTypeAt(offset + i, type_arg);
continue;
}
- if (!bound.IsInstantiated()) {
- bound = bound.InstantiateFrom(arguments, &malformed_error);
+ if (declared_bound.IsInstantiated()) {
+ instantiated_bound = declared_bound.raw();
+ } else {
+ instantiated_bound =
+ declared_bound.InstantiateFrom(arguments, &malformed_error);
}
// TODO(regis): We could simplify this code if we could differentiate
// between a failed bound check and a bound check that is undecidable at
// compile time.
- if (malformed_error.IsNull()) {
- type_param.CheckBound(type_arg, bound, &malformed_error);
+ // Shortcut the special case where we check a type parameter against its
+ // declared upper bound.
+ if (malformed_error.IsNull() &&
+ (!type_arg.Equals(type_param) ||
+ !instantiated_bound.Equals(declared_bound))) {
+ type_param.CheckBound(type_arg, instantiated_bound, &malformed_error);
}
if (!malformed_error.IsNull()) {
- if (!type_arg.IsInstantiated() || !bound.IsInstantiated()) {
- type_arg = BoundedType::New(type_arg, bound, type_param);
+ if (!type_arg.IsInstantiated() ||
+ !instantiated_bound.IsInstantiated()) {
+ type_arg = BoundedType::New(type_arg, instantiated_bound, type_param);
arguments.SetTypeAt(offset + i, type_arg);
} else if (bound_error->IsNull()) {
*bound_error = malformed_error.raw();
@@ -1021,7 +1030,8 @@
for (intptr_t i = 0; i < num_type_params; i++) {
type_param ^= type_params.TypeAt(i);
bound = type_param.bound();
- if (bound.IsFinalized()) {
+ if (bound.IsFinalized() || bound.IsBeingFinalized()) {
+ // A bound involved in F-bounded quantification may form a cycle.
continue;
}
ResolveType(cls, bound, kCanonicalize);
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index 121788b..3c1643c 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -9179,7 +9179,13 @@
num_type_params = 0;
}
} else {
- first_type_param_index = num_args - num_type_params;
+ // The actual type argument vector can be longer than necessary, because
+ // of type optimizations.
+ if (IsFinalized() && cls.is_finalized()) {
+ first_type_param_index = cls.NumTypeArguments() - num_type_params;
+ } else {
+ first_type_param_index = num_args - num_type_params;
+ }
}
if (cls.IsSignatureClass()) {
// We may be reporting an error about a malformed function type. In that
@@ -9192,8 +9198,10 @@
if (cls.IsCanonicalSignatureClass()) {
const Function& signature_function = Function::Handle(
cls.signature_function());
- // Signature classes have no super type.
- ASSERT(first_type_param_index == 0);
+ // Signature classes have no super type, however, they take as many
+ // type arguments as the owner class of their signature function (if it
+ // is non static and generic, see Class::NumTypeArguments()). Therefore,
+ // first_type_param_index may be greater than 0 here.
return signature_function.InstantiatedSignatureFrom(args,
name_visibility);
}
@@ -9548,8 +9556,9 @@
AbstractTypeArguments::Handle(arguments());
type_arguments = type_arguments.InstantiateFrom(instantiator_type_arguments,
malformed_error);
+ // Note that the type class has to be resolved at this time, but not
+ // necessarily finalized yet. We may be checking bounds at compile time.
const Class& cls = Class::Handle(type_class());
- ASSERT(cls.is_finalized());
// This uninstantiated type is not modified, as it can be instantiated
// with different instantiators.
Type& instantiated_type = Type::Handle(
@@ -9734,7 +9743,6 @@
bool TypeParameter::Equals(const Instance& other) const {
- ASSERT(IsFinalized());
if (raw() == other.raw()) {
return true;
}
@@ -9746,6 +9754,9 @@
if (parameterized_class() != other_type_param.parameterized_class()) {
return false;
}
+ if (IsFinalized() != other_type_param.IsFinalized()) {
+ return false;
+ }
if (index() != other_type_param.index()) {
return false;
}
diff --git a/tests/language/f_bounded_quantification2_test.dart b/tests/language/f_bounded_quantification2_test.dart
new file mode 100644
index 0000000..64db786
--- /dev/null
+++ b/tests/language/f_bounded_quantification2_test.dart
@@ -0,0 +1,20 @@
+// 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.
+
+// Test for F-Bounded Quantification. Regression test for issue 9291.
+
+class Entities<T extends ConceptEntity<T>> implements EntitiesApi<T> { }
+
+class ConceptEntity<T extends ConceptEntity<T>> implements EntityApi { }
+
+abstract class EntityApi<T extends EntityApi<T>> { }
+
+abstract class EntitiesApi<T extends EntityApi<T>> { }
+
+class Concept extends ConceptEntity<Concept> { }
+
+main() {
+ new ConceptEntity<Concept>();
+ new ConceptEntity<ConceptEntity>();
+}
diff --git a/tools/VERSION b/tools/VERSION
index beb3099..5c9f716 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -1,4 +1,4 @@
MAJOR 0
MINOR 4
BUILD 3
-PATCH 4
+PATCH 5
diff --git a/utils/pub/command_lish.dart b/utils/pub/command_lish.dart
index 84afdc0..2de62a8 100644
--- a/utils/pub/command_lish.dart
+++ b/utils/pub/command_lish.dart
@@ -147,7 +147,7 @@
return listDir(rootDir, recursive: true).then((entries) {
return entries
- .where(fileExists) // Skip directories.
+ .where(fileExists) // Skip directories and broken symlinks.
.map((entry) => path.relative(entry, from: rootDir));
});
}).then((files) => files.where(_shouldPublish).toList());
diff --git a/utils/pub/entrypoint.dart b/utils/pub/entrypoint.dart
index f689416..2b02f02 100644
--- a/utils/pub/entrypoint.dart
+++ b/utils/pub/entrypoint.dart
@@ -74,11 +74,11 @@
var future = defer(() {
ensureDir(path.dirname(packageDir));
- if (dirExists(packageDir)) {
+ if (entryExists(packageDir)) {
// TODO(nweiz): figure out when to actually delete the directory, and
// when we can just re-use the existing symlink.
log.fine("Deleting package directory for ${id.name} before install.");
- deleteDir(packageDir);
+ deleteEntry(packageDir);
}
if (id.source.shouldCache) {
@@ -210,7 +210,7 @@
/// exists. If it doesn't, this completes to an empty [LockFile].
LockFile loadLockFile() {
var lockFilePath = path.join(root.dir, 'pubspec.lock');
- if (!fileExists(lockFilePath)) return new LockFile.empty();
+ if (!entryExists(lockFilePath)) return new LockFile.empty();
return new LockFile.load(lockFilePath, cache.sources);
}
@@ -298,17 +298,7 @@
Future _linkSecondaryPackageDir(String dir) {
return defer(() {
var symlink = path.join(dir, 'packages');
- // The order of if tests is significant here. fileExists() will return
- // true for a symlink (broken or not) but deleteFile() cannot be used
- // to delete a broken symlink on Windows. So we test for the directory
- // first since deleteDir() does work on symlinks.
- // TODO(rnystrom): Make deleteFile() work for symlinks on Windows so this
- // doesn't matter.
- if (dirExists(symlink)) {
- deleteDir(symlink);
- } else if (fileExists(symlink)) {
- deleteFile(symlink);
- }
+ if (entryExists(symlink)) deleteEntry(symlink);
return createSymlink(packagesDir, symlink, relative: true);
});
}
diff --git a/utils/pub/io.dart b/utils/pub/io.dart
index f4bb6de..59172c8 100644
--- a/utils/pub/io.dart
+++ b/utils/pub/io.dart
@@ -28,12 +28,16 @@
}
/// Determines if a file or directory exists at [path].
-bool entryExists(String path) => dirExists(path) || fileExists(path);
+bool entryExists(String path) =>
+ dirExists(path) || fileExists(path) || linkExists(path);
-/// Determines if [file] exists on the file system. Will also return `true` if
-/// [file] points to a symlink, even a directory symlink.
-bool fileExists(String file) =>
- new File(file).existsSync() || new Link(file).existsSync();
+/// Returns whether [link] exists on the file system. This will return `true`
+/// for any symlink, regardless of what it points at or whether it's broken.
+bool linkExists(String path) => new Link(path).existsSync();
+
+/// Returns whether [file] exists on the file system. This will return `true`
+/// for a symlink only if that symlink is unbroken and points to a file.
+bool fileExists(String file) => new File(file).existsSync();
/// Reads the contents of the text file [file].
String readTextFile(String file) =>
@@ -61,11 +65,6 @@
return file;
}
-/// Deletes [file].
-void deleteFile(String file) {
- new File(file).deleteSync();
-}
-
/// Creates [file] and writes [contents] to it.
String writeBinaryFile(String file, List<int> contents) {
log.io("Writing ${contents.length} bytes to binary file $file.");
@@ -126,12 +125,6 @@
return tempDir.path;
}
-/// Recursively deletes [dir].
-void deleteDir(String dir) {
- log.io("Deleting directory $dir.");
- new Directory(dir).deleteSync(recursive: true);
-}
-
/// Asynchronously lists the contents of [dir]. If [recursive] is `true`, lists
/// subdirectory contents (defaults to `false`). If [includeHiddenFiles] is
/// `true`, includes files and directories beginning with `.` (defaults to
@@ -199,21 +192,34 @@
return doList(dir, new Set<String>());
}
-/// Determines if [dir] exists on the file system.
+/// Returns whether [dir] exists on the file system. This will return `true` for
+/// a symlink only if that symlink is unbroken and points to a directory.
bool dirExists(String dir) => new Directory(dir).existsSync();
+/// Deletes whatever's at [path], whether it's a file, directory, or symlink. If
+/// it's a directory, it will be deleted recursively.
+void deleteEntry(String path) {
+ if (linkExists(path)) {
+ log.io("Deleting link $path.");
+ if (Platform.operatingSystem == 'windows') {
+ // TODO(nweiz): remove this when issue 9278 is fixed.
+ new Directory(path).deleteSync();
+ } else {
+ new Link(path).deleteSync();
+ }
+ } else if (dirExists(path)) {
+ log.io("Deleting directory $path.");
+ new Directory(path).deleteSync(recursive: true);
+ } else {
+ log.io("Deleting file $path.");
+ new File(path).deleteSync();
+ }
+}
+
/// "Cleans" [dir]. If that directory already exists, it will be deleted. Then a
/// new empty directory will be created.
void cleanDir(String dir) {
- if (dirExists(dir)) {
- // Delete it first.
- deleteDir(dir);
- } else if (fileExists(dir)) {
- // If there is a non-directory there (file or symlink), delete it.
- deleteFile(dir);
- }
-
- // Just create it.
+ if (entryExists(dir)) deleteEntry(dir);
createDir(dir);
}
@@ -582,7 +588,7 @@
return defer(() {
var tempDir = createTempDir();
return new Future.of(() => fn(tempDir))
- .whenComplete(() => deleteDir(tempDir));
+ .whenComplete(() => deleteEntry(tempDir));
});
}
diff --git a/utils/pub/oauth2.dart b/utils/pub/oauth2.dart
index 55245f3..69364d3 100644
--- a/utils/pub/oauth2.dart
+++ b/utils/pub/oauth2.dart
@@ -66,9 +66,7 @@
void clearCredentials(SystemCache cache) {
_credentials = null;
var credentialsFile = _credentialsFile(cache);
- if (!fileExists(credentialsFile)) return;
-
- deleteFile(credentialsFile);
+ if (entryExists(credentialsFile)) deleteEntry(credentialsFile);
}
/// Asynchronously passes an OAuth2 [Client] to [fn], and closes the client when
diff --git a/utils/pub/path_source.dart b/utils/pub/path_source.dart
index 6cdc070..8b3b7a5 100644
--- a/utils/pub/path_source.dart
+++ b/utils/pub/path_source.dart
@@ -108,8 +108,6 @@
if (dirExists(dir)) return;
- // Check this after dirExists() so that symlinks to directories don't get
- // confused as files.
if (fileExists(dir)) {
throw new FormatException(
"Path dependency for package '$name' must refer to a "
diff --git a/utils/pub/pub.dart b/utils/pub/pub.dart
index 1c05c45..c2fb8f9 100644
--- a/utils/pub/pub.dart
+++ b/utils/pub/pub.dart
@@ -258,6 +258,8 @@
if (globalOptions['trace'] && trace != null) {
log.error(trace);
log.dumpTranscript();
+ } else {
+ log.fine(trace);
}
exit(_chooseExitCode(error));
diff --git a/utils/pub/source.dart b/utils/pub/source.dart
index 60d455b..1180967 100644
--- a/utils/pub/source.dart
+++ b/utils/pub/source.dart
@@ -119,7 +119,7 @@
if (!isCorrupted) return true;
// Busted, so wipe out the package and reinstall.
- deleteDir(packageDir);
+ deleteEntry(packageDir);
return false;
});
}).then((isInstalled) {
diff --git a/utils/pub/system_cache.dart b/utils/pub/system_cache.dart
index b6467b5..1b30ea8 100644
--- a/utils/pub/system_cache.dart
+++ b/utils/pub/system_cache.dart
@@ -111,7 +111,6 @@
/// Deletes the system cache's internal temp directory.
void deleteTempDir() {
log.fine('Clean up system cache temp directory $tempDir.');
- if (!dirExists(tempDir)) return;
- deleteDir(tempDir);
+ if (dirExists(tempDir)) deleteEntry(tempDir);
}
}
diff --git a/utils/tests/pub/install/git/lock_version_test.dart b/utils/tests/pub/install/git/lock_version_test.dart
index 7c3824b..ad6c92a 100644
--- a/utils/tests/pub/install/git/lock_version_test.dart
+++ b/utils/tests/pub/install/git/lock_version_test.dart
@@ -35,7 +35,7 @@
]).validate();
// Delete the packages path to simulate a new checkout of the application.
- schedule(() => deleteDir(path.join(sandboxDir, packagesPath)));
+ schedule(() => deleteEntry(path.join(sandboxDir, packagesPath)));
d.git('foo.git', [
d.libDir('foo', 'foo 2'),
diff --git a/utils/tests/pub/install/hosted/stay_locked_test.dart b/utils/tests/pub/install/hosted/stay_locked_test.dart
index dc8f72a..df7cf87 100644
--- a/utils/tests/pub/install/hosted/stay_locked_test.dart
+++ b/utils/tests/pub/install/hosted/stay_locked_test.dart
@@ -27,7 +27,7 @@
d.packagesDir({"foo": "1.0.0"}).validate();
// Delete the packages path to simulate a new checkout of the application.
- schedule(() => deleteDir(path.join(sandboxDir, packagesPath)));
+ schedule(() => deleteEntry(path.join(sandboxDir, packagesPath)));
// Start serving a newer package as well.
servePackages([packageMap("foo", "1.0.1")]);
diff --git a/utils/tests/pub/install/pub_install_test.dart b/utils/tests/pub/install/pub_install_test.dart
index bdaa25f..f927b54 100644
--- a/utils/tests/pub/install/pub_install_test.dart
+++ b/utils/tests/pub/install/pub_install_test.dart
@@ -6,8 +6,10 @@
import 'dart:io';
+import '../../../../pkg/pathos/lib/path.dart' as path;
import '../../../../pkg/scheduled_test/lib/scheduled_test.dart';
+import '../../../pub/io.dart';
import '../descriptor.dart' as d;
import '../test_pub.dart';
@@ -114,6 +116,29 @@
]).validate();
});
+ integration('overwrites a broken packages directory symlink', () {
+ d.dir(appPath, [
+ d.appPubspec([]),
+ d.dir('packages'),
+ d.libDir('myapp'),
+ d.dir('bin')
+ ]).create();
+
+ scheduleSymlink(
+ path.join(appPath, 'packages'),
+ path.join(appPath, 'bin', 'packages'));
+
+ schedule(() => deleteEntry(path.join(sandboxDir, appPath, 'packages')));
+
+ schedulePub(args: ['install'],
+ output: new RegExp(r"Dependencies installed!$"));
+
+ d.dir(packagesPath, [
+ d.nothing('foo'),
+ d.dir('myapp', [d.file('myapp.dart', 'main() => "myapp";')])
+ ]).validate();
+ });
+
group('creates a packages directory in', () {
integration('"test/" and its subdirectories', () {
d.dir(appPath, [
diff --git a/utils/tests/pub/io_test.dart b/utils/tests/pub/io_test.dart
index 526283d..2ebf55c 100644
--- a/utils/tests/pub/io_test.dart
+++ b/utils/tests/pub/io_test.dart
@@ -4,6 +4,8 @@
library io_test;
+import 'dart:io';
+
import '../../../pkg/pathos/lib/path.dart' as path;
import '../../../pkg/unittest/lib/unittest.dart';
@@ -134,4 +136,156 @@
}), 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 predicate(String path),
+ {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 path = path.join(temp, "test.txt");
+ writeTextFile(path, "contents");
+ expect(predicate(path), equals(forFile));
+ }), completes);
+ });
+
+ test('returns $forDirectory for a directory', () {
+ expect(withTempDir((temp) {
+ var path = path.join(temp, "dir");
+ createDir(path);
+ expect(predicate(path), 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);
+ return createSymlink(targetPath, symlinkPath).then((_) {
+ 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);
+ return createSymlink(targetPath, symlink1Path)
+ .then((_) => createSymlink(symlink1Path, symlink2Path))
+ .then((_) {
+ 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);
+ return createSymlink(targetPath, symlinkPath).then((_) {
+ 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);
+ return createSymlink(targetPath, symlink1Path)
+ .then((_) => createSymlink(symlink1Path, symlink2Path))
+ .then((_) {
+ deleteEntry(targetPath);
+ expect(predicate(symlink2Path), equals(forMultiLevelBrokenSymlink));
+ });
+ }), completes);
+ });
+
+ // Windows doesn't support symlinking to files.
+ if (Platform.operatingSystem != 'windows') {
+ 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");
+ return createSymlink(targetPath, symlinkPath).then((_) {
+ 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");
+ return createSymlink(targetPath, symlink1Path)
+ .then((_) => createSymlink(symlink1Path, symlink2Path))
+ .then((_) {
+ expect(predicate(symlink2Path), equals(forMultiLevelFileSymlink));
+ });
+ }), completes);
+ });
+ }
+ });
}
diff --git a/utils/tests/pub/test_pub.dart b/utils/tests/pub/test_pub.dart
index 2073f42..bf3722b 100644
--- a/utils/tests/pub/test_pub.dart
+++ b/utils/tests/pub/test_pub.dart
@@ -235,7 +235,7 @@
_sandboxDir = createTempDir();
d.defaultRoot = sandboxDir;
- currentSchedule.onComplete.schedule(() => deleteDir(_sandboxDir),
+ currentSchedule.onComplete.schedule(() => deleteEntry(_sandboxDir),
'deleting the sandbox directory');
// Schedule the test.
diff --git a/utils/tests/pub/validator_test.dart b/utils/tests/pub/validator_test.dart
index 12dbd82..9b3b583 100644
--- a/utils/tests/pub/validator_test.dart
+++ b/utils/tests/pub/validator_test.dart
@@ -121,19 +121,19 @@
});
integration('has a COPYING file', () {
- schedule(() => deleteFile(path.join(sandboxDir, appPath, 'LICENSE')));
+ schedule(() => deleteEntry(path.join(sandboxDir, appPath, 'LICENSE')));
d.file(path.join(appPath, 'COPYING'), '').create();
expectNoValidationError(license);
});
integration('has a prefixed LICENSE file', () {
- schedule(() => deleteFile(path.join(sandboxDir, appPath, 'LICENSE')));
+ schedule(() => deleteEntry(path.join(sandboxDir, appPath, 'LICENSE')));
d.file(path.join(appPath, 'MIT_LICENSE'), '').create();
expectNoValidationError(license);
});
integration('has a suffixed LICENSE file', () {
- schedule(() => deleteFile(path.join(sandboxDir, appPath, 'LICENSE')));
+ schedule(() => deleteEntry(path.join(sandboxDir, appPath, 'LICENSE')));
d.file(path.join(appPath, 'LICENSE.md'), '').create();
expectNoValidationError(license);
});
@@ -269,7 +269,7 @@
});
integration('has no LICENSE file', () {
- schedule(() => deleteFile(path.join(sandboxDir, appPath, 'LICENSE')));
+ schedule(() => deleteEntry(path.join(sandboxDir, appPath, 'LICENSE')));
expectValidationError(license);
});
@@ -332,7 +332,7 @@
integration('has a single library named differently than the package', () {
schedule(() =>
- deleteFile(path.join(sandboxDir, appPath, "lib", "test_pkg.dart")));
+ deleteEntry(path.join(sandboxDir, appPath, "lib", "test_pkg.dart")));
d.dir(appPath, [
d.dir("lib", [d.file("best_pkg.dart", "int i = 0;")])
]).create();
@@ -340,19 +340,19 @@
});
integration('has no lib directory', () {
- schedule(() => deleteDir(path.join(sandboxDir, appPath, "lib")));
+ schedule(() => deleteEntry(path.join(sandboxDir, appPath, "lib")));
expectValidationError(lib);
});
integration('has an empty lib directory', () {
schedule(() =>
- deleteFile(path.join(sandboxDir, appPath, "lib", "test_pkg.dart")));
+ deleteEntry(path.join(sandboxDir, appPath, "lib", "test_pkg.dart")));
expectValidationError(lib);
});
integration('has a lib directory containing only src', () {
schedule(() =>
- deleteFile(path.join(sandboxDir, appPath, "lib", "test_pkg.dart")));
+ deleteEntry(path.join(sandboxDir, appPath, "lib", "test_pkg.dart")));
d.dir(appPath, [
d.dir("lib", [
d.dir("src", [d.file("test_pkg.dart", "int i = 0;")])