#![allow(unused_assignments)]
use facet::Facet;
use facet_reflect::Partial;
use proptest::prelude::*;
use std::collections::HashMap;
#[derive(Facet, Debug, Clone, PartialEq)]
struct FuzzTarget {
name: String,
count: u32,
nested: NestedStruct,
items: Vec<String>,
mapping: HashMap<String, u32>,
maybe: Option<String>,
status: Status,
}
#[derive(Facet, Debug, Clone, PartialEq)]
struct NestedStruct {
x: i32,
y: i32,
label: String,
}
#[derive(Facet, Debug, Clone, PartialEq)]
#[repr(u8)]
#[allow(dead_code)] enum Status {
Active,
Inactive(String),
Pending(u32),
}
#[derive(Debug, Clone)]
enum PartialOp {
BeginField(FieldName),
BeginNthField(u8),
SetFieldU32(FieldName, u32),
SetFieldString(FieldName, String),
SetU32(u32),
SetString(String),
SetI32(i32),
End,
BeginList,
BeginListItem,
Push(String),
BeginMap,
BeginKey,
BeginValue,
BeginSome,
BeginInner,
SelectVariant(VariantName),
BeginDeferred,
FinishDeferred,
Build,
}
#[derive(Debug, Clone, Copy)]
enum FieldName {
Name,
Count,
Nested,
Items,
Mapping,
Maybe,
Status,
X,
Y,
Label,
Bogus, }
impl FieldName {
const fn as_str(&self) -> &'static str {
match self {
FieldName::Name => "name",
FieldName::Count => "count",
FieldName::Nested => "nested",
FieldName::Items => "items",
FieldName::Mapping => "mapping",
FieldName::Maybe => "maybe",
FieldName::Status => "status",
FieldName::X => "x",
FieldName::Y => "y",
FieldName::Label => "label",
FieldName::Bogus => "this_field_does_not_exist",
}
}
}
#[derive(Debug, Clone, Copy)]
enum VariantName {
Active,
Inactive,
Pending,
Bogus,
}
impl VariantName {
const fn as_str(&self) -> &'static str {
match self {
VariantName::Active => "Active",
VariantName::Inactive => "Inactive",
VariantName::Pending => "Pending",
VariantName::Bogus => "NonExistentVariant",
}
}
}
fn field_name_strategy() -> impl Strategy<Value = FieldName> {
prop_oneof![
Just(FieldName::Name),
Just(FieldName::Count),
Just(FieldName::Nested),
Just(FieldName::Items),
Just(FieldName::Mapping),
Just(FieldName::Maybe),
Just(FieldName::Status),
Just(FieldName::X),
Just(FieldName::Y),
Just(FieldName::Label),
Just(FieldName::Bogus),
]
}
fn variant_name_strategy() -> impl Strategy<Value = VariantName> {
prop_oneof![
Just(VariantName::Active),
Just(VariantName::Inactive),
Just(VariantName::Pending),
Just(VariantName::Bogus),
]
}
fn small_string_strategy() -> impl Strategy<Value = String> {
prop_oneof![
Just(String::new()),
Just(String::from("a")),
Just(String::from("test")),
Just(String::from("hello world")),
"[a-z]{0,10}".prop_map(String::from),
]
}
fn partial_op_strategy() -> impl Strategy<Value = PartialOp> {
prop_oneof![
3 => field_name_strategy().prop_map(PartialOp::BeginField),
2 => (0u8..10).prop_map(PartialOp::BeginNthField),
2 => (field_name_strategy(), any::<u32>())
.prop_map(|(f, v)| PartialOp::SetFieldU32(f, v)),
2 => (field_name_strategy(), small_string_strategy())
.prop_map(|(f, v)| PartialOp::SetFieldString(f, v)),
2 => any::<u32>().prop_map(PartialOp::SetU32),
2 => small_string_strategy().prop_map(PartialOp::SetString),
2 => any::<i32>().prop_map(PartialOp::SetI32),
5 => Just(PartialOp::End),
2 => Just(PartialOp::BeginList),
2 => Just(PartialOp::BeginListItem),
2 => small_string_strategy().prop_map(PartialOp::Push),
1 => Just(PartialOp::BeginMap),
1 => Just(PartialOp::BeginKey),
1 => Just(PartialOp::BeginValue),
1 => Just(PartialOp::BeginSome),
1 => Just(PartialOp::BeginInner),
2 => variant_name_strategy().prop_map(PartialOp::SelectVariant),
1 => Just(PartialOp::BeginDeferred),
1 => Just(PartialOp::FinishDeferred),
1 => Just(PartialOp::Build),
]
}
fn op_sequence_strategy() -> impl Strategy<Value = Vec<PartialOp>> {
prop::collection::vec(partial_op_strategy(), 1..50)
}
fn apply_ops(ops: &[PartialOp]) -> Result<(), String> {
let mut partial: Partial<'_> =
Partial::alloc::<FuzzTarget>().map_err(|e| format!("alloc failed: {e}"))?;
for (i, op) in ops.iter().enumerate() {
match apply_single_op(partial, op) {
Ok(p) => partial = p,
Err(e) => {
return Err(format!("op {i} ({op:?}) failed: {e}"));
}
}
}
Ok(())
}
fn apply_single_op<'a>(partial: Partial<'a>, op: &'a PartialOp) -> Result<Partial<'a>, String> {
let partial = match op {
PartialOp::BeginField(field) => partial
.begin_field(field.as_str())
.map_err(|e| e.to_string())?,
PartialOp::BeginNthField(idx) => partial
.begin_nth_field(*idx as usize)
.map_err(|e| e.to_string())?,
PartialOp::SetFieldU32(field, value) => partial
.set_field(field.as_str(), *value)
.map_err(|e| e.to_string())?,
PartialOp::SetFieldString(field, value) => partial
.set_field(field.as_str(), value.clone())
.map_err(|e| e.to_string())?,
PartialOp::SetU32(value) => partial.set(*value).map_err(|e| e.to_string())?,
PartialOp::SetString(value) => partial.set(value.clone()).map_err(|e| e.to_string())?,
PartialOp::SetI32(value) => partial.set(*value).map_err(|e| e.to_string())?,
PartialOp::End => partial.end().map_err(|e| e.to_string())?,
PartialOp::BeginList => partial.init_list().map_err(|e| e.to_string())?,
PartialOp::BeginListItem => partial.begin_list_item().map_err(|e| e.to_string())?,
PartialOp::Push(value) => partial.push(value.clone()).map_err(|e| e.to_string())?,
PartialOp::BeginMap => partial.init_map().map_err(|e| e.to_string())?,
PartialOp::BeginKey => partial.begin_key().map_err(|e| e.to_string())?,
PartialOp::BeginValue => partial.begin_value().map_err(|e| e.to_string())?,
PartialOp::BeginSome => partial.begin_some().map_err(|e| e.to_string())?,
PartialOp::BeginInner => partial.begin_inner().map_err(|e| e.to_string())?,
PartialOp::SelectVariant(variant) => partial
.select_variant_named(variant.as_str())
.map_err(|e| e.to_string())?,
PartialOp::BeginDeferred => partial.begin_deferred().map_err(|e| e.to_string())?,
PartialOp::FinishDeferred => partial.finish_deferred().map_err(|e| e.to_string())?,
PartialOp::Build => {
partial.build().map_err(|e| e.to_string())?;
return Err("build_succeeded".to_string());
}
};
Ok(partial)
}
proptest! {
#![proptest_config(ProptestConfig {
cases: 1000,
failure_persistence: None,
..ProptestConfig::default()
})]
#[test]
#[cfg_attr(any(miri, not(feature = "fuzz-tests")), ignore)]
fn fuzz_partial_safety(ops in op_sequence_strategy()) {
let _ = apply_ops(&ops);
}
}
#[derive(Facet, Debug)]
struct SimpleStruct {
a: u32,
b: String,
}
#[derive(Facet, Debug)]
struct WithList {
items: Vec<u32>,
}
#[derive(Facet, Debug)]
struct WithMap {
data: HashMap<String, u32>,
}
proptest! {
#![proptest_config(ProptestConfig {
cases: 500,
failure_persistence: None,
..ProptestConfig::default()
})]
#[test]
#[cfg_attr(any(miri, not(feature = "fuzz-tests")), ignore)]
fn fuzz_simple_struct(
ops in prop::collection::vec(
prop_oneof![
Just("begin_a"),
Just("begin_b"),
Just("begin_bogus"),
Just("set_u32"),
Just("set_string"),
Just("end"),
Just("build"),
],
1..30
),
values_u32 in prop::collection::vec(any::<u32>(), 10),
values_str in prop::collection::vec("[a-z]{0,5}", 10),
) {
let mut partial: Partial<'_> = Partial::alloc::<SimpleStruct>().unwrap();
let mut u32_idx = 0;
let mut str_idx = 0;
for op in ops {
if op == "build" {
let _ = partial.build();
break;
}
let result = match op {
"begin_a" => partial.begin_field("a"),
"begin_b" => partial.begin_field("b"),
"begin_bogus" => partial.begin_field("bogus"),
"set_u32" => {
let v = values_u32[u32_idx % values_u32.len()];
u32_idx += 1;
partial.set(v)
}
"set_string" => {
let v = &values_str[str_idx % values_str.len()];
str_idx += 1;
partial.set(v.clone())
}
"end" => partial.end(),
_ => Ok(partial),
};
match result {
Ok(p) => partial = p,
Err(_) => break,
}
}
}
#[test]
#[cfg_attr(any(miri, not(feature = "fuzz-tests")), ignore)]
fn fuzz_list_ops(
ops in prop::collection::vec(
prop_oneof![
Just("begin_items"),
Just("begin_list"),
Just("begin_list_item"),
Just("push"),
Just("set"),
Just("end"),
Just("build"),
],
1..40
),
values in prop::collection::vec(any::<u32>(), 20),
) {
let mut partial: Partial<'_> = Partial::alloc::<WithList>().unwrap();
let mut idx = 0;
for op in ops {
if op == "build" {
let _ = partial.build();
break;
}
let result = match op {
"begin_items" => partial.begin_field("items"),
"begin_list" => partial.init_list(),
"begin_list_item" => partial.begin_list_item(),
"push" => {
let v = values[idx % values.len()];
idx += 1;
partial.push(v)
}
"set" => {
let v = values[idx % values.len()];
idx += 1;
partial.set(v)
}
"end" => partial.end(),
_ => Ok(partial),
};
match result {
Ok(p) => partial = p,
Err(_) => break,
}
}
}
#[test]
#[cfg_attr(any(miri, not(feature = "fuzz-tests")), ignore)]
fn fuzz_map_ops(
ops in prop::collection::vec(
prop_oneof![
Just("begin_data"),
Just("begin_map"),
Just("begin_key"),
Just("begin_value"),
Just("set_key"),
Just("set_value"),
Just("end"),
Just("build"),
],
1..40
),
keys in prop::collection::vec("[a-z]{1,5}", 10),
values in prop::collection::vec(any::<u32>(), 10),
) {
let mut partial: Partial<'_> = Partial::alloc::<WithMap>().unwrap();
let mut key_idx = 0;
let mut val_idx = 0;
for op in ops {
if op == "build" {
let _ = partial.build();
break;
}
let result = match op {
"begin_data" => partial.begin_field("data"),
"begin_map" => partial.init_map(),
"begin_key" => partial.begin_key(),
"begin_value" => partial.begin_value(),
"set_key" => {
let k = &keys[key_idx % keys.len()];
key_idx += 1;
partial.set(k.clone())
}
"set_value" => {
let v = values[val_idx % values.len()];
val_idx += 1;
partial.set(v)
}
"end" => partial.end(),
_ => Ok(partial),
};
match result {
Ok(p) => partial = p,
Err(_) => break,
}
}
}
}
#[::core::prelude::v1::test]
fn wip_fuzz_drop_after_partial_init() {
let partial: Partial<'_> = Partial::alloc::<FuzzTarget>().unwrap();
let partial = partial.set_field("name", String::from("test")).ok();
if let Some(partial) = partial {
let _ = partial.set_field("count", 42u32);
}
}
#[::core::prelude::v1::test]
fn wip_fuzz_drop_mid_nested() {
let partial: Partial<'_> = Partial::alloc::<FuzzTarget>().unwrap();
let partial = partial.begin_field("nested").ok();
if let Some(partial) = partial {
let _ = partial.set_field("x", 10i32);
}
}
#[::core::prelude::v1::test]
fn wip_fuzz_drop_mid_list() {
let partial: Partial<'_> = Partial::alloc::<FuzzTarget>().unwrap();
if let Ok(partial) = partial.begin_field("items")
&& let Ok(partial) = partial.init_list()
&& let Ok(partial) = partial.push(String::from("item1"))
{
let _ = partial.push(String::from("item2"));
}
}
#[::core::prelude::v1::test]
fn wip_fuzz_drop_mid_map() {
let partial: Partial<'_> = Partial::alloc::<FuzzTarget>().unwrap();
if let Ok(partial) = partial.begin_field("mapping")
&& let Ok(partial) = partial.init_map()
&& let Ok(partial) = partial.begin_key()
&& let Ok(partial) = partial.set(String::from("key1"))
&& let Ok(partial) = partial.end()
{
let _ = partial.begin_value();
}
}
#[::core::prelude::v1::test]
fn wip_fuzz_invalid_ops_sequence() {
let partial: Partial<'_> = Partial::alloc::<FuzzTarget>().unwrap();
assert!(partial.end().is_err());
let partial: Partial<'_> = Partial::alloc::<FuzzTarget>().unwrap();
assert!(partial.init_list().is_err());
let partial: Partial<'_> = Partial::alloc::<FuzzTarget>().unwrap();
assert!(partial.set(42u32).is_err());
let partial: Partial<'_> = Partial::alloc::<FuzzTarget>().unwrap();
assert!(partial.begin_field("nonexistent").is_err());
let partial: Partial<'_> = Partial::alloc::<FuzzTarget>().unwrap();
assert!(partial.build().is_err());
}
#[::core::prelude::v1::test]
fn wip_fuzz_deferred_drop_without_finish() {
let partial: Partial<'_> = Partial::alloc::<FuzzTarget>().unwrap();
let mut partial = partial.begin_deferred().unwrap();
partial = partial
.set_field("name", String::from("test"))
.ok()
.unwrap();
partial = partial.begin_field("nested").ok().unwrap();
partial = partial.set_field("x", 1i32).ok().unwrap();
let _ = partial.end();
}
#[::core::prelude::v1::test]
fn wip_fuzz_deferred_interleaved_fields() {
let partial: Partial<'_> = Partial::alloc::<FuzzTarget>().unwrap();
let mut partial = partial.begin_deferred().unwrap();
partial = partial.begin_field("nested").ok().unwrap();
partial = partial.set_field("x", 1i32).ok().unwrap();
partial = partial.end().ok().unwrap();
partial = partial
.set_field("name", String::from("test"))
.ok()
.unwrap();
partial = partial.begin_field("nested").ok().unwrap();
partial = partial.set_field("y", 2i32).ok().unwrap();
let _ = partial.end();
}
#[::core::prelude::v1::test]
fn wip_fuzz_deferred_double_begin() {
let partial: Partial<'_> = Partial::alloc::<FuzzTarget>().unwrap();
let partial = partial.begin_deferred().unwrap();
assert!(partial.begin_deferred().is_err());
}
#[::core::prelude::v1::test]
fn wip_fuzz_deferred_finish_without_begin() {
let partial: Partial<'_> = Partial::alloc::<FuzzTarget>().unwrap();
let result = partial.finish_deferred();
assert!(result.is_err());
}
#[::core::prelude::v1::test]
fn wip_fuzz_begin_field_set_string_drop() {
let partial: Partial<'_> = Partial::alloc::<FuzzTarget>().unwrap();
if let Ok(partial) = partial.begin_field("name") {
let _ = partial.set(String::from("aaaaaaaaaaaa"));
}
}
#[::core::prelude::v1::test]
fn wip_fuzz_reg_test_2() {
let mut partial: Partial<'_> = Partial::alloc::<FuzzTarget>().unwrap();
partial = partial.begin_field("mapping").unwrap();
partial = partial.init_map().unwrap();
partial = partial.begin_key().unwrap();
partial = partial.set(String::from("aaaaaaaaaaaaaaaa")).unwrap();
}
#[::core::prelude::v1::test]
fn wip_fuzz_reg_test_5() {
let mut partial: Partial<'_> = Partial::alloc::<FuzzTarget>().unwrap();
partial = partial.begin_field("mapping").unwrap();
partial = partial.init_map().unwrap();
partial = partial.begin_key().unwrap();
partial = partial.set(String::from("mxwvhqpvvv")).unwrap();
let _ = partial.begin_inner();
}