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