3 # SPDX-License-Identifier: BSD-2-Clause
5 # Copyright (c) 2018-2021 Gavin D. Howard and contributors.
7 # Redistribution and use in source and binary forms, with or without
8 # modification, are permitted provided that the following conditions are met:
10 # * Redistributions of source code must retain the above copyright notice, this
11 # list of conditions and the following disclaimer.
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.
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.
38 print("Could not find pexpect. Skipping...")
43 testdir = os.path.dirname(script)
45 if "BC_TEST_OUTPUT_DIR" in os.environ:
46 outputdir = os.environ["BC_TEST_OUTPUT_DIR"]
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.
62 utf8_stress1 = "ᆬḰ䋔䗅㜲ತ咡䒢岤䳰稨⣡嶣㷡嶏ⵐ䄺嵕ਅ奰痚㆜䊛拂䅙૩➋䛿ቬ竳Ϳᅠ❄产翷䮊௷Ỉ䷒䳜㛠➕傎ᗋᏯਕ䆐悙癐㺨"
63 utf8_stress2 = "韠싧돳넨큚ꉿ뮴픷ꉲ긌�최릙걆鳬낽ꪁ퍼鈴핐黙헶ꪈ뮩쭀锻끥鉗겉욞며뛯�ﻼ�度錐�"
64 utf8_stress3 = "곻�䣹昲蜴Ὓ桢㎏⚦珢畣갴ﭱ鶶ๅ뀁彻ꖒ䔾ꢚﱤ햔햞㐹�鼳뵡▿ⶾ꠩�纞⊐佧�ⵟ霘紳㱔籠뎼⊓搧硤"
65 utf8_stress4 = "ᄀ𖢾🏴��"
67 # An easy array for UTF-8 tests.
76 def expect(child, data):
80 # Eats all of the child's data.
81 # @param child The child whose data should be eaten.
83 while child.buffer is not None and len(child.buffer) > 0:
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.
90 def send(child, data):
100 child.kill(signal.SIGTERM)
103 child.kill(signal.SIGKILL)
107 # Check that the child output the expected line. If history is false, then
108 # the output should change.
109 def check_line(child, expected, prompt=">>> ", history=True):
111 prefix = "\r\n" if history else ""
112 expect(child, prefix + expected + "\r\n" + prompt)
115 # Write a string to output, checking all of the characters are output,
117 def write_str(child, s):
121 expect(child, "\\{}".format(c))
126 # Check the bc banner.
127 # @param child The child process.
128 def 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)
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.
146 def test_utf8(exe, args, env, idx, bc=True):
148 # Because both bc and dc use this, make sure the banner doesn't pop.
149 env["BC_BANNER"] = "0"
151 child = pexpect.spawn(exe, args=args, env=env, encoding='utf-8', codec_errors='ignore')
155 # Write the stress string.
156 send(child, utf8_stress_strs[idx])
168 except pexpect.TIMEOUT:
169 traceback.print_tb(sys.exc_info()[2])
176 print(str(child.buffer))
177 print(str(child.before))
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.
186 def test_utf8_0(exe, args, env, bc=True):
188 # Because both bc and dc use this, make sure the banner doesn't pop.
189 env["BC_BANNER"] = "0"
191 child = pexpect.spawn(exe, args=args, env=env, encoding='utf-8', codec_errors='ignore')
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ℐ")
210 except pexpect.TIMEOUT:
211 traceback.print_tb(sys.exc_info()[2])
218 print(str(child.buffer))
219 print(str(child.before))
225 def test_utf8_1(exe, args, env, bc=True):
226 return test_utf8(exe, args, env, 0, bc)
229 def test_utf8_2(exe, args, env, bc=True):
230 return test_utf8(exe, args, env, 1, bc)
233 def test_utf8_3(exe, args, env, bc=True):
234 return test_utf8(exe, args, env, 2, bc)
237 def test_utf8_4(exe, args, env, bc=True):
238 return test_utf8(exe, args, env, 3, bc)
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.
245 def test_sigint_sigquit(exe, args, env):
247 # Because both bc and dc use this, make sure the banner doesn't pop.
248 env["BC_BANNER"] = "0"
250 child = pexpect.spawn(exe, args=args, env=env)
258 except pexpect.TIMEOUT:
259 traceback.print_tb(sys.exc_info()[2])
266 print(str(child.buffer))
267 print(str(child.before))
274 # @param exe The executable.
275 # @param args The arguments to pass to the executable.
276 # @param env The environment.
277 def test_eof(exe, args, env):
279 # Because both bc and dc use this, make sure the banner doesn't pop.
280 env["BC_BANNER"] = "0"
282 child = pexpect.spawn(exe, args=args, env=env)
289 except pexpect.TIMEOUT:
290 traceback.print_tb(sys.exc_info()[2])
297 print(str(child.buffer))
298 print(str(child.before))
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.
308 def test_sigint(exe, args, env):
310 # Because both bc and dc use this, make sure the banner doesn't pop.
311 env["BC_BANNER"] = "0"
313 env["BC_SIGINT_RESET"] = "0"
314 env["DC_SIGINT_RESET"] = "0"
316 child = pexpect.spawn(exe, args=args, env=env)
323 except pexpect.TIMEOUT:
324 traceback.print_tb(sys.exc_info()[2])
331 print(str(child.buffer))
332 print(str(child.before))
339 # @param exe The executable.
340 # @param args The arguments to pass to the executable.
341 # @param env The environment.
342 def test_sigtstp(exe, args, env):
344 # This test does not work on FreeBSD, so skip.
345 if sys.platform.startswith("freebsd"):
348 # Because both bc and dc use this, make sure the banner doesn't pop.
349 env["BC_BANNER"] = "0"
351 child = pexpect.spawn(exe, args=args, env=env)
358 if not child.isalive():
359 print("child exited early")
361 print(str(child.buffer))
363 child.kill(signal.SIGCONT)
367 except pexpect.TIMEOUT:
368 traceback.print_tb(sys.exc_info()[2])
375 print(str(child.buffer))
376 print(str(child.before))
383 # @param exe The executable.
384 # @param args The arguments to pass to the executable.
385 # @param env The environment.
386 def test_sigstop(exe, args, env):
388 # Because both bc and dc use this, make sure the banner doesn't pop.
389 env["BC_BANNER"] = "0"
391 child = pexpect.spawn(exe, args=args, env=env)
398 if not child.isalive():
399 print("child exited early")
401 print(str(child.buffer))
405 if not child.isalive():
406 print("child exited early")
408 print(str(child.buffer))
410 child.kill(signal.SIGCONT)
414 except pexpect.TIMEOUT:
415 traceback.print_tb(sys.exc_info()[2])
422 print(str(child.buffer))
423 print(str(child.before))
429 def test_bc_utf8_0(exe, args, env):
430 return test_utf8_0(exe, args, env, True)
433 def test_bc_utf8_1(exe, args, env):
434 return test_utf8_1(exe, args, env, True)
437 def test_bc_utf8_2(exe, args, env):
438 return test_utf8_2(exe, args, env, True)
441 def test_bc_utf8_3(exe, args, env):
442 return test_utf8_3(exe, args, env, True)
445 def test_bc_utf8_4(exe, args, env):
446 return test_utf8_4(exe, args, env, True)
450 # @param exe The executable.
451 # @param args The arguments to pass to the executable.
452 # @param env The environment.
453 def test_bc1(exe, args, env):
455 child = pexpect.spawn(exe, args=args, env=env)
459 write_str(child, "1")
460 check_line(child, "1")
461 write_str(child, "1")
462 check_line(child, "1")
466 except pexpect.TIMEOUT:
467 traceback.print_tb(sys.exc_info()[2])
474 print(str(child.buffer))
475 print(str(child.before))
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.
485 def test_bc2(exe, args, env):
489 child = pexpect.spawn(exe, args=args, env=env)
494 check_line(child, "1", history=False)
497 child.sendline("quit")
499 except pexpect.TIMEOUT:
500 traceback.print_tb(sys.exc_info()[2])
507 print(str(child.buffer))
508 print(str(child.before))
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.
518 def test_bc3(exe, args, env):
520 child = pexpect.spawn(exe, args=args, env=env)
524 send(child, "\x1b[D\x1b[D\x1b[C\x1b[C")
526 expect(child, prompt)
527 send(child, "12\x1b[D3\x1b[C4\x1bOD5\x1bOC6")
529 check_line(child, "132546")
530 send(child, "12\x023\x064")
532 check_line(child, "1324")
533 send(child, "12\x1b[H3\x1bOH\x01\x1b[H45\x1bOF6\x05\x1b[F7\x1bOH8")
535 check_line(child, "84531267")
539 except pexpect.TIMEOUT:
540 traceback.print_tb(sys.exc_info()[2])
547 print(str(child.buffer))
548 print(str(child.before))
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.
558 def test_bc4(exe, args, env):
560 child = pexpect.spawn(exe, args=args, env=env)
564 send(child, "\x1b[A\x1bOA\x1b[B\x1bOB")
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")
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")
578 check_line(child, "65536")
582 except pexpect.TIMEOUT:
583 traceback.print_tb(sys.exc_info()[2])
590 print(str(child.buffer))
591 print(str(child.before))
598 # @param exe The executable.
599 # @param args The arguments to pass to the executable.
600 # @param env The environment.
601 def test_bc5(exe, args, env):
603 child = pexpect.spawn(exe, args=args, env=env)
611 except pexpect.TIMEOUT:
612 traceback.print_tb(sys.exc_info()[2])
619 print(str(child.buffer))
620 print(str(child.before))
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.
630 def test_bc6(exe, args, env):
632 child = pexpect.spawn(exe, args=args, env=env)
636 send(child, "print \"Enter number: \"")
638 expect(child, "Enter number: ")
639 send(child, "4\x1b[A\x1b[A")
644 except pexpect.TIMEOUT:
645 traceback.print_tb(sys.exc_info()[2])
652 print(str(child.buffer))
653 print(str(child.before))
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.
663 def test_bc7(exe, args, env):
665 child = pexpect.spawn(exe, args=args, env=env)
669 send(child, "\x1bb\x1bb\x1bf\x1bf")
671 expect(child, prompt)
672 send(child, "\x1b[0~\x1b[3a")
674 expect(child, prompt)
675 send(child, "\x1b[0;4\x1b[0A")
677 expect(child, prompt)
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")
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 ")
692 check_line(child, "284")
696 except pexpect.TIMEOUT:
697 traceback.print_tb(sys.exc_info()[2])
704 print(str(child.buffer))
705 print(str(child.before))
712 # @param exe The executable.
713 # @param args The arguments to pass to the executable.
714 # @param env The environment.
715 def test_bc8(exe, args, env):
717 child = pexpect.spawn(exe, args=args, env=env)
721 send(child, "12\x1b[D3\x1b[C4\x08\x7f")
723 check_line(child, "13")
727 except pexpect.TIMEOUT:
728 traceback.print_tb(sys.exc_info()[2])
735 print(str(child.buffer))
736 print(str(child.before))
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.
746 def test_bc9(exe, args, env):
748 child = pexpect.spawn(exe, args=args, env=env)
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")
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")
760 check_line(child, "102")
761 send(child, "\x1b[A")
762 send(child, "\x17\x17")
764 check_line(child, "46")
765 send(child, "\x17\x17")
767 expect(child, prompt)
771 except pexpect.TIMEOUT:
772 traceback.print_tb(sys.exc_info()[2])
779 print(str(child.buffer))
780 print(str(child.before))
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.
790 def test_bc10(exe, args, env):
792 child = pexpect.spawn(exe, args=args, env=env)
796 send(child, "\x1b[3~\x1b[3~")
798 expect(child, prompt)
799 send(child, " \x1b[3~\x1b[3~")
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")
806 check_line(child, "270")
807 send(child, "\x1b[A\x1b[0;5D\x1b[0;5D\x0b")
809 check_line(child, "180")
810 send(child, "\x1b[A\x1521")
811 check_line(child, "21")
815 except pexpect.TIMEOUT:
816 traceback.print_tb(sys.exc_info()[2])
823 print(str(child.buffer))
824 print(str(child.before))
831 # @param exe The executable.
832 # @param args The arguments to pass to the executable.
833 # @param env The environment.
834 def test_bc11(exe, args, env):
836 child = pexpect.spawn(exe, args=args, env=env)
840 send(child, "\x1b[A\x02\x14")
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")
850 except pexpect.TIMEOUT:
851 traceback.print_tb(sys.exc_info()[2])
858 print(str(child.buffer))
859 print(str(child.before))
866 # @param exe The executable.
867 # @param args The arguments to pass to the executable.
868 # @param env The environment.
869 def test_bc12(exe, args, env):
871 child = pexpect.spawn(exe, args=args, env=env)
878 if not child.isalive():
879 print("child exited early")
881 print(str(child.buffer))
886 except pexpect.TIMEOUT:
887 traceback.print_tb(sys.exc_info()[2])
894 print(str(child.buffer))
895 print(str(child.before))
901 def test_dc_utf8_0(exe, args, env):
902 return test_utf8_0(exe, args, env, False)
905 def test_dc_utf8_1(exe, args, env):
906 return test_utf8_1(exe, args, env, False)
909 def test_dc_utf8_2(exe, args, env):
910 return test_utf8_2(exe, args, env, False)
913 def test_dc_utf8_3(exe, args, env):
914 return test_utf8_3(exe, args, env, False)
917 def test_dc_utf8_4(exe, args, env):
918 return test_utf8_4(exe, args, env, False)
922 # @param exe The executable.
923 # @param args The arguments to pass to the executable.
924 # @param env The environment.
925 def test_dc1(exe, args, env):
927 child = pexpect.spawn(exe, args=args, env=env)
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")
937 except pexpect.TIMEOUT:
938 traceback.print_tb(sys.exc_info()[2])
945 print(str(child.buffer))
946 print(str(child.before))
953 # @param exe The executable.
954 # @param args The arguments to pass to the executable.
955 # @param env The environment.
956 def test_dc2(exe, args, env):
960 child = pexpect.spawn(exe, args=args, env=env)
963 child.sendline("1pR")
964 check_line(child, "1", history=False)
969 except pexpect.TIMEOUT:
970 traceback.print_tb(sys.exc_info()[2])
977 print(str(child.buffer))
978 print(str(child.before))
985 # @param exe The executable.
986 # @param args The arguments to pass to the executable.
987 # @param env The environment.
988 def test_dc3(exe, args, env):
990 child = pexpect.spawn(exe, args=args, env=env)
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")
1000 except pexpect.TIMEOUT:
1001 traceback.print_tb(sys.exc_info()[2])
1008 print(str(child.buffer))
1009 print(str(child.before))
1015 # The array of bc tests.
1022 test_sigint_sigquit,
1041 # The array of dc tests.
1047 test_sigint_sigquit,
1056 # Print the usage and exit with an error.
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.")
1067 # Must run this script alone.
1068 if __name__ != "__main__":
1071 if len(sys.argv) < 2:
1076 exedir = sys.argv[idx]
1082 exedir = sys.argv[idx]
1087 test_idx = sys.argv[idx]
1091 if test_idx == "-a":
1096 print("{}".format(l))
1099 test_idx = int(test_idx)
1101 # Set a default executable unless we have one.
1102 if len(sys.argv) >= idx + 1:
1105 exe = testdir + "/../bin/" + exedir
1107 exebase = os.path.basename(exe)
1109 # Use the correct options.
1113 test_array = bc_tests
1117 test_array = dc_tests
1119 # More command-line processing.
1120 if len(sys.argv) > idx + 1:
1121 exe = [ exe, sys.argv[idx + 1:], options ]
1123 exe = [ exe, options ]
1125 # This is the environment necessary for most tests.
1132 "BC_SIGINT_RESET": "1",
1133 "DC_SIGINT_RESET": "1",
1136 # Make sure to include the outside environment.
1137 env.update(os.environ)
1138 env.pop("BC_ENV_ARGS", None)
1139 env.pop("BC_LINE_LENGTH", None)
1140 env.pop("DC_ENV_ARGS", None)
1141 env.pop("DC_LINE_LENGTH", None)
1143 # Run the correct test.
1144 child = test_array[test_idx](exe[0], exe[1:], env)
1148 exit = child.exitstatus
1150 if exit is not None and exit != 0:
1151 print("child failed; expected exit code 0, got {}".format(exit))