Add preliminary plumbing to run property test through the CLI.
This is very very rough at the moment. But it does a couple of thing:
1. The 'ArgVia' now contains an Expr/TypedExpr which should unify to a Fuzzer. This is to avoid having to introduce custom logic to handle fuzzer referencing. So this now accepts function call, field access etc.. so long as they unify to the right thing.
2. I've done quite a lot of cleanup in aiken-project mostly around the tests and the naming surrounding them. What we used to call 'Script' is now called 'Test' and is an enum between UnitTest (ex-Script) and PropertyTest. I've moved some boilerplate and relevant function under those module Impl.
3. I've completed the end-to-end pipeline of:
- Compiling the property test
- Compiling the fuzzer
- Generating an initial seed
- Running property tests sequentially, threading the seed through each step.
An interesting finding is that, I had to wrap the prop test in a similar wrapper that we use for validator, to ensure we convert primitive types wrapped in Data back to UPLC terms. This is necessary because the fuzzer return a ProtoPair (and soon an Array) which holds 'Data'.
At the moment, we do nothing with the size, though the size should ideally grow after each iteration (up to a certain cap).
In addition, there are a couple of todo/fixme that I left in the code as reminders of what's left to do beyond the obvious (error and success reporting, testing, etc..)
This commit is contained in:
@@ -183,7 +183,11 @@ pub type UntypedTypeAlias = TypeAlias<()>;
|
||||
|
||||
impl TypedTest {
|
||||
pub fn test_hint(&self) -> Option<(BinOp, Box<TypedExpr>, Box<TypedExpr>)> {
|
||||
do_test_hint(&self.body)
|
||||
if self.arguments.is_empty() {
|
||||
do_test_hint(&self.body)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -367,17 +371,11 @@ pub struct Validator<T, Expr> {
|
||||
pub params: Vec<Arg<T>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct DefinitionIdentifier {
|
||||
pub module: Option<String>,
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
pub type TypedDefinition = Definition<Rc<Type>, TypedExpr, String, ()>;
|
||||
pub type UntypedDefinition = Definition<(), UntypedExpr, (), DefinitionIdentifier>;
|
||||
pub type TypedDefinition = Definition<Rc<Type>, TypedExpr, String>;
|
||||
pub type UntypedDefinition = Definition<(), UntypedExpr, ()>;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum Definition<T, Expr, PackageName, Ann> {
|
||||
pub enum Definition<T, Expr, PackageName> {
|
||||
Fn(Function<T, Expr, Arg<T>>),
|
||||
|
||||
TypeAlias(TypeAlias<T>),
|
||||
@@ -388,12 +386,12 @@ pub enum Definition<T, Expr, PackageName, Ann> {
|
||||
|
||||
ModuleConstant(ModuleConstant<T>),
|
||||
|
||||
Test(Function<T, Expr, ArgVia<T, Ann>>),
|
||||
Test(Function<T, Expr, ArgVia<T, Expr>>),
|
||||
|
||||
Validator(Validator<T, Expr>),
|
||||
}
|
||||
|
||||
impl<A, B, C, D> Definition<A, B, C, D> {
|
||||
impl<A, B, C> Definition<A, B, C> {
|
||||
pub fn location(&self) -> Span {
|
||||
match self {
|
||||
Definition::Fn(Function { location, .. })
|
||||
@@ -643,14 +641,14 @@ impl<A> Arg<A> {
|
||||
}
|
||||
}
|
||||
|
||||
pub type TypedArgVia = ArgVia<Rc<Type>, ()>;
|
||||
pub type UntypedArgVia = ArgVia<(), DefinitionIdentifier>;
|
||||
pub type TypedArgVia = ArgVia<Rc<Type>, TypedExpr>;
|
||||
pub type UntypedArgVia = ArgVia<(), UntypedExpr>;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct ArgVia<T, Ann> {
|
||||
pub struct ArgVia<T, Expr> {
|
||||
pub arg_name: ArgName,
|
||||
pub location: Span,
|
||||
pub via: Ann,
|
||||
pub via: Expr,
|
||||
pub tipo: T,
|
||||
}
|
||||
|
||||
@@ -666,17 +664,6 @@ impl<T, Ann> From<ArgVia<T, Ann>> for Arg<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<TypedArg> for TypedArgVia {
|
||||
fn from(arg: TypedArg) -> TypedArgVia {
|
||||
ArgVia {
|
||||
arg_name: arg.arg_name,
|
||||
tipo: arg.tipo,
|
||||
location: arg.location,
|
||||
via: (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum ArgName {
|
||||
Discarded {
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
use crate::{
|
||||
ast::{
|
||||
Annotation, Arg, ArgName, ArgVia, AssignmentKind, BinOp, ByteArrayFormatPreference,
|
||||
CallArg, ClauseGuard, Constant, CurveType, DataType, Definition, DefinitionIdentifier,
|
||||
Function, IfBranch, LogicalOpChainKind, ModuleConstant, Pattern, RecordConstructor,
|
||||
RecordConstructorArg, RecordUpdateSpread, Span, TraceKind, TypeAlias, TypedArg, UnOp,
|
||||
UnqualifiedImport, UntypedArg, UntypedArgVia, UntypedClause, UntypedClauseGuard,
|
||||
UntypedDefinition, UntypedFunction, UntypedModule, UntypedPattern, UntypedRecordUpdateArg,
|
||||
Use, Validator, CAPTURE_VARIABLE,
|
||||
CallArg, ClauseGuard, Constant, CurveType, DataType, Definition, Function, IfBranch,
|
||||
LogicalOpChainKind, ModuleConstant, Pattern, RecordConstructor, RecordConstructorArg,
|
||||
RecordUpdateSpread, Span, TraceKind, TypeAlias, TypedArg, UnOp, UnqualifiedImport,
|
||||
UntypedArg, UntypedArgVia, UntypedClause, UntypedClauseGuard, UntypedDefinition,
|
||||
UntypedFunction, UntypedModule, UntypedPattern, UntypedRecordUpdateArg, Use, Validator,
|
||||
CAPTURE_VARIABLE,
|
||||
},
|
||||
docvec,
|
||||
expr::{FnStyle, UntypedExpr, DEFAULT_ERROR_STR, DEFAULT_TODO_STR},
|
||||
@@ -471,19 +471,17 @@ impl<'comments> Formatter<'comments> {
|
||||
commented(doc, comments)
|
||||
}
|
||||
|
||||
fn fn_arg_via<'a, A>(&mut self, arg: &'a ArgVia<A, DefinitionIdentifier>) -> Document<'a> {
|
||||
fn fn_arg_via<'a, A>(&mut self, arg: &'a ArgVia<A, UntypedExpr>) -> Document<'a> {
|
||||
let comments = self.pop_comments(arg.location.start);
|
||||
|
||||
let doc_comments = self.doc_comments(arg.location.start);
|
||||
|
||||
let doc = arg.arg_name.to_doc().append(" via ");
|
||||
|
||||
let doc = match arg.via.module {
|
||||
Some(ref module) => doc.append(module.to_doc()).append("."),
|
||||
None => doc,
|
||||
}
|
||||
.append(arg.via.name.to_doc())
|
||||
.group();
|
||||
let doc = arg
|
||||
.arg_name
|
||||
.to_doc()
|
||||
.append(" via ")
|
||||
.append(self.expr(&arg.via, false))
|
||||
.group();
|
||||
|
||||
let doc = doc_comments.append(doc.group()).group();
|
||||
|
||||
|
||||
@@ -198,7 +198,7 @@ impl<'a> CodeGenerator<'a> {
|
||||
self.finalize(term)
|
||||
}
|
||||
|
||||
pub fn generate_test(&mut self, test_body: &TypedExpr, module_name: &String) -> Program<Name> {
|
||||
pub fn generate_raw(&mut self, test_body: &TypedExpr, module_name: &String) -> Program<Name> {
|
||||
let mut air_tree = self.build(test_body, module_name, &[]);
|
||||
|
||||
air_tree = AirTree::no_op(air_tree);
|
||||
|
||||
@@ -3,7 +3,12 @@ use chumsky::prelude::*;
|
||||
use crate::{
|
||||
ast,
|
||||
expr::UntypedExpr,
|
||||
parser::{error::ParseError, expr, token::Token},
|
||||
parser::{
|
||||
chain::{call::parser as call, field_access, tuple_index::parser as tuple_index, Chain},
|
||||
error::ParseError,
|
||||
expr::{self, var},
|
||||
token::Token,
|
||||
},
|
||||
};
|
||||
|
||||
pub fn parser() -> impl Parser<Token, ast::UntypedDefinition, Error = ParseError> {
|
||||
@@ -63,20 +68,33 @@ pub fn via() -> impl Parser<Token, ast::UntypedArgVia, Error = ParseError> {
|
||||
}),
|
||||
))
|
||||
.then_ignore(just(Token::Via))
|
||||
.then(
|
||||
select! { Token::Name { name } => name }
|
||||
.then_ignore(just(Token::Dot))
|
||||
.or_not(),
|
||||
)
|
||||
.then(select! { Token::Name { name } => name })
|
||||
.map_with_span(|((arg_name, module), name), location| ast::ArgVia {
|
||||
.then(fuzzer())
|
||||
.map_with_span(|(arg_name, via), location| ast::ArgVia {
|
||||
arg_name,
|
||||
via: ast::DefinitionIdentifier { module, name },
|
||||
via,
|
||||
tipo: (),
|
||||
location,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn fuzzer<'a>() -> impl Parser<Token, UntypedExpr, Error = ParseError> + 'a {
|
||||
recursive(|expression| {
|
||||
let chain = choice((
|
||||
tuple_index(),
|
||||
field_access::parser(),
|
||||
call(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;
|
||||
|
||||
@@ -2,7 +2,7 @@ use std::collections::HashMap;
|
||||
|
||||
use crate::{
|
||||
ast::{
|
||||
Annotation, Arg, ArgName, DataType, Definition, Function, Layer, ModuleConstant,
|
||||
Annotation, Arg, ArgName, ArgVia, DataType, Definition, Function, Layer, ModuleConstant,
|
||||
ModuleKind, RecordConstructor, RecordConstructorArg, Tracing, TypeAlias, TypedArg,
|
||||
TypedDefinition, TypedFunction, TypedModule, UntypedArg, UntypedDefinition, UntypedModule,
|
||||
Use, Validator,
|
||||
@@ -383,7 +383,7 @@ fn infer_definition(
|
||||
}
|
||||
}
|
||||
|
||||
let annotation = match f.arguments.first() {
|
||||
let (typed_via, annotation) = match f.arguments.first() {
|
||||
Some(arg) => {
|
||||
if f.arguments.len() > 1 {
|
||||
return Err(Error::IncorrectTestArity {
|
||||
@@ -392,12 +392,17 @@ fn infer_definition(
|
||||
});
|
||||
}
|
||||
|
||||
let ValueConstructor { tipo, .. } = ExprTyper::new(environment, lines, tracing)
|
||||
.infer_value_constructor(&arg.via.module, &arg.via.name, &arg.location)?;
|
||||
let typed_via =
|
||||
ExprTyper::new(environment, lines, tracing).infer(arg.via.clone())?;
|
||||
|
||||
Ok(Some(annotate_fuzzer(&tipo, &arg.location)?))
|
||||
let tipo = typed_via.tipo();
|
||||
|
||||
Ok((
|
||||
Some(typed_via),
|
||||
Some(annotate_fuzzer(&tipo, &arg.location)?),
|
||||
))
|
||||
}
|
||||
None => Ok(None),
|
||||
None => Ok((None, None)),
|
||||
}?;
|
||||
|
||||
let typed_f = infer_function(
|
||||
@@ -439,13 +444,25 @@ fn infer_definition(
|
||||
location: typed_f.location,
|
||||
name: typed_f.name,
|
||||
public: typed_f.public,
|
||||
arguments: match annotation {
|
||||
Some(_) => vec![typed_f
|
||||
.arguments
|
||||
.first()
|
||||
.expect("has exactly one argument")
|
||||
.to_owned()
|
||||
.into()],
|
||||
arguments: match typed_via {
|
||||
Some(via) => {
|
||||
let Arg {
|
||||
arg_name,
|
||||
location,
|
||||
tipo,
|
||||
..
|
||||
} = typed_f
|
||||
.arguments
|
||||
.first()
|
||||
.expect("has exactly one argument")
|
||||
.to_owned();
|
||||
vec![ArgVia {
|
||||
arg_name,
|
||||
location,
|
||||
tipo,
|
||||
via,
|
||||
}]
|
||||
}
|
||||
None => vec![],
|
||||
},
|
||||
return_annotation: typed_f.return_annotation,
|
||||
|
||||
Reference in New Issue
Block a user