Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(569)

Unified Diff: utils/tests/pub/test_pub.dart

Issue 10699068: Add numerous features to the Pub test infrastructure. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 8 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « utils/tests/pub/pub_test.dart ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: utils/tests/pub/test_pub.dart
diff --git a/utils/tests/pub/test_pub.dart b/utils/tests/pub/test_pub.dart
index 26da7d8d7ebdff69e80f8a1a39da42c7c7da0b1e..0cccbf959dcffb4fb4e509c931141ccf18672974 100644
--- a/utils/tests/pub/test_pub.dart
+++ b/utils/tests/pub/test_pub.dart
@@ -16,30 +16,31 @@
#import('../../../lib/unittest/unittest.dart');
#import('../../lib/file_system.dart', prefix: 'fs');
#import('../../pub/io.dart');
+#import('../../pub/utils.dart');
#import('../../pub/yaml/yaml.dart');
/**
* Creates a new [FileDescriptor] with [name] and [contents].
*/
-FileDescriptor file(String name, String contents) =>
+FileDescriptor file(Pattern name, String contents) =>
new FileDescriptor(name, contents);
/**
* Creates a new [DirectoryDescriptor] with [name] and [contents].
*/
-DirectoryDescriptor dir(String name, [List<Descriptor> contents]) =>
+DirectoryDescriptor dir(Pattern name, [List<Descriptor> contents]) =>
new DirectoryDescriptor(name, contents);
/**
* Creates a new [GitRepoDescriptor] with [name] and [contents].
*/
-DirectoryDescriptor git(String name, [List<Descriptor> contents]) =>
+DirectoryDescriptor git(Pattern name, [List<Descriptor> contents]) =>
new GitRepoDescriptor(name, contents);
/**
* Creates a new [TarFileDescriptor] with [name] and [contents].
*/
-TarFileDescriptor tar(String name, [List<Descriptor> contents]) =>
+TarFileDescriptor tar(Pattern name, [List<Descriptor> contents]) =>
new TarFileDescriptor(name, contents);
/**
@@ -52,7 +53,7 @@ void serve(String host, int port, [List<Descriptor> contents]) {
host = '127.0.0.1';
}
- _scheduleBeforePub((_) {
+ _schedule((_) {
var server = new HttpServer();
server.defaultRequestHandler = (request, response) {
var path = request.uri.replaceFirst("/", "").split("/");
@@ -145,67 +146,38 @@ final String packagesPath = "$appPath/packages";
typedef Future _ScheduledEvent(Directory parentDir);
/**
- * The list of events that are scheduled to run after the sandbox directory has
- * been created but before Pub is run.
+ * The list of events that are scheduled to run as part of the test case.
*/
-List<_ScheduledEvent> _scheduledBeforePub;
+List<_ScheduledEvent> _scheduled;
/**
- * The list of events that are scheduled to run after Pub has been run.
+ * The list of events that are scheduled to run after the test case, even if it
+ * failed.
*/
-List<_ScheduledEvent> _scheduledAfterPub;
+List<_ScheduledEvent> _scheduledCleanup;
/**
- * The list of events that are scheduled to run after Pub has been run, even if
- * it failed.
+ * Runs all the scheduled events for a test case. This should only be called
+ * once per test case.
*/
-List<_ScheduledEvent> _scheduledCleanup;
-
-void runPub([List<String> args, Pattern output, Pattern error,
- int exitCode = 0]) {
+void run() {
var createdSandboxDir;
var asyncDone = expectAsync0(() {});
Future cleanup() {
return _runScheduled(createdSandboxDir, _scheduledCleanup).chain((_) {
- _scheduledBeforePub = null;
- _scheduledAfterPub = null;
+ _scheduled = null;
if (createdSandboxDir != null) return deleteDir(createdSandboxDir);
return new Future.immediate(null);
});
}
- String pathInSandbox(path) => join(getFullPath(createdSandboxDir), path);
-
final future = _setUpSandbox().chain((sandboxDir) {
createdSandboxDir = sandboxDir;
- return _runScheduled(sandboxDir, _scheduledBeforePub);
- }).chain((_) {
- return ensureDir(pathInSandbox(appPath));
- }).chain((_) {
- // TODO(rnystrom): Hack in the cache directory path. Should pass this
- // in using environment var once #752 is done.
- args.add('--cachedir=${pathInSandbox(cachePath)}');
-
- // TODO(rnystrom): Hack in the SDK path. Should pass this in using
- // environment var once #752 is done.
- args.add('--sdkdir=${pathInSandbox(sdkPath)}');
-
- return _runPub(args, pathInSandbox(appPath), pipeStdout: output == null,
- pipeStderr: error == null);
- }).chain((result) {
- _validateOutput(output, result.stdout);
- _validateOutput(error, result.stderr);
-
- Expect.equals(result.exitCode, exitCode,
- 'Pub returned exit code ${result.exitCode}, expected $exitCode.');
-
- return _runScheduled(createdSandboxDir, _scheduledAfterPub);
+ return _runScheduled(sandboxDir, _scheduled);
});
- future.chain((_) => cleanup()).then((_) => asyncDone());
-
future.handleException((error) {
// If an error occurs during testing, delete the sandbox, throw the error so
// that the test framework sees it, then finally call asyncDone so that the
@@ -215,8 +187,51 @@ void runPub([List<String> args, Pattern output, Pattern error,
});
return true;
});
+
+ future.chain((_) => cleanup()).then((_) => asyncDone());
+}
+
+/**
+ * Schedules a call to the Pub command-line utility. Runs Pub with [args] and
+ * validates that its results match [output], [error], and [exitCode].
+ */
+void schedulePub([List<String> args, Pattern output, Pattern error,
+ int exitCode = 0]) {
+ _schedule((sandboxDir) {
+ String pathInSandbox(path) => join(getFullPath(sandboxDir), path);
+
+ return ensureDir(pathInSandbox(appPath)).chain((_) {
+ // TODO(rnystrom): Hack in the cache directory path. Should pass this in
+ // using environment var once #752 is done.
+ args.add('--cachedir=${pathInSandbox(cachePath)}');
+
+ // TODO(rnystrom): Hack in the SDK path. Should pass this in using
+ // environment var once #752 is done.
+ args.add('--sdkdir=${pathInSandbox(sdkPath)}');
+
+ return _runPub(args, pathInSandbox(appPath), pipeStdout: output == null,
+ pipeStderr: error == null);
+ }).transform((result) {
+ _validateOutput(output, result.stdout);
+ _validateOutput(error, result.stderr);
+
+ Expect.equals(result.exitCode, exitCode,
+ 'Pub returned exit code ${result.exitCode}, expected $exitCode.');
+
+ return null;
+ });
+ });
}
+/**
+ * A shorthand for [schedulePub] and [run] when no validation needs to be done
+ * after Pub has been run.
+ */
+void runPub([List<String> args, Pattern output, Pattern error,
+ int exitCode = 0]) {
+ schedulePub(args, output, error, exitCode);
+ run();
+}
/**
* Wraps a test that needs git in order to run. This validates that the test is
@@ -237,14 +252,25 @@ Future<Directory> _setUpSandbox() {
return createTempDir('pub-test-sandbox-');
}
-_runScheduled(Directory parentDir, List<_ScheduledEvent> scheduled) {
+Future _runScheduled(Directory parentDir, List<_ScheduledEvent> scheduled) {
if (scheduled == null) return new Future.immediate(null);
- var future = Futures.wait(scheduled.map((event) {
- var subFuture = event(parentDir);
- return subFuture == null ? new Future.immediate(null) : subFuture;
- }));
- scheduled.clear();
- return future;
+ var iterator = scheduled.iterator();
+
+ Future runNextEvent([_]) {
+ if (!iterator.hasNext()) {
+ scheduled.clear();
+ return new Future.immediate(null);
+ }
+
+ var future = iterator.next()(parentDir);
+ if (future != null) {
+ return future.chain(runNextEvent);
+ } else {
+ return runNextEvent();
+ }
+ }
+
+ return runNextEvent();
}
Future<ProcessResult> _runPub(List<String> pubArgs, String workingDir,
@@ -326,9 +352,10 @@ void _validateOutputString(String expectedText, List<String> actual) {
*/
class Descriptor {
/**
- * The short name of this file or directory.
+ * The name of this file or directory. This must be a [String] if the fiel or
+ * directory is going to be created.
*/
- final String name;
+ final Pattern name;
Descriptor(this.name);
@@ -346,6 +373,12 @@ class Descriptor {
abstract Future validate(String dir);
/**
+ * Deletes the file or directory within [dir]. Returns a [Future] that is
+ * completed after the deletion is done.
+ */
+ abstract Future delete(String dir);
+
+ /**
* Loads the file at [path] from within this descriptor. If [path] is empty,
* loads the contents of the descriptor itself.
*/
@@ -356,14 +389,72 @@ class Descriptor {
* directory will be created relative to the sandbox directory.
*/
// TODO(nweiz): Use implicit closurization once issue 2984 is fixed.
- void scheduleCreate() => _scheduleBeforePub((dir) => this.create(dir));
+ void scheduleCreate() => _schedule((dir) => this.create(dir));
+
+ /**
+ * Schedules the file or directory to be deleted recursively.
+ */
+ void scheduleDelete() => _schedule((dir) => this.delete(dir));
/**
* Schedules the directory to be validated after Pub is run with [runPub]. The
* directory will be validated relative to the sandbox directory.
*/
- void scheduleValidate() =>
- _scheduleAfterPub((parentDir) => validate(parentDir.path));
+ void scheduleValidate() => _schedule((parentDir) => validate(parentDir.path));
+
+ /**
+ * Asserts that the name of the descriptor is a [String] and returns it.
+ */
+ String get _stringName() {
+ if (name is String) return name;
+ throw 'Pattern $name must be a string.';
+ }
+
+ /**
+ * Validates that at least one file in [dir] matching [name] is valid
+ * according to [validate]. [validate] should complete to an exception if the
+ * input path is invalid.
+ */
+ Future _validateOneMatch(String dir, Future validate(String path)) {
+ // Special-case strings to support multi-level names like "myapp/packages".
+ if (name is String) {
+ var path = join(dir, name);
+ return exists(path).chain((exists) {
+ if (!exists) Expect.fail('File $name in $dir not found.');
+ return validate(path);
+ });
+ }
+
+ return listDir(dir).chain((files) {
+ var matches = files.filter((file) => endsWithPattern(file, name));
+ if (matches.length == 0) {
+ Expect.fail('No files in $dir match pattern $name.');
+ }
+ if (matches.length == 1) return validate(matches[0]);
+
+ var failures = [];
+ var completer = new Completer();
+ for (var match in matches) {
+ var future = validate(match);
+
+ future.handleException((e) {
+ failures.add(e);
+ if (failures.length != matches.length) return true;
+
+ var error = new StringBuffer();
+ error.add("No files named $name in $dir were valid:\n");
+ for (var failure in failures) {
+ error.add(" ").add(failure).add("\n");
+ }
+ completer.completeException(new ExpectException(error.toString()));
+ return true;
+ });
+
+ future.then(completer.complete);
+ }
+ return completer.future;
+ });
+ }
}
/**
@@ -377,28 +468,33 @@ class FileDescriptor extends Descriptor {
*/
final String contents;
- FileDescriptor(String name, this.contents) : super(name);
+ FileDescriptor(Pattern name, this.contents) : super(name);
/**
* Creates the file within [dir]. Returns a [Future] that is completed after
* the creation is done.
*/
Future<File> create(dir) {
- return writeTextFile(join(dir, name), contents);
+ return writeTextFile(join(dir, _stringName), contents);
+ }
+
+ /**
+ * Deletes the file within [dir]. Returns a [Future] that is completed after
+ * the deletion is done.
+ */
+ Future delete(dir) {
+ return deleteFile(join(dir, _stringName));
}
/**
* Validates that this file correctly matches the actual file at [path].
*/
Future validate(String path) {
- path = join(path, name);
- return fileExists(path).chain((exists) {
- if (!exists) Expect.fail('Expected file $path does not exist.');
-
- return readTextFile(path).transform((text) {
+ return _validateOneMatch(path, (file) {
+ return readTextFile(file).transform((text) {
if (text == contents) return null;
- Expect.fail('File $path should contain:\n\n$contents\n\n'
+ Expect.fail('File $file should contain:\n\n$contents\n\n'
'but contained:\n\n$text');
});
});
@@ -430,7 +526,7 @@ class DirectoryDescriptor extends Descriptor {
*/
final List<Descriptor> contents;
- DirectoryDescriptor(String name, this.contents) : super(name);
+ DirectoryDescriptor(Pattern name, this.contents) : super(name);
/**
* Creates the file within [dir]. Returns a [Future] that is completed after
@@ -440,7 +536,7 @@ class DirectoryDescriptor extends Descriptor {
final completer = new Completer<Directory>();
// Create the directory.
- createDir(join(parentDir, name)).then((dir) {
+ ensureDir(join(parentDir, _stringName)).then((dir) {
if (contents == null) {
completer.complete(dir);
} else {
@@ -457,18 +553,27 @@ class DirectoryDescriptor extends Descriptor {
}
/**
+ * Deletes the directory within [dir]. Returns a [Future] that is completed
+ * after the deletion is done.
+ */
+ Future delete(dir) {
+ return deleteDir(join(dir, _stringName));
+ }
+
+ /**
* Validates that the directory at [path] contains all of the expected
* contents in this descriptor. Note that this does *not* check that the
* directory doesn't contain other unexpected stuff, just that it *does*
* contain the stuff we do expect.
*/
Future validate(String path) {
- // Validate each of the items in this directory.
- final entryFutures = contents.map(
- (entry) => entry.validate(join(path, name)));
+ return _validateOneMatch(path, (dir) {
+ // Validate each of the items in this directory.
+ final entryFutures = contents.map((entry) => entry.validate(dir));
- // If they are all valid, the directory is valid.
- return Futures.wait(entryFutures).transform((entries) => null);
+ // If they are all valid, the directory is valid.
+ return Futures.wait(entryFutures).transform((entries) => null);
+ });
}
/**
@@ -493,7 +598,7 @@ class DirectoryDescriptor extends Descriptor {
* Describes a Git repository and its contents.
*/
class GitRepoDescriptor extends DirectoryDescriptor {
- GitRepoDescriptor(String name, List<Descriptor> contents)
+ GitRepoDescriptor(Pattern name, List<Descriptor> contents)
: super(name, contents);
/**
@@ -501,13 +606,7 @@ class GitRepoDescriptor extends DirectoryDescriptor {
*/
Future<Directory> create(parentDir) {
var workingDir;
- Future runGit(List<String> args) {
- return runProcess('git', args, workingDir: workingDir.path).
- transform((result) {
- if (!result.success) throw "Error running git: ${result.stderr}";
- return null;
- });
- }
+ Future runGit(List<String> args) => _runGit(args, workingDir);
return super.create(parentDir).chain((rootDir) {
workingDir = rootDir;
@@ -516,6 +615,32 @@ class GitRepoDescriptor extends DirectoryDescriptor {
.chain((_) => runGit(['commit', '-m', 'initial commit']))
.transform((_) => workingDir);
}
+
+ /**
+ * Commits any changes to the Git repository.
+ */
+ Future commit(parentDir) {
Jennifer Messerly 2012/07/03 02:03:10 anyone using this yet?
nweiz 2012/07/03 18:04:49 No, all these changes are going to be used in a fu
+ var workingDir;
+ Future runGit(List<String> args) => _runGit(args, workingDir);
+
+ return super.create(parentDir).chain((rootDir) {
+ workingDir = rootDir;
+ return runGit(['add', '.']);
+ }).chain((_) => runGit(['commit', '-m', 'update']));
+ }
+
+ /**
+ * Schedules changes to be committed to the Git repository.
+ */
+ void scheduleCommit() => _schedule((dir) => this.commit(dir));
+
+ Future _runGit(List<String> args, Directory workingDir) {
+ return runProcess('git', args, workingDir: workingDir.path).
+ transform((result) {
+ if (!result.success) throw "Error running git: ${result.stderr}";
+ return null;
+ });
+ }
}
/**
@@ -524,7 +649,7 @@ class GitRepoDescriptor extends DirectoryDescriptor {
class TarFileDescriptor extends Descriptor {
final List<Descriptor> contents;
- TarFileDescriptor(String name, this.contents)
+ TarFileDescriptor(Pattern name, this.contents)
: super(name);
/**
@@ -538,7 +663,7 @@ class TarFileDescriptor extends Descriptor {
return Futures.wait(contents.map((child) => child.create(tempDir)));
}).chain((_) {
var args = ["--directory", tempDir.path, "--create", "--gzip", "--file",
- join(parentDir, name)];
+ join(parentDir, _stringName)];
args.addAll(contents.map((child) => child.name));
return runProcess("tar", args);
}).chain((result) {
@@ -548,7 +673,7 @@ class TarFileDescriptor extends Descriptor {
}
return deleteDir(tempDir);
}).transform((_) {
- return new File(join(parentDir, name));
+ return new File(join(parentDir, _stringName));
});
}
@@ -584,19 +709,11 @@ class TarFileDescriptor extends Descriptor {
}
/**
- * Schedules a callback to be called before Pub is run with [runPub].
- */
-void _scheduleBeforePub(_ScheduledEvent event) {
- if (_scheduledBeforePub == null) _scheduledBeforePub = [];
- _scheduledBeforePub.add(event);
-}
-
-/**
- * Schedules a callback to be called after Pub is run with [runPub].
+ * Schedules a callback to be called as part of the test case.
*/
-void _scheduleAfterPub(_ScheduledEvent event) {
- if (_scheduledAfterPub == null) _scheduledAfterPub = [];
- _scheduledAfterPub.add(event);
+void _schedule(_ScheduledEvent event) {
+ if (_scheduled == null) _scheduled = [];
+ _scheduled.add(event);
}
/**
« no previous file with comments | « utils/tests/pub/pub_test.dart ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698