xref: /freebsd/contrib/bc/tests/history.py (revision 22d7dd834bc5cd189810e414701e3ad1e98102e4)
1#! /usr/bin/python
2#
3# SPDX-License-Identifier: BSD-2-Clause
4#
5# Copyright (c) 2018-2023 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.gavinhoward.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, "\t")
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, "123")
286		expect(child, "123")
287		send(child, "\x01")
288		send(child, "\x04")
289		send(child, "\x04")
290		send(child, "\x04")
291		wait(child)
292	except pexpect.TIMEOUT:
293		traceback.print_tb(sys.exc_info()[2])
294		print("timed out")
295		print(str(child))
296		sys.exit(2)
297	except pexpect.EOF:
298		print("EOF")
299		print(str(child))
300		print(str(child.buffer))
301		print(str(child.before))
302		sys.exit(2)
303
304	return child
305
306
307# Test for quiting SIGINT.
308# @param exe   The executable.
309# @param args  The arguments to pass to the executable.
310# @param env   The environment.
311def test_sigint(exe, args, env):
312
313	# Because both bc and dc use this, make sure the banner doesn't pop.
314	env["BC_BANNER"] = "0"
315
316	env["BC_SIGINT_RESET"] = "0"
317	env["DC_SIGINT_RESET"] = "0"
318
319	child = pexpect.spawn(exe, args=args, env=env)
320
321	try:
322		send(child, "\t")
323		expect(child, "\t")
324		send(child, "\x03")
325		wait(child)
326	except pexpect.TIMEOUT:
327		traceback.print_tb(sys.exc_info()[2])
328		print("timed out")
329		print(str(child))
330		sys.exit(2)
331	except pexpect.EOF:
332		print("EOF")
333		print(str(child))
334		print(str(child.buffer))
335		print(str(child.before))
336		sys.exit(2)
337
338	return child
339
340
341# Test for SIGTSTP.
342# @param exe   The executable.
343# @param args  The arguments to pass to the executable.
344# @param env   The environment.
345def test_sigtstp(exe, args, env):
346
347	# This test does not work on FreeBSD, so skip.
348	if sys.platform.startswith("freebsd"):
349		sys.exit(0)
350
351	# Because both bc and dc use this, make sure the banner doesn't pop.
352	env["BC_BANNER"] = "0"
353
354	child = pexpect.spawn(exe, args=args, env=env)
355
356	try:
357		send(child, "\t")
358		expect(child, "\t")
359		send(child, "\x13")
360		time.sleep(1)
361		if not child.isalive():
362			print("child exited early")
363			print(str(child))
364			print(str(child.buffer))
365			sys.exit(1)
366		child.kill(signal.SIGCONT)
367		send(child, "quit")
368		send(child, "\n")
369		wait(child)
370	except pexpect.TIMEOUT:
371		traceback.print_tb(sys.exc_info()[2])
372		print("timed out")
373		print(str(child))
374		sys.exit(2)
375	except pexpect.EOF:
376		print("EOF")
377		print(str(child))
378		print(str(child.buffer))
379		print(str(child.before))
380		sys.exit(2)
381
382	return child
383
384
385# Test for SIGSTOP.
386# @param exe   The executable.
387# @param args  The arguments to pass to the executable.
388# @param env   The environment.
389def test_sigstop(exe, args, env):
390
391	# Because both bc and dc use this, make sure the banner doesn't pop.
392	env["BC_BANNER"] = "0"
393
394	child = pexpect.spawn(exe, args=args, env=env)
395
396	try:
397		send(child, "\t")
398		expect(child, "\t")
399		send(child, "\x14")
400		time.sleep(1)
401		if not child.isalive():
402			print("child exited early")
403			print(str(child))
404			print(str(child.buffer))
405			sys.exit(1)
406		send(child, "\x13")
407		time.sleep(1)
408		if not child.isalive():
409			print("child exited early")
410			print(str(child))
411			print(str(child.buffer))
412			sys.exit(1)
413		child.kill(signal.SIGCONT)
414		send(child, "quit")
415		send(child, "\n")
416		wait(child)
417	except pexpect.TIMEOUT:
418		traceback.print_tb(sys.exc_info()[2])
419		print("timed out")
420		print(str(child))
421		sys.exit(2)
422	except pexpect.EOF:
423		print("EOF")
424		print(str(child))
425		print(str(child.buffer))
426		print(str(child.before))
427		sys.exit(2)
428
429	return child
430
431
432def test_bc_utf8_0(exe, args, env):
433	return test_utf8_0(exe, args, env, True)
434
435
436def test_bc_utf8_1(exe, args, env):
437	return test_utf8_1(exe, args, env, True)
438
439
440def test_bc_utf8_2(exe, args, env):
441	return test_utf8_2(exe, args, env, True)
442
443
444def test_bc_utf8_3(exe, args, env):
445	return test_utf8_3(exe, args, env, True)
446
447
448def test_bc_utf8_4(exe, args, env):
449	return test_utf8_4(exe, args, env, True)
450
451
452# Basic bc test.
453# @param exe   The executable.
454# @param args  The arguments to pass to the executable.
455# @param env   The environment.
456def test_bc1(exe, args, env):
457
458	child = pexpect.spawn(exe, args=args, env=env)
459
460	try:
461		bc_banner(child)
462		write_str(child, "1")
463		check_line(child, "1")
464		write_str(child, "1")
465		check_line(child, "1")
466		send(child, "quit")
467		send(child, "\n")
468		wait(child)
469	except pexpect.TIMEOUT:
470		traceback.print_tb(sys.exc_info()[2])
471		print("timed out")
472		print(str(child))
473		sys.exit(2)
474	except pexpect.EOF:
475		print("EOF")
476		print(str(child))
477		print(str(child.buffer))
478		print(str(child.before))
479		sys.exit(2)
480
481	return child
482
483
484# SIGINT with no history.
485# @param exe   The executable.
486# @param args  The arguments to pass to the executable.
487# @param env   The environment.
488def test_bc2(exe, args, env):
489
490	env["TERM"] = "dumb"
491
492	child = pexpect.spawn(exe, args=args, env=env)
493
494	try:
495		bc_banner(child)
496		child.sendline("1")
497		check_line(child, "1", history=False)
498		time.sleep(1)
499		child.sendintr()
500		child.sendline("quit")
501		wait(child)
502	except pexpect.TIMEOUT:
503		traceback.print_tb(sys.exc_info()[2])
504		print("timed out")
505		print(str(child))
506		sys.exit(2)
507	except pexpect.EOF:
508		print("EOF")
509		print(str(child))
510		print(str(child.buffer))
511		print(str(child.before))
512		sys.exit(2)
513
514	return child
515
516
517# Left and right arrows.
518# @param exe   The executable.
519# @param args  The arguments to pass to the executable.
520# @param env   The environment.
521def test_bc3(exe, args, env):
522
523	child = pexpect.spawn(exe, args=args, env=env)
524
525	try:
526		bc_banner(child)
527		send(child, "\x1b[D\x1b[D\x1b[C\x1b[C")
528		send(child, "\n")
529		expect(child, prompt)
530		send(child, "12\x1b[D3\x1b[C4\x1bOD5\x1bOC6")
531		send(child, "\n")
532		check_line(child, "132546")
533		send(child, "12\x023\x064")
534		send(child, "\n")
535		check_line(child, "1324")
536		send(child, "12\x1b[H3\x1bOH\x01\x1b[H45\x1bOF6\x05\x1b[F7\x1bOH8")
537		send(child, "\n")
538		check_line(child, "84531267")
539		send(child, "quit")
540		send(child, "\n")
541		wait(child)
542	except pexpect.TIMEOUT:
543		traceback.print_tb(sys.exc_info()[2])
544		print("timed out")
545		print(str(child))
546		sys.exit(2)
547	except pexpect.EOF:
548		print("EOF")
549		print(str(child))
550		print(str(child.buffer))
551		print(str(child.before))
552		sys.exit(2)
553
554	return child
555
556
557# Up and down arrows.
558# @param exe   The executable.
559# @param args  The arguments to pass to the executable.
560# @param env   The environment.
561def test_bc4(exe, args, env):
562
563	child = pexpect.spawn(exe, args=args, env=env)
564
565	try:
566		bc_banner(child)
567		send(child, "\x1b[A\x1bOA\x1b[B\x1bOB")
568		send(child, "\n")
569		expect(child, prompt)
570		write_str(child, "15")
571		check_line(child, "15")
572		write_str(child, "2^16")
573		check_line(child, "65536")
574		send(child, "\x1b[A\x1bOA")
575		send(child, "\n")
576		check_line(child, "15")
577		send(child, "\x1b[A\x1bOA\x1b[A\x1b[B")
578		check_line(child, "65536")
579		send(child, "\x1b[A\x1bOA\x0e\x1b[A\x1b[A\x1b[A\x1b[B\x10\x1b[B\x1b[B\x1bOB\x1b[B\x1bOA")
580		send(child, "\n")
581		check_line(child, "65536")
582		send(child, "quit")
583		send(child, "\n")
584		wait(child)
585	except pexpect.TIMEOUT:
586		traceback.print_tb(sys.exc_info()[2])
587		print("timed out")
588		print(str(child))
589		sys.exit(2)
590	except pexpect.EOF:
591		print("EOF")
592		print(str(child))
593		print(str(child.buffer))
594		print(str(child.before))
595		sys.exit(2)
596
597	return child
598
599
600# Clear screen.
601# @param exe   The executable.
602# @param args  The arguments to pass to the executable.
603# @param env   The environment.
604def test_bc5(exe, args, env):
605
606	child = pexpect.spawn(exe, args=args, env=env)
607
608	try:
609		bc_banner(child)
610		send(child, "\x0c")
611		send(child, "quit")
612		send(child, "\n")
613		wait(child)
614	except pexpect.TIMEOUT:
615		traceback.print_tb(sys.exc_info()[2])
616		print("timed out")
617		print(str(child))
618		sys.exit(2)
619	except pexpect.EOF:
620		print("EOF")
621		print(str(child))
622		print(str(child.buffer))
623		print(str(child.before))
624		sys.exit(2)
625
626	return child
627
628
629# Printed material without a newline.
630# @param exe   The executable.
631# @param args  The arguments to pass to the executable.
632# @param env   The environment.
633def test_bc6(exe, args, env):
634
635	child = pexpect.spawn(exe, args=args, env=env)
636
637	try:
638		bc_banner(child)
639		send(child, "print \"Enter number: \"")
640		send(child, "\n")
641		expect(child, "Enter number: ")
642		send(child, "4\x1b[A\x1b[A")
643		send(child, "\n")
644		send(child, "quit")
645		send(child, "\n")
646		wait(child)
647	except pexpect.TIMEOUT:
648		traceback.print_tb(sys.exc_info()[2])
649		print("timed out")
650		print(str(child))
651		sys.exit(2)
652	except pexpect.EOF:
653		print("EOF")
654		print(str(child))
655		print(str(child.buffer))
656		print(str(child.before))
657		sys.exit(2)
658
659	return child
660
661
662# Word start and word end.
663# @param exe   The executable.
664# @param args  The arguments to pass to the executable.
665# @param env   The environment.
666def test_bc7(exe, args, env):
667
668	child = pexpect.spawn(exe, args=args, env=env)
669
670	try:
671		bc_banner(child)
672		send(child, "\x1bb\x1bb\x1bf\x1bf")
673		send(child, "\n")
674		expect(child, prompt)
675		send(child, "\x1b[0~\x1b[3a")
676		send(child, "\n")
677		expect(child, prompt)
678		send(child, "\x1b[0;4\x1b[0A")
679		send(child, "\n")
680		expect(child, prompt)
681		send(child, "\t")
682		send(child, "\x1bb\x1bb\x1bb\x1bb\x1bb\x1bb\x1bb\x1bb\x1bb\x1bb\x1bb\x1bb")
683		send(child, "\x1bf\x1bf\x1bf\x1bf\x1bf\x1bf\x1bf\x1bf\x1bf\x1bf\x1bf\x1bf")
684		send(child, "\n")
685		expect(child, prompt)
686		write_str(child, "12 + 34 + 56 + 78 + 90")
687		check_line(child, "270")
688		send(child, "\x1b[A")
689		send(child, "\x1bb\x1bb\x1bb\x1bb\x1bb\x1bb\x1bb\x1bb\x1bb\x1bb\x1bb")
690		send(child, "\x1bf\x1bf\x1bf\x1bf\x1bf\x1bf\x1bf\x1bf\x1bf\x1bf\x1bf")
691		check_line(child, "270")
692		send(child, "\x1b[A")
693		send(child, "\x1bh\x1bh\x1bf + 14 ")
694		send(child, "\n")
695		check_line(child, "284")
696		send(child, "quit")
697		send(child, "\n")
698		wait(child)
699	except pexpect.TIMEOUT:
700		traceback.print_tb(sys.exc_info()[2])
701		print("timed out")
702		print(str(child))
703		sys.exit(2)
704	except pexpect.EOF:
705		print("EOF")
706		print(str(child))
707		print(str(child.buffer))
708		print(str(child.before))
709		sys.exit(2)
710
711	return child
712
713
714# Backspace.
715# @param exe   The executable.
716# @param args  The arguments to pass to the executable.
717# @param env   The environment.
718def test_bc8(exe, args, env):
719
720	child = pexpect.spawn(exe, args=args, env=env)
721
722	try:
723		bc_banner(child)
724		send(child, "12\x1b[D3\x1b[C4\x08\x7f")
725		send(child, "\n")
726		check_line(child, "13")
727		send(child, "quit")
728		send(child, "\n")
729		wait(child)
730	except pexpect.TIMEOUT:
731		traceback.print_tb(sys.exc_info()[2])
732		print("timed out")
733		print(str(child))
734		sys.exit(2)
735	except pexpect.EOF:
736		print("EOF")
737		print(str(child))
738		print(str(child.buffer))
739		print(str(child.before))
740		sys.exit(2)
741
742	return child
743
744
745# Backspace and delete words.
746# @param exe   The executable.
747# @param args  The arguments to pass to the executable.
748# @param env   The environment.
749def test_bc9(exe, args, env):
750
751	child = pexpect.spawn(exe, args=args, env=env)
752
753	try:
754		bc_banner(child)
755		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")
756		send(child, "\n")
757		expect(child, prompt)
758		write_str(child, "12 + 34 + 56 + 78 + 90")
759		check_line(child, "270")
760		send(child, "\x1b[A")
761		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")
762		send(child, "\n")
763		check_line(child, "102")
764		send(child, "\x1b[A")
765		send(child, "\x17\x17")
766		send(child, "\n")
767		check_line(child, "46")
768		send(child, "\x17\x17")
769		send(child, "\n")
770		expect(child, prompt)
771		send(child, "quit")
772		send(child, "\n")
773		wait(child)
774	except pexpect.TIMEOUT:
775		traceback.print_tb(sys.exc_info()[2])
776		print("timed out")
777		print(str(child))
778		sys.exit(2)
779	except pexpect.EOF:
780		print("EOF")
781		print(str(child))
782		print(str(child.buffer))
783		print(str(child.before))
784		sys.exit(2)
785
786	return child
787
788
789# Backspace and delete words 2.
790# @param exe   The executable.
791# @param args  The arguments to pass to the executable.
792# @param env   The environment.
793def test_bc10(exe, args, env):
794
795	child = pexpect.spawn(exe, args=args, env=env)
796
797	try:
798		bc_banner(child)
799		send(child, "\x1b[3~\x1b[3~")
800		send(child, "\n")
801		expect(child, prompt)
802		send(child, "    \x1b[3~\x1b[3~")
803		send(child, "\n")
804		expect(child, prompt)
805		write_str(child, "12 + 34 + 56 + 78 + 90")
806		check_line(child, "270")
807		send(child, "\x1b[A\x1b[A\x1b[A\x1b[B\x1b[B\x1b[B\x1b[A")
808		send(child, "\n")
809		check_line(child, "270")
810		send(child, "\x1b[A\x1b[0;5D\x1b[0;5D\x0b")
811		send(child, "\n")
812		check_line(child, "180")
813		send(child, "\x1b[A\x1521")
814		check_line(child, "21")
815		send(child, "quit")
816		send(child, "\n")
817		wait(child)
818	except pexpect.TIMEOUT:
819		traceback.print_tb(sys.exc_info()[2])
820		print("timed out")
821		print(str(child))
822		sys.exit(2)
823	except pexpect.EOF:
824		print("EOF")
825		print(str(child))
826		print(str(child.buffer))
827		print(str(child.before))
828		sys.exit(2)
829
830	return child
831
832
833# Swap.
834# @param exe   The executable.
835# @param args  The arguments to pass to the executable.
836# @param env   The environment.
837def test_bc11(exe, args, env):
838
839	child = pexpect.spawn(exe, args=args, env=env)
840
841	try:
842		bc_banner(child)
843		send(child, "\x1b[A\x02\x14")
844		send(child, "\n")
845		expect(child, prompt)
846		write_str(child, "12 + 34 + 56 + 78")
847		check_line(child, "180")
848		send(child, "\x1b[A\x02\x14")
849		check_line(child, "189")
850		send(child, "quit")
851		send(child, "\n")
852		wait(child)
853	except pexpect.TIMEOUT:
854		traceback.print_tb(sys.exc_info()[2])
855		print("timed out")
856		print(str(child))
857		sys.exit(2)
858	except pexpect.EOF:
859		print("EOF")
860		print(str(child))
861		print(str(child.buffer))
862		print(str(child.before))
863		sys.exit(2)
864
865	return child
866
867
868# Non-fatal error.
869# @param exe   The executable.
870# @param args  The arguments to pass to the executable.
871# @param env   The environment.
872def test_bc12(exe, args, env):
873
874	child = pexpect.spawn(exe, args=args, env=env)
875
876	try:
877		bc_banner(child)
878		send(child, "12 +")
879		send(child, "\n")
880		time.sleep(1)
881		if not child.isalive():
882			print("child exited early")
883			print(str(child))
884			print(str(child.buffer))
885			sys.exit(1)
886		send(child, "quit")
887		send(child, "\n")
888		wait(child)
889	except pexpect.TIMEOUT:
890		traceback.print_tb(sys.exc_info()[2])
891		print("timed out")
892		print(str(child))
893		sys.exit(2)
894	except pexpect.EOF:
895		print("EOF")
896		print(str(child))
897		print(str(child.buffer))
898		print(str(child.before))
899		sys.exit(2)
900
901	return child
902
903
904def test_dc_utf8_0(exe, args, env):
905	return test_utf8_0(exe, args, env, False)
906
907
908def test_dc_utf8_1(exe, args, env):
909	return test_utf8_1(exe, args, env, False)
910
911
912def test_dc_utf8_2(exe, args, env):
913	return test_utf8_2(exe, args, env, False)
914
915
916def test_dc_utf8_3(exe, args, env):
917	return test_utf8_3(exe, args, env, False)
918
919
920def test_dc_utf8_4(exe, args, env):
921	return test_utf8_4(exe, args, env, False)
922
923
924# Basic dc test.
925# @param exe   The executable.
926# @param args  The arguments to pass to the executable.
927# @param env   The environment.
928def test_dc1(exe, args, env):
929
930	child = pexpect.spawn(exe, args=args, env=env)
931
932	try:
933		write_str(child, "1pR")
934		check_line(child, "1")
935		write_str(child, "1pR")
936		check_line(child, "1")
937		write_str(child, "q")
938		send(child, "\n")
939		wait(child)
940	except pexpect.TIMEOUT:
941		traceback.print_tb(sys.exc_info()[2])
942		print("timed out")
943		print(str(child))
944		sys.exit(2)
945	except pexpect.EOF:
946		print("EOF")
947		print(str(child))
948		print(str(child.buffer))
949		print(str(child.before))
950		sys.exit(2)
951
952	return child
953
954
955# SIGINT with quit.
956# @param exe   The executable.
957# @param args  The arguments to pass to the executable.
958# @param env   The environment.
959def test_dc2(exe, args, env):
960
961	env["TERM"] = "dumb"
962
963	child = pexpect.spawn(exe, args=args, env=env)
964
965	try:
966		child.sendline("1pR")
967		check_line(child, "1", history=False)
968		time.sleep(1)
969		child.sendintr()
970		child.sendline("q")
971		wait(child)
972	except pexpect.TIMEOUT:
973		traceback.print_tb(sys.exc_info()[2])
974		print("timed out")
975		print(str(child))
976		sys.exit(2)
977	except pexpect.EOF:
978		print("EOF")
979		print(str(child))
980		print(str(child.buffer))
981		print(str(child.before))
982		sys.exit(2)
983
984	return child
985
986
987# Execute string.
988# @param exe   The executable.
989# @param args  The arguments to pass to the executable.
990# @param env   The environment.
991def test_dc3(exe, args, env):
992
993	child = pexpect.spawn(exe, args=args, env=env)
994
995	try:
996		write_str(child, "[1 15+pR]x")
997		check_line(child, "16")
998		write_str(child, "1pR")
999		check_line(child, "1")
1000		write_str(child, "q")
1001		send(child, "\n")
1002		wait(child)
1003	except pexpect.TIMEOUT:
1004		traceback.print_tb(sys.exc_info()[2])
1005		print("timed out")
1006		print(str(child))
1007		sys.exit(2)
1008	except pexpect.EOF:
1009		print("EOF")
1010		print(str(child))
1011		print(str(child.buffer))
1012		print(str(child.before))
1013		sys.exit(2)
1014
1015	return child
1016
1017
1018# The array of bc tests.
1019bc_tests = [
1020	test_bc_utf8_0,
1021	test_bc_utf8_1,
1022	test_bc_utf8_2,
1023	test_bc_utf8_3,
1024	test_bc_utf8_4,
1025	test_sigint_sigquit,
1026	test_eof,
1027	test_sigint,
1028	test_sigtstp,
1029	test_sigstop,
1030	test_bc1,
1031	test_bc2,
1032	test_bc3,
1033	test_bc4,
1034	test_bc5,
1035	test_bc6,
1036	test_bc7,
1037	test_bc8,
1038	test_bc9,
1039	test_bc10,
1040	test_bc11,
1041	test_bc12,
1042]
1043
1044# The array of dc tests.
1045dc_tests = [
1046	test_dc_utf8_0,
1047	test_dc_utf8_1,
1048	test_dc_utf8_2,
1049	test_dc_utf8_3,
1050	test_dc_utf8_4,
1051	test_sigint_sigquit,
1052	test_eof,
1053	test_sigint,
1054	test_dc1,
1055	test_dc2,
1056	test_dc3,
1057]
1058
1059
1060# Print the usage and exit with an error.
1061def usage():
1062	print("usage: {} [-t] dir [-a] test_idx [exe options...]".format(script))
1063	print("       The valid values for dir are: 'bc' and 'dc'.")
1064	print("       The max test_idx for bc is {}.".format(len(bc_tests) - 1))
1065	print("       The max test_idx for dc is {}.".format(len(dc_tests) - 1))
1066	print("       If -a is given, the number of tests for dir is printed.")
1067	print("       No tests are run.")
1068	sys.exit(1)
1069
1070
1071# Must run this script alone.
1072if __name__ != "__main__":
1073	usage()
1074
1075if len(sys.argv) < 2:
1076	usage()
1077
1078idx = 1
1079
1080exedir = sys.argv[idx]
1081
1082idx += 1
1083
1084if exedir == "-t":
1085	do_test = True
1086	exedir = sys.argv[idx]
1087	idx += 1
1088else:
1089	do_test = False
1090
1091test_idx = sys.argv[idx]
1092
1093idx += 1
1094
1095if test_idx == "-a":
1096	if exedir == "bc":
1097		l = len(bc_tests)
1098	else:
1099		l = len(dc_tests)
1100	print("{}".format(l))
1101	sys.exit(0)
1102
1103test_idx = int(test_idx)
1104
1105# Set a default executable unless we have one.
1106if len(sys.argv) >= idx + 1:
1107	exe = sys.argv[idx]
1108else:
1109	exe = testdir + "/../bin/" + exedir
1110
1111exebase = os.path.basename(exe)
1112
1113# Use the correct options.
1114if exebase == "bc":
1115	halt = "halt\n"
1116	options = "-l"
1117	test_array = bc_tests
1118else:
1119	halt = "q\n"
1120	options = "-x"
1121	test_array = dc_tests
1122
1123# More command-line processing.
1124if len(sys.argv) > idx + 1:
1125	exe = [ exe, sys.argv[idx + 1:], options ]
1126else:
1127	exe = [ exe, options ]
1128
1129# This is the environment necessary for most tests.
1130env = {
1131	"BC_BANNER": "1",
1132	"BC_PROMPT": "1",
1133	"DC_PROMPT": "1",
1134	"BC_TTY_MODE": "1",
1135	"DC_TTY_MODE": "1",
1136	"BC_SIGINT_RESET": "1",
1137	"DC_SIGINT_RESET": "1",
1138}
1139
1140# Make sure to include the outside environment.
1141env.update(os.environ)
1142env.pop("BC_ENV_ARGS", None)
1143env.pop("BC_LINE_LENGTH", None)
1144env.pop("DC_ENV_ARGS", None)
1145env.pop("DC_LINE_LENGTH", None)
1146
1147# Run the correct test.
1148child = test_array[test_idx](exe[0], exe[1:], env)
1149
1150child.close()
1151
1152exit = child.exitstatus
1153
1154if exit is not None and exit != 0:
1155	print("child failed; expected exit code 0, got {}".format(exit))
1156	print(str(child))
1157	sys.exit(1)
1158