xref: /freebsd/contrib/bc/tests/history.py (revision f7c32ed617858bcd22f8d1b03199099d50125721)
1#! /usr/bin/python
2#
3# SPDX-License-Identifier: BSD-2-Clause
4#
5# Copyright (c) 2018-2021 Gavin D. Howard and contributors.
6#
7# Redistribution and use in source and binary forms, with or without
8# modification, are permitted provided that the following conditions are met:
9#
10# * Redistributions of source code must retain the above copyright notice, this
11#   list of conditions and the following disclaimer.
12#
13# * Redistributions in binary form must reproduce the above copyright notice,
14#   this list of conditions and the following disclaimer in the documentation
15#   and/or other materials provided with the distribution.
16#
17# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
21# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27# POSSIBILITY OF SUCH DAMAGE.
28#
29
30import os, sys
31import time
32import signal
33import traceback
34
35try:
36	import pexpect
37except ImportError:
38	print("Could not find pexpect. Skipping...")
39	sys.exit(0)
40
41# Housekeeping.
42script = sys.argv[0]
43testdir = os.path.dirname(script)
44
45if "BC_TEST_OUTPUT_DIR" in os.environ:
46	outputdir = os.environ["BC_TEST_OUTPUT_DIR"]
47else:
48	outputdir = testdir
49
50prompt = ">>> "
51
52# This array is for escaping characters that are necessary to escape when
53# outputting to pexpect. Since pexpect takes regexes, these characters confuse
54# it unless we escape them.
55escapes = [
56	']',
57	'[',
58	'+',
59]
60
61# UTF-8 stress tests.
62utf8_stress1 = "ᆬḰ䋔䗅㜲ತ咡䒢岤䳰稨⣡嶣㷡嶏ⵐ䄺嵕ਅ奰痚㆜䊛拂䅙૩➋䛿ቬ竳Ϳᅠ❄产翷䮊௷Ỉ䷒䳜㛠➕傎ᗋᏯਕ䆐悙癐㺨"
63utf8_stress2 = "韠싧돳넨큚ꉿ뮴픷ꉲ긌�최릙걆鳬낽ꪁ퍼鈴핐黙헶ꪈ뮩쭀锻끥鉗겉욞며뛯꬐�ﻼ�度錐�"
64utf8_stress3 = "곻�䣹昲蜴Ὓ桢㎏⚦珢畣갴ﭱ鶶ๅ⶛뀁彻ꖒ䔾ꢚﱤ햔햞㐹�鼳뵡▿ⶾ꠩�纞⊐佧�ⵟ霘紳㱔籠뎼⊓搧硤"
65utf8_stress4 = "ᄀ������"
66
67# An easy array for UTF-8 tests.
68utf8_stress_strs = [
69	utf8_stress1,
70	utf8_stress2,
71	utf8_stress3,
72	utf8_stress4,
73]
74
75
76def expect(child, data):
77	child.expect(data)
78
79
80# Eats all of the child's data.
81# @param child  The child whose data should be eaten.
82def eat(child):
83	while child.buffer is not None and len(child.buffer) > 0:
84		expect(child, ".+")
85
86
87# Send data to a child. This makes sure the buffers are empty first.
88# @param child  The child to send data to.
89# @param data   The data to send.
90def send(child, data):
91	eat(child)
92	child.send(data)
93
94
95def wait(child):
96	if child.isalive():
97		child.sendeof()
98		time.sleep(1)
99		if child.isalive():
100			child.kill(signal.SIGTERM)
101			time.sleep(1)
102			if child.isalive():
103				child.kill(signal.SIGKILL)
104	child.wait()
105
106
107# Check that the child output the expected line. If history is false, then
108# the output should change.
109def check_line(child, expected, prompt=">>> ", history=True):
110	send(child, "\n")
111	prefix = "\r\n" if history else ""
112	expect(child, prefix + expected + "\r\n" + prompt)
113
114
115# Write a string to output, checking all of the characters are output,
116# one-by-one.
117def write_str(child, s):
118	for c in s:
119		send(child, c)
120		if c in escapes:
121			expect(child, "\\{}".format(c))
122		else:
123			expect(child, c)
124
125
126# Check the bc banner.
127# @param child  The child process.
128def bc_banner(child):
129	bc_banner1 = "bc [0-9]+\.[0-9]+\.[0-9]+\r\n"
130	bc_banner2 = "Copyright \(c\) 2018-[2-9][0-9][0-9][0-9] Gavin D. Howard and contributors\r\n"
131	bc_banner3 = "Report bugs at: https://git.yzena.com/gavin/bc\r\n\r\n"
132	bc_banner4 = "This is free software with ABSOLUTELY NO WARRANTY.\r\n\r\n"
133	expect(child, bc_banner1)
134	expect(child, bc_banner2)
135	expect(child, bc_banner3)
136	expect(child, bc_banner4)
137	expect(child, prompt)
138
139
140# Common UTF-8 testing function. The index is the index into utf8_stress_strs
141# for which stress string to use.
142# @param exe   The executable.
143# @param args  The arguments to pass to the executable.
144# @param env   The environment.
145# @param idx   The index of the UTF-8 stress string.
146def test_utf8(exe, args, env, idx, bc=True):
147
148	# Because both bc and dc use this, make sure the banner doesn't pop.
149	env["BC_BANNER"] = "0"
150
151	child = pexpect.spawn(exe, args=args, env=env, encoding='utf-8', codec_errors='ignore')
152
153	try:
154
155		# Write the stress string.
156		send(child, utf8_stress_strs[idx])
157		send(child, "\n")
158
159		if bc:
160			send(child, "quit")
161		else:
162			send(child, "q")
163
164		send(child, "\n")
165
166		wait(child)
167
168	except pexpect.TIMEOUT:
169		traceback.print_tb(sys.exc_info()[2])
170		print("timed out")
171		print(str(child))
172		sys.exit(2)
173	except pexpect.EOF:
174		print("EOF")
175		print(str(child))
176		print(str(child.buffer))
177		print(str(child.before))
178		sys.exit(2)
179
180	return child
181
182# A random UTF-8 test with insert.
183# @param exe   The executable.
184# @param args  The arguments to pass to the executable.
185# @param env   The environment.
186def test_utf8_0(exe, args, env, bc=True):
187
188	# Because both bc and dc use this, make sure the banner doesn't pop.
189	env["BC_BANNER"] = "0"
190
191	child = pexpect.spawn(exe, args=args, env=env, encoding='utf-8', codec_errors='ignore')
192
193	try:
194
195		# Just random UTF-8 I generated somewhow, plus ensuring that insert works.
196		write_str(child, "ﴪáá̵̗��ã")
197		send(child, "\x1b[D\x1b[D\x1b[D\x1b\x1b[Aℐ")
198		send(child, "\n")
199
200		if bc:
201			send(child, "quit")
202		else:
203			send(child, "q")
204
205		send(child, "\n")
206		eat(child)
207
208		wait(child)
209
210	except pexpect.TIMEOUT:
211		traceback.print_tb(sys.exc_info()[2])
212		print("timed out")
213		print(str(child))
214		sys.exit(2)
215	except pexpect.EOF:
216		print("EOF")
217		print(str(child))
218		print(str(child.buffer))
219		print(str(child.before))
220		sys.exit(2)
221
222	return child
223
224
225def test_utf8_1(exe, args, env, bc=True):
226	return test_utf8(exe, args, env, 0, bc)
227
228
229def test_utf8_2(exe, args, env, bc=True):
230	return test_utf8(exe, args, env, 1, bc)
231
232
233def test_utf8_3(exe, args, env, bc=True):
234	return test_utf8(exe, args, env, 2, bc)
235
236
237def test_utf8_4(exe, args, env, bc=True):
238	return test_utf8(exe, args, env, 3, bc)
239
240
241# This tests a SIGINT with reset followed by a SIGQUIT.
242# @param exe   The executable.
243# @param args  The arguments to pass to the executable.
244# @param env   The environment.
245def test_sigint_sigquit(exe, args, env):
246
247	# Because both bc and dc use this, make sure the banner doesn't pop.
248	env["BC_BANNER"] = "0"
249
250	child = pexpect.spawn(exe, args=args, env=env)
251
252	try:
253		send(child, "\t")
254		expect(child, "        ")
255		send(child, "\x03")
256		send(child, "\x1c")
257		wait(child)
258	except pexpect.TIMEOUT:
259		traceback.print_tb(sys.exc_info()[2])
260		print("timed out")
261		print(str(child))
262		sys.exit(2)
263	except pexpect.EOF:
264		print("EOF")
265		print(str(child))
266		print(str(child.buffer))
267		print(str(child.before))
268		sys.exit(2)
269
270	return child
271
272
273# Test for EOF.
274# @param exe   The executable.
275# @param args  The arguments to pass to the executable.
276# @param env   The environment.
277def test_eof(exe, args, env):
278
279	# Because both bc and dc use this, make sure the banner doesn't pop.
280	env["BC_BANNER"] = "0"
281
282	child = pexpect.spawn(exe, args=args, env=env)
283
284	try:
285		send(child, "\t")
286		expect(child, "        ")
287		send(child, "\x04")
288		wait(child)
289	except pexpect.TIMEOUT:
290		traceback.print_tb(sys.exc_info()[2])
291		print("timed out")
292		print(str(child))
293		sys.exit(2)
294	except pexpect.EOF:
295		print("EOF")
296		print(str(child))
297		print(str(child.buffer))
298		print(str(child.before))
299		sys.exit(2)
300
301	return child
302
303
304# Test for quiting SIGINT.
305# @param exe   The executable.
306# @param args  The arguments to pass to the executable.
307# @param env   The environment.
308def test_sigint(exe, args, env):
309
310	# Because both bc and dc use this, make sure the banner doesn't pop.
311	env["BC_BANNER"] = "0"
312
313	env["BC_SIGINT_RESET"] = "0"
314	env["DC_SIGINT_RESET"] = "0"
315
316	child = pexpect.spawn(exe, args=args, env=env)
317
318	try:
319		send(child, "\t")
320		expect(child, "        ")
321		send(child, "\x03")
322		wait(child)
323	except pexpect.TIMEOUT:
324		traceback.print_tb(sys.exc_info()[2])
325		print("timed out")
326		print(str(child))
327		sys.exit(2)
328	except pexpect.EOF:
329		print("EOF")
330		print(str(child))
331		print(str(child.buffer))
332		print(str(child.before))
333		sys.exit(2)
334
335	return child
336
337
338# Test for SIGTSTP.
339# @param exe   The executable.
340# @param args  The arguments to pass to the executable.
341# @param env   The environment.
342def test_sigtstp(exe, args, env):
343
344	# This test does not work on FreeBSD, so skip.
345	if sys.platform.startswith("freebsd"):
346		sys.exit(0)
347
348	# Because both bc and dc use this, make sure the banner doesn't pop.
349	env["BC_BANNER"] = "0"
350
351	child = pexpect.spawn(exe, args=args, env=env)
352
353	try:
354		send(child, "\t")
355		expect(child, "        ")
356		send(child, "\x13")
357		time.sleep(1)
358		if not child.isalive():
359			print("child exited early")
360			print(str(child))
361			print(str(child.buffer))
362			sys.exit(1)
363		child.kill(signal.SIGCONT)
364		send(child, "quit")
365		send(child, "\n")
366		wait(child)
367	except pexpect.TIMEOUT:
368		traceback.print_tb(sys.exc_info()[2])
369		print("timed out")
370		print(str(child))
371		sys.exit(2)
372	except pexpect.EOF:
373		print("EOF")
374		print(str(child))
375		print(str(child.buffer))
376		print(str(child.before))
377		sys.exit(2)
378
379	return child
380
381
382# Test for SIGSTOP.
383# @param exe   The executable.
384# @param args  The arguments to pass to the executable.
385# @param env   The environment.
386def test_sigstop(exe, args, env):
387
388	# Because both bc and dc use this, make sure the banner doesn't pop.
389	env["BC_BANNER"] = "0"
390
391	child = pexpect.spawn(exe, args=args, env=env)
392
393	try:
394		send(child, "\t")
395		expect(child, "        ")
396		send(child, "\x14")
397		time.sleep(1)
398		if not child.isalive():
399			print("child exited early")
400			print(str(child))
401			print(str(child.buffer))
402			sys.exit(1)
403		send(child, "\x13")
404		time.sleep(1)
405		if not child.isalive():
406			print("child exited early")
407			print(str(child))
408			print(str(child.buffer))
409			sys.exit(1)
410		child.kill(signal.SIGCONT)
411		send(child, "quit")
412		send(child, "\n")
413		wait(child)
414	except pexpect.TIMEOUT:
415		traceback.print_tb(sys.exc_info()[2])
416		print("timed out")
417		print(str(child))
418		sys.exit(2)
419	except pexpect.EOF:
420		print("EOF")
421		print(str(child))
422		print(str(child.buffer))
423		print(str(child.before))
424		sys.exit(2)
425
426	return child
427
428
429def test_bc_utf8_0(exe, args, env):
430	return test_utf8_0(exe, args, env, True)
431
432
433def test_bc_utf8_1(exe, args, env):
434	return test_utf8_1(exe, args, env, True)
435
436
437def test_bc_utf8_2(exe, args, env):
438	return test_utf8_2(exe, args, env, True)
439
440
441def test_bc_utf8_3(exe, args, env):
442	return test_utf8_3(exe, args, env, True)
443
444
445def test_bc_utf8_4(exe, args, env):
446	return test_utf8_4(exe, args, env, True)
447
448
449# Basic bc test.
450# @param exe   The executable.
451# @param args  The arguments to pass to the executable.
452# @param env   The environment.
453def test_bc1(exe, args, env):
454
455	child = pexpect.spawn(exe, args=args, env=env)
456
457	try:
458		bc_banner(child)
459		write_str(child, "1")
460		check_line(child, "1")
461		write_str(child, "1")
462		check_line(child, "1")
463		send(child, "quit")
464		send(child, "\n")
465		wait(child)
466	except pexpect.TIMEOUT:
467		traceback.print_tb(sys.exc_info()[2])
468		print("timed out")
469		print(str(child))
470		sys.exit(2)
471	except pexpect.EOF:
472		print("EOF")
473		print(str(child))
474		print(str(child.buffer))
475		print(str(child.before))
476		sys.exit(2)
477
478	return child
479
480
481# SIGINT with no history.
482# @param exe   The executable.
483# @param args  The arguments to pass to the executable.
484# @param env   The environment.
485def test_bc2(exe, args, env):
486
487	env["TERM"] = "dumb"
488
489	child = pexpect.spawn(exe, args=args, env=env)
490
491	try:
492		bc_banner(child)
493		child.sendline("1")
494		check_line(child, "1", history=False)
495		time.sleep(1)
496		child.sendintr()
497		child.sendline("quit")
498		wait(child)
499	except pexpect.TIMEOUT:
500		traceback.print_tb(sys.exc_info()[2])
501		print("timed out")
502		print(str(child))
503		sys.exit(2)
504	except pexpect.EOF:
505		print("EOF")
506		print(str(child))
507		print(str(child.buffer))
508		print(str(child.before))
509		sys.exit(2)
510
511	return child
512
513
514# Left and right arrows.
515# @param exe   The executable.
516# @param args  The arguments to pass to the executable.
517# @param env   The environment.
518def test_bc3(exe, args, env):
519
520	child = pexpect.spawn(exe, args=args, env=env)
521
522	try:
523		bc_banner(child)
524		send(child, "\x1b[D\x1b[D\x1b[C\x1b[C")
525		send(child, "\n")
526		expect(child, prompt)
527		send(child, "12\x1b[D3\x1b[C4\x1bOD5\x1bOC6")
528		send(child, "\n")
529		check_line(child, "132546")
530		send(child, "12\x023\x064")
531		send(child, "\n")
532		check_line(child, "1324")
533		send(child, "12\x1b[H3\x1bOH\x01\x1b[H45\x1bOF6\x05\x1b[F7\x1bOH8")
534		send(child, "\n")
535		check_line(child, "84531267")
536		send(child, "quit")
537		send(child, "\n")
538		wait(child)
539	except pexpect.TIMEOUT:
540		traceback.print_tb(sys.exc_info()[2])
541		print("timed out")
542		print(str(child))
543		sys.exit(2)
544	except pexpect.EOF:
545		print("EOF")
546		print(str(child))
547		print(str(child.buffer))
548		print(str(child.before))
549		sys.exit(2)
550
551	return child
552
553
554# Up and down arrows.
555# @param exe   The executable.
556# @param args  The arguments to pass to the executable.
557# @param env   The environment.
558def test_bc4(exe, args, env):
559
560	child = pexpect.spawn(exe, args=args, env=env)
561
562	try:
563		bc_banner(child)
564		send(child, "\x1b[A\x1bOA\x1b[B\x1bOB")
565		send(child, "\n")
566		expect(child, prompt)
567		write_str(child, "15")
568		check_line(child, "15")
569		write_str(child, "2^16")
570		check_line(child, "65536")
571		send(child, "\x1b[A\x1bOA")
572		send(child, "\n")
573		check_line(child, "15")
574		send(child, "\x1b[A\x1bOA\x1b[A\x1b[B")
575		check_line(child, "65536")
576		send(child, "\x1b[A\x1bOA\x0e\x1b[A\x1b[A\x1b[A\x1b[B\x10\x1b[B\x1b[B\x1bOB\x1b[B\x1bOA")
577		send(child, "\n")
578		check_line(child, "65536")
579		send(child, "quit")
580		send(child, "\n")
581		wait(child)
582	except pexpect.TIMEOUT:
583		traceback.print_tb(sys.exc_info()[2])
584		print("timed out")
585		print(str(child))
586		sys.exit(2)
587	except pexpect.EOF:
588		print("EOF")
589		print(str(child))
590		print(str(child.buffer))
591		print(str(child.before))
592		sys.exit(2)
593
594	return child
595
596
597# Clear screen.
598# @param exe   The executable.
599# @param args  The arguments to pass to the executable.
600# @param env   The environment.
601def test_bc5(exe, args, env):
602
603	child = pexpect.spawn(exe, args=args, env=env)
604
605	try:
606		bc_banner(child)
607		send(child, "\x0c")
608		send(child, "quit")
609		send(child, "\n")
610		wait(child)
611	except pexpect.TIMEOUT:
612		traceback.print_tb(sys.exc_info()[2])
613		print("timed out")
614		print(str(child))
615		sys.exit(2)
616	except pexpect.EOF:
617		print("EOF")
618		print(str(child))
619		print(str(child.buffer))
620		print(str(child.before))
621		sys.exit(2)
622
623	return child
624
625
626# Printed material without a newline.
627# @param exe   The executable.
628# @param args  The arguments to pass to the executable.
629# @param env   The environment.
630def test_bc6(exe, args, env):
631
632	child = pexpect.spawn(exe, args=args, env=env)
633
634	try:
635		bc_banner(child)
636		send(child, "print \"Enter number: \"")
637		send(child, "\n")
638		expect(child, "Enter number: ")
639		send(child, "4\x1b[A\x1b[A")
640		send(child, "\n")
641		send(child, "quit")
642		send(child, "\n")
643		wait(child)
644	except pexpect.TIMEOUT:
645		traceback.print_tb(sys.exc_info()[2])
646		print("timed out")
647		print(str(child))
648		sys.exit(2)
649	except pexpect.EOF:
650		print("EOF")
651		print(str(child))
652		print(str(child.buffer))
653		print(str(child.before))
654		sys.exit(2)
655
656	return child
657
658
659# Word start and word end.
660# @param exe   The executable.
661# @param args  The arguments to pass to the executable.
662# @param env   The environment.
663def test_bc7(exe, args, env):
664
665	child = pexpect.spawn(exe, args=args, env=env)
666
667	try:
668		bc_banner(child)
669		send(child, "\x1bb\x1bb\x1bf\x1bf")
670		send(child, "\n")
671		expect(child, prompt)
672		send(child, "\x1b[0~\x1b[3a")
673		send(child, "\n")
674		expect(child, prompt)
675		send(child, "\x1b[0;4\x1b[0A")
676		send(child, "\n")
677		expect(child, prompt)
678		send(child, "        ")
679		send(child, "\x1bb\x1bb\x1bb\x1bb\x1bb\x1bb\x1bb\x1bb\x1bb\x1bb\x1bb\x1bb")
680		send(child, "\x1bf\x1bf\x1bf\x1bf\x1bf\x1bf\x1bf\x1bf\x1bf\x1bf\x1bf\x1bf")
681		send(child, "\n")
682		expect(child, prompt)
683		write_str(child, "12 + 34 + 56 + 78 + 90")
684		check_line(child, "270")
685		send(child, "\x1b[A")
686		send(child, "\x1bb\x1bb\x1bb\x1bb\x1bb\x1bb\x1bb\x1bb\x1bb\x1bb\x1bb")
687		send(child, "\x1bf\x1bf\x1bf\x1bf\x1bf\x1bf\x1bf\x1bf\x1bf\x1bf\x1bf")
688		check_line(child, "270")
689		send(child, "\x1b[A")
690		send(child, "\x1bh\x1bh\x1bf + 14 ")
691		send(child, "\n")
692		check_line(child, "284")
693		send(child, "quit")
694		send(child, "\n")
695		wait(child)
696	except pexpect.TIMEOUT:
697		traceback.print_tb(sys.exc_info()[2])
698		print("timed out")
699		print(str(child))
700		sys.exit(2)
701	except pexpect.EOF:
702		print("EOF")
703		print(str(child))
704		print(str(child.buffer))
705		print(str(child.before))
706		sys.exit(2)
707
708	return child
709
710
711# Backspace.
712# @param exe   The executable.
713# @param args  The arguments to pass to the executable.
714# @param env   The environment.
715def test_bc8(exe, args, env):
716
717	child = pexpect.spawn(exe, args=args, env=env)
718
719	try:
720		bc_banner(child)
721		send(child, "12\x1b[D3\x1b[C4\x08\x7f")
722		send(child, "\n")
723		check_line(child, "13")
724		send(child, "quit")
725		send(child, "\n")
726		wait(child)
727	except pexpect.TIMEOUT:
728		traceback.print_tb(sys.exc_info()[2])
729		print("timed out")
730		print(str(child))
731		sys.exit(2)
732	except pexpect.EOF:
733		print("EOF")
734		print(str(child))
735		print(str(child.buffer))
736		print(str(child.before))
737		sys.exit(2)
738
739	return child
740
741
742# Backspace and delete words.
743# @param exe   The executable.
744# @param args  The arguments to pass to the executable.
745# @param env   The environment.
746def test_bc9(exe, args, env):
747
748	child = pexpect.spawn(exe, args=args, env=env)
749
750	try:
751		bc_banner(child)
752		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")
753		send(child, "\n")
754		expect(child, prompt)
755		write_str(child, "12 + 34 + 56 + 78 + 90")
756		check_line(child, "270")
757		send(child, "\x1b[A")
758		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")
759		send(child, "\n")
760		check_line(child, "102")
761		send(child, "\x1b[A")
762		send(child, "\x17\x17")
763		send(child, "\n")
764		check_line(child, "46")
765		send(child, "\x17\x17")
766		send(child, "\n")
767		expect(child, prompt)
768		send(child, "quit")
769		send(child, "\n")
770		wait(child)
771	except pexpect.TIMEOUT:
772		traceback.print_tb(sys.exc_info()[2])
773		print("timed out")
774		print(str(child))
775		sys.exit(2)
776	except pexpect.EOF:
777		print("EOF")
778		print(str(child))
779		print(str(child.buffer))
780		print(str(child.before))
781		sys.exit(2)
782
783	return child
784
785
786# Backspace and delete words 2.
787# @param exe   The executable.
788# @param args  The arguments to pass to the executable.
789# @param env   The environment.
790def test_bc10(exe, args, env):
791
792	child = pexpect.spawn(exe, args=args, env=env)
793
794	try:
795		bc_banner(child)
796		send(child, "\x1b[3~\x1b[3~")
797		send(child, "\n")
798		expect(child, prompt)
799		send(child, "    \x1b[3~\x1b[3~")
800		send(child, "\n")
801		expect(child, prompt)
802		write_str(child, "12 + 34 + 56 + 78 + 90")
803		check_line(child, "270")
804		send(child, "\x1b[A\x1b[A\x1b[A\x1b[B\x1b[B\x1b[B\x1b[A")
805		send(child, "\n")
806		check_line(child, "270")
807		send(child, "\x1b[A\x1b[0;5D\x1b[0;5D\x0b")
808		send(child, "\n")
809		check_line(child, "180")
810		send(child, "\x1b[A\x1521")
811		check_line(child, "21")
812		send(child, "quit")
813		send(child, "\n")
814		wait(child)
815	except pexpect.TIMEOUT:
816		traceback.print_tb(sys.exc_info()[2])
817		print("timed out")
818		print(str(child))
819		sys.exit(2)
820	except pexpect.EOF:
821		print("EOF")
822		print(str(child))
823		print(str(child.buffer))
824		print(str(child.before))
825		sys.exit(2)
826
827	return child
828
829
830# Swap.
831# @param exe   The executable.
832# @param args  The arguments to pass to the executable.
833# @param env   The environment.
834def test_bc11(exe, args, env):
835
836	child = pexpect.spawn(exe, args=args, env=env)
837
838	try:
839		bc_banner(child)
840		send(child, "\x1b[A\x02\x14")
841		send(child, "\n")
842		expect(child, prompt)
843		write_str(child, "12 + 34 + 56 + 78")
844		check_line(child, "180")
845		send(child, "\x1b[A\x02\x14")
846		check_line(child, "189")
847		send(child, "quit")
848		send(child, "\n")
849		wait(child)
850	except pexpect.TIMEOUT:
851		traceback.print_tb(sys.exc_info()[2])
852		print("timed out")
853		print(str(child))
854		sys.exit(2)
855	except pexpect.EOF:
856		print("EOF")
857		print(str(child))
858		print(str(child.buffer))
859		print(str(child.before))
860		sys.exit(2)
861
862	return child
863
864
865# Non-fatal error.
866# @param exe   The executable.
867# @param args  The arguments to pass to the executable.
868# @param env   The environment.
869def test_bc12(exe, args, env):
870
871	child = pexpect.spawn(exe, args=args, env=env)
872
873	try:
874		bc_banner(child)
875		send(child, "12 +")
876		send(child, "\n")
877		time.sleep(1)
878		if not child.isalive():
879			print("child exited early")
880			print(str(child))
881			print(str(child.buffer))
882			sys.exit(1)
883		send(child, "quit")
884		send(child, "\n")
885		wait(child)
886	except pexpect.TIMEOUT:
887		traceback.print_tb(sys.exc_info()[2])
888		print("timed out")
889		print(str(child))
890		sys.exit(2)
891	except pexpect.EOF:
892		print("EOF")
893		print(str(child))
894		print(str(child.buffer))
895		print(str(child.before))
896		sys.exit(2)
897
898	return child
899
900
901def test_dc_utf8_0(exe, args, env):
902	return test_utf8_0(exe, args, env, False)
903
904
905def test_dc_utf8_1(exe, args, env):
906	return test_utf8_1(exe, args, env, False)
907
908
909def test_dc_utf8_2(exe, args, env):
910	return test_utf8_2(exe, args, env, False)
911
912
913def test_dc_utf8_3(exe, args, env):
914	return test_utf8_3(exe, args, env, False)
915
916
917def test_dc_utf8_4(exe, args, env):
918	return test_utf8_4(exe, args, env, False)
919
920
921# Basic dc test.
922# @param exe   The executable.
923# @param args  The arguments to pass to the executable.
924# @param env   The environment.
925def test_dc1(exe, args, env):
926
927	child = pexpect.spawn(exe, args=args, env=env)
928
929	try:
930		write_str(child, "1pR")
931		check_line(child, "1")
932		write_str(child, "1pR")
933		check_line(child, "1")
934		write_str(child, "q")
935		send(child, "\n")
936		wait(child)
937	except pexpect.TIMEOUT:
938		traceback.print_tb(sys.exc_info()[2])
939		print("timed out")
940		print(str(child))
941		sys.exit(2)
942	except pexpect.EOF:
943		print("EOF")
944		print(str(child))
945		print(str(child.buffer))
946		print(str(child.before))
947		sys.exit(2)
948
949	return child
950
951
952# SIGINT with quit.
953# @param exe   The executable.
954# @param args  The arguments to pass to the executable.
955# @param env   The environment.
956def test_dc2(exe, args, env):
957
958	env["TERM"] = "dumb"
959
960	child = pexpect.spawn(exe, args=args, env=env)
961
962	try:
963		child.sendline("1pR")
964		check_line(child, "1", history=False)
965		time.sleep(1)
966		child.sendintr()
967		child.sendline("q")
968		wait(child)
969	except pexpect.TIMEOUT:
970		traceback.print_tb(sys.exc_info()[2])
971		print("timed out")
972		print(str(child))
973		sys.exit(2)
974	except pexpect.EOF:
975		print("EOF")
976		print(str(child))
977		print(str(child.buffer))
978		print(str(child.before))
979		sys.exit(2)
980
981	return child
982
983
984# Execute string.
985# @param exe   The executable.
986# @param args  The arguments to pass to the executable.
987# @param env   The environment.
988def test_dc3(exe, args, env):
989
990	child = pexpect.spawn(exe, args=args, env=env)
991
992	try:
993		write_str(child, "[1 15+pR]x")
994		check_line(child, "16")
995		write_str(child, "1pR")
996		check_line(child, "1")
997		write_str(child, "q")
998		send(child, "\n")
999		wait(child)
1000	except pexpect.TIMEOUT:
1001		traceback.print_tb(sys.exc_info()[2])
1002		print("timed out")
1003		print(str(child))
1004		sys.exit(2)
1005	except pexpect.EOF:
1006		print("EOF")
1007		print(str(child))
1008		print(str(child.buffer))
1009		print(str(child.before))
1010		sys.exit(2)
1011
1012	return child
1013
1014
1015# The array of bc tests.
1016bc_tests = [
1017	test_bc_utf8_0,
1018	test_bc_utf8_1,
1019	test_bc_utf8_2,
1020	test_bc_utf8_3,
1021	test_bc_utf8_4,
1022	test_sigint_sigquit,
1023	test_eof,
1024	test_sigint,
1025	test_sigtstp,
1026	test_sigstop,
1027	test_bc1,
1028	test_bc2,
1029	test_bc3,
1030	test_bc4,
1031	test_bc5,
1032	test_bc6,
1033	test_bc7,
1034	test_bc8,
1035	test_bc9,
1036	test_bc10,
1037	test_bc11,
1038	test_bc12,
1039]
1040
1041# The array of dc tests.
1042dc_tests = [
1043	test_dc_utf8_0,
1044	test_dc_utf8_1,
1045	test_dc_utf8_2,
1046	test_dc_utf8_3,
1047	test_sigint_sigquit,
1048	test_eof,
1049	test_sigint,
1050	test_dc1,
1051	test_dc2,
1052	test_dc3,
1053]
1054
1055
1056# Print the usage and exit with an error.
1057def usage():
1058	print("usage: {} [-t] dir [-a] test_idx [exe options...]".format(script))
1059	print("       The valid values for dir are: 'bc' and 'dc'.")
1060	print("       The max test_idx for bc is {}.".format(len(bc_tests) - 1))
1061	print("       The max test_idx for dc is {}.".format(len(dc_tests) - 1))
1062	print("       If -a is given, the number of tests for dir is printed.")
1063	print("       No tests are run.")
1064	sys.exit(1)
1065
1066
1067# Must run this script alone.
1068if __name__ != "__main__":
1069	usage()
1070
1071if len(sys.argv) < 2:
1072	usage()
1073
1074idx = 1
1075
1076exedir = sys.argv[idx]
1077
1078idx += 1
1079
1080if exedir == "-t":
1081	do_test = True
1082	exedir = sys.argv[idx]
1083	idx += 1
1084else:
1085	do_test = False
1086
1087test_idx = sys.argv[idx]
1088
1089idx += 1
1090
1091if test_idx == "-a":
1092	if exedir == "bc":
1093		l = len(bc_tests)
1094	else:
1095		l = len(dc_tests)
1096	print("{}".format(l))
1097	sys.exit(0)
1098
1099test_idx = int(test_idx)
1100
1101# Set a default executable unless we have one.
1102if len(sys.argv) >= idx + 1:
1103	exe = sys.argv[idx]
1104else:
1105	exe = testdir + "/../bin/" + exedir
1106
1107exebase = os.path.basename(exe)
1108
1109# Use the correct options.
1110if exebase == "bc":
1111	halt = "halt\n"
1112	options = "-l"
1113	test_array = bc_tests
1114else:
1115	halt = "q\n"
1116	options = "-x"
1117	test_array = dc_tests
1118
1119# More command-line processing.
1120if len(sys.argv) > idx + 1:
1121	exe = [ exe, sys.argv[idx + 1:], options ]
1122else:
1123	exe = [ exe, options ]
1124
1125# This is the environment necessary for most tests.
1126env = {
1127	"BC_BANNER": "1",
1128	"BC_PROMPT": "1",
1129	"DC_PROMPT": "1",
1130	"BC_TTY_MODE": "1",
1131	"DC_TTY_MODE": "1",
1132	"BC_SIGINT_RESET": "1",
1133	"DC_SIGINT_RESET": "1",
1134}
1135
1136# Make sure to include the outside environment.
1137env.update(os.environ)
1138env.pop("BC_ENV_ARGS", None)
1139env.pop("BC_LINE_LENGTH", None)
1140env.pop("DC_ENV_ARGS", None)
1141env.pop("DC_LINE_LENGTH", None)
1142
1143# Run the correct test.
1144child = test_array[test_idx](exe[0], exe[1:], env)
1145
1146child.close()
1147
1148exit = child.exitstatus
1149
1150if exit is not None and exit != 0:
1151	print("child failed; expected exit code 0, got {}".format(exit))
1152	print(str(child))
1153	sys.exit(1)
1154