OLD | NEW |
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_upgrade_test; | 1 library pub_upgrade_test; |
6 | |
7 import 'dart:async'; | 2 import 'dart:async'; |
8 | |
9 import 'package:unittest/unittest.dart'; | 3 import 'package:unittest/unittest.dart'; |
10 | |
11 import '../lib/src/lock_file.dart'; | 4 import '../lib/src/lock_file.dart'; |
12 import '../lib/src/log.dart' as log; | 5 import '../lib/src/log.dart' as log; |
13 import '../lib/src/package.dart'; | 6 import '../lib/src/package.dart'; |
14 import '../lib/src/pubspec.dart'; | 7 import '../lib/src/pubspec.dart'; |
15 import '../lib/src/sdk.dart' as sdk; | 8 import '../lib/src/sdk.dart' as sdk; |
16 import '../lib/src/source/cached.dart'; | 9 import '../lib/src/source/cached.dart'; |
17 import '../lib/src/system_cache.dart'; | 10 import '../lib/src/system_cache.dart'; |
18 import '../lib/src/utils.dart'; | 11 import '../lib/src/utils.dart'; |
19 import '../lib/src/version.dart'; | 12 import '../lib/src/version.dart'; |
20 import '../lib/src/solver/version_solver.dart'; | 13 import '../lib/src/solver/version_solver.dart'; |
21 import 'test_pub.dart'; | 14 import 'test_pub.dart'; |
22 | |
23 MockSource source1; | 15 MockSource source1; |
24 MockSource source2; | 16 MockSource source2; |
25 | |
26 main() { | 17 main() { |
27 initConfig(); | 18 initConfig(); |
28 | |
29 // Uncomment this to debug failing tests. | |
30 // log.verbosity = log.Verbosity.SOLVER; | |
31 | |
32 // Since this test isn't run from the SDK, it can't find the "version" file | |
33 // to load. Instead, just manually inject a version. | |
34 sdk.version = new Version(1, 2, 3); | 19 sdk.version = new Version(1, 2, 3); |
35 | |
36 group('basic graph', basicGraph); | 20 group('basic graph', basicGraph); |
37 group('with lockfile', withLockFile); | 21 group('with lockfile', withLockFile); |
38 group('root dependency', rootDependency); | 22 group('root dependency', rootDependency); |
39 group('dev dependency', devDependency); | 23 group('dev dependency', devDependency); |
40 group('unsolvable', unsolvable); | 24 group('unsolvable', unsolvable); |
41 group('bad source', badSource); | 25 group('bad source', badSource); |
42 group('backtracking', backtracking); | 26 group('backtracking', backtracking); |
43 group('SDK constraint', sdkConstraint); | 27 group('SDK constraint', sdkConstraint); |
44 group('pre-release', prerelease); | 28 group('pre-release', prerelease); |
45 group('override', override); | 29 group('override', override); |
46 group('downgrade', downgrade); | 30 group('downgrade', downgrade); |
47 } | 31 } |
48 | |
49 void basicGraph() { | 32 void basicGraph() { |
50 testResolve('no dependencies', { | 33 testResolve('no dependencies', { |
51 'myapp 0.0.0': {} | 34 'myapp 0.0.0': {} |
52 }, result: { | 35 }, result: { |
53 'myapp from root': '0.0.0' | 36 'myapp from root': '0.0.0' |
54 }); | 37 }); |
55 | |
56 testResolve('simple dependency tree', { | 38 testResolve('simple dependency tree', { |
57 'myapp 0.0.0': { | 39 'myapp 0.0.0': { |
58 'a': '1.0.0', | 40 'a': '1.0.0', |
59 'b': '1.0.0' | 41 'b': '1.0.0' |
60 }, | 42 }, |
61 'a 1.0.0': { | 43 'a 1.0.0': { |
62 'aa': '1.0.0', | 44 'aa': '1.0.0', |
63 'ab': '1.0.0' | 45 'ab': '1.0.0' |
64 }, | 46 }, |
65 'aa 1.0.0': {}, | 47 'aa 1.0.0': {}, |
66 'ab 1.0.0': {}, | 48 'ab 1.0.0': {}, |
67 'b 1.0.0': { | 49 'b 1.0.0': { |
68 'ba': '1.0.0', | 50 'ba': '1.0.0', |
69 'bb': '1.0.0' | 51 'bb': '1.0.0' |
70 }, | 52 }, |
71 'ba 1.0.0': {}, | 53 'ba 1.0.0': {}, |
72 'bb 1.0.0': {} | 54 'bb 1.0.0': {} |
73 }, result: { | 55 }, result: { |
74 'myapp from root': '0.0.0', | 56 'myapp from root': '0.0.0', |
75 'a': '1.0.0', | 57 'a': '1.0.0', |
76 'aa': '1.0.0', | 58 'aa': '1.0.0', |
77 'ab': '1.0.0', | 59 'ab': '1.0.0', |
78 'b': '1.0.0', | 60 'b': '1.0.0', |
79 'ba': '1.0.0', | 61 'ba': '1.0.0', |
80 'bb': '1.0.0' | 62 'bb': '1.0.0' |
81 }); | 63 }); |
82 | |
83 testResolve('shared dependency with overlapping constraints', { | 64 testResolve('shared dependency with overlapping constraints', { |
84 'myapp 0.0.0': { | 65 'myapp 0.0.0': { |
85 'a': '1.0.0', | 66 'a': '1.0.0', |
86 'b': '1.0.0' | 67 'b': '1.0.0' |
87 }, | 68 }, |
88 'a 1.0.0': { | 69 'a 1.0.0': { |
89 'shared': '>=2.0.0 <4.0.0' | 70 'shared': '>=2.0.0 <4.0.0' |
90 }, | 71 }, |
91 'b 1.0.0': { | 72 'b 1.0.0': { |
92 'shared': '>=3.0.0 <5.0.0' | 73 'shared': '>=3.0.0 <5.0.0' |
93 }, | 74 }, |
94 'shared 2.0.0': {}, | 75 'shared 2.0.0': {}, |
95 'shared 3.0.0': {}, | 76 'shared 3.0.0': {}, |
96 'shared 3.6.9': {}, | 77 'shared 3.6.9': {}, |
97 'shared 4.0.0': {}, | 78 'shared 4.0.0': {}, |
98 'shared 5.0.0': {}, | 79 'shared 5.0.0': {} |
99 }, result: { | 80 }, result: { |
100 'myapp from root': '0.0.0', | 81 'myapp from root': '0.0.0', |
101 'a': '1.0.0', | 82 'a': '1.0.0', |
102 'b': '1.0.0', | 83 'b': '1.0.0', |
103 'shared': '3.6.9' | 84 'shared': '3.6.9' |
104 }); | 85 }); |
105 | 86 testResolve( |
106 testResolve('shared dependency where dependent version in turn affects ' | 87 'shared dependency where dependent version in turn affects ' |
107 'other dependencies', { | 88 'other dependencies', |
| 89 { |
108 'myapp 0.0.0': { | 90 'myapp 0.0.0': { |
109 'foo': '<=1.0.2', | 91 'foo': '<=1.0.2', |
110 'bar': '1.0.0' | 92 'bar': '1.0.0' |
111 }, | 93 }, |
112 'foo 1.0.0': {}, | 94 'foo 1.0.0': {}, |
113 'foo 1.0.1': { 'bang': '1.0.0' }, | 95 'foo 1.0.1': { |
114 'foo 1.0.2': { 'whoop': '1.0.0' }, | 96 'bang': '1.0.0' |
115 'foo 1.0.3': { 'zoop': '1.0.0' }, | 97 }, |
116 'bar 1.0.0': { 'foo': '<=1.0.1' }, | 98 'foo 1.0.2': { |
| 99 'whoop': '1.0.0' |
| 100 }, |
| 101 'foo 1.0.3': { |
| 102 'zoop': '1.0.0' |
| 103 }, |
| 104 'bar 1.0.0': { |
| 105 'foo': '<=1.0.1' |
| 106 }, |
117 'bang 1.0.0': {}, | 107 'bang 1.0.0': {}, |
118 'whoop 1.0.0': {}, | 108 'whoop 1.0.0': {}, |
119 'zoop 1.0.0': {} | 109 'zoop 1.0.0': {} |
120 }, result: { | 110 }, result: { |
121 'myapp from root': '0.0.0', | 111 'myapp from root': '0.0.0', |
122 'foo': '1.0.1', | 112 'foo': '1.0.1', |
123 'bar': '1.0.0', | 113 'bar': '1.0.0', |
124 'bang': '1.0.0' | 114 'bang': '1.0.0' |
125 }, maxTries: 2); | 115 }, maxTries: 2); |
126 | |
127 testResolve('circular dependency', { | 116 testResolve('circular dependency', { |
128 'myapp 1.0.0': { | 117 'myapp 1.0.0': { |
129 'foo': '1.0.0' | 118 'foo': '1.0.0' |
130 }, | 119 }, |
131 'foo 1.0.0': { | 120 'foo 1.0.0': { |
132 'bar': '1.0.0' | 121 'bar': '1.0.0' |
133 }, | 122 }, |
134 'bar 1.0.0': { | 123 'bar 1.0.0': { |
135 'foo': '1.0.0' | 124 'foo': '1.0.0' |
136 } | 125 } |
137 }, result: { | 126 }, result: { |
138 'myapp from root': '1.0.0', | 127 'myapp from root': '1.0.0', |
139 'foo': '1.0.0', | 128 'foo': '1.0.0', |
140 'bar': '1.0.0' | 129 'bar': '1.0.0' |
141 }); | 130 }); |
142 } | 131 } |
143 | |
144 withLockFile() { | 132 withLockFile() { |
145 testResolve('with compatible locked dependency', { | 133 testResolve('with compatible locked dependency', { |
146 'myapp 0.0.0': { | 134 'myapp 0.0.0': { |
147 'foo': 'any' | 135 'foo': 'any' |
148 }, | 136 }, |
149 'foo 1.0.0': { 'bar': '1.0.0' }, | 137 'foo 1.0.0': { |
150 'foo 1.0.1': { 'bar': '1.0.1' }, | 138 'bar': '1.0.0' |
151 'foo 1.0.2': { 'bar': '1.0.2' }, | 139 }, |
| 140 'foo 1.0.1': { |
| 141 'bar': '1.0.1' |
| 142 }, |
| 143 'foo 1.0.2': { |
| 144 'bar': '1.0.2' |
| 145 }, |
152 'bar 1.0.0': {}, | 146 'bar 1.0.0': {}, |
153 'bar 1.0.1': {}, | 147 'bar 1.0.1': {}, |
154 'bar 1.0.2': {} | 148 'bar 1.0.2': {} |
155 }, lockfile: { | 149 }, lockfile: { |
156 'foo': '1.0.1' | 150 'foo': '1.0.1' |
157 }, result: { | 151 }, result: { |
158 'myapp from root': '0.0.0', | 152 'myapp from root': '0.0.0', |
159 'foo': '1.0.1', | 153 'foo': '1.0.1', |
160 'bar': '1.0.1' | 154 'bar': '1.0.1' |
161 }); | 155 }); |
162 | |
163 testResolve('with incompatible locked dependency', { | 156 testResolve('with incompatible locked dependency', { |
164 'myapp 0.0.0': { | 157 'myapp 0.0.0': { |
165 'foo': '>1.0.1' | 158 'foo': '>1.0.1' |
166 }, | 159 }, |
167 'foo 1.0.0': { 'bar': '1.0.0' }, | 160 'foo 1.0.0': { |
168 'foo 1.0.1': { 'bar': '1.0.1' }, | 161 'bar': '1.0.0' |
169 'foo 1.0.2': { 'bar': '1.0.2' }, | 162 }, |
| 163 'foo 1.0.1': { |
| 164 'bar': '1.0.1' |
| 165 }, |
| 166 'foo 1.0.2': { |
| 167 'bar': '1.0.2' |
| 168 }, |
170 'bar 1.0.0': {}, | 169 'bar 1.0.0': {}, |
171 'bar 1.0.1': {}, | 170 'bar 1.0.1': {}, |
172 'bar 1.0.2': {} | 171 'bar 1.0.2': {} |
173 }, lockfile: { | 172 }, lockfile: { |
174 'foo': '1.0.1' | 173 'foo': '1.0.1' |
175 }, result: { | 174 }, result: { |
176 'myapp from root': '0.0.0', | 175 'myapp from root': '0.0.0', |
177 'foo': '1.0.2', | 176 'foo': '1.0.2', |
178 'bar': '1.0.2' | 177 'bar': '1.0.2' |
179 }); | 178 }); |
180 | |
181 testResolve('with unrelated locked dependency', { | 179 testResolve('with unrelated locked dependency', { |
182 'myapp 0.0.0': { | 180 'myapp 0.0.0': { |
183 'foo': 'any' | 181 'foo': 'any' |
184 }, | 182 }, |
185 'foo 1.0.0': { 'bar': '1.0.0' }, | 183 'foo 1.0.0': { |
186 'foo 1.0.1': { 'bar': '1.0.1' }, | 184 'bar': '1.0.0' |
187 'foo 1.0.2': { 'bar': '1.0.2' }, | 185 }, |
| 186 'foo 1.0.1': { |
| 187 'bar': '1.0.1' |
| 188 }, |
| 189 'foo 1.0.2': { |
| 190 'bar': '1.0.2' |
| 191 }, |
188 'bar 1.0.0': {}, | 192 'bar 1.0.0': {}, |
189 'bar 1.0.1': {}, | 193 'bar 1.0.1': {}, |
190 'bar 1.0.2': {}, | 194 'bar 1.0.2': {}, |
191 'baz 1.0.0': {} | 195 'baz 1.0.0': {} |
192 }, lockfile: { | 196 }, lockfile: { |
193 'baz': '1.0.0' | 197 'baz': '1.0.0' |
194 }, result: { | 198 }, result: { |
195 'myapp from root': '0.0.0', | 199 'myapp from root': '0.0.0', |
196 'foo': '1.0.2', | 200 'foo': '1.0.2', |
197 'bar': '1.0.2' | 201 'bar': '1.0.2' |
198 }); | 202 }); |
199 | 203 testResolve( |
200 testResolve('unlocks dependencies if necessary to ensure that a new ' | 204 'unlocks dependencies if necessary to ensure that a new ' |
201 'dependency is satisfied', { | 205 'dependency is satisfied', |
| 206 { |
202 'myapp 0.0.0': { | 207 'myapp 0.0.0': { |
203 'foo': 'any', | 208 'foo': 'any', |
204 'newdep': 'any' | 209 'newdep': 'any' |
205 }, | 210 }, |
206 'foo 1.0.0': { 'bar': '<2.0.0' }, | 211 'foo 1.0.0': { |
207 'bar 1.0.0': { 'baz': '<2.0.0' }, | 212 'bar': '<2.0.0' |
208 'baz 1.0.0': { 'qux': '<2.0.0' }, | 213 }, |
| 214 'bar 1.0.0': { |
| 215 'baz': '<2.0.0' |
| 216 }, |
| 217 'baz 1.0.0': { |
| 218 'qux': '<2.0.0' |
| 219 }, |
209 'qux 1.0.0': {}, | 220 'qux 1.0.0': {}, |
210 'foo 2.0.0': { 'bar': '<3.0.0' }, | 221 'foo 2.0.0': { |
211 'bar 2.0.0': { 'baz': '<3.0.0' }, | 222 'bar': '<3.0.0' |
212 'baz 2.0.0': { 'qux': '<3.0.0' }, | 223 }, |
| 224 'bar 2.0.0': { |
| 225 'baz': '<3.0.0' |
| 226 }, |
| 227 'baz 2.0.0': { |
| 228 'qux': '<3.0.0' |
| 229 }, |
213 'qux 2.0.0': {}, | 230 'qux 2.0.0': {}, |
214 'newdep 2.0.0': { 'baz': '>=1.5.0' } | 231 'newdep 2.0.0': { |
| 232 'baz': '>=1.5.0' |
| 233 } |
215 }, lockfile: { | 234 }, lockfile: { |
216 'foo': '1.0.0', | 235 'foo': '1.0.0', |
217 'bar': '1.0.0', | 236 'bar': '1.0.0', |
218 'baz': '1.0.0', | 237 'baz': '1.0.0', |
219 'qux': '1.0.0' | 238 'qux': '1.0.0' |
220 }, result: { | 239 }, result: { |
221 'myapp from root': '0.0.0', | 240 'myapp from root': '0.0.0', |
222 'foo': '2.0.0', | 241 'foo': '2.0.0', |
223 'bar': '2.0.0', | 242 'bar': '2.0.0', |
224 'baz': '2.0.0', | 243 'baz': '2.0.0', |
225 'qux': '1.0.0', | 244 'qux': '1.0.0', |
226 'newdep': '2.0.0' | 245 'newdep': '2.0.0' |
227 }, maxTries: 3); | 246 }, maxTries: 3); |
228 } | 247 } |
229 | |
230 rootDependency() { | 248 rootDependency() { |
231 testResolve('with root source', { | 249 testResolve('with root source', { |
232 'myapp 1.0.0': { | 250 'myapp 1.0.0': { |
233 'foo': '1.0.0' | 251 'foo': '1.0.0' |
234 }, | 252 }, |
235 'foo 1.0.0': { | 253 'foo 1.0.0': { |
236 'myapp from root': '>=1.0.0' | 254 'myapp from root': '>=1.0.0' |
237 } | 255 } |
238 }, result: { | 256 }, result: { |
239 'myapp from root': '1.0.0', | 257 'myapp from root': '1.0.0', |
240 'foo': '1.0.0' | 258 'foo': '1.0.0' |
241 }); | 259 }); |
242 | |
243 testResolve('with different source', { | 260 testResolve('with different source', { |
244 'myapp 1.0.0': { | 261 'myapp 1.0.0': { |
245 'foo': '1.0.0' | 262 'foo': '1.0.0' |
246 }, | 263 }, |
247 'foo 1.0.0': { | 264 'foo 1.0.0': { |
248 'myapp': '>=1.0.0' | 265 'myapp': '>=1.0.0' |
249 } | 266 } |
250 }, result: { | 267 }, result: { |
251 'myapp from root': '1.0.0', | 268 'myapp from root': '1.0.0', |
252 'foo': '1.0.0' | 269 'foo': '1.0.0' |
253 }); | 270 }); |
254 | |
255 testResolve('with mismatched sources', { | 271 testResolve('with mismatched sources', { |
256 'myapp 1.0.0': { | 272 'myapp 1.0.0': { |
257 'foo': '1.0.0', | 273 'foo': '1.0.0', |
258 'bar': '1.0.0' | 274 'bar': '1.0.0' |
259 }, | 275 }, |
260 'foo 1.0.0': { | 276 'foo 1.0.0': { |
261 'myapp': '>=1.0.0' | 277 'myapp': '>=1.0.0' |
262 }, | 278 }, |
263 'bar 1.0.0': { | 279 'bar 1.0.0': { |
264 'myapp from mock2': '>=1.0.0' | 280 'myapp from mock2': '>=1.0.0' |
265 } | 281 } |
266 }, error: sourceMismatch('myapp', 'foo', 'bar')); | 282 }, error: sourceMismatch('myapp', 'foo', 'bar')); |
267 | |
268 testResolve('with wrong version', { | 283 testResolve('with wrong version', { |
269 'myapp 1.0.0': { | 284 'myapp 1.0.0': { |
270 'foo': '1.0.0' | 285 'foo': '1.0.0' |
271 }, | 286 }, |
272 'foo 1.0.0': { | 287 'foo 1.0.0': { |
273 'myapp': '<1.0.0' | 288 'myapp': '<1.0.0' |
274 } | 289 } |
275 }, error: couldNotSolve); | 290 }, error: couldNotSolve); |
276 } | 291 } |
277 | |
278 devDependency() { | 292 devDependency() { |
279 testResolve("includes root package's dev dependencies", { | 293 testResolve("includes root package's dev dependencies", { |
280 'myapp 1.0.0': { | 294 'myapp 1.0.0': { |
281 '(dev) foo': '1.0.0', | 295 '(dev) foo': '1.0.0', |
282 '(dev) bar': '1.0.0' | 296 '(dev) bar': '1.0.0' |
283 }, | 297 }, |
284 'foo 1.0.0': {}, | 298 'foo 1.0.0': {}, |
285 'bar 1.0.0': {} | 299 'bar 1.0.0': {} |
286 }, result: { | 300 }, result: { |
287 'myapp from root': '1.0.0', | 301 'myapp from root': '1.0.0', |
288 'foo': '1.0.0', | 302 'foo': '1.0.0', |
289 'bar': '1.0.0' | 303 'bar': '1.0.0' |
290 }); | 304 }); |
291 | |
292 testResolve("includes dev dependency's transitive dependencies", { | 305 testResolve("includes dev dependency's transitive dependencies", { |
293 'myapp 1.0.0': { | 306 'myapp 1.0.0': { |
294 '(dev) foo': '1.0.0' | 307 '(dev) foo': '1.0.0' |
295 }, | 308 }, |
296 'foo 1.0.0': { | 309 'foo 1.0.0': { |
297 'bar': '1.0.0' | 310 'bar': '1.0.0' |
298 }, | 311 }, |
299 'bar 1.0.0': {} | 312 'bar 1.0.0': {} |
300 }, result: { | 313 }, result: { |
301 'myapp from root': '1.0.0', | 314 'myapp from root': '1.0.0', |
302 'foo': '1.0.0', | 315 'foo': '1.0.0', |
303 'bar': '1.0.0' | 316 'bar': '1.0.0' |
304 }); | 317 }); |
305 | |
306 testResolve("ignores transitive dependency's dev dependencies", { | 318 testResolve("ignores transitive dependency's dev dependencies", { |
307 'myapp 1.0.0': { | 319 'myapp 1.0.0': { |
308 'foo': '1.0.0' | 320 'foo': '1.0.0' |
309 }, | 321 }, |
310 'foo 1.0.0': { | 322 'foo 1.0.0': { |
311 '(dev) bar': '1.0.0' | 323 '(dev) bar': '1.0.0' |
312 }, | 324 }, |
313 'bar 1.0.0': {} | 325 'bar 1.0.0': {} |
314 }, result: { | 326 }, result: { |
315 'myapp from root': '1.0.0', | 327 'myapp from root': '1.0.0', |
316 'foo': '1.0.0' | 328 'foo': '1.0.0' |
317 }); | 329 }); |
318 } | 330 } |
319 | |
320 unsolvable() { | 331 unsolvable() { |
321 testResolve('no version that matches requirement', { | 332 testResolve('no version that matches requirement', { |
322 'myapp 0.0.0': { | 333 'myapp 0.0.0': { |
323 'foo': '>=1.0.0 <2.0.0' | 334 'foo': '>=1.0.0 <2.0.0' |
324 }, | 335 }, |
325 'foo 2.0.0': {}, | 336 'foo 2.0.0': {}, |
326 'foo 2.1.3': {} | 337 'foo 2.1.3': {} |
327 }, error: noVersion(['myapp', 'foo'])); | 338 }, error: noVersion(['myapp', 'foo'])); |
328 | |
329 testResolve('no version that matches combined constraint', { | 339 testResolve('no version that matches combined constraint', { |
330 'myapp 0.0.0': { | 340 'myapp 0.0.0': { |
331 'foo': '1.0.0', | 341 'foo': '1.0.0', |
332 'bar': '1.0.0' | 342 'bar': '1.0.0' |
333 }, | 343 }, |
334 'foo 1.0.0': { | 344 'foo 1.0.0': { |
335 'shared': '>=2.0.0 <3.0.0' | 345 'shared': '>=2.0.0 <3.0.0' |
336 }, | 346 }, |
337 'bar 1.0.0': { | 347 'bar 1.0.0': { |
338 'shared': '>=2.9.0 <4.0.0' | 348 'shared': '>=2.9.0 <4.0.0' |
339 }, | 349 }, |
340 'shared 2.5.0': {}, | 350 'shared 2.5.0': {}, |
341 'shared 3.5.0': {} | 351 'shared 3.5.0': {} |
342 }, error: noVersion(['shared', 'foo', 'bar'])); | 352 }, error: noVersion(['shared', 'foo', 'bar'])); |
343 | |
344 testResolve('disjoint constraints', { | 353 testResolve('disjoint constraints', { |
345 'myapp 0.0.0': { | 354 'myapp 0.0.0': { |
346 'foo': '1.0.0', | 355 'foo': '1.0.0', |
347 'bar': '1.0.0' | 356 'bar': '1.0.0' |
348 }, | 357 }, |
349 'foo 1.0.0': { | 358 'foo 1.0.0': { |
350 'shared': '<=2.0.0' | 359 'shared': '<=2.0.0' |
351 }, | 360 }, |
352 'bar 1.0.0': { | 361 'bar 1.0.0': { |
353 'shared': '>3.0.0' | 362 'shared': '>3.0.0' |
354 }, | 363 }, |
355 'shared 2.0.0': {}, | 364 'shared 2.0.0': {}, |
356 'shared 4.0.0': {} | 365 'shared 4.0.0': {} |
357 }, error: disjointConstraint(['shared', 'foo', 'bar'])); | 366 }, error: disjointConstraint(['shared', 'foo', 'bar'])); |
358 | |
359 testResolve('mismatched descriptions', { | 367 testResolve('mismatched descriptions', { |
360 'myapp 0.0.0': { | 368 'myapp 0.0.0': { |
361 'foo': '1.0.0', | 369 'foo': '1.0.0', |
362 'bar': '1.0.0' | 370 'bar': '1.0.0' |
363 }, | 371 }, |
364 'foo 1.0.0': { | 372 'foo 1.0.0': { |
365 'shared-x': '1.0.0' | 373 'shared-x': '1.0.0' |
366 }, | 374 }, |
367 'bar 1.0.0': { | 375 'bar 1.0.0': { |
368 'shared-y': '1.0.0' | 376 'shared-y': '1.0.0' |
369 }, | 377 }, |
370 'shared-x 1.0.0': {}, | 378 'shared-x 1.0.0': {}, |
371 'shared-y 1.0.0': {} | 379 'shared-y 1.0.0': {} |
372 }, error: descriptionMismatch('shared', 'foo', 'bar')); | 380 }, error: descriptionMismatch('shared', 'foo', 'bar')); |
373 | |
374 testResolve('mismatched sources', { | 381 testResolve('mismatched sources', { |
375 'myapp 0.0.0': { | 382 'myapp 0.0.0': { |
376 'foo': '1.0.0', | 383 'foo': '1.0.0', |
377 'bar': '1.0.0' | 384 'bar': '1.0.0' |
378 }, | 385 }, |
379 'foo 1.0.0': { | 386 'foo 1.0.0': { |
380 'shared': '1.0.0' | 387 'shared': '1.0.0' |
381 }, | 388 }, |
382 'bar 1.0.0': { | 389 'bar 1.0.0': { |
383 'shared from mock2': '1.0.0' | 390 'shared from mock2': '1.0.0' |
384 }, | 391 }, |
385 'shared 1.0.0': {}, | 392 'shared 1.0.0': {}, |
386 'shared 1.0.0 from mock2': {} | 393 'shared 1.0.0 from mock2': {} |
387 }, error: sourceMismatch('shared', 'foo', 'bar')); | 394 }, error: sourceMismatch('shared', 'foo', 'bar')); |
388 | |
389 testResolve('no valid solution', { | 395 testResolve('no valid solution', { |
390 'myapp 0.0.0': { | 396 'myapp 0.0.0': { |
391 'a': 'any', | 397 'a': 'any', |
392 'b': 'any' | 398 'b': 'any' |
393 }, | 399 }, |
394 'a 1.0.0': { | 400 'a 1.0.0': { |
395 'b': '1.0.0' | 401 'b': '1.0.0' |
396 }, | 402 }, |
397 'a 2.0.0': { | 403 'a 2.0.0': { |
398 'b': '2.0.0' | 404 'b': '2.0.0' |
399 }, | 405 }, |
400 'b 1.0.0': { | 406 'b 1.0.0': { |
401 'a': '2.0.0' | 407 'a': '2.0.0' |
402 }, | 408 }, |
403 'b 2.0.0': { | 409 'b 2.0.0': { |
404 'a': '1.0.0' | 410 'a': '1.0.0' |
405 } | 411 } |
406 }, error: couldNotSolve, maxTries: 4); | 412 }, error: couldNotSolve, maxTries: 4); |
407 | |
408 // This is a regression test for #15550. | |
409 testResolve('no version that matches while backtracking', { | 413 testResolve('no version that matches while backtracking', { |
410 'myapp 0.0.0': { | 414 'myapp 0.0.0': { |
411 'a': 'any', | 415 'a': 'any', |
412 'b': '>1.0.0' | 416 'b': '>1.0.0' |
413 }, | 417 }, |
414 'a 1.0.0': {}, | 418 'a 1.0.0': {}, |
415 'b 1.0.0': {} | 419 'b 1.0.0': {} |
416 }, error: noVersion(['myapp', 'b']), maxTries: 1); | 420 }, error: noVersion(['myapp', 'b']), maxTries: 1); |
417 | |
418 | |
419 // This is a regression test for #18300. | |
420 testResolve('...', { | 421 testResolve('...', { |
421 "myapp 0.0.0": { | 422 "myapp 0.0.0": { |
422 "angular": "any", | 423 "angular": "any", |
423 "collection": "any" | 424 "collection": "any" |
424 }, | 425 }, |
425 "analyzer 0.12.2": {}, | 426 "analyzer 0.12.2": {}, |
426 "angular 0.10.0": { | 427 "angular 0.10.0": { |
427 "di": ">=0.0.32 <0.1.0", | 428 "di": ">=0.0.32 <0.1.0", |
428 "collection": ">=0.9.1 <1.0.0" | 429 "collection": ">=0.9.1 <1.0.0" |
429 }, | 430 }, |
430 "angular 0.9.11": { | 431 "angular 0.9.11": { |
431 "di": ">=0.0.32 <0.1.0", | 432 "di": ">=0.0.32 <0.1.0", |
432 "collection": ">=0.9.1 <1.0.0" | 433 "collection": ">=0.9.1 <1.0.0" |
433 }, | 434 }, |
434 "angular 0.9.10": { | 435 "angular 0.9.10": { |
435 "di": ">=0.0.32 <0.1.0", | 436 "di": ">=0.0.32 <0.1.0", |
436 "collection": ">=0.9.1 <1.0.0" | 437 "collection": ">=0.9.1 <1.0.0" |
437 }, | 438 }, |
438 "collection 0.9.0": {}, | 439 "collection 0.9.0": {}, |
439 "collection 0.9.1": {}, | 440 "collection 0.9.1": {}, |
440 "di 0.0.37": {"analyzer": ">=0.13.0 <0.14.0"}, | 441 "di 0.0.37": { |
441 "di 0.0.36": {"analyzer": ">=0.13.0 <0.14.0"} | 442 "analyzer": ">=0.13.0 <0.14.0" |
| 443 }, |
| 444 "di 0.0.36": { |
| 445 "analyzer": ">=0.13.0 <0.14.0" |
| 446 } |
442 }, error: noVersion(['myapp', 'angular', 'collection']), maxTries: 9); | 447 }, error: noVersion(['myapp', 'angular', 'collection']), maxTries: 9); |
443 } | 448 } |
444 | |
445 badSource() { | 449 badSource() { |
446 testResolve('fail if the root package has a bad source in dep', { | 450 testResolve('fail if the root package has a bad source in dep', { |
447 'myapp 0.0.0': { | 451 'myapp 0.0.0': { |
448 'foo from bad': 'any' | 452 'foo from bad': 'any' |
449 }, | 453 } |
450 }, error: unknownSource('myapp', 'foo', 'bad')); | 454 }, error: unknownSource('myapp', 'foo', 'bad')); |
451 | |
452 testResolve('fail if the root package has a bad source in dev dep', { | 455 testResolve('fail if the root package has a bad source in dev dep', { |
453 'myapp 0.0.0': { | 456 'myapp 0.0.0': { |
454 '(dev) foo from bad': 'any' | 457 '(dev) foo from bad': 'any' |
455 }, | 458 } |
456 }, error: unknownSource('myapp', 'foo', 'bad')); | 459 }, error: unknownSource('myapp', 'foo', 'bad')); |
457 | |
458 testResolve('fail if all versions have bad source in dep', { | 460 testResolve('fail if all versions have bad source in dep', { |
459 'myapp 0.0.0': { | 461 'myapp 0.0.0': { |
460 'foo': 'any' | 462 'foo': 'any' |
461 }, | 463 }, |
462 'foo 1.0.0': { | 464 'foo 1.0.0': { |
463 'bar from bad': 'any' | 465 'bar from bad': 'any' |
464 }, | 466 }, |
465 'foo 1.0.1': { | 467 'foo 1.0.1': { |
466 'baz from bad': 'any' | 468 'baz from bad': 'any' |
467 }, | 469 }, |
468 'foo 1.0.3': { | 470 'foo 1.0.3': { |
469 'bang from bad': 'any' | 471 'bang from bad': 'any' |
470 }, | 472 } |
471 }, error: unknownSource('foo', 'bar', 'bad'), maxTries: 3); | 473 }, error: unknownSource('foo', 'bar', 'bad'), maxTries: 3); |
472 | |
473 testResolve('ignore versions with bad source in dep', { | 474 testResolve('ignore versions with bad source in dep', { |
474 'myapp 1.0.0': { | 475 'myapp 1.0.0': { |
475 'foo': 'any' | 476 'foo': 'any' |
476 }, | 477 }, |
477 'foo 1.0.0': { | 478 'foo 1.0.0': { |
478 'bar': 'any' | 479 'bar': 'any' |
479 }, | 480 }, |
480 'foo 1.0.1': { | 481 'foo 1.0.1': { |
481 'bar from bad': 'any' | 482 'bar from bad': 'any' |
482 }, | 483 }, |
483 'foo 1.0.3': { | 484 'foo 1.0.3': { |
484 'bar from bad': 'any' | 485 'bar from bad': 'any' |
485 }, | 486 }, |
486 'bar 1.0.0': {} | 487 'bar 1.0.0': {} |
487 }, result: { | 488 }, result: { |
488 'myapp from root': '1.0.0', | 489 'myapp from root': '1.0.0', |
489 'foo': '1.0.0', | 490 'foo': '1.0.0', |
490 'bar': '1.0.0' | 491 'bar': '1.0.0' |
491 }, maxTries: 3); | 492 }, maxTries: 3); |
492 } | 493 } |
493 | |
494 backtracking() { | 494 backtracking() { |
495 testResolve('circular dependency on older version', { | 495 testResolve('circular dependency on older version', { |
496 'myapp 0.0.0': { | 496 'myapp 0.0.0': { |
497 'a': '>=1.0.0' | 497 'a': '>=1.0.0' |
498 }, | 498 }, |
499 'a 1.0.0': {}, | 499 'a 1.0.0': {}, |
500 'a 2.0.0': { | 500 'a 2.0.0': { |
501 'b': '1.0.0' | 501 'b': '1.0.0' |
502 }, | 502 }, |
503 'b 1.0.0': { | 503 'b 1.0.0': { |
504 'a': '1.0.0' | 504 'a': '1.0.0' |
505 } | 505 } |
506 }, result: { | 506 }, result: { |
507 'myapp from root': '0.0.0', | 507 'myapp from root': '0.0.0', |
508 'a': '1.0.0' | 508 'a': '1.0.0' |
509 }, maxTries: 2); | 509 }, maxTries: 2); |
510 | |
511 // The latest versions of a and b disagree on c. An older version of either | |
512 // will resolve the problem. This test validates that b, which is farther | |
513 // in the dependency graph from myapp is downgraded first. | |
514 testResolve('rolls back leaf versions first', { | 510 testResolve('rolls back leaf versions first', { |
515 'myapp 0.0.0': { | 511 'myapp 0.0.0': { |
516 'a': 'any' | 512 'a': 'any' |
517 }, | 513 }, |
518 'a 1.0.0': { | 514 'a 1.0.0': { |
519 'b': 'any' | 515 'b': 'any' |
520 }, | 516 }, |
521 'a 2.0.0': { | 517 'a 2.0.0': { |
522 'b': 'any', | 518 'b': 'any', |
523 'c': '2.0.0' | 519 'c': '2.0.0' |
524 }, | 520 }, |
525 'b 1.0.0': {}, | 521 'b 1.0.0': {}, |
526 'b 2.0.0': { | 522 'b 2.0.0': { |
527 'c': '1.0.0' | 523 'c': '1.0.0' |
528 }, | 524 }, |
529 'c 1.0.0': {}, | 525 'c 1.0.0': {}, |
530 'c 2.0.0': {} | 526 'c 2.0.0': {} |
531 }, result: { | 527 }, result: { |
532 'myapp from root': '0.0.0', | 528 'myapp from root': '0.0.0', |
533 'a': '2.0.0', | 529 'a': '2.0.0', |
534 'b': '1.0.0', | 530 'b': '1.0.0', |
535 'c': '2.0.0' | 531 'c': '2.0.0' |
536 }, maxTries: 2); | 532 }, maxTries: 2); |
537 | |
538 // Only one version of baz, so foo and bar will have to downgrade until they | |
539 // reach it. | |
540 testResolve('simple transitive', { | 533 testResolve('simple transitive', { |
541 'myapp 0.0.0': {'foo': 'any'}, | 534 'myapp 0.0.0': { |
542 'foo 1.0.0': {'bar': '1.0.0'}, | 535 'foo': 'any' |
543 'foo 2.0.0': {'bar': '2.0.0'}, | 536 }, |
544 'foo 3.0.0': {'bar': '3.0.0'}, | 537 'foo 1.0.0': { |
545 'bar 1.0.0': {'baz': 'any'}, | 538 'bar': '1.0.0' |
546 'bar 2.0.0': {'baz': '2.0.0'}, | 539 }, |
547 'bar 3.0.0': {'baz': '3.0.0'}, | 540 'foo 2.0.0': { |
| 541 'bar': '2.0.0' |
| 542 }, |
| 543 'foo 3.0.0': { |
| 544 'bar': '3.0.0' |
| 545 }, |
| 546 'bar 1.0.0': { |
| 547 'baz': 'any' |
| 548 }, |
| 549 'bar 2.0.0': { |
| 550 'baz': '2.0.0' |
| 551 }, |
| 552 'bar 3.0.0': { |
| 553 'baz': '3.0.0' |
| 554 }, |
548 'baz 1.0.0': {} | 555 'baz 1.0.0': {} |
549 }, result: { | 556 }, result: { |
550 'myapp from root': '0.0.0', | 557 'myapp from root': '0.0.0', |
551 'foo': '1.0.0', | 558 'foo': '1.0.0', |
552 'bar': '1.0.0', | 559 'bar': '1.0.0', |
553 'baz': '1.0.0' | 560 'baz': '1.0.0' |
554 }, maxTries: 3); | 561 }, maxTries: 3); |
555 | |
556 // This ensures it doesn't exhaustively search all versions of b when it's | |
557 // a-2.0.0 whose dependency on c-2.0.0-nonexistent led to the problem. We | |
558 // make sure b has more versions than a so that the solver tries a first | |
559 // since it sorts sibling dependencies by number of versions. | |
560 testResolve('backjump to nearer unsatisfied package', { | 562 testResolve('backjump to nearer unsatisfied package', { |
561 'myapp 0.0.0': { | 563 'myapp 0.0.0': { |
562 'a': 'any', | 564 'a': 'any', |
563 'b': 'any' | 565 'b': 'any' |
564 }, | 566 }, |
565 'a 1.0.0': { 'c': '1.0.0' }, | 567 'a 1.0.0': { |
566 'a 2.0.0': { 'c': '2.0.0-nonexistent' }, | 568 'c': '1.0.0' |
| 569 }, |
| 570 'a 2.0.0': { |
| 571 'c': '2.0.0-nonexistent' |
| 572 }, |
567 'b 1.0.0': {}, | 573 'b 1.0.0': {}, |
568 'b 2.0.0': {}, | 574 'b 2.0.0': {}, |
569 'b 3.0.0': {}, | 575 'b 3.0.0': {}, |
570 'c 1.0.0': {}, | 576 'c 1.0.0': {} |
571 }, result: { | 577 }, result: { |
572 'myapp from root': '0.0.0', | 578 'myapp from root': '0.0.0', |
573 'a': '1.0.0', | 579 'a': '1.0.0', |
574 'b': '3.0.0', | 580 'b': '3.0.0', |
575 'c': '1.0.0' | 581 'c': '1.0.0' |
576 }, maxTries: 2); | 582 }, maxTries: 2); |
577 | |
578 // Tests that the backjumper will jump past unrelated selections when a | |
579 // source conflict occurs. This test selects, in order: | |
580 // - myapp -> a | |
581 // - myapp -> b | |
582 // - myapp -> c (1 of 5) | |
583 // - b -> a | |
584 // It selects a and b first because they have fewer versions than c. It | |
585 // traverses b's dependency on a after selecting a version of c because | |
586 // dependencies are traversed breadth-first (all of myapps's immediate deps | |
587 // before any other their deps). | |
588 // | |
589 // This means it doesn't discover the source conflict until after selecting | |
590 // c. When that happens, it should backjump past c instead of trying older | |
591 // versions of it since they aren't related to the conflict. | |
592 testResolve('backjump to conflicting source', { | 583 testResolve('backjump to conflicting source', { |
593 'myapp 0.0.0': { | 584 'myapp 0.0.0': { |
594 'a': 'any', | 585 'a': 'any', |
595 'b': 'any', | 586 'b': 'any', |
596 'c': 'any' | 587 'c': 'any' |
597 }, | 588 }, |
598 'a 1.0.0': {}, | 589 'a 1.0.0': {}, |
599 'a 1.0.0 from mock2': {}, | 590 'a 1.0.0 from mock2': {}, |
600 'b 1.0.0': { | 591 'b 1.0.0': { |
601 'a': 'any' | 592 'a': 'any' |
602 }, | 593 }, |
603 'b 2.0.0': { | 594 'b 2.0.0': { |
604 'a from mock2': 'any' | 595 'a from mock2': 'any' |
605 }, | 596 }, |
606 'c 1.0.0': {}, | 597 'c 1.0.0': {}, |
607 'c 2.0.0': {}, | 598 'c 2.0.0': {}, |
608 'c 3.0.0': {}, | 599 'c 3.0.0': {}, |
609 'c 4.0.0': {}, | 600 'c 4.0.0': {}, |
610 'c 5.0.0': {}, | 601 'c 5.0.0': {} |
611 }, result: { | 602 }, result: { |
612 'myapp from root': '0.0.0', | 603 'myapp from root': '0.0.0', |
613 'a': '1.0.0', | 604 'a': '1.0.0', |
614 'b': '1.0.0', | 605 'b': '1.0.0', |
615 'c': '5.0.0' | 606 'c': '5.0.0' |
616 }, maxTries: 2); | 607 }, maxTries: 2); |
617 | |
618 // Like the above test, but for a conflicting description. | |
619 testResolve('backjump to conflicting description', { | 608 testResolve('backjump to conflicting description', { |
620 'myapp 0.0.0': { | 609 'myapp 0.0.0': { |
621 'a-x': 'any', | 610 'a-x': 'any', |
622 'b': 'any', | 611 'b': 'any', |
623 'c': 'any' | 612 'c': 'any' |
624 }, | 613 }, |
625 'a-x 1.0.0': {}, | 614 'a-x 1.0.0': {}, |
626 'a-y 1.0.0': {}, | 615 'a-y 1.0.0': {}, |
627 'b 1.0.0': { | 616 'b 1.0.0': { |
628 'a-x': 'any' | 617 'a-x': 'any' |
629 }, | 618 }, |
630 'b 2.0.0': { | 619 'b 2.0.0': { |
631 'a-y': 'any' | 620 'a-y': 'any' |
632 }, | 621 }, |
633 'c 1.0.0': {}, | 622 'c 1.0.0': {}, |
634 'c 2.0.0': {}, | 623 'c 2.0.0': {}, |
635 'c 3.0.0': {}, | 624 'c 3.0.0': {}, |
636 'c 4.0.0': {}, | 625 'c 4.0.0': {}, |
637 'c 5.0.0': {}, | 626 'c 5.0.0': {} |
638 }, result: { | 627 }, result: { |
639 'myapp from root': '0.0.0', | 628 'myapp from root': '0.0.0', |
640 'a': '1.0.0', | 629 'a': '1.0.0', |
641 'b': '1.0.0', | 630 'b': '1.0.0', |
642 'c': '5.0.0' | 631 'c': '5.0.0' |
643 }, maxTries: 2); | 632 }, maxTries: 2); |
644 | |
645 // Similar to the above two tests but where there is no solution. It should | |
646 // fail in this case with no backtracking. | |
647 testResolve('backjump to conflicting source', { | 633 testResolve('backjump to conflicting source', { |
648 'myapp 0.0.0': { | 634 'myapp 0.0.0': { |
649 'a': 'any', | 635 'a': 'any', |
650 'b': 'any', | 636 'b': 'any', |
651 'c': 'any' | 637 'c': 'any' |
652 }, | 638 }, |
653 'a 1.0.0': {}, | 639 'a 1.0.0': {}, |
654 'a 1.0.0 from mock2': {}, | 640 'a 1.0.0 from mock2': {}, |
655 'b 1.0.0': { | 641 'b 1.0.0': { |
656 'a from mock2': 'any' | 642 'a from mock2': 'any' |
657 }, | 643 }, |
658 'c 1.0.0': {}, | 644 'c 1.0.0': {}, |
659 'c 2.0.0': {}, | 645 'c 2.0.0': {}, |
660 'c 3.0.0': {}, | 646 'c 3.0.0': {}, |
661 'c 4.0.0': {}, | 647 'c 4.0.0': {}, |
662 'c 5.0.0': {}, | 648 'c 5.0.0': {} |
663 }, error: sourceMismatch('a', 'myapp', 'b'), maxTries: 1); | 649 }, error: sourceMismatch('a', 'myapp', 'b'), maxTries: 1); |
664 | |
665 testResolve('backjump to conflicting description', { | 650 testResolve('backjump to conflicting description', { |
666 'myapp 0.0.0': { | 651 'myapp 0.0.0': { |
667 'a-x': 'any', | 652 'a-x': 'any', |
668 'b': 'any', | 653 'b': 'any', |
669 'c': 'any' | 654 'c': 'any' |
670 }, | 655 }, |
671 'a-x 1.0.0': {}, | 656 'a-x 1.0.0': {}, |
672 'a-y 1.0.0': {}, | 657 'a-y 1.0.0': {}, |
673 'b 1.0.0': { | 658 'b 1.0.0': { |
674 'a-y': 'any' | 659 'a-y': 'any' |
675 }, | 660 }, |
676 'c 1.0.0': {}, | 661 'c 1.0.0': {}, |
677 'c 2.0.0': {}, | 662 'c 2.0.0': {}, |
678 'c 3.0.0': {}, | 663 'c 3.0.0': {}, |
679 'c 4.0.0': {}, | 664 'c 4.0.0': {}, |
680 'c 5.0.0': {}, | 665 'c 5.0.0': {} |
681 }, error: descriptionMismatch('a', 'myapp', 'b'), maxTries: 1); | 666 }, error: descriptionMismatch('a', 'myapp', 'b'), maxTries: 1); |
682 | |
683 // Dependencies are ordered so that packages with fewer versions are tried | |
684 // first. Here, there are two valid solutions (either a or b must be | |
685 // downgraded once). The chosen one depends on which dep is traversed first. | |
686 // Since b has fewer versions, it will be traversed first, which means a will | |
687 // come later. Since later selections are revised first, a gets downgraded. | |
688 testResolve('traverse into package with fewer versions first', { | 667 testResolve('traverse into package with fewer versions first', { |
689 'myapp 0.0.0': { | 668 'myapp 0.0.0': { |
690 'a': 'any', | 669 'a': 'any', |
691 'b': 'any' | 670 'b': 'any' |
692 }, | 671 }, |
693 'a 1.0.0': {'c': 'any'}, | 672 'a 1.0.0': { |
694 'a 2.0.0': {'c': 'any'}, | 673 'c': 'any' |
695 'a 3.0.0': {'c': 'any'}, | 674 }, |
696 'a 4.0.0': {'c': 'any'}, | 675 'a 2.0.0': { |
697 'a 5.0.0': {'c': '1.0.0'}, | 676 'c': 'any' |
698 'b 1.0.0': {'c': 'any'}, | 677 }, |
699 'b 2.0.0': {'c': 'any'}, | 678 'a 3.0.0': { |
700 'b 3.0.0': {'c': 'any'}, | 679 'c': 'any' |
701 'b 4.0.0': {'c': '2.0.0'}, | 680 }, |
| 681 'a 4.0.0': { |
| 682 'c': 'any' |
| 683 }, |
| 684 'a 5.0.0': { |
| 685 'c': '1.0.0' |
| 686 }, |
| 687 'b 1.0.0': { |
| 688 'c': 'any' |
| 689 }, |
| 690 'b 2.0.0': { |
| 691 'c': 'any' |
| 692 }, |
| 693 'b 3.0.0': { |
| 694 'c': 'any' |
| 695 }, |
| 696 'b 4.0.0': { |
| 697 'c': '2.0.0' |
| 698 }, |
702 'c 1.0.0': {}, | 699 'c 1.0.0': {}, |
703 'c 2.0.0': {}, | 700 'c 2.0.0': {} |
704 }, result: { | 701 }, result: { |
705 'myapp from root': '0.0.0', | 702 'myapp from root': '0.0.0', |
706 'a': '4.0.0', | 703 'a': '4.0.0', |
707 'b': '4.0.0', | 704 'b': '4.0.0', |
708 'c': '2.0.0' | 705 'c': '2.0.0' |
709 }, maxTries: 2); | 706 }, maxTries: 2); |
710 | |
711 // This is similar to the above test. When getting the number of versions of | |
712 // a package to determine which to traverse first, versions that are | |
713 // disallowed by the root package's constraints should not be considered. | |
714 // Here, foo has more versions of bar in total (4), but fewer that meet | |
715 // myapp's constraints (only 2). There is no solution, but we will do less | |
716 // backtracking if foo is tested first. | |
717 testResolve('take root package constraints into counting versions', { | 707 testResolve('take root package constraints into counting versions', { |
718 "myapp 0.0.0": { | 708 "myapp 0.0.0": { |
719 "foo": ">2.0.0", | 709 "foo": ">2.0.0", |
720 "bar": "any" | 710 "bar": "any" |
721 }, | 711 }, |
722 "foo 1.0.0": {"none": "2.0.0"}, | 712 "foo 1.0.0": { |
723 "foo 2.0.0": {"none": "2.0.0"}, | 713 "none": "2.0.0" |
724 "foo 3.0.0": {"none": "2.0.0"}, | 714 }, |
725 "foo 4.0.0": {"none": "2.0.0"}, | 715 "foo 2.0.0": { |
| 716 "none": "2.0.0" |
| 717 }, |
| 718 "foo 3.0.0": { |
| 719 "none": "2.0.0" |
| 720 }, |
| 721 "foo 4.0.0": { |
| 722 "none": "2.0.0" |
| 723 }, |
726 "bar 1.0.0": {}, | 724 "bar 1.0.0": {}, |
727 "bar 2.0.0": {}, | 725 "bar 2.0.0": {}, |
728 "bar 3.0.0": {}, | 726 "bar 3.0.0": {}, |
729 "none 1.0.0": {} | 727 "none 1.0.0": {} |
730 }, error: noVersion(["foo", "none"]), maxTries: 2); | 728 }, error: noVersion(["foo", "none"]), maxTries: 2); |
731 | |
732 // This sets up a hundred versions of foo and bar, 0.0.0 through 9.9.0. Each | |
733 // version of foo depends on a baz with the same major version. Each version | |
734 // of bar depends on a baz with the same minor version. There is only one | |
735 // version of baz, 0.0.0, so only older versions of foo and bar will | |
736 // satisfy it. | |
737 var map = { | 729 var map = { |
738 'myapp 0.0.0': { | 730 'myapp 0.0.0': { |
739 'foo': 'any', | 731 'foo': 'any', |
740 'bar': 'any' | 732 'bar': 'any' |
741 }, | 733 }, |
742 'baz 0.0.0': {} | 734 'baz 0.0.0': {} |
743 }; | 735 }; |
744 | |
745 for (var i = 0; i < 10; i++) { | 736 for (var i = 0; i < 10; i++) { |
746 for (var j = 0; j < 10; j++) { | 737 for (var j = 0; j < 10; j++) { |
747 map['foo $i.$j.0'] = {'baz': '$i.0.0'}; | 738 map['foo $i.$j.0'] = { |
748 map['bar $i.$j.0'] = {'baz': '0.$j.0'}; | 739 'baz': '$i.0.0' |
| 740 }; |
| 741 map['bar $i.$j.0'] = { |
| 742 'baz': '0.$j.0' |
| 743 }; |
749 } | 744 } |
750 } | 745 } |
751 | |
752 testResolve('complex backtrack', map, result: { | 746 testResolve('complex backtrack', map, result: { |
753 'myapp from root': '0.0.0', | 747 'myapp from root': '0.0.0', |
754 'foo': '0.9.0', | 748 'foo': '0.9.0', |
755 'bar': '9.0.0', | 749 'bar': '9.0.0', |
756 'baz': '0.0.0' | 750 'baz': '0.0.0' |
757 }, maxTries: 100); | 751 }, maxTries: 100); |
758 | |
759 // If there's a disjoint constraint on a package, then selecting other | |
760 // versions of it is a waste of time: no possible versions can match. We need | |
761 // to jump past it to the most recent package that affected the constraint. | |
762 testResolve('backjump past failed package on disjoint constraint', { | 752 testResolve('backjump past failed package on disjoint constraint', { |
763 'myapp 0.0.0': { | 753 'myapp 0.0.0': { |
764 'a': 'any', | 754 'a': 'any', |
765 'foo': '>2.0.0' | 755 'foo': '>2.0.0' |
766 }, | 756 }, |
767 'a 1.0.0': { | 757 'a 1.0.0': { |
768 'foo': 'any' // ok | 758 'foo': 'any' |
769 }, | 759 }, |
770 'a 2.0.0': { | 760 'a 2.0.0': { |
771 'foo': '<1.0.0' // disjoint with myapp's constraint on foo | 761 'foo': '<1.0.0' |
772 }, | 762 }, |
773 'foo 2.0.0': {}, | 763 'foo 2.0.0': {}, |
774 'foo 2.0.1': {}, | 764 'foo 2.0.1': {}, |
775 'foo 2.0.2': {}, | 765 'foo 2.0.2': {}, |
776 'foo 2.0.3': {}, | 766 'foo 2.0.3': {}, |
777 'foo 2.0.4': {} | 767 'foo 2.0.4': {} |
778 }, result: { | 768 }, result: { |
779 'myapp from root': '0.0.0', | 769 'myapp from root': '0.0.0', |
780 'a': '1.0.0', | 770 'a': '1.0.0', |
781 'foo': '2.0.4' | 771 'foo': '2.0.4' |
782 }, maxTries: 2); | 772 }, maxTries: 2); |
783 | |
784 // This is a regression test for #18666. It was possible for the solver to | |
785 // "forget" that a package had previously led to an error. In that case, it | |
786 // would backtrack over the failed package instead of trying different | |
787 // versions of it. | |
788 testResolve("finds solution with less strict constraint", { | 773 testResolve("finds solution with less strict constraint", { |
789 "myapp 1.0.0": { | 774 "myapp 1.0.0": { |
790 "a": "any", | 775 "a": "any", |
791 "c": "any", | 776 "c": "any", |
792 "d": "any" | 777 "d": "any" |
793 }, | 778 }, |
794 "a 2.0.0": {}, | 779 "a 2.0.0": {}, |
795 "a 1.0.0": {}, | 780 "a 1.0.0": {}, |
796 "b 1.0.0": {"a": "1.0.0"}, | 781 "b 1.0.0": { |
797 "c 1.0.0": {"b": "any"}, | 782 "a": "1.0.0" |
798 "d 2.0.0": {"myapp": "any"}, | 783 }, |
799 "d 1.0.0": {"myapp": "<1.0.0"} | 784 "c 1.0.0": { |
| 785 "b": "any" |
| 786 }, |
| 787 "d 2.0.0": { |
| 788 "myapp": "any" |
| 789 }, |
| 790 "d 1.0.0": { |
| 791 "myapp": "<1.0.0" |
| 792 } |
800 }, result: { | 793 }, result: { |
801 'myapp from root': '1.0.0', | 794 'myapp from root': '1.0.0', |
802 'a': '1.0.0', | 795 'a': '1.0.0', |
803 'b': '1.0.0', | 796 'b': '1.0.0', |
804 'c': '1.0.0', | 797 'c': '1.0.0', |
805 'd': '2.0.0' | 798 'd': '2.0.0' |
806 }, maxTries: 3); | 799 }, maxTries: 3); |
807 } | 800 } |
808 | |
809 sdkConstraint() { | 801 sdkConstraint() { |
810 var badVersion = '0.0.0-nope'; | 802 var badVersion = '0.0.0-nope'; |
811 var goodVersion = sdk.version.toString(); | 803 var goodVersion = sdk.version.toString(); |
812 | |
813 testResolve('root matches SDK', { | 804 testResolve('root matches SDK', { |
814 'myapp 0.0.0': {'sdk': goodVersion } | 805 'myapp 0.0.0': { |
| 806 'sdk': goodVersion |
| 807 } |
815 }, result: { | 808 }, result: { |
816 'myapp from root': '0.0.0' | 809 'myapp from root': '0.0.0' |
817 }); | 810 }); |
818 | |
819 testResolve('root does not match SDK', { | 811 testResolve('root does not match SDK', { |
820 'myapp 0.0.0': {'sdk': badVersion } | 812 'myapp 0.0.0': { |
| 813 'sdk': badVersion |
| 814 } |
821 }, error: couldNotSolve); | 815 }, error: couldNotSolve); |
822 | |
823 testResolve('dependency does not match SDK', { | 816 testResolve('dependency does not match SDK', { |
824 'myapp 0.0.0': {'foo': 'any'}, | 817 'myapp 0.0.0': { |
825 'foo 0.0.0': {'sdk': badVersion } | 818 'foo': 'any' |
| 819 }, |
| 820 'foo 0.0.0': { |
| 821 'sdk': badVersion |
| 822 } |
826 }, error: couldNotSolve); | 823 }, error: couldNotSolve); |
827 | |
828 testResolve('transitive dependency does not match SDK', { | 824 testResolve('transitive dependency does not match SDK', { |
829 'myapp 0.0.0': {'foo': 'any'}, | 825 'myapp 0.0.0': { |
830 'foo 0.0.0': {'bar': 'any'}, | 826 'foo': 'any' |
831 'bar 0.0.0': {'sdk': badVersion } | 827 }, |
| 828 'foo 0.0.0': { |
| 829 'bar': 'any' |
| 830 }, |
| 831 'bar 0.0.0': { |
| 832 'sdk': badVersion |
| 833 } |
832 }, error: couldNotSolve); | 834 }, error: couldNotSolve); |
833 | |
834 testResolve('selects a dependency version that allows the SDK', { | 835 testResolve('selects a dependency version that allows the SDK', { |
835 'myapp 0.0.0': {'foo': 'any'}, | 836 'myapp 0.0.0': { |
836 'foo 1.0.0': {'sdk': goodVersion }, | 837 'foo': 'any' |
837 'foo 2.0.0': {'sdk': goodVersion }, | 838 }, |
838 'foo 3.0.0': {'sdk': badVersion }, | 839 'foo 1.0.0': { |
839 'foo 4.0.0': {'sdk': badVersion } | 840 'sdk': goodVersion |
| 841 }, |
| 842 'foo 2.0.0': { |
| 843 'sdk': goodVersion |
| 844 }, |
| 845 'foo 3.0.0': { |
| 846 'sdk': badVersion |
| 847 }, |
| 848 'foo 4.0.0': { |
| 849 'sdk': badVersion |
| 850 } |
840 }, result: { | 851 }, result: { |
841 'myapp from root': '0.0.0', | 852 'myapp from root': '0.0.0', |
842 'foo': '2.0.0' | 853 'foo': '2.0.0' |
843 }, maxTries: 3); | 854 }, maxTries: 3); |
844 | |
845 testResolve('selects a transitive dependency version that allows the SDK', { | 855 testResolve('selects a transitive dependency version that allows the SDK', { |
846 'myapp 0.0.0': {'foo': 'any'}, | 856 'myapp 0.0.0': { |
847 'foo 1.0.0': {'bar': 'any'}, | 857 'foo': 'any' |
848 'bar 1.0.0': {'sdk': goodVersion }, | 858 }, |
849 'bar 2.0.0': {'sdk': goodVersion }, | 859 'foo 1.0.0': { |
850 'bar 3.0.0': {'sdk': badVersion }, | 860 'bar': 'any' |
851 'bar 4.0.0': {'sdk': badVersion } | 861 }, |
| 862 'bar 1.0.0': { |
| 863 'sdk': goodVersion |
| 864 }, |
| 865 'bar 2.0.0': { |
| 866 'sdk': goodVersion |
| 867 }, |
| 868 'bar 3.0.0': { |
| 869 'sdk': badVersion |
| 870 }, |
| 871 'bar 4.0.0': { |
| 872 'sdk': badVersion |
| 873 } |
852 }, result: { | 874 }, result: { |
853 'myapp from root': '0.0.0', | 875 'myapp from root': '0.0.0', |
854 'foo': '1.0.0', | 876 'foo': '1.0.0', |
855 'bar': '2.0.0' | 877 'bar': '2.0.0' |
856 }, maxTries: 3); | 878 }, maxTries: 3); |
857 | 879 testResolve( |
858 testResolve('selects a dependency version that allows a transitive ' | 880 'selects a dependency version that allows a transitive ' |
859 'dependency that allows the SDK', { | 881 'dependency that allows the SDK', |
860 'myapp 0.0.0': {'foo': 'any'}, | 882 { |
861 'foo 1.0.0': {'bar': '1.0.0'}, | 883 'myapp 0.0.0': { |
862 'foo 2.0.0': {'bar': '2.0.0'}, | 884 'foo': 'any' |
863 'foo 3.0.0': {'bar': '3.0.0'}, | 885 }, |
864 'foo 4.0.0': {'bar': '4.0.0'}, | 886 'foo 1.0.0': { |
865 'bar 1.0.0': {'sdk': goodVersion }, | 887 'bar': '1.0.0' |
866 'bar 2.0.0': {'sdk': goodVersion }, | 888 }, |
867 'bar 3.0.0': {'sdk': badVersion }, | 889 'foo 2.0.0': { |
868 'bar 4.0.0': {'sdk': badVersion } | 890 'bar': '2.0.0' |
| 891 }, |
| 892 'foo 3.0.0': { |
| 893 'bar': '3.0.0' |
| 894 }, |
| 895 'foo 4.0.0': { |
| 896 'bar': '4.0.0' |
| 897 }, |
| 898 'bar 1.0.0': { |
| 899 'sdk': goodVersion |
| 900 }, |
| 901 'bar 2.0.0': { |
| 902 'sdk': goodVersion |
| 903 }, |
| 904 'bar 3.0.0': { |
| 905 'sdk': badVersion |
| 906 }, |
| 907 'bar 4.0.0': { |
| 908 'sdk': badVersion |
| 909 } |
869 }, result: { | 910 }, result: { |
870 'myapp from root': '0.0.0', | 911 'myapp from root': '0.0.0', |
871 'foo': '2.0.0', | 912 'foo': '2.0.0', |
872 'bar': '2.0.0' | 913 'bar': '2.0.0' |
873 }, maxTries: 3); | 914 }, maxTries: 3); |
874 } | 915 } |
875 | |
876 void prerelease() { | 916 void prerelease() { |
877 testResolve('prefer stable versions over unstable', { | 917 testResolve('prefer stable versions over unstable', { |
878 'myapp 0.0.0': { | 918 'myapp 0.0.0': { |
879 'a': 'any' | 919 'a': 'any' |
880 }, | 920 }, |
881 'a 1.0.0': {}, | 921 'a 1.0.0': {}, |
882 'a 1.1.0-dev': {}, | 922 'a 1.1.0-dev': {}, |
883 'a 2.0.0-dev': {}, | 923 'a 2.0.0-dev': {}, |
884 'a 3.0.0-dev': {} | 924 'a 3.0.0-dev': {} |
885 }, result: { | 925 }, result: { |
886 'myapp from root': '0.0.0', | 926 'myapp from root': '0.0.0', |
887 'a': '1.0.0' | 927 'a': '1.0.0' |
888 }); | 928 }); |
889 | |
890 testResolve('use latest allowed prerelease if no stable versions match', { | 929 testResolve('use latest allowed prerelease if no stable versions match', { |
891 'myapp 0.0.0': { | 930 'myapp 0.0.0': { |
892 'a': '<2.0.0' | 931 'a': '<2.0.0' |
893 }, | 932 }, |
894 'a 1.0.0-dev': {}, | 933 'a 1.0.0-dev': {}, |
895 'a 1.1.0-dev': {}, | 934 'a 1.1.0-dev': {}, |
896 'a 1.9.0-dev': {}, | 935 'a 1.9.0-dev': {}, |
897 'a 3.0.0': {} | 936 'a 3.0.0': {} |
898 }, result: { | 937 }, result: { |
899 'myapp from root': '0.0.0', | 938 'myapp from root': '0.0.0', |
900 'a': '1.9.0-dev' | 939 'a': '1.9.0-dev' |
901 }); | 940 }); |
902 | |
903 testResolve('use an earlier stable version on a < constraint', { | 941 testResolve('use an earlier stable version on a < constraint', { |
904 'myapp 0.0.0': { | 942 'myapp 0.0.0': { |
905 'a': '<2.0.0' | 943 'a': '<2.0.0' |
906 }, | 944 }, |
907 'a 1.0.0': {}, | 945 'a 1.0.0': {}, |
908 'a 1.1.0': {}, | 946 'a 1.1.0': {}, |
909 'a 2.0.0-dev': {}, | 947 'a 2.0.0-dev': {}, |
910 'a 2.0.0': {} | 948 'a 2.0.0': {} |
911 }, result: { | 949 }, result: { |
912 'myapp from root': '0.0.0', | 950 'myapp from root': '0.0.0', |
913 'a': '1.1.0' | 951 'a': '1.1.0' |
914 }); | 952 }); |
915 | |
916 testResolve('prefer a stable version even if constraint mentions unstable', { | 953 testResolve('prefer a stable version even if constraint mentions unstable', { |
917 'myapp 0.0.0': { | 954 'myapp 0.0.0': { |
918 'a': '<=2.0.0-dev' | 955 'a': '<=2.0.0-dev' |
919 }, | 956 }, |
920 'a 1.0.0': {}, | 957 'a 1.0.0': {}, |
921 'a 1.1.0': {}, | 958 'a 1.1.0': {}, |
922 'a 2.0.0-dev': {}, | 959 'a 2.0.0-dev': {}, |
923 'a 2.0.0': {} | 960 'a 2.0.0': {} |
924 }, result: { | 961 }, result: { |
925 'myapp from root': '0.0.0', | 962 'myapp from root': '0.0.0', |
926 'a': '1.1.0' | 963 'a': '1.1.0' |
927 }); | 964 }); |
928 } | 965 } |
929 | |
930 void override() { | 966 void override() { |
931 testResolve('chooses best version matching override constraint', { | 967 testResolve('chooses best version matching override constraint', { |
932 'myapp 0.0.0': { | 968 'myapp 0.0.0': { |
933 'a': 'any' | 969 'a': 'any' |
934 }, | 970 }, |
935 'a 1.0.0': {}, | 971 'a 1.0.0': {}, |
936 'a 2.0.0': {}, | 972 'a 2.0.0': {}, |
937 'a 3.0.0': {} | 973 'a 3.0.0': {} |
938 }, overrides: { | 974 }, overrides: { |
939 'a': '<3.0.0' | 975 'a': '<3.0.0' |
940 }, result: { | 976 }, result: { |
941 'myapp from root': '0.0.0', | 977 'myapp from root': '0.0.0', |
942 'a': '2.0.0' | 978 'a': '2.0.0' |
943 }); | 979 }); |
944 | |
945 testResolve('uses override as dependency', { | 980 testResolve('uses override as dependency', { |
946 'myapp 0.0.0': {}, | 981 'myapp 0.0.0': {}, |
947 'a 1.0.0': {}, | 982 'a 1.0.0': {}, |
948 'a 2.0.0': {}, | 983 'a 2.0.0': {}, |
949 'a 3.0.0': {} | 984 'a 3.0.0': {} |
950 }, overrides: { | 985 }, overrides: { |
951 'a': '<3.0.0' | 986 'a': '<3.0.0' |
952 }, result: { | 987 }, result: { |
953 'myapp from root': '0.0.0', | 988 'myapp from root': '0.0.0', |
954 'a': '2.0.0' | 989 'a': '2.0.0' |
955 }); | 990 }); |
956 | |
957 testResolve('ignores other constraints on overridden package', { | 991 testResolve('ignores other constraints on overridden package', { |
958 'myapp 0.0.0': { | 992 'myapp 0.0.0': { |
959 'b': 'any', | 993 'b': 'any', |
960 'c': 'any' | 994 'c': 'any' |
961 }, | 995 }, |
962 'a 1.0.0': {}, | 996 'a 1.0.0': {}, |
963 'a 2.0.0': {}, | 997 'a 2.0.0': {}, |
964 'a 3.0.0': {}, | 998 'a 3.0.0': {}, |
965 'b 1.0.0': { | 999 'b 1.0.0': { |
966 'a': '1.0.0' | 1000 'a': '1.0.0' |
967 }, | 1001 }, |
968 'c 1.0.0': { | 1002 'c 1.0.0': { |
969 'a': '3.0.0' | 1003 'a': '3.0.0' |
970 } | 1004 } |
971 }, overrides: { | 1005 }, overrides: { |
972 'a': '2.0.0' | 1006 'a': '2.0.0' |
973 }, result: { | 1007 }, result: { |
974 'myapp from root': '0.0.0', | 1008 'myapp from root': '0.0.0', |
975 'a': '2.0.0', | 1009 'a': '2.0.0', |
976 'b': '1.0.0', | 1010 'b': '1.0.0', |
977 'c': '1.0.0' | 1011 'c': '1.0.0' |
978 }); | 1012 }); |
979 | |
980 testResolve('backtracks on overidden package for its constraints', { | 1013 testResolve('backtracks on overidden package for its constraints', { |
981 'myapp 0.0.0': { | 1014 'myapp 0.0.0': { |
982 'shared': '2.0.0' | 1015 'shared': '2.0.0' |
983 }, | 1016 }, |
984 'a 1.0.0': { | 1017 'a 1.0.0': { |
985 'shared': 'any' | 1018 'shared': 'any' |
986 }, | 1019 }, |
987 'a 2.0.0': { | 1020 'a 2.0.0': { |
988 'shared': '1.0.0' | 1021 'shared': '1.0.0' |
989 }, | 1022 }, |
990 'shared 1.0.0': {}, | 1023 'shared 1.0.0': {}, |
991 'shared 2.0.0': {} | 1024 'shared 2.0.0': {} |
992 }, overrides: { | 1025 }, overrides: { |
993 'a': '<3.0.0' | 1026 'a': '<3.0.0' |
994 }, result: { | 1027 }, result: { |
995 'myapp from root': '0.0.0', | 1028 'myapp from root': '0.0.0', |
996 'a': '1.0.0', | 1029 'a': '1.0.0', |
997 'shared': '2.0.0' | 1030 'shared': '2.0.0' |
998 }, maxTries: 2); | 1031 }, maxTries: 2); |
999 | |
1000 testResolve('override compatible with locked dependency', { | 1032 testResolve('override compatible with locked dependency', { |
1001 'myapp 0.0.0': { | 1033 'myapp 0.0.0': { |
1002 'foo': 'any' | 1034 'foo': 'any' |
1003 }, | 1035 }, |
1004 'foo 1.0.0': { 'bar': '1.0.0' }, | 1036 'foo 1.0.0': { |
1005 'foo 1.0.1': { 'bar': '1.0.1' }, | 1037 'bar': '1.0.0' |
1006 'foo 1.0.2': { 'bar': '1.0.2' }, | 1038 }, |
| 1039 'foo 1.0.1': { |
| 1040 'bar': '1.0.1' |
| 1041 }, |
| 1042 'foo 1.0.2': { |
| 1043 'bar': '1.0.2' |
| 1044 }, |
1007 'bar 1.0.0': {}, | 1045 'bar 1.0.0': {}, |
1008 'bar 1.0.1': {}, | 1046 'bar 1.0.1': {}, |
1009 'bar 1.0.2': {} | 1047 'bar 1.0.2': {} |
1010 }, lockfile: { | 1048 }, lockfile: { |
1011 'foo': '1.0.1' | 1049 'foo': '1.0.1' |
1012 }, overrides: { | 1050 }, overrides: { |
1013 'foo': '<1.0.2' | 1051 'foo': '<1.0.2' |
1014 }, result: { | 1052 }, result: { |
1015 'myapp from root': '0.0.0', | 1053 'myapp from root': '0.0.0', |
1016 'foo': '1.0.1', | 1054 'foo': '1.0.1', |
1017 'bar': '1.0.1' | 1055 'bar': '1.0.1' |
1018 }); | 1056 }); |
1019 | |
1020 testResolve('override incompatible with locked dependency', { | 1057 testResolve('override incompatible with locked dependency', { |
1021 'myapp 0.0.0': { | 1058 'myapp 0.0.0': { |
1022 'foo': 'any' | 1059 'foo': 'any' |
1023 }, | 1060 }, |
1024 'foo 1.0.0': { 'bar': '1.0.0' }, | 1061 'foo 1.0.0': { |
1025 'foo 1.0.1': { 'bar': '1.0.1' }, | 1062 'bar': '1.0.0' |
1026 'foo 1.0.2': { 'bar': '1.0.2' }, | 1063 }, |
| 1064 'foo 1.0.1': { |
| 1065 'bar': '1.0.1' |
| 1066 }, |
| 1067 'foo 1.0.2': { |
| 1068 'bar': '1.0.2' |
| 1069 }, |
1027 'bar 1.0.0': {}, | 1070 'bar 1.0.0': {}, |
1028 'bar 1.0.1': {}, | 1071 'bar 1.0.1': {}, |
1029 'bar 1.0.2': {} | 1072 'bar 1.0.2': {} |
1030 }, lockfile: { | 1073 }, lockfile: { |
1031 'foo': '1.0.1' | 1074 'foo': '1.0.1' |
1032 }, overrides: { | 1075 }, overrides: { |
1033 'foo': '>1.0.1' | 1076 'foo': '>1.0.1' |
1034 }, result: { | 1077 }, result: { |
1035 'myapp from root': '0.0.0', | 1078 'myapp from root': '0.0.0', |
1036 'foo': '1.0.2', | 1079 'foo': '1.0.2', |
1037 'bar': '1.0.2' | 1080 'bar': '1.0.2' |
1038 }); | 1081 }); |
1039 | |
1040 testResolve('no version that matches override', { | 1082 testResolve('no version that matches override', { |
1041 'myapp 0.0.0': {}, | 1083 'myapp 0.0.0': {}, |
1042 'foo 2.0.0': {}, | 1084 'foo 2.0.0': {}, |
1043 'foo 2.1.3': {} | 1085 'foo 2.1.3': {} |
1044 }, overrides: { | 1086 }, overrides: { |
1045 'foo': '>=1.0.0 <2.0.0' | 1087 'foo': '>=1.0.0 <2.0.0' |
1046 }, error: noVersion(['myapp'])); | 1088 }, error: noVersion(['myapp'])); |
1047 | |
1048 testResolve('override a bad source without error', { | 1089 testResolve('override a bad source without error', { |
1049 'myapp 0.0.0': { | 1090 'myapp 0.0.0': { |
1050 'foo from bad': 'any' | 1091 'foo from bad': 'any' |
1051 }, | 1092 }, |
1052 'foo 0.0.0': {} | 1093 'foo 0.0.0': {} |
1053 }, overrides: { | 1094 }, overrides: { |
1054 'foo': 'any' | 1095 'foo': 'any' |
1055 }, result: { | 1096 }, result: { |
1056 'myapp from root': '0.0.0', | 1097 'myapp from root': '0.0.0', |
1057 'foo': '0.0.0' | 1098 'foo': '0.0.0' |
1058 }); | 1099 }); |
1059 } | 1100 } |
1060 | |
1061 void downgrade() { | 1101 void downgrade() { |
1062 testResolve("downgrades a dependency to the lowest matching version", { | 1102 testResolve("downgrades a dependency to the lowest matching version", { |
1063 'myapp 0.0.0': { | 1103 'myapp 0.0.0': { |
1064 'foo': '>=2.0.0 <3.0.0' | 1104 'foo': '>=2.0.0 <3.0.0' |
1065 }, | 1105 }, |
1066 'foo 1.0.0': {}, | 1106 'foo 1.0.0': {}, |
1067 'foo 2.0.0-dev': {}, | 1107 'foo 2.0.0-dev': {}, |
1068 'foo 2.0.0': {}, | 1108 'foo 2.0.0': {}, |
1069 'foo 2.1.0': {} | 1109 'foo 2.1.0': {} |
1070 }, lockfile: { | 1110 }, lockfile: { |
1071 'foo': '2.1.0' | 1111 'foo': '2.1.0' |
1072 }, result: { | 1112 }, result: { |
1073 'myapp from root': '0.0.0', | 1113 'myapp from root': '0.0.0', |
1074 'foo': '2.0.0' | 1114 'foo': '2.0.0' |
1075 }, downgrade: true); | 1115 }, downgrade: true); |
1076 | 1116 testResolve( |
1077 testResolve('use earliest allowed prerelease if no stable versions match ' | 1117 'use earliest allowed prerelease if no stable versions match ' |
1078 'while downgrading', { | 1118 'while downgrading', |
| 1119 { |
1079 'myapp 0.0.0': { | 1120 'myapp 0.0.0': { |
1080 'a': '>=2.0.0-dev.1 <3.0.0' | 1121 'a': '>=2.0.0-dev.1 <3.0.0' |
1081 }, | 1122 }, |
1082 'a 1.0.0': {}, | 1123 'a 1.0.0': {}, |
1083 'a 2.0.0-dev.1': {}, | 1124 'a 2.0.0-dev.1': {}, |
1084 'a 2.0.0-dev.2': {}, | 1125 'a 2.0.0-dev.2': {}, |
1085 'a 2.0.0-dev.3': {} | 1126 'a 2.0.0-dev.3': {} |
1086 }, result: { | 1127 }, result: { |
1087 'myapp from root': '0.0.0', | 1128 'myapp from root': '0.0.0', |
1088 'a': '2.0.0-dev.1' | 1129 'a': '2.0.0-dev.1' |
1089 }, downgrade: true); | 1130 }, downgrade: true); |
1090 } | 1131 } |
1091 | 1132 testResolve(String description, Map packages, {Map lockfile, Map overrides, |
1092 testResolve(String description, Map packages, { | 1133 Map result, FailMatcherBuilder error, int maxTries, bool downgrade: false})
{ |
1093 Map lockfile, Map overrides, Map result, FailMatcherBuilder error, | 1134 _testResolve( |
1094 int maxTries, bool downgrade: false}) { | 1135 test, |
1095 _testResolve(test, description, packages, lockfile: lockfile, | 1136 description, |
1096 overrides: overrides, result: result, error: error, maxTries: maxTries, | 1137 packages, |
| 1138 lockfile: lockfile, |
| 1139 overrides: overrides, |
| 1140 result: result, |
| 1141 error: error, |
| 1142 maxTries: maxTries, |
1097 downgrade: downgrade); | 1143 downgrade: downgrade); |
1098 } | 1144 } |
1099 | 1145 solo_testResolve(String description, Map packages, {Map lockfile, Map overrides, |
1100 solo_testResolve(String description, Map packages, { | 1146 Map result, FailMatcherBuilder error, int maxTries, bool downgrade: false})
{ |
1101 Map lockfile, Map overrides, Map result, FailMatcherBuilder error, | |
1102 int maxTries, bool downgrade: false}) { | |
1103 log.verbosity = log.Verbosity.SOLVER; | 1147 log.verbosity = log.Verbosity.SOLVER; |
1104 _testResolve(solo_test, description, packages, lockfile: lockfile, | 1148 _testResolve( |
1105 overrides: overrides, result: result, error: error, maxTries: maxTries, | 1149 solo_test, |
| 1150 description, |
| 1151 packages, |
| 1152 lockfile: lockfile, |
| 1153 overrides: overrides, |
| 1154 result: result, |
| 1155 error: error, |
| 1156 maxTries: maxTries, |
1106 downgrade: downgrade); | 1157 downgrade: downgrade); |
1107 } | 1158 } |
1108 | 1159 _testResolve(void testFn(String description, Function body), String description, |
1109 _testResolve(void testFn(String description, Function body), | 1160 Map packages, {Map lockfile, Map overrides, Map result, |
1110 String description, Map packages, { | 1161 FailMatcherBuilder error, int maxTries, bool downgrade: false}) { |
1111 Map lockfile, Map overrides, Map result, FailMatcherBuilder error, | |
1112 int maxTries, bool downgrade: false}) { | |
1113 if (maxTries == null) maxTries = 1; | 1162 if (maxTries == null) maxTries = 1; |
1114 | |
1115 testFn(description, () { | 1163 testFn(description, () { |
1116 var cache = new SystemCache('.'); | 1164 var cache = new SystemCache('.'); |
1117 source1 = new MockSource('mock1'); | 1165 source1 = new MockSource('mock1'); |
1118 source2 = new MockSource('mock2'); | 1166 source2 = new MockSource('mock2'); |
1119 cache.register(source1); | 1167 cache.register(source1); |
1120 cache.register(source2); | 1168 cache.register(source2); |
1121 cache.sources.setDefault(source1.name); | 1169 cache.sources.setDefault(source1.name); |
1122 | |
1123 // Build the test package graph. | |
1124 var root; | 1170 var root; |
1125 packages.forEach((description, dependencies) { | 1171 packages.forEach((description, dependencies) { |
1126 var id = parseSpec(description); | 1172 var id = parseSpec(description); |
1127 var package = mockPackage(id, dependencies, | 1173 var package = |
1128 id.name == 'myapp' ? overrides : null); | 1174 mockPackage(id, dependencies, id.name == 'myapp' ? overrides : null); |
1129 if (id.name == 'myapp') { | 1175 if (id.name == 'myapp') { |
1130 // Don't add the root package to the server, so we can verify that Pub | |
1131 // doesn't try to look up information about the local package on the | |
1132 // remote server. | |
1133 root = package; | 1176 root = package; |
1134 } else { | 1177 } else { |
1135 (cache.sources[id.source] as MockSource).addPackage( | 1178 (cache.sources[id.source] as MockSource).addPackage( |
1136 id.description, package); | 1179 id.description, |
| 1180 package); |
1137 } | 1181 } |
1138 }); | 1182 }); |
1139 | |
1140 // Clean up the expectation. | |
1141 if (result != null) { | 1183 if (result != null) { |
1142 var newResult = {}; | 1184 var newResult = {}; |
1143 result.forEach((description, version) { | 1185 result.forEach((description, version) { |
1144 var id = parseSpec(description, version); | 1186 var id = parseSpec(description, version); |
1145 newResult[id.name] = id; | 1187 newResult[id.name] = id; |
1146 }); | 1188 }); |
1147 result = newResult; | 1189 result = newResult; |
1148 } | 1190 } |
1149 | |
1150 // Parse the lockfile. | |
1151 var realLockFile = new LockFile.empty(); | 1191 var realLockFile = new LockFile.empty(); |
1152 if (lockfile != null) { | 1192 if (lockfile != null) { |
1153 lockfile.forEach((name, version) { | 1193 lockfile.forEach((name, version) { |
1154 version = new Version.parse(version); | 1194 version = new Version.parse(version); |
1155 realLockFile.packages[name] = | 1195 realLockFile.packages[name] = |
1156 new PackageId(name, source1.name, version, name); | 1196 new PackageId(name, source1.name, version, name); |
1157 }); | 1197 }); |
1158 } | 1198 } |
1159 | |
1160 // Resolve the versions. | |
1161 var future = resolveVersions( | 1199 var future = resolveVersions( |
1162 downgrade ? SolveType.DOWNGRADE : SolveType.GET, | 1200 downgrade ? SolveType.DOWNGRADE : SolveType.GET, |
1163 cache.sources, root, lockFile: realLockFile); | 1201 cache.sources, |
1164 | 1202 root, |
| 1203 lockFile: realLockFile); |
1165 var matcher; | 1204 var matcher; |
1166 if (result != null) { | 1205 if (result != null) { |
1167 matcher = new SolveSuccessMatcher(result, maxTries); | 1206 matcher = new SolveSuccessMatcher(result, maxTries); |
1168 } else if (error != null) { | 1207 } else if (error != null) { |
1169 matcher = error(maxTries); | 1208 matcher = error(maxTries); |
1170 } | 1209 } |
1171 | |
1172 expect(future, completion(matcher)); | 1210 expect(future, completion(matcher)); |
1173 }); | 1211 }); |
1174 } | 1212 } |
1175 | |
1176 typedef SolveFailMatcher FailMatcherBuilder(int maxTries); | 1213 typedef SolveFailMatcher FailMatcherBuilder(int maxTries); |
1177 | |
1178 FailMatcherBuilder noVersion(List<String> packages) { | 1214 FailMatcherBuilder noVersion(List<String> packages) { |
1179 return (maxTries) => new SolveFailMatcher(packages, maxTries, | 1215 return (maxTries) => |
1180 NoVersionException); | 1216 new SolveFailMatcher(packages, maxTries, NoVersionException); |
1181 } | 1217 } |
1182 | |
1183 FailMatcherBuilder disjointConstraint(List<String> packages) { | 1218 FailMatcherBuilder disjointConstraint(List<String> packages) { |
1184 return (maxTries) => new SolveFailMatcher(packages, maxTries, | 1219 return (maxTries) => |
1185 DisjointConstraintException); | 1220 new SolveFailMatcher(packages, maxTries, DisjointConstraintException); |
1186 } | 1221 } |
1187 | 1222 FailMatcherBuilder descriptionMismatch(String package, String depender1, |
1188 FailMatcherBuilder descriptionMismatch( | 1223 String depender2) { |
1189 String package, String depender1, String depender2) { | 1224 return (maxTries) => |
1190 return (maxTries) => new SolveFailMatcher([package, depender1, depender2], | 1225 new SolveFailMatcher( |
1191 maxTries, DescriptionMismatchException); | 1226 [package, depender1, depender2], |
| 1227 maxTries, |
| 1228 DescriptionMismatchException); |
1192 } | 1229 } |
1193 | |
1194 // If no solution can be found, the solver just reports the last failure that | |
1195 // happened during propagation. Since we don't specify the order that solutions | |
1196 // are tried, this just validates that *some* failure occurred, but not which. | |
1197 SolveFailMatcher couldNotSolve(maxTries) => | 1230 SolveFailMatcher couldNotSolve(maxTries) => |
1198 new SolveFailMatcher([], maxTries, null); | 1231 new SolveFailMatcher([], maxTries, null); |
1199 | 1232 FailMatcherBuilder sourceMismatch(String package, String depender1, |
1200 FailMatcherBuilder sourceMismatch( | 1233 String depender2) { |
1201 String package, String depender1, String depender2) { | 1234 return (maxTries) => |
1202 return (maxTries) => new SolveFailMatcher([package, depender1, depender2], | 1235 new SolveFailMatcher( |
1203 maxTries, SourceMismatchException); | 1236 [package, depender1, depender2], |
| 1237 maxTries, |
| 1238 SourceMismatchException); |
1204 } | 1239 } |
1205 | |
1206 unknownSource(String depender, String dependency, String source) { | 1240 unknownSource(String depender, String dependency, String source) { |
1207 return (maxTries) => new SolveFailMatcher([depender, dependency, source], | 1241 return (maxTries) => |
1208 maxTries, UnknownSourceException); | 1242 new SolveFailMatcher( |
| 1243 [depender, dependency, source], |
| 1244 maxTries, |
| 1245 UnknownSourceException); |
1209 } | 1246 } |
1210 | |
1211 class SolveSuccessMatcher implements Matcher { | 1247 class SolveSuccessMatcher implements Matcher { |
1212 /// The expected concrete package selections. | |
1213 final Map<String, PackageId> _expected; | 1248 final Map<String, PackageId> _expected; |
1214 | |
1215 /// The maximum number of attempts that should have been tried before finding | |
1216 /// the solution. | |
1217 final int _maxTries; | 1249 final int _maxTries; |
1218 | |
1219 SolveSuccessMatcher(this._expected, this._maxTries); | 1250 SolveSuccessMatcher(this._expected, this._maxTries); |
1220 | |
1221 Description describe(Description description) { | 1251 Description describe(Description description) { |
1222 return description.add( | 1252 return description.add( |
1223 'Solver to use at most $_maxTries attempts to find:\n' | 1253 'Solver to use at most $_maxTries attempts to find:\n' |
1224 '${_listPackages(_expected.values)}'); | 1254 '${_listPackages(_expected.values)}'); |
1225 } | 1255 } |
1226 | 1256 Description describeMismatch(SolveResult result, Description description, |
1227 Description describeMismatch(SolveResult result, | 1257 Map state, bool verbose) { |
1228 Description description, | |
1229 Map state, bool verbose) { | |
1230 if (!result.succeeded) { | 1258 if (!result.succeeded) { |
1231 description.add('Solver failed with:\n${result.error}'); | 1259 description.add('Solver failed with:\n${result.error}'); |
1232 return null; | 1260 return null; |
1233 } | 1261 } |
1234 | |
1235 description.add('Resolved:\n${_listPackages(result.packages)}\n'); | 1262 description.add('Resolved:\n${_listPackages(result.packages)}\n'); |
1236 description.add(state['failures']); | 1263 description.add(state['failures']); |
1237 return description; | 1264 return description; |
1238 } | 1265 } |
1239 | |
1240 bool matches(SolveResult result, Map state) { | 1266 bool matches(SolveResult result, Map state) { |
1241 if (!result.succeeded) return false; | 1267 if (!result.succeeded) return false; |
1242 | |
1243 var expected = new Map.from(_expected); | 1268 var expected = new Map.from(_expected); |
1244 var failures = new StringBuffer(); | 1269 var failures = new StringBuffer(); |
1245 | |
1246 for (var id in result.packages) { | 1270 for (var id in result.packages) { |
1247 if (!expected.containsKey(id.name)) { | 1271 if (!expected.containsKey(id.name)) { |
1248 failures.writeln('Should not have selected $id'); | 1272 failures.writeln('Should not have selected $id'); |
1249 } else { | 1273 } else { |
1250 var expectedId = expected.remove(id.name); | 1274 var expectedId = expected.remove(id.name); |
1251 if (id != expectedId) { | 1275 if (id != expectedId) { |
1252 failures.writeln('Expected $expectedId, not $id'); | 1276 failures.writeln('Expected $expectedId, not $id'); |
1253 } | 1277 } |
1254 } | 1278 } |
1255 } | 1279 } |
1256 | |
1257 if (!expected.isEmpty) { | 1280 if (!expected.isEmpty) { |
1258 failures.writeln('Missing:\n${_listPackages(expected.values)}'); | 1281 failures.writeln('Missing:\n${_listPackages(expected.values)}'); |
1259 } | 1282 } |
1260 | |
1261 // Allow 1 here because the greedy solver will only make one attempt. | |
1262 if (result.attemptedSolutions != 1 && | 1283 if (result.attemptedSolutions != 1 && |
1263 result.attemptedSolutions != _maxTries) { | 1284 result.attemptedSolutions != _maxTries) { |
1264 failures.writeln('Took ${result.attemptedSolutions} attempts'); | 1285 failures.writeln('Took ${result.attemptedSolutions} attempts'); |
1265 } | 1286 } |
1266 | |
1267 if (!failures.isEmpty) { | 1287 if (!failures.isEmpty) { |
1268 state['failures'] = failures.toString(); | 1288 state['failures'] = failures.toString(); |
1269 return false; | 1289 return false; |
1270 } | 1290 } |
1271 | |
1272 return true; | 1291 return true; |
1273 } | 1292 } |
1274 | |
1275 String _listPackages(Iterable<PackageId> packages) { | 1293 String _listPackages(Iterable<PackageId> packages) { |
1276 return '- ${packages.join('\n- ')}'; | 1294 return '- ${packages.join('\n- ')}'; |
1277 } | 1295 } |
1278 } | 1296 } |
1279 | |
1280 class SolveFailMatcher implements Matcher { | 1297 class SolveFailMatcher implements Matcher { |
1281 /// The strings that should appear in the resulting error message. | |
1282 // TODO(rnystrom): This seems to always be package names. Make that explicit. | |
1283 final Iterable<String> _expected; | 1298 final Iterable<String> _expected; |
1284 | |
1285 /// The maximum number of attempts that should be tried before failing. | |
1286 final int _maxTries; | 1299 final int _maxTries; |
1287 | |
1288 /// The concrete error type that should be found, or `null` if any | |
1289 /// [SolveFailure] is allowed. | |
1290 final Type _expectedType; | 1300 final Type _expectedType; |
1291 | |
1292 SolveFailMatcher(this._expected, this._maxTries, this._expectedType); | 1301 SolveFailMatcher(this._expected, this._maxTries, this._expectedType); |
1293 | |
1294 Description describe(Description description) { | 1302 Description describe(Description description) { |
1295 description.add('Solver should fail after at most $_maxTries attempts.'); | 1303 description.add('Solver should fail after at most $_maxTries attempts.'); |
1296 if (!_expected.isEmpty) { | 1304 if (!_expected.isEmpty) { |
1297 var textList = _expected.map((s) => '"$s"').join(", "); | 1305 var textList = _expected.map((s) => '"$s"').join(", "); |
1298 description.add(' The error should contain $textList.'); | 1306 description.add(' The error should contain $textList.'); |
1299 } | 1307 } |
1300 return description; | 1308 return description; |
1301 } | 1309 } |
1302 | 1310 Description describeMismatch(SolveResult result, Description description, |
1303 Description describeMismatch(SolveResult result, | 1311 Map state, bool verbose) { |
1304 Description description, | |
1305 Map state, bool verbose) { | |
1306 description.add(state['failures']); | 1312 description.add(state['failures']); |
1307 return description; | 1313 return description; |
1308 } | 1314 } |
1309 | |
1310 bool matches(SolveResult result, Map state) { | 1315 bool matches(SolveResult result, Map state) { |
1311 var failures = new StringBuffer(); | 1316 var failures = new StringBuffer(); |
1312 | |
1313 if (result.succeeded) { | 1317 if (result.succeeded) { |
1314 failures.writeln('Solver succeeded'); | 1318 failures.writeln('Solver succeeded'); |
1315 } else { | 1319 } else { |
1316 if (_expectedType != null && result.error.runtimeType != _expectedType) { | 1320 if (_expectedType != null && result.error.runtimeType != _expectedType) { |
1317 failures.writeln('Should have error type $_expectedType, got ' | 1321 failures.writeln( |
1318 '${result.error.runtimeType}'); | 1322 'Should have error type $_expectedType, got ' '${result.error.runtim
eType}'); |
1319 } | 1323 } |
1320 | |
1321 var message = result.error.toString(); | 1324 var message = result.error.toString(); |
1322 for (var expected in _expected) { | 1325 for (var expected in _expected) { |
1323 if (!message.contains(expected)) { | 1326 if (!message.contains(expected)) { |
1324 failures.writeln( | 1327 failures.writeln( |
1325 'Expected error to contain "$expected", got:\n$message'); | 1328 'Expected error to contain "$expected", got:\n$message'); |
1326 } | 1329 } |
1327 } | 1330 } |
1328 | |
1329 // Allow 1 here because the greedy solver will only make one attempt. | |
1330 if (result.attemptedSolutions != 1 && | 1331 if (result.attemptedSolutions != 1 && |
1331 result.attemptedSolutions != _maxTries) { | 1332 result.attemptedSolutions != _maxTries) { |
1332 failures.writeln('Took ${result.attemptedSolutions} attempts'); | 1333 failures.writeln('Took ${result.attemptedSolutions} attempts'); |
1333 } | 1334 } |
1334 } | 1335 } |
1335 | |
1336 if (!failures.isEmpty) { | 1336 if (!failures.isEmpty) { |
1337 state['failures'] = failures.toString(); | 1337 state['failures'] = failures.toString(); |
1338 return false; | 1338 return false; |
1339 } | 1339 } |
1340 | |
1341 return true; | 1340 return true; |
1342 } | 1341 } |
1343 } | 1342 } |
1344 | |
1345 /// A source used for testing. This both creates mock package objects and acts | |
1346 /// as a source for them. | |
1347 /// | |
1348 /// In order to support testing packages that have the same name but different | |
1349 /// descriptions, a package's name is calculated by taking the description | |
1350 /// string and stripping off any trailing hyphen followed by non-hyphen | |
1351 /// characters. | |
1352 class MockSource extends CachedSource { | 1343 class MockSource extends CachedSource { |
1353 final _packages = <String, Map<Version, Package>>{}; | 1344 final _packages = <String, Map<Version, Package>>{}; |
1354 | |
1355 /// Keeps track of which package version lists have been requested. Ensures | |
1356 /// that a source is only hit once for a given package and that pub | |
1357 /// internally caches the results. | |
1358 final _requestedVersions = new Set<String>(); | 1345 final _requestedVersions = new Set<String>(); |
1359 | |
1360 /// Keeps track of which package pubspecs have been requested. Ensures that a | |
1361 /// source is only hit once for a given package and that pub internally | |
1362 /// caches the results. | |
1363 final _requestedPubspecs = new Map<String, Set<Version>>(); | 1346 final _requestedPubspecs = new Map<String, Set<Version>>(); |
1364 | |
1365 final String name; | 1347 final String name; |
1366 final hasMultipleVersions = true; | 1348 final hasMultipleVersions = true; |
1367 | |
1368 MockSource(this.name); | 1349 MockSource(this.name); |
1369 | |
1370 dynamic parseDescription(String containingPath, description, | 1350 dynamic parseDescription(String containingPath, description, |
1371 {bool fromLockFile: false}) => description; | 1351 {bool fromLockFile: false}) => |
1372 | 1352 description; |
1373 bool descriptionsEqual(description1, description2) => | 1353 bool descriptionsEqual(description1, description2) => |
1374 description1 == description2; | 1354 description1 == description2; |
1375 | |
1376 Future<String> getDirectory(PackageId id) { | 1355 Future<String> getDirectory(PackageId id) { |
1377 return new Future.value('${id.name}-${id.version}'); | 1356 return new Future.value('${id.name}-${id.version}'); |
1378 } | 1357 } |
1379 | |
1380 Future<List<Version>> getVersions(String name, String description) { | 1358 Future<List<Version>> getVersions(String name, String description) { |
1381 return syncFuture(() { | 1359 return syncFuture(() { |
1382 // Make sure the solver doesn't request the same thing twice. | |
1383 if (_requestedVersions.contains(description)) { | 1360 if (_requestedVersions.contains(description)) { |
1384 throw new Exception('Version list for $description was already ' | 1361 throw new Exception( |
1385 'requested.'); | 1362 'Version list for $description was already ' 'requested.'); |
1386 } | 1363 } |
1387 | |
1388 _requestedVersions.add(description); | 1364 _requestedVersions.add(description); |
1389 | 1365 if (!_packages.containsKey(description)) { |
1390 if (!_packages.containsKey(description)){ | 1366 throw new Exception( |
1391 throw new Exception('MockSource does not have a package matching ' | 1367 'MockSource does not have a package matching ' '"$description".'); |
1392 '"$description".'); | |
1393 } | 1368 } |
1394 | |
1395 return _packages[description].keys.toList(); | 1369 return _packages[description].keys.toList(); |
1396 }); | 1370 }); |
1397 } | 1371 } |
1398 | |
1399 Future<Pubspec> describeUncached(PackageId id) { | 1372 Future<Pubspec> describeUncached(PackageId id) { |
1400 return syncFuture(() { | 1373 return syncFuture(() { |
1401 // Make sure the solver doesn't request the same thing twice. | |
1402 if (_requestedPubspecs.containsKey(id.description) && | 1374 if (_requestedPubspecs.containsKey(id.description) && |
1403 _requestedPubspecs[id.description].contains(id.version)) { | 1375 _requestedPubspecs[id.description].contains(id.version)) { |
1404 throw new Exception('Pubspec for $id was already requested.'); | 1376 throw new Exception('Pubspec for $id was already requested.'); |
1405 } | 1377 } |
1406 | |
1407 _requestedPubspecs.putIfAbsent(id.description, () => new Set<Version>()); | 1378 _requestedPubspecs.putIfAbsent(id.description, () => new Set<Version>()); |
1408 _requestedPubspecs[id.description].add(id.version); | 1379 _requestedPubspecs[id.description].add(id.version); |
1409 | |
1410 return _packages[id.description][id.version].pubspec; | 1380 return _packages[id.description][id.version].pubspec; |
1411 }); | 1381 }); |
1412 } | 1382 } |
1413 | |
1414 Future<Package> downloadToSystemCache(PackageId id) => | 1383 Future<Package> downloadToSystemCache(PackageId id) => |
1415 throw new UnsupportedError('Cannot download mock packages'); | 1384 throw new UnsupportedError('Cannot download mock packages'); |
1416 | |
1417 List<Package> getCachedPackages() => | 1385 List<Package> getCachedPackages() => |
1418 throw new UnsupportedError('Cannot get mock packages'); | 1386 throw new UnsupportedError('Cannot get mock packages'); |
1419 | |
1420 Future<Pair<int, int>> repairCachedPackages() => | 1387 Future<Pair<int, int>> repairCachedPackages() => |
1421 throw new UnsupportedError('Cannot repair mock packages'); | 1388 throw new UnsupportedError('Cannot repair mock packages'); |
1422 | |
1423 void addPackage(String description, Package package) { | 1389 void addPackage(String description, Package package) { |
1424 _packages.putIfAbsent(description, () => new Map<Version, Package>()); | 1390 _packages.putIfAbsent(description, () => new Map<Version, Package>()); |
1425 _packages[description][package.version] = package; | 1391 _packages[description][package.version] = package; |
1426 } | 1392 } |
1427 } | 1393 } |
1428 | |
1429 Package mockPackage(PackageId id, Map dependencyStrings, Map overrides) { | 1394 Package mockPackage(PackageId id, Map dependencyStrings, Map overrides) { |
1430 var sdkConstraint = null; | 1395 var sdkConstraint = null; |
1431 | |
1432 // Build the pubspec dependencies. | |
1433 var dependencies = <PackageDep>[]; | 1396 var dependencies = <PackageDep>[]; |
1434 var devDependencies = <PackageDep>[]; | 1397 var devDependencies = <PackageDep>[]; |
1435 | |
1436 dependencyStrings.forEach((spec, constraint) { | 1398 dependencyStrings.forEach((spec, constraint) { |
1437 var isDev = spec.startsWith("(dev) "); | 1399 var isDev = spec.startsWith("(dev) "); |
1438 if (isDev) { | 1400 if (isDev) { |
1439 spec = spec.substring("(dev) ".length); | 1401 spec = spec.substring("(dev) ".length); |
1440 } | 1402 } |
1441 | 1403 var dep = |
1442 var dep = parseSpec(spec).withConstraint( | 1404 parseSpec(spec).withConstraint(new VersionConstraint.parse(constraint)); |
1443 new VersionConstraint.parse(constraint)); | |
1444 | |
1445 if (dep.name == 'sdk') { | 1405 if (dep.name == 'sdk') { |
1446 sdkConstraint = dep.constraint; | 1406 sdkConstraint = dep.constraint; |
1447 return; | 1407 return; |
1448 } | 1408 } |
1449 | |
1450 if (isDev) { | 1409 if (isDev) { |
1451 devDependencies.add(dep); | 1410 devDependencies.add(dep); |
1452 } else { | 1411 } else { |
1453 dependencies.add(dep); | 1412 dependencies.add(dep); |
1454 } | 1413 } |
1455 }); | 1414 }); |
1456 | |
1457 var dependencyOverrides = <PackageDep>[]; | 1415 var dependencyOverrides = <PackageDep>[]; |
1458 if (overrides != null) { | 1416 if (overrides != null) { |
1459 overrides.forEach((spec, constraint) { | 1417 overrides.forEach((spec, constraint) { |
1460 dependencyOverrides.add(parseSpec(spec).withConstraint( | 1418 dependencyOverrides.add( |
1461 new VersionConstraint.parse(constraint))); | 1419 parseSpec(spec).withConstraint(new VersionConstraint.parse(constraint)
)); |
1462 }); | 1420 }); |
1463 } | 1421 } |
1464 | 1422 return new Package.inMemory( |
1465 return new Package.inMemory(new Pubspec(id.name, | 1423 new Pubspec( |
1466 version: id.version, | 1424 id.name, |
1467 dependencies: dependencies, | 1425 version: id.version, |
1468 devDependencies: devDependencies, | 1426 dependencies: dependencies, |
1469 dependencyOverrides: dependencyOverrides, | 1427 devDependencies: devDependencies, |
1470 sdkConstraint: sdkConstraint)); | 1428 dependencyOverrides: dependencyOverrides, |
| 1429 sdkConstraint: sdkConstraint)); |
1471 } | 1430 } |
1472 | |
1473 /// Creates a new [PackageId] parsed from [text], which looks something like | |
1474 /// this: | |
1475 /// | |
1476 /// foo-xyz 1.0.0 from mock | |
1477 /// | |
1478 /// The package name is "foo". A hyphenated suffix like "-xyz" here is part | |
1479 /// of the package description, but not its name, so the description here is | |
1480 /// "foo-xyz". | |
1481 /// | |
1482 /// This is followed by an optional [Version]. If [version] is provided, then | |
1483 /// it is parsed to a [Version], and [text] should *not* also contain a | |
1484 /// version string. | |
1485 /// | |
1486 /// The "from mock" optional suffix is the name of a source for the package. | |
1487 /// If omitted, it defaults to "mock1". | |
1488 PackageId parseSpec(String text, [String version]) { | 1431 PackageId parseSpec(String text, [String version]) { |
1489 var pattern = new RegExp(r"(([a-z_]*)(-[a-z_]+)?)( ([^ ]+))?( from (.*))?$"); | 1432 var pattern = new RegExp(r"(([a-z_]*)(-[a-z_]+)?)( ([^ ]+))?( from (.*))?$"); |
1490 var match = pattern.firstMatch(text); | 1433 var match = pattern.firstMatch(text); |
1491 if (match == null) { | 1434 if (match == null) { |
1492 throw new FormatException("Could not parse spec '$text'."); | 1435 throw new FormatException("Could not parse spec '$text'."); |
1493 } | 1436 } |
1494 | |
1495 var description = match[1]; | 1437 var description = match[1]; |
1496 var name = match[2]; | 1438 var name = match[2]; |
1497 | |
1498 var parsedVersion; | 1439 var parsedVersion; |
1499 if (version != null) { | 1440 if (version != null) { |
1500 // Spec string shouldn't also contain a version. | |
1501 if (match[5] != null) { | 1441 if (match[5] != null) { |
1502 throw new ArgumentError("Spec '$text' should not contain a version " | 1442 throw new ArgumentError( |
1503 "since '$version' was passed in explicitly."); | 1443 "Spec '$text' should not contain a version " |
| 1444 "since '$version' was passed in explicitly."); |
1504 } | 1445 } |
1505 parsedVersion = new Version.parse(version); | 1446 parsedVersion = new Version.parse(version); |
1506 } else { | 1447 } else { |
1507 if (match[5] != null) { | 1448 if (match[5] != null) { |
1508 parsedVersion = new Version.parse(match[5]); | 1449 parsedVersion = new Version.parse(match[5]); |
1509 } else { | 1450 } else { |
1510 parsedVersion = Version.none; | 1451 parsedVersion = Version.none; |
1511 } | 1452 } |
1512 } | 1453 } |
1513 | |
1514 var source = "mock1"; | 1454 var source = "mock1"; |
1515 if (match[7] != null) { | 1455 if (match[7] != null) { |
1516 source = match[7]; | 1456 source = match[7]; |
1517 if (source == "root") source = null; | 1457 if (source == "root") source = null; |
1518 } | 1458 } |
1519 | |
1520 return new PackageId(name, source, parsedVersion, description); | 1459 return new PackageId(name, source, parsedVersion, description); |
1521 } | 1460 } |
OLD | NEW |