a start on using proptest to fuzz the resolver

This commit is contained in:
Eh2406 2018-08-21 10:39:18 -04:00
parent 0ec7281b9c
commit 56a222cd30
3 changed files with 91 additions and 0 deletions

View file

@ -93,6 +93,7 @@ features = [
[dev-dependencies]
bufstream = "0.1"
proptest = "0.8.4"
[[bin]]
name = "cargo"

View file

@ -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;

View file

@ -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<Dependency>,
@ -185,6 +191,88 @@ fn loc_names(names: &[(&'static str, &'static str)]) -> Vec<PackageId> {
.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<Value = Vec<Summary>> {
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<Value = (usize, usize)> {
(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::<Vec<_>>(),
b.iter()
.flat_map(|(name, &num)| {
iter::repeat(name.clone())
.take(num)
.enumerate()
.map(|(i, name)| (name, format!("{}.0.0", i + 1)))
})
.collect::<Vec<_>>(),
)
})
.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<Dependency> = 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::<String, Vec<String>>::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)],
&reg,
).unwrap();
}
}
#[test]
#[should_panic(expected = "assertion failed: !name.is_empty()")]
fn test_dependency_with_empty_name() {