Skip to content

Commit

Permalink
feat(test): Snapshot .crate validation
Browse files Browse the repository at this point in the history
  • Loading branch information
epage committed Oct 4, 2024
1 parent 3531081 commit 88a8438
Show file tree
Hide file tree
Showing 11 changed files with 617 additions and 283 deletions.
132 changes: 124 additions & 8 deletions crates/cargo-test-support/src/compare.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,11 @@ use crate::paths;
use crate::{diff, rustc_host};
use anyhow::{bail, Context, Result};
use serde_json::Value;
use snapbox::Data;
use snapbox::IntoData;
use std::fmt;
use std::path::Path;
use std::path::PathBuf;
use std::str;
use url::Url;

Expand Down Expand Up @@ -461,14 +464,6 @@ pub(crate) fn match_exact(
);
}

/// Convenience wrapper around [`match_exact`] which will panic on error.
#[track_caller]
pub(crate) fn assert_match_exact(expected: &str, actual: &str) {
if let Err(e) = match_exact(expected, actual, "", "", None) {
crate::panic_error("", e);
}
}

/// Checks that the given string contains the given lines, ignoring the order
/// of the lines.
///
Expand Down Expand Up @@ -860,6 +855,127 @@ impl fmt::Debug for WildStr<'_> {
}
}

pub struct InMemoryDir {
files: Vec<(PathBuf, Data)>,
}

impl InMemoryDir {
pub fn paths(&self) -> impl Iterator<Item = &Path> {
self.files.iter().map(|(p, _)| p.as_path())
}

#[track_caller]
pub fn assert_contains(&self, expected: &Self) {
let assert = assert_e2e();
for (path, expected_data) in &expected.files {
let actual_data = self
.files
.iter()
.find_map(|(p, d)| (path == p).then(|| d.clone()))
.unwrap_or_else(|| Data::new());
assert.eq(actual_data, expected_data);
}
}
}

impl<P, D> FromIterator<(P, D)> for InMemoryDir
where
P: Into<std::path::PathBuf>,
D: IntoData,
{
fn from_iter<I: IntoIterator<Item = (P, D)>>(files: I) -> Self {
let files = files
.into_iter()
.map(|(p, d)| (p.into(), d.into_data()))
.collect();
Self { files }
}
}

impl<const N: usize, P, D> From<[(P, D); N]> for InMemoryDir
where
P: Into<PathBuf>,
D: IntoData,
{
fn from(files: [(P, D); N]) -> Self {
let files = files
.into_iter()
.map(|(p, d)| (p.into(), d.into_data()))
.collect();
Self { files }
}
}

impl<P, D> From<std::collections::HashMap<P, D>> for InMemoryDir
where
P: Into<PathBuf>,
D: IntoData,
{
fn from(files: std::collections::HashMap<P, D>) -> Self {
let files = files
.into_iter()
.map(|(p, d)| (p.into(), d.into_data()))
.collect();
Self { files }
}
}

impl<P, D> From<std::collections::BTreeMap<P, D>> for InMemoryDir
where
P: Into<PathBuf>,
D: IntoData,
{
fn from(files: std::collections::BTreeMap<P, D>) -> Self {
let files = files
.into_iter()
.map(|(p, d)| (p.into(), d.into_data()))
.collect();
Self { files }
}
}

impl From<()> for InMemoryDir {
fn from(_files: ()) -> Self {
let files = Vec::new();
Self { files }
}
}

macro_rules! impl_from_tuple_for_dirsnapshot {
($($var:ident $path:ident $data:ident),+) => {
impl<$($path: Into<PathBuf>, $data: IntoData),+> From<($(($path, $data)),+ ,)> for InMemoryDir {
fn from(files: ($(($path, $data)),+,)) -> Self {
let ($($var),+ ,) = files;
let files = [$(($var.0.into(), $var.1.into_data())),+];
files.into()
}
}
};
}

macro_rules! impl_from_tuples_for_dirsnapshot {
($var1:ident $path1:ident $data1:ident, $($var:ident $path:ident $data:ident),+) => {
impl_from_tuples_for_dirsnapshot!(__impl $var1 $path1 $data1; $($var $path $data),+);
};
(__impl $($var:ident $path:ident $data:ident),+; $var1:ident $path1:ident $data1:ident $(,$var2:ident $path2:ident $data2:ident)*) => {
impl_from_tuple_for_dirsnapshot!($($var $path $data),+);
impl_from_tuples_for_dirsnapshot!(__impl $($var $path $data),+, $var1 $path1 $data1; $($var2 $path2 $data2),*);
};
(__impl $($var:ident $path:ident $data:ident),+;) => {
impl_from_tuple_for_dirsnapshot!($($var $path $data),+);
}
}

impl_from_tuples_for_dirsnapshot!(
s1 P1 D1,
s2 P2 D2,
s3 P3 D3,
s4 P4 D4,
s5 P5 D5,
s6 P6 D6,
s7 P7 D7
);

#[cfg(test)]
mod test {
use snapbox::assert_data_eq;
Expand Down
45 changes: 26 additions & 19 deletions crates/cargo-test-support/src/publish.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,14 +57,14 @@
//! );
//! ```

use crate::compare::{assert_match_exact, find_json_mismatch};
use crate::compare::{find_json_mismatch, InMemoryDir};
use crate::registry::{self, alt_api_path, FeatureMap};
use flate2::read::GzDecoder;
use std::collections::{HashMap, HashSet};
use std::collections::HashSet;
use std::fs;
use std::fs::File;
use std::io::{self, prelude::*, SeekFrom};
use std::path::{Path, PathBuf};
use std::path::Path;
use tar::Archive;

fn read_le_u32<R>(mut reader: R) -> io::Result<u32>
Expand All @@ -84,7 +84,7 @@ pub fn validate_upload(expected_json: &str, expected_crate_name: &str, expected_
expected_json,
expected_crate_name,
expected_files,
&[],
(),
);
}

Expand All @@ -93,7 +93,7 @@ pub fn validate_upload_with_contents(
expected_json: &str,
expected_crate_name: &str,
expected_files: &[&str],
expected_contents: &[(&str, &str)],
expected_contents: impl Into<InMemoryDir>,
) {
let new_path = registry::api_path().join("api/v1/crates/new");
_validate_upload(
Expand All @@ -117,7 +117,7 @@ pub fn validate_alt_upload(
expected_json,
expected_crate_name,
expected_files,
&[],
(),
);
}

Expand All @@ -126,7 +126,7 @@ fn _validate_upload(
expected_json: &str,
expected_crate_name: &str,
expected_files: &[&str],
expected_contents: &[(&str, &str)],
expected_contents: impl Into<InMemoryDir>,
) {
let (actual_json, krate_bytes) = read_new_post(new_path);

Expand Down Expand Up @@ -177,7 +177,22 @@ pub fn validate_crate_contents(
reader: impl Read,
expected_crate_name: &str,
expected_files: &[&str],
expected_contents: &[(&str, &str)],
expected_contents: impl Into<InMemoryDir>,
) {
let expected_contents = expected_contents.into();
validate_crate_contents_(
reader,
expected_crate_name,
expected_files,
expected_contents,
)
}

fn validate_crate_contents_(
reader: impl Read,
expected_crate_name: &str,
expected_files: &[&str],
expected_contents: InMemoryDir,
) {
let mut rdr = GzDecoder::new(reader);
assert_eq!(
Expand All @@ -192,7 +207,7 @@ pub fn validate_crate_contents(
.strip_suffix(".crate")
.expect("must end with .crate"),
);
let files: HashMap<PathBuf, String> = ar
let actual_contents: InMemoryDir = ar
.entries()
.unwrap()
.map(|entry| {
Expand All @@ -208,7 +223,7 @@ pub fn validate_crate_contents(
(name, contents)
})
.collect();
let actual_files: HashSet<&Path> = files.keys().map(|p| p.as_path()).collect();
let actual_files: HashSet<&Path> = actual_contents.paths().collect();
let expected_files: HashSet<&Path> =
expected_files.iter().map(|name| Path::new(name)).collect();
let missing: Vec<&&Path> = expected_files.difference(&actual_files).collect();
Expand All @@ -219,15 +234,7 @@ pub fn validate_crate_contents(
missing, extra
);
}
if !expected_contents.is_empty() {
for (e_file_name, e_file_contents) in expected_contents {
let e_file_name = Path::new(e_file_name);
let actual_contents = files
.get(e_file_name)
.unwrap_or_else(|| panic!("file `{}` missing in archive", e_file_name.display()));
assert_match_exact(e_file_contents, actual_contents);
}
}
actual_contents.assert_contains(&expected_contents);
}

pub(crate) fn create_index_line(
Expand Down
22 changes: 16 additions & 6 deletions tests/testsuite/artifact_dep.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2260,10 +2260,20 @@ You may press ctrl-c to skip waiting; the crate should be available shortly.
"#,
"foo-0.1.0.crate",
&["Cargo.toml", "Cargo.toml.orig", "src/lib.rs"],
&[(
[(
"Cargo.toml",
&format!(
r#"{}
str![[r##"
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
#
# When uploading crates to the registry Cargo will automatically
# "normalize" Cargo.toml files for maximal compatibility
# with all versions of Cargo and also rewrite `path` dependencies
# to registry (e.g., crates.io) dependencies.
#
# If you are reading this file be aware that the original Cargo.toml
# will likely look very different (and much more reasonable).
# See Cargo.toml.orig for the original contents.
[package]
edition = "2015"
name = "foo"
Expand Down Expand Up @@ -2299,9 +2309,9 @@ artifact = [
"cdylib",
"staticlib",
]
target = "target""#,
cargo::core::manifest::MANIFEST_PREAMBLE
),
target = "target"
"##]],
)],
);
}
Expand Down
2 changes: 1 addition & 1 deletion tests/testsuite/cross_publish.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ fn simple_cross_package() {
f,
"foo-0.0.0.crate",
&["Cargo.lock", "Cargo.toml", "Cargo.toml.orig", "src/main.rs"],
&[],
(),
);
}

Expand Down
21 changes: 15 additions & 6 deletions tests/testsuite/features2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1788,8 +1788,18 @@ fn package_includes_resolve_behavior() {

p.cargo("package").cwd("a").run();

let rewritten_toml = format!(
r#"{}
let rewritten_toml = str![[r##"
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
#
# When uploading crates to the registry Cargo will automatically
# "normalize" Cargo.toml files for maximal compatibility
# with all versions of Cargo and also rewrite `path` dependencies
# to registry (e.g., crates.io) dependencies.
#
# If you are reading this file be aware that the original Cargo.toml
# will likely look very different (and much more reasonable).
# See Cargo.toml.orig for the original contents.
[package]
edition = "2015"
name = "a"
Expand All @@ -1810,16 +1820,15 @@ resolver = "2"
[lib]
name = "a"
path = "src/lib.rs"
"#,
cargo::core::manifest::MANIFEST_PREAMBLE
);
"##]];

let f = File::open(&p.root().join("target/package/a-0.1.0.crate")).unwrap();
validate_crate_contents(
f,
"a-0.1.0.crate",
&["Cargo.toml", "Cargo.toml.orig", "src/lib.rs"],
&[("Cargo.toml", &rewritten_toml)],
[("Cargo.toml", rewritten_toml)],
);
}

Expand Down
Loading

0 comments on commit 88a8438

Please sign in to comment.