Skip to content

Commit

Permalink
test(freshness_checksum): verify cargo depinfo is properly encoded
Browse files Browse the repository at this point in the history
  • Loading branch information
Xaeroxe authored and weihanglo committed Oct 8, 2024
1 parent c0e550e commit cf893c1
Show file tree
Hide file tree
Showing 3 changed files with 152 additions and 84 deletions.
79 changes: 79 additions & 0 deletions crates/cargo-test-support/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1582,3 +1582,82 @@ where
let thread = std::thread::spawn(|| f());
thread_wait_timeout(n, thread)
}

// Helper for testing dep-info files in the fingerprint dir.
#[track_caller]
pub fn assert_deps(project: &Project, fingerprint: &str, test_cb: impl Fn(&Path, &[(u8, &str)])) {
let mut files = project
.glob(fingerprint)
.map(|f| f.expect("unwrap glob result"))
// Filter out `.json` entries.
.filter(|f| f.extension().is_none());
let info_path = files
.next()
.unwrap_or_else(|| panic!("expected 1 dep-info file at {}, found 0", fingerprint));
assert!(files.next().is_none(), "expected only 1 dep-info file");
let dep_info = fs::read(&info_path).unwrap();
let dep_info = &mut &dep_info[..];
let deps = (0..read_usize(dep_info))
.map(|_| {
let ty = read_u8(dep_info);
let path = std::str::from_utf8(read_bytes(dep_info)).unwrap();
let checksum_present = read_bool(dep_info);
if checksum_present {
// Read out the checksum info without using it
let _file_len = read_u64(dep_info);
let _checksum = read_bytes(dep_info);
}
(ty, path)
})
.collect::<Vec<_>>();
test_cb(&info_path, &deps);

fn read_usize(bytes: &mut &[u8]) -> usize {
let ret = &bytes[..4];
*bytes = &bytes[4..];

u32::from_le_bytes(ret.try_into().unwrap()) as usize
}

fn read_u8(bytes: &mut &[u8]) -> u8 {
let ret = bytes[0];
*bytes = &bytes[1..];
ret
}

fn read_bool(bytes: &mut &[u8]) -> bool {
read_u8(bytes) != 0
}

fn read_u64(bytes: &mut &[u8]) -> u64 {
let ret = &bytes[..8];
*bytes = &bytes[8..];

u64::from_le_bytes(ret.try_into().unwrap())
}

fn read_bytes<'a>(bytes: &mut &'a [u8]) -> &'a [u8] {
let n = read_usize(bytes);
let ret = &bytes[..n];
*bytes = &bytes[n..];
ret
}
}

pub fn assert_deps_contains(project: &Project, fingerprint: &str, expected: &[(u8, &str)]) {
assert_deps(project, fingerprint, |info_path, entries| {
for (e_kind, e_path) in expected {
let pattern = glob::Pattern::new(e_path).unwrap();
let count = entries
.iter()
.filter(|(kind, path)| kind == e_kind && pattern.matches(path))
.count();
if count != 1 {
panic!(
"Expected 1 match of {} {} in {:?}, got {}:\n{:#?}",
e_kind, e_path, info_path, count, entries
);
}
}
})
}
86 changes: 2 additions & 84 deletions tests/testsuite/dep_info.rs
Original file line number Diff line number Diff line change
@@ -1,99 +1,17 @@
//! Tests for dep-info files. This includes the dep-info file Cargo creates in
//! the output directory, and the ones stored in the fingerprint.

use std::fs;
use std::path::Path;
use std::str;

use cargo_test_support::compare::assert_e2e;
use cargo_test_support::paths;
use cargo_test_support::prelude::*;
use cargo_test_support::registry::Package;
use cargo_test_support::str;
use cargo_test_support::{
basic_bin_manifest, basic_manifest, main_file, project, rustc_host, Project,
};
use cargo_test_support::{assert_deps, assert_deps_contains};
use cargo_test_support::{basic_bin_manifest, basic_manifest, main_file, project, rustc_host};
use filetime::FileTime;

// Helper for testing dep-info files in the fingerprint dir.
#[track_caller]
fn assert_deps(project: &Project, fingerprint: &str, test_cb: impl Fn(&Path, &[(u8, &str)])) {
let mut files = project
.glob(fingerprint)
.map(|f| f.expect("unwrap glob result"))
// Filter out `.json` entries.
.filter(|f| f.extension().is_none());
let info_path = files
.next()
.unwrap_or_else(|| panic!("expected 1 dep-info file at {}, found 0", fingerprint));
assert!(files.next().is_none(), "expected only 1 dep-info file");
let dep_info = fs::read(&info_path).unwrap();
let dep_info = &mut &dep_info[..];
let deps = (0..read_usize(dep_info))
.map(|_| {
let ty = read_u8(dep_info);
let path = str::from_utf8(read_bytes(dep_info)).unwrap();
let checksum_present = read_bool(dep_info);
if checksum_present {
// Read out the checksum info without using it
let _file_len = read_u64(dep_info);
let _checksum = read_bytes(dep_info);
}
(ty, path)
})
.collect::<Vec<_>>();
test_cb(&info_path, &deps);

fn read_usize(bytes: &mut &[u8]) -> usize {
let ret = &bytes[..4];
*bytes = &bytes[4..];

u32::from_le_bytes(ret.try_into().unwrap()) as usize
}

fn read_u8(bytes: &mut &[u8]) -> u8 {
let ret = bytes[0];
*bytes = &bytes[1..];
ret
}

fn read_bool(bytes: &mut &[u8]) -> bool {
read_u8(bytes) != 0
}

fn read_u64(bytes: &mut &[u8]) -> u64 {
let ret = &bytes[..8];
*bytes = &bytes[8..];

u64::from_le_bytes(ret.try_into().unwrap())
}

fn read_bytes<'a>(bytes: &mut &'a [u8]) -> &'a [u8] {
let n = read_usize(bytes);
let ret = &bytes[..n];
*bytes = &bytes[n..];
ret
}
}

fn assert_deps_contains(project: &Project, fingerprint: &str, expected: &[(u8, &str)]) {
assert_deps(project, fingerprint, |info_path, entries| {
for (e_kind, e_path) in expected {
let pattern = glob::Pattern::new(e_path).unwrap();
let count = entries
.iter()
.filter(|(kind, path)| kind == e_kind && pattern.matches(path))
.count();
if count != 1 {
panic!(
"Expected 1 match of {} {} in {:?}, got {}:\n{:#?}",
e_kind, e_path, info_path, count, entries
);
}
}
})
}

#[cargo_test]
fn build_dep_info() {
let p = project()
Expand Down
71 changes: 71 additions & 0 deletions tests/testsuite/freshness_checksum.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use std::net::TcpListener;
use std::process::Stdio;
use std::thread;

use cargo_test_support::assert_deps_contains;
use cargo_test_support::prelude::*;
use cargo_test_support::registry::Package;
use cargo_test_support::{
Expand Down Expand Up @@ -129,6 +130,76 @@ fn same_size_different_content() {
.run();
}

#[cargo_test(
nightly,
reason = "-Zbinary-dep-depinfo is unstable, also requires -Zchecksum-hash-algorithm"
)]
fn binary_depinfo_correctly_encoded() {
Package::new("regdep", "0.1.0")
.file("src/lib.rs", "pub fn f() {}")
.publish();

let p = project()
.file(
"Cargo.toml",
r#"
[package]
name = "foo"
version = "0.1.0"
edition = "2018"
[dependencies]
regdep = "0.1"
bar = {path = "./bar"}
"#,
)
.file(
"src/main.rs",
r#"
fn main() {
regdep::f();
bar::f();
}
"#,
)
/*********** Path Dependency `bar` ***********/
.file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0"))
.file("bar/src/lib.rs", "pub fn f() {}")
.build();

let host = rustc_host();
p.cargo("build -Zbinary-dep-depinfo -Zchecksum-freshness --target")
.arg(&host)
.masquerade_as_nightly_cargo(&["binary-dep-depinfo", "checksum-freshness"])
.with_stderr_data(str![[r#"
...
[COMPILING] foo v0.1.0 ([ROOT]/foo)
...
"#]])
.run();

assert_deps_contains(
&p,
&format!("target/{}/debug/.fingerprint/foo-*/dep-bin-foo", host),
&[
(0, "src/main.rs"),
(1, &format!("{}/debug/deps/libbar-*.rlib", host)),
(1, &format!("{}/debug/deps/libregdep-*.rlib", host)),
],
);

// Make sure it stays fresh.
p.cargo("build -Zbinary-dep-depinfo -Zchecksum-freshness --target")
.arg(&host)
.masquerade_as_nightly_cargo(&["binary-dep-depinfo", "checksum-freshness"])
.with_stderr_data(str![[r#"
[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s
"#]])
.run();
}

#[cargo_test(nightly, reason = "requires -Zchecksum-hash-algorithm")]
fn modifying_and_moving() {
let p = project()
Expand Down

0 comments on commit cf893c1

Please sign in to comment.