swig/Examples/test-suite/python/argcargvtest_runme.py

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()