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