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

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

Issue 10628016: Add a Pub source for pub.dartlang.org. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Code review changes 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
« utils/pub/io.dart ('K') | « 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 7172c7ba00728b148c6d1e31f422619201199e62..39e39a24024a19625e4bea5272735d81dea02d5d 100644
--- a/utils/tests/pub/test_pub.dart
+++ b/utils/tests/pub/test_pub.dart
@@ -11,10 +11,12 @@
#library('test_pub');
#import('dart:io');
+#import('dart:uri');
#import('../../../lib/unittest/unittest.dart');
#import('../../lib/file_system.dart', prefix: 'fs');
#import('../../pub/io.dart');
+#import('../../pub/yaml/yaml.dart');
/**
* Creates a new [FileDescriptor] with [name] and [contents].
@@ -35,6 +37,84 @@ DirectoryDescriptor git(String name, [List<Descriptor> contents]) =>
new GitRepoDescriptor(name, contents);
/**
+ * Creates a new [TarFileDescriptor] with [name] and [contents].
+ */
+TarFileDescriptor tar(String name, [List<Descriptor> contents]) =>
+ new TarFileDescriptor(name, contents);
+
+/**
+ * Creates an HTTP server to serve [contents] as static files. This server will
+ * exist only for the duration of the pub run.
+ */
+void serve(String host, int port, [List<Descriptor> contents]) {
+ var baseDir = dir("serve-dir", contents);
+ if (host == 'localhost') {
+ host = '127.0.0.1';
+ }
+
+ _scheduleBeforePub((_) {
+ var server = new HttpServer();
+ server.defaultRequestHandler = (request, response) {
+ var path = request.uri.replaceFirst("/", "").split("/");
+ var stream = baseDir.load(path);
+ response.persistentConnection = false;
+ if (stream == null) {
+ response.statusCode = 404;
+ response.outputStream.close();
+ return;
+ }
+
+ var future = consumeInputStream(stream);
+ future.then((data) {
+ response.statusCode = 200;
+ response.contentLength = data.length;
+ response.outputStream.write(data);
+ response.outputStream.close();
+ });
+
+ future.handleException((e) {
+ print("Exception while handling ${request.uri}: $e");
+ response.statusCode = 500;
+ response.reasonPhrase = e.message;
+ response.outputStream.close();
+ });
+ };
+ server.listen(host, port);
+ _scheduleCleanup((_) => server.close());
+
+ return new Future.immediate(null);
+ });
+}
+
+/**
+ * Creates an HTTP server that replicates the structure of pub.dartlang.org.
+ * [pubspecs] is a list of YAML-format pubspecs representing the packages to
+ * serve.
+ */
+void servePackages(String host, int port, List<String> pubspecs) {
+ var packages = <Map<String, String>>{};
+ pubspecs.forEach((spec) {
+ var parsed = loadYaml(spec);
+ var name = parsed['name'];
+ var version = parsed['version'];
+ packages.putIfAbsent(name, () => <String>{})[version] = spec;
+ });
+
+ serve(host, port, [
+ dir('packages', packages.getKeys().map((name) {
+ return dir(name, [
+ dir('versions', packages[name].getKeys().map((version) {
+ return tar('$version.tar.gz', [
+ file('pubspec.yaml', packages[name][version]),
+ file('$name.dart', 'main() => print("$name $version");')
+ ]);
+ }))
+ ]);
+ }))
+ ]);
+}
+
+/**
* The path of the package cache directory used for tests. Relative to the
* sandbox directory.
*/
@@ -75,20 +155,25 @@ List<_ScheduledEvent> _scheduledBeforePub;
*/
List<_ScheduledEvent> _scheduledAfterPub;
+/**
+ * The list of events that are scheduled to run after Pub has been run, even if
+ * it failed.
+ */
+List<_ScheduledEvent> _scheduledCleanup;
+
void runPub([List<String> args, Pattern output, Pattern error,
int exitCode = 0]) {
var createdSandboxDir;
var asyncDone = expectAsync0(() {});
- deleteSandboxIfCreated(onDeleted()) {
- _scheduledBeforePub = null;
- _scheduledAfterPub = null;
- if (createdSandboxDir != null) {
- deleteDir(createdSandboxDir).then((_) => onDeleted());
- } else {
- onDeleted();
- }
+ Future cleanup() {
+ return _runScheduled(createdSandboxDir, _scheduledCleanup).chain((_) {
+ _scheduledBeforePub = null;
+ _scheduledAfterPub = null;
+ if (createdSandboxDir != null) return deleteDir(createdSandboxDir);
+ return new Future.immediate(null);
+ });
}
String pathInSandbox(path) => join(getFullPath(createdSandboxDir), path);
@@ -107,7 +192,8 @@ void runPub([List<String> args, Pattern output, Pattern error,
// environment var once #752 is done.
args.add('--sdkdir=${pathInSandbox(sdkPath)}');
- return _runPub(args, pathInSandbox(appPath));
+ return _runPub(args, pathInSandbox(appPath), pipeStdout: output == null,
+ pipeStderr: error == null);
}).chain((result) {
_validateOutput(output, result.stdout);
_validateOutput(error, result.stderr);
@@ -118,15 +204,13 @@ void runPub([List<String> args, Pattern output, Pattern error,
return _runScheduled(createdSandboxDir, _scheduledAfterPub);
});
- future.then((_) {
- deleteSandboxIfCreated(asyncDone);
- });
+ 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
// test framework knows we're done doing asynchronous stuff.
- deleteSandboxIfCreated(() {
+ cleanup().then((_) {
guardAsync(() { throw error; }, asyncDone);
});
return true;
@@ -155,12 +239,16 @@ Future<Directory> _setUpSandbox() {
_runScheduled(Directory parentDir, List<_ScheduledEvent> scheduled) {
if (scheduled == null) return new Future.immediate(null);
- var future = Futures.wait(scheduled.map((event) => event(parentDir)));
+ var future = Futures.wait(scheduled.map((event) {
+ var subFuture = event(parentDir);
+ return subFuture == null ? new Future.immediate(null) : subFuture;
+ }));
scheduled.clear();
return future;
}
-Future<ProcessResult> _runPub(List<String> pubArgs, String workingDir) {
+Future<ProcessResult> _runPub(List<String> pubArgs, String workingDir,
+ [bool pipeStdout=false, bool pipeStderr=false]) {
// Find a dart executable we can use to run pub. Uses the one that the
// test infrastructure uses. We are not using new Options.executable here
// because that gets confused if you invoked Dart through a shell script.
@@ -174,7 +262,7 @@ Future<ProcessResult> _runPub(List<String> pubArgs, String workingDir) {
final args = ['--enable-type-checks', '--enable-asserts', pubPath];
args.addAll(pubArgs);
- return runProcess(dartBin, args, workingDir);
+ return runProcess(dartBin, args, workingDir, pipeStdout, pipeStderr);
}
/**
@@ -258,6 +346,12 @@ class Descriptor {
abstract Future validate(String dir);
/**
+ * Loads the file at [path] from within this descriptor. If [path] is empty,
+ * loads the contents of the descriptor itself.
+ */
+ abstract InputStream load(List<String> path);
+
+ /**
* Schedules the directory to be created before Pub is run with [runPub]. The
* directory will be created relative to the sandbox directory.
*/
@@ -309,6 +403,20 @@ class FileDescriptor extends Descriptor {
});
});
}
+
+ /**
+ * Loads the contents of the file.
+ */
+ InputStream load(List<String> path) {
+ if (!path.isEmpty()) {
+ var joinedPath = Strings.join('/', path);
+ throw "Can't load $joinedPath from within $name: not a directory.";
+ }
+
+ var stream = new ListInputStream();
+ stream.write(contents.charCodes());
+ return stream;
+ }
}
/**
@@ -362,6 +470,23 @@ class DirectoryDescriptor extends Descriptor {
// If they are all valid, the directory is valid.
return Futures.wait(entryFutures).transform((entries) => null);
}
+
+ /**
+ * Loads [path] from within this directory.
+ */
+ InputStream load(List<String> path) {
+ if (path.isEmpty()) {
+ throw "Can't load the contents of $name: is a directory.";
+ }
+
+ for (var descriptor in contents) {
+ if (descriptor.name == path[0]) {
+ return descriptor.load(path.getRange(1, path.length - 1));
+ }
+ }
+
+ throw "Directory $name doesn't contain ${Strings.join('/', path)}.";
+ }
}
/**
@@ -394,6 +519,70 @@ class GitRepoDescriptor extends DirectoryDescriptor {
}
/**
+ * Describes a gzipped tar file and its contents.
+ */
+class TarFileDescriptor extends Descriptor {
+ final List<Descriptor> contents;
+
+ TarFileDescriptor(String name, this.contents)
+ : super(name);
+
+ /**
+ * Creates the files and directories within this tar file, then archives them,
+ * compresses them, and saves the result to [parentDir].
+ */
+ Future<File> create(parentDir) {
+ var tempDir;
+ return parentDir.createTemp().chain((_tempDir) {
+ tempDir = _tempDir;
+ return Futures.wait(contents.map((child) => child.create(tempDir)));
+ }).chain((_) {
+ var args = ["--directory", tempDir.path, "--create", "--gzip", "--file",
+ join(parentDir, name)];
+ args.addAll(contents.map((child) => child.name));
+ return runProcess("tar", args);
+ }).chain((result) {
+ if (!result.success) {
+ throw "Failed to create tar file $name.\n"
+ "STDERR: ${Strings.join(result.stderr, "\n")}";
+ }
+ return deleteDir(tempDir);
+ }).transform((_) {
+ return new File(join(parentDir, name));
+ });
+ }
+
+ /**
+ * Validates that the `.tar.gz` file at [path] contains the expected contents.
+ */
+ Future validate(String path) {
+ throw "TODO(nweiz): implement this";
+ }
+
+ /**
+ * Loads the contents of this tar file.
+ */
+ InputStream load(List<String> path) {
+ if (!path.isEmpty()) {
+ var joinedPath = Strings.join('/', path);
+ throw "Can't load $joinedPath from within $name: not a directory.";
+ }
+
+ var stream = new ListInputStream();
+ var tempDir;
+ // TODO(nweiz): propagate any errors to the return value. See issue 3657.
+ createTempDir("pub-test-tmp-").chain((_tempDir) {
+ tempDir = _tempDir;
+ return create(tempDir);
+ }).then((tar) {
+ pipeInputToInput(tar.openInputStream(), stream);
+ tempDir.deleteRecursively();
+ });
+ return stream;
+ }
+}
+
+/**
* Schedules a callback to be called before Pub is run with [runPub].
*/
void _scheduleBeforePub(_ScheduledEvent event) {
@@ -408,3 +597,12 @@ void _scheduleAfterPub(_ScheduledEvent event) {
if (_scheduledAfterPub == null) _scheduledAfterPub = [];
_scheduledAfterPub.add(event);
}
+
+/**
+ * Schedules a callback to be called after Pub is run with [runPub], even if it
+ * fails.
+ */
+void _scheduleCleanup(_ScheduledEvent event) {
+ if (_scheduledCleanup == null) _scheduledCleanup = [];
+ _scheduledCleanup.add(event);
+}
« utils/pub/io.dart ('K') | « utils/tests/pub/pub_test.dart ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698