123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352 |
- #!/usr/bin/python
- # Copyright 2017 the V8 project authors. All rights reserved.
- # Use of this source code is governed by a BSD-style license that can be
- # found in the LICENSE file.
- # for py2/py3 compatibility
- from __future__ import print_function
- import argparse
- import math
- import multiprocessing
- import os
- import random
- import subprocess
- import sys
- import tempfile
- # Configuration.
- kChars = "0123456789abcdef"
- kBase = 16
- kLineLength = 70 # A bit less than 80.
- kNumInputsGenerate = 20
- kNumInputsStress = 1000
- # Internally used sentinels.
- kNo = 0
- kYes = 1
- kRandom = 2
- TEST_HEADER = """\
- // Copyright 2017 the V8 project authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
- // Generated by %s.
- """ % sys.argv[0]
- TEST_BODY = """
- var error_count = 0;
- for (var i = 0; i < data.length; i++) {
- var d = data[i];
- %s
- }
- if (error_count !== 0) {
- print("Finished with " + error_count + " errors.")
- quit(1);
- }"""
- def GenRandom(length, negative=kRandom):
- if length == 0: return "0n"
- s = []
- if negative == kYes or (negative == kRandom and (random.randint(0, 1) == 0)):
- s.append("-") # 50% chance of negative.
- s.append("0x")
- s.append(kChars[random.randint(1, kBase - 1)]) # No leading zero.
- for i in range(1, length):
- s.append(kChars[random.randint(0, kBase - 1)])
- s.append("n")
- return "".join(s)
- def Parse(x):
- assert x[-1] == 'n', x
- return int(x[:-1], kBase)
- def Format(x):
- original = x
- negative = False
- if x == 0: return "0n"
- if x < 0:
- negative = True
- x = -x
- s = ""
- while x > 0:
- s = kChars[x % kBase] + s
- x = x / kBase
- s = "0x" + s + "n"
- if negative:
- s = "-" + s
- assert Parse(s) == original
- return s
- class TestGenerator(object):
- # Subclasses must implement these.
- # Returns a JSON snippet defining inputs and expected output for one test.
- def EmitOne(self): raise NotImplementedError
- # Returns a snippet of JavaScript that will operate on a variable "d"
- # whose content is defined by the result of a call to "EmitOne".
- def EmitTestCore(self): raise NotImplementedError
- def EmitHeader(self):
- return TEST_HEADER
- def EmitData(self, count):
- s = []
- for i in range(count):
- s.append(self.EmitOne())
- return "var data = [" + ", ".join(s) + "];"
- def EmitTestBody(self):
- return TEST_BODY % self.EmitTestCore()
- def PrintTest(self, count):
- print(self.EmitHeader())
- print(self.EmitData(count))
- print(self.EmitTestBody())
- def RunTest(self, count, binary):
- try:
- fd, path = tempfile.mkstemp(suffix=".js", prefix="bigint-test-")
- with open(path, "w") as f:
- f.write(self.EmitData(count))
- f.write(self.EmitTestBody())
- return subprocess.call("%s %s" % (binary, path),
- shell=True)
- finally:
- os.close(fd)
- os.remove(path)
- class UnaryOp(TestGenerator):
- # Subclasses must implement these two.
- def GetOpString(self): raise NotImplementedError
- def GenerateResult(self, x): raise NotImplementedError
- # Subclasses may override this:
- def GenerateInput(self):
- return GenRandom(random.randint(0, kLineLength))
- # Subclasses should not override anything below.
- def EmitOne(self):
- x_str = self.GenerateInput()
- x_num = Parse(x_str)
- result_num = self.GenerateResult(x_num)
- result_str = Format(result_num)
- return "{\n a: %s,\n r: %s\n}" % (x_str, result_str)
- def EmitTestCore(self):
- return """\
- var r = %(op)sd.a;
- if (d.r !== r) {
- print("Input: " + d.a.toString(%(base)d));
- print("Result: " + r.toString(%(base)d));
- print("Expected: " + d.r);
- error_count++;
- }""" % {"op": self.GetOpString(), "base": kBase}
- class BinaryOp(TestGenerator):
- # Subclasses must implement these two.
- def GetOpString(self): raise NotImplementedError
- def GenerateResult(self, left, right): raise NotImplementedError
- # Subclasses may override these:
- def GenerateInputLengths(self):
- return random.randint(0, kLineLength), random.randint(0, kLineLength)
- def GenerateInputs(self):
- left_length, right_length = self.GenerateInputLengths()
- return GenRandom(left_length), GenRandom(right_length)
- # Subclasses should not override anything below.
- def EmitOne(self):
- left_str, right_str = self.GenerateInputs()
- left_num = Parse(left_str)
- right_num = Parse(right_str)
- result_num = self.GenerateResult(left_num, right_num)
- result_str = Format(result_num)
- return ("{\n a: %s,\n b: %s,\n r: %s\n}" %
- (left_str, right_str, result_str))
- def EmitTestCore(self):
- return """\
- var r = d.a %(op)s d.b;
- if (d.r !== r) {
- print("Input A: " + d.a.toString(%(base)d));
- print("Input B: " + d.b.toString(%(base)d));
- print("Result: " + r.toString(%(base)d));
- print("Expected: " + d.r);
- print("Op: %(op)s");
- error_count++;
- }""" % {"op": self.GetOpString(), "base": kBase}
- class Neg(UnaryOp):
- def GetOpString(self): return "-"
- def GenerateResult(self, x): return -x
- class BitNot(UnaryOp):
- def GetOpString(self): return "~"
- def GenerateResult(self, x): return ~x
- class Inc(UnaryOp):
- def GetOpString(self): return "++"
- def GenerateResult(self, x): return x + 1
- class Dec(UnaryOp):
- def GetOpString(self): return "--"
- def GenerateResult(self, x): return x - 1
- class Add(BinaryOp):
- def GetOpString(self): return "+"
- def GenerateResult(self, a, b): return a + b
- class Sub(BinaryOp):
- def GetOpString(self): return "-"
- def GenerateResult(self, a, b): return a - b
- class Mul(BinaryOp):
- def GetOpString(self): return "*"
- def GenerateResult(self, a, b): return a * b
- def GenerateInputLengths(self):
- left_length = random.randint(1, kLineLength)
- return left_length, kLineLength - left_length
- class Div(BinaryOp):
- def GetOpString(self): return "/"
- def GenerateResult(self, a, b):
- result = abs(a) / abs(b)
- if (a < 0) != (b < 0): result = -result
- return result
- def GenerateInputLengths(self):
- # Let the left side be longer than the right side with high probability,
- # because that case is more interesting.
- min_left = kLineLength * 6 / 10
- max_right = kLineLength * 7 / 10
- return random.randint(min_left, kLineLength), random.randint(1, max_right)
- class Mod(Div): # Sharing GenerateInputLengths.
- def GetOpString(self): return "%"
- def GenerateResult(self, a, b):
- result = a % b
- if a < 0 and result > 0:
- result -= abs(b)
- if a > 0 and result < 0:
- result += abs(b)
- return result
- class Shl(BinaryOp):
- def GetOpString(self): return "<<"
- def GenerateInputsInternal(self, small_shift_positive):
- left_length = random.randint(0, kLineLength - 1)
- left = GenRandom(left_length)
- small_shift = random.randint(0, 1) == 0
- if small_shift:
- right_length = 1 + int(math.log((kLineLength - left_length), kBase))
- neg = kNo if small_shift_positive else kYes
- else:
- right_length = random.randint(0, 3)
- neg = kYes if small_shift_positive else kNo
- right = GenRandom(right_length, negative=neg)
- return left, right
- def GenerateInputs(self): return self.GenerateInputsInternal(True)
- def GenerateResult(self, a, b):
- if b < 0: return a >> -b
- return a << b
- class Sar(Shl): # Sharing GenerateInputsInternal.
- def GetOpString(self): return ">>"
- def GenerateInputs(self):
- return self.GenerateInputsInternal(False)
- def GenerateResult(self, a, b):
- if b < 0: return a << -b
- return a >> b
- class BitAnd(BinaryOp):
- def GetOpString(self): return "&"
- def GenerateResult(self, a, b): return a & b
- class BitOr(BinaryOp):
- def GetOpString(self): return "|"
- def GenerateResult(self, a, b): return a | b
- class BitXor(BinaryOp):
- def GetOpString(self): return "^"
- def GenerateResult(self, a, b): return a ^ b
- OPS = {
- "add": Add,
- "sub": Sub,
- "mul": Mul,
- "div": Div,
- "mod": Mod,
- "inc": Inc,
- "dec": Dec,
- "neg": Neg,
- "not": BitNot,
- "shl": Shl,
- "sar": Sar,
- "and": BitAnd,
- "or": BitOr,
- "xor": BitXor
- }
- OPS_NAMES = ", ".join(sorted(OPS.keys()))
- def RunOne(op, num_inputs, binary):
- return OPS[op]().RunTest(num_inputs, binary)
- def WrapRunOne(args):
- return RunOne(*args)
- def RunAll(args):
- for op in args.op:
- for r in range(args.runs):
- yield (op, args.num_inputs, args.binary)
- def Main():
- parser = argparse.ArgumentParser(
- description="Helper for generating or running BigInt tests.")
- parser.add_argument(
- "action", help="Action to perform: 'generate' or 'stress'")
- parser.add_argument(
- "op", nargs="+",
- help="Operation(s) to test, one or more of: %s. In 'stress' mode, "
- "special op 'all' tests all ops." % OPS_NAMES)
- parser.add_argument(
- "-n", "--num-inputs", type=int, default=-1,
- help="Number of input/output sets in each generated test. Defaults to "
- "%d for 'generate' and '%d' for 'stress' mode." %
- (kNumInputsGenerate, kNumInputsStress))
- stressopts = parser.add_argument_group("'stress' mode arguments")
- stressopts.add_argument(
- "-r", "--runs", type=int, default=1000,
- help="Number of tests (with NUM_INPUTS each) to generate and run. "
- "Default: %(default)s.")
- stressopts.add_argument(
- "-b", "--binary", default="out/x64.debug/d8",
- help="The 'd8' binary to use. Default: %(default)s.")
- args = parser.parse_args()
- for op in args.op:
- if op not in OPS.keys() and op != "all":
- print("Invalid op '%s'. See --help." % op)
- return 1
- if len(args.op) == 1 and args.op[0] == "all":
- args.op = OPS.keys()
- if args.action == "generate":
- if args.num_inputs < 0: args.num_inputs = kNumInputsGenerate
- for op in args.op:
- OPS[op]().PrintTest(args.num_inputs)
- elif args.action == "stress":
- if args.num_inputs < 0: args.num_inputs = kNumInputsStress
- result = 0
- pool = multiprocessing.Pool(multiprocessing.cpu_count())
- for r in pool.imap_unordered(WrapRunOne, RunAll(args)):
- result = result or r
- return result
- else:
- print("Invalid action '%s'. See --help." % args.action)
- return 1
- if __name__ == "__main__":
- sys.exit(Main())
|