Skip to content

Commit 279109b

Browse files
authored
include more examples (#220)
1 parent 17014af commit 279109b

File tree

10 files changed

+385
-8
lines changed

10 files changed

+385
-8
lines changed

.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,5 @@ crash-*
77
*.o
88
corpus
99
crashes
10+
perf.data*
11+
*.svg

examples/boolean-tree/Cargo.toml

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
[package]
2+
name = "boolean-tree"
3+
version = "0.1.0"
4+
edition = "2021"
5+
6+
[dependencies]
7+
bolero-generator = { path = "../../lib/bolero-generator" }
8+
9+
[dev-dependencies]
10+
bolero = { path = "../../lib/bolero" }
11+
12+
[workspace]
13+
members = ["."]
14+
15+
[profile.fuzz]
16+
inherits = "dev"
17+
opt-level = 3
18+
incremental = false
19+
codegen-units = 1

examples/boolean-tree/README.md

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# boolean-tree
2+
3+
A simple boolean logic language

examples/boolean-tree/src/lib.rs

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
use bolero_generator::TypeGenerator;
2+
3+
thread_local! {
4+
static SHOULD_PANIC: bool = {
5+
#[cfg(bolero_should_panic)]
6+
return true;
7+
8+
#[cfg(not(bolero_should_panic))]
9+
return std::env::var("SHOULD_PANIC").is_ok();
10+
};
11+
}
12+
13+
#[cfg(test)]
14+
mod tests;
15+
16+
#[derive(Clone, Debug, TypeGenerator)]
17+
pub enum Expr {
18+
Value(bool),
19+
And(Box<Expr>, Box<Expr>),
20+
Or(Box<Expr>, Box<Expr>),
21+
Xor(Box<Expr>, Box<Expr>),
22+
Nand(Box<Expr>, Box<Expr>),
23+
Not(Box<Expr>),
24+
}
25+
26+
impl Expr {
27+
pub fn eval(&self) -> bool {
28+
match self {
29+
Expr::Value(value) => *value,
30+
Expr::And(a, b) => a.eval() && b.eval(),
31+
Expr::Or(a, b) => a.eval() || b.eval(),
32+
Expr::Xor(a, b) => a.eval() ^ b.eval(),
33+
Expr::Nand(a, b) => !(a.eval() && b.eval()),
34+
Expr::Not(_) if SHOULD_PANIC.with(|v| *v) => unreachable!(),
35+
Expr::Not(a) => !a.eval(),
36+
}
37+
}
38+
}

examples/boolean-tree/src/tests.rs

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
use super::*;
2+
use bolero::check;
3+
4+
#[test]
5+
fn model_test() {
6+
check!()
7+
.with_type::<Expr>()
8+
.with_max_depth(3)
9+
.for_each(|ops| {
10+
let _value = ops.eval();
11+
})
12+
}

examples/rle-stack/Cargo.toml

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
[package]
2+
name = "rle-stack"
3+
version = "0.1.0"
4+
edition = "2021"
5+
6+
[dev-dependencies]
7+
bolero = { path = "../../lib/bolero" }
8+
9+
[workspace]
10+
members = ["."]
11+
12+
[profile.fuzz]
13+
inherits = "dev"
14+
opt-level = 3
15+
incremental = false
16+
codegen-units = 1

examples/rle-stack/README.md

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# rle-stack
2+
A stack which compresses value with run-length encoding

examples/rle-stack/src/lib.rs

+154
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
use core::fmt;
2+
3+
thread_local! {
4+
static SHOULD_PANIC: bool = {
5+
#[cfg(bolero_should_panic)]
6+
return true;
7+
8+
#[cfg(not(bolero_should_panic))]
9+
return std::env::var("SHOULD_PANIC").is_ok();
10+
};
11+
}
12+
13+
type Counter = u16;
14+
15+
//= https://en.wikipedia.org/wiki/Run-length_encoding
16+
//# Run-length encoding (RLE) is a form of lossless data compression in which
17+
//# runs of data (sequences in which the same data value occurs in many consecutive
18+
//# data elements) are stored as a single data value and count, rather than as
19+
//# the original run.
20+
21+
pub struct RleStack<T> {
22+
stack: Vec<Entry<T>>,
23+
len: usize,
24+
}
25+
26+
impl<T> Default for RleStack<T> {
27+
fn default() -> Self {
28+
Self {
29+
stack: vec![],
30+
len: 0,
31+
}
32+
}
33+
}
34+
35+
impl<T: fmt::Debug> fmt::Debug for RleStack<T> {
36+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
37+
f.debug_struct("RleStack")
38+
.field("stack", &self.stack)
39+
.field("len", &self.len)
40+
.finish()
41+
}
42+
}
43+
44+
impl<T: Clone + Eq> RleStack<T> {
45+
pub fn push(&mut self, value: T) {
46+
self.len += 1;
47+
48+
if let Some(prev) = self.stack.last_mut() {
49+
if prev.merge(&value) {
50+
return;
51+
}
52+
}
53+
54+
self.stack.push(Entry::new(value));
55+
}
56+
57+
pub fn pop(&mut self) -> Option<T> {
58+
let entry = self.stack.last_mut()?;
59+
60+
self.len -= 1;
61+
62+
if entry.additional == 0 {
63+
return self.stack.pop().map(|entry| entry.value);
64+
}
65+
entry.additional -= 1;
66+
Some(entry.value.clone())
67+
}
68+
69+
pub fn clear(&mut self) {
70+
// inject faults into the model if configured
71+
if SHOULD_PANIC.with(|v| *v) {
72+
return;
73+
}
74+
75+
self.len = 0;
76+
self.stack.clear();
77+
}
78+
79+
pub fn len(&self) -> usize {
80+
self.len
81+
}
82+
83+
pub fn is_empty(&self) -> bool {
84+
self.len == 0
85+
}
86+
87+
pub fn iter(&self) -> Iter<T> {
88+
Iter {
89+
entry: 0,
90+
count: 0,
91+
stack: self,
92+
}
93+
}
94+
}
95+
96+
pub struct Iter<'a, T> {
97+
entry: usize,
98+
count: Counter,
99+
stack: &'a RleStack<T>,
100+
}
101+
102+
impl<'a, T> Iterator for Iter<'a, T> {
103+
type Item = &'a T;
104+
105+
fn next(&mut self) -> Option<Self::Item> {
106+
let entry = self.stack.stack.get(self.entry)?;
107+
108+
if entry.additional <= self.count {
109+
self.entry += 1;
110+
self.count = 0;
111+
} else {
112+
self.count += 1;
113+
}
114+
115+
Some(&entry.value)
116+
}
117+
}
118+
119+
struct Entry<T> {
120+
value: T,
121+
additional: Counter,
122+
}
123+
124+
impl<T: fmt::Debug> fmt::Debug for Entry<T> {
125+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
126+
f.debug_struct("Entry")
127+
.field("value", &self.value)
128+
.field("additional", &self.additional)
129+
.finish()
130+
}
131+
}
132+
133+
impl<T> Entry<T> {
134+
pub fn new(value: T) -> Self {
135+
Self {
136+
value,
137+
additional: 0,
138+
}
139+
}
140+
}
141+
142+
impl<T: Eq> Entry<T> {
143+
pub fn merge(&mut self, other: &T) -> bool {
144+
if &self.value == other {
145+
self.additional += 1;
146+
true
147+
} else {
148+
false
149+
}
150+
}
151+
}
152+
153+
#[cfg(test)]
154+
mod tests;

examples/rle-stack/src/tests.rs

+109
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
use super::*;
2+
use bolero::{check, generator::*};
3+
use core::fmt;
4+
5+
#[derive(Clone, Copy, Debug, TypeGenerator)]
6+
enum Operation<T> {
7+
Push { count: u8, value: T },
8+
Pop { count: u8 },
9+
Clear,
10+
}
11+
12+
#[derive(Default)]
13+
struct Model<T: Copy + fmt::Debug + Eq> {
14+
oracle: Vec<T>,
15+
subject: RleStack<T>,
16+
}
17+
18+
impl<T: Copy + fmt::Debug + Eq> Model<T> {
19+
pub fn push(&mut self, value: T) {
20+
self.oracle.push(value);
21+
self.subject.push(value);
22+
self.invariants();
23+
}
24+
25+
pub fn pop(&mut self) -> Option<T> {
26+
let expected = self.oracle.pop();
27+
let actual = self.subject.pop();
28+
assert_eq!(expected, actual);
29+
self.invariants();
30+
actual
31+
}
32+
33+
pub fn clear(&mut self) {
34+
self.oracle.clear();
35+
self.subject.clear();
36+
self.invariants();
37+
}
38+
39+
pub fn apply(&mut self, operation: Operation<T>) {
40+
match operation {
41+
Operation::Push { count, value } => {
42+
for _ in 0..count {
43+
self.push(value);
44+
}
45+
}
46+
Operation::Pop { count } => {
47+
for _ in 0..count {
48+
self.pop();
49+
}
50+
}
51+
Operation::Clear => {
52+
self.clear();
53+
}
54+
}
55+
}
56+
57+
pub fn finish(&self) {
58+
self.invariants();
59+
let actual: Vec<_> = self.subject.iter().copied().collect();
60+
assert_eq!(
61+
self.oracle, actual,
62+
"\n\nSubject state: {:#?}",
63+
self.subject
64+
);
65+
}
66+
67+
fn invariants(&self) {
68+
assert_eq!(
69+
self.oracle.len(),
70+
self.subject.len(),
71+
"lengths do not match"
72+
);
73+
assert_eq!(
74+
self.oracle.is_empty(),
75+
self.subject.is_empty(),
76+
"is_empty does not match"
77+
);
78+
}
79+
}
80+
81+
impl<T: Copy + fmt::Debug + Eq> Drop for Model<T> {
82+
fn drop(&mut self) {
83+
if !std::thread::panicking() {
84+
self.finish();
85+
}
86+
}
87+
}
88+
89+
#[test]
90+
fn model_test() {
91+
check!().with_type::<Vec<Operation<u8>>>().for_each(|ops| {
92+
let mut model = Model::default();
93+
for op in ops {
94+
model.apply(*op);
95+
}
96+
})
97+
}
98+
99+
#[test]
100+
fn unit_test() {
101+
let mut model = <Model<u8>>::default();
102+
103+
assert!(model.pop().is_none());
104+
model.clear();
105+
assert!(model.pop().is_none());
106+
107+
model.push(123);
108+
assert_eq!(model.pop(), Some(123));
109+
}

0 commit comments

Comments
 (0)