@@ -236,12 +236,12 @@ proptest! {
236
236
}
237
237
238
238
#[ test]
239
- fn public_dependency ( ) {
239
+ fn basic_public_dependency ( ) {
240
240
let reg = registry ( vec ! [
241
241
pkg!( ( "A" , "0.1.0" ) ) ,
242
242
pkg!( ( "A" , "0.2.0" ) ) ,
243
243
pkg!( "B" => [ dep_req_kind( "A" , "0.1" , Kind :: Normal , true ) ] ) ,
244
- pkg!( "C" => [ dep_req ( "A" , "*" ) , dep_req ( "B" , "* ") ] ) ,
244
+ pkg!( "C" => [ dep ( "A" ) , dep ( "B" ) ] ) ,
245
245
] ) ;
246
246
247
247
let res = resolve_and_validated ( & pkg_id ( "root" ) , vec ! [ dep( "C" ) ] , & reg) . unwrap ( ) ;
@@ -256,6 +256,78 @@ fn public_dependency() {
256
256
) ;
257
257
}
258
258
259
+ #[ test]
260
+ fn public_dependency_filling_in ( ) {
261
+ // The resolver has an optimization where if a candidate to resolve a dependency
262
+ // has already bean activated then we skip looking at the candidates dependencies.
263
+ // However, we have to be careful as the new path may make pub dependencies invalid.
264
+
265
+ // Triggering this case requires dependencies to be resolved in a specific order.
266
+ // Fuzzing found this unintuitive case, that triggers this unfortunate order of operations:
267
+ // 1. `d`'s dep on `c` is resolved
268
+ // 2. `d`'s dep on `a` is resolved with `0.1.1`
269
+ // 3. `c`'s dep on `b` is resolved with `0.0.2`
270
+ // 4. `b`'s dep on `a` is resolved with `0.0.6` no pub dev conflict as `b` is private to `c`
271
+ // 5. `d`'s dep on `b` is resolved with `0.0.2` triggering the optimization.
272
+ // Do we notice that `d` has a pub dep conflict on `a`? Lets try it and see.
273
+ let reg = registry ( vec ! [
274
+ pkg!( ( "a" , "0.0.6" ) ) ,
275
+ pkg!( ( "a" , "0.1.1" ) ) ,
276
+ pkg!( ( "b" , "0.0.0" ) => [ dep( "bad" ) ] ) ,
277
+ pkg!( ( "b" , "0.0.1" ) => [ dep( "bad" ) ] ) ,
278
+ pkg!( ( "b" , "0.0.2" ) => [ dep_req_kind( "a" , "=0.0.6" , Kind :: Normal , true ) ] ) ,
279
+ pkg!( "c" => [ dep_req( "b" , ">=0.0.1" ) ] ) ,
280
+ pkg!( "d" => [ dep( "c" ) , dep( "a" ) , dep( "b" ) ] ) ,
281
+ ] ) ;
282
+
283
+ let res = resolve_and_validated ( & pkg_id ( "root" ) , vec ! [ dep( "d" ) ] , & reg) . unwrap ( ) ;
284
+ assert_same (
285
+ & res,
286
+ & names ( & [
287
+ ( "root" , "1.0.0" ) ,
288
+ ( "d" , "1.0.0" ) ,
289
+ ( "c" , "1.0.0" ) ,
290
+ ( "b" , "0.0.2" ) ,
291
+ ( "a" , "0.0.6" ) ,
292
+ ] ) ,
293
+ ) ;
294
+ }
295
+
296
+ #[ test]
297
+ fn public_dependency_filling_in_and_update ( ) {
298
+ // The resolver has an optimization where if a candidate to resolve a dependency
299
+ // has already bean activated then we skip looking at the candidates dependencies.
300
+ // However, we have to be careful as the new path may make pub dependencies invalid.
301
+
302
+ // Triggering this case requires dependencies to be resolved in a specific order.
303
+ // Fuzzing found this unintuitive case, that triggers this unfortunate order of operations:
304
+ // 1. `D`'s dep on `B` is resolved
305
+ // 2. `D`'s dep on `C` is resolved
306
+ // 3. `B`'s dep on `A` is resolved with `0.0.0`
307
+ // 4. `C`'s dep on `B` triggering the optimization.
308
+ // So did we add `A 0.0.0` to the deps `C` can see?
309
+ // Or are we going to resolve `C`'s dep on `A` with `0.0.2`?
310
+ // Lets try it and see.
311
+ let reg = registry ( vec ! [
312
+ pkg!( ( "A" , "0.0.0" ) ) ,
313
+ pkg!( ( "A" , "0.0.2" ) ) ,
314
+ pkg!( "B" => [ dep_req_kind( "A" , "=0.0.0" , Kind :: Normal , true ) , ] ) ,
315
+ pkg!( "C" => [ dep( "A" ) , dep( "B" ) ] ) ,
316
+ pkg!( "D" => [ dep( "B" ) , dep( "C" ) ] ) ,
317
+ ] ) ;
318
+ let res = resolve_and_validated ( & pkg_id ( "root" ) , vec ! [ dep( "D" ) ] , & reg) . unwrap ( ) ;
319
+ assert_same (
320
+ & res,
321
+ & names ( & [
322
+ ( "root" , "1.0.0" ) ,
323
+ ( "D" , "1.0.0" ) ,
324
+ ( "C" , "1.0.0" ) ,
325
+ ( "B" , "1.0.0" ) ,
326
+ ( "A" , "0.0.0" ) ,
327
+ ] ) ,
328
+ ) ;
329
+ }
330
+
259
331
#[ test]
260
332
#[ should_panic( expected = "assertion failed: !name.is_empty()" ) ]
261
333
fn test_dependency_with_empty_name ( ) {
0 commit comments