mirror of https://github.com/swig/swig.git
128 lines
3.9 KiB
Python
128 lines
3.9 KiB
Python
import random
|
|
from swig_test_utils import (
|
|
swig_assert,
|
|
swig_assert_raises,
|
|
swig_random_string,
|
|
swig_run_threaded,
|
|
)
|
|
from argcargvtest import initializeApp, mainc, mainv
|
|
|
|
|
|
def test_list_args():
|
|
args = ["hi", "hola"]
|
|
swig_assert(mainc(args) == 2)
|
|
swig_assert(mainv(args, 0) == "hi")
|
|
swig_assert(mainv(args, 1) == "hola")
|
|
# These constant strings are injected by mainv; see argcargvtest.i
|
|
swig_assert(mainv(args, 2) == "<<NULL>>") # char **argv is NULL-terminated
|
|
swig_assert(mainv(args, 3) == "<<OUT OF BOUNDS>>")
|
|
initializeApp(args)
|
|
|
|
|
|
def test_tuple_args():
|
|
args = ("hi", "hola")
|
|
swig_assert(mainc(args) == 2)
|
|
swig_assert(mainv(args, 0) == "hi")
|
|
swig_assert(mainv(args, 1) == "hola")
|
|
swig_assert(mainv(args, 2) == "<<NULL>>")
|
|
swig_assert(mainv(args, 3) == "<<OUT OF BOUNDS>>")
|
|
initializeApp(args)
|
|
|
|
|
|
def test_empty_args():
|
|
swig_assert(mainc([]) == 0)
|
|
swig_assert(mainv([], 0) == "<<NULL>>")
|
|
swig_assert(mainv([], 1) == "<<OUT OF BOUNDS>>")
|
|
swig_assert(mainc(()) == 0)
|
|
swig_assert(mainv((), 0) == "<<NULL>>")
|
|
swig_assert(mainv((), 1) == "<<OUT OF BOUNDS>>")
|
|
initializeApp([])
|
|
initializeApp(())
|
|
|
|
|
|
def test_empty_strings():
|
|
args = ["hello", "", "world"]
|
|
swig_assert(mainc(args) == 3)
|
|
swig_assert(mainv(args, 0) == "hello")
|
|
swig_assert(mainv(args, 1) == "")
|
|
swig_assert(mainv(args, 2) == "world")
|
|
swig_assert(mainv(args, 3) == "<<NULL>>")
|
|
swig_assert(mainv(args, 4) == "<<OUT OF BOUNDS>>")
|
|
initializeApp(args)
|
|
|
|
|
|
def test_bytes_args():
|
|
args = [b"hello"]
|
|
with swig_assert_raises(TypeError):
|
|
mainc(args)
|
|
|
|
|
|
def test_bad_sequence():
|
|
# Container is neither a list nor tuple
|
|
with swig_assert_raises(TypeError):
|
|
mainc(1)
|
|
with swig_assert_raises(TypeError):
|
|
mainc("hello") # strings are Sequences but not legal here
|
|
|
|
|
|
def test_bad_elements():
|
|
with swig_assert_raises(TypeError):
|
|
mainc([1])
|
|
|
|
|
|
def test_bad_args_memory_leak():
|
|
# Allocate argv and some of its elements, then raise.
|
|
# valgrind or similar should not report any memory leaks after running this test.
|
|
x = "x"
|
|
x += "x" # constants are immortal
|
|
y = "this is a long string"
|
|
y += "that can be deallocated"
|
|
with swig_assert_raises(TypeError):
|
|
mainc([x, y, 1])
|
|
|
|
|
|
def test_threading_race_conditions():
|
|
# Increase these for local torture testing
|
|
MAX_WORKERS = 4
|
|
NITER = 200
|
|
|
|
# Shared list of arguments to be used by all threads
|
|
args = []
|
|
|
|
def torture():
|
|
"""In 60% of the cases, append a random string to a shared list of
|
|
arguments; otherwise, remove a random element from it, causing the string to
|
|
be dereferenced and the list to shrink.
|
|
Finally call SWIG_AsArgcArgv. Repeat many times.
|
|
"""
|
|
for _ in range(NITER):
|
|
nargs = len(args)
|
|
if nargs == 0 or random.random() > 0.4:
|
|
args.append(swig_random_string())
|
|
else:
|
|
idx = random.randint(0, nargs - 1)
|
|
try:
|
|
del args[idx]
|
|
except IndexError: # Race condition
|
|
pass
|
|
# Race conditions will cause this to segfault unless SWIG_AsArgcArgv is
|
|
# thread-safe.
|
|
nargs = mainc(args)
|
|
swig_assert(0 <= nargs <= MAX_WORKERS * NITER, nargs)
|
|
if nargs:
|
|
idx = random.randint(0, nargs - 1)
|
|
elem = mainv(args, idx)
|
|
# Either return an element or, in case of race condition, <<NULL>>
|
|
swig_assert(isinstance(elem, str))
|
|
# It's very likely the last element is out of bounds by now
|
|
elem = mainv(args, nargs - 1)
|
|
swig_assert(isinstance(elem, str))
|
|
|
|
swig_run_threaded(torture, max_workers=MAX_WORKERS)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
for k, v in list(globals().items()):
|
|
if k.startswith("test_") and callable(v):
|
|
v()
|