OLD | NEW |
(Empty) | |
| 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 |
| 3 // BSD-style license that can be found in the LICENSE file. |
| 4 |
| 5 #library('pub_update_test'); |
| 6 |
| 7 #import('dart:io'); |
| 8 |
| 9 #import('../../pub/package.dart'); |
| 10 #import('../../pub/pubspec.dart'); |
| 11 #import('../../pub/source.dart'); |
| 12 #import('../../pub/source_registry.dart'); |
| 13 #import('../../pub/version.dart'); |
| 14 #import('../../pub/version_solver.dart'); |
| 15 #import('../../../lib/unittest/unittest.dart'); |
| 16 |
| 17 final noVersion = 'no version'; |
| 18 final disjointConstraint = 'disjoint'; |
| 19 final couldNotSolve = 'unsolved'; |
| 20 |
| 21 main() { |
| 22 testResolve('no dependencies', { |
| 23 'myapp 0.0.0': {} |
| 24 }, result: { |
| 25 'myapp': '0.0.0' |
| 26 }); |
| 27 |
| 28 testResolve('simple dependency tree', { |
| 29 'myapp 0.0.0': { |
| 30 'a': '1.0.0', |
| 31 'b': '1.0.0' |
| 32 }, |
| 33 'a 1.0.0': { |
| 34 'aa': '1.0.0', |
| 35 'ab': '1.0.0' |
| 36 }, |
| 37 'aa 1.0.0': {}, |
| 38 'ab 1.0.0': {}, |
| 39 'b 1.0.0': { |
| 40 'ba': '1.0.0', |
| 41 'bb': '1.0.0' |
| 42 }, |
| 43 'ba 1.0.0': {}, |
| 44 'bb 1.0.0': {} |
| 45 }, result: { |
| 46 'myapp': '0.0.0', |
| 47 'a': '1.0.0', |
| 48 'aa': '1.0.0', |
| 49 'ab': '1.0.0', |
| 50 'b': '1.0.0', |
| 51 'ba': '1.0.0', |
| 52 'bb': '1.0.0' |
| 53 }); |
| 54 |
| 55 testResolve('shared dependency with overlapping constraints', { |
| 56 'myapp 0.0.0': { |
| 57 'a': '1.0.0', |
| 58 'b': '1.0.0' |
| 59 }, |
| 60 'a 1.0.0': { |
| 61 'shared': '>=2.0.0 <4.0.0' |
| 62 }, |
| 63 'b 1.0.0': { |
| 64 'shared': '>=3.0.0 <5.0.0' |
| 65 }, |
| 66 'shared 2.0.0': {}, |
| 67 'shared 3.0.0': {}, |
| 68 'shared 3.6.9': {}, |
| 69 'shared 4.0.0': {}, |
| 70 'shared 5.0.0': {}, |
| 71 }, result: { |
| 72 'myapp': '0.0.0', |
| 73 'a': '1.0.0', |
| 74 'b': '1.0.0', |
| 75 'shared': '3.6.9' |
| 76 }); |
| 77 |
| 78 testResolve('shared dependency where dependent version in turn affects ' |
| 79 'other dependencies', { |
| 80 'myapp 0.0.0': { |
| 81 'foo': '<=1.0.2', |
| 82 'bar': '1.0.0' |
| 83 }, |
| 84 'foo 1.0.0': {}, |
| 85 'foo 1.0.1': { 'bang': '1.0.0' }, |
| 86 'foo 1.0.2': { 'whoop': '1.0.0' }, |
| 87 'foo 1.0.3': { 'zoop': '1.0.0' }, |
| 88 'bar 1.0.0': { 'foo': '<=1.0.1' }, |
| 89 'bang 1.0.0': {}, |
| 90 'whoop 1.0.0': {}, |
| 91 'zoop 1.0.0': {} |
| 92 }, result: { |
| 93 'myapp': '0.0.0', |
| 94 'foo': '1.0.1', |
| 95 'bar': '1.0.0', |
| 96 'bang': '1.0.0' |
| 97 }); |
| 98 |
| 99 testResolve('dependency back onto root package', { |
| 100 'myapp 1.0.0': { |
| 101 'foo': '1.0.0' |
| 102 }, |
| 103 'foo 1.0.0': { |
| 104 'myapp': '>=1.0.0' |
| 105 } |
| 106 }, result: { |
| 107 'myapp': '1.0.0', |
| 108 'foo': '1.0.0' |
| 109 }); |
| 110 |
| 111 testResolve("dependency back onto root package that doesn't contain root's " |
| 112 "version", { |
| 113 'myapp 1.0.0': { |
| 114 'foo': '1.0.0' |
| 115 }, |
| 116 'foo 1.0.0': { |
| 117 'myapp': '>=2.0.0' |
| 118 } |
| 119 }, error: disjointConstraint); |
| 120 |
| 121 testResolve('no version that matches requirement', { |
| 122 'myapp 0.0.0': { |
| 123 'foo': '>=1.0.0 <2.0.0' |
| 124 }, |
| 125 'foo 2.0.0': {}, |
| 126 'foo 2.1.3': {} |
| 127 }, error: noVersion); |
| 128 |
| 129 testResolve('no version that matches combined constraint', { |
| 130 'myapp 0.0.0': { |
| 131 'foo': '1.0.0', |
| 132 'bar': '1.0.0' |
| 133 }, |
| 134 'foo 1.0.0': { |
| 135 'shared': '>=2.0.0 <3.0.0' |
| 136 }, |
| 137 'bar 1.0.0': { |
| 138 'shared': '>=2.9.0 <4.0.0' |
| 139 }, |
| 140 'shared 2.5.0': {}, |
| 141 'shared 3.5.0': {} |
| 142 }, error: noVersion); |
| 143 |
| 144 testResolve('disjoint constraints', { |
| 145 'myapp 0.0.0': { |
| 146 'foo': '1.0.0', |
| 147 'bar': '1.0.0' |
| 148 }, |
| 149 'foo 1.0.0': { |
| 150 'shared': '<=2.0.0' |
| 151 }, |
| 152 'bar 1.0.0': { |
| 153 'shared': '>3.0.0' |
| 154 }, |
| 155 'shared 2.0.0': {}, |
| 156 'shared 4.0.0': {} |
| 157 }, error: disjointConstraint); |
| 158 |
| 159 testResolve('unstable dependency graph', { |
| 160 'myapp 0.0.0': { |
| 161 'a': '>=1.0.0' |
| 162 }, |
| 163 'a 1.0.0': {}, |
| 164 'a 2.0.0': { |
| 165 'b': '1.0.0' |
| 166 }, |
| 167 'b 1.0.0': { |
| 168 'a': '1.0.0' |
| 169 } |
| 170 }, error: couldNotSolve); |
| 171 |
| 172 // TODO(rnystrom): More stuff to test: |
| 173 // - Two packages depend on the same package, but from different sources. Should |
| 174 // fail. |
| 175 // - Depending on a non-existent package. |
| 176 // - Test that only a certain number requests are sent to the mock source so we |
| 177 // can keep track of server traffic. |
| 178 } |
| 179 |
| 180 testResolve(description, packages, [result, error]) { |
| 181 test(description, () { |
| 182 var sources = new SourceRegistry(); |
| 183 var source = new MockSource(); |
| 184 sources.register(source); |
| 185 sources.setDefault(source.name); |
| 186 |
| 187 // Build the test package graph. |
| 188 var root; |
| 189 packages.forEach((nameVersion, dependencies) { |
| 190 var parts = nameVersion.split(' '); |
| 191 var name = parts[0]; |
| 192 var version = parts[1]; |
| 193 var package = source.mockPackage(name, version, dependencies); |
| 194 if (name == 'myapp') root = package; |
| 195 }); |
| 196 |
| 197 // Clean up the expectation. |
| 198 if (result != null) { |
| 199 result.forEach((name, version) { |
| 200 result[name] = new Version.parse(version); |
| 201 }); |
| 202 } |
| 203 |
| 204 // Resolve the versions. |
| 205 var future = resolveVersions(sources, root); |
| 206 |
| 207 if (result != null) { |
| 208 expect(future, completion(recursivelyMatches(result))); |
| 209 } else if (error == noVersion) { |
| 210 expect(future, throwsA(new isInstanceOf<NoVersionException>())); |
| 211 } else if (error == disjointConstraint) { |
| 212 expect(future, throwsA(new isInstanceOf<DisjointConstraintException>())); |
| 213 } else if (error == couldNotSolve) { |
| 214 expect(future, throwsA(new isInstanceOf<CouldNotSolveException>())); |
| 215 } else { |
| 216 expect(future, throwsA(error)); |
| 217 } |
| 218 |
| 219 // If we aren't expecting an error, print some debugging info if we get one. |
| 220 if (error == null) { |
| 221 future.handleException((ex) { |
| 222 print(ex); |
| 223 print(future.stackTrace); |
| 224 return true; |
| 225 }); |
| 226 } |
| 227 }); |
| 228 } |
| 229 |
| 230 class MockSource extends Source { |
| 231 final Map<String, Map<Version, Package>> _packages; |
| 232 |
| 233 String get name() => 'mock'; |
| 234 bool get shouldCache() => true; |
| 235 |
| 236 MockSource() |
| 237 : _packages = <Map<Version, Package>>{}; |
| 238 |
| 239 Future<List<Version>> getVersions(String name) { |
| 240 return fakeAsync(() => _packages[name].getKeys()); |
| 241 } |
| 242 |
| 243 Future<Pubspec> describe(String package, Version version) { |
| 244 return fakeAsync(() { |
| 245 return _packages[package][version].pubspec; |
| 246 }); |
| 247 } |
| 248 |
| 249 Future<bool> install(PackageId id, String path) { |
| 250 throw 'no'; |
| 251 } |
| 252 |
| 253 Package mockPackage(String name, String version, Map dependencyStrings) { |
| 254 // Build the pubspec dependencies. |
| 255 var dependencies = <PackageRef>[]; |
| 256 dependencyStrings.forEach((name, constraint) { |
| 257 dependencies.add(new PackageRef(name, this, |
| 258 new VersionConstraint.parse(constraint), name)); |
| 259 }); |
| 260 |
| 261 var pubspec = new Pubspec(new Version.parse(version), dependencies); |
| 262 var package = new Package.inMemory(name, pubspec); |
| 263 |
| 264 _packages.putIfAbsent(name, () => new Map<Version, Package>()); |
| 265 _packages[name][package.version] = package; |
| 266 return package; |
| 267 } |
| 268 } |
| 269 |
| 270 Future fakeAsync(callback()) { |
| 271 var completer = new Completer(); |
| 272 new Timer(0, (_) { |
| 273 completer.complete(callback()); |
| 274 }); |
| 275 |
| 276 return completer.future; |
| 277 } |
OLD | NEW |