import os, sys
import time
import signal
import traceback
try:
import pexpect
except ImportError:
print("Could not find pexpect. Skipping...")
sys.exit(0)
script = sys.argv[0]
testdir = os.path.dirname(script)
if "BC_TEST_OUTPUT_DIR" in os.environ:
outputdir = os.environ["BC_TEST_OUTPUT_DIR"]
else:
outputdir = testdir
prompt = ">>> "
escapes = [
']',
'[',
'+',
]
utf8_stress1 = "ᆬḰ䋔䗅㜲ತ咡䒢岤䳰稨⣡嶣㷡嶏ⵐ䄺嵕ਅ奰痚㆜䊛拂䅙૩➋䛿ቬ竳Ϳᅠ❄产翷䮊௷Ỉ䷒䳜㛠➕傎ᗋᏯਕ䆐悙癐㺨"
utf8_stress2 = "韠싧돳넨큚ꉿ뮴픷ꉲ긌�최릙걆鳬낽ꪁ퍼鈴핐黙헶ꪈ뮩쭀锻끥鉗겉욞며뛯�ﻼ�度錐�"
utf8_stress3 = "곻�䣹昲蜴Ὓ桢㎏⚦珢畣갴ﭱ鶶ๅ뀁彻ꖒ䔾ꢚﱤ햔햞㐹�鼳뵡▿ⶾ꠩�纞⊐佧�ⵟ霘紳㱔籠뎼⊓搧硤"
utf8_stress4 = "ᄀ𖢾🏴��"
utf8_stress_strs = [
utf8_stress1,
utf8_stress2,
utf8_stress3,
utf8_stress4,
]
def expect(child, data):
child.expect(data)
def eat(child):
while child.buffer is not None and len(child.buffer) > 0:
expect(child, ".+")
def send(child, data):
eat(child)
child.send(data)
def wait(child):
if child.isalive():
child.sendeof()
time.sleep(1)
if child.isalive():
child.kill(signal.SIGTERM)
time.sleep(1)
if child.isalive():
child.kill(signal.SIGKILL)
child.wait()
def check_line(child, expected, prompt=">>> ", history=True):
send(child, "\n")
prefix = "\r\n" if history else ""
expect(child, prefix + expected + "\r\n" + prompt)
def write_str(child, s):
for c in s:
send(child, c)
if c in escapes:
expect(child, "\\{}".format(c))
else:
expect(child, c)
def bc_banner(child):
bc_banner1 = "bc [0-9]+\\.[0-9]+\\.[0-9]+\r\n"
bc_banner2 = "Copyright \\(c\\) 2018-[2-9][0-9][0-9][0-9] Gavin D. Howard and contributors\r\n"
bc_banner3 = "Report bugs at: https://github.com/gavinhoward/bc\r\n\r\n"
bc_banner4 = "This is free software with ABSOLUTELY NO WARRANTY.\r\n\r\n"
expect(child, bc_banner1)
expect(child, bc_banner2)
expect(child, bc_banner3)
expect(child, bc_banner4)
expect(child, prompt)
def test_utf8(exe, args, env, idx, bc=True):
env["BC_BANNER"] = "0"
child = pexpect.spawn(exe, args=args, env=env, encoding='utf-8', codec_errors='ignore')
try:
send(child, utf8_stress_strs[idx])
send(child, "\n")
if bc:
send(child, "quit")
else:
send(child, "q")
send(child, "\n")
wait(child)
except pexpect.TIMEOUT:
traceback.print_tb(sys.exc_info()[2])
print("timed out")
print(str(child))
sys.exit(2)
except pexpect.EOF:
print("EOF")
print(str(child))
print(str(child.buffer))
print(str(child.before))
sys.exit(2)
return child
def test_utf8_0(exe, args, env, bc=True):
env["BC_BANNER"] = "0"
child = pexpect.spawn(exe, args=args, env=env, encoding='utf-8', codec_errors='ignore')
try:
write_str(child, "ﴪáá̵̗🈐ã")
send(child, "\x1b[D\x1b[D\x1b[D\x1b\x1b[Aℐ")
send(child, "\n")
if bc:
send(child, "quit")
else:
send(child, "q")
send(child, "\n")
eat(child)
wait(child)
except pexpect.TIMEOUT:
traceback.print_tb(sys.exc_info()[2])
print("timed out")
print(str(child))
sys.exit(2)
except pexpect.EOF:
print("EOF")
print(str(child))
print(str(child.buffer))
print(str(child.before))
sys.exit(2)
return child
def test_utf8_1(exe, args, env, bc=True):
return test_utf8(exe, args, env, 0, bc)
def test_utf8_2(exe, args, env, bc=True):
return test_utf8(exe, args, env, 1, bc)
def test_utf8_3(exe, args, env, bc=True):
return test_utf8(exe, args, env, 2, bc)
def test_utf8_4(exe, args, env, bc=True):
return test_utf8(exe, args, env, 3, bc)
def test_sigint_sigquit(exe, args, env):
env["BC_BANNER"] = "0"
child = pexpect.spawn(exe, args=args, env=env)
try:
send(child, "\t")
expect(child, "\t")
send(child, "\x03")
wait(child)
except pexpect.TIMEOUT:
traceback.print_tb(sys.exc_info()[2])
print("timed out")
print(str(child))
sys.exit(2)
except pexpect.EOF:
print("EOF")
print(str(child))
print(str(child.buffer))
print(str(child.before))
sys.exit(2)
return child
def test_eof(exe, args, env):
env["BC_BANNER"] = "0"
child = pexpect.spawn(exe, args=args, env=env)
try:
send(child, "123")
expect(child, "123")
send(child, "\x01")
send(child, "\x04")
send(child, "\x04")
send(child, "\x04")
wait(child)
except pexpect.TIMEOUT:
traceback.print_tb(sys.exc_info()[2])
print("timed out")
print(str(child))
sys.exit(2)
except pexpect.EOF:
print("EOF")
print(str(child))
print(str(child.buffer))
print(str(child.before))
sys.exit(2)
return child
def test_sigint(exe, args, env):
env["BC_BANNER"] = "0"
env["BC_SIGINT_RESET"] = "0"
env["DC_SIGINT_RESET"] = "0"
child = pexpect.spawn(exe, args=args, env=env)
try:
send(child, "\t")
expect(child, "\t")
send(child, "\x03")
wait(child)
except pexpect.TIMEOUT:
traceback.print_tb(sys.exc_info()[2])
print("timed out")
print(str(child))
sys.exit(2)
except pexpect.EOF:
print("EOF")
print(str(child))
print(str(child.buffer))
print(str(child.before))
sys.exit(2)
return child
def test_sigtstp(exe, args, env):
if sys.platform.startswith("freebsd"):
sys.exit(0)
env["BC_BANNER"] = "0"
child = pexpect.spawn(exe, args=args, env=env)
try:
send(child, "\t")
expect(child, "\t")
send(child, "\x13")
time.sleep(1)
if not child.isalive():
print("child exited early")
print(str(child))
print(str(child.buffer))
sys.exit(1)
child.kill(signal.SIGCONT)
send(child, "quit")
send(child, "\n")
wait(child)
except pexpect.TIMEOUT:
traceback.print_tb(sys.exc_info()[2])
print("timed out")
print(str(child))
sys.exit(2)
except pexpect.EOF:
print("EOF")
print(str(child))
print(str(child.buffer))
print(str(child.before))
sys.exit(2)
return child
def test_sigstop(exe, args, env):
env["BC_BANNER"] = "0"
child = pexpect.spawn(exe, args=args, env=env)
try:
send(child, "\t")
expect(child, "\t")
send(child, "\x14")
time.sleep(1)
if not child.isalive():
print("child exited early")
print(str(child))
print(str(child.buffer))
sys.exit(1)
send(child, "\x13")
time.sleep(1)
if not child.isalive():
print("child exited early")
print(str(child))
print(str(child.buffer))
sys.exit(1)
child.kill(signal.SIGCONT)
send(child, "quit")
send(child, "\n")
wait(child)
except pexpect.TIMEOUT:
traceback.print_tb(sys.exc_info()[2])
print("timed out")
print(str(child))
sys.exit(2)
except pexpect.EOF:
print("EOF")
print(str(child))
print(str(child.buffer))
print(str(child.before))
sys.exit(2)
return child
def test_bc_utf8_0(exe, args, env):
return test_utf8_0(exe, args, env, True)
def test_bc_utf8_1(exe, args, env):
return test_utf8_1(exe, args, env, True)
def test_bc_utf8_2(exe, args, env):
return test_utf8_2(exe, args, env, True)
def test_bc_utf8_3(exe, args, env):
return test_utf8_3(exe, args, env, True)
def test_bc_utf8_4(exe, args, env):
return test_utf8_4(exe, args, env, True)
def test_bc1(exe, args, env):
child = pexpect.spawn(exe, args=args, env=env)
try:
bc_banner(child)
write_str(child, "1")
check_line(child, "1")
write_str(child, "1")
check_line(child, "1")
send(child, "quit")
send(child, "\n")
wait(child)
except pexpect.TIMEOUT:
traceback.print_tb(sys.exc_info()[2])
print("timed out")
print(str(child))
sys.exit(2)
except pexpect.EOF:
print("EOF")
print(str(child))
print(str(child.buffer))
print(str(child.before))
sys.exit(2)
return child
def test_bc2(exe, args, env):
env["TERM"] = "dumb"
child = pexpect.spawn(exe, args=args, env=env)
try:
bc_banner(child)
child.sendline("1")
check_line(child, "1", history=False)
time.sleep(1)
child.sendintr()
child.sendline("quit")
wait(child)
except pexpect.TIMEOUT:
traceback.print_tb(sys.exc_info()[2])
print("timed out")
print(str(child))
sys.exit(2)
except pexpect.EOF:
print("EOF")
print(str(child))
print(str(child.buffer))
print(str(child.before))
sys.exit(2)
return child
def test_bc3(exe, args, env):
child = pexpect.spawn(exe, args=args, env=env)
try:
bc_banner(child)
send(child, "\x1b[D\x1b[D\x1b[C\x1b[C")
send(child, "\n")
expect(child, prompt)
send(child, "12\x1b[D3\x1b[C4\x1bOD5\x1bOC6")
send(child, "\n")
check_line(child, "132546")
send(child, "12\x023\x064")
send(child, "\n")
check_line(child, "1324")
send(child, "12\x1b[H3\x1bOH\x01\x1b[H45\x1bOF6\x05\x1b[F7\x1bOH8")
send(child, "\n")
check_line(child, "84531267")
send(child, "quit")
send(child, "\n")
wait(child)
except pexpect.TIMEOUT:
traceback.print_tb(sys.exc_info()[2])
print("timed out")
print(str(child))
sys.exit(2)
except pexpect.EOF:
print("EOF")
print(str(child))
print(str(child.buffer))
print(str(child.before))
sys.exit(2)
return child
def test_bc4(exe, args, env):
child = pexpect.spawn(exe, args=args, env=env)
try:
bc_banner(child)
send(child, "\x1b[A\x1bOA\x1b[B\x1bOB")
send(child, "\n")
expect(child, prompt)
write_str(child, "15")
check_line(child, "15")
write_str(child, "2^16")
check_line(child, "65536")
send(child, "\x1b[A\x1bOA")
send(child, "\n")
check_line(child, "15")
send(child, "\x1b[A\x1bOA\x1b[A\x1b[B")
check_line(child, "65536")
send(child, "\x1b[A\x1bOA\x0e\x1b[A\x1b[A\x1b[A\x1b[B\x10\x1b[B\x1b[B\x1bOB\x1b[B\x1bOA")
send(child, "\n")
check_line(child, "65536")
send(child, "quit")
send(child, "\n")
wait(child)
except pexpect.TIMEOUT:
traceback.print_tb(sys.exc_info()[2])
print("timed out")
print(str(child))
sys.exit(2)
except pexpect.EOF:
print("EOF")
print(str(child))
print(str(child.buffer))
print(str(child.before))
sys.exit(2)
return child
def test_bc5(exe, args, env):
child = pexpect.spawn(exe, args=args, env=env)
try:
bc_banner(child)
send(child, "\x0c")
send(child, "quit")
send(child, "\n")
wait(child)
except pexpect.TIMEOUT:
traceback.print_tb(sys.exc_info()[2])
print("timed out")
print(str(child))
sys.exit(2)
except pexpect.EOF:
print("EOF")
print(str(child))
print(str(child.buffer))
print(str(child.before))
sys.exit(2)
return child
def test_bc6(exe, args, env):
child = pexpect.spawn(exe, args=args, env=env)
try:
bc_banner(child)
send(child, "print \"Enter number: \"")
send(child, "\n")
expect(child, "Enter number: ")
send(child, "4\x1b[A\x1b[A")
send(child, "\n")
send(child, "quit")
send(child, "\n")
wait(child)
except pexpect.TIMEOUT:
traceback.print_tb(sys.exc_info()[2])
print("timed out")
print(str(child))
sys.exit(2)
except pexpect.EOF:
print("EOF")
print(str(child))
print(str(child.buffer))
print(str(child.before))
sys.exit(2)
return child
def test_bc7(exe, args, env):
child = pexpect.spawn(exe, args=args, env=env)
try:
bc_banner(child)
send(child, "\x1bb\x1bb\x1bf\x1bf")
send(child, "\n")
expect(child, prompt)
send(child, "\x1b[0~\x1b[3a")
send(child, "\n")
expect(child, prompt)
send(child, "\x1b[0;4\x1b[0A")
send(child, "\n")
expect(child, prompt)
send(child, "\t")
send(child, "\x1bb\x1bb\x1bb\x1bb\x1bb\x1bb\x1bb\x1bb\x1bb\x1bb\x1bb\x1bb")
send(child, "\x1bf\x1bf\x1bf\x1bf\x1bf\x1bf\x1bf\x1bf\x1bf\x1bf\x1bf\x1bf")
send(child, "\n")
expect(child, prompt)
write_str(child, "12 + 34 + 56 + 78 + 90")
check_line(child, "270")
send(child, "\x1b[A")
send(child, "\x1bb\x1bb\x1bb\x1bb\x1bb\x1bb\x1bb\x1bb\x1bb\x1bb\x1bb")
send(child, "\x1bf\x1bf\x1bf\x1bf\x1bf\x1bf\x1bf\x1bf\x1bf\x1bf\x1bf")
check_line(child, "270")
send(child, "\x1b[A")
send(child, "\x1bh\x1bh\x1bf + 14 ")
send(child, "\n")
check_line(child, "284")
send(child, "quit")
send(child, "\n")
wait(child)
except pexpect.TIMEOUT:
traceback.print_tb(sys.exc_info()[2])
print("timed out")
print(str(child))
sys.exit(2)
except pexpect.EOF:
print("EOF")
print(str(child))
print(str(child.buffer))
print(str(child.before))
sys.exit(2)
return child
def test_bc8(exe, args, env):
child = pexpect.spawn(exe, args=args, env=env)
try:
bc_banner(child)
send(child, "12\x1b[D3\x1b[C4\x08\x7f")
send(child, "\n")
check_line(child, "13")
send(child, "quit")
send(child, "\n")
wait(child)
except pexpect.TIMEOUT:
traceback.print_tb(sys.exc_info()[2])
print("timed out")
print(str(child))
sys.exit(2)
except pexpect.EOF:
print("EOF")
print(str(child))
print(str(child.buffer))
print(str(child.before))
sys.exit(2)
return child
def test_bc9(exe, args, env):
child = pexpect.spawn(exe, args=args, env=env)
try:
bc_banner(child)
send(child, "\x1b[0;5D\x1b[0;5D\x1b[0;5D\x1b[0;5C\x1b[0;5D\x1bd\x1b[3~\x1b[d\x1b[d\x1b[d\x1b[d\x7f\x7f\x7f")
send(child, "\n")
expect(child, prompt)
write_str(child, "12 + 34 + 56 + 78 + 90")
check_line(child, "270")
send(child, "\x1b[A")
send(child, "\x1b[0;5D\x1b[0;5D\x1b[0;5D\x1b[0;5C\x1b[0;5D\x1bd\x1b[3~\x1b[d\x1b[d\x1b[d\x1b[d\x7f\x7f\x7f")
send(child, "\n")
check_line(child, "102")
send(child, "\x1b[A")
send(child, "\x17\x17")
send(child, "\n")
check_line(child, "46")
send(child, "\x17\x17")
send(child, "\n")
expect(child, prompt)
send(child, "quit")
send(child, "\n")
wait(child)
except pexpect.TIMEOUT:
traceback.print_tb(sys.exc_info()[2])
print("timed out")
print(str(child))
sys.exit(2)
except pexpect.EOF:
print("EOF")
print(str(child))
print(str(child.buffer))
print(str(child.before))
sys.exit(2)
return child
def test_bc10(exe, args, env):
child = pexpect.spawn(exe, args=args, env=env)
try:
bc_banner(child)
send(child, "\x1b[3~\x1b[3~")
send(child, "\n")
expect(child, prompt)
send(child, " \x1b[3~\x1b[3~")
send(child, "\n")
expect(child, prompt)
write_str(child, "12 + 34 + 56 + 78 + 90")
check_line(child, "270")
send(child, "\x1b[A\x1b[A\x1b[A\x1b[B\x1b[B\x1b[B\x1b[A")
send(child, "\n")
check_line(child, "270")
send(child, "\x1b[A\x1b[0;5D\x1b[0;5D\x0b")
send(child, "\n")
check_line(child, "180")
send(child, "\x1b[A\x1521")
check_line(child, "21")
send(child, "quit")
send(child, "\n")
wait(child)
except pexpect.TIMEOUT:
traceback.print_tb(sys.exc_info()[2])
print("timed out")
print(str(child))
sys.exit(2)
except pexpect.EOF:
print("EOF")
print(str(child))
print(str(child.buffer))
print(str(child.before))
sys.exit(2)
return child
def test_bc11(exe, args, env):
child = pexpect.spawn(exe, args=args, env=env)
try:
bc_banner(child)
send(child, "\x1b[A\x02\x14")
send(child, "\n")
expect(child, prompt)
write_str(child, "12 + 34 + 56 + 78")
check_line(child, "180")
send(child, "\x1b[A\x02\x14")
check_line(child, "189")
send(child, "quit")
send(child, "\n")
wait(child)
except pexpect.TIMEOUT:
traceback.print_tb(sys.exc_info()[2])
print("timed out")
print(str(child))
sys.exit(2)
except pexpect.EOF:
print("EOF")
print(str(child))
print(str(child.buffer))
print(str(child.before))
sys.exit(2)
return child
def test_bc12(exe, args, env):
child = pexpect.spawn(exe, args=args, env=env)
try:
bc_banner(child)
send(child, "12 +")
send(child, "\n")
time.sleep(1)
if not child.isalive():
print("child exited early")
print(str(child))
print(str(child.buffer))
sys.exit(1)
send(child, "quit")
send(child, "\n")
wait(child)
except pexpect.TIMEOUT:
traceback.print_tb(sys.exc_info()[2])
print("timed out")
print(str(child))
sys.exit(2)
except pexpect.EOF:
print("EOF")
print(str(child))
print(str(child.buffer))
print(str(child.before))
sys.exit(2)
return child
def test_dc_utf8_0(exe, args, env):
return test_utf8_0(exe, args, env, False)
def test_dc_utf8_1(exe, args, env):
return test_utf8_1(exe, args, env, False)
def test_dc_utf8_2(exe, args, env):
return test_utf8_2(exe, args, env, False)
def test_dc_utf8_3(exe, args, env):
return test_utf8_3(exe, args, env, False)
def test_dc_utf8_4(exe, args, env):
return test_utf8_4(exe, args, env, False)
def test_dc1(exe, args, env):
child = pexpect.spawn(exe, args=args, env=env)
try:
write_str(child, "1pR")
check_line(child, "1")
write_str(child, "1pR")
check_line(child, "1")
write_str(child, "q")
send(child, "\n")
wait(child)
except pexpect.TIMEOUT:
traceback.print_tb(sys.exc_info()[2])
print("timed out")
print(str(child))
sys.exit(2)
except pexpect.EOF:
print("EOF")
print(str(child))
print(str(child.buffer))
print(str(child.before))
sys.exit(2)
return child
def test_dc2(exe, args, env):
env["TERM"] = "dumb"
child = pexpect.spawn(exe, args=args, env=env)
try:
child.sendline("1pR")
check_line(child, "1", history=False)
time.sleep(1)
child.sendintr()
child.sendline("q")
wait(child)
except pexpect.TIMEOUT:
traceback.print_tb(sys.exc_info()[2])
print("timed out")
print(str(child))
sys.exit(2)
except pexpect.EOF:
print("EOF")
print(str(child))
print(str(child.buffer))
print(str(child.before))
sys.exit(2)
return child
def test_dc3(exe, args, env):
child = pexpect.spawn(exe, args=args, env=env)
try:
write_str(child, "[1 15+pR]x")
check_line(child, "16")
write_str(child, "1pR")
check_line(child, "1")
write_str(child, "q")
send(child, "\n")
wait(child)
except pexpect.TIMEOUT:
traceback.print_tb(sys.exc_info()[2])
print("timed out")
print(str(child))
sys.exit(2)
except pexpect.EOF:
print("EOF")
print(str(child))
print(str(child.buffer))
print(str(child.before))
sys.exit(2)
return child
bc_tests = [
test_bc_utf8_0,
test_bc_utf8_1,
test_bc_utf8_2,
test_bc_utf8_3,
test_bc_utf8_4,
test_sigint_sigquit,
test_eof,
test_sigint,
test_sigtstp,
test_sigstop,
test_bc1,
test_bc2,
test_bc3,
test_bc4,
test_bc5,
test_bc6,
test_bc7,
test_bc8,
test_bc9,
test_bc10,
test_bc11,
test_bc12,
]
dc_tests = [
test_dc_utf8_0,
test_dc_utf8_1,
test_dc_utf8_2,
test_dc_utf8_3,
test_dc_utf8_4,
test_sigint_sigquit,
test_eof,
test_sigint,
test_dc1,
test_dc2,
test_dc3,
]
def usage():
print("usage: {} [-t] dir [-a] test_idx [exe options...]".format(script))
print(" The valid values for dir are: 'bc' and 'dc'.")
print(" The max test_idx for bc is {}.".format(len(bc_tests) - 1))
print(" The max test_idx for dc is {}.".format(len(dc_tests) - 1))
print(" If -a is given, the number of tests for dir is printed.")
print(" No tests are run.")
sys.exit(1)
if __name__ != "__main__":
usage()
if len(sys.argv) < 2:
usage()
idx = 1
exedir = sys.argv[idx]
idx += 1
if exedir == "-t":
do_test = True
exedir = sys.argv[idx]
idx += 1
else:
do_test = False
test_idx = sys.argv[idx]
idx += 1
if test_idx == "-a":
if exedir == "bc":
l = len(bc_tests)
else:
l = len(dc_tests)
print("{}".format(l))
sys.exit(0)
test_idx = int(test_idx)
if len(sys.argv) >= idx + 1:
exe = sys.argv[idx]
else:
exe = testdir + "/../bin/" + exedir
exebase = os.path.basename(exe)
if exebase == "bc":
halt = "halt\n"
options = "-l"
test_array = bc_tests
else:
halt = "q\n"
options = "-x"
test_array = dc_tests
if len(sys.argv) > idx + 1:
exe = [ exe, sys.argv[idx + 1:], options ]
else:
exe = [ exe, options ]
env = {
"BC_BANNER": "1",
"BC_PROMPT": "1",
"DC_PROMPT": "1",
"BC_TTY_MODE": "1",
"DC_TTY_MODE": "1",
"BC_SIGINT_RESET": "1",
"DC_SIGINT_RESET": "1",
}
env.update(os.environ)
env.pop("BC_ENV_ARGS", None)
env.pop("BC_LINE_LENGTH", None)
env.pop("DC_ENV_ARGS", None)
env.pop("DC_LINE_LENGTH", None)
child = test_array[test_idx](exe[0], exe[1:], env)
child.close()
exit = child.exitstatus
if exit is not None and exit != 0:
print("child failed; expected exit code 0, got {}".format(exit))
print(str(child))
sys.exit(1)