From 57a40a378beb8a72d471562fae7e61200ccecfa6 Mon Sep 17 00:00:00 2001 From: Arthur Silva Date: Wed, 20 Mar 2024 10:02:37 +0100 Subject: [PATCH] Update fuzz tests and have run them in CI --- .github/workflows/ci.yml | 13 +++++++++ fuzz/Cargo.toml | 10 ++++--- fuzz/fuzz_targets/hashset.rs | 11 ++++---- fuzz/fuzz_targets/ordset.rs | 52 +++++++++++++++++++++++++++++++----- fuzz/fuzz_targets/vector.rs | 9 ++++--- 5 files changed, 77 insertions(+), 18 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3c630d3..987a350 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -53,3 +53,16 @@ jobs: with: token: ${{ secrets.GITHUB_TOKEN }} args: --all-features + + fuzz-tests: + name: Fuzz tests + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: nightly + override: true + - run: cargo install cargo-fuzz + - run: for fuzz_test in `cargo fuzz list`; do cargo fuzz run $fuzz_test -- -max_total_time=10 || exit 1; done diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml index 6a8e12a..883b4d9 100644 --- a/fuzz/Cargo.toml +++ b/fuzz/Cargo.toml @@ -3,14 +3,18 @@ name = "im-fuzz" version = "0.0.0" authors = ["Automatically generated"] publish = false -edition = "2018" +edition = "2021" [package.metadata] cargo-fuzz = true +[features] +default = ["small-chunks"] +small-chunks = ["imbl/small-chunks"] + [dependencies] -libfuzzer-sys = "0.2" -arbitrary = { version = "0.3", features = ["derive"] } +libfuzzer-sys = "0.4" +arbitrary = { version = "1", features = ["derive"] } [dependencies.imbl] path = ".." diff --git a/fuzz/fuzz_targets/hashset.rs b/fuzz/fuzz_targets/hashset.rs index c76f573..dfec9a2 100644 --- a/fuzz/fuzz_targets/hashset.rs +++ b/fuzz/fuzz_targets/hashset.rs @@ -2,6 +2,7 @@ use std::collections::HashSet as NatSet; use std::fmt::Debug; +use std::iter::FromIterator; use arbitrary::Arbitrary; use libfuzzer_sys::fuzz_target; @@ -14,10 +15,7 @@ enum Action { Remove(A), } -#[derive(Arbitrary)] -struct Actions(Vec>); - -fuzz_target!(|actions: Vec>| { +fuzz_target!(|actions: Vec>| { let mut set = HashSet::new(); let mut nat = NatSet::new(); for action in actions { @@ -36,6 +34,9 @@ fuzz_target!(|actions: Vec>| { } } assert_eq!(nat.len(), set.len()); - assert_eq!(HashSet::from(nat.clone()), set); } + assert_eq!(HashSet::from(nat.clone()), set); + assert_eq!(NatSet::from_iter(set.iter().cloned()), nat); + assert_eq!(set.iter().count(), nat.len()); + assert_eq!(set.into_iter().count(), nat.len()); }); diff --git a/fuzz/fuzz_targets/ordset.rs b/fuzz/fuzz_targets/ordset.rs index c317ccc..5b34e86 100644 --- a/fuzz/fuzz_targets/ordset.rs +++ b/fuzz/fuzz_targets/ordset.rs @@ -1,7 +1,8 @@ #![no_main] -use std::collections::HashSet as NatSet; +use std::collections::BTreeSet as NatSet; use std::fmt::Debug; +use std::ops::Range; use arbitrary::Arbitrary; use libfuzzer_sys::fuzz_target; @@ -9,15 +10,13 @@ use libfuzzer_sys::fuzz_target; use imbl::OrdSet; #[derive(Arbitrary, Debug)] -enum Action { +enum Action { Insert(A), Remove(A), + Range(Range), } -#[derive(Arbitrary)] -struct Actions(Vec>); - -fuzz_target!(|actions: Vec>| { +fuzz_target!(|actions: Vec>| { let mut set = OrdSet::new(); let mut nat = NatSet::new(); for action in actions { @@ -34,8 +33,47 @@ fuzz_target!(|actions: Vec>| { set.remove(&value); assert_eq!(len, set.len()); } + Action::Range(range) => { + assert_eq!(set.get_min(), nat.first()); + assert_eq!(set.get_max(), nat.last()); + assert_eq!(set.get_next(&range.start), nat.range(range.start..).next()); + assert_eq!(set.get_prev(&range.start), nat.range(..=range.start).last()); + + let mut set_it = set.range(range.clone()); + let mut nat_it = nat.range(range.clone()); + loop { + let (a, b) = (set_it.next(), nat_it.next()); + assert_eq!(a, b); + if a.is_none() { + break; + } + } + let range = range.start..=range.end; + let mut set_it = set.range(range.clone()); + let mut nat_it = nat.range(range); + loop { + let (a, b) = (set_it.next(), nat_it.next()); + assert_eq!(a, b); + if a.is_none() { + break; + } + } + } } assert_eq!(nat.len(), set.len()); - assert_eq!(OrdSet::from(nat.clone()), set); + } + assert_eq!(OrdSet::from(nat.clone()), set); + assert_eq!(OrdSet::from_iter(nat.iter().cloned()), set); + for (a, b) in set.range(..).zip(nat.range(..)) { + assert_eq!(a, b); + } + for (a, b) in set.iter().zip(&nat) { + assert_eq!(a, b); + } + for (a, b) in set.iter().rev().zip(nat.iter().rev()) { + assert_eq!(a, b); + } + for (a, b) in set.into_iter().zip(nat) { + assert_eq!(a, b); } }); diff --git a/fuzz/fuzz_targets/vector.rs b/fuzz/fuzz_targets/vector.rs index f850e4c..28aae06 100644 --- a/fuzz/fuzz_targets/vector.rs +++ b/fuzz/fuzz_targets/vector.rs @@ -30,7 +30,7 @@ fn cap_index(len: usize, index: usize) -> usize { } } -fuzz_target!(|actions: Vec>| { +fuzz_target!(|actions: Vec>| { let mut vec = Vector::new(); let mut nat = Vec::new(); vec.assert_invariants(); @@ -121,8 +121,11 @@ fuzz_target!(|actions: Vec>| { nat = nat_right; } } - vec.assert_invariants(); assert_eq!(nat.len(), vec.len()); - assert_eq!(Vector::from_iter(nat.iter().cloned()), vec); } + vec.assert_invariants(); + assert_eq!(Vector::from_iter(nat.iter().cloned()), vec); + assert_eq!(Vec::from_iter(vec.iter().cloned()), nat); + assert_eq!(Vec::from_iter(vec.clone().into_iter()), nat); + assert_eq!(Vec::from_iter(vec.iter().rev()), Vec::from_iter(nat.iter().rev())); });