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

Side by Side Diff: test/test_pub.dart

Issue 2184303002: Make pub strong-mode clean. (Closed) Base URL: git@github.com:dart-lang/pub.git@master
Patch Set: Code review changes Created 4 years, 4 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 unified diff | Download patch
« no previous file with comments | « test/pub_uploader_test.dart ('k') | no next file » | 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) 2013, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2013, 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 /// Test infrastructure for testing pub. 5 /// Test infrastructure for testing pub.
6 /// 6 ///
7 /// Unlike typical unit tests, most pub tests are integration tests that stage 7 /// Unlike typical unit tests, most pub tests are integration tests that stage
8 /// some stuff on the file system, run pub, and then validate the results. This 8 /// some stuff on the file system, run pub, and then validate the results. This
9 /// library provides an API to build tests like that. 9 /// library provides an API to build tests like that.
10 import 'dart:async'; 10 import 'dart:async';
11 import 'dart:convert'; 11 import 'dart:convert';
12 import 'dart:io'; 12 import 'dart:io';
13 import 'dart:math'; 13 import 'dart:math';
14 14
15 import 'package:async/async.dart';
15 import 'package:http/testing.dart'; 16 import 'package:http/testing.dart';
16 import 'package:path/path.dart' as p; 17 import 'package:path/path.dart' as p;
17 import 'package:pub/src/entrypoint.dart'; 18 import 'package:pub/src/entrypoint.dart';
18 import 'package:pub/src/exceptions.dart'; 19 import 'package:pub/src/exceptions.dart';
19 import 'package:pub/src/exit_codes.dart' as exit_codes; 20 import 'package:pub/src/exit_codes.dart' as exit_codes;
20 // TODO(rnystrom): Using "gitlib" as the prefix here is ugly, but "git" collides 21 // TODO(rnystrom): Using "gitlib" as the prefix here is ugly, but "git" collides
21 // with the git descriptor method. Maybe we should try to clean up the top level 22 // with the git descriptor method. Maybe we should try to clean up the top level
22 // scope a bit? 23 // scope a bit?
23 import 'package:pub/src/git.dart' as gitlib; 24 import 'package:pub/src/git.dart' as gitlib;
24 import 'package:pub/src/http.dart'; 25 import 'package:pub/src/http.dart';
(...skipping 128 matching lines...) Expand 10 before | Expand all | Expand 10 after
153 warning: warning, exitCode: exitCode, environment: environment); 154 warning: warning, exitCode: exitCode, environment: environment);
154 } 155 }
155 156
156 /// Schedules starting the "pub [global] run" process and validates the 157 /// Schedules starting the "pub [global] run" process and validates the
157 /// expected startup output. 158 /// expected startup output.
158 /// 159 ///
159 /// If [global] is `true`, this invokes "pub global run", otherwise it does 160 /// If [global] is `true`, this invokes "pub global run", otherwise it does
160 /// "pub run". 161 /// "pub run".
161 /// 162 ///
162 /// Returns the `pub run` process. 163 /// Returns the `pub run` process.
163 ScheduledProcess pubRun({bool global: false, Iterable<String> args}) { 164 PubProcess pubRun({bool global: false, Iterable<String> args}) {
164 var pubArgs = global ? ["global", "run"] : ["run"]; 165 var pubArgs = global ? ["global", "run"] : ["run"];
165 pubArgs.addAll(args); 166 pubArgs.addAll(args);
166 var pub = startPub(args: pubArgs); 167 var pub = startPub(args: pubArgs);
167 168
168 // Loading sources and transformers isn't normally printed, but the pub test 169 // Loading sources and transformers isn't normally printed, but the pub test
169 // infrastructure runs pub in verbose mode, which enables this. 170 // infrastructure runs pub in verbose mode, which enables this.
170 pub.stdout.expect(consumeWhile(startsWith("Loading"))); 171 pub.stdout.expect(consumeWhile(startsWith("Loading")));
171 172
172 return pub; 173 return pub;
173 } 174 }
(...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after
236 assert(output == null || outputJson == null); 237 assert(output == null || outputJson == null);
237 238
238 var pub = startPub(args: args, environment: environment); 239 var pub = startPub(args: args, environment: environment);
239 pub.shouldExit(exitCode); 240 pub.shouldExit(exitCode);
240 241
241 expect(() async { 242 expect(() async {
242 var actualOutput = (await pub.stdoutStream().toList()).join("\n"); 243 var actualOutput = (await pub.stdoutStream().toList()).join("\n");
243 var actualError = (await pub.stderrStream().toList()).join("\n"); 244 var actualError = (await pub.stderrStream().toList()).join("\n");
244 var actualSilent = (await pub.silentStream().toList()).join("\n"); 245 var actualSilent = (await pub.silentStream().toList()).join("\n");
245 246
246 var failures = []; 247 var failures = <String>[];
247 if (outputJson == null) { 248 if (outputJson == null) {
248 _validateOutput(failures, 'stdout', output, actualOutput); 249 _validateOutput(failures, 'stdout', output, actualOutput);
249 } else { 250 } else {
250 _validateOutputJson( 251 _validateOutputJson(
251 failures, 'stdout', await awaitObject(outputJson), actualOutput); 252 failures, 'stdout', await awaitObject(outputJson), actualOutput);
252 } 253 }
253 254
254 _validateOutput(failures, 'stderr', error, actualError); 255 _validateOutput(failures, 'stderr', error, actualError);
255 _validateOutput(failures, 'silent', silent, actualSilent); 256 _validateOutput(failures, 'silent', silent, actualSilent);
256 257
257 if (!failures.isEmpty) throw new TestFailure(failures.join('\n')); 258 if (!failures.isEmpty) throw new TestFailure(failures.join('\n'));
258 }(), completes); 259 }(), completes);
259 } 260 }
260 261
261 /// Like [startPub], but runs `pub lish` in particular with [server] used both 262 /// Like [startPub], but runs `pub lish` in particular with [server] used both
262 /// as the OAuth2 server (with "/token" as the token endpoint) and as the 263 /// as the OAuth2 server (with "/token" as the token endpoint) and as the
263 /// package server. 264 /// package server.
264 /// 265 ///
265 /// Any futures in [args] will be resolved before the process is started. 266 /// Any futures in [args] will be resolved before the process is started.
266 ScheduledProcess startPublish(ScheduledServer server, {List args}) { 267 PubProcess startPublish(ScheduledServer server, {List args}) {
267 var tokenEndpoint = server.url.then((url) => 268 var tokenEndpoint = server.url.then((url) =>
268 url.resolve('/token').toString()); 269 url.resolve('/token').toString());
269 if (args == null) args = []; 270 if (args == null) args = [];
270 args = flatten(['lish', '--server', tokenEndpoint, args]); 271 args = ['lish', '--server', tokenEndpoint]..addAll(args);
271 return startPub(args: args, tokenEndpoint: tokenEndpoint); 272 return startPub(args: args, tokenEndpoint: tokenEndpoint);
272 } 273 }
273 274
274 /// Handles the beginning confirmation process for uploading a packages. 275 /// Handles the beginning confirmation process for uploading a packages.
275 /// 276 ///
276 /// Ensures that the right output is shown and then enters "y" to confirm the 277 /// Ensures that the right output is shown and then enters "y" to confirm the
277 /// upload. 278 /// upload.
278 void confirmPublish(ScheduledProcess pub) { 279 void confirmPublish(ScheduledProcess pub) {
279 // TODO(rnystrom): This is overly specific and inflexible regarding different 280 // TODO(rnystrom): This is overly specific and inflexible regarding different
280 // test packages. Should validate this a little more loosely. 281 // test packages. Should validate this a little more loosely.
(...skipping 23 matching lines...) Expand all
304 } 305 }
305 306
306 if (globalServer != null) { 307 if (globalServer != null) {
307 environment['PUB_HOSTED_URL'] = 308 environment['PUB_HOSTED_URL'] =
308 "http://localhost:${await globalServer.port}"; 309 "http://localhost:${await globalServer.port}";
309 } 310 }
310 311
311 return environment; 312 return environment;
312 } 313 }
313 314
314 /// Starts a Pub process and returns a [ScheduledProcess] that supports 315 /// Starts a Pub process and returns a [PubProcess] that supports interaction
315 /// interaction with that process. 316 /// with that process.
316 /// 317 ///
317 /// Any futures in [args] will be resolved before the process is started. 318 /// Any futures in [args] will be resolved before the process is started.
318 /// 319 ///
319 /// If [environment] is given, any keys in it will override the environment 320 /// If [environment] is given, any keys in it will override the environment
320 /// variables passed to the spawned process. 321 /// variables passed to the spawned process.
321 ScheduledProcess startPub({List args, Future<String> tokenEndpoint, 322 PubProcess startPub({List args, Future<String> tokenEndpoint,
322 Map<String, String> environment}) { 323 Map<String, String> environment}) {
324 args ??= [];
325
323 schedule(() { 326 schedule(() {
324 ensureDir(_pathInSandbox(appPath)); 327 ensureDir(_pathInSandbox(appPath));
325 }, "ensuring $appPath exists"); 328 }, "ensuring $appPath exists");
326 329
327 // Find a Dart executable we can use to spawn. Use the same one that was 330 // Find a Dart executable we can use to spawn. Use the same one that was
328 // used to run this script itself. 331 // used to run this script itself.
329 var dartBin = Platform.executable; 332 var dartBin = Platform.executable;
330 333
331 // If the executable looks like a path, get its full path. That way we 334 // If the executable looks like a path, get its full path. That way we
332 // can still find it when we spawn it with a different working directory. 335 // can still find it when we spawn it with a different working directory.
333 if (dartBin.contains(Platform.pathSeparator)) { 336 if (dartBin.contains(Platform.pathSeparator)) {
334 dartBin = p.absolute(dartBin); 337 dartBin = p.absolute(dartBin);
335 } 338 }
336 339
337 // If there's a snapshot available, use it. The user is responsible for 340 // If there's a snapshot available, use it. The user is responsible for
338 // ensuring this is up-to-date.. 341 // ensuring this is up-to-date..
339 // 342 //
340 // TODO(nweiz): When the test runner supports plugins, create one to 343 // TODO(nweiz): When the test runner supports plugins, create one to
341 // auto-generate the snapshot before each run. 344 // auto-generate the snapshot before each run.
342 var pubPath = p.absolute(p.join(pubRoot, 'bin/pub.dart')); 345 var pubPath = p.absolute(p.join(pubRoot, 'bin/pub.dart'));
343 if (fileExists('$pubPath.snapshot')) pubPath += '.snapshot'; 346 if (fileExists('$pubPath.snapshot')) pubPath += '.snapshot';
344 347
345 var dartArgs = [ 348 var dartArgs = <dynamic>[
346 '--package-root=${p.toUri(p.absolute(p.fromUri(Platform.packageRoot)))}', 349 '--package-root=${p.toUri(p.absolute(p.fromUri(Platform.packageRoot)))}',
347 pubPath, 350 pubPath,
348 '--verbose' 351 '--verbose'
349 ]..addAll(args); 352 ]..addAll(args);
350 353
351 if (tokenEndpoint == null) tokenEndpoint = new Future.value(); 354 if (tokenEndpoint == null) tokenEndpoint = new Future.value();
352 var environmentFuture = tokenEndpoint 355 var environmentFuture = tokenEndpoint
353 .then((tokenEndpoint) => getPubTestEnvironment(tokenEndpoint)) 356 .then((tokenEndpoint) => getPubTestEnvironment(tokenEndpoint))
354 .then((pubEnvironment) { 357 .then((pubEnvironment) {
355 if (environment != null) pubEnvironment.addAll(environment); 358 if (environment != null) pubEnvironment.addAll(environment);
(...skipping 18 matching lines...) Expand all
374 {workingDirectory, environment, String description, 377 {workingDirectory, environment, String description,
375 Encoding encoding: UTF8}) 378 Encoding encoding: UTF8})
376 : super.start(executable, arguments, 379 : super.start(executable, arguments,
377 workingDirectory: workingDirectory, 380 workingDirectory: workingDirectory,
378 environment: environment, 381 environment: environment,
379 description: description, 382 description: description,
380 encoding: encoding); 383 encoding: encoding);
381 384
382 Stream<Pair<log.Level, String>> _logStream() { 385 Stream<Pair<log.Level, String>> _logStream() {
383 if (_log == null) { 386 if (_log == null) {
384 _log = mergeStreams( 387 _log = StreamGroup.merge([
385 _outputToLog(super.stdoutStream(), log.Level.MESSAGE), 388 _outputToLog(super.stdoutStream(), log.Level.MESSAGE),
386 _outputToLog(super.stderrStream(), log.Level.ERROR)); 389 _outputToLog(super.stderrStream(), log.Level.ERROR)
390 ]);
387 } 391 }
388 392
389 var pair = tee(_log); 393 var logs = StreamSplitter.splitFrom(_log);
390 _log = pair.first; 394 _log = logs.first;
391 return pair.last; 395 return logs.last;
392 } 396 }
393 397
394 final _logLineRegExp = new RegExp(r"^([A-Z ]{4})[:|] (.*)$"); 398 final _logLineRegExp = new RegExp(r"^([A-Z ]{4})[:|] (.*)$");
395 final _logLevels = [ 399 final _logLevels = [
396 log.Level.ERROR, log.Level.WARNING, log.Level.MESSAGE, log.Level.IO, 400 log.Level.ERROR, log.Level.WARNING, log.Level.MESSAGE, log.Level.IO,
397 log.Level.SOLVER, log.Level.FINE 401 log.Level.SOLVER, log.Level.FINE
398 ].fold(<String, log.Level>{}, (levels, level) { 402 ].fold(<String, log.Level>{}, (levels, level) {
399 levels[level.name] = level; 403 levels[level.name] = level;
400 return levels; 404 return levels;
401 }); 405 });
(...skipping 13 matching lines...) Expand all
415 } 419 }
416 420
417 Stream<String> stdoutStream() { 421 Stream<String> stdoutStream() {
418 if (_stdout == null) { 422 if (_stdout == null) {
419 _stdout = _logStream().expand((entry) { 423 _stdout = _logStream().expand((entry) {
420 if (entry.first != log.Level.MESSAGE) return []; 424 if (entry.first != log.Level.MESSAGE) return [];
421 return [entry.last]; 425 return [entry.last];
422 }); 426 });
423 } 427 }
424 428
425 var pair = tee(_stdout); 429 var stdouts = StreamSplitter.splitFrom(_stdout);
426 _stdout = pair.first; 430 _stdout = stdouts.first;
427 return pair.last; 431 return stdouts.last;
428 } 432 }
429 433
430 Stream<String> stderrStream() { 434 Stream<String> stderrStream() {
431 if (_stderr == null) { 435 if (_stderr == null) {
432 _stderr = _logStream().expand((entry) { 436 _stderr = _logStream().expand((entry) {
433 if (entry.first != log.Level.ERROR && 437 if (entry.first != log.Level.ERROR &&
434 entry.first != log.Level.WARNING) { 438 entry.first != log.Level.WARNING) {
435 return []; 439 return [];
436 } 440 }
437 return [entry.last]; 441 return [entry.last];
438 }); 442 });
439 } 443 }
440 444
441 var pair = tee(_stderr); 445 var stderrs = StreamSplitter.splitFrom(_stderr);
442 _stderr = pair.first; 446 _stderr = stderrs.first;
443 return pair.last; 447 return stderrs.last;
444 } 448 }
445 449
446 /// A stream of log messages that are silent by default. 450 /// A stream of log messages that are silent by default.
447 Stream<String> silentStream() { 451 Stream<String> silentStream() {
448 if (_silent == null) { 452 if (_silent == null) {
449 _silent = _logStream().expand((entry) { 453 _silent = _logStream().expand((entry) {
450 if (entry.first == log.Level.MESSAGE) return []; 454 if (entry.first == log.Level.MESSAGE) return [];
451 if (entry.first == log.Level.ERROR) return []; 455 if (entry.first == log.Level.ERROR) return [];
452 if (entry.first == log.Level.WARNING) return []; 456 if (entry.first == log.Level.WARNING) return [];
453 return [entry.last]; 457 return [entry.last];
454 }); 458 });
455 } 459 }
456 460
457 var pair = tee(_silent); 461 var silents = StreamSplitter.splitFrom(_silent);
458 _silent = pair.first; 462 _silent = silents.first;
459 return pair.last; 463 return silents.last;
460 } 464 }
461 } 465 }
462 466
463 /// Fails the current test if Git is not installed. 467 /// Fails the current test if Git is not installed.
464 /// 468 ///
465 /// We require machines running these tests to have git installed. This 469 /// We require machines running these tests to have git installed. This
466 /// validation gives an easier-to-understand error when that requirement isn't 470 /// validation gives an easier-to-understand error when that requirement isn't
467 /// met than just failing in the middle of a test when pub invokes git. 471 /// met than just failing in the middle of a test when pub invokes git.
468 /// 472 ///
469 /// This also increases the [Schedule] timeout to 30 seconds on Windows, 473 /// This also increases the [Schedule] timeout to 30 seconds on Windows,
(...skipping 104 matching lines...) Expand 10 before | Expand all | Expand 10 after
574 var oldInnerClient = innerHttpClient; 578 var oldInnerClient = innerHttpClient;
575 innerHttpClient = client; 579 innerHttpClient = client;
576 currentSchedule.onComplete.schedule(() { 580 currentSchedule.onComplete.schedule(() {
577 innerHttpClient = oldInnerClient; 581 innerHttpClient = oldInnerClient;
578 }, 'de-activating the mock client'); 582 }, 'de-activating the mock client');
579 } 583 }
580 584
581 /// Describes a map representing a library package with the given [name], 585 /// Describes a map representing a library package with the given [name],
582 /// [version], and [dependencies]. 586 /// [version], and [dependencies].
583 Map packageMap(String name, String version, [Map dependencies]) { 587 Map packageMap(String name, String version, [Map dependencies]) {
584 var package = { 588 var package = <String, dynamic>{
585 "name": name, 589 "name": name,
586 "version": version, 590 "version": version,
587 "author": "Natalie Weizenbaum <nweiz@google.com>", 591 "author": "Natalie Weizenbaum <nweiz@google.com>",
588 "homepage": "http://pub.dartlang.org", 592 "homepage": "http://pub.dartlang.org",
589 "description": "A package, I guess." 593 "description": "A package, I guess."
590 }; 594 };
591 595
592 if (dependencies != null) package["dependencies"] = dependencies; 596 if (dependencies != null) package["dependencies"] = dependencies;
593 597
594 return package; 598 return package;
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after
656 var actualLines = actual.split("\n"); 660 var actualLines = actual.split("\n");
657 var expectedLines = expected.split("\n"); 661 var expectedLines = expected.split("\n");
658 662
659 // Strip off the last line. This lets us have expected multiline strings 663 // Strip off the last line. This lets us have expected multiline strings
660 // where the closing ''' is on its own line. It also fixes '' expected output 664 // where the closing ''' is on its own line. It also fixes '' expected output
661 // to expect zero lines of output, not a single empty line. 665 // to expect zero lines of output, not a single empty line.
662 if (expectedLines.last.trim() == '') { 666 if (expectedLines.last.trim() == '') {
663 expectedLines.removeLast(); 667 expectedLines.removeLast();
664 } 668 }
665 669
666 var results = []; 670 var results = <String>[];
667 var failed = false; 671 var failed = false;
668 672
669 // Compare them line by line to see which ones match. 673 // Compare them line by line to see which ones match.
670 var length = max(expectedLines.length, actualLines.length); 674 var length = max(expectedLines.length, actualLines.length);
671 for (var i = 0; i < length; i++) { 675 for (var i = 0; i < length; i++) {
672 if (i >= actualLines.length) { 676 if (i >= actualLines.length) {
673 // Missing output. 677 // Missing output.
674 failed = true; 678 failed = true;
675 results.add('? ${expectedLines[i]}'); 679 results.add('? ${expectedLines[i]}');
676 } else if (i >= expectedLines.length) { 680 } else if (i >= expectedLines.length) {
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after
721 725
722 /// A function that creates a [Validator] subclass. 726 /// A function that creates a [Validator] subclass.
723 typedef Validator ValidatorCreator(Entrypoint entrypoint); 727 typedef Validator ValidatorCreator(Entrypoint entrypoint);
724 728
725 /// Schedules a single [Validator] to run on the [appPath]. 729 /// Schedules a single [Validator] to run on the [appPath].
726 /// 730 ///
727 /// Returns a scheduled Future that contains the errors and warnings produced 731 /// Returns a scheduled Future that contains the errors and warnings produced
728 /// by that validator. 732 /// by that validator.
729 Future<Pair<List<String>, List<String>>> schedulePackageValidation( 733 Future<Pair<List<String>, List<String>>> schedulePackageValidation(
730 ValidatorCreator fn) { 734 ValidatorCreator fn) {
731 return schedule(() { 735 return schedule/*<Future<Pair<List<String>, List<String>>>>*/(() async {
732 var cache = new SystemCache(rootDir: p.join(sandboxDir, cachePath)); 736 var cache = new SystemCache(rootDir: p.join(sandboxDir, cachePath));
733 return new Future.sync(() { 737 var validator = fn(new Entrypoint(p.join(sandboxDir, appPath), cache));
734 var validator = fn(new Entrypoint(p.join(sandboxDir, appPath), cache)); 738 await validator.validate();
735 return validator.validate().then((_) { 739 return new Pair(validator.errors, validator.warnings);
736 return new Pair(validator.errors, validator.warnings);
737 });
738 });
739 }, "validating package"); 740 }, "validating package");
740 } 741 }
741 742
742 /// A matcher that matches a Pair. 743 /// A matcher that matches a Pair.
743 Matcher pairOf(Matcher firstMatcher, Matcher lastMatcher) => 744 Matcher pairOf(Matcher firstMatcher, Matcher lastMatcher) =>
744 new _PairMatcher(firstMatcher, lastMatcher); 745 new _PairMatcher(firstMatcher, lastMatcher);
745 746
746 class _PairMatcher extends Matcher { 747 class _PairMatcher extends Matcher {
747 final Matcher _firstMatcher; 748 final Matcher _firstMatcher;
748 final Matcher _lastMatcher; 749 final Matcher _lastMatcher;
749 750
750 _PairMatcher(this._firstMatcher, this._lastMatcher); 751 _PairMatcher(this._firstMatcher, this._lastMatcher);
751 752
752 bool matches(item, Map matchState) { 753 bool matches(item, Map matchState) {
753 if (item is! Pair) return false; 754 if (item is! Pair) return false;
754 return _firstMatcher.matches(item.first, matchState) && 755 return _firstMatcher.matches(item.first, matchState) &&
755 _lastMatcher.matches(item.last, matchState); 756 _lastMatcher.matches(item.last, matchState);
756 } 757 }
757 758
758 Description describe(Description description) { 759 Description describe(Description description) {
759 return description.addAll("(", ", ", ")", [_firstMatcher, _lastMatcher]); 760 return description.addAll("(", ", ", ")", [_firstMatcher, _lastMatcher]);
760 } 761 }
761 } 762 }
762 763
763 /// A [StreamMatcher] that matches multiple lines of output. 764 /// A [StreamMatcher] that matches multiple lines of output.
764 StreamMatcher emitsLines(String output) => inOrder(output.split("\n")); 765 StreamMatcher emitsLines(String output) => inOrder(output.split("\n"));
OLDNEW
« no previous file with comments | « test/pub_uploader_test.dart ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698