Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

main into tx_fuzz #21

Merged
merged 4 commits into from
Oct 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions .github/workflows/continuous-integration.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,9 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: 🧰 Install Aiken
run: cargo install --verbose --git https://github.com/aiken-lang/aiken.git
- uses: aiken-lang/setup-aiken@v1
with:
version: v1.1.3
- run: aiken fmt --check
- run: aiken check -D
- run: aiken build
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,5 @@ artifacts/
build/
# Aiken's default documentation export
docs/
# Aiken's auto-generated lock file
aiken.lock
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,19 @@
# Changelog

## v2.1.0 - 2024-09-24

### Added

- More primitives for combining fuzzers:
- `either2`, `either3`, `either4`, `either5`, `either6`, `either7`, `either8`, `either9`
- `pick(xs: List<a>) -> Fuzzer<(Int, a)>`
- `label_if(str: String, predicate: Bool) -> Void`

### Changed

- Have bytearray primitives simplify towards the null bytestring (32 zeroes),
so that it's easier to identify in simplified examples.

## v2.0.0 - 2024-09-01

### Changed
Expand Down
16 changes: 0 additions & 16 deletions aiken.lock

This file was deleted.

2 changes: 1 addition & 1 deletion aiken.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name = "aiken-lang/fuzz"
version = "main"
compiler = "v1.1.0"
compiler = "v1.1.3"
plutus = "v3"
license = "Apache-2.0"
description = "A library for writing Aiken's fuzzers."
Expand Down
263 changes: 258 additions & 5 deletions lib/aiken/fuzz.ak
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,14 @@ pub fn byte() -> Fuzzer<Int> {
pub fn bytearray() -> Fuzzer<ByteArray> {
let lsb <- and_then(rand)
let msb <- map(rand)
""
|> builtin.cons_bytearray(lsb, _)
|> builtin.cons_bytearray(msb, _)
|> builtin.blake2b_256
if lsb + msb == 0 {
#"0000000000000000000000000000000000000000000000000000000000000000"
} else {
""
|> builtin.cons_bytearray(lsb, _)
|> builtin.cons_bytearray(msb, _)
|> builtin.blake2b_256
}
}

/// Generate a random [`ByteArray`](https://aiken-lang.github.io/prelude/aiken.html#ByteArray) of
Expand Down Expand Up @@ -324,6 +328,13 @@ pub fn list_with_elem(fuzzer: Fuzzer<a>) -> Fuzzer<(List<a>, a)> {
(xs, x)
}

/// Pick an element from a list, returning its index.
pub fn pick(xs: List<a>) -> Fuzzer<(Int, a)> {
let ix <- map(int_between(0, list.length(xs) - 1))
expect Some(x) = list.at(xs, ix)
(ix, x)
}

/// Generate a random list of **unique** elements (a.k.a. a set) from a given fuzzer.
/// The list contains *at most `20`* elements, and has a higher probability of
/// generating smaller lists.
Expand Down Expand Up @@ -453,6 +464,225 @@ pub fn either(left: Fuzzer<a>, right: Fuzzer<a>) -> Fuzzer<a> {
}
}

/// Choose either of three fuzzers with an equal probability.
pub fn either3(
a: Fuzzer<result>,
b: Fuzzer<result>,
c: Fuzzer<result>,
) -> Fuzzer<result> {
let ix <- and_then(byte())
if ix < 85 {
a
} else if ix < 170 {
b
} else {
c
}
}

/// Choose either of four fuzzers with an equal probability.
pub fn either4(
a: Fuzzer<result>,
b: Fuzzer<result>,
c: Fuzzer<result>,
d: Fuzzer<result>,
) -> Fuzzer<result> {
let ix <- and_then(byte())
if ix < 128 {
if ix < 64 {
a
} else {
b
}
} else {
if ix < 192 {
c
} else {
d
}
}
}

/// Choose either of five fuzzers with an equal probability.
pub fn either5(
a: Fuzzer<result>,
b: Fuzzer<result>,
c: Fuzzer<result>,
d: Fuzzer<result>,
e: Fuzzer<result>,
) -> Fuzzer<result> {
let ix <- and_then(byte())
if ix < 102 {
if ix < 51 {
a
} else {
b
}
} else if ix < 204 {
if ix < 153 {
c
} else {
d
}
} else {
e
}
}

/// Choose either of six fuzzers with an equal probability.
pub fn either6(
a: Fuzzer<result>,
b: Fuzzer<result>,
c: Fuzzer<result>,
d: Fuzzer<result>,
e: Fuzzer<result>,
f: Fuzzer<result>,
) -> Fuzzer<result> {
let ix <- and_then(byte())
if ix < 128 {
if ix < 42 {
a
} else if ix < 85 {
b
} else {
c
}
} else {
if ix < 170 {
d
} else if ix < 212 {
e
} else {
f
}
}
}

/// Choose either of seven fuzzers with an equal probability.
pub fn either7(
a: Fuzzer<result>,
b: Fuzzer<result>,
c: Fuzzer<result>,
d: Fuzzer<result>,
e: Fuzzer<result>,
f: Fuzzer<result>,
g: Fuzzer<result>,
) -> Fuzzer<result> {
let ix <- and_then(byte())
if ix < 110 {
if ix < 36 {
a
} else if ix < 72 {
b
} else {
c
}
} else {
if ix < 182 {
if ix < 145 {
d
} else {
e
}
} else {
if ix < 218 {
f
} else {
g
}
}
}
}

/// Choose either of height fuzzers with an equal probability.
pub fn either8(
a: Fuzzer<result>,
b: Fuzzer<result>,
c: Fuzzer<result>,
d: Fuzzer<result>,
e: Fuzzer<result>,
f: Fuzzer<result>,
g: Fuzzer<result>,
h: Fuzzer<result>,
) -> Fuzzer<result> {
let ix <- and_then(byte())
if ix < 128 {
if ix < 64 {
if ix < 32 {
a
} else {
b
}
} else {
if ix < 96 {
c
} else {
d
}
}
} else {
if ix < 192 {
if ix < 160 {
e
} else {
f
}
} else {
if ix < 224 {
g
} else {
h
}
}
}
}

/// Choose either of nine fuzzers with an equal probability.
pub fn either9(
a: Fuzzer<result>,
b: Fuzzer<result>,
c: Fuzzer<result>,
d: Fuzzer<result>,
e: Fuzzer<result>,
f: Fuzzer<result>,
g: Fuzzer<result>,
h: Fuzzer<result>,
i: Fuzzer<result>,
) -> Fuzzer<result> {
let ix <- and_then(byte())
if ix < 112 {
if ix < 56 {
if ix < 28 {
a
} else {
b
}
} else {
if ix < 84 {
c
} else {
d
}
}
} else {
if ix < 172 {
if ix < 144 {
e
} else {
f
}
} else {
if ix < 200 {
g
} else if ix < 228 {
h
} else {
i
}
}
}
}

/// Transform the result of a [Fuzzer](https://aiken-lang.github.io/prelude/aiken.html#Fuzzer) using a function.
/// This function works great with [backpassing](https://aiken-lang.org/language-tour/functions#backpassing-).
///
Expand Down Expand Up @@ -882,7 +1112,8 @@ pub fn label(str: String) -> Void {
}

/// Apply a label when a predicate is true, or fallback to a default label.
/// Useful for labelling specific scenarios based on a predicate.
/// Useful for labelling dichotomies while ensuring that the final label
/// distribution reflects all cases.
///
/// ```aiken
/// test prop_u16(operands via fuzz.both(byte(), byte())) {
Expand All @@ -905,6 +1136,28 @@ pub fn label_when(predicate: Bool, str: String, default: String) -> Void {
}
}

/// Apply a label when a predicate is true, or do nothing. Useful for
/// conditionally labelling scenarios in a single line.
///
/// ```aiken
/// test post_conditions(steps via any_scenario()) {
/// let (is_register, is_reregister, is_unregister, is_forward) =
/// classify_steps(steps)
///
/// @"contains solo registration" |> label_if(is_register)
/// @"contains re-registration" |> label_if(is_reregister)
/// @"contains solo unregistration" |> label_if(is_unregister)
/// @"contains forward-only" |> label_if(is_forward)
/// }
/// ```
pub fn label_if(str: String, predicate: Bool) -> Void {
if predicate {
label(str)
} else {
Void
}
}

// Internal

const max_rand = 255
Expand Down
Loading
Loading