| 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:io'); | 7 #import('dart:io'); |
| 8 #import('dart:isolate'); | 8 #import('dart:isolate'); |
| 9 | 9 |
| 10 #import('../../pub/package.dart'); | 10 #import('../../pub/package.dart'); |
| 11 #import('../../pub/pubspec.dart'); | 11 #import('../../pub/pubspec.dart'); |
| 12 #import('../../pub/source.dart'); | 12 #import('../../pub/source.dart'); |
| 13 #import('../../pub/source_registry.dart'); | 13 #import('../../pub/source_registry.dart'); |
| 14 #import('../../pub/utils.dart'); |
| 14 #import('../../pub/version.dart'); | 15 #import('../../pub/version.dart'); |
| 15 #import('../../pub/version_solver.dart'); | 16 #import('../../pub/version_solver.dart'); |
| 16 #import('../../../lib/unittest/unittest.dart'); | 17 #import('../../../lib/unittest/unittest.dart'); |
| 17 | 18 |
| 18 final noVersion = 'no version'; | 19 final noVersion = 'no version'; |
| 19 final disjointConstraint = 'disjoint'; | 20 final disjointConstraint = 'disjoint'; |
| 21 final sourceMismatch = 'source mismatch'; |
| 22 final descriptionMismatch = 'description mismatch'; |
| 20 final couldNotSolve = 'unsolved'; | 23 final couldNotSolve = 'unsolved'; |
| 21 | 24 |
| 25 Source source1; |
| 26 Source source2; |
| 27 |
| 22 main() { | 28 main() { |
| 23 testResolve('no dependencies', { | 29 testResolve('no dependencies', { |
| 24 'myapp 0.0.0': {} | 30 'myapp 0.0.0': {} |
| 25 }, result: { | 31 }, result: { |
| 26 'myapp': '0.0.0' | 32 'myapp': '0.0.0' |
| 27 }); | 33 }); |
| 28 | 34 |
| 29 testResolve('simple dependency tree', { | 35 testResolve('simple dependency tree', { |
| 30 'myapp 0.0.0': { | 36 'myapp 0.0.0': { |
| 31 'a': '1.0.0', | 37 'a': '1.0.0', |
| (...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 97 'bang': '1.0.0' | 103 'bang': '1.0.0' |
| 98 }); | 104 }); |
| 99 | 105 |
| 100 testResolve('dependency back onto root package', { | 106 testResolve('dependency back onto root package', { |
| 101 'myapp 1.0.0': { | 107 'myapp 1.0.0': { |
| 102 'foo': '1.0.0' | 108 'foo': '1.0.0' |
| 103 }, | 109 }, |
| 104 'foo 1.0.0': { | 110 'foo 1.0.0': { |
| 105 'myapp': '>=1.0.0' | 111 'myapp': '>=1.0.0' |
| 106 } | 112 } |
| 107 }, result: { | 113 }, error: sourceMismatch); |
| 108 'myapp': '1.0.0', | |
| 109 'foo': '1.0.0' | |
| 110 }); | |
| 111 | |
| 112 testResolve("dependency back onto root package that doesn't contain root's " | |
| 113 "version", { | |
| 114 'myapp 1.0.0': { | |
| 115 'foo': '1.0.0' | |
| 116 }, | |
| 117 'foo 1.0.0': { | |
| 118 'myapp': '>=2.0.0' | |
| 119 } | |
| 120 }, error: disjointConstraint); | |
| 121 | 114 |
| 122 testResolve('no version that matches requirement', { | 115 testResolve('no version that matches requirement', { |
| 123 'myapp 0.0.0': { | 116 'myapp 0.0.0': { |
| 124 'foo': '>=1.0.0 <2.0.0' | 117 'foo': '>=1.0.0 <2.0.0' |
| 125 }, | 118 }, |
| 126 'foo 2.0.0': {}, | 119 'foo 2.0.0': {}, |
| 127 'foo 2.1.3': {} | 120 'foo 2.1.3': {} |
| 128 }, error: noVersion); | 121 }, error: noVersion); |
| 129 | 122 |
| 130 testResolve('no version that matches combined constraint', { | 123 testResolve('no version that matches combined constraint', { |
| (...skipping 19 matching lines...) Expand all Loading... |
| 150 'foo 1.0.0': { | 143 'foo 1.0.0': { |
| 151 'shared': '<=2.0.0' | 144 'shared': '<=2.0.0' |
| 152 }, | 145 }, |
| 153 'bar 1.0.0': { | 146 'bar 1.0.0': { |
| 154 'shared': '>3.0.0' | 147 'shared': '>3.0.0' |
| 155 }, | 148 }, |
| 156 'shared 2.0.0': {}, | 149 'shared 2.0.0': {}, |
| 157 'shared 4.0.0': {} | 150 'shared 4.0.0': {} |
| 158 }, error: disjointConstraint); | 151 }, error: disjointConstraint); |
| 159 | 152 |
| 153 testResolve('mismatched descriptions', { |
| 154 'myapp 0.0.0': { |
| 155 'foo': '1.0.0', |
| 156 'bar': '1.0.0' |
| 157 }, |
| 158 'foo 1.0.0': { |
| 159 'shared-x': '1.0.0' |
| 160 }, |
| 161 'bar 1.0.0': { |
| 162 'shared-y': '1.0.0' |
| 163 }, |
| 164 'shared-x 1.0.0': {}, |
| 165 'shared-y 1.0.0': {} |
| 166 }, error: descriptionMismatch); |
| 167 |
| 168 testResolve('mismatched sources', { |
| 169 'myapp 0.0.0': { |
| 170 'foo': '1.0.0', |
| 171 'bar': '1.0.0' |
| 172 }, |
| 173 'foo 1.0.0': { |
| 174 'shared': '1.0.0' |
| 175 }, |
| 176 'bar 1.0.0': { |
| 177 'shared from mock2': '1.0.0' |
| 178 }, |
| 179 'shared 1.0.0': {}, |
| 180 'shared 1.0.0 from mock2': {} |
| 181 }, error: sourceMismatch); |
| 182 |
| 160 testResolve('unstable dependency graph', { | 183 testResolve('unstable dependency graph', { |
| 161 'myapp 0.0.0': { | 184 'myapp 0.0.0': { |
| 162 'a': '>=1.0.0' | 185 'a': '>=1.0.0' |
| 163 }, | 186 }, |
| 164 'a 1.0.0': {}, | 187 'a 1.0.0': {}, |
| 165 'a 2.0.0': { | 188 'a 2.0.0': { |
| 166 'b': '1.0.0' | 189 'b': '1.0.0' |
| 167 }, | 190 }, |
| 168 'b 1.0.0': { | 191 'b 1.0.0': { |
| 169 'a': '1.0.0' | 192 'a': '1.0.0' |
| 170 } | 193 } |
| 171 }, error: couldNotSolve); | 194 }, error: couldNotSolve); |
| 172 | 195 |
| 173 // TODO(rnystrom): More stuff to test: | 196 // TODO(rnystrom): More stuff to test: |
| 174 // - Two packages depend on the same package, but from different sources. Should | 197 // - Two packages depend on the same package, but from different sources. Should |
| 175 // fail. | 198 // fail. |
| 176 // - Depending on a non-existent package. | 199 // - Depending on a non-existent package. |
| 177 // - Test that only a certain number requests are sent to the mock source so we | 200 // - Test that only a certain number requests are sent to the mock source so we |
| 178 // can keep track of server traffic. | 201 // can keep track of server traffic. |
| 179 } | 202 } |
| 180 | 203 |
| 181 testResolve(description, packages, [result, error]) { | 204 testResolve(description, packages, [result, error]) { |
| 182 test(description, () { | 205 test(description, () { |
| 183 var sources = new SourceRegistry(); | 206 var sources = new SourceRegistry(); |
| 184 var source = new MockSource(); | 207 source1 = new MockSource('mock1'); |
| 185 sources.register(source); | 208 source2 = new MockSource('mock2'); |
| 186 sources.setDefault(source.name); | 209 sources.register(source1); |
| 210 sources.register(source2); |
| 211 sources.setDefault(source1.name); |
| 187 | 212 |
| 188 // Build the test package graph. | 213 // Build the test package graph. |
| 189 var root; | 214 var root; |
| 190 packages.forEach((nameVersion, dependencies) { | 215 packages.forEach((nameVersion, dependencies) { |
| 216 var parsed = parseSource(nameVersion); |
| 217 nameVersion = parsed.first; |
| 218 var source = parsed.last; |
| 219 |
| 191 var parts = nameVersion.split(' '); | 220 var parts = nameVersion.split(' '); |
| 192 var name = parts[0]; | 221 var name = parts[0]; |
| 193 var version = parts[1]; | 222 var version = parts[1]; |
| 223 |
| 194 var package = source.mockPackage(name, version, dependencies); | 224 var package = source.mockPackage(name, version, dependencies); |
| 195 if (name == 'myapp') { | 225 if (name == 'myapp') { |
| 196 // Don't add the root package to the server, so we can verify that Pub | 226 // Don't add the root package to the server, so we can verify that Pub |
| 197 // doesn't try to look up information about the local package on the | 227 // doesn't try to look up information about the local package on the |
| 198 // remote server. | 228 // remote server. |
| 199 root = package; | 229 root = package; |
| 200 } else { | 230 } else { |
| 201 source.addPackage(package); | 231 source.addPackage(package); |
| 202 } | 232 } |
| 203 }); | 233 }); |
| 204 | 234 |
| 205 // Clean up the expectation. | 235 // Clean up the expectation. |
| 206 if (result != null) { | 236 if (result != null) { |
| 207 result.forEach((name, version) { | 237 result.forEach((name, version) { |
| 208 result[name] = new Version.parse(version); | 238 result[name] = new Version.parse(version); |
| 209 }); | 239 }); |
| 210 } | 240 } |
| 211 | 241 |
| 212 // Resolve the versions. | 242 // Resolve the versions. |
| 213 var future = resolveVersions(sources, root); | 243 var future = resolveVersions(sources, root); |
| 214 | 244 |
| 215 if (result != null) { | 245 if (result != null) { |
| 216 expect(future, completion(equals(result))); | 246 expect(future, completion(equals(result))); |
| 217 } else if (error == noVersion) { | 247 } else if (error == noVersion) { |
| 218 expect(future, throwsA(new isInstanceOf<NoVersionException>())); | 248 expect(future, throwsA(new isInstanceOf<NoVersionException>())); |
| 219 } else if (error == disjointConstraint) { | 249 } else if (error == disjointConstraint) { |
| 220 expect(future, throwsA(new isInstanceOf<DisjointConstraintException>())); | 250 expect(future, throwsA(new isInstanceOf<DisjointConstraintException>())); |
| 251 } else if (error == sourceMismatch) { |
| 252 expect(future, throwsA(new isInstanceOf<SourceMismatchException>())); |
| 253 } else if (error == descriptionMismatch) { |
| 254 expect(future, throwsA(new isInstanceOf<DescriptionMismatchException>())); |
| 221 } else if (error == couldNotSolve) { | 255 } else if (error == couldNotSolve) { |
| 222 expect(future, throwsA(new isInstanceOf<CouldNotSolveException>())); | 256 expect(future, throwsA(new isInstanceOf<CouldNotSolveException>())); |
| 223 } else { | 257 } else { |
| 224 expect(future, throwsA(error)); | 258 expect(future, throwsA(error)); |
| 225 } | 259 } |
| 226 | 260 |
| 227 // If we aren't expecting an error, print some debugging info if we get one. | 261 // If we aren't expecting an error, print some debugging info if we get one. |
| 228 if (error == null) { | 262 if (error == null) { |
| 229 future.handleException((ex) { | 263 future.handleException((ex) { |
| 230 print(ex); | 264 print(ex); |
| 231 print(future.stackTrace); | 265 print(future.stackTrace); |
| 232 return true; | 266 return true; |
| 233 }); | 267 }); |
| 234 } | 268 } |
| 235 }); | 269 }); |
| 236 } | 270 } |
| 237 | 271 |
| 272 /** |
| 273 * A source used for testing. This both creates mock package objects and acts as |
| 274 * a source for them. |
| 275 * |
| 276 * In order to support testing packages that have the same name but different |
| 277 * descriptions, a package's name is calculated by taking the description string |
| 278 * and stripping off any trailing hyphen followed by non-hyphen characters. |
| 279 */ |
| 238 class MockSource extends Source { | 280 class MockSource extends Source { |
| 239 final Map<String, Map<Version, Package>> _packages; | 281 final Map<String, Map<Version, Package>> _packages; |
| 240 | 282 |
| 241 String get name() => 'mock'; | 283 final String name; |
| 242 bool get shouldCache() => true; | 284 bool get shouldCache() => true; |
| 243 | 285 |
| 244 MockSource() | 286 MockSource(this.name) |
| 245 : _packages = <Map<Version, Package>>{}; | 287 : _packages = <Map<Version, Package>>{}; |
| 246 | 288 |
| 247 Future<List<Version>> getVersions(String name) { | 289 Future<List<Version>> getVersions(String name) { |
| 248 return fakeAsync(() => _packages[name].getKeys()); | 290 return fakeAsync(() => _packages[name].getKeys()); |
| 249 } | 291 } |
| 250 | 292 |
| 251 Future<Pubspec> describe(String package, Version version) { | 293 Future<Pubspec> describe(PackageId id) { |
| 252 return fakeAsync(() { | 294 return fakeAsync(() { |
| 253 return _packages[package][version].pubspec; | 295 return _packages[id.name][id.version].pubspec; |
| 254 }); | 296 }); |
| 255 } | 297 } |
| 256 | 298 |
| 257 Future<bool> install(PackageId id, String path) { | 299 Future<bool> install(PackageId id, String path) { |
| 258 throw 'no'; | 300 throw 'no'; |
| 259 } | 301 } |
| 260 | 302 |
| 261 Package mockPackage(String name, String version, Map dependencyStrings) { | 303 Package mockPackage(String description, String version, |
| 304 Map dependencyStrings) { |
| 262 // Build the pubspec dependencies. | 305 // Build the pubspec dependencies. |
| 263 var dependencies = <PackageRef>[]; | 306 var dependencies = <PackageRef>[]; |
| 264 dependencyStrings.forEach((name, constraint) { | 307 dependencyStrings.forEach((name, constraint) { |
| 265 dependencies.add(new PackageRef(name, this, | 308 var parsed = parseSource(name); |
| 266 new VersionConstraint.parse(constraint), name)); | 309 dependencies.add(new PackageRef( |
| 310 parsed.last, new VersionConstraint.parse(constraint), parsed.first)); |
| 267 }); | 311 }); |
| 268 | 312 |
| 269 var pubspec = new Pubspec(new Version.parse(version), dependencies); | 313 var pubspec = new Pubspec(new Version.parse(version), dependencies); |
| 270 return new Package.inMemory(name, pubspec); | 314 return new Package.inMemory(description, pubspec); |
| 271 } | 315 } |
| 272 | 316 |
| 273 void addPackage(Package package) { | 317 void addPackage(Package package) { |
| 274 _packages.putIfAbsent(package.name, () => new Map<Version, Package>()); | 318 _packages.putIfAbsent(package.name, () => new Map<Version, Package>()); |
| 275 _packages[package.name][package.version] = package; | 319 _packages[package.name][package.version] = package; |
| 276 return package; | 320 return package; |
| 277 } | 321 } |
| 322 |
| 323 String packageName(String description) => |
| 324 description.replaceFirst(new RegExp(@"-[^-]+$"), ""); |
| 278 } | 325 } |
| 279 | 326 |
| 280 Future fakeAsync(callback()) { | 327 Future fakeAsync(callback()) { |
| 281 var completer = new Completer(); | 328 var completer = new Completer(); |
| 282 new Timer(0, (_) { | 329 new Timer(0, (_) { |
| 283 completer.complete(callback()); | 330 completer.complete(callback()); |
| 284 }); | 331 }); |
| 285 | 332 |
| 286 return completer.future; | 333 return completer.future; |
| 287 } | 334 } |
| 335 |
| 336 Pair<String, Source> parseSource(String name) { |
| 337 var match = new RegExp(@"(.*) from (.*)").firstMatch(name); |
| 338 if (match == null) return new Pair<String, Source>(name, source1); |
| 339 switch (match[2]) { |
| 340 case 'mock1': return new Pair<String, Source>(match[1], source1); |
| 341 case 'mock2': return new Pair<String, Source>(match[1], source2); |
| 342 } |
| 343 } |
| OLD | NEW |