feat: init the repo
This commit is contained in:
@@ -0,0 +1,32 @@
|
||||
[package]
|
||||
name = "ferro_ta_fuzz"
|
||||
version = "0.0.1"
|
||||
edition = "2021"
|
||||
publish = false
|
||||
|
||||
# Exclude from the root workspace so cargo doesn't reject it as an unlisted member
|
||||
[workspace]
|
||||
|
||||
[package.metadata]
|
||||
cargo-fuzz = true
|
||||
|
||||
[dependencies]
|
||||
libfuzzer-sys = "0.4"
|
||||
ferro_ta_core = { path = "../crates/ferro_ta_core" }
|
||||
|
||||
[[bin]]
|
||||
name = "fuzz_sma"
|
||||
path = "fuzz_targets/fuzz_sma.rs"
|
||||
test = false
|
||||
doc = false
|
||||
bench = false
|
||||
|
||||
[[bin]]
|
||||
name = "fuzz_rsi"
|
||||
path = "fuzz_targets/fuzz_rsi.rs"
|
||||
test = false
|
||||
doc = false
|
||||
bench = false
|
||||
|
||||
[profile.release]
|
||||
debug = 1
|
||||
@@ -0,0 +1,52 @@
|
||||
/*!
|
||||
Fuzz target for `ferro_ta_core::momentum::rsi`.
|
||||
|
||||
Generates arbitrary f64 slices (via raw bytes) and arbitrary timeperiods,
|
||||
verifying that RSI never panics and that all finite output values lie in
|
||||
the range [0, 100].
|
||||
*/
|
||||
|
||||
#![no_main]
|
||||
|
||||
use libfuzzer_sys::fuzz_target;
|
||||
use ferro_ta_core::momentum;
|
||||
|
||||
fuzz_target!(|data: &[u8]| {
|
||||
// Need at least 1 byte for timeperiod + 8 bytes for one f64
|
||||
if data.len() < 2 {
|
||||
return;
|
||||
}
|
||||
|
||||
// Extract timeperiod from first byte (1-64)
|
||||
let timeperiod = ((data[0] as usize) % 64) + 1;
|
||||
|
||||
// Interpret remaining bytes as f64 values
|
||||
let float_bytes = &data[1..];
|
||||
let n_floats = float_bytes.len() / 8;
|
||||
if n_floats == 0 {
|
||||
return;
|
||||
}
|
||||
|
||||
let close: Vec<f64> = (0..n_floats)
|
||||
.map(|i| {
|
||||
let chunk: [u8; 8] = float_bytes[i * 8..(i + 1) * 8].try_into().unwrap();
|
||||
f64::from_le_bytes(chunk)
|
||||
})
|
||||
.collect();
|
||||
|
||||
// Must not panic
|
||||
let result = momentum::rsi(&close, timeperiod);
|
||||
|
||||
// Result length must match input
|
||||
assert_eq!(result.len(), close.len(), "RSI output length mismatch");
|
||||
|
||||
// All finite output values must be in [0, 100]
|
||||
for (i, &v) in result.iter().enumerate() {
|
||||
if v.is_finite() {
|
||||
assert!(
|
||||
v >= 0.0 && v <= 100.0,
|
||||
"RSI result[{i}] = {v} is out of [0, 100]"
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,51 @@
|
||||
/*!
|
||||
Fuzz target for `ferro_ta_core::overlap::sma`.
|
||||
|
||||
The fuzzer generates arbitrary byte sequences and interprets them as
|
||||
`f64` values plus a `timeperiod`. The invariant under test is that the
|
||||
function **never panics** for any input — it may return `NaN`, `Inf`, or
|
||||
an all-NaN slice, but it must not crash.
|
||||
*/
|
||||
|
||||
#![no_main]
|
||||
|
||||
use libfuzzer_sys::fuzz_target;
|
||||
use ferro_ta_core::overlap;
|
||||
|
||||
fuzz_target!(|data: &[u8]| {
|
||||
// Need at least 1 byte for timeperiod + 8 bytes for one f64
|
||||
if data.len() < 2 {
|
||||
return;
|
||||
}
|
||||
|
||||
// Extract timeperiod from first byte (1-64 to keep runs fast)
|
||||
let timeperiod = ((data[0] as usize) % 64) + 1;
|
||||
|
||||
// Interpret remaining bytes as f64 values (skip incomplete trailing bytes)
|
||||
let float_bytes = &data[1..];
|
||||
let n_floats = float_bytes.len() / 8;
|
||||
if n_floats == 0 {
|
||||
return;
|
||||
}
|
||||
|
||||
let close: Vec<f64> = (0..n_floats)
|
||||
.map(|i| {
|
||||
let chunk: [u8; 8] = float_bytes[i * 8..(i + 1) * 8].try_into().unwrap();
|
||||
f64::from_le_bytes(chunk)
|
||||
})
|
||||
.collect();
|
||||
|
||||
// Must not panic for any input
|
||||
let result = overlap::sma(&close, timeperiod);
|
||||
|
||||
// Result length must match input length
|
||||
assert_eq!(result.len(), close.len(), "SMA output length mismatch");
|
||||
|
||||
// The first (timeperiod - 1) values must be NaN
|
||||
for i in 0..(timeperiod.min(close.len()).saturating_sub(1)) {
|
||||
assert!(
|
||||
result[i].is_nan(),
|
||||
"SMA result[{i}] should be NaN (warm-up period)"
|
||||
);
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user