Old Fuzzer, new Sampler
This commit is contained in:
parent
c0fabcd26a
commit
84a0abeb0f
|
@ -258,7 +258,7 @@ fn str_to_keyword(word: &str) -> Option<Token> {
|
||||||
"or" => Some(Token::Or),
|
"or" => Some(Token::Or),
|
||||||
"validator" => Some(Token::Validator),
|
"validator" => Some(Token::Validator),
|
||||||
"via" => Some(Token::Via),
|
"via" => Some(Token::Via),
|
||||||
"benchmark" => Some(Token::Benchmark),
|
"bench" => Some(Token::Benchmark),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,8 @@ pub const BOOL: &str = "Bool";
|
||||||
pub const BOOL_CONSTRUCTORS: &[&str] = &["False", "True"];
|
pub const BOOL_CONSTRUCTORS: &[&str] = &["False", "True"];
|
||||||
pub const BYTE_ARRAY: &str = "ByteArray";
|
pub const BYTE_ARRAY: &str = "ByteArray";
|
||||||
pub const DATA: &str = "Data";
|
pub const DATA: &str = "Data";
|
||||||
pub const GENERATOR: &str = "Generator";
|
pub const FUZZER: &str = "Fuzzer";
|
||||||
|
pub const SAMPLER: &str = "Sampler";
|
||||||
pub const G1_ELEMENT: &str = "G1Element";
|
pub const G1_ELEMENT: &str = "G1Element";
|
||||||
pub const G2_ELEMENT: &str = "G2Element";
|
pub const G2_ELEMENT: &str = "G2Element";
|
||||||
pub const INT: &str = "Int";
|
pub const INT: &str = "Int";
|
||||||
|
@ -179,7 +180,7 @@ impl Type {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn generator(c: Rc<Type>, a: Rc<Type>) -> Rc<Type> {
|
pub fn fuzzer(a: Rc<Type>) -> Rc<Type> {
|
||||||
let prng_annotation = Annotation::Constructor {
|
let prng_annotation = Annotation::Constructor {
|
||||||
location: Span::empty(),
|
location: Span::empty(),
|
||||||
module: None,
|
module: None,
|
||||||
|
@ -188,15 +189,15 @@ impl Type {
|
||||||
};
|
};
|
||||||
|
|
||||||
Rc::new(Type::Fn {
|
Rc::new(Type::Fn {
|
||||||
args: vec![c, Type::prng()],
|
args: vec![Type::prng()],
|
||||||
ret: Type::option(Type::tuple(vec![Type::prng(), a])),
|
ret: Type::option(Type::tuple(vec![Type::prng(), a])),
|
||||||
alias: Some(
|
alias: Some(
|
||||||
TypeAliasAnnotation {
|
TypeAliasAnnotation {
|
||||||
alias: GENERATOR.to_string(),
|
alias: FUZZER.to_string(),
|
||||||
parameters: vec!["c".to_string(), "a".to_string()],
|
parameters: vec!["a".to_string()],
|
||||||
annotation: Annotation::Fn {
|
annotation: Annotation::Fn {
|
||||||
location: Span::empty(),
|
location: Span::empty(),
|
||||||
arguments: vec![Annotation::data(Span::empty()), prng_annotation.clone()],
|
arguments: vec![prng_annotation.clone()],
|
||||||
ret: Annotation::Constructor {
|
ret: Annotation::Constructor {
|
||||||
location: Span::empty(),
|
location: Span::empty(),
|
||||||
module: None,
|
module: None,
|
||||||
|
@ -220,6 +221,53 @@ impl Type {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn sampler(a: Rc<Type>) -> Rc<Type> {
|
||||||
|
let prng_annotation = Annotation::Constructor {
|
||||||
|
location: Span::empty(),
|
||||||
|
module: None,
|
||||||
|
name: PRNG.to_string(),
|
||||||
|
arguments: vec![],
|
||||||
|
};
|
||||||
|
|
||||||
|
Rc::new(Type::Fn {
|
||||||
|
args: vec![Type::int()],
|
||||||
|
ret: Type::fuzzer(a),
|
||||||
|
alias: Some(
|
||||||
|
TypeAliasAnnotation {
|
||||||
|
alias: SAMPLER.to_string(),
|
||||||
|
parameters: vec!["a".to_string()],
|
||||||
|
annotation: Annotation::Fn {
|
||||||
|
location: Span::empty(),
|
||||||
|
arguments: vec![Annotation::int(Span::empty())],
|
||||||
|
ret: Annotation::Fn {
|
||||||
|
location: Span::empty(),
|
||||||
|
arguments: vec![prng_annotation.clone()],
|
||||||
|
ret: Annotation::Constructor {
|
||||||
|
location: Span::empty(),
|
||||||
|
module: None,
|
||||||
|
name: OPTION.to_string(),
|
||||||
|
arguments: vec![Annotation::Tuple {
|
||||||
|
location: Span::empty(),
|
||||||
|
elems: vec![
|
||||||
|
prng_annotation,
|
||||||
|
Annotation::Var {
|
||||||
|
location: Span::empty(),
|
||||||
|
name: "a".to_string(),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}],
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
pub fn map(k: Rc<Type>, v: Rc<Type>) -> Rc<Type> {
|
pub fn map(k: Rc<Type>, v: Rc<Type>) -> Rc<Type> {
|
||||||
Rc::new(Type::App {
|
Rc::new(Type::App {
|
||||||
public: true,
|
public: true,
|
||||||
|
|
|
@ -477,23 +477,38 @@ pub fn prelude(id_gen: &IdGenerator) -> TypeInfo {
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Generator
|
// Fuzzer
|
||||||
//
|
//
|
||||||
// pub type Generator<c, a> =
|
// pub type Fuzzer<a> =
|
||||||
// fn(Data, PRNG) -> Option<(PRNG, a)>
|
// fn(PRNG) -> Option<(PRNG, a)>
|
||||||
let generator_context = Type::generic_var(id_gen.next());
|
let fuzzer_generic = Type::generic_var(id_gen.next());
|
||||||
let generator_value = Type::generic_var(id_gen.next());
|
|
||||||
prelude.types.insert(
|
prelude.types.insert(
|
||||||
well_known::GENERATOR.to_string(),
|
well_known::FUZZER.to_string(),
|
||||||
TypeConstructor {
|
TypeConstructor {
|
||||||
location: Span::empty(),
|
location: Span::empty(),
|
||||||
parameters: vec![generator_context.clone(), generator_value.clone()],
|
parameters: vec![fuzzer_generic.clone()],
|
||||||
tipo: Type::generator(generator_context, generator_value),
|
tipo: Type::fuzzer(fuzzer_generic),
|
||||||
module: "".to_string(),
|
module: "".to_string(),
|
||||||
public: true,
|
public: true,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Sampler
|
||||||
|
//
|
||||||
|
// pub type Sampler<a> =
|
||||||
|
// fn(Int) -> Fuzzer<a>
|
||||||
|
let sampler_generic = Type::generic_var(id_gen.next());
|
||||||
|
prelude.types.insert(
|
||||||
|
well_known::SAMPLER.to_string(),
|
||||||
|
TypeConstructor {
|
||||||
|
location: Span::empty(),
|
||||||
|
parameters: vec![sampler_generic.clone()],
|
||||||
|
tipo: Type::sampler(sampler_generic),
|
||||||
|
module: "".to_string(),
|
||||||
|
public: true,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
prelude
|
prelude
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -135,7 +135,7 @@ mod tests {
|
||||||
fn def_benchmark() {
|
fn def_benchmark() {
|
||||||
assert_definition!(
|
assert_definition!(
|
||||||
r#"
|
r#"
|
||||||
benchmark foo(x via fuzz.any_int) {
|
bench foo(x via fuzz.any_int) {
|
||||||
True
|
True
|
||||||
}
|
}
|
||||||
"#
|
"#
|
||||||
|
@ -146,7 +146,7 @@ mod tests {
|
||||||
fn def_invalid_benchmark() {
|
fn def_invalid_benchmark() {
|
||||||
assert_definition!(
|
assert_definition!(
|
||||||
r#"
|
r#"
|
||||||
benchmark foo(x via f, y via g) {
|
bench foo(x via f, y via g) {
|
||||||
True
|
True
|
||||||
}
|
}
|
||||||
"#
|
"#
|
||||||
|
@ -157,7 +157,7 @@ mod tests {
|
||||||
fn def_benchmark_annotated_fuzzer() {
|
fn def_benchmark_annotated_fuzzer() {
|
||||||
assert_definition!(
|
assert_definition!(
|
||||||
r#"
|
r#"
|
||||||
benchmark foo(x: Int via foo()) {
|
bench foo(x: Int via foo()) {
|
||||||
True
|
True
|
||||||
}
|
}
|
||||||
"#
|
"#
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
---
|
---
|
||||||
source: crates/aiken-lang/src/parser/definition/benchmark.rs
|
source: crates/aiken-lang/src/parser/definition/benchmark.rs
|
||||||
assertion_line: 136
|
assertion_line: 136
|
||||||
description: "Code:\n\nbenchmark foo(x via fuzz.any_int) {\n True\n}\n"
|
description: "Code:\n\nbench foo(x via fuzz.any_int) {\n True\n}\n"
|
||||||
snapshot_kind: text
|
snapshot_kind: text
|
||||||
---
|
---
|
||||||
Benchmark(
|
Benchmark(
|
||||||
|
@ -13,35 +13,35 @@ Benchmark(
|
||||||
Named {
|
Named {
|
||||||
name: "x",
|
name: "x",
|
||||||
label: "x",
|
label: "x",
|
||||||
location: 14..15,
|
location: 10..11,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
location: 14..15,
|
location: 10..11,
|
||||||
annotation: None,
|
annotation: None,
|
||||||
doc: None,
|
doc: None,
|
||||||
is_validator_param: false,
|
is_validator_param: false,
|
||||||
},
|
},
|
||||||
via: FieldAccess {
|
via: FieldAccess {
|
||||||
location: 20..32,
|
location: 16..28,
|
||||||
label: "any_int",
|
label: "any_int",
|
||||||
container: Var {
|
container: Var {
|
||||||
location: 20..24,
|
location: 16..20,
|
||||||
name: "fuzz",
|
name: "fuzz",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
body: Var {
|
body: Var {
|
||||||
location: 40..44,
|
location: 36..40,
|
||||||
name: "True",
|
name: "True",
|
||||||
},
|
},
|
||||||
doc: None,
|
doc: None,
|
||||||
location: 0..33,
|
location: 0..29,
|
||||||
name: "foo",
|
name: "foo",
|
||||||
public: false,
|
public: false,
|
||||||
return_annotation: None,
|
return_annotation: None,
|
||||||
return_type: (),
|
return_type: (),
|
||||||
end_position: 45,
|
end_position: 41,
|
||||||
on_test_failure: FailImmediately,
|
on_test_failure: FailImmediately,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
---
|
---
|
||||||
source: crates/aiken-lang/src/parser/definition/benchmark.rs
|
source: crates/aiken-lang/src/parser/definition/benchmark.rs
|
||||||
assertion_line: 158
|
assertion_line: 158
|
||||||
description: "Code:\n\nbenchmark foo(x: Int via foo()) {\n True\n}\n"
|
description: "Code:\n\nbench foo(x: Int via foo()) {\n True\n}\n"
|
||||||
snapshot_kind: text
|
snapshot_kind: text
|
||||||
---
|
---
|
||||||
Benchmark(
|
Benchmark(
|
||||||
|
@ -13,13 +13,13 @@ Benchmark(
|
||||||
Named {
|
Named {
|
||||||
name: "x",
|
name: "x",
|
||||||
label: "x",
|
label: "x",
|
||||||
location: 14..15,
|
location: 10..11,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
location: 14..20,
|
location: 10..16,
|
||||||
annotation: Some(
|
annotation: Some(
|
||||||
Constructor {
|
Constructor {
|
||||||
location: 17..20,
|
location: 13..16,
|
||||||
module: None,
|
module: None,
|
||||||
name: "Int",
|
name: "Int",
|
||||||
arguments: [],
|
arguments: [],
|
||||||
|
@ -31,24 +31,24 @@ Benchmark(
|
||||||
via: Call {
|
via: Call {
|
||||||
arguments: [],
|
arguments: [],
|
||||||
fun: Var {
|
fun: Var {
|
||||||
location: 25..28,
|
location: 21..24,
|
||||||
name: "foo",
|
name: "foo",
|
||||||
},
|
},
|
||||||
location: 25..30,
|
location: 21..26,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
body: Var {
|
body: Var {
|
||||||
location: 38..42,
|
location: 34..38,
|
||||||
name: "True",
|
name: "True",
|
||||||
},
|
},
|
||||||
doc: None,
|
doc: None,
|
||||||
location: 0..31,
|
location: 0..27,
|
||||||
name: "foo",
|
name: "foo",
|
||||||
public: false,
|
public: false,
|
||||||
return_annotation: None,
|
return_annotation: None,
|
||||||
return_type: (),
|
return_type: (),
|
||||||
end_position: 43,
|
end_position: 39,
|
||||||
on_test_failure: FailImmediately,
|
on_test_failure: FailImmediately,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
---
|
---
|
||||||
source: crates/aiken-lang/src/parser/definition/benchmark.rs
|
source: crates/aiken-lang/src/parser/definition/benchmark.rs
|
||||||
assertion_line: 147
|
assertion_line: 147
|
||||||
description: "Code:\n\nbenchmark foo(x via f, y via g) {\n True\n}\n"
|
description: "Code:\n\nbench foo(x via f, y via g) {\n True\n}\n"
|
||||||
snapshot_kind: text
|
snapshot_kind: text
|
||||||
---
|
---
|
||||||
Benchmark(
|
Benchmark(
|
||||||
|
@ -13,16 +13,16 @@ Benchmark(
|
||||||
Named {
|
Named {
|
||||||
name: "x",
|
name: "x",
|
||||||
label: "x",
|
label: "x",
|
||||||
location: 14..15,
|
location: 10..11,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
location: 14..15,
|
location: 10..11,
|
||||||
annotation: None,
|
annotation: None,
|
||||||
doc: None,
|
doc: None,
|
||||||
is_validator_param: false,
|
is_validator_param: false,
|
||||||
},
|
},
|
||||||
via: Var {
|
via: Var {
|
||||||
location: 20..21,
|
location: 16..17,
|
||||||
name: "f",
|
name: "f",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -32,31 +32,31 @@ Benchmark(
|
||||||
Named {
|
Named {
|
||||||
name: "y",
|
name: "y",
|
||||||
label: "y",
|
label: "y",
|
||||||
location: 23..24,
|
location: 19..20,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
location: 23..24,
|
location: 19..20,
|
||||||
annotation: None,
|
annotation: None,
|
||||||
doc: None,
|
doc: None,
|
||||||
is_validator_param: false,
|
is_validator_param: false,
|
||||||
},
|
},
|
||||||
via: Var {
|
via: Var {
|
||||||
location: 29..30,
|
location: 25..26,
|
||||||
name: "g",
|
name: "g",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
body: Var {
|
body: Var {
|
||||||
location: 38..42,
|
location: 34..38,
|
||||||
name: "True",
|
name: "True",
|
||||||
},
|
},
|
||||||
doc: None,
|
doc: None,
|
||||||
location: 0..31,
|
location: 0..27,
|
||||||
name: "foo",
|
name: "foo",
|
||||||
public: false,
|
public: false,
|
||||||
return_annotation: None,
|
return_annotation: None,
|
||||||
return_type: (),
|
return_type: (),
|
||||||
end_position: 43,
|
end_position: 39,
|
||||||
on_test_failure: FailImmediately,
|
on_test_failure: FailImmediately,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
|
@ -243,7 +243,7 @@ pub fn lexer() -> impl Parser<char, Vec<(Token, Span)>, Error = ParseError> {
|
||||||
"when" => Token::When,
|
"when" => Token::When,
|
||||||
"validator" => Token::Validator,
|
"validator" => Token::Validator,
|
||||||
"via" => Token::Via,
|
"via" => Token::Via,
|
||||||
"benchmark" => Token::Benchmark,
|
"bench" => Token::Benchmark,
|
||||||
_ => {
|
_ => {
|
||||||
if s.chars().next().map_or(false, |c| c.is_uppercase()) {
|
if s.chars().next().map_or(false, |c| c.is_uppercase()) {
|
||||||
Token::UpName {
|
Token::UpName {
|
||||||
|
|
|
@ -183,7 +183,7 @@ impl fmt::Display for Token {
|
||||||
Token::Once => "once",
|
Token::Once => "once",
|
||||||
Token::Validator => "validator",
|
Token::Validator => "validator",
|
||||||
Token::Via => "via",
|
Token::Via => "via",
|
||||||
Token::Benchmark => "benchmark",
|
Token::Benchmark => "bench",
|
||||||
};
|
};
|
||||||
write!(f, "{s}")
|
write!(f, "{s}")
|
||||||
}
|
}
|
||||||
|
|
|
@ -332,14 +332,12 @@ impl PropertyTest {
|
||||||
) -> Result<Option<Counterexample<'a>>, FuzzerError> {
|
) -> Result<Option<Counterexample<'a>>, FuzzerError> {
|
||||||
let mut prng = initial_prng;
|
let mut prng = initial_prng;
|
||||||
let mut counterexample = None;
|
let mut counterexample = None;
|
||||||
let mut iteration = 0;
|
|
||||||
|
|
||||||
while *remaining > 0 && counterexample.is_none() {
|
while *remaining > 0 && counterexample.is_none() {
|
||||||
let (next_prng, cex) = self.run_once(prng, labels, plutus_version, iteration)?;
|
let (next_prng, cex) = self.run_once(prng, labels, plutus_version)?;
|
||||||
prng = next_prng;
|
prng = next_prng;
|
||||||
counterexample = cex;
|
counterexample = cex;
|
||||||
*remaining -= 1;
|
*remaining -= 1;
|
||||||
iteration += 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(counterexample)
|
Ok(counterexample)
|
||||||
|
@ -350,12 +348,11 @@ impl PropertyTest {
|
||||||
prng: Prng,
|
prng: Prng,
|
||||||
labels: &mut BTreeMap<String, usize>,
|
labels: &mut BTreeMap<String, usize>,
|
||||||
plutus_version: &'a PlutusVersion,
|
plutus_version: &'a PlutusVersion,
|
||||||
iteration: usize,
|
|
||||||
) -> Result<(Prng, Option<Counterexample<'a>>), FuzzerError> {
|
) -> Result<(Prng, Option<Counterexample<'a>>), FuzzerError> {
|
||||||
use OnTestFailure::*;
|
use OnTestFailure::*;
|
||||||
|
|
||||||
let (next_prng, value) = prng
|
let (next_prng, value) = prng
|
||||||
.sample(&self.fuzzer.program, iteration)?
|
.sample(&self.fuzzer.program)?
|
||||||
.expect("A seeded PRNG returned 'None' which indicates a fuzzer is ill-formed and implemented wrongly; please contact library's authors.");
|
.expect("A seeded PRNG returned 'None' which indicates a fuzzer is ill-formed and implemented wrongly; please contact library's authors.");
|
||||||
|
|
||||||
let mut result = self.eval(&value, plutus_version);
|
let mut result = self.eval(&value, plutus_version);
|
||||||
|
@ -386,8 +383,8 @@ impl PropertyTest {
|
||||||
value,
|
value,
|
||||||
choices: next_prng.choices(),
|
choices: next_prng.choices(),
|
||||||
cache: Cache::new(move |choices| {
|
cache: Cache::new(move |choices| {
|
||||||
match Prng::from_choices(choices, iteration)
|
match Prng::from_choices(choices)
|
||||||
.sample(&self.fuzzer.program, iteration)
|
.sample(&self.fuzzer.program)
|
||||||
{
|
{
|
||||||
Err(..) => Status::Invalid,
|
Err(..) => Status::Invalid,
|
||||||
Ok(None) => Status::Invalid,
|
Ok(None) => Status::Invalid,
|
||||||
|
@ -454,7 +451,7 @@ pub struct Benchmark {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub on_test_failure: OnTestFailure,
|
pub on_test_failure: OnTestFailure,
|
||||||
pub program: Program<Name>,
|
pub program: Program<Name>,
|
||||||
pub fuzzer: Fuzzer<Name>,
|
pub sampler: Fuzzer<Name>,
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl Send for Benchmark {}
|
unsafe impl Send for Benchmark {}
|
||||||
|
@ -467,11 +464,12 @@ impl Benchmark {
|
||||||
plutus_version: &PlutusVersion,
|
plutus_version: &PlutusVersion,
|
||||||
) -> Vec<BenchmarkResult> {
|
) -> Vec<BenchmarkResult> {
|
||||||
let mut results = Vec::with_capacity(n);
|
let mut results = Vec::with_capacity(n);
|
||||||
let mut remaining = n;
|
let mut iteration = 0;
|
||||||
let mut prng = Prng::from_seed(seed);
|
let mut prng = Prng::from_seed(seed);
|
||||||
|
|
||||||
while remaining > 0 {
|
while n > iteration {
|
||||||
match prng.sample(&self.fuzzer.program, n - remaining) {
|
let fuzzer = self.sampler.program.apply_data(Data::integer(num_bigint::BigInt::from(iteration as i64)));
|
||||||
|
match prng.sample(&fuzzer) {
|
||||||
Ok(Some((new_prng, value))) => {
|
Ok(Some((new_prng, value))) => {
|
||||||
prng = new_prng;
|
prng = new_prng;
|
||||||
let mut eval_result = self.eval(&value, plutus_version);
|
let mut eval_result = self.eval(&value, plutus_version);
|
||||||
|
@ -496,7 +494,7 @@ impl Benchmark {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
remaining -= 1;
|
iteration += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
results
|
results
|
||||||
|
@ -534,12 +532,10 @@ pub enum Prng {
|
||||||
Seeded {
|
Seeded {
|
||||||
choices: Vec<u8>,
|
choices: Vec<u8>,
|
||||||
uplc: PlutusData,
|
uplc: PlutusData,
|
||||||
iteration: usize,
|
|
||||||
},
|
},
|
||||||
Replayed {
|
Replayed {
|
||||||
choices: Vec<u8>,
|
choices: Vec<u8>,
|
||||||
uplc: PlutusData,
|
uplc: PlutusData,
|
||||||
iteration: usize,
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -588,12 +584,11 @@ impl Prng {
|
||||||
Data::bytestring(vec![]), // Random choices
|
Data::bytestring(vec![]), // Random choices
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
iteration: 0,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Construct a Pseudo-random number generator from a pre-defined list of choices.
|
/// Construct a Pseudo-random number generator from a pre-defined list of choices.
|
||||||
pub fn from_choices(choices: &[u8], iteration: usize) -> Prng {
|
pub fn from_choices(choices: &[u8]) -> Prng {
|
||||||
Prng::Replayed {
|
Prng::Replayed {
|
||||||
uplc: Data::constr(
|
uplc: Data::constr(
|
||||||
Prng::REPLAYED,
|
Prng::REPLAYED,
|
||||||
|
@ -603,7 +598,6 @@ impl Prng {
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
choices: choices.to_vec(),
|
choices: choices.to_vec(),
|
||||||
iteration,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -611,11 +605,10 @@ impl Prng {
|
||||||
pub fn sample(
|
pub fn sample(
|
||||||
&self,
|
&self,
|
||||||
fuzzer: &Program<Name>,
|
fuzzer: &Program<Name>,
|
||||||
iteration: usize,
|
// iteration: usize,
|
||||||
) -> Result<Option<(Prng, PlutusData)>, FuzzerError> {
|
) -> Result<Option<(Prng, PlutusData)>, FuzzerError> {
|
||||||
let program = Program::<NamedDeBruijn>::try_from(
|
let program = Program::<NamedDeBruijn>::try_from(
|
||||||
fuzzer
|
fuzzer
|
||||||
.apply_data(Data::integer(num_bigint::BigInt::from(iteration as i64)))
|
|
||||||
.apply_data(self.uplc())).unwrap();
|
.apply_data(self.uplc())).unwrap();
|
||||||
let mut result = program.eval(ExBudget::max());
|
let mut result = program.eval(ExBudget::max());
|
||||||
result
|
result
|
||||||
|
@ -624,7 +617,7 @@ impl Prng {
|
||||||
traces: result.logs(),
|
traces: result.logs(),
|
||||||
uplc_error,
|
uplc_error,
|
||||||
})
|
})
|
||||||
.map(|term| Prng::from_result(term, iteration))
|
.map(|term| Prng::from_result(term))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Obtain a Prng back from a fuzzer execution. As a reminder, fuzzers have the following
|
/// Obtain a Prng back from a fuzzer execution. As a reminder, fuzzers have the following
|
||||||
|
@ -639,10 +632,9 @@ impl Prng {
|
||||||
/// aborted altogether with 'None'.
|
/// aborted altogether with 'None'.
|
||||||
pub fn from_result(
|
pub fn from_result(
|
||||||
result: Term<NamedDeBruijn>,
|
result: Term<NamedDeBruijn>,
|
||||||
iteration: usize,
|
|
||||||
) -> Option<(Self, PlutusData)> {
|
) -> Option<(Self, PlutusData)> {
|
||||||
/// Interpret the given 'PlutusData' as one of two Prng constructors.
|
/// Interpret the given 'PlutusData' as one of two Prng constructors.
|
||||||
fn as_prng(cst: &PlutusData, iteration: usize) -> Prng {
|
fn as_prng(cst: &PlutusData) -> Prng {
|
||||||
if let PlutusData::Constr(Constr { tag, fields, .. }) = cst {
|
if let PlutusData::Constr(Constr { tag, fields, .. }) = cst {
|
||||||
if *tag == 121 + Prng::SEEDED {
|
if *tag == 121 + Prng::SEEDED {
|
||||||
if let [PlutusData::BoundedBytes(bytes), PlutusData::BoundedBytes(choices)] =
|
if let [PlutusData::BoundedBytes(bytes), PlutusData::BoundedBytes(choices)] =
|
||||||
|
@ -659,7 +651,6 @@ impl Prng {
|
||||||
PlutusData::BoundedBytes(vec![].into()),
|
PlutusData::BoundedBytes(vec![].into()),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
iteration,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -670,7 +661,6 @@ impl Prng {
|
||||||
return Prng::Replayed {
|
return Prng::Replayed {
|
||||||
choices: choices.to_vec(),
|
choices: choices.to_vec(),
|
||||||
uplc: cst.clone(),
|
uplc: cst.clone(),
|
||||||
iteration,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -684,7 +674,7 @@ impl Prng {
|
||||||
if *tag == 121 + Prng::SOME {
|
if *tag == 121 + Prng::SOME {
|
||||||
if let [PlutusData::Array(elems)] = &fields[..] {
|
if let [PlutusData::Array(elems)] = &fields[..] {
|
||||||
if let [new_seed, value] = &elems[..] {
|
if let [new_seed, value] = &elems[..] {
|
||||||
return Some((as_prng(new_seed, iteration), value.clone()));
|
return Some((as_prng(new_seed), value.clone()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1773,17 +1773,27 @@ fn pipe_wrong_arity_fully_saturated_return_fn() {
|
||||||
#[test]
|
#[test]
|
||||||
fn fuzzer_ok_basic() {
|
fn fuzzer_ok_basic() {
|
||||||
let source_code = r#"
|
let source_code = r#"
|
||||||
fn int() -> Generator<Void, Int> { todo }
|
fn int() -> Fuzzer<Int> { todo }
|
||||||
test prop(n via int()) { True }
|
test prop(n via int()) { True }
|
||||||
"#;
|
"#;
|
||||||
|
|
||||||
assert!(check(parse(source_code)).is_ok());
|
assert!(check(parse(source_code)).is_ok());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn sampler_ok_basic() {
|
||||||
|
let source_code = r#"
|
||||||
|
fn int() -> Sampler<Int> { todo }
|
||||||
|
bench prop(n via int()) { True }
|
||||||
|
"#;
|
||||||
|
|
||||||
|
assert!(check(parse(source_code)).is_ok());
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn fuzzer_ok_explicit() {
|
fn fuzzer_ok_explicit() {
|
||||||
let source_code = r#"
|
let source_code = r#"
|
||||||
fn int(void: Void, prng: PRNG) -> Option<(PRNG, Int)> { todo }
|
fn int(prng: PRNG) -> Option<(PRNG, Int)> { todo }
|
||||||
test prop(n via int) { Void }
|
test prop(n via int) { Void }
|
||||||
"#;
|
"#;
|
||||||
|
|
||||||
|
@ -1793,8 +1803,8 @@ fn fuzzer_ok_explicit() {
|
||||||
#[test]
|
#[test]
|
||||||
fn fuzzer_ok_list() {
|
fn fuzzer_ok_list() {
|
||||||
let source_code = r#"
|
let source_code = r#"
|
||||||
fn int() -> Generator<Void, Int> { todo }
|
fn int() -> Fuzzer<Int> { todo }
|
||||||
fn list(a: Generator<Void, a>) -> Generator<Void, List<a>> { todo }
|
fn list(a: Fuzzer<a>) -> Fuzzer<List<a>> { todo }
|
||||||
|
|
||||||
test prop(xs via list(int())) { True }
|
test prop(xs via list(int())) { True }
|
||||||
"#;
|
"#;
|
||||||
|
@ -1805,8 +1815,8 @@ fn fuzzer_ok_list() {
|
||||||
#[test]
|
#[test]
|
||||||
fn fuzzer_err_unbound() {
|
fn fuzzer_err_unbound() {
|
||||||
let source_code = r#"
|
let source_code = r#"
|
||||||
fn any() -> Generator<Void, a> { todo }
|
fn any() -> Fuzzer<a> { todo }
|
||||||
fn list(a: Generator<Void, a>) -> Generator<Void, List<a>> { todo }
|
fn list(a: Fuzzer<a>) -> Fuzzer<List<a>> { todo }
|
||||||
|
|
||||||
test prop(xs via list(any())) { todo }
|
test prop(xs via list(any())) { todo }
|
||||||
"#;
|
"#;
|
||||||
|
@ -1838,7 +1848,7 @@ fn fuzzer_err_unify_1() {
|
||||||
#[test]
|
#[test]
|
||||||
fn fuzzer_err_unify_2() {
|
fn fuzzer_err_unify_2() {
|
||||||
let source_code = r#"
|
let source_code = r#"
|
||||||
fn any() -> Generator<Void, a> { todo }
|
fn any() -> Fuzzer<a> { todo }
|
||||||
test prop(xs via any) { todo }
|
test prop(xs via any) { todo }
|
||||||
"#;
|
"#;
|
||||||
|
|
||||||
|
@ -1857,8 +1867,8 @@ fn fuzzer_err_unify_2() {
|
||||||
#[test]
|
#[test]
|
||||||
fn fuzzer_err_unify_3() {
|
fn fuzzer_err_unify_3() {
|
||||||
let source_code = r#"
|
let source_code = r#"
|
||||||
fn list(a: Generator<Void, a>) -> Generator<Void, List<a>> { todo }
|
fn list(a: Fuzzer<a>) -> Fuzzer<List<a>> { todo }
|
||||||
fn int() -> Generator<Void, Int> { todo }
|
fn int() -> Fuzzer<Int> { todo }
|
||||||
|
|
||||||
test prop(xs: Int via list(int())) { todo }
|
test prop(xs: Int via list(int())) { todo }
|
||||||
"#;
|
"#;
|
||||||
|
|
|
@ -838,8 +838,7 @@ fn infer_fuzzer(
|
||||||
) -> Result<(Annotation, Rc<Type>), Error> {
|
) -> Result<(Annotation, Rc<Type>), Error> {
|
||||||
let could_not_unify = || Error::CouldNotUnify {
|
let could_not_unify = || Error::CouldNotUnify {
|
||||||
location: *location,
|
location: *location,
|
||||||
expected: Type::generator(
|
expected: Type::fuzzer(
|
||||||
Type::void(),
|
|
||||||
expected_inner_type
|
expected_inner_type
|
||||||
.clone()
|
.clone()
|
||||||
.unwrap_or_else(|| Type::generic_var(0)),
|
.unwrap_or_else(|| Type::generic_var(0)),
|
||||||
|
@ -863,7 +862,7 @@ fn infer_fuzzer(
|
||||||
contains_opaque: _,
|
contains_opaque: _,
|
||||||
alias: _,
|
alias: _,
|
||||||
} if module.is_empty() && name == "Option" && args.len() == 1 => {
|
} if module.is_empty() && name == "Option" && args.len() == 1 => {
|
||||||
match args.first().expect("args.len() == 2 && args[0].is_void()").borrow() {
|
match args.first().expect("args.len() == 1").borrow() {
|
||||||
Type::Tuple { elems, .. } if elems.len() == 2 => {
|
Type::Tuple { elems, .. } if elems.len() == 2 => {
|
||||||
let wrapped = elems.get(1).expect("Tuple has two elements");
|
let wrapped = elems.get(1).expect("Tuple has two elements");
|
||||||
|
|
||||||
|
@ -878,7 +877,7 @@ fn infer_fuzzer(
|
||||||
// `unify` now that we have figured out the type carried by the fuzzer.
|
// `unify` now that we have figured out the type carried by the fuzzer.
|
||||||
environment.unify(
|
environment.unify(
|
||||||
tipo.clone(),
|
tipo.clone(),
|
||||||
Type::generator(Type::void(), wrapped.clone()),
|
Type::fuzzer(wrapped.clone()),
|
||||||
*location,
|
*location,
|
||||||
false,
|
false,
|
||||||
)?;
|
)?;
|
||||||
|
@ -916,8 +915,7 @@ fn infer_sampler(
|
||||||
) -> Result<(Annotation, Rc<Type>), Error> {
|
) -> Result<(Annotation, Rc<Type>), Error> {
|
||||||
let could_not_unify = || Error::CouldNotUnify {
|
let could_not_unify = || Error::CouldNotUnify {
|
||||||
location: *location,
|
location: *location,
|
||||||
expected: Type::generator(
|
expected: Type::sampler(
|
||||||
Type::int(),
|
|
||||||
expected_inner_type
|
expected_inner_type
|
||||||
.clone()
|
.clone()
|
||||||
.unwrap_or_else(|| Type::generic_var(0)),
|
.unwrap_or_else(|| Type::generic_var(0)),
|
||||||
|
@ -930,34 +928,12 @@ fn infer_sampler(
|
||||||
match tipo.borrow() {
|
match tipo.borrow() {
|
||||||
Type::Fn {
|
Type::Fn {
|
||||||
ret,
|
ret,
|
||||||
args: _,
|
|
||||||
alias: _,
|
|
||||||
} => match ret.borrow() {
|
|
||||||
Type::App {
|
|
||||||
module,
|
|
||||||
name,
|
|
||||||
args,
|
args,
|
||||||
public: _,
|
|
||||||
contains_opaque: _,
|
|
||||||
alias: _,
|
alias: _,
|
||||||
} if module.is_empty() && name == "Option" && args.len() == 1 => {
|
} => if args.len() == 1 && args[0].is_int() {
|
||||||
match args.first().expect("args.len() == 2 && args[0].is_int()").borrow() {
|
infer_fuzzer(environment, expected_inner_type, ret, &Span::empty())
|
||||||
Type::Tuple { elems, .. } if elems.len() == 2 => {
|
} else {
|
||||||
let wrapped = elems.get(1).expect("Tuple has two elements");
|
Err(could_not_unify())
|
||||||
|
|
||||||
environment.unify(
|
|
||||||
tipo.clone(),
|
|
||||||
Type::generator(Type::int(), wrapped.clone()),
|
|
||||||
*location,
|
|
||||||
false,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
Ok((annotate_fuzzer(wrapped, location)?, wrapped.clone()))
|
|
||||||
}
|
|
||||||
_ => Err(could_not_unify()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => Err(could_not_unify()),
|
|
||||||
},
|
},
|
||||||
|
|
||||||
Type::Var { tipo, alias } => match &*tipo.deref().borrow() {
|
Type::Var { tipo, alias } => match &*tipo.deref().borrow() {
|
||||||
|
|
|
@ -193,7 +193,7 @@ impl Error {
|
||||||
test.input_path.to_path_buf(),
|
test.input_path.to_path_buf(),
|
||||||
test.program.to_pretty(),
|
test.program.to_pretty(),
|
||||||
),
|
),
|
||||||
TestResult::Benchmark(_) => ("benchmark".to_string(), PathBuf::new(), String::new()), // todo
|
TestResult::Benchmark(_) => ("bench".to_string(), PathBuf::new(), String::new()), // todo
|
||||||
};
|
};
|
||||||
|
|
||||||
Error::TestFailure {
|
Error::TestFailure {
|
||||||
|
|
|
@ -112,10 +112,8 @@ mod test {
|
||||||
|
|
||||||
const max_int: Int = 255
|
const max_int: Int = 255
|
||||||
|
|
||||||
pub type Fuzzer<a> = Generator<Void, a>
|
|
||||||
|
|
||||||
pub fn int() -> Fuzzer<Int> {
|
pub fn int() -> Fuzzer<Int> {
|
||||||
fn(v: Void, prng: PRNG) -> Option<(PRNG, Int)> {
|
fn(prng: PRNG) -> Option<(PRNG, Int)> {
|
||||||
when prng is {
|
when prng is {
|
||||||
Seeded { seed, choices } -> {
|
Seeded { seed, choices } -> {
|
||||||
let choice =
|
let choice =
|
||||||
|
@ -163,21 +161,21 @@ mod test {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn constant(a: a) -> Fuzzer<a> {
|
pub fn constant(a: a) -> Fuzzer<a> {
|
||||||
fn(v, s0) { Some((s0, a)) }
|
fn(s0) { Some((s0, a)) }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn and_then(fuzz_a: Fuzzer<a>, f: fn(a) -> Fuzzer<b>) -> Fuzzer<b> {
|
pub fn and_then(fuzz_a: Fuzzer<a>, f: fn(a) -> Fuzzer<b>) -> Fuzzer<b> {
|
||||||
fn(v, s0) {
|
fn(s0) {
|
||||||
when fuzz_a(v, s0) is {
|
when fuzz_a(s0) is {
|
||||||
Some((s1, a)) -> f(a)(v, s1)
|
Some((s1, a)) -> f(a)(s1)
|
||||||
None -> None
|
None -> None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn map(fuzz_a: Fuzzer<a>, f: fn(a) -> b) -> Fuzzer<b> {
|
pub fn map(fuzz_a: Fuzzer<a>, f: fn(a) -> b) -> Fuzzer<b> {
|
||||||
fn(v, s0) {
|
fn(s0) {
|
||||||
when fuzz_a(v, s0) is {
|
when fuzz_a(s0) is {
|
||||||
Some((s1, a)) -> Some((s1, f(a)))
|
Some((s1, a)) -> Some((s1, f(a)))
|
||||||
None -> None
|
None -> None
|
||||||
}
|
}
|
||||||
|
@ -185,10 +183,10 @@ mod test {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn map2(fuzz_a: Fuzzer<a>, fuzz_b: Fuzzer<b>, f: fn(a, b) -> c) -> Fuzzer<c> {
|
pub fn map2(fuzz_a: Fuzzer<a>, fuzz_b: Fuzzer<b>, f: fn(a, b) -> c) -> Fuzzer<c> {
|
||||||
fn(v, s0) {
|
fn(s0) {
|
||||||
when fuzz_a(v, s0) is {
|
when fuzz_a(s0) is {
|
||||||
Some((s1, a)) ->
|
Some((s1, a)) ->
|
||||||
when fuzz_b(Void, s1) is {
|
when fuzz_b(s1) is {
|
||||||
Some((s2, b)) -> Some((s2, f(a, b)))
|
Some((s2, b)) -> Some((s2, f(a, b)))
|
||||||
None -> None
|
None -> None
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue