Gavin Howard | fd06ac2 | 2018-10-10 16:48:32 -0600 | [diff] [blame] | 1 | #! /usr/bin/python3 -B |
| 2 | # |
Gavin Howard | 29e00ba | 2020-06-30 09:25:21 -0600 | [diff] [blame] | 3 | # SPDX-License-Identifier: BSD-2-Clause |
| 4 | # |
Gavin Howard | 4feb708 | 2021-01-26 01:19:25 -0700 | [diff] [blame^] | 5 | # Copyright (c) 2018-2021 Gavin D. Howard and contributors. |
Gavin Howard | fd06ac2 | 2018-10-10 16:48:32 -0600 | [diff] [blame] | 6 | # |
Gavin Howard | 7345cb9 | 2019-04-08 14:13:43 -0600 | [diff] [blame] | 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. |
Gavin Howard | fd06ac2 | 2018-10-10 16:48:32 -0600 | [diff] [blame] | 28 | # |
| 29 | |
| 30 | import os, errno |
| 31 | import random |
| 32 | import sys |
| 33 | import subprocess |
| 34 | |
Gavin Howard | fd06ac2 | 2018-10-10 16:48:32 -0600 | [diff] [blame] | 35 | def gen(limit=4): |
| 36 | return random.randint(0, 2 ** (8 * limit)) |
| 37 | |
| 38 | def negative(): |
| 39 | return random.randint(0, 1) == 1 |
| 40 | |
| 41 | def zero(): |
| 42 | return random.randint(0, 2 ** (8) - 1) == 0 |
| 43 | |
Gavin Howard | efa4d74 | 2018-10-11 11:59:59 -0600 | [diff] [blame] | 44 | def num(op, neg, real, z, limit=4): |
Gavin Howard | fd06ac2 | 2018-10-10 16:48:32 -0600 | [diff] [blame] | 45 | |
| 46 | if z: |
| 47 | z = zero() |
| 48 | else: |
| 49 | z = False |
| 50 | |
| 51 | if z: |
| 52 | return 0 |
| 53 | |
| 54 | if neg: |
| 55 | neg = negative() |
| 56 | |
| 57 | g = gen(limit) |
| 58 | |
| 59 | if real and negative(): |
Gavin Howard | c9ba1ee | 2018-10-12 08:43:10 -0600 | [diff] [blame] | 60 | n = str(gen(25)) |
Gavin Howard | 4f07e6e | 2018-10-13 20:29:52 -0600 | [diff] [blame] | 61 | length = gen(7 / 8) |
Gavin Howard | c9ba1ee | 2018-10-12 08:43:10 -0600 | [diff] [blame] | 62 | if len(n) < length: |
| 63 | n = ("0" * (length - len(n))) + n |
Gavin Howard | fd06ac2 | 2018-10-10 16:48:32 -0600 | [diff] [blame] | 64 | else: |
Gavin Howard | c9ba1ee | 2018-10-12 08:43:10 -0600 | [diff] [blame] | 65 | n = "0" |
Gavin Howard | fd06ac2 | 2018-10-10 16:48:32 -0600 | [diff] [blame] | 66 | |
| 67 | g = str(g) |
Gavin Howard | c9ba1ee | 2018-10-12 08:43:10 -0600 | [diff] [blame] | 68 | if n != "0": |
| 69 | g = g + "." + n |
Gavin Howard | fd06ac2 | 2018-10-10 16:48:32 -0600 | [diff] [blame] | 70 | |
| 71 | if neg and g != "0": |
Gavin Howard | efa4d74 | 2018-10-11 11:59:59 -0600 | [diff] [blame] | 72 | if op != modexp: |
| 73 | g = "-" + g |
| 74 | else: |
| 75 | g = "_" + g |
Gavin Howard | fd06ac2 | 2018-10-10 16:48:32 -0600 | [diff] [blame] | 76 | |
| 77 | return g |
| 78 | |
| 79 | |
Gavin Howard | a8f61ed | 2018-10-10 17:03:42 -0600 | [diff] [blame] | 80 | def add(test, op): |
Gavin Howard | fd06ac2 | 2018-10-10 16:48:32 -0600 | [diff] [blame] | 81 | |
Gavin Howard | 31a2275 | 2018-10-12 08:45:03 -0600 | [diff] [blame] | 82 | tests.append(test) |
| 83 | gen_ops.append(op) |
Gavin Howard | fd06ac2 | 2018-10-10 16:48:32 -0600 | [diff] [blame] | 84 | |
| 85 | def compare(exe, options, p, test, halt, expected, op, do_add=True): |
| 86 | |
| 87 | if p.returncode != 0: |
| 88 | |
| 89 | print(" {} returned an error ({})".format(exe, p.returncode)) |
| 90 | |
| 91 | if do_add: |
Gavin Howard | 4d00e1e | 2018-10-13 20:35:53 -0600 | [diff] [blame] | 92 | print(" adding to checklist...") |
Gavin Howard | a8f61ed | 2018-10-10 17:03:42 -0600 | [diff] [blame] | 93 | add(test, op) |
Gavin Howard | fd06ac2 | 2018-10-10 16:48:32 -0600 | [diff] [blame] | 94 | |
| 95 | return |
| 96 | |
| 97 | actual = p.stdout.decode() |
| 98 | |
| 99 | if actual != expected: |
| 100 | |
| 101 | if op >= exponent: |
| 102 | |
| 103 | indata = "scale += 10; {}; {}".format(test, halt) |
| 104 | args = [ exe, options ] |
| 105 | p2 = subprocess.run(args, input=indata.encode(), stdout=subprocess.PIPE, stderr=subprocess.PIPE) |
| 106 | expected = p2.stdout[:-10].decode() |
| 107 | |
| 108 | if actual == expected: |
| 109 | print(" failed because of bug in other {}".format(exe)) |
| 110 | print(" continuing...") |
| 111 | return |
| 112 | |
Gavin Howard | fd06ac2 | 2018-10-10 16:48:32 -0600 | [diff] [blame] | 113 | if do_add: |
Gavin Howard | 4d00e1e | 2018-10-13 20:35:53 -0600 | [diff] [blame] | 114 | print(" failed; adding to checklist...") |
Gavin Howard | a8f61ed | 2018-10-10 17:03:42 -0600 | [diff] [blame] | 115 | add(test, op) |
Gavin Howard | 4d00e1e | 2018-10-13 20:35:53 -0600 | [diff] [blame] | 116 | else: |
| 117 | print(" failed {}".format(test)) |
| 118 | print(" expected:") |
| 119 | print(" {}".format(expected)) |
| 120 | print(" actual:") |
| 121 | print(" {}".format(actual)) |
Gavin Howard | fd06ac2 | 2018-10-10 16:48:32 -0600 | [diff] [blame] | 122 | |
| 123 | |
| 124 | def gen_test(op): |
| 125 | |
Gavin Howard | efa4d74 | 2018-10-11 11:59:59 -0600 | [diff] [blame] | 126 | scale = num(op, False, False, True, 5 / 8) |
Gavin Howard | fd06ac2 | 2018-10-10 16:48:32 -0600 | [diff] [blame] | 127 | |
| 128 | if op < div: |
Gavin Howard | efa4d74 | 2018-10-11 11:59:59 -0600 | [diff] [blame] | 129 | s = fmts[op].format(scale, num(op, True, True, True), num(op, True, True, True)) |
Gavin Howard | fd06ac2 | 2018-10-10 16:48:32 -0600 | [diff] [blame] | 130 | elif op == div or op == mod: |
Gavin Howard | efa4d74 | 2018-10-11 11:59:59 -0600 | [diff] [blame] | 131 | s = fmts[op].format(scale, num(op, True, True, True), num(op, True, True, False)) |
Gavin Howard | fd06ac2 | 2018-10-10 16:48:32 -0600 | [diff] [blame] | 132 | elif op == power: |
Gavin Howard | efa4d74 | 2018-10-11 11:59:59 -0600 | [diff] [blame] | 133 | s = fmts[op].format(scale, num(op, True, True, True, 7 / 8), num(op, True, False, True, 6 / 8)) |
Gavin Howard | fd06ac2 | 2018-10-10 16:48:32 -0600 | [diff] [blame] | 134 | elif op == modexp: |
Gavin Howard | efa4d74 | 2018-10-11 11:59:59 -0600 | [diff] [blame] | 135 | s = fmts[op].format(scale, num(op, True, False, True), num(op, True, False, True), |
| 136 | num(op, True, False, False)) |
Gavin Howard | fd06ac2 | 2018-10-10 16:48:32 -0600 | [diff] [blame] | 137 | elif op == sqrt: |
| 138 | s = "1" |
| 139 | while s == "1": |
Gavin Howard | efa4d74 | 2018-10-11 11:59:59 -0600 | [diff] [blame] | 140 | s = num(op, False, True, True, 1) |
Gavin Howard | fd06ac2 | 2018-10-10 16:48:32 -0600 | [diff] [blame] | 141 | s = fmts[op].format(scale, s) |
| 142 | else: |
| 143 | |
| 144 | if op == exponent: |
Gavin Howard | efa4d74 | 2018-10-11 11:59:59 -0600 | [diff] [blame] | 145 | first = num(op, True, True, True, 6 / 8) |
Gavin Howard | fd06ac2 | 2018-10-10 16:48:32 -0600 | [diff] [blame] | 146 | elif op == bessel: |
Gavin Howard | efa4d74 | 2018-10-11 11:59:59 -0600 | [diff] [blame] | 147 | first = num(op, False, True, True, 6 / 8) |
Gavin Howard | fd06ac2 | 2018-10-10 16:48:32 -0600 | [diff] [blame] | 148 | else: |
Gavin Howard | efa4d74 | 2018-10-11 11:59:59 -0600 | [diff] [blame] | 149 | first = num(op, True, True, True) |
Gavin Howard | fd06ac2 | 2018-10-10 16:48:32 -0600 | [diff] [blame] | 150 | |
| 151 | if op != bessel: |
| 152 | s = fmts[op].format(scale, first) |
| 153 | else: |
| 154 | s = fmts[op].format(scale, first, 6 / 8) |
| 155 | |
| 156 | return s |
| 157 | |
| 158 | def run_test(t): |
| 159 | |
| 160 | op = random.randrange(bessel + 1) |
| 161 | |
| 162 | if op != modexp: |
| 163 | exe = "bc" |
| 164 | halt = "halt" |
| 165 | options = "-lq" |
| 166 | else: |
| 167 | exe = "dc" |
| 168 | halt = "q" |
Gavin Howard | 96f3e2e | 2019-06-23 09:18:07 -0600 | [diff] [blame] | 169 | options = "" |
Gavin Howard | fd06ac2 | 2018-10-10 16:48:32 -0600 | [diff] [blame] | 170 | |
| 171 | test = gen_test(op) |
| 172 | |
Gavin Howard | ddc1696 | 2018-10-12 08:46:01 -0600 | [diff] [blame] | 173 | if "c(0)" in test or "scale = 4; j(4" in test: |
| 174 | return |
| 175 | |
Gavin Howard | fd06ac2 | 2018-10-10 16:48:32 -0600 | [diff] [blame] | 176 | bcexe = exedir + "/" + exe |
| 177 | indata = test + "\n" + halt |
| 178 | |
Gavin Howard | fd06ac2 | 2018-10-10 16:48:32 -0600 | [diff] [blame] | 179 | print("Test {}: {}".format(t, test)) |
| 180 | |
Gavin Howard | e2b88b5 | 2019-06-23 12:29:36 -0600 | [diff] [blame] | 181 | if exe == "bc": |
| 182 | args = [ exe, options ] |
| 183 | else: |
| 184 | args = [ exe ] |
Gavin Howard | a8f61ed | 2018-10-10 17:03:42 -0600 | [diff] [blame] | 185 | |
Gavin Howard | fd06ac2 | 2018-10-10 16:48:32 -0600 | [diff] [blame] | 186 | p = subprocess.run(args, input=indata.encode(), stdout=subprocess.PIPE, stderr=subprocess.PIPE) |
| 187 | |
Gavin Howard | a8f61ed | 2018-10-10 17:03:42 -0600 | [diff] [blame] | 188 | output1 = p.stdout.decode() |
| 189 | |
| 190 | if p.returncode != 0 or output1 == "": |
Gavin Howard | fd06ac2 | 2018-10-10 16:48:32 -0600 | [diff] [blame] | 191 | print(" other {} returned an error ({}); continuing...".format(exe, p.returncode)) |
| 192 | return |
| 193 | |
Gavin Howard | 3e4ce69 | 2018-10-13 18:29:24 -0600 | [diff] [blame] | 194 | if output1 == "\n": |
| 195 | print(" other {} has a bug; continuing...".format(exe)) |
| 196 | return |
| 197 | |
| 198 | if output1 == "-0\n": |
| 199 | output1 = "0\n" |
Gavin Howard | 1fe84c4 | 2018-10-13 20:00:28 -0600 | [diff] [blame] | 200 | elif output1 == "-0": |
| 201 | output1 = "0" |
Gavin Howard | 3e4ce69 | 2018-10-13 18:29:24 -0600 | [diff] [blame] | 202 | |
Gavin Howard | fd06ac2 | 2018-10-10 16:48:32 -0600 | [diff] [blame] | 203 | args = [ bcexe, options ] |
| 204 | |
| 205 | p = subprocess.run(args, input=indata.encode(), stdout=subprocess.PIPE, stderr=subprocess.PIPE) |
| 206 | compare(exe, options, p, test, halt, output1, op) |
| 207 | |
| 208 | |
| 209 | if __name__ != "__main__": |
| 210 | sys.exit(1) |
| 211 | |
| 212 | script = sys.argv[0] |
| 213 | testdir = os.path.dirname(script) |
| 214 | |
Gavin Howard | 5f361b4 | 2018-10-12 18:06:59 -0600 | [diff] [blame] | 215 | exedir = testdir + "/../bin" |
Gavin Howard | fd06ac2 | 2018-10-10 16:48:32 -0600 | [diff] [blame] | 216 | |
| 217 | ops = [ '+', '-', '*', '/', '%', '^', '|' ] |
| 218 | files = [ "add", "subtract", "multiply", "divide", "modulus", "power", "modexp", |
| 219 | "sqrt", "exponent", "log", "arctangent", "sine", "cosine", "bessel" ] |
| 220 | funcs = [ "sqrt", "e", "l", "a", "s", "c", "j" ] |
| 221 | |
| 222 | fmts = [ "scale = {}; {} + {}", "scale = {}; {} - {}", "scale = {}; {} * {}", |
| 223 | "scale = {}; {} / {}", "scale = {}; {} % {}", "scale = {}; {} ^ {}", |
| 224 | "{}k {} {} {}|pR", "scale = {}; sqrt({})", "scale = {}; e({})", |
| 225 | "scale = {}; l({})", "scale = {}; a({})", "scale = {}; s({})", |
| 226 | "scale = {}; c({})", "scale = {}; j({}, {})" ] |
| 227 | |
| 228 | div = 3 |
| 229 | mod = 4 |
| 230 | power = 5 |
| 231 | modexp = 6 |
| 232 | sqrt = 7 |
| 233 | exponent = 8 |
| 234 | bessel = 13 |
| 235 | |
Gavin Howard | 31a2275 | 2018-10-12 08:45:03 -0600 | [diff] [blame] | 236 | gen_ops = [] |
| 237 | tests = [] |
Gavin Howard | fd06ac2 | 2018-10-10 16:48:32 -0600 | [diff] [blame] | 238 | |
| 239 | try: |
| 240 | i = 0 |
| 241 | while True: |
| 242 | run_test(i) |
| 243 | i = i + 1 |
| 244 | except KeyboardInterrupt: |
| 245 | pass |
| 246 | |
Gavin Howard | 8972240 | 2018-10-12 10:43:20 -0600 | [diff] [blame] | 247 | if len(tests) == 0: |
Gavin Howard | fd06ac2 | 2018-10-10 16:48:32 -0600 | [diff] [blame] | 248 | print("\nNo items in checklist.") |
| 249 | print("Exiting") |
| 250 | sys.exit(0) |
| 251 | |
| 252 | print("\nGoing through the checklist...\n") |
| 253 | |
Gavin Howard | 31a2275 | 2018-10-12 08:45:03 -0600 | [diff] [blame] | 254 | if len(tests) != len(gen_ops): |
Gavin Howard | fd06ac2 | 2018-10-10 16:48:32 -0600 | [diff] [blame] | 255 | print("Corrupted checklist!") |
| 256 | print("Exiting...") |
| 257 | sys.exit(1) |
| 258 | |
| 259 | for i in range(0, len(tests)): |
| 260 | |
Gavin Howard | fd06ac2 | 2018-10-10 16:48:32 -0600 | [diff] [blame] | 261 | print("\n{}".format(tests[i])) |
| 262 | |
Gavin Howard | 31a2275 | 2018-10-12 08:45:03 -0600 | [diff] [blame] | 263 | op = int(gen_ops[i]) |
Gavin Howard | a8f61ed | 2018-10-10 17:03:42 -0600 | [diff] [blame] | 264 | |
Gavin Howard | a8f61ed | 2018-10-10 17:03:42 -0600 | [diff] [blame] | 265 | if op != modexp: |
Gavin Howard | fd06ac2 | 2018-10-10 16:48:32 -0600 | [diff] [blame] | 266 | exe = "bc" |
| 267 | halt = "halt" |
| 268 | options = "-lq" |
| 269 | else: |
| 270 | exe = "dc" |
| 271 | halt = "q" |
| 272 | options = "" |
| 273 | |
Gavin Howard | fd06ac2 | 2018-10-10 16:48:32 -0600 | [diff] [blame] | 274 | indata = tests[i] + "\n" + halt |
| 275 | |
Gavin Howard | a8f61ed | 2018-10-10 17:03:42 -0600 | [diff] [blame] | 276 | args = [ exe, options ] |
| 277 | |
Gavin Howard | fd06ac2 | 2018-10-10 16:48:32 -0600 | [diff] [blame] | 278 | p = subprocess.run(args, input=indata.encode(), stdout=subprocess.PIPE, stderr=subprocess.PIPE) |
| 279 | |
Gavin Howard | a8f61ed | 2018-10-10 17:03:42 -0600 | [diff] [blame] | 280 | expected = p.stdout.decode() |
| 281 | |
| 282 | bcexe = exedir + "/" + exe |
Gavin Howard | a8f61ed | 2018-10-10 17:03:42 -0600 | [diff] [blame] | 283 | args = [ bcexe, options ] |
| 284 | |
| 285 | p = subprocess.run(args, input=indata.encode(), stdout=subprocess.PIPE, stderr=subprocess.PIPE) |
| 286 | |
| 287 | compare(exe, options, p, tests[i], halt, expected, op, False) |
Gavin Howard | fd06ac2 | 2018-10-10 16:48:32 -0600 | [diff] [blame] | 288 | |
Gavin Howard | 06e0243 | 2018-10-12 08:46:28 -0600 | [diff] [blame] | 289 | answer = input("\nAdd test ({}/{}) to test suite? [y/N]: ".format(i + 1, len(tests))) |
Gavin Howard | fd06ac2 | 2018-10-10 16:48:32 -0600 | [diff] [blame] | 290 | |
| 291 | if 'Y' in answer or 'y' in answer: |
Gavin Howard | 0f46afc | 2018-10-26 10:25:08 -0600 | [diff] [blame] | 292 | |
Gavin Howard | fd06ac2 | 2018-10-10 16:48:32 -0600 | [diff] [blame] | 293 | print("Yes") |
Gavin Howard | fd06ac2 | 2018-10-10 16:48:32 -0600 | [diff] [blame] | 294 | |
Gavin Howard | a8f61ed | 2018-10-10 17:03:42 -0600 | [diff] [blame] | 295 | name = testdir + "/" + exe + "/" + files[op] |
Gavin Howard | fd06ac2 | 2018-10-10 16:48:32 -0600 | [diff] [blame] | 296 | |
| 297 | with open(name + ".txt", "a") as f: |
Gavin Howard | e9238d2 | 2019-05-14 12:40:29 -0600 | [diff] [blame] | 298 | f.write(tests[i] + "\n") |
Gavin Howard | fd06ac2 | 2018-10-10 16:48:32 -0600 | [diff] [blame] | 299 | |
| 300 | with open(name + "_results.txt", "a") as f: |
Gavin Howard | a8f61ed | 2018-10-10 17:03:42 -0600 | [diff] [blame] | 301 | f.write(expected) |
Gavin Howard | fd06ac2 | 2018-10-10 16:48:32 -0600 | [diff] [blame] | 302 | |
| 303 | else: |
| 304 | print("No") |
| 305 | |
Gavin Howard | 31a2275 | 2018-10-12 08:45:03 -0600 | [diff] [blame] | 306 | print("Done!") |