OLD | NEW |
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 library pub_update_test; | 5 library pub_update_test; |
6 | 6 |
7 import 'dart:async'; | 7 import 'dart:async'; |
8 import 'dart:io'; | 8 import 'dart:io'; |
9 | 9 |
10 import 'package:unittest/unittest.dart'; | 10 import 'package:unittest/unittest.dart'; |
11 | 11 |
12 import '../../pub/lock_file.dart'; | 12 import '../../pub/lock_file.dart'; |
13 import '../../pub/package.dart'; | 13 import '../../pub/package.dart'; |
14 import '../../pub/pubspec.dart'; | 14 import '../../pub/pubspec.dart'; |
15 import '../../pub/source.dart'; | 15 import '../../pub/source.dart'; |
16 import '../../pub/source_registry.dart'; | 16 import '../../pub/source_registry.dart'; |
17 import '../../pub/system_cache.dart'; | 17 import '../../pub/system_cache.dart'; |
18 import '../../pub/utils.dart'; | 18 import '../../pub/utils.dart'; |
19 import '../../pub/version.dart'; | 19 import '../../pub/version.dart'; |
20 import '../../pub/version_solver.dart'; | 20 import '../../pub/solver/version_solver.dart'; |
21 import 'test_pub.dart'; | 21 import 'test_pub.dart'; |
22 | 22 |
23 Matcher noVersion(List<String> packages) { | 23 Matcher noVersion(List<String> packages) { |
24 return predicate((x) { | 24 return predicate((x) { |
25 if (x is! NoVersionException) return false; | 25 if (x is! NoVersionException) return false; |
26 | 26 |
27 // Make sure the error string mentions the conflicting dependers. | 27 // Make sure the error string mentions the conflicting dependers. |
28 var message = x.toString(); | 28 var message = x.toString(); |
29 return packages.every((package) => message.contains(package)); | 29 return packages.every((package) => message.contains(package)); |
30 }, "is a NoVersionException"); | 30 }, "is a NoVersionException"); |
(...skipping 14 matching lines...) Expand all Loading... |
45 if (x is! DescriptionMismatchException) return false; | 45 if (x is! DescriptionMismatchException) return false; |
46 | 46 |
47 // Make sure the error string mentions the conflicting dependers. | 47 // Make sure the error string mentions the conflicting dependers. |
48 if (!x.toString().contains(package1)) return false; | 48 if (!x.toString().contains(package1)) return false; |
49 if (!x.toString().contains(package2)) return false; | 49 if (!x.toString().contains(package2)) return false; |
50 | 50 |
51 return true; | 51 return true; |
52 }, "is a DescriptionMismatchException"); | 52 }, "is a DescriptionMismatchException"); |
53 } | 53 } |
54 | 54 |
55 final couldNotSolve = predicate((x) => x is CouldNotSolveException, | 55 // If no solution can be found, the solver just reports the last failure that |
56 "is a CouldNotSolveException"); | 56 // happened during propagation. Since we don't specify the order that solutions |
| 57 // are tried, this just validates that *some* failure occurred, but not which. |
| 58 final couldNotSolve = predicate((x) => x is SolverFailure, |
| 59 "is a SolverFailure"); |
57 | 60 |
58 Matcher sourceMismatch(String package1, String package2) { | 61 Matcher sourceMismatch(String package1, String package2) { |
59 return predicate((x) { | 62 return predicate((x) { |
60 if (x is! SourceMismatchException) return false; | 63 if (x is! SourceMismatchException) return false; |
61 | 64 |
62 // Make sure the error string mentions the conflicting dependers. | 65 // Make sure the error string mentions the conflicting dependers. |
63 if (!x.toString().contains(package1)) return false; | 66 if (!x.toString().contains(package1)) return false; |
64 if (!x.toString().contains(package2)) return false; | 67 if (!x.toString().contains(package2)) return false; |
65 | 68 |
66 return true; | 69 return true; |
(...skipping 131 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
198 'bar 1.0.2': {}, | 201 'bar 1.0.2': {}, |
199 'baz 1.0.0': {} | 202 'baz 1.0.0': {} |
200 }, lockfile: { | 203 }, lockfile: { |
201 'baz': '1.0.0' | 204 'baz': '1.0.0' |
202 }, result: { | 205 }, result: { |
203 'myapp from root': '0.0.0', | 206 'myapp from root': '0.0.0', |
204 'foo': '1.0.2', | 207 'foo': '1.0.2', |
205 'bar': '1.0.2' | 208 'bar': '1.0.2' |
206 }); | 209 }); |
207 | 210 |
| 211 testResolve('unlocks dependencies if necessary to ensure that a new ' |
| 212 'dependency is satisfied', { |
| 213 'myapp 0.0.0': { |
| 214 'foo': 'any', |
| 215 'newdep': 'any' |
| 216 }, |
| 217 'foo 1.0.0': { 'bar': '<2.0.0' }, |
| 218 'bar 1.0.0': { 'baz': '<2.0.0' }, |
| 219 'baz 1.0.0': { 'qux': '<2.0.0' }, |
| 220 'qux 1.0.0': {}, |
| 221 'foo 2.0.0': { 'bar': '<3.0.0' }, |
| 222 'bar 2.0.0': { 'baz': '<3.0.0' }, |
| 223 'baz 2.0.0': { 'qux': '<3.0.0' }, |
| 224 'qux 2.0.0': {}, |
| 225 'newdep 2.0.0': { 'baz': '>=1.5.0' } |
| 226 }, lockfile: { |
| 227 'foo': '1.0.0', |
| 228 'bar': '1.0.0', |
| 229 'baz': '1.0.0', |
| 230 'qux': '1.0.0' |
| 231 }, result: { |
| 232 'myapp from root': '0.0.0', |
| 233 'foo': '2.0.0', |
| 234 'bar': '2.0.0', |
| 235 'baz': '2.0.0', |
| 236 'qux': '1.0.0', |
| 237 'newdep': '2.0.0' |
| 238 }); |
| 239 |
208 testResolve('circular dependency', { | 240 testResolve('circular dependency', { |
209 'myapp 1.0.0': { | 241 'myapp 1.0.0': { |
210 'foo': '1.0.0' | 242 'foo': '1.0.0' |
211 }, | 243 }, |
212 'foo 1.0.0': { | 244 'foo 1.0.0': { |
213 'bar': '1.0.0' | 245 'bar': '1.0.0' |
214 }, | 246 }, |
215 'bar 1.0.0': { | 247 'bar 1.0.0': { |
216 'foo': '1.0.0' | 248 'foo': '1.0.0' |
217 } | 249 } |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
258 } | 290 } |
259 }, error: sourceMismatch('foo', 'bar')); | 291 }, error: sourceMismatch('foo', 'bar')); |
260 | 292 |
261 testResolve('dependency back onto root package with wrong version', { | 293 testResolve('dependency back onto root package with wrong version', { |
262 'myapp 1.0.0': { | 294 'myapp 1.0.0': { |
263 'foo': '1.0.0' | 295 'foo': '1.0.0' |
264 }, | 296 }, |
265 'foo 1.0.0': { | 297 'foo 1.0.0': { |
266 'myapp': '<1.0.0' | 298 'myapp': '<1.0.0' |
267 } | 299 } |
268 }, error: disjointConstraint(['foo'])); | 300 }, error: couldNotSolve); |
269 | 301 |
270 testResolve('no version that matches requirement', { | 302 testResolve('no version that matches requirement', { |
271 'myapp 0.0.0': { | 303 'myapp 0.0.0': { |
272 'foo': '>=1.0.0 <2.0.0' | 304 'foo': '>=1.0.0 <2.0.0' |
273 }, | 305 }, |
274 'foo 2.0.0': {}, | 306 'foo 2.0.0': {}, |
275 'foo 2.1.3': {} | 307 'foo 2.1.3': {} |
276 }, error: noVersion(['myapp'])); | 308 }, error: noVersion(['myapp'])); |
277 | 309 |
278 testResolve('no version that matches combined constraint', { | 310 testResolve('no version that matches combined constraint', { |
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
328 'foo 1.0.0': { | 360 'foo 1.0.0': { |
329 'shared': '1.0.0' | 361 'shared': '1.0.0' |
330 }, | 362 }, |
331 'bar 1.0.0': { | 363 'bar 1.0.0': { |
332 'shared from mock2': '1.0.0' | 364 'shared from mock2': '1.0.0' |
333 }, | 365 }, |
334 'shared 1.0.0': {}, | 366 'shared 1.0.0': {}, |
335 'shared 1.0.0 from mock2': {} | 367 'shared 1.0.0 from mock2': {} |
336 }, error: sourceMismatch('foo', 'bar')); | 368 }, error: sourceMismatch('foo', 'bar')); |
337 | 369 |
338 testResolve('unstable dependency graph', { | 370 testResolve('circular dependency on older version', { |
339 'myapp 0.0.0': { | 371 'myapp 0.0.0': { |
340 'a': '>=1.0.0' | 372 'a': '>=1.0.0' |
341 }, | 373 }, |
342 'a 1.0.0': {}, | 374 'a 1.0.0': {}, |
343 'a 2.0.0': { | 375 'a 2.0.0': { |
344 'b': '1.0.0' | 376 'b': '1.0.0' |
345 }, | 377 }, |
346 'b 1.0.0': { | 378 'b 1.0.0': { |
347 'a': '1.0.0' | 379 'a': '1.0.0' |
348 } | 380 } |
| 381 }, result: { |
| 382 'myapp from root': '0.0.0', |
| 383 'a': '1.0.0' |
| 384 }, requiresBacktracking: true); |
| 385 |
| 386 testResolve('unsolvable', { |
| 387 'myapp 0.0.0': { |
| 388 'a': 'any', |
| 389 'b': 'any' |
| 390 }, |
| 391 'a 1.0.0': { |
| 392 'b': '1.0.0' |
| 393 }, |
| 394 'a 2.0.0': { |
| 395 'b': '2.0.0' |
| 396 }, |
| 397 'b 1.0.0': { |
| 398 'a': '2.0.0' |
| 399 }, |
| 400 'b 2.0.0': { |
| 401 'a': '1.0.0' |
| 402 } |
349 }, error: couldNotSolve); | 403 }, error: couldNotSolve); |
350 | 404 |
351 group('dev dependencies', () { | 405 group('dev dependencies', () { |
352 testResolve("includes root package's dev dependencies", { | 406 testResolve("includes root package's dev dependencies", { |
353 'myapp 1.0.0': { | 407 'myapp 1.0.0': { |
354 '(dev) foo': '1.0.0', | 408 '(dev) foo': '1.0.0', |
355 '(dev) bar': '1.0.0' | 409 '(dev) bar': '1.0.0' |
356 }, | 410 }, |
357 'foo 1.0.0': {}, | 411 'foo 1.0.0': {}, |
358 'bar 1.0.0': {} | 412 'bar 1.0.0': {} |
(...skipping 23 matching lines...) Expand all Loading... |
382 }, | 436 }, |
383 'foo 1.0.0': { | 437 'foo 1.0.0': { |
384 '(dev) bar': '1.0.0' | 438 '(dev) bar': '1.0.0' |
385 }, | 439 }, |
386 'bar 1.0.0': {} | 440 'bar 1.0.0': {} |
387 }, result: { | 441 }, result: { |
388 'myapp from root': '1.0.0', | 442 'myapp from root': '1.0.0', |
389 'foo': '1.0.0' | 443 'foo': '1.0.0' |
390 }); | 444 }); |
391 }); | 445 }); |
| 446 |
| 447 // Only one version of baz, so foo and bar will have to downgrade until they |
| 448 // reach it. |
| 449 testResolve('simple backtracking', { |
| 450 'myapp 0.0.0': {'foo': 'any'}, |
| 451 'foo 1.0.0': {'bar': '1.0.0'}, |
| 452 'foo 2.0.0': {'bar': '2.0.0'}, |
| 453 'foo 3.0.0': {'bar': '3.0.0'}, |
| 454 'bar 1.0.0': {'baz': 'any'}, |
| 455 'bar 2.0.0': {'baz': '2.0.0'}, |
| 456 'bar 3.0.0': {'baz': '3.0.0'}, |
| 457 'baz 1.0.0': {} |
| 458 }, result: { |
| 459 'myapp from root': '0.0.0', |
| 460 'foo': '1.0.0', |
| 461 'bar': '1.0.0', |
| 462 'baz': '1.0.0' |
| 463 }, requiresBacktracking: true); |
| 464 |
| 465 // This sets up a hundred versions of foo and bar, 0.0.0 through 9.9.0. Each |
| 466 // version of foo depends on a baz with the same major version. Each version |
| 467 // of bar depends on a baz with the same minor version. There is only one |
| 468 // version of baz, 0.0.0, so only older versions of foo and bar will |
| 469 // satisfy it. |
| 470 var map = { |
| 471 'myapp 0.0.0': { |
| 472 'foo': 'any', |
| 473 'bar': 'any' |
| 474 }, |
| 475 'baz 0.0.0': {} |
| 476 }; |
| 477 |
| 478 for (var i = 0; i < 10; i++) { |
| 479 for (var j = 0; j < 10; j++) { |
| 480 map['foo $i.$j.0'] = {'baz': '$i.0.0'}; |
| 481 map['bar $i.$j.0'] = {'baz': '0.$j.0'}; |
| 482 } |
| 483 } |
| 484 |
| 485 testResolve('complex backtrack', map, result: { |
| 486 'myapp from root': '0.0.0', |
| 487 'foo': '0.9.0', |
| 488 'bar': '9.0.0', |
| 489 'baz': '0.0.0' |
| 490 }, requiresBacktracking: true); |
392 } | 491 } |
393 | 492 |
394 // TODO(rnystrom): More stuff to test: | 493 // TODO(rnystrom): More stuff to test: |
395 // - Depending on a non-existent package. | 494 // - Depending on a non-existent package. |
396 // - Test that only a certain number requests are sent to the mock source so we | |
397 // can keep track of server traffic. | |
398 | 495 |
399 testResolve(description, packages, {lockfile, result, Matcher error}) { | 496 testResolve(description, packages, |
| 497 {lockfile, result, Matcher error, bool requiresBacktracking}) { |
| 498 _testResolveWithSolver(description, packages, |
| 499 allowBacktracking: false, lockfile: lockfile, result: result, error: error
, |
| 500 requiresBacktracking: requiresBacktracking); |
| 501 _testResolveWithSolver(description, packages, |
| 502 allowBacktracking: true, lockfile: lockfile, result: result, error: error, |
| 503 requiresBacktracking: requiresBacktracking); |
| 504 } |
| 505 |
| 506 _testResolveWithSolver(description, packages, {bool allowBacktracking, lockfile, |
| 507 result, Matcher error, bool requiresBacktracking}) { |
| 508 if (requiresBacktracking == null) requiresBacktracking = false; |
| 509 |
| 510 if (allowBacktracking) { |
| 511 description = 'backtracking solver $description'; |
| 512 } else { |
| 513 description = 'greedy solver $description'; |
| 514 |
| 515 // The greedy solver should fail any graph that does require backtracking. |
| 516 if (requiresBacktracking) { |
| 517 result = null; |
| 518 error = couldNotSolve; |
| 519 } |
| 520 } |
| 521 |
400 test(description, () { | 522 test(description, () { |
401 var cache = new SystemCache('.'); | 523 var cache = new SystemCache('.'); |
402 source1 = new MockSource('mock1'); | 524 source1 = new MockSource('mock1'); |
403 source2 = new MockSource('mock2'); | 525 source2 = new MockSource('mock2'); |
404 cache.register(source1); | 526 cache.register(source1); |
405 cache.register(source2); | 527 cache.register(source2); |
406 cache.sources.setDefault(source1.name); | 528 cache.sources.setDefault(source1.name); |
407 | 529 |
408 // Build the test package graph. | 530 // Build the test package graph. |
409 var root; | 531 var root; |
410 packages.forEach((nameVersion, dependencies) { | 532 packages.forEach((nameVersion, dependencies) { |
411 var parsed = parseSource(nameVersion, (isDev, nameVersion, source) { | 533 var parsed = parseSource(nameVersion, (isDev, nameVersion, source) { |
412 var parts = nameVersion.split(' '); | 534 var parts = nameVersion.split(' '); |
413 var name = parts[0]; | 535 var name = parts[0]; |
414 var version = parts[1]; | 536 var version = parts[1]; |
415 | 537 |
416 var package = source1.mockPackage(name, version, dependencies); | 538 var package = mockPackage(name, version, dependencies); |
417 if (name == 'myapp') { | 539 if (name == 'myapp') { |
418 // Don't add the root package to the server, so we can verify that Pub | 540 // Don't add the root package to the server, so we can verify that Pub |
419 // doesn't try to look up information about the local package on the | 541 // doesn't try to look up information about the local package on the |
420 // remote server. | 542 // remote server. |
421 root = package; | 543 root = package; |
422 } else { | 544 } else { |
423 source.addPackage(package); | 545 source.addPackage(name, package); |
424 } | 546 } |
425 }); | 547 }); |
426 }); | 548 }); |
427 | 549 |
428 // Clean up the expectation. | 550 // Clean up the expectation. |
429 if (result != null) { | 551 if (result != null) { |
430 var newResult = {}; | 552 var newResult = {}; |
431 result.forEach((name, version) { | 553 result.forEach((name, version) { |
432 parseSource(name, (isDev, name, source) { | 554 parseSource(name, (isDev, name, source) { |
433 version = new Version.parse(version); | 555 version = new Version.parse(version); |
434 newResult[name] = new PackageId(name, source, version, name); | 556 newResult[name] = new PackageId(name, source, version, name); |
435 }); | 557 }); |
436 }); | 558 }); |
437 result = newResult; | 559 result = newResult; |
438 } | 560 } |
439 | 561 |
440 var realLockFile = new LockFile.empty(); | 562 var realLockFile = new LockFile.empty(); |
441 if (lockfile != null) { | 563 if (lockfile != null) { |
442 lockfile.forEach((name, version) { | 564 lockfile.forEach((name, version) { |
443 version = new Version.parse(version); | 565 version = new Version.parse(version); |
444 realLockFile.packages[name] = | 566 realLockFile.packages[name] = |
445 new PackageId(name, source1, version, name); | 567 new PackageId(name, source1, version, name); |
446 }); | 568 }); |
447 } | 569 } |
448 | 570 |
449 // Resolve the versions. | 571 // Resolve the versions. |
450 var future = resolveVersions(cache.sources, root, realLockFile); | 572 var future = resolveVersions(cache.sources, root, |
| 573 allowBacktracking: allowBacktracking, lockFile: realLockFile); |
451 | 574 |
452 if (result != null) { | 575 if (result != null) { |
453 expect(future, completion(predicate((actualResult) { | 576 expect(future, completion(predicate((actualResult) { |
454 for (var actualId in actualResult) { | 577 for (var actualId in actualResult) { |
455 if (!result.containsKey(actualId.name)) return false; | 578 if (!result.containsKey(actualId.name)) return false; |
456 var expectedId = result.remove(actualId.name); | 579 var expectedId = result.remove(actualId.name); |
457 if (actualId != expectedId) return false; | 580 if (actualId != expectedId) return false; |
458 } | 581 } |
459 return result.isEmpty; | 582 return result.isEmpty; |
460 }, 'packages to match $result'))); | 583 }, 'packages to match $result'))); |
461 } else if (error != null) { | 584 } else if (error != null) { |
462 expect(future, throwsA(error)); | 585 expect(future, throwsA(error)); |
463 } | 586 } |
464 }); | 587 }); |
465 } | 588 } |
466 | 589 |
467 /// A source used for testing. This both creates mock package objects and acts | 590 /// A source used for testing. This both creates mock package objects and acts |
468 /// as a source for them. | 591 /// as a source for them. |
469 /// | 592 /// |
470 /// In order to support testing packages that have the same name but different | 593 /// In order to support testing packages that have the same name but different |
471 /// descriptions, a package's name is calculated by taking the description | 594 /// descriptions, a package's name is calculated by taking the description |
472 /// string and stripping off any trailing hyphen followed by non-hyphen | 595 /// string and stripping off any trailing hyphen followed by non-hyphen |
473 /// characters. | 596 /// characters. |
474 class MockSource extends Source { | 597 class MockSource extends Source { |
475 final Map<String, Map<Version, Package>> _packages; | 598 final _packages = <String, Map<Version, Package>>{}; |
| 599 |
| 600 /// Keeps track of which package version lists have been requested. Ensures |
| 601 /// that a source is only hit once for a given package and that pub |
| 602 /// internally caches the results. |
| 603 final _requestedVersions = new Set<String>(); |
| 604 |
| 605 /// Keeps track of which package pubspecs have been requested. Ensures that a |
| 606 /// source is only hit once for a given package and that pub internally |
| 607 /// caches the results. |
| 608 final _requestedPubspecs = new Map<String, Set<Version>>(); |
476 | 609 |
477 final String name; | 610 final String name; |
478 bool get shouldCache => true; | 611 bool get shouldCache => true; |
479 | 612 |
480 MockSource(this.name) | 613 MockSource(this.name); |
481 : _packages = <String, Map<Version, Package>>{}; | |
482 | 614 |
483 Future<List<Version>> getVersions(String name, String description) { | 615 Future<List<Version>> getVersions(String name, String description) { |
484 return defer(() => _packages[description].keys.toList()); | 616 return defer(() { |
| 617 // Make sure the solver doesn't request the same thing twice. |
| 618 if (_requestedVersions.contains(description)) { |
| 619 throw 'Version list for $description was already requested.'; |
| 620 } |
| 621 |
| 622 _requestedVersions.add(description); |
| 623 |
| 624 if (!_packages.containsKey(description)){ |
| 625 throw 'MockSource does not have a package matching "$description".'; |
| 626 } |
| 627 return _packages[description].keys.toList(); |
| 628 }); |
485 } | 629 } |
486 | 630 |
487 Future<Pubspec> describe(PackageId id) { | 631 Future<Pubspec> describe(PackageId id) { |
488 return defer(() { | 632 return defer(() { |
489 return _packages[id.name][id.version].pubspec; | 633 // Make sure the solver doesn't request the same thing twice. |
| 634 if (_requestedPubspecs.containsKey(id.description) && |
| 635 _requestedPubspecs[id.description].contains(id.version)) { |
| 636 throw 'Pubspec for $id was already requested.'; |
| 637 } |
| 638 |
| 639 _requestedPubspecs.putIfAbsent(id.description, () => new Set<Version>()); |
| 640 _requestedPubspecs[id.description].add(id.version); |
| 641 |
| 642 return _packages[id.description][id.version].pubspec; |
490 }); | 643 }); |
491 } | 644 } |
492 | 645 |
493 Future<bool> install(PackageId id, String path) { | 646 Future<bool> install(PackageId id, String path) { |
494 throw 'no'; | 647 throw 'no'; |
495 } | 648 } |
496 | 649 |
497 Package mockPackage(String description, String version, | 650 void addPackage(String description, Package package) { |
498 Map dependencyStrings) { | 651 _packages.putIfAbsent(description, () => new Map<Version, Package>()); |
499 // Build the pubspec dependencies. | 652 _packages[description][package.version] = package; |
500 var dependencies = <PackageRef>[]; | |
501 var devDependencies = <PackageRef>[]; | |
502 | |
503 dependencyStrings.forEach((name, constraint) { | |
504 parseSource(name, (isDev, name, source) { | |
505 var packageName = name.replaceFirst(new RegExp(r"-[^-]+$"), ""); | |
506 var ref = new PackageRef(packageName, source, | |
507 new VersionConstraint.parse(constraint), name); | |
508 | |
509 if (isDev) { | |
510 devDependencies.add(ref); | |
511 } else { | |
512 dependencies.add(ref); | |
513 } | |
514 }); | |
515 }); | |
516 | |
517 var pubspec = new Pubspec( | |
518 description, new Version.parse(version), dependencies, devDependencies, | |
519 new PubspecEnvironment()); | |
520 return new Package.inMemory(pubspec); | |
521 } | |
522 | |
523 void addPackage(Package package) { | |
524 _packages.putIfAbsent(package.name, () => new Map<Version, Package>()); | |
525 _packages[package.name][package.version] = package; | |
526 } | 653 } |
527 } | 654 } |
528 | 655 |
| 656 Package mockPackage(String description, String version, |
| 657 Map dependencyStrings) { |
| 658 // Build the pubspec dependencies. |
| 659 var dependencies = <PackageRef>[]; |
| 660 var devDependencies = <PackageRef>[]; |
| 661 |
| 662 dependencyStrings.forEach((name, constraint) { |
| 663 parseSource(name, (isDev, name, source) { |
| 664 var packageName = name.replaceFirst(new RegExp(r"-[^-]+$"), ""); |
| 665 var ref = new PackageRef(packageName, source, |
| 666 new VersionConstraint.parse(constraint), name); |
| 667 |
| 668 if (isDev) { |
| 669 devDependencies.add(ref); |
| 670 } else { |
| 671 dependencies.add(ref); |
| 672 } |
| 673 }); |
| 674 }); |
| 675 |
| 676 var name = description.replaceFirst(new RegExp(r"-[^-]+$"), ""); |
| 677 var pubspec = new Pubspec( |
| 678 name, new Version.parse(version), dependencies, devDependencies, |
| 679 new PubspecEnvironment()); |
| 680 return new Package.inMemory(pubspec); |
| 681 } |
| 682 |
529 void parseSource(String description, | 683 void parseSource(String description, |
530 callback(bool isDev, String name, Source source)) { | 684 callback(bool isDev, String name, Source source)) { |
531 var isDev = false; | 685 var isDev = false; |
532 | 686 |
533 if (description.startsWith("(dev) ")) { | 687 if (description.startsWith("(dev) ")) { |
534 description = description.substring("(dev) ".length); | 688 description = description.substring("(dev) ".length); |
535 isDev = true; | 689 isDev = true; |
536 } | 690 } |
537 | 691 |
538 var name = description; | 692 var name = description; |
539 var source = source1; | 693 var source = source1; |
540 | 694 |
541 var sourceNames = { | 695 var sourceNames = { |
542 'mock1': source1, | 696 'mock1': source1, |
543 'mock2': source2, | 697 'mock2': source2, |
544 'root': null | 698 'root': null |
545 }; | 699 }; |
546 | 700 |
547 var match = new RegExp(r"(.*) from (.*)").firstMatch(description); | 701 var match = new RegExp(r"(.*) from (.*)").firstMatch(description); |
548 if (match != null) { | 702 if (match != null) { |
549 name = match[1]; | 703 name = match[1]; |
550 source = sourceNames[match[2]]; | 704 source = sourceNames[match[2]]; |
551 } | 705 } |
552 | 706 |
553 callback(isDev, name, source); | 707 callback(isDev, name, source); |
554 } | 708 } |
OLD | NEW |