Skip to content

Commit

Permalink
Merge pull request #73 from flightaware/inoffensive-tests-bugfixes
Browse files Browse the repository at this point in the history
Add some inoffensive tests and a bugfix
  • Loading branch information
lehenbauer authored Nov 19, 2021
2 parents 6bb7a84 + 74aa495 commit d17a8d8
Show file tree
Hide file tree
Showing 8 changed files with 119 additions and 14 deletions.
3 changes: 3 additions & 0 deletions generic/tohil.c
Original file line number Diff line number Diff line change
Expand Up @@ -769,6 +769,9 @@ Tohil_ReturnExceptionToTcl(Tcl_Interp *interp, PyThreadState *prior, char *descr
PyObject *handle_exception = PyObject_GetAttrString(m, "handle_exception");
if (handle_exception == NULL || !PyCallable_Check(handle_exception)) {
Py_XDECREF(handle_exception);
// Be sure to clear any error that might have been set by us trying to
// dig out handle_exception(). Tohil_ReturnTclError will recurse otherwise.
PyErr_Clear();
return Tohil_ReturnTclError(interp, prior, "unable to find tohil.handle_exception function in python interpreter");
}

Expand Down
4 changes: 1 addition & 3 deletions tests/all.tcl
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,4 @@ namespace import -force tcltest::*
# tcltest::configure -verbose {body pass skip error}

tcltest::testsDirectory [file dir [info script]]
tcltest::runAllTests

return
exit [tcltest::runAllTests]
13 changes: 13 additions & 0 deletions tests/subinterpreters.test
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,19 @@ test tohil_subinterp-1.3 {continue proving that two python interpreters are diff
} \
-result {frammistan}

test tohil_subinterp-1.4 {make sure exceptions come up from subinterps correctly} \
-body {
set interp [interp create]
$interp eval "package require tohil"
try {
$interp eval {tohil::exec "raise Exception('Something awful')"}
} trap {PYTHON Exception {Something awful}} {result options} {
return 1
}
return 0
} \
-result {1}


# =========
# cleanup
Expand Down
32 changes: 32 additions & 0 deletions tests/test_recursion.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import unittest

import tohil

class TestRecursion(unittest.TestCase):
def test_with_callback(self):
# Can't do this without a callback due to how unittest imports the tests.
# Results in the global environment of this module not being visible from within Tcl.
tohil.eval("""
package require tohil
proc isodd {num} {
return [expr {![iseven $num]}]
}
""")
def iseven(num):
num = int(num)
if num == 1:
return False
elif num == 0:
return True
else:
return not tohil.call("isodd", num - 2, to=bool)
tohil.register_callback("iseven", iseven)
self.assertTrue(iseven(0))
self.assertTrue(iseven(2))
self.assertTrue(iseven(4))
self.assertTrue(iseven(6))
self.assertTrue(iseven(200))

self.assertFalse(iseven(1))
self.assertFalse(iseven(3))
self.assertFalse(iseven(99))
12 changes: 8 additions & 4 deletions tests/test_shadowdict.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,14 @@


class TestShadowDicts(unittest.TestCase):
def setUp(self):
tohil.eval("array set x [list a 1 b 2 c 3 d 4]")

def tearDown(self):
tohil.eval("array unset x")

def test_shadowdict1(self):
"""create and access shadow dict as int and compare"""
tohil.eval("array set x [list a 1 b 2 c 3 d 4]")
x = tohil.ShadowDict("x", to=int)
self.assertEqual(x["a"], 1)

Expand All @@ -24,18 +29,18 @@ def test_shadowdict3(self):
def test_shadowdict4(self):
"""length of shadow dict"""
x = tohil.ShadowDict("x", to=int)
x["e"] = 5
self.assertEqual(len(x), 5)

def test_shadowdict5(self):
"""delete element from shadow dict and length"""
x = tohil.ShadowDict("x", to=int)
del x["d"]
self.assertEqual(len(x), 4)
self.assertEqual(len(x), 3)

def test_shadowdict6(self):
"""get element from shadow dict, with and without
specified defaults"""
tohil.eval("array set x [list a 1 b 2 c 3 d 4]")
x = tohil.ShadowDict("x", to=int)
self.assertEqual(x.get('d'), 4)
self.assertEqual(x.get('d', 'defval'), 4)
Expand All @@ -48,7 +53,6 @@ def test_shadowdict6(self):
def test_shadowdict7(self):
"""pop elements from shadow dict, with and without
specified defaults"""
tohil.eval("array set x [list a 1 b 2 c 3 d 4]")
x = tohil.ShadowDict("x", to=int)
self.assertEqual(x.pop('e', 5), 5)
self.assertEqual(x.pop('d'), 4)
Expand Down
55 changes: 54 additions & 1 deletion tests/test_subinterpreters.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,60 @@ def test_subinterp4(self):
self.assertEqual(tohil.call('interp', 'slaves'), '')


class TestSubinterpreters(unittest.TestCase):
def test_subinterpreter_explicit_delete(self):
subinterp = tohil.call("interp", "create", to=str)
# This can often cause spooky behavior since it creates a python
# subinterpreter which is then deleted when we delete the Tcl
# subinterpreter
tohil.call(subinterp, "eval", "package require tohil")
self.assertEqual(len(tohil.call("interp", "slaves", to=list)), 1)
tohil.call("interp", "delete", subinterp)
self.assertEqual(tohil.call("interp", "slaves", to=list), [])

def test_subinterpreter_gc_delete(self):
class TohilInterp:
deleted = False

def __init__(self):
self.interp = tohil.call("interp", "create")
def __del__(self):
type(self).deleted = True
tohil.call("interp", "delete", self.interp)
def call(self, command, *args):
return tohil.call(self.interp, "eval", [command, *args])

subinterp = TohilInterp()
# This can often cause spooky behavior since it creates a python
# subinterpreter which is then deleted when we delete the Tcl
# subinterpreter
subinterp.call("package", "require", "tohil")
self.assertEqual(len(tohil.call("interp", "slaves", to=list)), 1)
del subinterp
self.assertEqual(tohil.call("interp", "slaves", to=list), [])
self.assertTrue(TohilInterp.deleted)

def test_subinterpreter_gc_delete_loop(self):
class TohilInterp:
deleted = False

def __init__(self):
self.interp = tohil.call("interp", "create")
def __del__(self):
type(self).deleted = True
tohil.call("interp", "delete", self.interp)
def call(self, command, *args):
return tohil.call(self.interp, "eval", [command, *args])

for i in range(5):
TohilInterp.deleted = False
subinterp = TohilInterp()
subinterp.call("package", "require", "tohil")
self.assertEqual(len(tohil.call("interp", "slaves", to=list)), 1)
del subinterp
self.assertEqual(tohil.call("interp", "slaves", to=list), [])
self.assertTrue(TohilInterp.deleted)


if __name__ == "__main__":
unittest.main()

13 changes: 7 additions & 6 deletions tests/test_trampoline.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,15 @@


class TestTrampoline(unittest.TestCase):
def test_trampoline1(self):
"""create test proc and try different default values"""
def setUp(self):
tohil.eval("""proc ab_test {a {b b_default}} {return "a is '$a', b is '$b'"}""")
tohil.eval(
"""proc abc_test {a {b b_default} {c c_default}} {return "a is '$a', b is '$b', c is '$c'"}"""
)


def test_trampoline1(self):
"""create test proc and try different default values"""
ab_test = tohil.TclProc("ab_test")

self.assertEqual(ab_test("a_val"), "a is 'a_val', b is 'b_default'")
Expand All @@ -33,10 +38,6 @@ def test_trampoline2(self):

def test_trampoline3(self):
"""different things with a 3-argument test proc"""
tohil.eval(
"""proc abc_test {a {b b_default} {c c_default}} {return "a is '$a', b is '$b', c is '$c'"}"""
)

abc_test = tohil.TclProc("abc_test")

self.assertEqual(
Expand Down
1 change: 1 addition & 0 deletions tests/test_vars.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ def test_unset1(self):

def test_unset2(self):
"""unset of array element"""
tohil.eval("array unset x; array set x [list a 1 b 2 c 3 d 4]")
self.assertEqual(tohil.eval("info exists x(d)", to=int), 1)
self.assertEqual(tohil.exists("x(d)"), True)
tohil.unset("x(c)", "x(d)")
Expand Down

0 comments on commit d17a8d8

Please sign in to comment.