@@ -236,12 +236,12 @@ proptest! {
236236}
237237
238238#[ test]
239- fn public_dependency ( ) {
239+ fn basic_public_dependency ( ) {
240240 let reg = registry ( vec ! [
241241 pkg!( ( "A" , "0.1.0" ) ) ,
242242 pkg!( ( "A" , "0.2.0" ) ) ,
243243 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" ) ] ) ,
245245 ] ) ;
246246
247247 let res = resolve_and_validated ( & pkg_id ( "root" ) , vec ! [ dep( "C" ) ] , & reg) . unwrap ( ) ;
@@ -256,6 +256,78 @@ fn public_dependency() {
256256 ) ;
257257}
258258
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+
259331#[ test]
260332#[ should_panic( expected = "assertion failed: !name.is_empty()" ) ]
261333fn test_dependency_with_empty_name ( ) {
0 commit comments