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

Side by Side Diff: utils/tests/pub/test_pub.dart

Issue 11434118: Add validation infrastructure for "pub lish". (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Code review changes Created 8 years 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 unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « utils/tests/pub/pub_lish_test.dart ('k') | utils/tests/pub/validator_test.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a 2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 /** 5 /**
6 * Test infrastructure for testing pub. Unlike typical unit tests, most pub 6 * Test infrastructure for testing pub. Unlike typical unit tests, most pub
7 * tests are integration tests that stage some stuff on the file system, run 7 * tests are integration tests that stage some stuff on the file system, run
8 * pub, and then validate the results. This library provides an API to build 8 * pub, and then validate the results. This library provides an API to build
9 * tests like that. 9 * tests like that.
10 */ 10 */
11 library test_pub; 11 library test_pub;
12 12
13 import 'dart:io'; 13 import 'dart:io';
14 import 'dart:isolate'; 14 import 'dart:isolate';
15 import 'dart:json'; 15 import 'dart:json';
16 import 'dart:math'; 16 import 'dart:math';
17 import 'dart:uri'; 17 import 'dart:uri';
18 18
19 import '../../../pkg/oauth2/lib/oauth2.dart' as oauth2; 19 import '../../../pkg/oauth2/lib/oauth2.dart' as oauth2;
20 import '../../../pkg/unittest/lib/unittest.dart'; 20 import '../../../pkg/unittest/lib/unittest.dart';
21 import '../../lib/file_system.dart' as fs; 21 import '../../lib/file_system.dart' as fs;
22 import '../../pub/entrypoint.dart';
22 import '../../pub/git_source.dart'; 23 import '../../pub/git_source.dart';
23 import '../../pub/hosted_source.dart'; 24 import '../../pub/hosted_source.dart';
24 import '../../pub/io.dart'; 25 import '../../pub/io.dart';
25 import '../../pub/sdk_source.dart'; 26 import '../../pub/sdk_source.dart';
27 import '../../pub/system_cache.dart';
26 import '../../pub/utils.dart'; 28 import '../../pub/utils.dart';
29 import '../../pub/validator.dart';
27 import '../../pub/yaml/yaml.dart'; 30 import '../../pub/yaml/yaml.dart';
28 31
29 /** 32 /**
30 * Creates a new [FileDescriptor] with [name] and [contents]. 33 * Creates a new [FileDescriptor] with [name] and [contents].
31 */ 34 */
32 FileDescriptor file(Pattern name, String contents) => 35 FileDescriptor file(Pattern name, String contents) =>
33 new FileDescriptor(name, contents); 36 new FileDescriptor(name, contents);
34 37
35 /** 38 /**
36 * Creates a new [DirectoryDescriptor] with [name] and [contents]. 39 * Creates a new [DirectoryDescriptor] with [name] and [contents].
(...skipping 224 matching lines...) Expand 10 before | Expand all | Expand 10 after
261 return dir("lib", [ 264 return dir("lib", [
262 file("$name.dart", 'main() => "$code";') 265 file("$name.dart", 'main() => "$code";')
263 ]); 266 ]);
264 } 267 }
265 268
266 /** 269 /**
267 * Describes a map representing a library package with the given [name], 270 * Describes a map representing a library package with the given [name],
268 * [version], and [dependencies]. 271 * [version], and [dependencies].
269 */ 272 */
270 Map package(String name, String version, [List dependencies]) { 273 Map package(String name, String version, [List dependencies]) {
271 var package = {"name": name, "version": version}; 274 var package = {
275 "name": name,
276 "version": version,
277 "author": "Nathan Weizenbaum <nweiz@google.com>",
278 "homepage": "http://pub.dartlang.org"
279 };
272 if (dependencies != null) { 280 if (dependencies != null) {
273 package["dependencies"] = _dependencyListToMap(dependencies); 281 package["dependencies"] = _dependencyListToMap(dependencies);
274 } 282 }
275 return package; 283 return package;
276 } 284 }
277 285
278 /** 286 /**
279 * Describes a map representing a dependency on a package in the package 287 * Describes a map representing a dependency on a package in the package
280 * repository. 288 * repository.
281 */ 289 */
(...skipping 237 matching lines...) Expand 10 before | Expand all | Expand 10 after
519 // that the test framework sees it, then finally call asyncDone so that the 527 // that the test framework sees it, then finally call asyncDone so that the
520 // test framework knows we're done doing asynchronous stuff. 528 // test framework knows we're done doing asynchronous stuff.
521 var future = _runScheduled(createdSandboxDir, _scheduledOnException) 529 var future = _runScheduled(createdSandboxDir, _scheduledOnException)
522 .chain((_) => cleanup()); 530 .chain((_) => cleanup());
523 future.handleException((e) { 531 future.handleException((e) {
524 print("Exception while cleaning up: $e"); 532 print("Exception while cleaning up: $e");
525 print(future.stackTrace); 533 print(future.stackTrace);
526 registerException(error, future.stackTrace); 534 registerException(error, future.stackTrace);
527 return true; 535 return true;
528 }); 536 });
529 future.then((_) { 537 future.then((_) => registerException(error, future.stackTrace));
530 print("Registering exception");
531 registerException(error, future.stackTrace);
532 });
533 return true; 538 return true;
534 }); 539 });
535 540
536 future.chain((_) => cleanup()).then((_) { 541 future.chain((_) => cleanup()).then((_) {
537 asyncDone(); 542 asyncDone();
538 }); 543 });
539 } 544 }
540 545
541 /// Get the path to the root "util/test/pub" directory containing the pub tests. 546 /// Get the path to the root "util/test/pub" directory containing the pub tests.
542 String get testDirectory { 547 String get testDirectory {
(...skipping 690 matching lines...) Expand 10 before | Expand all | Expand 10 after
1233 InputStream load(List<String> path) { 1238 InputStream load(List<String> path) {
1234 if (path.isEmpty) { 1239 if (path.isEmpty) {
1235 throw "Can't load the contents of $name: it doesn't exist."; 1240 throw "Can't load the contents of $name: it doesn't exist.";
1236 } else { 1241 } else {
1237 throw "Can't load ${Strings.join(path, '/')} from within $name: $name " 1242 throw "Can't load ${Strings.join(path, '/')} from within $name: $name "
1238 "doesn't exist."; 1243 "doesn't exist.";
1239 } 1244 }
1240 } 1245 }
1241 } 1246 }
1242 1247
1248 /// A function that creates a [Validator] subclass.
1249 typedef Validator ValidatorCreator(Entrypoint entrypoint);
1250
1251 /// Schedules a single [Validator] to run on the [appPath]. Returns a scheduled
1252 /// Future that contains the erros and warnings produced by that validator.
1253 Future<Pair<List<String>, List<String>>> schedulePackageValidation(
1254 ValidatorCreator fn) {
1255 return _scheduleValue((sandboxDir) {
1256 var cache = new SystemCache.withSources(
1257 join(sandboxDir, cachePath),
1258 join(sandboxDir, sdkPath));
1259
1260 return Entrypoint.load(join(sandboxDir, appPath), cache)
1261 .chain((entrypoint) {
1262 var validator = fn(entrypoint);
1263 return validator.validate().transform((_) {
1264 return new Pair(validator.errors, validator.warnings);
1265 });
1266 });
1267 });
1268 }
1269
1270 /// A matcher that matches a Pair.
1271 Matcher pairOf(Matcher firstMatcher, Matcher lastMatcher) =>
1272 new _PairMatcher(firstMatcher, lastMatcher);
1273
1274 class _PairMatcher extends BaseMatcher {
1275 final Matcher _firstMatcher;
1276 final Matcher _lastMatcher;
1277
1278 _PairMatcher(this._firstMatcher, this._lastMatcher);
1279
1280 bool matches(item, MatchState matchState) {
1281 if (item is! Pair) return false;
1282 return _firstMatcher.matches(item.first, matchState) &&
1283 _lastMatcher.matches(item.last, matchState);
1284 }
1285
1286 Description describe(Description description) {
1287 description.addAll("(", ", ", ")", [_firstMatcher, _lastMatcher]);
1288 }
1289 }
1290
1291 /// The time (in milliseconds) to wait for scheduled events that could run
1292 /// forever.
1293 const _SCHEDULE_TIMEOUT = 5000;
1294
1243 /// A class representing a [Process] that is scheduled to run in the course of 1295 /// A class representing a [Process] that is scheduled to run in the course of
1244 /// the test. This class allows actions on the process to be scheduled 1296 /// the test. This class allows actions on the process to be scheduled
1245 /// synchronously. All operations on this class are scheduled. 1297 /// synchronously. All operations on this class are scheduled.
1246 /// 1298 ///
1247 /// Before running the test, either [shouldExit] or [kill] must be called on 1299 /// Before running the test, either [shouldExit] or [kill] must be called on
1248 /// this to ensure that the process terminates when expected. 1300 /// this to ensure that the process terminates when expected.
1249 /// 1301 ///
1250 /// If the test fails, this will automatically print out any remaining stdout 1302 /// If the test fails, this will automatically print out any remaining stdout
1251 /// and stderr from the process to aid debugging. 1303 /// and stderr from the process to aid debugging.
1252 class ScheduledProcess { 1304 class ScheduledProcess {
(...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after
1332 var process = _process.value; 1384 var process = _process.value;
1333 process.kill(); 1385 process.kill();
1334 process.stdout.close(); 1386 process.stdout.close();
1335 process.stderr.close(); 1387 process.stderr.close();
1336 }); 1388 });
1337 } 1389 }
1338 1390
1339 /// Reads the next line of stdout from the process. 1391 /// Reads the next line of stdout from the process.
1340 Future<String> nextLine() { 1392 Future<String> nextLine() {
1341 return _scheduleValue((_) { 1393 return _scheduleValue((_) {
1342 return timeout(_stdout.chain(readLine), 5000, 1394 return timeout(_stdout.chain(readLine), _SCHEDULE_TIMEOUT,
1343 "waiting for the next stdout line from process $name"); 1395 "waiting for the next stdout line from process $name");
1344 }); 1396 });
1345 } 1397 }
1346 1398
1347 /// Reads the next line of stderr from the process. 1399 /// Reads the next line of stderr from the process.
1348 Future<String> nextErrLine() { 1400 Future<String> nextErrLine() {
1349 return _scheduleValue((_) { 1401 return _scheduleValue((_) {
1350 return timeout(_stderr.chain(readLine), 5000, 1402 return timeout(_stderr.chain(readLine), _SCHEDULE_TIMEOUT,
1351 "waiting for the next stderr line from process $name"); 1403 "waiting for the next stderr line from process $name");
1352 }); 1404 });
1353 } 1405 }
1354 1406
1407 /// Reads the remaining stdout from the process. This should only be called
1408 /// after kill() or shouldExit().
1409 Future<String> remainingStdout() {
1410 if (!_endScheduled) {
1411 throw new StateError("remainingStdout() should only be called after "
1412 "kill() or shouldExit().");
1413 }
1414
1415 return _scheduleValue((_) {
1416 return timeout(_stdout.chain(consumeStringInputStream), _SCHEDULE_TIMEOUT,
1417 "waiting for the last stdout line from process $name");
1418 });
1419 }
1420
1421 /// Reads the remaining stderr from the process. This should only be called
1422 /// after kill() or shouldExit().
1423 Future<String> remainingStderr() {
1424 if (!_endScheduled) {
1425 throw new StateError("remainingStderr() should only be called after "
1426 "kill() or shouldExit().");
1427 }
1428
1429 return _scheduleValue((_) {
1430 return timeout(_stderr.chain(consumeStringInputStream), _SCHEDULE_TIMEOUT,
1431 "waiting for the last stderr line from process $name");
1432 });
1433 }
1434
1355 /// Writes [line] to the process as stdin. 1435 /// Writes [line] to the process as stdin.
1356 void writeLine(String line) { 1436 void writeLine(String line) {
1357 _schedule((_) => _process.transform((p) => p.stdin.writeString('$line\n'))); 1437 _schedule((_) => _process.transform((p) => p.stdin.writeString('$line\n')));
1358 } 1438 }
1359 1439
1360 /// Kills the process, and waits until it's dead. 1440 /// Kills the process, and waits until it's dead.
1361 void kill() { 1441 void kill() {
1362 _endScheduled = true; 1442 _endScheduled = true;
1363 _schedule((_) { 1443 _schedule((_) {
1364 _endExpected = true; 1444 _endExpected = true;
1365 return _process.chain((p) { 1445 return _process.chain((p) {
1366 p.kill(); 1446 p.kill();
1367 return timeout(_exitCode, 5000, "waiting for process $name to die"); 1447 return timeout(_exitCode, _SCHEDULE_TIMEOUT,
1448 "waiting for process $name to die");
1368 }); 1449 });
1369 }); 1450 });
1370 } 1451 }
1371 1452
1372 /// Waits for the process to exit, and verifies that the exit code matches 1453 /// Waits for the process to exit, and verifies that the exit code matches
1373 /// [expectedExitCode] (if given). 1454 /// [expectedExitCode] (if given).
1374 void shouldExit([int expectedExitCode]) { 1455 void shouldExit([int expectedExitCode]) {
1375 _endScheduled = true; 1456 _endScheduled = true;
1376 _schedule((_) { 1457 _schedule((_) {
1377 _endExpected = true; 1458 _endExpected = true;
1378 return timeout(_exitCode, 5000, "waiting for process $name to exit") 1459 return timeout(_exitCode, _SCHEDULE_TIMEOUT,
1379 .transform((exitCode) { 1460 "waiting for process $name to exit").transform((exitCode) {
1380 if (expectedExitCode != null) { 1461 if (expectedExitCode != null) {
1381 expect(exitCode, equals(expectedExitCode)); 1462 expect(exitCode, equals(expectedExitCode));
1382 } 1463 }
1383 }); 1464 });
1384 }); 1465 });
1385 } 1466 }
1386 1467
1387 /// Prints the remaining data in the process's stdout and stderr streams. 1468 /// Prints the remaining data in the process's stdout and stderr streams.
1388 /// Prints nothing if the straems are empty. 1469 /// Prints nothing if the straems are empty.
1389 Future _printStreams() { 1470 Future _printStreams() {
(...skipping 17 matching lines...) Expand all
1407 /// A class representing an [HttpServer] that's scheduled to run in the course 1488 /// A class representing an [HttpServer] that's scheduled to run in the course
1408 /// of the test. This class allows the server's request handling to be scheduled 1489 /// of the test. This class allows the server's request handling to be scheduled
1409 /// synchronously. All operations on this class are scheduled. 1490 /// synchronously. All operations on this class are scheduled.
1410 class ScheduledServer { 1491 class ScheduledServer {
1411 /// The wrapped server. 1492 /// The wrapped server.
1412 final Future<HttpServer> _server; 1493 final Future<HttpServer> _server;
1413 1494
1414 /// The queue of handlers to run for upcoming requests. 1495 /// The queue of handlers to run for upcoming requests.
1415 final _handlers = new Queue<Future>(); 1496 final _handlers = new Queue<Future>();
1416 1497
1498 /// The requests to be ignored.
1499 final _ignored = new Set<Pair<String, String>>();
1500
1417 ScheduledServer._(this._server); 1501 ScheduledServer._(this._server);
1418 1502
1419 /// Creates a new server listening on an automatically-allocated port on 1503 /// Creates a new server listening on an automatically-allocated port on
1420 /// localhost. 1504 /// localhost.
1421 factory ScheduledServer() { 1505 factory ScheduledServer() {
1422 var scheduledServer; 1506 var scheduledServer;
1423 scheduledServer = new ScheduledServer._(_scheduleValue((_) { 1507 scheduledServer = new ScheduledServer._(_scheduleValue((_) {
1424 var server = new HttpServer(); 1508 var server = new HttpServer();
1425 server.defaultRequestHandler = scheduledServer._awaitHandle; 1509 server.defaultRequestHandler = scheduledServer._awaitHandle;
1426 server.listen("127.0.0.1", 0); 1510 server.listen("127.0.0.1", 0);
(...skipping 20 matching lines...) Expand all
1447 var requestCompleteCompleter = new Completer(); 1531 var requestCompleteCompleter = new Completer();
1448 handlerCompleter.complete((request, response) { 1532 handlerCompleter.complete((request, response) {
1449 expect(request.method, equals(method)); 1533 expect(request.method, equals(method));
1450 expect(request.path, equals(path)); 1534 expect(request.path, equals(path));
1451 1535
1452 var future = handler(request, response); 1536 var future = handler(request, response);
1453 if (future == null) future = new Future.immediate(null); 1537 if (future == null) future = new Future.immediate(null);
1454 chainToCompleter(future, requestCompleteCompleter); 1538 chainToCompleter(future, requestCompleteCompleter);
1455 }); 1539 });
1456 return timeout(requestCompleteCompleter.future, 1540 return timeout(requestCompleteCompleter.future,
1457 5000, "waiting for $method $path"); 1541 _SCHEDULE_TIMEOUT, "waiting for $method $path");
1458 }); 1542 });
1459 _handlers.add(handlerCompleter.future); 1543 _handlers.add(handlerCompleter.future);
1460 } 1544 }
1461 1545
1546 /// Ignore all requests with the given [method] and [path]. If one is
1547 /// received, don't respond to it.
1548 void ignore(String method, String path) =>
1549 _ignored.add(new Pair(method, path));
1550
1462 /// Raises an error complaining of an unexpected request. 1551 /// Raises an error complaining of an unexpected request.
1463 void _awaitHandle(HttpRequest request, HttpResponse response) { 1552 void _awaitHandle(HttpRequest request, HttpResponse response) {
1553 if (_ignored.contains(new Pair(request.method, request.path))) return;
1464 var future = timeout(new Future.immediate(null).chain((_) { 1554 var future = timeout(new Future.immediate(null).chain((_) {
1465 if (_handlers.isEmpty) { 1555 if (_handlers.isEmpty) {
1466 fail('Unexpected ${request.method} request to ${request.path}.'); 1556 fail('Unexpected ${request.method} request to ${request.path}.');
1467 } 1557 }
1468 return _handlers.removeFirst(); 1558 return _handlers.removeFirst();
1469 }).transform((handler) { 1559 }).transform((handler) {
1470 handler(request, response); 1560 handler(request, response);
1471 }), 5000, "waiting for a handler for ${request.method} ${request.path}"); 1561 }), _SCHEDULE_TIMEOUT, "waiting for a handler for ${request.method} "
1562 "${request.path}");
1472 expect(future, completes); 1563 expect(future, completes);
1473 } 1564 }
1474 } 1565 }
1475 1566
1476 /** 1567 /**
1477 * Takes a simple data structure (composed of [Map]s, [List]s, scalar objects, 1568 * Takes a simple data structure (composed of [Map]s, [List]s, scalar objects,
1478 * and [Future]s) and recursively resolves all the [Future]s contained within. 1569 * and [Future]s) and recursively resolves all the [Future]s contained within.
1479 * Completes with the fully resolved structure. 1570 * Completes with the fully resolved structure.
1480 */ 1571 */
1481 Future _awaitObject(object) { 1572 Future _awaitObject(object) {
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after
1539 /// calling [completion] is unnecessary. 1630 /// calling [completion] is unnecessary.
1540 void expectLater(Future actual, matcher, {String reason, 1631 void expectLater(Future actual, matcher, {String reason,
1541 FailureHandler failureHandler, bool verbose: false}) { 1632 FailureHandler failureHandler, bool verbose: false}) {
1542 _schedule((_) { 1633 _schedule((_) {
1543 return actual.transform((value) { 1634 return actual.transform((value) {
1544 expect(value, matcher, reason: reason, failureHandler: failureHandler, 1635 expect(value, matcher, reason: reason, failureHandler: failureHandler,
1545 verbose: false); 1636 verbose: false);
1546 }); 1637 });
1547 }); 1638 });
1548 } 1639 }
OLDNEW
« no previous file with comments | « utils/tests/pub/pub_lish_test.dart ('k') | utils/tests/pub/validator_test.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698