Skip to content

Commit

Permalink
chore: add fuzzer
Browse files Browse the repository at this point in the history
  • Loading branch information
arriqaaq committed Oct 9, 2024
1 parent fe149e6 commit abd181d
Show file tree
Hide file tree
Showing 6 changed files with 421 additions and 0 deletions.
40 changes: 40 additions & 0 deletions .github/workflows/fuzz.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
name: Run Fuzztest

on:
push:
branches: ["chore/fuzz"]
pull_request:
branches: ["chore/fuzz"]

env:
RUST_BACKTRACE: "1"

jobs:
base:
name: Fuzztest
strategy:
fail-fast: false
matrix:
toolchain:
- nightly
include:
- toolchain: nightly
components: rustfmt
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ matrix.toolchain }}
components: ${{ matrix.components }}
- uses: Swatinem/[email protected]
with:
key: ubuntu-latest_${{ matrix.toolchain }}"
- name: Build
run: cargo build --all-features
- name: Install cargo-fuzz
run: cargo install cargo-fuzz
- name: Change to fuzz directory
run: cd fuzz
- name: Get fuzzy for 30 seconds
run: cargo fuzz run fuzz -- -max_total_time=30s
3 changes: 3 additions & 0 deletions fuzz/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
target
corpus
artifacts
120 changes: 120 additions & 0 deletions fuzz/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

26 changes: 26 additions & 0 deletions fuzz/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
[package]
name = "vart-fuzz"
version = "0.1.0"
authors = ["Automatically generated"]
publish = false
edition = "2021"

[package.metadata]
cargo-fuzz = true

[dependencies]
libfuzzer-sys = "0.4.6"
arbitrary = { version = "1.3.0", features = ["derive"] }

[dependencies.vart]
path = ".."

# Prevent this from interfering with workspaces
[workspace]
members = ["."]

[[bin]]
name = "fuzz"
path = "src/vart_check.rs"
test = false
doc = false
114 changes: 114 additions & 0 deletions fuzz/src/vart_check.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
#![no_main]

use std::collections::BTreeMap;

use arbitrary::Arbitrary;
use libfuzzer_sys::fuzz_target;

use vart::{art::Tree, FixedSizeKey};

#[derive(Arbitrary, Debug)]
enum TreeOperation {
Get { key: u16 },
Insert { key: u16, value: usize },
Delete { key: u16 },
RangeQuery { start: u16, end: u16 },
}

fn assert_tree_consistency(
art_tree: &Tree<FixedSizeKey<16>, usize>,
reference_map: &BTreeMap<u16, usize>,
key: u16,
) {
let art_key: FixedSizeKey<16> = FixedSizeKey::from(key);
let expected_value = reference_map.get(&key).copied();
let actual_value = art_tree.get(&art_key, 0).map(|(v, _, _)| v);

assert_eq!(
actual_value, expected_value,
"Inconsistent values for key {}: ART {:?} != BTreeMap {:?}",
key, actual_value, expected_value
);
}

fn assert_range_query_consistency(
art_tree: &Tree<FixedSizeKey<16>, usize>,
reference_map: &BTreeMap<u16, usize>,
start: u16,
end: u16,
) {
let expected_values: Vec<(FixedSizeKey<16>, usize)> = reference_map
.range(start..=end)
.map(|(&k, &v)| (FixedSizeKey::from(k), v))
.collect();

let start_key: FixedSizeKey<16> = start.into();
let end_key: FixedSizeKey<16> = end.into();
let actual_values: Vec<_> = art_tree
.range(start_key..=end_key)
.map(|(k, v, _, _)| (k.as_slice().into(), *v))
.collect();

assert_eq!(
actual_values, expected_values,
"Inconsistent range query results for range {}..={}: ART {:?} != BTreeMap {:?}",
start, end, actual_values, expected_values
);
}


fuzz_target!(|operations: Vec<TreeOperation>| {
let mut art_tree = Tree::<FixedSizeKey<16>, usize>::new();
let mut reference_map = BTreeMap::<u16, usize>::new();

for operation in operations {
match operation {
TreeOperation::Get { key } => {
assert_tree_consistency(&art_tree, &reference_map, key);
}
TreeOperation::Insert { key, value } => {
let art_key: FixedSizeKey<16> = FixedSizeKey::from(key);

reference_map.insert(key, value);
art_tree.insert(&art_key, value, 0, 0).expect("Insert failed");

assert_tree_consistency(&art_tree, &reference_map, key);
}
TreeOperation::Delete { key } => {
reference_map.remove(&key);
let art_key: FixedSizeKey<16> = FixedSizeKey::from(key);
art_tree.remove(&art_key);

assert_tree_consistency(&art_tree, &reference_map, key);
}
TreeOperation::RangeQuery { start, end } => {
if start <= end{
assert_range_query_consistency(&art_tree, &reference_map, start, end);
}
}
}
}

for (key, expected_value) in reference_map.iter() {
let art_key: FixedSizeKey<16> = FixedSizeKey::from(*key);
let result = art_tree.get(&art_key, 0);
if let Some((actual_value, _, _)) = result {
assert_eq!(
Some(actual_value),
Some(*expected_value),
"Expected value for key {}: {:?} != {:?}, got {:?}",
key,
actual_value,
*expected_value,
result
);
} else {
assert!(
result.is_none(),
"Expected None for key {}, got {:?}",
key,
result
);
}
}
});
Loading

0 comments on commit abd181d

Please sign in to comment.