From 56a222cd30a3de3e169250e6f86ed34dedc2b45d Mon Sep 17 00:00:00 2001 From: Eh2406 Date: Tue, 21 Aug 2018 10:39:18 -0400 Subject: [PATCH] a start on using proptest to fuzz the resolver --- Cargo.toml | 1 + tests/testsuite/main.rs | 2 + tests/testsuite/resolve.rs | 88 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 91 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index 59b7427b8..1a942a933 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -93,6 +93,7 @@ features = [ [dev-dependencies] bufstream = "0.1" +proptest = "0.8.4" [[bin]] name = "cargo" diff --git a/tests/testsuite/main.rs b/tests/testsuite/main.rs index 7ec164ef0..280f0afd9 100644 --- a/tests/testsuite/main.rs +++ b/tests/testsuite/main.rs @@ -11,6 +11,8 @@ extern crate glob; extern crate hex; extern crate libc; #[macro_use] +extern crate proptest; +#[macro_use] extern crate serde_derive; #[macro_use] extern crate serde_json; diff --git a/tests/testsuite/resolve.rs b/tests/testsuite/resolve.rs index 2acaadab0..0660835c0 100644 --- a/tests/testsuite/resolve.rs +++ b/tests/testsuite/resolve.rs @@ -12,6 +12,12 @@ use support::ChannelChanger; use support::{execs, project}; use support::registry::Package; +use proptest::collection::btree_map; +use proptest::collection::vec; +use proptest::prelude::*; +use proptest::sample::subsequence; +use std::iter; + fn resolve( pkg: &PackageId, deps: Vec, @@ -185,6 +191,88 @@ fn loc_names(names: &[(&'static str, &'static str)]) -> Vec { .collect() } +/// This generates a random registry index. +/// Unlike vec((Name, Ver, vec((Name, VerRq), ..), ..) +/// This strategy has a high probability of having valid dependencies +fn registry_strategy() -> impl Strategy> { + const VALID_NAME_STRATEGY: &str = "[A-Za-z_-][A-Za-z0-9_-]*"; + const MAX_CRATES: usize = 10; + const MAX_VERSIONS: usize = 10; + + fn range(max: usize) -> impl Strategy { + (0..max).prop_flat_map(move |low| (Just(low), low..=max)) + } + btree_map(VALID_NAME_STRATEGY, 1..=MAX_CRATES, 1..=MAX_VERSIONS) + .prop_map(|mut b| { + // root is the name of the thing being compiled + // so it would be confusing to have it in the index + b.remove("root"); + ( + b.iter().map(|(name, _)| name.clone()).collect::>(), + b.iter() + .flat_map(|(name, &num)| { + iter::repeat(name.clone()) + .take(num) + .enumerate() + .map(|(i, name)| (name, format!("{}.0.0", i + 1))) + }) + .collect::>(), + ) + }) + .prop_flat_map(|(names, data)| { + let names_len = names.len(); + let data_len = data.len(); + ( + Just(data), + vec(subsequence(names, 0..names_len), data_len..=data_len), + ) + }) + .prop_flat_map(|(data, deps)| { + let len = deps.iter().map(|x| x.len()).sum(); + ( + Just(data), + Just(deps), + vec( + range(MAX_VERSIONS).prop_map(|(b, e)| format!(">={}.0.0, <={}.0.0", b, e)), + len..=len, + ), + ) + }) + .prop_map(|(data, deps, vers)| { + let mut i = 0; + data.into_iter() + .zip(deps.into_iter()) + .map(|((name, ver), deps)| { + let d: Vec = deps.into_iter() + .filter(|n| &name < n) + .map(|n| { + i += 1; + dep_req(&n, &vers[i - 1]) + }) + .collect(); + let pkgid = (name.as_str(), ver).to_pkgid(); + let link = if pkgid.name().ends_with("-sys") { Some(pkgid.name().as_str()) } else { None }; + + Summary::new(pkgid, d, &BTreeMap::>::new(), link, false).unwrap() + }) + .collect() + }) +} + +proptest! { + #[test] + fn doesnt_crash(reg in registry_strategy()) { + let this = reg.last().unwrap().name(); + let reg = registry(reg); + + resolve( + &pkg_id("root"), + vec![dep(&this)], + ®, + ).unwrap(); + } +} + #[test] #[should_panic(expected = "assertion failed: !name.is_empty()")] fn test_dependency_with_empty_name() {