mirror of
https://github.com/openharmony/third_party_rust_tinyvec.git
synced 2026-07-19 14:23:33 -04:00
Merge pull request #14 from Nemo157/arbitrary-model-tests
Add basic arbitrary-model-tests based fuzzing
This commit is contained in:
@@ -29,3 +29,10 @@ travis-ci = { repository = "Lokathor/tinyvec" }
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
all-features = true
|
||||
|
||||
[workspace]
|
||||
members = ["fuzz"]
|
||||
|
||||
[[test]]
|
||||
name = "tinyvec"
|
||||
required-features = ["extern_crate_alloc"]
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
hfuzz_target/
|
||||
hfuzz_workspace/
|
||||
@@ -0,0 +1,14 @@
|
||||
[package]
|
||||
name = "tinyvec-fuzz"
|
||||
version = "0.1.0"
|
||||
authors = []
|
||||
edition = "2018"
|
||||
publish = false
|
||||
|
||||
[dependencies]
|
||||
tinyvec = { path = "..", features = ["extern_crate_alloc", "nightly_slice_partition_dedup"] }
|
||||
arbitrary-model-tests = { git = "https://github.com/jakubadamw/arbitrary-model-tests" }
|
||||
honggfuzz = "0.5.45"
|
||||
arbitrary = "0.2.0"
|
||||
better-panic = "0.2.0"
|
||||
derive_arbitrary = "0.2.0"
|
||||
@@ -0,0 +1,11 @@
|
||||
## Quickstart
|
||||
|
||||
```console
|
||||
> cargo install honggfuzz
|
||||
> cargo hfuzz run arrayish
|
||||
```
|
||||
|
||||
When a crash is found:
|
||||
```console
|
||||
> cargo hfuzz run-debug arrayish hfuzz_workspace/arrayish/*.fuzz
|
||||
```
|
||||
@@ -0,0 +1,61 @@
|
||||
use arbitrary::{Arbitrary, Unstructured};
|
||||
use std::ops::{Range, RangeBounds, RangeFrom, RangeInclusive, RangeTo, RangeToInclusive, Bound};
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub enum ArbRange<T> {
|
||||
Range(Range<T>),
|
||||
RangeFrom(RangeFrom<T>),
|
||||
RangeInclusive(RangeInclusive<T>),
|
||||
RangeTo(RangeTo<T>),
|
||||
RangeToInclusive(RangeToInclusive<T>),
|
||||
}
|
||||
|
||||
impl<T> RangeBounds<T> for ArbRange<T> {
|
||||
fn start_bound(&self) -> Bound<&T> {
|
||||
match self {
|
||||
ArbRange::Range(range) => range.start_bound(),
|
||||
ArbRange::RangeFrom(range) => range.start_bound(),
|
||||
ArbRange::RangeInclusive(range) => range.start_bound(),
|
||||
ArbRange::RangeTo(range) => range.start_bound(),
|
||||
ArbRange::RangeToInclusive(range) => range.start_bound(),
|
||||
}
|
||||
}
|
||||
|
||||
fn end_bound(&self) -> Bound<&T> {
|
||||
match self {
|
||||
ArbRange::Range(range) => range.end_bound(),
|
||||
ArbRange::RangeFrom(range) => range.end_bound(),
|
||||
ArbRange::RangeInclusive(range) => range.end_bound(),
|
||||
ArbRange::RangeTo(range) => range.end_bound(),
|
||||
ArbRange::RangeToInclusive(range) => range.end_bound(),
|
||||
}
|
||||
}
|
||||
|
||||
fn contains<U: ?Sized>(&self, item: &U) -> bool
|
||||
where
|
||||
T: PartialOrd<U>,
|
||||
U: PartialOrd<T>,
|
||||
{
|
||||
match self {
|
||||
ArbRange::Range(range) => range.contains(item),
|
||||
ArbRange::RangeFrom(range) => range.contains(item),
|
||||
ArbRange::RangeInclusive(range) => range.contains(item),
|
||||
ArbRange::RangeTo(range) => range.contains(item),
|
||||
ArbRange::RangeToInclusive(range) => range.contains(item),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Arbitrary> Arbitrary for ArbRange<T> {
|
||||
fn arbitrary<U: Unstructured + ?Sized>(u: &mut U) -> Result<Self, U::Error> {
|
||||
let variant = u8::arbitrary(u)? % 5;
|
||||
Ok(match variant {
|
||||
0 => ArbRange::Range(T::arbitrary(u)?..T::arbitrary(u)?),
|
||||
1 => ArbRange::RangeFrom(T::arbitrary(u)?..),
|
||||
2 => ArbRange::RangeInclusive(T::arbitrary(u)?..=T::arbitrary(u)?),
|
||||
3 => ArbRange::RangeTo(..T::arbitrary(u)?),
|
||||
4 => ArbRange::RangeToInclusive(..=T::arbitrary(u)?),
|
||||
_ => unreachable!(),
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
use derive_arbitrary::Arbitrary;
|
||||
use arbitrary_model_tests::arbitrary_stateful_operations;
|
||||
use honggfuzz::fuzz;
|
||||
use std::{fmt::Debug, iter::FromIterator, ops::{RangeBounds, Bound}};
|
||||
|
||||
use tinyvec::ArrayishVec;
|
||||
use tinyvec_fuzz::ArbRange;
|
||||
|
||||
const CAPACITY: usize = 32;
|
||||
|
||||
arbitrary_stateful_operations! {
|
||||
model = Vec<T>,
|
||||
tested = ArrayishVec<[T; CAPACITY]>,
|
||||
|
||||
type_parameters = <
|
||||
T: Default + Clone + Debug + Eq + Ord,
|
||||
R: RangeBounds<usize> + Clone + Debug,
|
||||
>,
|
||||
|
||||
methods {
|
||||
equal {
|
||||
fn as_mut_slice(&mut self) -> &mut [T];
|
||||
fn as_slice(&self) -> &[T];
|
||||
fn clear(&mut self);
|
||||
fn dedup(&mut self);
|
||||
fn insert(&mut self, index: usize, item: T);
|
||||
fn is_empty(&self) -> bool;
|
||||
fn len(&self) -> usize;
|
||||
fn push(&mut self, item: T);
|
||||
fn truncate(&mut self, new_len: usize);
|
||||
}
|
||||
|
||||
equal_with(Vec::from_iter) {
|
||||
fn drain(&self, range: R) -> impl Iterator<Item = T>;
|
||||
fn iter(&self) -> impl Iterator<Item = &T>;
|
||||
fn iter_mut(&self) -> impl Iterator<Item = &mut T>;
|
||||
}
|
||||
}
|
||||
|
||||
pre {
|
||||
match self {
|
||||
Self::insert { index, .. } if index > model.len() => {
|
||||
// TODO: Should test that these identically panic
|
||||
return;
|
||||
}
|
||||
Self::insert { .. } | Self::push { .. } if model.len() == CAPACITY => {
|
||||
return;
|
||||
}
|
||||
Self::drain { ref range } => {
|
||||
// TODO: Should test that these identically panic
|
||||
let start = match range.start_bound() {
|
||||
Bound::Included(&n) => n,
|
||||
Bound::Excluded(&n) => n + 1,
|
||||
Bound::Unbounded => 0,
|
||||
};
|
||||
let end = match range.end_bound() {
|
||||
// If it's already usize::max, doesn't really matter about adding 1
|
||||
Bound::Included(&n) => n.checked_add(1).unwrap_or(n),
|
||||
Bound::Excluded(&n) => n,
|
||||
Bound::Unbounded => model.len(),
|
||||
};
|
||||
if start > end || end > model.len() {
|
||||
return;
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const MAX_RING_SIZE: usize = 16_384;
|
||||
|
||||
fn fuzz_cycle(data: &[u8]) -> Result<(), ()> {
|
||||
use arbitrary::{Arbitrary, FiniteBuffer};
|
||||
|
||||
let mut ring = FiniteBuffer::new(&data, MAX_RING_SIZE).map_err(|_| ())?;
|
||||
|
||||
let mut model = Vec::<u16>::default();
|
||||
let mut tested: ArrayishVec<[u16; 32]> = ArrayishVec::new();
|
||||
|
||||
let mut _op_trace = String::new();
|
||||
while let Ok(op) = <op::Op<u16, ArbRange<usize>> as Arbitrary>::arbitrary(&mut ring) {
|
||||
#[cfg(fuzzing_debug)]
|
||||
_op_trace.push_str(&format!("{}\n", op.to_string()));
|
||||
op.execute_and_compare(&mut model, &mut tested);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn main() -> Result<(), ()> {
|
||||
better_panic::install();
|
||||
|
||||
loop {
|
||||
fuzz!(|data: &[u8]| {
|
||||
let _ = fuzz_cycle(data);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
use derive_arbitrary::Arbitrary;
|
||||
use arbitrary_model_tests::arbitrary_stateful_operations;
|
||||
use honggfuzz::fuzz;
|
||||
use std::{fmt::Debug, iter::FromIterator, ops::{RangeBounds, Bound}};
|
||||
|
||||
use tinyvec::TinyVec;
|
||||
use tinyvec_fuzz::ArbRange;
|
||||
|
||||
const CAPACITY: usize = 32;
|
||||
|
||||
arbitrary_stateful_operations! {
|
||||
model = Vec<T>,
|
||||
tested = TinyVec<[T; CAPACITY]>,
|
||||
|
||||
type_parameters = <
|
||||
T: Default + Clone + Debug + Eq + Ord,
|
||||
R: RangeBounds<usize> + Clone + Debug,
|
||||
>,
|
||||
|
||||
methods {
|
||||
equal {
|
||||
fn as_mut_slice(&mut self) -> &mut [T];
|
||||
fn as_slice(&self) -> &[T];
|
||||
fn clear(&mut self);
|
||||
fn dedup(&mut self);
|
||||
fn insert(&mut self, index: usize, item: T);
|
||||
fn is_empty(&self) -> bool;
|
||||
fn len(&self) -> usize;
|
||||
fn push(&mut self, item: T);
|
||||
fn truncate(&mut self, new_len: usize);
|
||||
}
|
||||
|
||||
equal_with(Vec::from_iter) {
|
||||
fn drain(&self, range: R) -> impl Iterator<Item = T>;
|
||||
fn iter(&self) -> impl Iterator<Item = &T>;
|
||||
fn iter_mut(&self) -> impl Iterator<Item = &mut T>;
|
||||
}
|
||||
}
|
||||
|
||||
pre {
|
||||
match self {
|
||||
Self::insert { index, .. } if index > model.len() => {
|
||||
// TODO: Should test that these identically panic
|
||||
return;
|
||||
}
|
||||
Self::drain { ref range } => {
|
||||
// TODO: Should test that these identically panic
|
||||
let start = match range.start_bound() {
|
||||
Bound::Included(&n) => n,
|
||||
Bound::Excluded(&n) => n + 1,
|
||||
Bound::Unbounded => 0,
|
||||
};
|
||||
let end = match range.end_bound() {
|
||||
// If it's already usize::max, doesn't really matter about adding 1
|
||||
Bound::Included(&n) => n.checked_add(1).unwrap_or(n),
|
||||
Bound::Excluded(&n) => n,
|
||||
Bound::Unbounded => model.len(),
|
||||
};
|
||||
if start > end || end > model.len() {
|
||||
return;
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const MAX_RING_SIZE: usize = 16_384;
|
||||
|
||||
fn fuzz_cycle(data: &[u8]) -> Result<(), ()> {
|
||||
use arbitrary::{Arbitrary, FiniteBuffer};
|
||||
|
||||
let mut ring = FiniteBuffer::new(&data, MAX_RING_SIZE).map_err(|_| ())?;
|
||||
|
||||
let mut model = Vec::<u16>::default();
|
||||
let mut tested: TinyVec<[u16; 32]> = TinyVec::new();
|
||||
|
||||
let mut _op_trace = String::new();
|
||||
while let Ok(op) = <op::Op<u16, ArbRange<usize>> as Arbitrary>::arbitrary(&mut ring) {
|
||||
#[cfg(fuzzing_debug)]
|
||||
_op_trace.push_str(&format!("{}\n", op.to_string()));
|
||||
op.execute_and_compare(&mut model, &mut tested);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn main() -> Result<(), ()> {
|
||||
better_panic::install();
|
||||
|
||||
loop {
|
||||
fuzz!(|data: &[u8]| {
|
||||
let _ = fuzz_cycle(data);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
mod arb_range;
|
||||
|
||||
pub use arb_range::ArbRange;
|
||||
+2
-2
@@ -200,8 +200,8 @@ impl<A: Arrayish> ArrayishVec<A> {
|
||||
Bound::Unbounded => 0,
|
||||
};
|
||||
let end = match range.end_bound() {
|
||||
Bound::Included(x) => *x,
|
||||
Bound::Excluded(x) => x - 1,
|
||||
Bound::Included(x) => x + 1,
|
||||
Bound::Excluded(x) => *x,
|
||||
Bound::Unbounded => self.len,
|
||||
};
|
||||
assert!(
|
||||
|
||||
+2
-2
@@ -215,8 +215,8 @@ impl<A: Arrayish> TinyVec<A> {
|
||||
Bound::Unbounded => 0,
|
||||
};
|
||||
let end = match range.end_bound() {
|
||||
Bound::Included(x) => *x,
|
||||
Bound::Excluded(x) => x - 1,
|
||||
Bound::Included(x) => x + 1,
|
||||
Bound::Excluded(x) => *x,
|
||||
Bound::Unbounded => self.len(),
|
||||
};
|
||||
assert!(
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#![allow(bad_style)]
|
||||
|
||||
use tinyvec::*;
|
||||
use std::iter::FromIterator;
|
||||
|
||||
#[test]
|
||||
fn test_a_vec() {
|
||||
@@ -121,3 +122,32 @@ fn ArrayishVec_remove() {
|
||||
assert_eq!(av.remove(1), 2);
|
||||
assert_eq!(&av[..], &[1, 3][..]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ArrayishVec_drain() {
|
||||
let mut av: ArrayishVec<[i32; 10]> = Default::default();
|
||||
av.push(1);
|
||||
av.push(2);
|
||||
av.push(3);
|
||||
|
||||
assert_eq!(Vec::from_iter(av.clone().drain(..)), vec![1, 2, 3]);
|
||||
|
||||
assert_eq!(Vec::from_iter(av.clone().drain(..2)), vec![1, 2]);
|
||||
assert_eq!(Vec::from_iter(av.clone().drain(..3)), vec![1, 2, 3]);
|
||||
|
||||
assert_eq!(Vec::from_iter(av.clone().drain(..=1)), vec![1, 2]);
|
||||
assert_eq!(Vec::from_iter(av.clone().drain(..=2)), vec![1, 2, 3]);
|
||||
|
||||
assert_eq!(Vec::from_iter(av.clone().drain(0..)), vec![1, 2, 3]);
|
||||
assert_eq!(Vec::from_iter(av.clone().drain(1..)), vec![2, 3]);
|
||||
|
||||
assert_eq!(Vec::from_iter(av.clone().drain(0..2)), vec![1, 2]);
|
||||
assert_eq!(Vec::from_iter(av.clone().drain(0..3)), vec![1, 2, 3]);
|
||||
assert_eq!(Vec::from_iter(av.clone().drain(1..2)), vec![2]);
|
||||
assert_eq!(Vec::from_iter(av.clone().drain(1..3)), vec![2, 3]);
|
||||
|
||||
assert_eq!(Vec::from_iter(av.clone().drain(0..=1)), vec![1, 2]);
|
||||
assert_eq!(Vec::from_iter(av.clone().drain(0..=2)), vec![1, 2, 3]);
|
||||
assert_eq!(Vec::from_iter(av.clone().drain(1..=1)), vec![2]);
|
||||
assert_eq!(Vec::from_iter(av.clone().drain(1..=2)), vec![2, 3]);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
#![allow(bad_style)]
|
||||
|
||||
use tinyvec::*;
|
||||
use std::iter::FromIterator;
|
||||
|
||||
#[test]
|
||||
fn TinyVec_drain() {
|
||||
let mut tv: TinyVec<[i32; 10]> = Default::default();
|
||||
tv.push(1);
|
||||
tv.push(2);
|
||||
tv.push(3);
|
||||
|
||||
assert_eq!(Vec::from_iter(tv.clone().drain(..)), vec![1, 2, 3]);
|
||||
|
||||
assert_eq!(Vec::from_iter(tv.clone().drain(..2)), vec![1, 2]);
|
||||
assert_eq!(Vec::from_iter(tv.clone().drain(..3)), vec![1, 2, 3]);
|
||||
|
||||
assert_eq!(Vec::from_iter(tv.clone().drain(..=1)), vec![1, 2]);
|
||||
assert_eq!(Vec::from_iter(tv.clone().drain(..=2)), vec![1, 2, 3]);
|
||||
|
||||
assert_eq!(Vec::from_iter(tv.clone().drain(0..)), vec![1, 2, 3]);
|
||||
assert_eq!(Vec::from_iter(tv.clone().drain(1..)), vec![2, 3]);
|
||||
|
||||
assert_eq!(Vec::from_iter(tv.clone().drain(0..2)), vec![1, 2]);
|
||||
assert_eq!(Vec::from_iter(tv.clone().drain(0..3)), vec![1, 2, 3]);
|
||||
assert_eq!(Vec::from_iter(tv.clone().drain(1..2)), vec![2]);
|
||||
assert_eq!(Vec::from_iter(tv.clone().drain(1..3)), vec![2, 3]);
|
||||
|
||||
assert_eq!(Vec::from_iter(tv.clone().drain(0..=1)), vec![1, 2]);
|
||||
assert_eq!(Vec::from_iter(tv.clone().drain(0..=2)), vec![1, 2, 3]);
|
||||
assert_eq!(Vec::from_iter(tv.clone().drain(1..=1)), vec![2]);
|
||||
assert_eq!(Vec::from_iter(tv.clone().drain(1..=2)), vec![2, 3]);
|
||||
}
|
||||
Reference in New Issue
Block a user