test.py 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  1. from math import *
  2. from ctypes import *
  3. from random import *
  4. import os
  5. #
  6. # Where is the DLL? If missing, build using: `odin build . -build-mode:dll`
  7. #
  8. LIB_PATH = os.getcwd() + os.sep + "big.dll"
  9. #
  10. # Result values will be passed in a struct { res: cstring, err: Error }
  11. #
  12. class Res(Structure):
  13. _fields_ = [("res", c_char_p), ("err", c_byte)]
  14. #
  15. # Error enum values
  16. #
  17. E_None = 0
  18. E_Out_Of_Memory = 1
  19. E_Invalid_Pointer = 2
  20. E_Invalid_Argument = 3
  21. E_Unknown_Error = 4
  22. E_Max_Iterations_Reached = 5
  23. E_Buffer_Overflow = 6
  24. E_Integer_Overflow = 7
  25. E_Division_by_Zero = 8
  26. E_Math_Domain_Error = 9
  27. E_Unimplemented = 127
  28. #
  29. # Set up exported procedures
  30. #
  31. try:
  32. l = cdll.LoadLibrary(LIB_PATH)
  33. except:
  34. print("Couldn't find or load " + LIB_PATH + ".")
  35. exit(1)
  36. #
  37. # res = a + b, err
  38. #
  39. try:
  40. l.test_add_two.argtypes = [c_char_p, c_char_p, c_longlong]
  41. l.test_add_two.restype = Res
  42. except:
  43. print("Couldn't find exported function 'test_add_two'")
  44. exit(2)
  45. add_two = l.test_add_two
  46. #
  47. # res = a - b, err
  48. #
  49. try:
  50. l.test_sub_two.argtypes = [c_char_p, c_char_p, c_longlong]
  51. l.test_sub_two.restype = Res
  52. except:
  53. print("Couldn't find exported function 'test_sub_two'")
  54. exit(2)
  55. sub_two = l.test_sub_two
  56. #
  57. # res = a * b, err
  58. #
  59. try:
  60. l.test_mul_two.argtypes = [c_char_p, c_char_p, c_longlong]
  61. l.test_mul_two.restype = Res
  62. except:
  63. print("Couldn't find exported function 'test_add_two'")
  64. exit(2)
  65. mul_two = l.test_mul_two
  66. #
  67. # res = a / b, err
  68. #
  69. try:
  70. l.test_div_two.argtypes = [c_char_p, c_char_p, c_longlong]
  71. l.test_div_two.restype = Res
  72. except:
  73. print("Couldn't find exported function 'test_div_two'")
  74. exit(2)
  75. div_two = l.test_div_two
  76. try:
  77. l.test_error_string.argtypes = [c_byte]
  78. l.test_error_string.restype = c_char_p
  79. except:
  80. print("Couldn't find exported function 'test_error_string'")
  81. exit(2)
  82. def test(test_name: "", res: Res, param=[], expected_error = E_None, expected_result = ""):
  83. passed = True
  84. r = None
  85. if res.err != expected_error:
  86. error_type = l.test_error_string(res.err).decode('utf-8')
  87. error_loc = res.res.decode('utf-8')
  88. error_string = "{}: '{}' error in '{}'".format(test_name, error_type, error_loc)
  89. if len(param):
  90. error_string += " with params {}".format(param)
  91. print(error_string, flush=True)
  92. passed = False
  93. elif res.err == E_None:
  94. try:
  95. r = res.res.decode('utf-8')
  96. except:
  97. pass
  98. r = eval(res.res)
  99. if r != expected_result:
  100. error_string = "{}: Result was '{}', expected '{}'".format(test_name, r, expected_result)
  101. if len(param):
  102. error_string += " with params {}".format(param)
  103. print(error_string, flush=True)
  104. passed = False
  105. return passed
  106. def test_add_two(a = 0, b = 0, radix = 10, expected_error = E_None, expected_result = None):
  107. res = add_two(str(a).encode('utf-8'), str(b).encode('utf-8'), radix)
  108. if expected_result == None:
  109. expected_result = a + b
  110. return test("test_add_two", res, [str(a), str(b), radix], expected_error, expected_result)
  111. def test_sub_two(a = 0, b = 0, radix = 10, expected_error = E_None, expected_result = None):
  112. res = sub_two(str(a).encode('utf-8'), str(b).encode('utf-8'), radix)
  113. if expected_result == None:
  114. expected_result = a - b
  115. return test("test_sub_two", res, [str(a), str(b), radix], expected_error, expected_result)
  116. def test_mul_two(a = 0, b = 0, radix = 10, expected_error = E_None, expected_result = None):
  117. res = mul_two(str(a).encode('utf-8'), str(b).encode('utf-8'), radix)
  118. if expected_result == None:
  119. expected_result = a * b
  120. return test("test_mul_two", res, [str(a), str(b), radix], expected_error, expected_result)
  121. def test_div_two(a = 0, b = 0, radix = 10, expected_error = E_None, expected_result = None):
  122. res = div_two(str(a).encode('utf-8'), str(b).encode('utf-8'), radix)
  123. if expected_result == None:
  124. expected_result = a // b if b != 0 else None
  125. return test("test_add_two", res, [str(a), str(b), radix], expected_error, expected_result)
  126. # TODO(Jeroen): Make sure tests cover edge cases, fast paths, and so on.
  127. #
  128. # The last two arguments in tests are the expected error and expected result.
  129. #
  130. # The expected error defaults to None.
  131. # By default the Odin implementation will be tested against the Python one.
  132. # You can override that by supplying an expected result as the last argument instead.
  133. TESTS = {
  134. test_add_two: [
  135. [ 1234, 5432, 10, ],
  136. [ 1234, 5432, 110, E_Invalid_Argument, ],
  137. ],
  138. test_sub_two: [
  139. [ 1234, 5432, 10, ],
  140. ],
  141. test_mul_two: [
  142. [ 1234, 5432, 10, ],
  143. [ 1099243943008198766717263669950239669, 137638828577110581150675834234248871, 10, ]
  144. ],
  145. test_div_two: [
  146. [ 54321, 12345, 10, ],
  147. [ 55431, 0, 10, E_Division_by_Zero, ],
  148. ],
  149. }
  150. if __name__ == '__main__':
  151. print()
  152. print("---- core:math/big tests ----")
  153. print()
  154. for test_proc in TESTS:
  155. count_pass = 0
  156. count_fail = 0
  157. for t in TESTS[test_proc]:
  158. if test_proc(*t):
  159. count_pass += 1
  160. else:
  161. count_fail += 1
  162. print("{}: {} passes, {} failures.".format(test_proc.__name__, count_pass, count_fail))
  163. print()
  164. print("---- core:math/big random tests ----")
  165. print()
  166. for test_proc in [test_add_two, test_sub_two, test_mul_two, test_div_two]:
  167. count_pass = 0
  168. count_fail = 0
  169. a = randint(0, 1 << 120)
  170. b = randint(0, 1 << 120)
  171. res = None
  172. # We've already tested division by zero above.
  173. if b == 0 and test_proc == test_div_two:
  174. b = b + 1
  175. if test_proc(a, b):
  176. count_pass += 1
  177. else:
  178. count_fail += 1
  179. print("{} random: {} passes, {} failures.".format(test_proc.__name__, count_pass, count_fail))