Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
4288b42
thesis-pbt-effekt: add property-based testing support to stdlib testi…
Mar 26, 2025
28e8d45
Fix: Splices with non-String type break if there is no splice (#901)
dvdvgt Mar 28, 2025
e1b92bb
Add lambda calculus NbE example/benchmark (#898)
marvinborner Mar 28, 2025
fe047ce
Run tests without optimization (#851)
marvinborner Mar 28, 2025
6f8b155
Fix inferring effectful while header (#902)
PhictionalOne Mar 28, 2025
9195bbe
Allow trailing comma in list literals (#900)
mattisboeckle Mar 28, 2025
7219512
Standalone language server (#885)
timsueberkrueb Mar 28, 2025
d2bdb9f
Fix: make type pretty printer less confusing for boxed types (#906)
dvdvgt Mar 28, 2025
3773ff2
Add test for cyclic imports (#903)
marzipankaiser Mar 28, 2025
ee78f84
Bump to llvm version 18 in CI (#887)
mattisboeckle Mar 28, 2025
67f47cb
Improve performance of state (#907)
b-studios Mar 28, 2025
9ba3cfa
Fix lsp caching issues (#894)
timsueberkrueb Mar 28, 2025
86ebd77
Fix: ufcs integer lexing (#912)
dvdvgt Mar 28, 2025
1a71751
Add binary search benchmark (#904)
jiribenes Mar 28, 2025
9b9266c
Properly represent existentials in core (#880)
phischu Mar 28, 2025
87a080d
Allow lambda case patterns for multiple parameters (#914)
marzipankaiser Mar 28, 2025
4b8434e
Fix failing tests without optimization (#890)
marvinborner Mar 28, 2025
739914c
Go via trampoline in machine transformer (#917)
phischu Mar 28, 2025
8460593
Fix: dot notation using block params (#918)
dvdvgt Mar 29, 2025
895cafb
Bump version to 0.25.0
effekt-updater[bot] Mar 31, 2025
387b3e7
CI: Fix npm provenance
jiribenes Apr 4, 2025
2a9a36c
CI: Add a way to publish manually to npm
jiribenes Apr 4, 2025
717940a
Fix invalid links (#927)
dvdvgt Apr 8, 2025
a8d3b93
Reimplement C utilities in llvm (#910)
mattisboeckle Apr 10, 2025
e417c07
JSWeb: report all collected errors upon aborting (#931)
dvdvgt Apr 11, 2025
6d9b29b
Bump version to 0.26.0
effekt-updater[bot] Apr 14, 2025
f669047
Remove debug print in Normalizer (#937)
jiribenes Apr 16, 2025
70fb283
Simplify core.Renamer to produce smaller symbols (#938)
jiribenes Apr 17, 2025
d6472ec
Don't reallocate StringBuffer when flushing (#939)
jiribenes Apr 17, 2025
73edfdc
Renamer: Add infrastructure for Checking Uniqueness of Generated Name…
marzipankaiser Apr 17, 2025
28edaf2
Make 'core.Renamer' quicker by simplifying scope handling (#940)
jiribenes Apr 17, 2025
62b505f
Add tailrec annotations wherever possible (#945)
jiribenes Apr 19, 2025
d305794
Bump version to 0.27.0
effekt-updater[bot] Apr 21, 2025
1ba0939
Cache substitutions (#954)
b-studios Apr 22, 2025
28200a2
Specific info message for equally named effect operations (#956)
JakubSchwenkbeck Apr 23, 2025
16f0ce8
Bump version to 0.28.0
effekt-updater[bot] Apr 28, 2025
990fe10
Add wellformedness check for type of `var ... in ...` (#961)
marzipankaiser Apr 28, 2025
298d186
LLVM: Emit most declared definitions as private (#946)
mattisboeckle Apr 28, 2025
d42294b
Add bytestream-based 'random' module with PRNG and /dev/urandom handl…
jiribenes Apr 29, 2025
2f58c0d
use the random effect instead of FFI
Apr 30, 2025
539a8ae
added check files for golden tests, removed leftover debug print
Apr 30, 2025
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
166 changes: 166 additions & 0 deletions examples/stdlib/test/list_examples_pbt.effekt
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
import test

def main() = {
println(suite("PBT: List Functions and Other Simple Examples") {

// example unit tests to show that mixed test suites work
test("1 + 1 == 2") {
assertEqual(1 + 1, 2)
}

test("2 + 2 == 3") {
assertEqual(2 + 2, 3)
}

// reverse[x] === [x]
with arbitraryInt;
forall[Int]("reverse-singleton", 100){ x =>
assertEqual(
reverse([x]),
[x]
)
}
Comment on lines +20 to +27
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When you add the checkfiles, this will probably fail for two reasons on LLVM:

  1. we need to use the variant of forall that knows how to print the tested value (see previous comments on forall defn)
  2. assertEqual works only on JS; if we want it to work on LLVM, you have to use a more general form of assertEqual (defn below), so something like assertEqual(reverse[x], [x]) { list => <{ /* equality on lists */ }> } { list => show(list) }
    def assertEqual[A](obtained: A, expected: A) { equals: (A, A) => Bool } { show: A => String }: Unit / { Assertion, Formatted } =

where you might need to define equality on lists in libraries/common/list.effekt if we don't have it already.


// reverse[x] === [x], with explicit passing of the generator
with def g = arbitraryInt;
forall[Int]("reverse-singleton, explicitely passing the generator", 100){ g }
{ x =>
assertEqual(
reverse([x]),
[x]
)
}

// intented mistake: reverse[x] === [6]
forall[Int]("reverse-singleton mistake", 100)
{ x =>
assertEqual(
reverse([x]),
[6]
)
}

// shows that exists prints tried test cases and found examples correctly
exists[Int]("Is the Integer 2 among the generated values?", 100)
{ x =>
assertEqual(
x,
2
)
}

// reverse(xs ++ ys) === reverse(ys) ++ reverse(xs)
with arbitraryList[Int](3)
forall[List[Int], List[Int]]("reverse: distributivity over append", 100)
{ (xs, ys) =>
assertEqual(
reverse(xs.append(ys)),
reverse(ys).append(reverse(xs))
)
}

// intended mistake: reverse(xs ++ ys) === reverse(xs) ++ reverse(ys)
forall[List[Int], List[Int]]("reverse: distributivity mistake - swapped order", 20)
{ (xs, ys) =>
assertEqual(
reverse(xs.append(ys)),
reverse(xs).append(reverse(ys))
)
}

with arbitraryChar;
with arbitraryList[Char](4);
forall[List[Char], List[Int]]("|zip(xs,ys)| === min(|xs|,|ys|)",10)
{ (xs, ys) =>
assertEqual(
zip(xs, ys).size,
min(xs.size, ys.size)
)
}

with arbitraryChar;
with arbitraryList[Char](6);
forall[List[Char], List[Int]]("intended mistake: |zip(xs,ys)| != max(|xs|,|ys|)",10)
{ (xs, ys) =>
assertEqual(
zip(xs, ys).size,
max(xs.size, ys.size)
)
}

// unzip(zip(xs,ys)) === (xs.take(m), ys.take(m)) where m = min(|xs|,|ys|)
with arbitraryChar;
with arbitraryString(4);
with arbitraryList[String](2);
with arbitraryInt;
with arbitraryList[Int](3)
forall[List[Int], List[String]]("unzip-zip-take relation", 10)
{ (xs, ys) =>
val m = min(xs.size, ys.size)
assertEqual(
unzip(zip(xs, ys)),
(xs.take(m), ys.take(m))
)
}

// Dropping elements from the concatenation of two lists is equivalent to dropping elements from the first list,
// and then (if necessary) dropping the remaining count from the second list.
// (xs ++ ys).drop(n) === if n <= len(xs) then (xs.drop(n)) ++ ys else ys.drop(n - len(xs))
forall[List[Int], List[Int], Int]("drop: concatenation ", 10)
{ (xs, ys, n) =>
val res = if(n <= xs.size) {
xs.drop(n).append(ys)
}
else { ys.drop(n - xs.size) }
assertEqual(
(xs.append(ys).drop(n)),
res
)
}

// xs.drop(n) === xs.slice(n, x.size)
forall[List[Int], Int]("drop-slice relation", 10)
{ (xs, n) =>
assertEqual(
xs.drop(n),
xs.slice(n, xs.size)
)
}

// reverseOnto(reverse(xs), ys) === append(xs, ys)
forall[List[Int], List[Int]]("reverseOnto-reverse-append relation ", 10)
{ (xs, ys) =>
assertEqual(
reverseOnto(reverse(xs), ys),
append(xs, ys)
)
}

// size(xs) === foldLeft(xs, 0) { (acc, _) => acc + 1 }
forall[List[Int]]("size-foldLeft relation", 20)
{ xs =>
assertEqual(
size(xs),
foldLeft(xs, 0){(acc, _) => acc + 1}
)
}

//xs.take(n) ++ xs.drop(n) === xs
forall[Int, List[Int]]("take-drop: inversion over concatenation", 20)
{ (n, xs) =>
assertEqual(
append(xs.take(n), xs.drop(n)),
xs
)
}

// example for a property-based test with multiple assertions
with evenNumbers;
forall[Int]("even numbers are even and smaller than 4", 20)
{ n =>
assertTrue(n.mod(2) == 0)
assertTrue(n <= 4)
}
})
}

107 changes: 107 additions & 0 deletions examples/stdlib/test/tree_examples_pbt.effekt
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import map
import stream

import test

// user-defined generator for arbitrary, unique Integers in ascending order
def uniqueInt{body: => Unit / Generator[Int]}: Unit = {
try body() with Generator[Int]{
def generate() = resume{
var next = randomInt(-100, 100)
do emit(next)
while(true){
next = next + randomInt(1, 5)
do emit(next)
}
}
def shrink(v) = <>
}
}

// precondition: provided handler for Generator[K] needs to produce unique keys in ascending order!
// otherwise the generated trees aren't valid binary search trees
def arbitraryBinTree[K, V] (numKeys: Int) { body: => Unit / Generator[internal::Tree[K, V]] }: Unit / {Generator[K], Generator[V]} = {
def buildTree[K, V](pairs: List[(K, V)]): internal::Tree[K,V] = {
if (pairs.isEmpty) {
internal::Tip()
} else {
val midIdx = pairs.size() / 2
with on[OutOfBounds].panic;
val midEl = pairs.get(midIdx)
val leftTree = buildTree(pairs.take(midIdx))
val rightTree = buildTree(pairs.drop(midIdx + 1))
val size = 1 + internal::size(leftTree) + internal::size(rightTree)

internal::Bin(size, midEl.first, midEl.second, leftTree, rightTree)
}
}

try body() with Generator[internal::Tree[K, V]] {
def generate() = resume {
while(true) {
val l1 = collectList[K]{with boundary; with limit[K](numKeys); do generate[K]}
val l2 = collectList[V]{with boundary; with limit[V](numKeys); do generate[V]}
val sortedPairs = zip(l1,l2)
do emit(buildTree[K, V](sortedPairs))
//TODO try to use zip again
}
}
def shrink(v) = <>
}
}

def main()= {
with arbitraryChar;
with arbitraryString(5);
with uniqueInt;
with arbitraryBinTree[Int, String](8);

println(suite("PBT: Tree Map Examples") {

// get-put law: get(put(M, K, V), K) = V
forall[String, internal::Tree[Int, String], Int]("get-put law", 100)
{ (v, t, k) =>
val newT = internal::put(t, compareInt, k, v)
internal::get(newT, compareInt, k) match {
case Some(value) => assertEqual(value, v)
case None() => do assert(false, "Key not in the tree")
}
}

// put-put law: get(put(put(M, K, V1), K, V2), K) = V2
forall[String, internal::Tree[Int, String], Int]("put-put law", 100)
{ (v1, t, k) =>
val v2 = v1 ++ show(k) ++ v1
val newT1 = internal::put(t, compareInt, k, v1)
val newT2 = internal::put(newT1, compareInt, k, v2)
internal::get(newT2, compareInt, k) match {
case Some(v) => assertEqual(v, v2)
case None() => do assert(false, "Key in the tree")
}
}

// put-get law: put(M, K, get(M, K)) = M
forall[internal::Tree[Int, String]]("put-get law", 100)
{ t =>
// put-get law only make sense if K is present in the tree ~> get the keys of the tree
var keys = Nil()
t.internal::foreach[Int, String] { (k, _v) => keys = Cons(k, keys)}

with on[OutOfBounds].panic
val k = keys.get(randomInt(0, keys.size())) // only check the property for key's present in the tree (= non trivial test case)
internal::get(t, compareInt, k) match {
case Some(v) => {
val newT = internal::put(t, compareInt, k, v)
assertEqual(t, newT)}
case None() => do assert(false, "Key not in the tree")
}
}

// Law: `m.forget === m.map { (_k, _v) => () }
forall[internal::Tree[Int, String]]("forget-map relation", 100){t =>
val tForget = internal::forget(t)
val tMap = internal::map(t){ (_k, _v) => ()}
assertEqual(tForget, tMap)
}
})
}
Loading
Loading