Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(184)

Side by Side Diff: sdk/lib/_internal/pub_generated/test/version_solver_test.dart

Issue 557563002: Store the async-await compiled pub code directly in the repo. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 6 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698