Skip to content
Closed
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
73 changes: 73 additions & 0 deletions tests/compile_asserts.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
from pathlib import Path

from pyteal.compiler import compileTeal
from pyteal.ir import Mode


def compile_and_save(approval):
teal = Path.cwd() / "tests" / "teal"
compiled = compileTeal(approval(), mode=Mode.Application, version=6)
name = approval.__name__
with open(teal / (name + ".teal"), "w") as f:
f.write(compiled)
print(
f"""Successfuly tested approval program <<{name}>> having
compiled it into {len(compiled)} characters. See the results in:
{teal}
"""
)
return teal, name, compiled


def mismatch_ligature(expected, actual):
la, le = len(actual), len(expected)
mm_idx = -1
for i in range(min(la, le)):
if expected[i] != actual[i]:
mm_idx = i
break
if mm_idx < 0:
return ""
return " " * (mm_idx) + "X" + "-" * (max(la, le) - mm_idx - 1)


def assert_teal_as_expected(path2actual, path2expected):
with open(path2actual, "r") as fa, open(path2expected, "r") as fe:
alines = fa.read().split("\n")
elines = fe.read().split("\n")

assert len(elines) == len(
alines
), f"""EXPECTED {len(elines)} lines for {path2expected}
but ACTUALLY got {len(alines)} lines in {path2actual}"""

for i, actual in enumerate(alines):
expected = elines[i]
assert expected.startswith(
actual
), f"""ACTUAL line in {path2actual}
LINE{i+1}:
{actual}
{mismatch_ligature(expected, actual)}
DOES NOT prefix the EXPECTED (which should have been actual + some commentary) in {path2expected}:
LINE{i+1}:
{expected}
{mismatch_ligature(expected, actual)}
"""


def assert_new_v_old(approve_func):
try:
teal_dir, name, compiled = compile_and_save(approve_func)

print(
f"""Compilation resulted in TEAL program of length {len(compiled)}.
To view output SEE <{name}.teal> in ({teal_dir})
--------------"""
)

path2actual = teal_dir / (name + ".teal")
path2expected = teal_dir / (name + "_expected.teal")
assert_teal_as_expected(path2actual, path2expected)
except Exception as e:
assert not e, f"failed to ASSERT NEW v OLD for {approve_func.__name__}: {e}"
194 changes: 194 additions & 0 deletions tests/pass_by_ref_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
from pyteal import *

from .compile_asserts import assert_new_v_old, compile_and_save

OLD_CODE_ONLY = True

#### TESTS FOR PyTEAL THAT PREDATE PASS-BY-REF


@Subroutine(TealType.bytes)
def logcat(some_bytes, an_int):
catted = ScratchVar(TealType.bytes)
return Seq(
catted.store(Concat(some_bytes, Itob(an_int))),
Log(catted.load()),
catted.load(),
)


def sub_logcat():
return Seq(
Assert(logcat(Bytes("hello"), Int(42)) == Bytes("hello42")),
Int(1),
)


@Subroutine(TealType.uint64)
def slow_fibonacci(n):
return (
If(n <= Int(1))
.Then(n)
.Else(slow_fibonacci(n - Int(2)) + slow_fibonacci(n - Int(1)))
)


def sub_slowfib():
return slow_fibonacci(Int(3))


@Subroutine(TealType.uint64)
def fast_fibonacci(n):
i = ScratchVar(TealType.uint64)
a = ScratchVar(TealType.uint64)
b = ScratchVar(TealType.uint64)
return Seq(
a.store(Int(0)),
b.store(Int(1)),
For(i.store(Int(1)), i.load() <= n, i.store(i.load() + Int(1))).Do(
Seq(
b.store(a.load() + b.load()),
a.store(b.load() - a.load()),
)
),
a.load(),
)


def sub_fastfib():
return fast_fibonacci(Int(3))


@Subroutine(TealType.uint64)
def recursiveIsEven(i):
return (
If(i == Int(0))
.Then(Int(1))
.ElseIf(i == Int(1))
.Then(Int(0))
.Else(recursiveIsEven(i - Int(2)))
)


def sub_even():
return Seq(
Pop(recursiveIsEven(Int(1000))),
recursiveIsEven(Int(1001)),
)


#### TESTS FOR NEW PyTEAL THAT USES PASS-BY-REF / DYNAMIC

if not OLD_CODE_ONLY:

@Subroutine(TealType.none)
def logcat_dynamic(first: ScratchVar, an_int):
return Seq(
first.store(Concat(first.load(), Itob(an_int))),
Log(first.load()),
)

def sub_logcat_dynamic():
first = ScratchVar(TealType.bytes)
return Seq(
first.store(Bytes("hello")),
logcat_dynamic(first, Int(42)),
Assert(Bytes("hello42") == first.load()),
Int(1),
)

def wilt_the_stilt():
player_score = DynamicScratchVar(TealType.uint64)

wilt = ScratchVar(TealType.uint64, 129)
kobe = ScratchVar(TealType.uint64)
dt = ScratchVar(TealType.uint64, 131)

return Seq(
player_score.set_index(wilt),
player_score.store(Int(100)),
player_score.set_index(kobe),
player_score.store(Int(81)),
player_score.set_index(dt),
player_score.store(Int(73)),
Assert(player_score.load() == Int(73)),
Assert(player_score.index() == Int(131)),
player_score.set_index(wilt),
Assert(player_score.load() == Int(100)),
Assert(player_score.index() == Int(129)),
Int(100),
)

@Subroutine(TealType.none)
def swap(x: ScratchVar, y: ScratchVar):
z = ScratchVar(TealType.anytype)
return Seq(
z.store(x.load()),
x.store(y.load()),
y.store(z.load()),
)

@Subroutine(TealType.none)
def cat(x, y):
return Pop(Concat(x, y))

def swapper():
a = ScratchVar(TealType.bytes)
b = ScratchVar(TealType.bytes)
return Seq(
a.store(Bytes("hello")),
b.store(Bytes("goodbye")),
cat(a.load(), b.load()),
swap(a, b),
Assert(a.load() == Bytes("goodbye")),
Assert(b.load() == Bytes("hello")),
Int(1000),
)

@Subroutine(TealType.none)
def factorial(n: ScratchVar):
tmp = ScratchVar(TealType.uint64)
return (
If(n.load() <= Int(1))
.Then(n.store(Int(1)))
.Else(
Seq(
tmp.store(n.load() - Int(1)),
factorial(tmp),
n.store(n.load() * tmp.load()),
)
)
)

def fac_by_ref():
n = ScratchVar(TealType.uint64)
return Seq(
n.store(Int(42)),
factorial(n),
n.load(),
)


OLD_CASES = (sub_logcat, sub_slowfib, sub_fastfib, sub_even)


def test_old():
for pt in OLD_CASES:
assert_new_v_old(pt)


if not OLD_CODE_ONLY:
NEW_CASES = (sub_logcat_dynamic, swapper, wilt_the_stilt, fac_by_ref)

def test_swapper():
compile_and_save(swapper)

def test_new():
for pt in NEW_CASES:
assert_new_v_old(pt)


# if __name__ == "__main__":
# test_swapper()
# test_old()
# test_new()
35 changes: 35 additions & 0 deletions tests/teal/sub_even_expected.teal
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#pragma version 6
int 1000
callsub recursiveIsEven_0
pop
int 1001
callsub recursiveIsEven_0
return

// recursiveIsEven
recursiveIsEven_0:
store 0
load 0
int 0
==
bnz recursiveIsEven_0_l4
load 0
int 1
==
bnz recursiveIsEven_0_l3
load 0
int 2
-
load 0
swap
callsub recursiveIsEven_0
swap
store 0
b recursiveIsEven_0_l5
recursiveIsEven_0_l3:
int 0
b recursiveIsEven_0_l5
recursiveIsEven_0_l4:
int 1
recursiveIsEven_0_l5:
retsub
35 changes: 35 additions & 0 deletions tests/teal/sub_fastfib_expected.teal
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#pragma version 6
int 3
callsub fastfibonacci_0
return

// fast_fibonacci
fastfibonacci_0:
store 0
int 0
store 2
int 1
store 3
int 1
store 1
fastfibonacci_0_l1:
load 1
load 0
<=
bz fastfibonacci_0_l3
load 2
load 3
+
store 3
load 3
load 2
-
store 2
load 1
int 1
+
store 1
b fastfibonacci_0_l1
fastfibonacci_0_l3:
load 2
retsub
23 changes: 23 additions & 0 deletions tests/teal/sub_logcat_expected.teal
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#pragma version 6
byte "hello" >"hello"
int 42 >"hello",42
callsub logcat_0 >"hello42"
byte "hello42" >"hello42","hello42"
== >1
assert <<EMPTY>>
int 1 >1
return <<SUCCESS>>

// logcat
logcat_0: >"hello",42
store 1 1: 42
store 0 0: "hello"
load 0 >"hello"
load 1 >"hello",42
itob >"hello","42"
concat >"hello42"
store 2 2: "hello42"
load 2 >"hello42"
log
load 2 >"hello42"
retsub
Loading