Added benchmark keyword and unified Samplers and Fuzzers as Generator
This commit is contained in:
parent
d353e07ea1
commit
c0fabcd26a
|
@ -118,6 +118,7 @@ impl TypedModule {
|
||||||
Definition::Use(_) => false,
|
Definition::Use(_) => false,
|
||||||
Definition::Test(_) => false,
|
Definition::Test(_) => false,
|
||||||
Definition::Validator(_) => false,
|
Definition::Validator(_) => false,
|
||||||
|
Definition::Benchmark(_) => false,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -134,6 +135,7 @@ impl TypedModule {
|
||||||
Definition::Use(_) => false,
|
Definition::Use(_) => false,
|
||||||
Definition::Test(_) => false,
|
Definition::Test(_) => false,
|
||||||
Definition::Validator(_) => false,
|
Definition::Validator(_) => false,
|
||||||
|
Definition::Benchmark(_) => false,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -186,6 +188,16 @@ impl TypedModule {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Definition::Benchmark(benchmark) => {
|
||||||
|
functions.insert(
|
||||||
|
FunctionAccessKey {
|
||||||
|
module_name: self.name.clone(),
|
||||||
|
function_name: benchmark.name.clone(),
|
||||||
|
},
|
||||||
|
benchmark.clone().into(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
Definition::DataType(dt) => {
|
Definition::DataType(dt) => {
|
||||||
data_types.insert(
|
data_types.insert(
|
||||||
DataTypeKey {
|
DataTypeKey {
|
||||||
|
@ -246,6 +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),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -832,6 +845,8 @@ pub enum Definition<T, Arg, Expr, PackageName> {
|
||||||
|
|
||||||
Test(Function<T, Expr, ArgVia<Arg, Expr>>),
|
Test(Function<T, Expr, ArgVia<Arg, Expr>>),
|
||||||
|
|
||||||
|
Benchmark(Function<T, Expr, ArgVia<Arg, Expr>>),
|
||||||
|
|
||||||
Validator(Validator<T, Arg, Expr>),
|
Validator(Validator<T, Arg, Expr>),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -844,6 +859,7 @@ impl<A, B, C, D> Definition<A, B, C, D> {
|
||||||
| Definition::DataType(DataType { location, .. })
|
| Definition::DataType(DataType { location, .. })
|
||||||
| Definition::ModuleConstant(ModuleConstant { location, .. })
|
| Definition::ModuleConstant(ModuleConstant { location, .. })
|
||||||
| Definition::Validator(Validator { location, .. })
|
| Definition::Validator(Validator { location, .. })
|
||||||
|
| Definition::Benchmark(Function { location, .. })
|
||||||
| Definition::Test(Function { location, .. }) => *location,
|
| Definition::Test(Function { location, .. }) => *location,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -856,6 +872,7 @@ impl<A, B, C, D> Definition<A, B, C, D> {
|
||||||
| Definition::DataType(DataType { doc, .. })
|
| Definition::DataType(DataType { doc, .. })
|
||||||
| Definition::ModuleConstant(ModuleConstant { doc, .. })
|
| Definition::ModuleConstant(ModuleConstant { doc, .. })
|
||||||
| Definition::Validator(Validator { doc, .. })
|
| Definition::Validator(Validator { doc, .. })
|
||||||
|
| Definition::Benchmark(Function { doc, .. })
|
||||||
| Definition::Test(Function { doc, .. }) => {
|
| Definition::Test(Function { doc, .. }) => {
|
||||||
let _ = std::mem::replace(doc, Some(new_doc));
|
let _ = std::mem::replace(doc, Some(new_doc));
|
||||||
}
|
}
|
||||||
|
@ -870,6 +887,7 @@ impl<A, B, C, D> Definition<A, B, C, D> {
|
||||||
| Definition::DataType(DataType { doc, .. })
|
| Definition::DataType(DataType { doc, .. })
|
||||||
| Definition::ModuleConstant(ModuleConstant { doc, .. })
|
| Definition::ModuleConstant(ModuleConstant { doc, .. })
|
||||||
| Definition::Validator(Validator { doc, .. })
|
| Definition::Validator(Validator { doc, .. })
|
||||||
|
| Definition::Benchmark(Function { doc, .. })
|
||||||
| Definition::Test(Function { doc, .. }) => doc.clone(),
|
| Definition::Test(Function { doc, .. }) => doc.clone(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,8 +8,7 @@ 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 FUZZER: &str = "Fuzzer";
|
pub const GENERATOR: &str = "Generator";
|
||||||
pub const SCALED_FUZZER: &str = "ScaledFuzzer";
|
|
||||||
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";
|
||||||
|
@ -180,7 +179,7 @@ impl Type {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn fuzzer(a: Rc<Type>) -> Rc<Type> {
|
pub fn generator(c: Rc<Type>, 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,
|
||||||
|
@ -189,64 +188,15 @@ impl Type {
|
||||||
};
|
};
|
||||||
|
|
||||||
Rc::new(Type::Fn {
|
Rc::new(Type::Fn {
|
||||||
args: vec![Type::prng()],
|
args: vec![c, 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: FUZZER.to_string(),
|
alias: GENERATOR.to_string(),
|
||||||
parameters: vec!["a".to_string()],
|
parameters: vec!["c".to_string(), "a".to_string()],
|
||||||
annotation: Annotation::Fn {
|
annotation: Annotation::Fn {
|
||||||
location: Span::empty(),
|
location: Span::empty(),
|
||||||
arguments: vec![prng_annotation.clone()],
|
arguments: vec![Annotation::data(Span::empty()), 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(),
|
|
||||||
),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn scaled_fuzzer(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::prng(), Type::int()],
|
|
||||||
ret: Type::option(Type::tuple(vec![Type::prng(), a])),
|
|
||||||
alias: Some(
|
|
||||||
TypeAliasAnnotation {
|
|
||||||
alias: SCALED_FUZZER.to_string(),
|
|
||||||
parameters: vec!["a".to_string()],
|
|
||||||
annotation: Annotation::Fn {
|
|
||||||
location: Span::empty(),
|
|
||||||
arguments: vec![
|
|
||||||
prng_annotation.clone(),
|
|
||||||
Annotation::Constructor {
|
|
||||||
location: Span::empty(),
|
|
||||||
module: None,
|
|
||||||
name: INT.to_string(),
|
|
||||||
arguments: vec![],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
ret: Annotation::Constructor {
|
ret: Annotation::Constructor {
|
||||||
location: Span::empty(),
|
location: Span::empty(),
|
||||||
module: None,
|
module: None,
|
||||||
|
|
|
@ -477,33 +477,18 @@ pub fn prelude(id_gen: &IdGenerator) -> TypeInfo {
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Fuzzer
|
// Generator
|
||||||
//
|
//
|
||||||
// pub type Fuzzer<a> =
|
// pub type Generator<c, a> =
|
||||||
// fn(PRNG) -> Option<(PRNG, a)>
|
// fn(Data, PRNG) -> Option<(PRNG, a)>
|
||||||
let fuzzer_value = Type::generic_var(id_gen.next());
|
let generator_context = Type::generic_var(id_gen.next());
|
||||||
|
let generator_value = Type::generic_var(id_gen.next());
|
||||||
prelude.types.insert(
|
prelude.types.insert(
|
||||||
well_known::FUZZER.to_string(),
|
well_known::GENERATOR.to_string(),
|
||||||
TypeConstructor {
|
TypeConstructor {
|
||||||
location: Span::empty(),
|
location: Span::empty(),
|
||||||
parameters: vec![fuzzer_value.clone()],
|
parameters: vec![generator_context.clone(), generator_value.clone()],
|
||||||
tipo: Type::fuzzer(fuzzer_value),
|
tipo: Type::generator(generator_context, generator_value),
|
||||||
module: "".to_string(),
|
|
||||||
public: true,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
// ScaledFuzzer
|
|
||||||
//
|
|
||||||
// pub type ScaledFuzzer<a> =
|
|
||||||
// fn(PRNG, Int) -> Option<(PRNG, a)>
|
|
||||||
let scaled_fuzzer_value = Type::generic_var(id_gen.next());
|
|
||||||
prelude.types.insert(
|
|
||||||
well_known::SCALED_FUZZER.to_string(),
|
|
||||||
TypeConstructor {
|
|
||||||
location: Span::empty(),
|
|
||||||
parameters: vec![scaled_fuzzer_value.clone()],
|
|
||||||
tipo: Type::scaled_fuzzer(scaled_fuzzer_value),
|
|
||||||
module: "".to_string(),
|
module: "".to_string(),
|
||||||
public: true,
|
public: true,
|
||||||
},
|
},
|
||||||
|
|
|
@ -258,6 +258,15 @@ impl<'comments> Formatter<'comments> {
|
||||||
..
|
..
|
||||||
}) => self.definition_test(name, args, body, *end_position, on_test_failure),
|
}) => self.definition_test(name, args, body, *end_position, on_test_failure),
|
||||||
|
|
||||||
|
Definition::Benchmark(Function {
|
||||||
|
name,
|
||||||
|
arguments: args,
|
||||||
|
body,
|
||||||
|
end_position,
|
||||||
|
on_test_failure,
|
||||||
|
..
|
||||||
|
}) => self.definition_benchmark(name, args, body, *end_position, on_test_failure),
|
||||||
|
|
||||||
Definition::TypeAlias(TypeAlias {
|
Definition::TypeAlias(TypeAlias {
|
||||||
alias,
|
alias,
|
||||||
parameters: args,
|
parameters: args,
|
||||||
|
@ -631,6 +640,43 @@ impl<'comments> Formatter<'comments> {
|
||||||
.append("}")
|
.append("}")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
|
fn definition_benchmark<'a>(
|
||||||
|
&mut self,
|
||||||
|
name: &'a str,
|
||||||
|
args: &'a [UntypedArgVia],
|
||||||
|
body: &'a UntypedExpr,
|
||||||
|
end_location: usize,
|
||||||
|
on_test_failure: &'a OnTestFailure,
|
||||||
|
) -> Document<'a> {
|
||||||
|
// Fn name and args
|
||||||
|
let head = "benchmark "
|
||||||
|
.to_doc()
|
||||||
|
.append(name)
|
||||||
|
.append(wrap_args(args.iter().map(|e| (self.fn_arg_via(e), false))))
|
||||||
|
.append(match on_test_failure {
|
||||||
|
OnTestFailure::FailImmediately => "",
|
||||||
|
OnTestFailure::SucceedEventually => "",
|
||||||
|
OnTestFailure::SucceedImmediately => "",
|
||||||
|
})
|
||||||
|
.group();
|
||||||
|
|
||||||
|
// Format body
|
||||||
|
let body = self.expr(body, true);
|
||||||
|
|
||||||
|
// Add any trailing comments
|
||||||
|
let body = match printed_comments(self.pop_comments(end_location), false) {
|
||||||
|
Some(comments) => body.append(line()).append(comments),
|
||||||
|
None => body,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Stick it all together
|
||||||
|
head.append(" {")
|
||||||
|
.append(line().append(body).nest(INDENT).group())
|
||||||
|
.append(line())
|
||||||
|
.append("}")
|
||||||
|
}
|
||||||
|
|
||||||
fn definition_validator<'a>(
|
fn definition_validator<'a>(
|
||||||
&mut self,
|
&mut self,
|
||||||
name: &'a str,
|
name: &'a str,
|
||||||
|
|
|
@ -0,0 +1,166 @@
|
||||||
|
use crate::{
|
||||||
|
ast,
|
||||||
|
ast::OnTestFailure,
|
||||||
|
expr::UntypedExpr,
|
||||||
|
parser::{
|
||||||
|
annotation,
|
||||||
|
chain::{call::parser as call, field_access, tuple_index::parser as tuple_index, Chain},
|
||||||
|
error::ParseError,
|
||||||
|
expr::{self, bytearray, int as uint, list, string, tuple, var},
|
||||||
|
pattern,
|
||||||
|
token::Token,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
use chumsky::prelude::*;
|
||||||
|
|
||||||
|
pub fn parser() -> impl Parser<Token, ast::UntypedDefinition, Error = ParseError> {
|
||||||
|
just(Token::Benchmark)
|
||||||
|
.ignore_then(select! {Token::Name {name} => name})
|
||||||
|
.then(
|
||||||
|
via()
|
||||||
|
.separated_by(just(Token::Comma))
|
||||||
|
.allow_trailing()
|
||||||
|
.delimited_by(just(Token::LeftParen), just(Token::RightParen)),
|
||||||
|
)
|
||||||
|
.then(
|
||||||
|
just(Token::Fail)
|
||||||
|
.ignore_then(just(Token::Once).ignored().or_not().map(|once| {
|
||||||
|
once.map(|_| OnTestFailure::SucceedImmediately)
|
||||||
|
.unwrap_or(OnTestFailure::SucceedEventually)
|
||||||
|
}))
|
||||||
|
.or_not(),
|
||||||
|
)
|
||||||
|
.map_with_span(|name, span| (name, span))
|
||||||
|
.then(
|
||||||
|
expr::sequence()
|
||||||
|
.or_not()
|
||||||
|
.delimited_by(just(Token::LeftBrace), just(Token::RightBrace)),
|
||||||
|
)
|
||||||
|
.map_with_span(|((((name, arguments), fail), span_end), body), span| {
|
||||||
|
ast::UntypedDefinition::Benchmark(ast::Function {
|
||||||
|
arguments,
|
||||||
|
body: body.unwrap_or_else(|| UntypedExpr::todo(None, span)),
|
||||||
|
doc: None,
|
||||||
|
location: span_end,
|
||||||
|
end_position: span.end - 1,
|
||||||
|
name,
|
||||||
|
public: false,
|
||||||
|
return_annotation: None,
|
||||||
|
return_type: (),
|
||||||
|
on_test_failure: fail.unwrap_or(OnTestFailure::FailImmediately),
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn via() -> impl Parser<Token, ast::UntypedArgVia, Error = ParseError> {
|
||||||
|
choice((
|
||||||
|
select! {Token::DiscardName {name} => name}.map_with_span(|name, span| {
|
||||||
|
ast::ArgBy::ByName(ast::ArgName::Discarded {
|
||||||
|
label: name.clone(),
|
||||||
|
name,
|
||||||
|
location: span,
|
||||||
|
})
|
||||||
|
}),
|
||||||
|
select! {Token::Name {name} => name}.map_with_span(|name, location| {
|
||||||
|
ast::ArgBy::ByName(ast::ArgName::Named {
|
||||||
|
label: name.clone(),
|
||||||
|
name,
|
||||||
|
location,
|
||||||
|
})
|
||||||
|
}),
|
||||||
|
pattern().map(ast::ArgBy::ByPattern),
|
||||||
|
))
|
||||||
|
.then(just(Token::Colon).ignore_then(annotation()).or_not())
|
||||||
|
.map_with_span(|(arg_name, annotation), location| (arg_name, annotation, location))
|
||||||
|
.then_ignore(just(Token::Via))
|
||||||
|
.then(fuzzer())
|
||||||
|
.map(|((by, annotation, location), via)| ast::ArgVia {
|
||||||
|
arg: ast::UntypedArg {
|
||||||
|
by,
|
||||||
|
annotation,
|
||||||
|
location,
|
||||||
|
doc: None,
|
||||||
|
is_validator_param: false,
|
||||||
|
},
|
||||||
|
via,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn fuzzer<'a>() -> impl Parser<Token, UntypedExpr, Error = ParseError> + 'a {
|
||||||
|
recursive(|expression| {
|
||||||
|
let chain = choice((
|
||||||
|
tuple_index(),
|
||||||
|
field_access::parser(),
|
||||||
|
call(expression.clone()),
|
||||||
|
));
|
||||||
|
|
||||||
|
let int = || {
|
||||||
|
just(Token::Minus)
|
||||||
|
.to(ast::UnOp::Negate)
|
||||||
|
.map_with_span(|op, span| (op, span))
|
||||||
|
.or_not()
|
||||||
|
.then(uint())
|
||||||
|
.map(|(op, value)| match op {
|
||||||
|
None => value,
|
||||||
|
Some((op, location)) => UntypedExpr::UnOp {
|
||||||
|
op,
|
||||||
|
location,
|
||||||
|
value: Box::new(value),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
choice((
|
||||||
|
int(),
|
||||||
|
string(),
|
||||||
|
bytearray(),
|
||||||
|
tuple(expression.clone()),
|
||||||
|
list(expression.clone()),
|
||||||
|
var(),
|
||||||
|
))
|
||||||
|
.then(chain.repeated())
|
||||||
|
.foldl(|expr, chain| match chain {
|
||||||
|
Chain::Call(args, span) => expr.call(args, span),
|
||||||
|
Chain::FieldAccess(label, span) => expr.field_access(label, span),
|
||||||
|
Chain::TupleIndex(index, span) => expr.tuple_index(index, span),
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::assert_definition;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn def_benchmark() {
|
||||||
|
assert_definition!(
|
||||||
|
r#"
|
||||||
|
benchmark foo(x via fuzz.any_int) {
|
||||||
|
True
|
||||||
|
}
|
||||||
|
"#
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn def_invalid_benchmark() {
|
||||||
|
assert_definition!(
|
||||||
|
r#"
|
||||||
|
benchmark foo(x via f, y via g) {
|
||||||
|
True
|
||||||
|
}
|
||||||
|
"#
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn def_benchmark_annotated_fuzzer() {
|
||||||
|
assert_definition!(
|
||||||
|
r#"
|
||||||
|
benchmark foo(x: Int via foo()) {
|
||||||
|
True
|
||||||
|
}
|
||||||
|
"#
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,6 @@
|
||||||
use chumsky::prelude::*;
|
use chumsky::prelude::*;
|
||||||
|
|
||||||
|
pub mod benchmark;
|
||||||
pub mod constant;
|
pub mod constant;
|
||||||
mod data_type;
|
mod data_type;
|
||||||
mod function;
|
mod function;
|
||||||
|
@ -10,6 +11,7 @@ mod validator;
|
||||||
|
|
||||||
use super::{error::ParseError, token::Token};
|
use super::{error::ParseError, token::Token};
|
||||||
use crate::ast;
|
use crate::ast;
|
||||||
|
pub use benchmark::parser as benchmark;
|
||||||
pub use constant::parser as constant;
|
pub use constant::parser as constant;
|
||||||
pub use data_type::parser as data_type;
|
pub use data_type::parser as data_type;
|
||||||
pub use function::parser as function;
|
pub use function::parser as function;
|
||||||
|
@ -24,6 +26,7 @@ pub fn parser() -> impl Parser<Token, ast::UntypedDefinition, Error = ParseError
|
||||||
validator(),
|
validator(),
|
||||||
function(),
|
function(),
|
||||||
test(),
|
test(),
|
||||||
|
benchmark(),
|
||||||
constant(),
|
constant(),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,47 @@
|
||||||
|
---
|
||||||
|
source: crates/aiken-lang/src/parser/definition/benchmark.rs
|
||||||
|
assertion_line: 136
|
||||||
|
description: "Code:\n\nbenchmark foo(x via fuzz.any_int) {\n True\n}\n"
|
||||||
|
snapshot_kind: text
|
||||||
|
---
|
||||||
|
Benchmark(
|
||||||
|
Function {
|
||||||
|
arguments: [
|
||||||
|
ArgVia {
|
||||||
|
arg: UntypedArg {
|
||||||
|
by: ByName(
|
||||||
|
Named {
|
||||||
|
name: "x",
|
||||||
|
label: "x",
|
||||||
|
location: 14..15,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
location: 14..15,
|
||||||
|
annotation: None,
|
||||||
|
doc: None,
|
||||||
|
is_validator_param: false,
|
||||||
|
},
|
||||||
|
via: FieldAccess {
|
||||||
|
location: 20..32,
|
||||||
|
label: "any_int",
|
||||||
|
container: Var {
|
||||||
|
location: 20..24,
|
||||||
|
name: "fuzz",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
body: Var {
|
||||||
|
location: 40..44,
|
||||||
|
name: "True",
|
||||||
|
},
|
||||||
|
doc: None,
|
||||||
|
location: 0..33,
|
||||||
|
name: "foo",
|
||||||
|
public: false,
|
||||||
|
return_annotation: None,
|
||||||
|
return_type: (),
|
||||||
|
end_position: 45,
|
||||||
|
on_test_failure: FailImmediately,
|
||||||
|
},
|
||||||
|
)
|
54
crates/aiken-lang/src/parser/definition/snapshots/def_benchmark_annotated_fuzzer.snap
vendored
Normal file
54
crates/aiken-lang/src/parser/definition/snapshots/def_benchmark_annotated_fuzzer.snap
vendored
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
---
|
||||||
|
source: crates/aiken-lang/src/parser/definition/benchmark.rs
|
||||||
|
assertion_line: 158
|
||||||
|
description: "Code:\n\nbenchmark foo(x: Int via foo()) {\n True\n}\n"
|
||||||
|
snapshot_kind: text
|
||||||
|
---
|
||||||
|
Benchmark(
|
||||||
|
Function {
|
||||||
|
arguments: [
|
||||||
|
ArgVia {
|
||||||
|
arg: UntypedArg {
|
||||||
|
by: ByName(
|
||||||
|
Named {
|
||||||
|
name: "x",
|
||||||
|
label: "x",
|
||||||
|
location: 14..15,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
location: 14..20,
|
||||||
|
annotation: Some(
|
||||||
|
Constructor {
|
||||||
|
location: 17..20,
|
||||||
|
module: None,
|
||||||
|
name: "Int",
|
||||||
|
arguments: [],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
doc: None,
|
||||||
|
is_validator_param: false,
|
||||||
|
},
|
||||||
|
via: Call {
|
||||||
|
arguments: [],
|
||||||
|
fun: Var {
|
||||||
|
location: 25..28,
|
||||||
|
name: "foo",
|
||||||
|
},
|
||||||
|
location: 25..30,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
body: Var {
|
||||||
|
location: 38..42,
|
||||||
|
name: "True",
|
||||||
|
},
|
||||||
|
doc: None,
|
||||||
|
location: 0..31,
|
||||||
|
name: "foo",
|
||||||
|
public: false,
|
||||||
|
return_annotation: None,
|
||||||
|
return_type: (),
|
||||||
|
end_position: 43,
|
||||||
|
on_test_failure: FailImmediately,
|
||||||
|
},
|
||||||
|
)
|
62
crates/aiken-lang/src/parser/definition/snapshots/def_invalid_benchmark.snap
vendored
Normal file
62
crates/aiken-lang/src/parser/definition/snapshots/def_invalid_benchmark.snap
vendored
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
---
|
||||||
|
source: crates/aiken-lang/src/parser/definition/benchmark.rs
|
||||||
|
assertion_line: 147
|
||||||
|
description: "Code:\n\nbenchmark foo(x via f, y via g) {\n True\n}\n"
|
||||||
|
snapshot_kind: text
|
||||||
|
---
|
||||||
|
Benchmark(
|
||||||
|
Function {
|
||||||
|
arguments: [
|
||||||
|
ArgVia {
|
||||||
|
arg: UntypedArg {
|
||||||
|
by: ByName(
|
||||||
|
Named {
|
||||||
|
name: "x",
|
||||||
|
label: "x",
|
||||||
|
location: 14..15,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
location: 14..15,
|
||||||
|
annotation: None,
|
||||||
|
doc: None,
|
||||||
|
is_validator_param: false,
|
||||||
|
},
|
||||||
|
via: Var {
|
||||||
|
location: 20..21,
|
||||||
|
name: "f",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ArgVia {
|
||||||
|
arg: UntypedArg {
|
||||||
|
by: ByName(
|
||||||
|
Named {
|
||||||
|
name: "y",
|
||||||
|
label: "y",
|
||||||
|
location: 23..24,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
location: 23..24,
|
||||||
|
annotation: None,
|
||||||
|
doc: None,
|
||||||
|
is_validator_param: false,
|
||||||
|
},
|
||||||
|
via: Var {
|
||||||
|
location: 29..30,
|
||||||
|
name: "g",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
body: Var {
|
||||||
|
location: 38..42,
|
||||||
|
name: "True",
|
||||||
|
},
|
||||||
|
doc: None,
|
||||||
|
location: 0..31,
|
||||||
|
name: "foo",
|
||||||
|
public: false,
|
||||||
|
return_annotation: None,
|
||||||
|
return_type: (),
|
||||||
|
end_position: 43,
|
||||||
|
on_test_failure: FailImmediately,
|
||||||
|
},
|
||||||
|
)
|
|
@ -243,6 +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,
|
||||||
_ => {
|
_ => {
|
||||||
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 {
|
||||||
|
|
|
@ -73,6 +73,7 @@ pub enum Token {
|
||||||
NewLine,
|
NewLine,
|
||||||
// Keywords (alphabetically):
|
// Keywords (alphabetically):
|
||||||
As,
|
As,
|
||||||
|
Benchmark,
|
||||||
Const,
|
Const,
|
||||||
Fn,
|
Fn,
|
||||||
If,
|
If,
|
||||||
|
@ -182,6 +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",
|
||||||
};
|
};
|
||||||
write!(f, "{s}")
|
write!(f, "{s}")
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,6 +49,7 @@ use vec1::{vec1, Vec1};
|
||||||
pub enum Test {
|
pub enum Test {
|
||||||
UnitTest(UnitTest),
|
UnitTest(UnitTest),
|
||||||
PropertyTest(PropertyTest),
|
PropertyTest(PropertyTest),
|
||||||
|
Benchmark(Benchmark)
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl Send for Test {}
|
unsafe impl Send for Test {}
|
||||||
|
@ -442,7 +443,23 @@ impl PropertyTest {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ----- Benchmark -----------------------------------------------------------------
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Benchmark {
|
||||||
|
pub input_path: PathBuf,
|
||||||
|
pub module: String,
|
||||||
|
pub name: String,
|
||||||
|
pub on_test_failure: OnTestFailure,
|
||||||
|
pub program: Program<Name>,
|
||||||
|
pub fuzzer: Fuzzer<Name>,
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl Send for Benchmark {}
|
||||||
|
|
||||||
|
impl Benchmark {
|
||||||
pub fn benchmark(
|
pub fn benchmark(
|
||||||
self,
|
self,
|
||||||
seed: u32,
|
seed: u32,
|
||||||
|
@ -484,6 +501,14 @@ impl PropertyTest {
|
||||||
|
|
||||||
results
|
results
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn eval(&self, value: &PlutusData, plutus_version: &PlutusVersion) -> EvalResult {
|
||||||
|
let program = self.program.apply_data(value.clone());
|
||||||
|
|
||||||
|
Program::<NamedDeBruijn>::try_from(program)
|
||||||
|
.unwrap()
|
||||||
|
.eval_version(ExBudget::max(), &plutus_version.into())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// ----- PRNG -----------------------------------------------------------------
|
/// ----- PRNG -----------------------------------------------------------------
|
||||||
|
@ -558,7 +583,10 @@ impl Prng {
|
||||||
choices: vec![],
|
choices: vec![],
|
||||||
uplc: Data::constr(
|
uplc: Data::constr(
|
||||||
Prng::SEEDED,
|
Prng::SEEDED,
|
||||||
vec![Data::bytestring(digest.to_vec()), Data::bytestring(vec![])],
|
vec![
|
||||||
|
Data::bytestring(digest.to_vec()), // Prng's seed
|
||||||
|
Data::bytestring(vec![]), // Random choices
|
||||||
|
],
|
||||||
),
|
),
|
||||||
iteration: 0,
|
iteration: 0,
|
||||||
}
|
}
|
||||||
|
@ -585,35 +613,18 @@ impl Prng {
|
||||||
fuzzer: &Program<Name>,
|
fuzzer: &Program<Name>,
|
||||||
iteration: usize,
|
iteration: usize,
|
||||||
) -> Result<Option<(Prng, PlutusData)>, FuzzerError> {
|
) -> Result<Option<(Prng, PlutusData)>, FuzzerError> {
|
||||||
// First try evaluating as a regular fuzzer
|
let program = Program::<NamedDeBruijn>::try_from(
|
||||||
let program = Program::<NamedDeBruijn>::try_from(fuzzer.apply_data(self.uplc())).unwrap();
|
fuzzer
|
||||||
let program_clone = program.clone();
|
.apply_data(Data::integer(num_bigint::BigInt::from(iteration as i64)))
|
||||||
|
.apply_data(self.uplc())).unwrap();
|
||||||
let result = program.eval(ExBudget::max());
|
let mut result = program.eval(ExBudget::max());
|
||||||
|
result
|
||||||
match result.result() {
|
.result()
|
||||||
Ok(term) if matches!(term, Term::Constant(_)) => {
|
.map_err(|uplc_error| FuzzerError {
|
||||||
// If we got a valid constant result, process it
|
traces: result.logs(),
|
||||||
Ok(Prng::from_result(term, iteration))
|
uplc_error,
|
||||||
}
|
})
|
||||||
_ => {
|
.map(|term| Prng::from_result(term, iteration))
|
||||||
// Use the cloned program for the second attempt
|
|
||||||
let program_with_iteration = program_clone
|
|
||||||
.apply_data(Data::integer(num_bigint::BigInt::from(iteration as i64)));
|
|
||||||
|
|
||||||
let mut result = program_with_iteration.eval(ExBudget::max());
|
|
||||||
match result.result() {
|
|
||||||
Ok(term) if matches!(term, Term::Constant(_)) => {
|
|
||||||
Ok(Prng::from_result(term, iteration))
|
|
||||||
}
|
|
||||||
Err(uplc_error) => Err(FuzzerError {
|
|
||||||
traces: result.logs(),
|
|
||||||
uplc_error,
|
|
||||||
}),
|
|
||||||
_ => unreachable!("Fuzzer returned a malformed result? {result:#?}"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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
|
||||||
|
@ -1431,7 +1442,7 @@ impl Assertion<UntypedExpr> {
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct BenchmarkResult {
|
pub struct BenchmarkResult {
|
||||||
pub test: PropertyTest,
|
pub test: Benchmark,
|
||||||
pub cost: ExBudget,
|
pub cost: ExBudget,
|
||||||
pub success: bool,
|
pub success: bool,
|
||||||
pub traces: Vec<String>,
|
pub traces: Vec<String>,
|
||||||
|
|
|
@ -1773,7 +1773,7 @@ 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() -> Fuzzer<Int> { todo }
|
fn int() -> Generator<Void, Int> { todo }
|
||||||
test prop(n via int()) { True }
|
test prop(n via int()) { True }
|
||||||
"#;
|
"#;
|
||||||
|
|
||||||
|
@ -1783,7 +1783,7 @@ fn fuzzer_ok_basic() {
|
||||||
#[test]
|
#[test]
|
||||||
fn fuzzer_ok_explicit() {
|
fn fuzzer_ok_explicit() {
|
||||||
let source_code = r#"
|
let source_code = r#"
|
||||||
fn int(prng: PRNG) -> Option<(PRNG, Int)> { todo }
|
fn int(void: Void, prng: PRNG) -> Option<(PRNG, Int)> { todo }
|
||||||
test prop(n via int) { Void }
|
test prop(n via int) { Void }
|
||||||
"#;
|
"#;
|
||||||
|
|
||||||
|
@ -1793,8 +1793,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() -> Fuzzer<Int> { todo }
|
fn int() -> Generator<Void, Int> { todo }
|
||||||
fn list(a: Fuzzer<a>) -> Fuzzer<List<a>> { todo }
|
fn list(a: Generator<Void, a>) -> Generator<Void, List<a>> { todo }
|
||||||
|
|
||||||
test prop(xs via list(int())) { True }
|
test prop(xs via list(int())) { True }
|
||||||
"#;
|
"#;
|
||||||
|
@ -1805,8 +1805,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() -> Fuzzer<a> { todo }
|
fn any() -> Generator<Void, a> { todo }
|
||||||
fn list(a: Fuzzer<a>) -> Fuzzer<List<a>> { todo }
|
fn list(a: Generator<Void, a>) -> Generator<Void, List<a>> { todo }
|
||||||
|
|
||||||
test prop(xs via list(any())) { todo }
|
test prop(xs via list(any())) { todo }
|
||||||
"#;
|
"#;
|
||||||
|
@ -1838,7 +1838,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() -> Fuzzer<a> { todo }
|
fn any() -> Generator<Void, a> { todo }
|
||||||
test prop(xs via any) { todo }
|
test prop(xs via any) { todo }
|
||||||
"#;
|
"#;
|
||||||
|
|
||||||
|
@ -1857,8 +1857,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: Fuzzer<a>) -> Fuzzer<List<a>> { todo }
|
fn list(a: Generator<Void, a>) -> Generator<Void, List<a>> { todo }
|
||||||
fn int() -> Fuzzer<Int> { todo }
|
fn int() -> Generator<Void, Int> { todo }
|
||||||
|
|
||||||
test prop(xs: Int via list(int())) { todo }
|
test prop(xs: Int via list(int())) { todo }
|
||||||
"#;
|
"#;
|
||||||
|
@ -1875,45 +1875,6 @@ fn fuzzer_err_unify_3() {
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn scaled_fuzzer_ok_basic() {
|
|
||||||
let source_code = r#"
|
|
||||||
fn int() -> ScaledFuzzer<Int> { todo }
|
|
||||||
test prop(n via int()) { True }
|
|
||||||
"#;
|
|
||||||
|
|
||||||
assert!(check(parse(source_code)).is_ok());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn scaled_fuzzer_ok_explicit() {
|
|
||||||
let source_code = r#"
|
|
||||||
fn int(prng: PRNG, complexity: Int) -> Option<(PRNG, Int)> { todo }
|
|
||||||
test prop(n via int) { True }
|
|
||||||
"#;
|
|
||||||
|
|
||||||
assert!(check(parse(source_code)).is_ok());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn scaled_fuzzer_err_unify() {
|
|
||||||
let source_code = r#"
|
|
||||||
fn int() -> ScaledFuzzer<Int> { todo }
|
|
||||||
test prop(n: Bool via int()) { True }
|
|
||||||
"#;
|
|
||||||
|
|
||||||
assert!(matches!(
|
|
||||||
check(parse(source_code)),
|
|
||||||
Err((
|
|
||||||
_,
|
|
||||||
Error::CouldNotUnify {
|
|
||||||
situation: Some(UnifyErrorSituation::FuzzerAnnotationMismatch),
|
|
||||||
..
|
|
||||||
}
|
|
||||||
))
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn utf8_hex_literal_warning() {
|
fn utf8_hex_literal_warning() {
|
||||||
let source_code = r#"
|
let source_code = r#"
|
||||||
|
|
|
@ -367,6 +367,7 @@ impl<'a> Environment<'a> {
|
||||||
| Definition::DataType { .. }
|
| Definition::DataType { .. }
|
||||||
| Definition::Use { .. }
|
| Definition::Use { .. }
|
||||||
| Definition::Test { .. }
|
| Definition::Test { .. }
|
||||||
|
| Definition::Benchmark { .. }
|
||||||
| Definition::ModuleConstant { .. }) => definition,
|
| Definition::ModuleConstant { .. }) => definition,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1061,6 +1062,7 @@ impl<'a> Environment<'a> {
|
||||||
| Definition::Validator { .. }
|
| Definition::Validator { .. }
|
||||||
| Definition::Use { .. }
|
| Definition::Use { .. }
|
||||||
| Definition::ModuleConstant { .. }
|
| Definition::ModuleConstant { .. }
|
||||||
|
| Definition::Benchmark { .. }
|
||||||
| Definition::Test { .. } => None,
|
| Definition::Test { .. } => None,
|
||||||
})
|
})
|
||||||
.collect::<Vec<Span>>();
|
.collect::<Vec<Span>>();
|
||||||
|
@ -1184,6 +1186,7 @@ impl<'a> Environment<'a> {
|
||||||
Definition::Fn { .. }
|
Definition::Fn { .. }
|
||||||
| Definition::Validator { .. }
|
| Definition::Validator { .. }
|
||||||
| Definition::Test { .. }
|
| Definition::Test { .. }
|
||||||
|
| Definition::Benchmark { .. }
|
||||||
| Definition::Use { .. }
|
| Definition::Use { .. }
|
||||||
| Definition::ModuleConstant { .. } => {}
|
| Definition::ModuleConstant { .. } => {}
|
||||||
}
|
}
|
||||||
|
@ -1387,6 +1390,24 @@ impl<'a> Environment<'a> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Definition::Benchmark(benchmark) => {
|
||||||
|
let arguments = benchmark
|
||||||
|
.arguments
|
||||||
|
.iter()
|
||||||
|
.map(|arg| arg.clone().into())
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
self.register_function(
|
||||||
|
&benchmark.name,
|
||||||
|
&arguments,
|
||||||
|
&benchmark.return_annotation,
|
||||||
|
module_name,
|
||||||
|
hydrators,
|
||||||
|
names,
|
||||||
|
&benchmark.location,
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
|
||||||
Definition::Test(test) => {
|
Definition::Test(test) => {
|
||||||
let arguments = test
|
let arguments = test
|
||||||
.arguments
|
.arguments
|
||||||
|
|
|
@ -81,6 +81,7 @@ impl UntypedModule {
|
||||||
Definition::Validator { .. } => (),
|
Definition::Validator { .. } => (),
|
||||||
Definition::Fn { .. }
|
Definition::Fn { .. }
|
||||||
| Definition::Test { .. }
|
| Definition::Test { .. }
|
||||||
|
| Definition::Benchmark { .. }
|
||||||
| Definition::TypeAlias { .. }
|
| Definition::TypeAlias { .. }
|
||||||
| Definition::DataType { .. }
|
| Definition::DataType { .. }
|
||||||
| Definition::Use { .. } => not_consts.push(def),
|
| Definition::Use { .. } => not_consts.push(def),
|
||||||
|
@ -364,15 +365,7 @@ fn infer_definition(
|
||||||
&arg.via.location(),
|
&arg.via.location(),
|
||||||
) {
|
) {
|
||||||
Ok(result) => Ok(result),
|
Ok(result) => Ok(result),
|
||||||
Err(err) => match err {
|
Err(err) => Err(err)
|
||||||
Error::CouldNotUnify { .. } => infer_scaled_fuzzer(
|
|
||||||
environment,
|
|
||||||
provided_inner_type.clone(),
|
|
||||||
&typed_via.tipo(),
|
|
||||||
&arg.via.location(),
|
|
||||||
),
|
|
||||||
_ => Err(err),
|
|
||||||
},
|
|
||||||
}?;
|
}?;
|
||||||
|
|
||||||
// Ensure that the annotation, if any, matches the type inferred from the
|
// Ensure that the annotation, if any, matches the type inferred from the
|
||||||
|
@ -475,6 +468,142 @@ fn infer_definition(
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Definition::Benchmark(f) => {
|
||||||
|
let (typed_via, annotation) = match f.arguments.first() {
|
||||||
|
Some(arg) => {
|
||||||
|
if f.arguments.len() > 1 {
|
||||||
|
return Err(Error::IncorrectTestArity {
|
||||||
|
count: f.arguments.len(),
|
||||||
|
location: f
|
||||||
|
.arguments
|
||||||
|
.get(1)
|
||||||
|
.expect("arguments.len() > 1")
|
||||||
|
.arg
|
||||||
|
.location,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let typed_via = ExprTyper::new(environment, tracing).infer(arg.via.clone())?;
|
||||||
|
|
||||||
|
let hydrator: &mut Hydrator = hydrators.get_mut(&f.name).unwrap();
|
||||||
|
|
||||||
|
let provided_inner_type = arg
|
||||||
|
.arg
|
||||||
|
.annotation
|
||||||
|
.as_ref()
|
||||||
|
.map(|ann| hydrator.type_from_annotation(ann, environment))
|
||||||
|
.transpose()?;
|
||||||
|
|
||||||
|
let (inferred_annotation, inferred_inner_type) = match infer_sampler(
|
||||||
|
environment,
|
||||||
|
provided_inner_type.clone(),
|
||||||
|
&typed_via.tipo(),
|
||||||
|
&arg.via.location(),
|
||||||
|
) {
|
||||||
|
Ok(result) => Ok(result),
|
||||||
|
Err(err) => Err(err)
|
||||||
|
}?;
|
||||||
|
|
||||||
|
// Ensure that the annotation, if any, matches the type inferred from the
|
||||||
|
// Fuzzer.
|
||||||
|
if let Some(provided_inner_type) = provided_inner_type {
|
||||||
|
if !arg
|
||||||
|
.arg
|
||||||
|
.annotation
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.is_logically_equal(&inferred_annotation)
|
||||||
|
{
|
||||||
|
return Err(Error::CouldNotUnify {
|
||||||
|
location: arg.arg.location,
|
||||||
|
expected: inferred_inner_type.clone(),
|
||||||
|
given: provided_inner_type.clone(),
|
||||||
|
situation: Some(UnifyErrorSituation::FuzzerAnnotationMismatch),
|
||||||
|
rigid_type_names: hydrator.rigid_names(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Replace the pre-registered type for the test function, to allow inferring
|
||||||
|
// the function body with the right type arguments.
|
||||||
|
let scope = environment
|
||||||
|
.scope
|
||||||
|
.get_mut(&f.name)
|
||||||
|
.expect("Could not find preregistered type for benchmark");
|
||||||
|
if let Type::Fn {
|
||||||
|
ref ret,
|
||||||
|
ref alias,
|
||||||
|
args: _,
|
||||||
|
} = scope.tipo.as_ref()
|
||||||
|
{
|
||||||
|
scope.tipo = Rc::new(Type::Fn {
|
||||||
|
ret: ret.clone(),
|
||||||
|
args: vec![inferred_inner_type.clone()],
|
||||||
|
alias: alias.clone(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok((
|
||||||
|
Some((typed_via, inferred_inner_type)),
|
||||||
|
Some(inferred_annotation),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
None => Ok((None, None)),
|
||||||
|
}?;
|
||||||
|
|
||||||
|
let typed_f = infer_function(&f.into(), module_name, hydrators, environment, tracing)?;
|
||||||
|
|
||||||
|
let is_bool = environment.unify(
|
||||||
|
typed_f.return_type.clone(),
|
||||||
|
Type::bool(),
|
||||||
|
typed_f.location,
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
|
||||||
|
let is_void = environment.unify(
|
||||||
|
typed_f.return_type.clone(),
|
||||||
|
Type::void(),
|
||||||
|
typed_f.location,
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
|
||||||
|
if is_bool.or(is_void).is_err() {
|
||||||
|
return Err(Error::IllegalTestType {
|
||||||
|
location: typed_f.location,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Definition::Test(Function {
|
||||||
|
doc: typed_f.doc,
|
||||||
|
location: typed_f.location,
|
||||||
|
name: typed_f.name,
|
||||||
|
public: typed_f.public,
|
||||||
|
arguments: match typed_via {
|
||||||
|
Some((via, tipo)) => {
|
||||||
|
let arg = typed_f
|
||||||
|
.arguments
|
||||||
|
.first()
|
||||||
|
.expect("has exactly one argument")
|
||||||
|
.to_owned();
|
||||||
|
vec![ArgVia {
|
||||||
|
arg: TypedArg {
|
||||||
|
tipo,
|
||||||
|
annotation,
|
||||||
|
..arg
|
||||||
|
},
|
||||||
|
via,
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
None => vec![],
|
||||||
|
},
|
||||||
|
return_annotation: typed_f.return_annotation,
|
||||||
|
return_type: typed_f.return_type,
|
||||||
|
body: typed_f.body,
|
||||||
|
on_test_failure: typed_f.on_test_failure,
|
||||||
|
end_position: typed_f.end_position,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
Definition::TypeAlias(TypeAlias {
|
Definition::TypeAlias(TypeAlias {
|
||||||
doc,
|
doc,
|
||||||
location,
|
location,
|
||||||
|
@ -709,7 +838,8 @@ 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::fuzzer(
|
expected: Type::generator(
|
||||||
|
Type::void(),
|
||||||
expected_inner_type
|
expected_inner_type
|
||||||
.clone()
|
.clone()
|
||||||
.unwrap_or_else(|| Type::generic_var(0)),
|
.unwrap_or_else(|| Type::generic_var(0)),
|
||||||
|
@ -733,7 +863,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() == 1").borrow() {
|
match args.first().expect("args.len() == 2 && args[0].is_void()").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");
|
||||||
|
|
||||||
|
@ -748,7 +878,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::fuzzer(wrapped.clone()),
|
Type::generator(Type::void(), wrapped.clone()),
|
||||||
*location,
|
*location,
|
||||||
false,
|
false,
|
||||||
)?;
|
)?;
|
||||||
|
@ -777,6 +907,75 @@ fn infer_fuzzer(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::result_large_err)]
|
||||||
|
fn infer_sampler(
|
||||||
|
environment: &mut Environment<'_>,
|
||||||
|
expected_inner_type: Option<Rc<Type>>,
|
||||||
|
tipo: &Rc<Type>,
|
||||||
|
location: &Span,
|
||||||
|
) -> Result<(Annotation, Rc<Type>), Error> {
|
||||||
|
let could_not_unify = || Error::CouldNotUnify {
|
||||||
|
location: *location,
|
||||||
|
expected: Type::generator(
|
||||||
|
Type::int(),
|
||||||
|
expected_inner_type
|
||||||
|
.clone()
|
||||||
|
.unwrap_or_else(|| Type::generic_var(0)),
|
||||||
|
),
|
||||||
|
given: tipo.clone(),
|
||||||
|
situation: None,
|
||||||
|
rigid_type_names: HashMap::new(),
|
||||||
|
};
|
||||||
|
|
||||||
|
match tipo.borrow() {
|
||||||
|
Type::Fn {
|
||||||
|
ret,
|
||||||
|
args: _,
|
||||||
|
alias: _,
|
||||||
|
} => match ret.borrow() {
|
||||||
|
Type::App {
|
||||||
|
module,
|
||||||
|
name,
|
||||||
|
args,
|
||||||
|
public: _,
|
||||||
|
contains_opaque: _,
|
||||||
|
alias: _,
|
||||||
|
} if module.is_empty() && name == "Option" && args.len() == 1 => {
|
||||||
|
match args.first().expect("args.len() == 2 && args[0].is_int()").borrow() {
|
||||||
|
Type::Tuple { elems, .. } if elems.len() == 2 => {
|
||||||
|
let wrapped = elems.get(1).expect("Tuple has two elements");
|
||||||
|
|
||||||
|
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() {
|
||||||
|
TypeVar::Link { tipo } => infer_sampler(
|
||||||
|
environment,
|
||||||
|
expected_inner_type,
|
||||||
|
&Type::with_alias(tipo.clone(), alias.clone()),
|
||||||
|
location,
|
||||||
|
),
|
||||||
|
_ => Err(Error::GenericLeftAtBoundary {
|
||||||
|
location: *location,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
|
||||||
|
Type::App { .. } | Type::Tuple { .. } | Type::Pair { .. } => Err(could_not_unify()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[allow(clippy::result_large_err)]
|
#[allow(clippy::result_large_err)]
|
||||||
fn annotate_fuzzer(tipo: &Type, location: &Span) -> Result<Annotation, Error> {
|
fn annotate_fuzzer(tipo: &Type, location: &Span) -> Result<Annotation, Error> {
|
||||||
match tipo {
|
match tipo {
|
||||||
|
@ -837,75 +1036,6 @@ fn annotate_fuzzer(tipo: &Type, location: &Span) -> Result<Annotation, Error> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::result_large_err)]
|
|
||||||
fn infer_scaled_fuzzer(
|
|
||||||
environment: &mut Environment<'_>,
|
|
||||||
expected_inner_type: Option<Rc<Type>>,
|
|
||||||
tipo: &Rc<Type>,
|
|
||||||
location: &Span,
|
|
||||||
) -> Result<(Annotation, Rc<Type>), Error> {
|
|
||||||
let could_not_unify = || Error::CouldNotUnify {
|
|
||||||
location: *location,
|
|
||||||
expected: Type::scaled_fuzzer(
|
|
||||||
expected_inner_type
|
|
||||||
.clone()
|
|
||||||
.unwrap_or_else(|| Type::generic_var(0)),
|
|
||||||
),
|
|
||||||
given: tipo.clone(),
|
|
||||||
situation: None,
|
|
||||||
rigid_type_names: Default::default(),
|
|
||||||
};
|
|
||||||
|
|
||||||
match tipo.borrow() {
|
|
||||||
Type::Fn { ret, args, .. } => {
|
|
||||||
// Check if this is a ScaledFuzzer (fn(PRNG, Int) -> Option<(PRNG, a)>)
|
|
||||||
if args.len() == 2 {
|
|
||||||
match ret.borrow() {
|
|
||||||
Type::App {
|
|
||||||
module,
|
|
||||||
name,
|
|
||||||
args: ret_args,
|
|
||||||
..
|
|
||||||
} if module.is_empty() && name == "Option" && ret_args.len() == 1 => {
|
|
||||||
if let Type::Tuple { elems, .. } = ret_args[0].borrow() {
|
|
||||||
if elems.len() == 2 {
|
|
||||||
let wrapped = &elems[1];
|
|
||||||
|
|
||||||
// Unify with expected ScaledFuzzer type
|
|
||||||
environment.unify(
|
|
||||||
tipo.clone(),
|
|
||||||
Type::scaled_fuzzer(wrapped.clone()),
|
|
||||||
*location,
|
|
||||||
false,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
return Ok((annotate_fuzzer(wrapped, location)?, wrapped.clone()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Err(could_not_unify())
|
|
||||||
}
|
|
||||||
|
|
||||||
Type::Var { tipo, alias } => match &*tipo.deref().borrow() {
|
|
||||||
TypeVar::Link { tipo } => infer_scaled_fuzzer(
|
|
||||||
environment,
|
|
||||||
expected_inner_type,
|
|
||||||
&Type::with_alias(tipo.clone(), alias.clone()),
|
|
||||||
location,
|
|
||||||
),
|
|
||||||
_ => Err(Error::GenericLeftAtBoundary {
|
|
||||||
location: *location,
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
|
|
||||||
Type::App { .. } | Type::Tuple { .. } | Type::Pair { .. } => Err(could_not_unify()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn put_params_in_scope<'a>(
|
fn put_params_in_scope<'a>(
|
||||||
name: &'_ str,
|
name: &'_ str,
|
||||||
environment: &'a mut Environment,
|
environment: &'a mut Environment,
|
||||||
|
|
|
@ -540,6 +540,7 @@ fn find_data_type(name: &str, definitions: &[TypedDefinition]) -> Option<TypedDa
|
||||||
| Definition::TypeAlias { .. }
|
| Definition::TypeAlias { .. }
|
||||||
| Definition::Use { .. }
|
| Definition::Use { .. }
|
||||||
| Definition::ModuleConstant { .. }
|
| Definition::ModuleConstant { .. }
|
||||||
|
| Definition::Benchmark { .. }
|
||||||
| Definition::Test { .. } => continue,
|
| Definition::Test { .. } => continue,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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()),
|
TestResult::Benchmark(_) => ("benchmark".to_string(), PathBuf::new(), String::new()), // todo
|
||||||
};
|
};
|
||||||
|
|
||||||
Error::TestFailure {
|
Error::TestFailure {
|
||||||
|
|
|
@ -302,7 +302,7 @@ where
|
||||||
match_tests: Option<Vec<String>>,
|
match_tests: Option<Vec<String>>,
|
||||||
exact_match: bool,
|
exact_match: bool,
|
||||||
seed: u32,
|
seed: u32,
|
||||||
property_max_success: usize,
|
times_to_run: usize,
|
||||||
env: Option<String>,
|
env: Option<String>,
|
||||||
output: PathBuf,
|
output: PathBuf,
|
||||||
) -> Result<(), Vec<Error>> {
|
) -> Result<(), Vec<Error>> {
|
||||||
|
@ -313,7 +313,7 @@ where
|
||||||
match_tests,
|
match_tests,
|
||||||
exact_match,
|
exact_match,
|
||||||
seed,
|
seed,
|
||||||
property_max_success,
|
times_to_run,
|
||||||
output,
|
output,
|
||||||
},
|
},
|
||||||
blueprint_path: self.blueprint_path(None),
|
blueprint_path: self.blueprint_path(None),
|
||||||
|
@ -470,16 +470,17 @@ where
|
||||||
match_tests,
|
match_tests,
|
||||||
exact_match,
|
exact_match,
|
||||||
seed,
|
seed,
|
||||||
property_max_success,
|
times_to_run,
|
||||||
output,
|
output,
|
||||||
} => {
|
} => {
|
||||||
let tests = self.collect_tests(false, match_tests, exact_match, options.tracing)?;
|
// todo - collect benchmarks
|
||||||
|
let tests = self.collect_benchmarks(false, match_tests, exact_match, options.tracing)?;
|
||||||
|
|
||||||
if !tests.is_empty() {
|
if !tests.is_empty() {
|
||||||
self.event_listener.handle_event(Event::RunningBenchmarks);
|
self.event_listener.handle_event(Event::RunningBenchmarks);
|
||||||
}
|
}
|
||||||
|
|
||||||
let tests = self.run_benchmarks(tests, seed, property_max_success);
|
let tests = self.run_benchmarks(tests, seed, times_to_run);
|
||||||
|
|
||||||
let errors: Vec<Error> = tests
|
let errors: Vec<Error> = tests
|
||||||
.iter()
|
.iter()
|
||||||
|
@ -994,6 +995,107 @@ where
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn collect_benchmarks(
|
||||||
|
&mut self,
|
||||||
|
verbose: bool,
|
||||||
|
match_tests: Option<Vec<String>>,
|
||||||
|
exact_match: bool,
|
||||||
|
tracing: Tracing,
|
||||||
|
) -> Result<Vec<Test>, Error> {
|
||||||
|
let mut scripts = Vec::new();
|
||||||
|
|
||||||
|
let match_tests = match_tests.map(|mt| {
|
||||||
|
mt.into_iter()
|
||||||
|
.map(|match_test| {
|
||||||
|
let mut match_split_dot = match_test.split('.');
|
||||||
|
|
||||||
|
let match_module = if match_test.contains('.') || match_test.contains('/') {
|
||||||
|
match_split_dot.next().unwrap_or("")
|
||||||
|
} else {
|
||||||
|
""
|
||||||
|
};
|
||||||
|
|
||||||
|
let match_names = match_split_dot.next().map(|names| {
|
||||||
|
let names = names.replace(&['{', '}'][..], "");
|
||||||
|
|
||||||
|
let names_split_comma = names.split(',');
|
||||||
|
|
||||||
|
names_split_comma.map(str::to_string).collect()
|
||||||
|
});
|
||||||
|
|
||||||
|
(match_module.to_string(), match_names)
|
||||||
|
})
|
||||||
|
.collect::<Vec<(String, Option<Vec<String>>)>>()
|
||||||
|
});
|
||||||
|
|
||||||
|
for checked_module in self.checked_modules.values() {
|
||||||
|
if checked_module.package != self.config.name.to_string() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for def in checked_module.ast.definitions() {
|
||||||
|
if let Definition::Benchmark(func) = def {
|
||||||
|
if let Some(match_tests) = &match_tests {
|
||||||
|
let is_match = match_tests.iter().any(|(module, names)| {
|
||||||
|
let matched_module =
|
||||||
|
module.is_empty() || checked_module.name.contains(module);
|
||||||
|
|
||||||
|
let matched_name = match names {
|
||||||
|
None => true,
|
||||||
|
Some(names) => names.iter().any(|name| {
|
||||||
|
if exact_match {
|
||||||
|
name == &func.name
|
||||||
|
} else {
|
||||||
|
func.name.contains(name)
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
|
matched_module && matched_name
|
||||||
|
});
|
||||||
|
|
||||||
|
if is_match {
|
||||||
|
scripts.push((
|
||||||
|
checked_module.input_path.clone(),
|
||||||
|
checked_module.name.clone(),
|
||||||
|
func,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
scripts.push((
|
||||||
|
checked_module.input_path.clone(),
|
||||||
|
checked_module.name.clone(),
|
||||||
|
func,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut generator = self.new_generator(tracing);
|
||||||
|
|
||||||
|
let mut tests = Vec::new();
|
||||||
|
|
||||||
|
for (input_path, module_name, test) in scripts.into_iter() {
|
||||||
|
if verbose {
|
||||||
|
// TODO: We may want to handle the event listener differently for benchmarks
|
||||||
|
self.event_listener.handle_event(Event::GeneratingUPLCFor {
|
||||||
|
name: test.name.clone(),
|
||||||
|
path: input_path.clone(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
tests.push(Test::from_function_definition(
|
||||||
|
&mut generator,
|
||||||
|
test.to_owned(),
|
||||||
|
module_name,
|
||||||
|
input_path,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(tests)
|
||||||
|
}
|
||||||
|
|
||||||
fn collect_tests(
|
fn collect_tests(
|
||||||
&mut self,
|
&mut self,
|
||||||
verbose: bool,
|
verbose: bool,
|
||||||
|
@ -1113,6 +1215,7 @@ where
|
||||||
Test::PropertyTest(property_test) => {
|
Test::PropertyTest(property_test) => {
|
||||||
property_test.run(seed, property_max_success, plutus_version)
|
property_test.run(seed, property_max_success, plutus_version)
|
||||||
}
|
}
|
||||||
|
Test::Benchmark(_) => unreachable!("Benchmarks cannot be run in PBT.")
|
||||||
})
|
})
|
||||||
.collect::<Vec<TestResult<(Constant, Rc<Type>), PlutusData>>>()
|
.collect::<Vec<TestResult<(Constant, Rc<Type>), PlutusData>>>()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
@ -1134,8 +1237,8 @@ where
|
||||||
tests
|
tests
|
||||||
.into_par_iter()
|
.into_par_iter()
|
||||||
.flat_map(|test| match test {
|
.flat_map(|test| match test {
|
||||||
Test::UnitTest(_) => Vec::new(),
|
Test::UnitTest(_) | Test::PropertyTest(_) => unreachable!("Tests cannot be ran during benchmarking."),
|
||||||
Test::PropertyTest(property_test) => property_test
|
Test::Benchmark(benchmark) => benchmark
|
||||||
.benchmark(seed, property_max_success, plutus_version)
|
.benchmark(seed, property_max_success, plutus_version)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(TestResult::Benchmark)
|
.map(TestResult::Benchmark)
|
||||||
|
|
|
@ -33,7 +33,7 @@ pub enum CodeGenMode {
|
||||||
match_tests: Option<Vec<String>>,
|
match_tests: Option<Vec<String>>,
|
||||||
exact_match: bool,
|
exact_match: bool,
|
||||||
seed: u32,
|
seed: u32,
|
||||||
property_max_success: usize,
|
times_to_run: usize,
|
||||||
output: PathBuf,
|
output: PathBuf,
|
||||||
},
|
},
|
||||||
NoOp,
|
NoOp,
|
||||||
|
|
|
@ -112,8 +112,10 @@ 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(prng: PRNG) -> Option<(PRNG, Int)> {
|
fn(v: Void, prng: PRNG) -> Option<(PRNG, Int)> {
|
||||||
when prng is {
|
when prng is {
|
||||||
Seeded { seed, choices } -> {
|
Seeded { seed, choices } -> {
|
||||||
let choice =
|
let choice =
|
||||||
|
@ -161,21 +163,21 @@ mod test {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn constant(a: a) -> Fuzzer<a> {
|
pub fn constant(a: a) -> Fuzzer<a> {
|
||||||
fn(s0) { Some((s0, a)) }
|
fn(v, 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(s0) {
|
fn(v, s0) {
|
||||||
when fuzz_a(s0) is {
|
when fuzz_a(v, s0) is {
|
||||||
Some((s1, a)) -> f(a)(s1)
|
Some((s1, a)) -> f(a)(v, 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(s0) {
|
fn(v, s0) {
|
||||||
when fuzz_a(s0) is {
|
when fuzz_a(v, s0) is {
|
||||||
Some((s1, a)) -> Some((s1, f(a)))
|
Some((s1, a)) -> Some((s1, f(a)))
|
||||||
None -> None
|
None -> None
|
||||||
}
|
}
|
||||||
|
@ -183,10 +185,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(s0) {
|
fn(v, s0) {
|
||||||
when fuzz_a(s0) is {
|
when fuzz_a(v, s0) is {
|
||||||
Some((s1, a)) ->
|
Some((s1, a)) ->
|
||||||
when fuzz_b(s1) is {
|
when fuzz_b(Void, s1) is {
|
||||||
Some((s2, b)) -> Some((s2, f(a, b)))
|
Some((s2, b)) -> Some((s2, f(a, b)))
|
||||||
None -> None
|
None -> None
|
||||||
}
|
}
|
||||||
|
@ -214,6 +216,9 @@ mod test {
|
||||||
(Test::UnitTest(..), _) => {
|
(Test::UnitTest(..), _) => {
|
||||||
panic!("Expected to yield a PropertyTest but found a UnitTest")
|
panic!("Expected to yield a PropertyTest but found a UnitTest")
|
||||||
}
|
}
|
||||||
|
(Test::Benchmark(..), _) => {
|
||||||
|
panic!("Expected to yield a PropertyTest but found a Benchmark")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,9 +17,9 @@ pub struct Args {
|
||||||
#[clap(long)]
|
#[clap(long)]
|
||||||
seed: Option<u32>,
|
seed: Option<u32>,
|
||||||
|
|
||||||
/// Maximum number of successful test run for considering a property-based test valid.
|
/// How many times we will run each benchmark in the relevant project.
|
||||||
#[clap(long, default_value_t = PropertyTest::DEFAULT_MAX_SUCCESS)]
|
#[clap(long, default_value_t = PropertyTest::DEFAULT_MAX_SUCCESS)]
|
||||||
max_success: usize,
|
times_to_run: usize,
|
||||||
|
|
||||||
/// Only run tests if they match any of these strings.
|
/// Only run tests if they match any of these strings.
|
||||||
/// You can match a module with `-m aiken/list` or `-m list`.
|
/// You can match a module with `-m aiken/list` or `-m list`.
|
||||||
|
@ -46,7 +46,7 @@ pub fn exec(
|
||||||
match_tests,
|
match_tests,
|
||||||
exact_match,
|
exact_match,
|
||||||
seed,
|
seed,
|
||||||
max_success,
|
times_to_run,
|
||||||
env,
|
env,
|
||||||
output,
|
output,
|
||||||
}: Args,
|
}: Args,
|
||||||
|
@ -65,7 +65,7 @@ pub fn exec(
|
||||||
match_tests.clone(),
|
match_tests.clone(),
|
||||||
exact_match,
|
exact_match,
|
||||||
seed,
|
seed,
|
||||||
max_success,
|
times_to_run,
|
||||||
env.clone(),
|
env.clone(),
|
||||||
output.clone(),
|
output.clone(),
|
||||||
)
|
)
|
||||||
|
|
Loading…
Reference in New Issue