Skip to content

Instantly share code, notes, and snippets.

@moodmosaic
Last active April 24, 2025 21:20
Show Gist options
  • Save moodmosaic/5886807f96afbe81ad3f7dd68186b808 to your computer and use it in GitHub Desktop.
Save moodmosaic/5886807f96afbe81ad3f7dd68186b808 to your computer and use it in GitHub Desktop.
madhouse-rs playground
#!/bin/sh
# Test run summary
# ================
#
# Basic test run:
# ./play.sh
#
# MADHOUSE mode (randomized fuzzing):
# MADHOUSE=1 ./play.sh
#
# Navigate to preserved temp directory after failure:
# cd /tmp/play-<random>/
#
# Enter test crate subdirectory:
# cd counter/
#
# Re-run failed test with shrink iteration cap:
# MADHOUSE=1 PROPTEST_MAX_SHRINK_ITERS=50 cargo test
#
# Typical failure:
# Counter value exceeded maximum allowed.
#
# Failing inputs are saved in:
# counter/proptest-regressions/lib.txt
#
# Test commands:
# INCREMENT(n)
# DECREMENT(n)
#
# Observed behavior:
# - Normal runs test a few hand-picked inputs.
# - MADHOUSE mode generates large randomized sequences.
# - Panics if counter exceeds hardcoded max (100).
#
# Cleanup:
# Temp dirs preserved only on failure.
set -e
tmp=$(mktemp -d /tmp/play-XXXXXXXX)
echo "Using temp dir: $tmp"
cleanup() {
if [ $? -ne 0 ]; then
echo
echo "Test failed. Temp project preserved at:"
echo " $tmp"
else
rm -rf "$tmp"
fi
}
trap cleanup EXIT
cargo new --lib "$tmp/counter" >/dev/null
cd "$tmp/counter"
cat > Cargo.toml <<EOF
[package]
name = "counter"
version = "0.1.0"
edition = "2021"
[dependencies]
madhouse = { git = "https://github.com/stacks-network/madhouse-rs.git", rev = "4e993262740c80682dae112876f9252a759538a5" }
proptest = { git = "https://github.com/proptest-rs/proptest.git", rev = "c9bdf18c232665b2b740c667c81866b598d06dc7" }
EOF
cat > src/lib.rs <<'EOF'
use madhouse::*;
use proptest::prelude::*;
use std::sync::Arc;
#[derive(Debug, Clone, Default)]
struct CounterState {
value: u32,
}
impl State for CounterState {}
#[derive(Clone, Debug)]
struct CounterContext;
impl Default for CounterContext {
fn default() -> Self {
CounterContext
}
}
impl TestContext for CounterContext {}
#[derive(Clone, Debug)]
struct Increment {
amount: u32,
}
impl Command<CounterState, CounterContext> for Increment {
fn check(&self, _: &CounterState) -> bool {
true
}
fn apply(&self, state: &mut CounterState) {
state.value += self.amount;
assert!(
state.value <= 100,
"Counter value exceeded maximum allowed: {}",
state.value
);
}
fn label(&self) -> String {
format!("INCREMENT({})", self.amount)
}
fn build(_: Arc<CounterContext>)
-> impl Strategy<Value = CommandWrapper<CounterState, CounterContext>>
{
(1..=50u32).prop_map(|amount| {
CommandWrapper::new(Increment { amount })
})
}
}
#[derive(Clone, Debug)]
struct Decrement {
amount: u32,
}
impl Command<CounterState, CounterContext> for Decrement {
fn check(&self, state: &CounterState) -> bool {
state.value >= self.amount
}
fn apply(&self, state: &mut CounterState) {
state.value -= self.amount;
}
fn label(&self) -> String {
format!("DECREMENT({})", self.amount)
}
fn build(_: Arc<CounterContext>)
-> impl Strategy<Value = CommandWrapper<CounterState, CounterContext>>
{
(1..=10u32).prop_map(|amount| {
CommandWrapper::new(Decrement { amount })
})
}
}
#[test]
fn test_counter_increment() {
let ctx = Arc::new(CounterContext::default());
scenario![ctx, Increment, Decrement];
}
EOF
"$@" cargo test -- --nocapture
nikos@debian-linux:~/play$ ./play.sh
Using temp dir: /tmp/play-b88B4mna
Creating library `counter` package
running 1 test
=== New Test Run (deterministic mode) ===
Selected:
01. INCREMENT(38)
02. DECREMENT(6)
Executed:
01. INCREMENT(38) (42.00ns)
02. DECREMENT(6) (42.00ns)
test test_counter_increment ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Doc-tests counter
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
nikos@debian-linux:~/play$ MADHOUSE=1 ./play.sh
Using temp dir: /tmp/play-K6rke7L9
running 1 test
=== New Test Run (MADHOUSE mode) ===
Selected:
01. DECREMENT(6)
02. DECREMENT(3)
03. DECREMENT(1)
04. DECREMENT(4)
05. DECREMENT(8)
06. INCREMENT(15)
Executed:
01. INCREMENT(15) (84.00ns)
test test_counter_increment ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Doc-tests counter
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
nikos@debian-linux:~/play$ MADHOUSE=1 ./play.sh
Using temp dir: /tmp/play-CGtyEoye
Creating library `counter` package
running 1 test
=== New Test Run (MADHOUSE mode) ===
Selected:
01. INCREMENT(11)
02. DECREMENT(5)
03. DECREMENT(5)
04. DECREMENT(9)
05. DECREMENT(6)
06. INCREMENT(32)
07. DECREMENT(3)
08. DECREMENT(6)
09. INCREMENT(28)
10. DECREMENT(6)
11. DECREMENT(2)
Executed:
01. INCREMENT(11) (83.00ns)
02. DECREMENT(5) (42.00ns)
03. DECREMENT(5) (0.00ns)
04. INCREMENT(32) (42.00ns)
05. DECREMENT(3) (0.00ns)
06. DECREMENT(6) (42.00ns)
07. INCREMENT(28) (0.00ns)
08. DECREMENT(6) (42.00ns)
09. DECREMENT(2) (0.00ns)
test test_counter_increment ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Doc-tests counter
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
nikos@debian-linux:~/play$ MADHOUSE=1 ./play.sh
Using temp dir: /tmp/play-rzzoSh2k
running 1 test
=== New Test Run (MADHOUSE mode) ===
Selected:
01. DECREMENT(7)
02. INCREMENT(37)
Executed:
01. INCREMENT(37) (42.00ns)
test test_counter_increment ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Doc-tests counter
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
nikos@debian-linux:~/play$ MADHOUSE=1 ./play.sh
Using temp dir: /tmp/play-I3QUKSS3
running 1 test
=== New Test Run (MADHOUSE mode) ===
thread 'test_counter_increment' panicked at src/lib.rs:32:9:
Counter value exceeded maximum allowed: 108
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
proptest: Saving this and future failures in /tmp/play-I3QUKSS3/counter/proptest-regressions/lib.txt
proptest: If this test was run on a CI system, you may wish to add the following line to your copy of the file. (You may need to create it.)
cc 665578fc6cc5394c2021bf132da80bee0fa7191fe27581039fb7dd8b9362353d
thread 'test_counter_increment' panicked at src/lib.rs:81:5:
Test failed: Counter value exceeded maximum allowed: 108.
minimal failing input: commands = [
DECREMENT(8),
INCREMENT(38),
INCREMENT(25),
DECREMENT(8),
INCREMENT(43),
INCREMENT(10),
DECREMENT(5),
INCREMENT(36),
]
successes: 0
local rejects: 0
global rejects: 0
test test_counter_increment ... FAILED
failures:
failures:
test_counter_increment
test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
error: test failed, to rerun pass `--lib`
Test failed. Temp project preserved at:
/tmp/play-I3QUKSS3
nikos@debian-linux:~/play$ cd /tmp/play-I3QUKSS3/
nikos@debian-linux:/tmp/play-I3QUKSS3$ PROPTEST_MAX_SHRINK_ITERS=50 cargo test
error: could not find `Cargo.toml` in `/tmp/play-I3QUKSS3` or any parent directory
nikos@debian-linux:/tmp/play-I3QUKSS3$ ls
counter
nikos@debian-linux:/tmp/play-I3QUKSS3$ cd counter/
nikos@debian-linux:/tmp/play-I3QUKSS3/counter$ MADHOUSE=1 PROPTEST_MAX_SHRINK_ITERS=50 cargo test
Finished `test` profile [unoptimized + debuginfo] target(s) in 0.02s
Running unittests src/lib.rs (target/debug/deps/counter-ff4bc7cc425d0391)
running 1 test
test test_counter_increment ... FAILED
failures:
---- test_counter_increment stdout ----
=== New Test Run (MADHOUSE mode) ===
thread 'test_counter_increment' panicked at src/lib.rs:32:9:
Counter value exceeded maximum allowed: 108
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
=== New Test Run (MADHOUSE mode) ===
thread 'test_counter_increment' panicked at src/lib.rs:32:9:
Counter value exceeded maximum allowed: 108
=== New Test Run (MADHOUSE mode) ===
thread 'test_counter_increment' panicked at src/lib.rs:32:9:
Counter value exceeded maximum allowed: 101
=== New Test Run (MADHOUSE mode) ===
Selected:
01. DECREMENT(8)
02. INCREMENT(43)
03. INCREMENT(10)
04. DECREMENT(5)
05. INCREMENT(36)
Executed:
01. INCREMENT(43) (42.00ns)
02. INCREMENT(10) (42.00ns)
03. DECREMENT(5) (0.00ns)
04. INCREMENT(36) (42.00ns)
=== New Test Run (MADHOUSE mode) ===
thread 'test_counter_increment' panicked at src/lib.rs:32:9:
Counter value exceeded maximum allowed: 101
=== New Test Run (MADHOUSE mode) ===
thread 'test_counter_increment' panicked at src/lib.rs:32:9:
Counter value exceeded maximum allowed: 109
=== New Test Run (MADHOUSE mode) ===
Selected:
01. INCREMENT(25)
02. INCREMENT(10)
03. DECREMENT(5)
04. INCREMENT(36)
Executed:
01. INCREMENT(25) (41.00ns)
02. INCREMENT(10) (42.00ns)
03. DECREMENT(5) (42.00ns)
04. INCREMENT(36) (41.00ns)
=== New Test Run (MADHOUSE mode) ===
thread 'test_counter_increment' panicked at src/lib.rs:32:9:
Counter value exceeded maximum allowed: 109
=== New Test Run (MADHOUSE mode) ===
Selected:
01. INCREMENT(25)
02. INCREMENT(43)
03. DECREMENT(5)
04. INCREMENT(36)
Executed:
01. INCREMENT(25) (41.00ns)
02. INCREMENT(43) (42.00ns)
03. DECREMENT(5) (42.00ns)
04. INCREMENT(36) (41.00ns)
=== New Test Run (MADHOUSE mode) ===
thread 'test_counter_increment' panicked at src/lib.rs:32:9:
Counter value exceeded maximum allowed: 109
=== New Test Run (MADHOUSE mode) ===
thread 'test_counter_increment' panicked at src/lib.rs:32:9:
Counter value exceeded maximum allowed: 114
=== New Test Run (MADHOUSE mode) ===
Selected:
01. INCREMENT(25)
02. INCREMENT(43)
03. INCREMENT(10)
Executed:
01. INCREMENT(25) (41.00ns)
02. INCREMENT(43) (42.00ns)
03. INCREMENT(10) (42.00ns)
=== New Test Run (MADHOUSE mode) ===
thread 'test_counter_increment' panicked at src/lib.rs:32:9:
Counter value exceeded maximum allowed: 114
=== New Test Run (MADHOUSE mode) ===
thread 'test_counter_increment' panicked at src/lib.rs:32:9:
Counter value exceeded maximum allowed: 102
=== New Test Run (MADHOUSE mode) ===
Selected:
01. INCREMENT(7)
02. INCREMENT(43)
03. INCREMENT(10)
04. INCREMENT(36)
Executed:
01. INCREMENT(7) (0.00ns)
02. INCREMENT(43) (0.00ns)
03. INCREMENT(10) (0.00ns)
04. INCREMENT(36) (0.00ns)
=== New Test Run (MADHOUSE mode) ===
Selected:
01. INCREMENT(10)
02. INCREMENT(43)
03. INCREMENT(10)
04. INCREMENT(36)
Executed:
01. INCREMENT(10) (42.00ns)
02. INCREMENT(43) (42.00ns)
03. INCREMENT(10) (41.00ns)
04. INCREMENT(36) (42.00ns)
=== New Test Run (MADHOUSE mode) ===
thread 'test_counter_increment' panicked at src/lib.rs:32:9:
Counter value exceeded maximum allowed: 101
=== New Test Run (MADHOUSE mode) ===
Selected:
01. INCREMENT(11)
02. INCREMENT(43)
03. INCREMENT(10)
04. INCREMENT(36)
Executed:
01. INCREMENT(11) (41.00ns)
02. INCREMENT(43) (0.00ns)
03. INCREMENT(10) (42.00ns)
04. INCREMENT(36) (41.00ns)
=== New Test Run (MADHOUSE mode) ===
thread 'test_counter_increment' panicked at src/lib.rs:32:9:
Counter value exceeded maximum allowed: 101
=== New Test Run (MADHOUSE mode) ===
Selected:
01. INCREMENT(12)
02. INCREMENT(22)
03. INCREMENT(10)
04. INCREMENT(36)
Executed:
01. INCREMENT(12) (42.00ns)
02. INCREMENT(22) (0.00ns)
03. INCREMENT(10) (42.00ns)
04. INCREMENT(36) (42.00ns)
=== New Test Run (MADHOUSE mode) ===
Selected:
01. INCREMENT(12)
02. INCREMENT(33)
03. INCREMENT(10)
04. INCREMENT(36)
Executed:
01. INCREMENT(12) (0.00ns)
02. INCREMENT(33) (0.00ns)
03. INCREMENT(10) (42.00ns)
04. INCREMENT(36) (0.00ns)
=== New Test Run (MADHOUSE mode) ===
Selected:
01. INCREMENT(12)
02. INCREMENT(38)
03. INCREMENT(10)
04. INCREMENT(36)
Executed:
01. INCREMENT(12) (0.00ns)
02. INCREMENT(38) (0.00ns)
03. INCREMENT(10) (0.00ns)
04. INCREMENT(36) (42.00ns)
=== New Test Run (MADHOUSE mode) ===
Selected:
01. INCREMENT(12)
02. INCREMENT(41)
03. INCREMENT(10)
04. INCREMENT(36)
Executed:
01. INCREMENT(12) (0.00ns)
02. INCREMENT(41) (42.00ns)
03. INCREMENT(10) (0.00ns)
04. INCREMENT(36) (42.00ns)
=== New Test Run (MADHOUSE mode) ===
Selected:
01. INCREMENT(12)
02. INCREMENT(42)
03. INCREMENT(10)
04. INCREMENT(36)
Executed:
01. INCREMENT(12) (0.00ns)
02. INCREMENT(42) (0.00ns)
03. INCREMENT(10) (42.00ns)
04. INCREMENT(36) (42.00ns)
=== New Test Run (MADHOUSE mode) ===
thread 'test_counter_increment' panicked at src/lib.rs:32:9:
Counter value exceeded maximum allowed: 101
=== New Test Run (MADHOUSE mode) ===
Selected:
01. INCREMENT(12)
02. INCREMENT(43)
03. INCREMENT(5)
04. INCREMENT(36)
Executed:
01. INCREMENT(12) (0.00ns)
02. INCREMENT(43) (0.00ns)
03. INCREMENT(5) (0.00ns)
04. INCREMENT(36) (42.00ns)
=== New Test Run (MADHOUSE mode) ===
Selected:
01. INCREMENT(12)
02. INCREMENT(43)
03. INCREMENT(8)
04. INCREMENT(36)
Executed:
01. INCREMENT(12) (0.00ns)
02. INCREMENT(43) (0.00ns)
03. INCREMENT(8) (41.00ns)
04. INCREMENT(36) (42.00ns)
=== New Test Run (MADHOUSE mode) ===
Selected:
01. INCREMENT(12)
02. INCREMENT(43)
03. INCREMENT(9)
04. INCREMENT(36)
Executed:
01. INCREMENT(12) (42.00ns)
02. INCREMENT(43) (41.00ns)
03. INCREMENT(9) (0.00ns)
04. INCREMENT(36) (0.00ns)
=== New Test Run (MADHOUSE mode) ===
thread 'test_counter_increment' panicked at src/lib.rs:32:9:
Counter value exceeded maximum allowed: 101
=== New Test Run (MADHOUSE mode) ===
Selected:
01. INCREMENT(12)
02. INCREMENT(43)
03. INCREMENT(10)
04. INCREMENT(18)
Executed:
01. INCREMENT(12) (41.00ns)
02. INCREMENT(43) (42.00ns)
03. INCREMENT(10) (42.00ns)
04. INCREMENT(18) (0.00ns)
=== New Test Run (MADHOUSE mode) ===
Selected:
01. INCREMENT(12)
02. INCREMENT(43)
03. INCREMENT(10)
04. INCREMENT(27)
Executed:
01. INCREMENT(12) (41.00ns)
02. INCREMENT(43) (0.00ns)
03. INCREMENT(10) (0.00ns)
04. INCREMENT(27) (0.00ns)
=== New Test Run (MADHOUSE mode) ===
Selected:
01. INCREMENT(12)
02. INCREMENT(43)
03. INCREMENT(10)
04. INCREMENT(32)
Executed:
01. INCREMENT(12) (0.00ns)
02. INCREMENT(43) (0.00ns)
03. INCREMENT(10) (0.00ns)
04. INCREMENT(32) (0.00ns)
=== New Test Run (MADHOUSE mode) ===
Selected:
01. INCREMENT(12)
02. INCREMENT(43)
03. INCREMENT(10)
04. INCREMENT(34)
Executed:
01. INCREMENT(12) (42.00ns)
02. INCREMENT(43) (41.00ns)
03. INCREMENT(10) (42.00ns)
04. INCREMENT(34) (42.00ns)
=== New Test Run (MADHOUSE mode) ===
Selected:
01. INCREMENT(12)
02. INCREMENT(43)
03. INCREMENT(10)
04. INCREMENT(35)
Executed:
01. INCREMENT(12) (42.00ns)
02. INCREMENT(43) (0.00ns)
03. INCREMENT(10) (0.00ns)
04. INCREMENT(35) (0.00ns)
=== New Test Run (MADHOUSE mode) ===
thread 'test_counter_increment' panicked at src/lib.rs:32:9:
Counter value exceeded maximum allowed: 101
thread 'test_counter_increment' panicked at src/lib.rs:81:5:
Test failed: Counter value exceeded maximum allowed: 101.
minimal failing input: commands = [
INCREMENT(12),
INCREMENT(43),
INCREMENT(10),
INCREMENT(36),
]
successes: 0
local rejects: 0
global rejects: 0
failures:
test_counter_increment
test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
error: test failed, to rerun pass `--lib`
nikos@debian-linux:/tmp/play-I3QUKSS3/counter$
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment