Chromium Code Reviews| 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('no version that matches requirement', { | |
| 100 'myapp 0.0.0': { | |
| 101 'foo': '>=1.0.0 <2.0.0' | |
| 102 }, | |
| 103 'foo 2.0.0': {}, | |
| 104 'foo 2.1.3': {} | |
| 105 }, error: noVersion); | |
| 106 | |
| 107 testResolve('no version that matches combined constraint', { | |
| 108 'myapp 0.0.0': { | |
| 109 'foo': '1.0.0', | |
| 110 'bar': '1.0.0' | |
| 111 }, | |
| 112 'foo 1.0.0': { | |
| 113 'shared': '>=2.0.0 <3.0.0' | |
| 114 }, | |
| 115 'bar 1.0.0': { | |
| 116 'shared': '>=2.9.0 <4.0.0' | |
| 117 }, | |
| 118 'shared 2.5.0': {}, | |
| 119 'shared 3.5.0': {} | |
| 120 }, error: noVersion); | |
| 121 | |
| 122 testResolve('disjoint constraints', { | |
| 123 'myapp 0.0.0': { | |
| 124 'foo': '1.0.0', | |
| 125 'bar': '1.0.0' | |
| 126 }, | |
| 127 'foo 1.0.0': { | |
| 128 'shared': '<2.0.0' | |
| 129 }, | |
| 130 'bar 1.0.0': { | |
| 131 'shared': '>3.0.0' | |
| 132 }, | |
| 133 'shared 2.0.0': {}, | |
| 134 'shared 4.0.0': {} | |
| 135 }, error: disjointConstraint); | |
| 136 | |
| 137 // TODO(rnystrom): Uncomment this when the resolver doesn't get stuck in an | |
| 138 // infinite loop on unstable graphs. | |
|
nweiz
2012/06/18 18:29:19
Remove TODO
Bob Nystrom
2012/06/20 01:40:04
Done.
| |
| 139 testResolve('unstable dependency graph', { | |
|
nweiz
2012/06/18 18:29:19
Shouldn't this stabilize at a=1.0.0?
Bob Nystrom
2012/06/20 01:40:04
That would be a valid solution, but the solver is
nweiz
2012/06/20 21:08:48
But shouldn't it resolve this like so:
* Set a=2.
Bob Nystrom
2012/06/20 22:29:54
When it sets a=1.0.0, that removes the dependency
nweiz
2012/06/20 23:47:45
Ah, I see now.
| |
| 140 'myapp 0.0.0': { | |
| 141 'a': '>=1.0.0' | |
| 142 }, | |
| 143 'a 1.0.0': {}, | |
| 144 'a 2.0.0': { | |
| 145 'b': '1.0.0' | |
| 146 }, | |
| 147 'b 1.0.0': { | |
| 148 'a': '1.0.0' | |
| 149 } | |
| 150 }, error: couldNotSolve); | |
| 151 | |
| 152 // TODO(rnystrom): More stuff to test: | |
| 153 // - Root depends on A, A depends back on root, but doesn't allow current | |
| 154 // version. Should fail. | |
| 155 // - Root depends on A, A depends back on root, and does allow current version. | |
| 156 // - Two packages depend on the same package, but from different sources. Should | |
| 157 // fail. | |
| 158 // - Depending on a non-existent package. | |
| 159 // - Test that only a certain number requests are sent to the mock source so we | |
| 160 // can keep track of server traffic. | |
| 161 } | |
| 162 | |
| 163 testResolve(description, packages, [result, error]) { | |
| 164 test(description, () { | |
| 165 var sources = new SourceRegistry(); | |
| 166 var source = new MockSource(sources); | |
| 167 sources.register(source); | |
| 168 sources.setDefault(source.name); | |
| 169 | |
| 170 // Build the test package graph. | |
| 171 var root; | |
| 172 packages.forEach((nameVersion, dependencies) { | |
| 173 var parts = nameVersion.split(' '); | |
| 174 var name = parts[0]; | |
| 175 var version = parts[1]; | |
| 176 var package = source.mockPackage(name, version, dependencies); | |
| 177 if (name == 'myapp') root = package; | |
| 178 }); | |
| 179 | |
| 180 // Clean up the expectation. | |
| 181 if (result != null) { | |
| 182 result.forEach((name, version) { | |
| 183 result[name] = new Version.parse(version); | |
| 184 }); | |
| 185 } | |
| 186 | |
| 187 // Resolve the versions. | |
| 188 var future = resolveVersions(sources, root); | |
| 189 | |
| 190 if (result != null) { | |
| 191 expect(future, completion(recursivelyMatches(result))); | |
| 192 } else if (error == noVersion) { | |
| 193 expect(future, throwsA(new isInstanceOf<NoVersionException>())); | |
| 194 } else if (error == disjointConstraint) { | |
| 195 expect(future, throwsA(new isInstanceOf<DisjointConstraintException>())); | |
| 196 } else if (error == couldNotSolve) { | |
| 197 expect(future, throwsA(new isInstanceOf<CouldNotSolveException>())); | |
| 198 } else { | |
| 199 expect(future, throwsA(error)); | |
| 200 } | |
| 201 | |
| 202 // Uncomment this if you want to debug why a resolve is throwing an | |
| 203 // exception. | |
|
nweiz
2012/06/18 18:29:19
It seems like sufficient information for debugging
Bob Nystrom
2012/06/20 01:40:04
Done.
| |
| 204 /* | |
| 205 future.handleException((ex) { | |
| 206 print(ex); | |
| 207 print(future.stackTrace); | |
| 208 return true; | |
| 209 }); | |
| 210 */ | |
| 211 }); | |
| 212 } | |
| 213 | |
| 214 class MockSource extends Source { | |
|
nweiz
2012/06/18 18:29:19
Maybe it's time to make MockSource its own library
Bob Nystrom
2012/06/20 01:40:04
Maybe. I think each one does different things, and
nweiz
2012/06/20 21:08:48
I prefer libraries that are re-usable, personally.
| |
| 215 final SourceRegistry _sources; | |
| 216 final Map<String, Map<Version, Package>> _packages; | |
| 217 | |
| 218 String get name() => 'mock'; | |
| 219 bool get shouldCache() => true; | |
| 220 | |
| 221 MockSource(this._sources) | |
| 222 : _packages = <Map<Version, Package>>{}; | |
| 223 | |
| 224 Future<Version> findVersion(String name, | |
| 225 VersionConstraint constraint) { | |
| 226 return fakeAsync(() { | |
| 227 var best = null; | |
| 228 for (var version in _packages[name].getKeys()) { | |
| 229 if (constraint.allows(version)) { | |
| 230 if (best == null || version > best) best = version; | |
| 231 } | |
| 232 } | |
| 233 | |
| 234 return best; | |
| 235 }); | |
| 236 } | |
| 237 | |
| 238 Future<Pubspec> describe(String package, Version version) { | |
| 239 return fakeAsync(() { | |
| 240 return _packages[package][version].pubspec; | |
| 241 }); | |
| 242 } | |
| 243 | |
| 244 Future<bool> install(PackageId id, String path) { | |
| 245 throw 'no'; | |
| 246 } | |
| 247 | |
| 248 Package mockPackage(String name, String version, Map dependencies) { | |
|
nweiz
2012/06/18 18:29:19
It seems like you could simplify this method and a
Bob Nystrom
2012/06/20 01:40:04
Done.
| |
| 249 // Build the pubspec string. | |
| 250 var pubspec = new StringBuffer(); | |
| 251 pubspec.add('version: $version\n'); | |
| 252 if (!dependencies.isEmpty()) { | |
| 253 pubspec.add('dependencies:\n'); | |
| 254 dependencies.forEach((name, version) { | |
| 255 pubspec.add(' $name: "$version"\n'); | |
| 256 }); | |
| 257 } | |
| 258 | |
| 259 var package = new Package.inMemory(name, | |
| 260 new Pubspec.parse(pubspec.toString(), _sources)); | |
| 261 | |
| 262 _packages.putIfAbsent(name, () => new Map<Version, Package>()); | |
| 263 _packages[name][package.version] = package; | |
| 264 return package; | |
| 265 } | |
| 266 } | |
| 267 | |
| 268 Future fakeAsync(callback()) { | |
| 269 var completer = new Completer(); | |
| 270 new Timer(0, (_) { | |
| 271 completer.complete(callback()); | |
| 272 }); | |
| 273 | |
| 274 return completer.future; | |
| 275 } | |
| OLD | NEW |