initial thoughts
This commit is contained in:
parent
982eff449e
commit
c03d12b98c
|
@ -3,7 +3,7 @@ use chumsky::prelude::*;
|
|||
use crate::{
|
||||
ast,
|
||||
expr::UntypedExpr,
|
||||
parser::{error::ParseError, expr, token::Token},
|
||||
parser::{error::ParseError, expr, token::Token, definition::function::param},
|
||||
};
|
||||
|
||||
pub fn parser() -> impl Parser<Token, ast::UntypedDefinition, Error = ParseError> {
|
||||
|
@ -13,7 +13,15 @@ pub fn parser() -> impl Parser<Token, ast::UntypedDefinition, Error = ParseError
|
|||
.or_not()
|
||||
.then_ignore(just(Token::Test))
|
||||
.then(select! {Token::Name {name} => name})
|
||||
.then(
|
||||
param(false)
|
||||
.separated_by(just(Token::Comma))
|
||||
.allow_trailing()
|
||||
.delimited_by(just(Token::LeftParen), just(Token::RightParen))
|
||||
.map_with_span(|arguments, span| (arguments, span)),
|
||||
)
|
||||
.then_ignore(just(Token::LeftParen))
|
||||
|
||||
.then_ignore(just(Token::RightParen))
|
||||
.then(just(Token::Fail).ignored().or_not())
|
||||
.map_with_span(|name, span| (name, span))
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
build
|
|
@ -0,0 +1,114 @@
|
|||
# Moonrat
|
||||
|
||||
> Hedgehog's spineless cousin
|
||||
|
||||
## Aims
|
||||
|
||||
Property based testing for aiken inspired by hedgehog and elm-test.
|
||||
|
||||
Aims:
|
||||
|
||||
- Default gen and shrinking auto derived for any types
|
||||
- Support custom gen/shrinking
|
||||
- Friendly output (progress, sensible feedback such as diffs on large data)
|
||||
- Reasonably speedy
|
||||
|
||||
Non-aims:
|
||||
|
||||
- e2e testing.
|
||||
This is intended for functions rather than testing full txs against validators.
|
||||
Although it should still be possible, it is not our aim here to make writing and testing txs ergonomic.
|
||||
|
||||
|
||||
## Interface
|
||||
|
||||
An aiken file
|
||||
|
||||
```aiken
|
||||
// my_tests.ak
|
||||
|
||||
type T0 {
|
||||
f0 : Int,
|
||||
...
|
||||
}
|
||||
|
||||
fn gen_t0(seed : Int, complexity : Int) -> T0 {
|
||||
...
|
||||
}
|
||||
|
||||
fn shrink_t0(x : T0) -> List<T0> {
|
||||
// TODO : what should the signature of this be?!
|
||||
...
|
||||
}
|
||||
|
||||
type T1 {
|
||||
f0 : Int,
|
||||
...
|
||||
}
|
||||
|
||||
test prop_x (
|
||||
a0 : T0 via (gen_t0(0), shrink_t0),
|
||||
a1 : T0 via (gen_t0(1), shrink_t0),
|
||||
a2 : T0,
|
||||
a2 : T1,
|
||||
) {
|
||||
todo!
|
||||
}
|
||||
```
|
||||
|
||||
Comments on the sample.
|
||||
`prop_x` is our test - now supporting arguments.
|
||||
There is new syntax `via`.
|
||||
We have a custom generator and shrinker for `T0` which we may or may not use.
|
||||
In the absence of a specified gen/shrink pair, the default, autoderived one is used.
|
||||
|
||||
Run 100 times
|
||||
```
|
||||
aiken check -m "my_lib/my_test.{prop_x}"
|
||||
```
|
||||
|
||||
Run 1000 cases with a specified seed and shrink limit
|
||||
```
|
||||
aiken check --repeat 1000 --seed 123212123 --shrink-limit 5
|
||||
```
|
||||
|
||||
Reporting:
|
||||
```sample
|
||||
Testing ...
|
||||
|
||||
my_test
|
||||
prop_x PASS [100/100]
|
||||
```
|
||||
|
||||
```sample
|
||||
Testing ...
|
||||
|
||||
my_test
|
||||
prop_x FAIL (after 16 tests and 5 shrinks):
|
||||
a0 = T0 { f0 : 120201, ... }
|
||||
a1 = T0 { ... }
|
||||
...
|
||||
|
||||
RHS = True
|
||||
LHS = False
|
||||
|
||||
seed = 123212123
|
||||
|
||||
Rerun with
|
||||
aiken check -m "my_lib/my_test.{prop_x}" --args " [ T0 { }] ... "
|
||||
```
|
||||
|
||||
## Functionality
|
||||
|
||||
Aiken compiler finds all tests.
|
||||
Any tests with args are assumed subject to property based testing.
|
||||
|
||||
[Property config](https://hackage.haskell.org/package/hedgehog-1.4/docs/Hedgehog-Internal-Property.html#t:PropertyConfig) is global, rather than local.
|
||||
|
||||
The test is compiled as if it were a parametrized validator.
|
||||
Separate gen and shrink functions are also compiled.
|
||||
|
||||
To evaluate the test, the generator(s) are run to generate input for the test.
|
||||
Then the args are applied, and the code evaluated.
|
||||
On success this is repeated until `repeat` number of successes.
|
||||
On failure, the shrinker is employed to seek a simpler failure case.
|
|
@ -0,0 +1,16 @@
|
|||
# This file was generated by Aiken
|
||||
# You typically do not need to edit this file
|
||||
|
||||
[[requirements]]
|
||||
name = "aiken-lang/stdlib"
|
||||
version = "main"
|
||||
source = "github"
|
||||
|
||||
[[packages]]
|
||||
name = "aiken-lang/stdlib"
|
||||
version = "main"
|
||||
requirements = []
|
||||
source = "github"
|
||||
|
||||
[etags]
|
||||
"aiken-lang/stdlib@main" = [{ secs_since_epoch = 1707160390, nanos_since_epoch = 895305443 }, "cf946239d3dd481ed41f20e56bf24910b5229ea35aa171a708edc2a47fc20a7b"]
|
|
@ -0,0 +1,8 @@
|
|||
name = "aiken-lang/moonrat"
|
||||
version = "0.0.0"
|
||||
description = ""
|
||||
|
||||
[[dependencies]]
|
||||
name = 'aiken-lang/stdlib'
|
||||
version = 'main'
|
||||
source = 'github'
|
|
@ -0,0 +1,4 @@
|
|||
|
||||
test test_with_arg(x : Int) {
|
||||
x - x == 0
|
||||
}
|
|
@ -0,0 +1,89 @@
|
|||
{
|
||||
"preamble": {
|
||||
"title": "aiken-lang/acceptance_test_089",
|
||||
"version": "0.0.0",
|
||||
"plutusVersion": "v2",
|
||||
"compiler": {
|
||||
"name": "Aiken",
|
||||
"version": "v1.0.24-alpha+982eff4"
|
||||
}
|
||||
},
|
||||
"validators": [
|
||||
{
|
||||
"title": "test2.simple_oneshot",
|
||||
"redeemer": {
|
||||
"title": "_r",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/Void"
|
||||
}
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"title": "utxo_ref",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/aiken~1transaction~1OutputReference"
|
||||
}
|
||||
}
|
||||
],
|
||||
"compiledCode": "58d40100003232323232323232322225333006323232323232533300c3370e900018058018991919299980799b8748000c0380044c8c94ccc044cdc3a40000022944528180780099801002119baf3004300e00100d163300100323375e6006601a00201844646600200200644a6660280022980103d87a8000132325333013300500213374a90001980b80125eb804cc010010004c060008c0580048c04800458dd61808000980400198070009807001180600098020008a4c26cac4600a6ea80048c00cdd5000ab9a5573aaae7955cfaba05742ae89",
|
||||
"hash": "dd850cc95e173d7dbb3357a4a021afc350f405a3cc2e85ace58bfe8d"
|
||||
}
|
||||
],
|
||||
"definitions": {
|
||||
"ByteArray": {
|
||||
"dataType": "bytes"
|
||||
},
|
||||
"Int": {
|
||||
"dataType": "integer"
|
||||
},
|
||||
"Void": {
|
||||
"title": "Unit",
|
||||
"description": "The nullary constructor.",
|
||||
"anyOf": [
|
||||
{
|
||||
"dataType": "constructor",
|
||||
"index": 0,
|
||||
"fields": []
|
||||
}
|
||||
]
|
||||
},
|
||||
"aiken/transaction/OutputReference": {
|
||||
"title": "OutputReference",
|
||||
"description": "An `OutputReference` is a unique reference to an output on-chain. The `output_index`\n corresponds to the position in the output list of the transaction (identified by its id)\n that produced that output",
|
||||
"anyOf": [
|
||||
{
|
||||
"title": "OutputReference",
|
||||
"dataType": "constructor",
|
||||
"index": 0,
|
||||
"fields": [
|
||||
{
|
||||
"title": "transaction_id",
|
||||
"$ref": "#/definitions/aiken~1transaction~1TransactionId"
|
||||
},
|
||||
{
|
||||
"title": "output_index",
|
||||
"$ref": "#/definitions/Int"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"aiken/transaction/TransactionId": {
|
||||
"title": "TransactionId",
|
||||
"description": "A unique transaction identifier, as the hash of a transaction body. Note that the transaction id\n isn't a direct hash of the `Transaction` as visible on-chain. Rather, they correspond to hash\n digests of transaction body as they are serialized on the network.",
|
||||
"anyOf": [
|
||||
{
|
||||
"title": "TransactionId",
|
||||
"dataType": "constructor",
|
||||
"index": 0,
|
||||
"fields": [
|
||||
{
|
||||
"title": "hash",
|
||||
"$ref": "#/definitions/ByteArray"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue