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 |