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