| Index: utils/tests/pub/version_solver_test.dart
|
| diff --git a/utils/tests/pub/version_solver_test.dart b/utils/tests/pub/version_solver_test.dart
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..4c86ecfaaf973aad1e6a6a8e58c3eb5ba4bff371
|
| --- /dev/null
|
| +++ b/utils/tests/pub/version_solver_test.dart
|
| @@ -0,0 +1,277 @@
|
| +// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
|
| +// for details. All rights reserved. Use of this source code is governed by a
|
| +// BSD-style license that can be found in the LICENSE file.
|
| +
|
| +#library('pub_update_test');
|
| +
|
| +#import('dart:io');
|
| +
|
| +#import('../../pub/package.dart');
|
| +#import('../../pub/pubspec.dart');
|
| +#import('../../pub/source.dart');
|
| +#import('../../pub/source_registry.dart');
|
| +#import('../../pub/version.dart');
|
| +#import('../../pub/version_solver.dart');
|
| +#import('../../../lib/unittest/unittest.dart');
|
| +
|
| +final noVersion = 'no version';
|
| +final disjointConstraint = 'disjoint';
|
| +final couldNotSolve = 'unsolved';
|
| +
|
| +main() {
|
| + testResolve('no dependencies', {
|
| + 'myapp 0.0.0': {}
|
| + }, result: {
|
| + 'myapp': '0.0.0'
|
| + });
|
| +
|
| + testResolve('simple dependency tree', {
|
| + 'myapp 0.0.0': {
|
| + 'a': '1.0.0',
|
| + 'b': '1.0.0'
|
| + },
|
| + 'a 1.0.0': {
|
| + 'aa': '1.0.0',
|
| + 'ab': '1.0.0'
|
| + },
|
| + 'aa 1.0.0': {},
|
| + 'ab 1.0.0': {},
|
| + 'b 1.0.0': {
|
| + 'ba': '1.0.0',
|
| + 'bb': '1.0.0'
|
| + },
|
| + 'ba 1.0.0': {},
|
| + 'bb 1.0.0': {}
|
| + }, result: {
|
| + 'myapp': '0.0.0',
|
| + 'a': '1.0.0',
|
| + 'aa': '1.0.0',
|
| + 'ab': '1.0.0',
|
| + 'b': '1.0.0',
|
| + 'ba': '1.0.0',
|
| + 'bb': '1.0.0'
|
| + });
|
| +
|
| + testResolve('shared dependency with overlapping constraints', {
|
| + 'myapp 0.0.0': {
|
| + 'a': '1.0.0',
|
| + 'b': '1.0.0'
|
| + },
|
| + 'a 1.0.0': {
|
| + 'shared': '>=2.0.0 <4.0.0'
|
| + },
|
| + 'b 1.0.0': {
|
| + 'shared': '>=3.0.0 <5.0.0'
|
| + },
|
| + 'shared 2.0.0': {},
|
| + 'shared 3.0.0': {},
|
| + 'shared 3.6.9': {},
|
| + 'shared 4.0.0': {},
|
| + 'shared 5.0.0': {},
|
| + }, result: {
|
| + 'myapp': '0.0.0',
|
| + 'a': '1.0.0',
|
| + 'b': '1.0.0',
|
| + 'shared': '3.6.9'
|
| + });
|
| +
|
| + testResolve('shared dependency where dependent version in turn affects '
|
| + 'other dependencies', {
|
| + 'myapp 0.0.0': {
|
| + 'foo': '<=1.0.2',
|
| + 'bar': '1.0.0'
|
| + },
|
| + 'foo 1.0.0': {},
|
| + 'foo 1.0.1': { 'bang': '1.0.0' },
|
| + 'foo 1.0.2': { 'whoop': '1.0.0' },
|
| + 'foo 1.0.3': { 'zoop': '1.0.0' },
|
| + 'bar 1.0.0': { 'foo': '<=1.0.1' },
|
| + 'bang 1.0.0': {},
|
| + 'whoop 1.0.0': {},
|
| + 'zoop 1.0.0': {}
|
| + }, result: {
|
| + 'myapp': '0.0.0',
|
| + 'foo': '1.0.1',
|
| + 'bar': '1.0.0',
|
| + 'bang': '1.0.0'
|
| + });
|
| +
|
| + testResolve('dependency back onto root package', {
|
| + 'myapp 1.0.0': {
|
| + 'foo': '1.0.0'
|
| + },
|
| + 'foo 1.0.0': {
|
| + 'myapp': '>=1.0.0'
|
| + }
|
| + }, result: {
|
| + 'myapp': '1.0.0',
|
| + 'foo': '1.0.0'
|
| + });
|
| +
|
| + testResolve("dependency back onto root package that doesn't contain root's "
|
| + "version", {
|
| + 'myapp 1.0.0': {
|
| + 'foo': '1.0.0'
|
| + },
|
| + 'foo 1.0.0': {
|
| + 'myapp': '>=2.0.0'
|
| + }
|
| + }, error: disjointConstraint);
|
| +
|
| + testResolve('no version that matches requirement', {
|
| + 'myapp 0.0.0': {
|
| + 'foo': '>=1.0.0 <2.0.0'
|
| + },
|
| + 'foo 2.0.0': {},
|
| + 'foo 2.1.3': {}
|
| + }, error: noVersion);
|
| +
|
| + testResolve('no version that matches combined constraint', {
|
| + 'myapp 0.0.0': {
|
| + 'foo': '1.0.0',
|
| + 'bar': '1.0.0'
|
| + },
|
| + 'foo 1.0.0': {
|
| + 'shared': '>=2.0.0 <3.0.0'
|
| + },
|
| + 'bar 1.0.0': {
|
| + 'shared': '>=2.9.0 <4.0.0'
|
| + },
|
| + 'shared 2.5.0': {},
|
| + 'shared 3.5.0': {}
|
| + }, error: noVersion);
|
| +
|
| + testResolve('disjoint constraints', {
|
| + 'myapp 0.0.0': {
|
| + 'foo': '1.0.0',
|
| + 'bar': '1.0.0'
|
| + },
|
| + 'foo 1.0.0': {
|
| + 'shared': '<=2.0.0'
|
| + },
|
| + 'bar 1.0.0': {
|
| + 'shared': '>3.0.0'
|
| + },
|
| + 'shared 2.0.0': {},
|
| + 'shared 4.0.0': {}
|
| + }, error: disjointConstraint);
|
| +
|
| + testResolve('unstable dependency graph', {
|
| + 'myapp 0.0.0': {
|
| + 'a': '>=1.0.0'
|
| + },
|
| + 'a 1.0.0': {},
|
| + 'a 2.0.0': {
|
| + 'b': '1.0.0'
|
| + },
|
| + 'b 1.0.0': {
|
| + 'a': '1.0.0'
|
| + }
|
| + }, error: couldNotSolve);
|
| +
|
| +// TODO(rnystrom): More stuff to test:
|
| +// - Two packages depend on the same package, but from different sources. Should
|
| +// fail.
|
| +// - Depending on a non-existent package.
|
| +// - Test that only a certain number requests are sent to the mock source so we
|
| +// can keep track of server traffic.
|
| +}
|
| +
|
| +testResolve(description, packages, [result, error]) {
|
| + test(description, () {
|
| + var sources = new SourceRegistry();
|
| + var source = new MockSource();
|
| + sources.register(source);
|
| + sources.setDefault(source.name);
|
| +
|
| + // Build the test package graph.
|
| + var root;
|
| + packages.forEach((nameVersion, dependencies) {
|
| + var parts = nameVersion.split(' ');
|
| + var name = parts[0];
|
| + var version = parts[1];
|
| + var package = source.mockPackage(name, version, dependencies);
|
| + if (name == 'myapp') root = package;
|
| + });
|
| +
|
| + // Clean up the expectation.
|
| + if (result != null) {
|
| + result.forEach((name, version) {
|
| + result[name] = new Version.parse(version);
|
| + });
|
| + }
|
| +
|
| + // Resolve the versions.
|
| + var future = resolveVersions(sources, root);
|
| +
|
| + if (result != null) {
|
| + expect(future, completion(recursivelyMatches(result)));
|
| + } else if (error == noVersion) {
|
| + expect(future, throwsA(new isInstanceOf<NoVersionException>()));
|
| + } else if (error == disjointConstraint) {
|
| + expect(future, throwsA(new isInstanceOf<DisjointConstraintException>()));
|
| + } else if (error == couldNotSolve) {
|
| + expect(future, throwsA(new isInstanceOf<CouldNotSolveException>()));
|
| + } else {
|
| + expect(future, throwsA(error));
|
| + }
|
| +
|
| + // If we aren't expecting an error, print some debugging info if we get one.
|
| + if (error == null) {
|
| + future.handleException((ex) {
|
| + print(ex);
|
| + print(future.stackTrace);
|
| + return true;
|
| + });
|
| + }
|
| + });
|
| +}
|
| +
|
| +class MockSource extends Source {
|
| + final Map<String, Map<Version, Package>> _packages;
|
| +
|
| + String get name() => 'mock';
|
| + bool get shouldCache() => true;
|
| +
|
| + MockSource()
|
| + : _packages = <Map<Version, Package>>{};
|
| +
|
| + Future<List<Version>> getVersions(String name) {
|
| + return fakeAsync(() => _packages[name].getKeys());
|
| + }
|
| +
|
| + Future<Pubspec> describe(String package, Version version) {
|
| + return fakeAsync(() {
|
| + return _packages[package][version].pubspec;
|
| + });
|
| + }
|
| +
|
| + Future<bool> install(PackageId id, String path) {
|
| + throw 'no';
|
| + }
|
| +
|
| + Package mockPackage(String name, String version, Map dependencyStrings) {
|
| + // Build the pubspec dependencies.
|
| + var dependencies = <PackageRef>[];
|
| + dependencyStrings.forEach((name, constraint) {
|
| + dependencies.add(new PackageRef(name, this,
|
| + new VersionConstraint.parse(constraint), name));
|
| + });
|
| +
|
| + var pubspec = new Pubspec(new Version.parse(version), dependencies);
|
| + var package = new Package.inMemory(name, pubspec);
|
| +
|
| + _packages.putIfAbsent(name, () => new Map<Version, Package>());
|
| + _packages[name][package.version] = package;
|
| + return package;
|
| + }
|
| +}
|
| +
|
| +Future fakeAsync(callback()) {
|
| + var completer = new Completer();
|
| + new Timer(0, (_) {
|
| + completer.complete(callback());
|
| + });
|
| +
|
| + return completer.future;
|
| +}
|
|
|