use std::rc::Rc;
use fuzzcheck::{
mutators::grammar::{
alternation, concatenation, grammar_based_ast_mutator, literal, recurse, recursive, regex,
repetition, Grammar, AST,
},
Mutator,
};
pub fn json_grammar_mutator() -> impl Mutator<(String, AST)> {
let grammar = recursive(|json| {
alternation([
regex("null"),
alternation([regex("true"), regex("false")]),
number(),
concatenation([quote(), valid_possibly_empty_string(), quote()]),
concatenation([
literal('['),
repetition(concatenation([recurse(json), literal(',')]), 0..=usize::MAX),
recurse(json),
literal(']'),
]),
concatenation([
literal('{'),
repetition(
concatenation([
quote(),
valid_possibly_empty_string(),
quote(),
literal(':'),
recurse(json),
literal(','),
]),
0..=usize::MAX,
),
concatenation([
quote(),
valid_possibly_empty_string(),
quote(),
literal(':'),
recurse(json),
]),
literal('}'),
]),
])
});
grammar_based_ast_mutator(grammar).with_string()
}
fn quote() -> Rc<Grammar> {
literal('"')
}
fn number() -> Rc<Grammar> {
concatenation([digits(), fraction(), exponent()])
}
fn digits() -> Rc<Grammar> {
concatenation([regex("[1-9]"), repetition(digit(), 0..=32)])
}
fn digit() -> Rc<Grammar> {
regex("[0-9]")
}
fn fraction() -> Rc<Grammar> {
alternation([
blank(),
concatenation([literal('.'), digits()]),
])
}
fn exponent() -> Rc<Grammar> {
alternation([
blank(),
concatenation([
literal('E'),
sign(),
regex("[1-9]"),
repetition(regex("[0-9]"), 0..=1),
]),
concatenation([
literal('e'),
sign(),
regex("[1-9]"),
repetition(regex("[0-9]"), 0..=1),
]),
])
}
fn sign() -> Rc<Grammar> {
alternation([blank(), literal('+'), literal('-')])
}
fn valid_possibly_empty_string() -> Rc<Grammar> {
regex("[a-zA-Z0-9_]*")
}
fn blank() -> Rc<Grammar> {
repetition(literal(' '), 0..=0)
}
#[cfg(test)]
#[test]
fn test_mutator() {
use std::str::FromStr;
use fuzzcheck::fuzz_test;
use serde_json::Value;
let result = fuzz_test(|(string, _): &(String, AST)| {
Value::from_str(string).unwrap();
})
.mutator(json_grammar_mutator())
.serde_serializer()
.default_sensor_and_pool()
.arguments_from_cargo_fuzzcheck()
.launch();
assert!(!result.found_test_failure)
}