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

re-order and break-up tests to fail faster #313

Merged
merged 12 commits into from
Jan 31, 2024
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
nim.cfg
bin
deps
.tool-versions
2 changes: 1 addition & 1 deletion cps.nim
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ when (NimMajor, NimMinor, NimPatch) < (1, 7, 3):
# we recommend against threads:on without define:useMalloc
when not defined(useMalloc) and compileOption"threads":
{.warning:
"cps recommends against --threads:on without --define:useMalloc".}
"cps recommends --define:useMalloc when used with threads".}

proc state*(c: Continuation): State {.inline.} =
## Get the current state of a continuation
Expand Down
6 changes: 5 additions & 1 deletion tests/killer.nim
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,11 @@ proc `=destroy`*(k: var Killer) {.raises: [FailError].} =
if k.final != k.n:
let e = getCurrentException()
# don't obliterate current exception
if e.isNil or e isnot FailError or e isnot ExpectedError:
when defined(ExpectedError):
let shouldFail = e.isNil or e isnot FailError or e isnot ExpectedError
else:
let shouldFail = e.isNil or e isnot FailError
if shouldFail:
fail:
case k.n
of 0: "uninitialized"
Expand Down
107 changes: 57 additions & 50 deletions tests/preamble.nim
Original file line number Diff line number Diff line change
@@ -1,51 +1,58 @@
import pkg/balls

import pkg/cps except trampoline

type
EmptyLoop = CatchableError
InfiniteLoop = CatchableError
Cont* = ref object of Continuation

var jumps: int

proc trampoline[T: Continuation](c: sink T) {.used.} =
jumps = 0
var c: Continuation = move c
while c.running:
# capture the exception in the environment
let exception = getCurrentException()
#
# modules and sanity checking that we want to include
# in all test files
#
when not declaredInScope(InfiniteLoop):
import pkg/balls

import cps except trampoline

include killer

type
EmptyLoop = CatchableError
InfiniteLoop = CatchableError
Cont* = ref object of Continuation

var jumps: int

proc trampoline[T: Continuation](c: sink T) {.used.} =
jumps = 0
var c: Continuation = move c
while c.running:
# capture the exception in the environment
let exception = getCurrentException()
try:
var y = c.fn
var x = y(c)
c = x
except CatchableError:
if not c.dismissed:
writeStackFrames c
raise
# the current exception should not change
check getCurrentException() == exception
inc jumps
if jumps > 1000:
raise InfiniteLoop.newException: $jumps & " iterations"
if jumps == 0:
raise EmptyLoop.newException:
"continuations test best when they, uh, bounce"

proc noop*(c: Cont): Cont {.cpsMagic.} = c

# We have a lot of these for the purpose of control-flow validation
{.warning[UnreachableCode]: off.}

template shouldRun(wanted: int; body: untyped) {.used.} =
var measured {.inject.} = 0
try:
var y = c.fn
var x = y(c)
c = x
except CatchableError:
if not c.dismissed:
writeStackFrames c
raise
# the current exception should not change
check getCurrentException() == exception
inc jumps
if jumps > 1000:
raise InfiniteLoop.newException: $jumps & " iterations"
if jumps == 0:
raise EmptyLoop.newException:
"continuations test best when they, uh, bounce"

proc noop*(c: Cont): Cont {.cpsMagic.} = c

# We have a lot of these for the purpose of control-flow validation
{.warning[UnreachableCode]: off.}

template shouldRun(wanted: int; body: untyped) {.used.} =
var measured {.inject.} = 0
try:
body
finally:
check measured == wanted:
if wanted == 0: "oops; continuation ran"
elif measured == 0: "continuation never ran"
elif measured > wanted: "continuation ran too often"
else: "continuation ran too rarely"

template ran {.used, dirty.} = inc measured
body
finally:
check measured == wanted:
if wanted == 0: "oops; continuation ran"
elif measured == 0: "continuation never ran"
elif measured > wanted: "continuation ran too often"
else: "continuation ran too rarely"

template ran {.used, dirty.} = inc measured
File renamed without changes.
4 changes: 0 additions & 4 deletions tests/tloops.nim → tests/t10_loops.nim
Original file line number Diff line number Diff line change
@@ -1,8 +1,4 @@
import balls
import cps

include preamble
import killer

suite "loops":

Expand Down
8 changes: 5 additions & 3 deletions tests/tapi.nim → tests/t20_api.nim
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
include preamble
import killer

import tests/exports

Expand Down Expand Up @@ -230,16 +229,19 @@ suite "cps api":
step 2
send(42)
step 3
echo recv()
echo "level_two: ", recv()
let x = recv()
echo "level_two: ", x
step 4

proc level_one() {.cps:C.} =
step 1
level_two()
step 5
echo "level_one: ", recv()
let v = recv()
echo recv()
step 6
check v == 0

var a = whelp level_one()
trampoline a
3 changes: 0 additions & 3 deletions tests/tcc.nim → tests/t30_cc.nim
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
import std/macros

include preamble
include killer

suite "calling convention":

Expand Down
10 changes: 7 additions & 3 deletions tests/taste.nim → tests/t40_ast.nim
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import std/macros
import foreign

include preamble

import foreign

suite "tasteful tests":

var r = 0
Expand Down Expand Up @@ -403,6 +402,10 @@ suite "tasteful tests":
proc foo() {.cps: Continuation.} =
inc r
let o: RootRef = new O
#{.push warning[CondTrue]: off.}
check r == 1 and o is RootRef
#{.pop.}
check o of RootObj

foo()

Expand All @@ -420,6 +423,7 @@ suite "tasteful tests":
# implicit conversion in assignment
var j: int
j = n
check i == n

foo()

Expand Down
7 changes: 3 additions & 4 deletions tests/thooks.nim → tests/t50_hooks.nim
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
include preamble

import std/genasts
import std/macros
import std/os
Expand All @@ -7,9 +9,6 @@ import std/strutils
from cps/spec import Hook, cpsStackFrames
from cps/hooks import findColonLit

include preamble
import killer

suite "hooks":

block:
Expand Down Expand Up @@ -53,7 +52,7 @@ suite "hooks":
of Stack: sub
else: astToStr c
var path = info.filename.lastPathPart
path = if path == "thooks.nim": "👍" else: path
path = if path == "t50_hooks.nim": "👍" else: path
found.add "$#: $# $# $#" % [ $hook, $sub, last, path ]
body

Expand Down
3 changes: 0 additions & 3 deletions tests/treturns.nim → tests/t60_returns.nim
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
import std/macros
import balls
import cps

include preamble
import killer

suite "returns and results":

Expand Down
37 changes: 19 additions & 18 deletions tests/tlocals.nim → tests/t70_locals.nim
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
import std/sugar

include preamble
include killer

suite "locals":

Expand Down Expand Up @@ -405,21 +402,6 @@ suite "tuples":
foo()
check r == 2

block:
## sugary procedure arguments can be used in expressions
r = 0
proc bar(x: int): int {.cps: Cont.} =
inc r
result = x * 2

proc foo(fn: (int) -> int): int {.cps: Cont.} =
inc r
result = fn: bar(2)
inc r

check 12 == foo(x => x * 3)
check r == 3

type
K = distinct object

Expand Down Expand Up @@ -519,3 +501,22 @@ suite "lifetimes":
# destroy bar.m; eg. step == 9

foo()

import std/sugar

suite "high-cal":
var r = 0
block:
## sugary procedure arguments can be used in expressions
r = 0
proc bar(x: int): int {.cps: Cont.} =
inc r
result = x * 2

proc foo(fn: (int) -> int): int {.cps: Cont.} =
inc r
result = fn: bar(2)
inc r

check 12 == foo(x => x * 3)
check r == 3
Loading
Loading