3636//! tagged lock build the patched copy with no network on cargo 1.41 / 1.56
3737//! — the local `rust:1.41-slim` / `rust:1.56-slim` docker images, preferred,
3838//! else installed rustup toolchains 1.36..=1.56 (type-check only) —
39- //! config-file `[patch]` needs 1.56+; two versions of one crate there need
40- //! `--offline`; skipped when neither is available, required by the
39+ //! config-file `[patch]` needs 1.56+; two versions of one crate need cargo
40+ //! 1.45+ and `--offline`; skipped when neither is available, required by the
4141//! `cargo-old-toolchains` CI leg). Also: a
4242//! URL-spelled crates.io `[patch]` table is refused, an ancestor-directory
4343//! config entry cannot silently shadow the vendored copy, and the pre-v5
@@ -1190,7 +1190,14 @@ async fn cargo_get_uuid_vendored_fresh_checkout_locked_build() {
11901190
11911191// ── v5 manifest wiring: multi-version, legacy migration, old toolchains ─
11921192
1193- const UUID_OLD : & str = "3c4d5e6f-7081-4b2c-9d3e-123456789abc" ;
1193+ /// The second patch uuid of the multi-version fixtures. Deliberately
1194+ /// ADVERSARIAL: its `cfg-if-socket-1a2b3c4d` key sorts BEFORE `UUID`'s, so
1195+ /// the HIGHER version's `[patch.crates-io]` key sorts LAST — the order
1196+ /// cargo before 1.45 cannot resolve (see
1197+ /// `cargo_vendored_two_versions_are_refused_by_cargo_below_1_45`). With the
1198+ /// opposite order 1.41 happens to build, so a favorable pair would let a
1199+ /// regression in the modern-cargo path go unnoticed here.
1200+ const UUID_OLD : & str = "1a2b3c4d-5e6f-4a7b-8c9d-0e1f2a3b4c5d" ;
11941201const PATCH_SUFFIX_OLD : & str =
11951202 "\n /// Socket-patch marker for the OLD major (added by the vendored patch).\n pub fn socket_patched_old() -> u32 { 2 }\n " ;
11961203
@@ -2070,6 +2077,12 @@ const OLD_TOOLCHAINS_REQUIRED_ENV: &str = "SOCKET_PATCH_CARGO_OLD_TOOLCHAINS_REQ
20702077/// locally (the test never pulls; the CI leg does).
20712078const OLD_CARGO_IMAGES : [ ( & str , u32 ) ; 2 ] = [ ( "rust:1.41-slim" , 41 ) , ( "rust:1.56-slim" , 56 ) ] ;
20722079
2080+ /// The cargo minor from which two vendored versions of one crate resolve in
2081+ /// EITHER `[patch.crates-io]` key order — the floor
2082+ /// `socket_patch_core`'s `MULTI_VERSION_CARGO_MINOR` warns below. Measured
2083+ /// on 1.41.1/1.42/1.43/1.44 (refuse) and 1.45/1.49/1.53/1.56 (build).
2084+ const MULTI_VERSION_FLOOR : u32 = 45 ;
2085+
20732086/// One old cargo under test.
20742087#[ derive( Clone , Debug ) ]
20752088enum OldCargo {
@@ -2312,26 +2325,33 @@ fn cargo_vendored_manifest_patch_builds_on_old_toolchains() {
23122325 }
23132326}
23142327
2315- /// OLD TOOLCHAIN, TWO VERSIONS of one crate: older cargo loads the
2316- /// crates.io index to tell two same-named `[patch]` entries apart. Every
2317- /// clause of the documented constraint is ASSERTED here, in both
2318- /// directions, because both arms run with no network (docker `--network
2319- /// none`, rustup pointed at a dead proxy):
2328+ /// OLD TOOLCHAIN, TWO VERSIONS of one crate: cargo 1.45 is the FLOOR.
2329+ ///
2330+ /// Cargo before 1.45 resolves every source-less `Cargo.lock` entry for a
2331+ /// crate through ONE `[patch.crates-io]` path — the entry whose KEY sorts
2332+ /// last — so with two vendored versions one lock entry is pinned to the
2333+ /// other version's copy. The fixture uuids are chosen so the HIGHER
2334+ /// version's key sorts last (see `UUID_OLD`), the order 1.41 cannot take;
2335+ /// with the opposite order 1.41 happens to build, which is how the
2336+ /// limitation went unnoticed. Asserted in every direction, with no network
2337+ /// (docker `--network none`, rustup pointed at a dead proxy):
23202338///
2321- /// * without `--offline` the build MUST fail, and fail on the unreachable
2322- /// index — the negative control for "pass `--offline`";
2323- /// * cargo 1.56 then builds under `--offline` from an EMPTY CARGO_HOME;
2324- /// * older cargo (1.41) MUST first fail even under `--offline` (no registry
2325- /// cache to read), and the documented remedy — a populated crates.io
2326- /// index in `$CARGO_HOME` — must then build (and run) both patched
2327- /// copies with no network. That step is unconditional below 1.56, so a
2328- /// cargo that stops needing it fails this test instead of silently
2329- /// skipping the remedy.
2339+ /// * without `--offline` EVERY old cargo fails on the unreachable
2340+ /// crates.io index (it loads the index to tell two same-named `[patch]`
2341+ /// entries apart) — the negative control for "pass `--offline`";
2342+ /// * cargo 1.45 and later then build BOTH copies under `--offline` from an
2343+ /// EMPTY CARGO_HOME (measured on 1.45, 1.49, 1.53 and 1.56);
2344+ /// * below 1.45 `--offline` fails on the patch resolution itself —
2345+ /// ``patch for `cfg-if` … did not resolve to any crates`` — from an empty
2346+ /// CARGO_HOME AND with the index remedy that makes a single vendored
2347+ /// version work there (measured on 1.41.1, 1.42, 1.43 and 1.44). A cargo
2348+ /// below 1.45 that starts building this fails the test instead of
2349+ /// silently widening the supported range.
23302350///
2331- /// Current stable needs neither — see
2351+ /// Current stable needs none of it — see
23322352/// `cargo_vendor_two_versions_of_one_crate_locked_build`.
23332353#[ test]
2334- fn cargo_vendored_two_versions_on_old_toolchains_need_offline ( ) {
2354+ fn cargo_vendored_two_versions_are_refused_by_cargo_below_1_45 ( ) {
23352355 const SUITE : & str = "e2e_vendor_cargo_build (old-toolchain multi-version)" ;
23362356 let Some ( cargos) = old_cargos_or_skip ( SUITE ) else {
23372357 return ;
@@ -2345,6 +2365,22 @@ fn cargo_vendored_two_versions_on_old_toolchains_need_offline() {
23452365 } ;
23462366 let env = vendor_ok ( & fx. proj , & fx. cargo_home , "old-toolchain multi-version" ) ;
23472367 assert_eq ! ( env[ "summary" ] [ "failed" ] , 0 , "{env}" ) ;
2368+ // The vendor says the project now needs cargo 1.45 (the fixture
2369+ // declares no `rust-version` and pins no toolchain).
2370+ assert ! (
2371+ env[ "events" ]
2372+ . as_array( )
2373+ . unwrap( )
2374+ . iter( )
2375+ . any( |e| e[ "errorCode" ] == "cargo_multi_version_old_cargo" ) ,
2376+ "the second version must warn about the cargo floor: {env}"
2377+ ) ;
2378+ // The adversarial key order is what this test pins.
2379+ let manifest = std:: fs:: read_to_string ( fx. proj . join ( "Cargo.toml" ) ) . unwrap ( ) ;
2380+ assert ! (
2381+ socket_key( UUID_OLD ) < socket_key( UUID ) ,
2382+ "the HIGHER version's key must sort last: {manifest}"
2383+ ) ;
23482384 std:: fs:: write ( fx. proj . join ( "src/main.rs" ) , TWO_VERSION_MAIN ) . unwrap ( ) ;
23492385 let lock = std:: fs:: read_to_string ( fx. proj . join ( "Cargo.lock" ) ) . unwrap ( ) ;
23502386 if !lock. contains ( "version = 4\n " ) {
@@ -2373,43 +2409,66 @@ fn cargo_vendored_two_versions_on_old_toolchains_need_offline() {
23732409 wiring:\n {online_stderr}"
23742410 ) ;
23752411 println ! ( "old-toolchain multi-version {name} without --offline: needs the index" ) ;
2376- let mut run = old_cargo_run ( & old, & fresh, & [ "--offline" ] ) ;
2377- if old. minor ( ) < 56 {
2378- let stderr = String :: from_utf8_lossy ( & run. out . stderr ) . into_owned ( ) ;
2379- assert ! (
2380- !run. out. status. success( ) ,
2381- "{name}: below 1.56 an EMPTY CARGO_HOME cannot tell the two [patch] \
2382- entries apart offline — the reason the index remedy is documented:\n \
2383- {stderr}"
2384- ) ;
2412+
2413+ let run = old_cargo_run ( & old, & fresh, & [ "--offline" ] ) ;
2414+ let stderr = String :: from_utf8_lossy ( & run. out . stderr ) . into_owned ( ) ;
2415+ if old. minor ( ) >= MULTI_VERSION_FLOOR {
23852416 assert ! (
2386- stderr . contains ( "unable to fetch registry" ) && stderr . contains ( "in offline mode" ) ,
2387- "{name}: the only accepted failure below 1.56 is the missing registry \
2388- index :\n {stderr}"
2417+ run . out . status . success ( ) ,
2418+ "{name}: 1.{MULTI_VERSION_FLOOR}+ must build two vendored versions under \
2419+ --offline from an empty CARGO_HOME :\n {stderr}"
23892420 ) ;
2390- println ! ( "old-toolchain multi-version {name}: needs a registry index (documented)" ) ;
2391- seed_old_crates_io_index ( & fresh. join ( ".old-cargo-home" ) , & [ & fx. new_v , & fx. old_v ] ) ;
2392- run = old_cargo_run ( & old, & fresh, & [ "--offline" ] ) ;
2421+ if matches ! ( old, OldCargo :: Docker { .. } ) {
2422+ assert ! (
2423+ String :: from_utf8_lossy( & run. out. stdout) . contains( "MARKER:1:2" ) ,
2424+ "{name}: both patched copies run: {}" ,
2425+ String :: from_utf8_lossy( & run. out. stdout)
2426+ ) ;
2427+ }
2428+ let _ = std:: fs:: remove_dir_all ( & fresh) ;
2429+ continue ;
23932430 }
2431+
2432+ // Below 1.45 the patch resolution itself fails, with an empty
2433+ // CARGO_HOME and with the documented index remedy alike.
23942434 assert ! (
2395- run. out. status. success( ) ,
2396- "{name}: two vendored versions must build under --offline:\n {}" ,
2397- String :: from_utf8_lossy( & run. out. stderr)
2435+ !run. out. status. success( ) ,
2436+ "{name}: below 1.{MULTI_VERSION_FLOOR} two vendored versions must NOT build \
2437+ offline:\n {stderr}"
2438+ ) ;
2439+ assert ! (
2440+ stderr. contains( "did not resolve to any crates" ) ,
2441+ "{name}: below 1.{MULTI_VERSION_FLOOR} the failure is the unresolvable second \
2442+ `[patch]` entry:\n {stderr}"
2443+ ) ;
2444+ seed_old_crates_io_index ( & fresh. join ( ".old-cargo-home" ) , & [ & fx. new_v , & fx. old_v ] ) ;
2445+ let seeded = old_cargo_run ( & old, & fresh, & [ "--offline" ] ) ;
2446+ let seeded_stderr = String :: from_utf8_lossy ( & seeded. out . stderr ) . into_owned ( ) ;
2447+ assert ! (
2448+ !seeded. out. status. success( ) ,
2449+ "{name}: below 1.{MULTI_VERSION_FLOOR} two vendored versions must NOT build \
2450+ even with a populated crates.io index — if this cargo now resolves both \
2451+ `[patch]` entries, the documented floor is wrong:\n stdout:\n {}\n stderr:\n \
2452+ {seeded_stderr}",
2453+ String :: from_utf8_lossy( & seeded. out. stdout)
2454+ ) ;
2455+ assert ! (
2456+ seeded_stderr. contains( "did not resolve to any crates" ) ,
2457+ "{name}: the pinned failure is the unresolvable second `[patch]` entry, not \
2458+ something else:\n {seeded_stderr}"
2459+ ) ;
2460+ println ! (
2461+ "old-toolchain multi-version {name}: refused below 1.{MULTI_VERSION_FLOOR} \
2462+ (documented)"
23982463 ) ;
2399- if matches ! ( old, OldCargo :: Docker { .. } ) {
2400- assert ! (
2401- String :: from_utf8_lossy( & run. out. stdout) . contains( "MARKER:1:2" ) ,
2402- "{name}: both patched copies run: {}" ,
2403- String :: from_utf8_lossy( & run. out. stdout)
2404- ) ;
2405- }
24062464 let _ = std:: fs:: remove_dir_all ( & fresh) ;
24072465 }
24082466}
24092467
2410- /// The documented remedy for two vendored versions on cargo older than
2411- /// 1.56: a populated crates.io index in `$CARGO_HOME`. Writes the minimal
2412- /// one an old cargo reads offline — the git index at the pre-1.85
2468+ /// A populated crates.io index in `$CARGO_HOME` — what an old cargo needs
2469+ /// before it can even look at two same-named `[patch]` entries offline, and
2470+ /// the state in which the sub-1.45 refusal is pinned. Writes the minimal
2471+ /// index an old cargo reads offline — the git index at the pre-1.85
24132472/// `registry/index/github.com-1ecc6299db9ec823` path (`origin/HEAD` names
24142473/// the tree cargo loads), listing `versions` of the patched crate. No
24152474/// `.crate` is cached: every listed version is patched to a path copy, so
0 commit comments