Skip to content

Commit c1fa740

Browse files
authored
Merge pull request #159 from fitzgen/arbitrary-take-rest-and-collections-of-collections
Fix `Unstructured::arbitrary_take_rest_iter` for collections of collections
2 parents 80d6bfe + f19fd7a commit c1fa740

File tree

4 files changed

+141
-24
lines changed

4 files changed

+141
-24
lines changed

Cargo.toml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ authors = [
1010
"Corey Farwell <[email protected]>",
1111
]
1212
categories = ["development-tools::testing"]
13-
edition = "2018"
13+
edition = "2021"
1414
keywords = ["arbitrary", "testing"]
1515
readme = "README.md"
1616
description = "The trait for generating structured data from unstructured data"
@@ -37,3 +37,6 @@ required-features = ["derive"]
3737

3838
[workspace]
3939
members = ["./fuzz"]
40+
41+
[dev-dependencies]
42+
exhaustigen = "0.1.0"

src/lib.rs

Lines changed: 131 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -440,6 +440,7 @@ macro_rules! impl_range {
440440

441441
#[inline]
442442
fn size_hint(depth: usize) -> (usize, Option<usize>) {
443+
#[allow(clippy::redundant_closure_call)]
443444
$size_hint_closure(depth)
444445
}
445446
}
@@ -1161,6 +1162,56 @@ impl<'a> Arbitrary<'a> for IpAddr {
11611162
mod test {
11621163
use super::*;
11631164

1165+
/// Assert that the given expected values are all generated.
1166+
///
1167+
/// Exhaustively enumerates all buffers up to length 10 containing the
1168+
/// following bytes: `0x00`, `0x01`, `0x61` (aka ASCII 'a'), and `0xff`
1169+
fn assert_generates<T>(expected_values: impl IntoIterator<Item = T>)
1170+
where
1171+
T: Clone + std::fmt::Debug + std::hash::Hash + Eq + for<'a> Arbitrary<'a>,
1172+
{
1173+
let expected_values: HashSet<_> = expected_values.into_iter().collect();
1174+
let mut arbitrary_expected = expected_values.clone();
1175+
let mut arbitrary_take_rest_expected = expected_values;
1176+
1177+
let bytes = [0, 1, b'a', 0xff];
1178+
let max_len = 10;
1179+
1180+
let mut buf = Vec::with_capacity(max_len);
1181+
1182+
let mut g = exhaustigen::Gen::new();
1183+
while !g.done() {
1184+
let len = g.gen(max_len);
1185+
1186+
buf.clear();
1187+
buf.extend(
1188+
std::iter::repeat_with(|| {
1189+
let index = g.gen(bytes.len() - 1);
1190+
bytes[index]
1191+
})
1192+
.take(len),
1193+
);
1194+
1195+
let mut u = Unstructured::new(&buf);
1196+
let val = T::arbitrary(&mut u).unwrap();
1197+
arbitrary_expected.remove(&val);
1198+
1199+
let u = Unstructured::new(&buf);
1200+
let val = T::arbitrary_take_rest(u).unwrap();
1201+
arbitrary_take_rest_expected.remove(&val);
1202+
1203+
if arbitrary_expected.is_empty() && arbitrary_take_rest_expected.is_empty() {
1204+
return;
1205+
}
1206+
}
1207+
1208+
panic!(
1209+
"failed to generate all expected values!\n\n\
1210+
T::arbitrary did not generate: {arbitrary_expected:#?}\n\n\
1211+
T::arbitrary_take_rest did not generate {arbitrary_take_rest_expected:#?}"
1212+
)
1213+
}
1214+
11641215
/// Generates an arbitrary `T`, and checks that the result is consistent with the
11651216
/// `size_hint()` reported by `T`.
11661217
fn checked_arbitrary<'a, T: Arbitrary<'a>>(u: &mut Unstructured<'a>) -> Result<T> {
@@ -1231,6 +1282,16 @@ mod test {
12311282
let expected = 1 | (2 << 8) | (3 << 16) | (4 << 24);
12321283
let actual = checked_arbitrary::<i32>(&mut buf).unwrap();
12331284
assert_eq!(expected, actual);
1285+
1286+
assert_generates([
1287+
i32::from_ne_bytes([0, 0, 0, 0]),
1288+
i32::from_ne_bytes([0, 0, 0, 1]),
1289+
i32::from_ne_bytes([0, 0, 1, 0]),
1290+
i32::from_ne_bytes([0, 1, 0, 0]),
1291+
i32::from_ne_bytes([1, 0, 0, 0]),
1292+
i32::from_ne_bytes([1, 1, 1, 1]),
1293+
i32::from_ne_bytes([0xff, 0xff, 0xff, 0xff]),
1294+
]);
12341295
}
12351296

12361297
#[test]
@@ -1251,6 +1312,74 @@ mod test {
12511312
assert_eq!(expected, actual);
12521313
}
12531314

1315+
#[test]
1316+
fn arbitrary_for_vec_u8() {
1317+
assert_generates::<Vec<u8>>([
1318+
vec![],
1319+
vec![0],
1320+
vec![1],
1321+
vec![0, 0],
1322+
vec![0, 1],
1323+
vec![1, 0],
1324+
vec![1, 1],
1325+
vec![0, 0, 0],
1326+
vec![0, 0, 1],
1327+
vec![0, 1, 0],
1328+
vec![0, 1, 1],
1329+
vec![1, 0, 0],
1330+
vec![1, 0, 1],
1331+
vec![1, 1, 0],
1332+
vec![1, 1, 1],
1333+
]);
1334+
}
1335+
1336+
#[test]
1337+
fn arbitrary_for_vec_vec_u8() {
1338+
assert_generates::<Vec<Vec<u8>>>([
1339+
vec![],
1340+
vec![vec![]],
1341+
vec![vec![0]],
1342+
vec![vec![1]],
1343+
vec![vec![0, 1]],
1344+
vec![vec![], vec![]],
1345+
vec![vec![0], vec![]],
1346+
vec![vec![], vec![1]],
1347+
vec![vec![0], vec![1]],
1348+
vec![vec![0, 1], vec![]],
1349+
vec![vec![], vec![1, 0]],
1350+
vec![vec![], vec![], vec![]],
1351+
]);
1352+
}
1353+
1354+
#[test]
1355+
fn arbitrary_for_vec_vec_vec_u8() {
1356+
assert_generates::<Vec<Vec<Vec<u8>>>>([
1357+
vec![],
1358+
vec![vec![]],
1359+
vec![vec![vec![0]]],
1360+
vec![vec![vec![1]]],
1361+
vec![vec![vec![0, 1]]],
1362+
vec![vec![], vec![]],
1363+
vec![vec![], vec![vec![]]],
1364+
vec![vec![vec![]], vec![]],
1365+
vec![vec![vec![]], vec![vec![]]],
1366+
vec![vec![vec![0]], vec![]],
1367+
vec![vec![], vec![vec![1]]],
1368+
vec![vec![vec![0]], vec![vec![1]]],
1369+
vec![vec![vec![0, 1]], vec![]],
1370+
vec![vec![], vec![vec![0, 1]]],
1371+
vec![vec![], vec![], vec![]],
1372+
vec![vec![vec![]], vec![], vec![]],
1373+
vec![vec![], vec![vec![]], vec![]],
1374+
vec![vec![], vec![], vec![vec![]]],
1375+
]);
1376+
}
1377+
1378+
#[test]
1379+
fn arbitrary_for_string() {
1380+
assert_generates::<String>(["".into(), "a".into(), "aa".into(), "aaa".into()]);
1381+
}
1382+
12541383
#[test]
12551384
fn arbitrary_collection() {
12561385
let x = [
@@ -1284,11 +1413,11 @@ mod test {
12841413
);
12851414
assert_eq!(
12861415
checked_arbitrary_take_rest::<Vec<u8>>(Unstructured::new(&x)).unwrap(),
1287-
&[1, 2, 3, 4]
1416+
&[2, 4]
12881417
);
12891418
assert_eq!(
12901419
checked_arbitrary_take_rest::<Vec<u32>>(Unstructured::new(&x)).unwrap(),
1291-
&[0x4030201]
1420+
&[0x040302]
12921421
);
12931422
assert_eq!(
12941423
checked_arbitrary_take_rest::<String>(Unstructured::new(&x)).unwrap(),

src/unstructured.rs

Lines changed: 5 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -620,14 +620,8 @@ impl<'a> Unstructured<'a> {
620620
pub fn arbitrary_take_rest_iter<ElementType: Arbitrary<'a>>(
621621
self,
622622
) -> Result<ArbitraryTakeRestIter<'a, ElementType>> {
623-
let (lower, upper) = ElementType::size_hint(0);
624-
625-
let elem_size = upper.unwrap_or(lower * 2);
626-
let elem_size = std::cmp::max(1, elem_size);
627-
let size = self.len() / elem_size;
628623
Ok(ArbitraryTakeRestIter {
629-
size,
630-
u: Some(self),
624+
u: self,
631625
_marker: PhantomData,
632626
})
633627
}
@@ -735,25 +729,16 @@ impl<'a, 'b, ElementType: Arbitrary<'a>> Iterator for ArbitraryIter<'a, 'b, Elem
735729

736730
/// Utility iterator produced by [`Unstructured::arbitrary_take_rest_iter`]
737731
pub struct ArbitraryTakeRestIter<'a, ElementType> {
738-
u: Option<Unstructured<'a>>,
739-
size: usize,
732+
u: Unstructured<'a>,
740733
_marker: PhantomData<ElementType>,
741734
}
742735

743736
impl<'a, ElementType: Arbitrary<'a>> Iterator for ArbitraryTakeRestIter<'a, ElementType> {
744737
type Item = Result<ElementType>;
745738
fn next(&mut self) -> Option<Result<ElementType>> {
746-
if let Some(mut u) = self.u.take() {
747-
if self.size == 1 {
748-
Some(Arbitrary::arbitrary_take_rest(u))
749-
} else if self.size == 0 {
750-
None
751-
} else {
752-
self.size -= 1;
753-
let ret = Arbitrary::arbitrary(&mut u);
754-
self.u = Some(u);
755-
Some(ret)
756-
}
739+
let keep_going = self.u.arbitrary().unwrap_or(false);
740+
if keep_going {
741+
Some(Arbitrary::arbitrary(&mut self.u))
757742
} else {
758743
None
759744
}

tests/derive.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ fn test_take_rest() {
5959
assert_eq!(s2.1, true);
6060
assert_eq!(s1.2, 0x4030201);
6161
assert_eq!(s2.2, 0x4030201);
62-
assert_eq!(s1.3, vec![0x605, 0x807]);
62+
assert_eq!(s1.3, vec![0x0706]);
6363
assert_eq!(s2.3, "\x05\x06\x07\x08");
6464
}
6565

0 commit comments

Comments
 (0)