ut_obj.py 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  1. #!/usr/bin/env python3
  2. """
  3. test.modtest_d.ut_obj: data object unit tests for the MMGen suite
  4. """
  5. from decimal import Decimal, getcontext
  6. from ..include.common import vmsg, cfg, parity_dev_amt
  7. from mmgen.protocol import init_proto
  8. def test_equal(res, chk):
  9. vmsg(f' checking {res}')
  10. if type(res) is type:
  11. assert res is chk, f'{res} != {chk}'
  12. else:
  13. assert res == chk, f'{res} != {chk}'
  14. def coinamt_test(cls, aa, bb, ut):
  15. def do(desc, res, chk):
  16. vmsg(f'{desc:10} = {res:<{cls.max_prec+10}} [{type(res).__name__}]')
  17. if chk is not None:
  18. assert res == chk, f'{res} != {chk}'
  19. assert type(res) is cls, f'{type(res).__name__} != {cls.__name__}'
  20. vmsg(f'\nTesting {cls.__name__} arithmetic operations...')
  21. A, B = (Decimal(aa), Decimal(bb))
  22. a, b = (cls(aa), cls(bb))
  23. do('A', A, None)
  24. do('B', B, None)
  25. do('a', a, A)
  26. do('b', b, B)
  27. do('b + a', b + a, B + A)
  28. do('sum([b,a])', sum([b, a]), B + A)
  29. do('b - a', b - a, B - A)
  30. do('b * a', b * a, B * A)
  31. do('b * A', b * A, B * A)
  32. do('B * a', B * a, B * A)
  33. do('b / a', b / a, cls(B / A, from_decimal=True))
  34. do('b / A', b / A, cls(B / A, from_decimal=True))
  35. do('a / b', a / b, cls(A / B, from_decimal=True))
  36. do('a * a / a', a * a / a, A * A / A)
  37. do('a * b / a', a * b / a, A * B / A)
  38. do('a * b / b', a * b / b, A * B / B)
  39. vmsg(f'\nChecking {cls.__name__} error handling...')
  40. bad_data = (
  41. ('negation', 'NotImplementedError', 'not implemented', lambda: -a),
  42. ('modulus', 'NotImplementedError', 'not implemented', lambda: b % a),
  43. ('floor division', 'NotImplementedError', 'not implemented', lambda: b // a),
  44. ('negative result', 'ObjectInitError', 'cannot be negative', lambda: a - b),
  45. ('operand type', 'TypeError', 'incorrect type', lambda: a + B),
  46. ('operand type', 'TypeError', 'incorrect type', lambda: b - A),
  47. )
  48. if cls.max_amt is not None:
  49. bad_data += (
  50. ('result', 'ObjectInitError', 'too large', lambda: b + b),
  51. ('result', 'ObjectInitError', 'too large', lambda: b * b),
  52. )
  53. ut.process_bad_data(bad_data)
  54. vmsg('OK')
  55. class unit_tests:
  56. altcoin_deps = ('coinamt_alt', 'coinamt_alt2')
  57. def coinamt(self, name, ut, desc='BTCAmt class'):
  58. from mmgen.amt import BTCAmt
  59. for cls, aa, bb in (
  60. (BTCAmt, '1.2345', '11234567.897'),
  61. ):
  62. coinamt_test(cls, aa, bb, ut)
  63. return True
  64. def coinamt_alt(self, name, ut, desc='LTCAmt, XMRAmt and ETHAmt classes'):
  65. from mmgen.amt import LTCAmt, XMRAmt, ETHAmt
  66. for cls, aa, bb in (
  67. (LTCAmt, '1.2345', '44938271.588'),
  68. (XMRAmt, '1.2345', '11234567.98765432'),
  69. (ETHAmt, '1.2345', '11234567.98765432123456'),
  70. ):
  71. coinamt_test(cls, aa, bb, ut)
  72. return True
  73. def coinamt2(self, name, ut, desc='CoinAmt class'):
  74. from decimal import Decimal
  75. proto = init_proto(cfg, 'btc', network='testnet', need_amt=True)
  76. test_equal(getcontext().prec, proto.decimal_prec)
  77. coin_amt = proto.coin_amt
  78. test_equal(coin_amt.__name__, 'BTCAmt')
  79. a = coin_amt('1.234')
  80. a2 = coin_amt('2.468')
  81. # addition with integer zero:
  82. b = a + 0 # __add__
  83. b = 0 + a # __radd__
  84. test_equal(sum([a, a]), a2) # __radd__ (sum() starts with integer 0)
  85. # __add__
  86. b = coin_amt('333.2456')
  87. test_equal(a + b, coin_amt('334.4796'))
  88. # __sub__
  89. test_equal(a - coin_amt('1'), coin_amt('0.234'))
  90. test_equal(coin_amt('2') - a, coin_amt('0.766'))
  91. # __mul__
  92. b = a * 2
  93. test_equal(type(b), coin_amt)
  94. test_equal(b, a2)
  95. # __rmul__
  96. b = 2 * a
  97. test_equal(type(b), coin_amt)
  98. test_equal(b, a2)
  99. # __truediv__
  100. b = a / 2
  101. test_equal(type(b), coin_amt)
  102. test_equal(b, coin_amt('0.617'))
  103. # __rtruediv__
  104. b = 2 / a
  105. test_equal(type(b), coin_amt)
  106. test_equal(b, coin_amt('1.62074554'))
  107. def bad1(): b = a + 1
  108. def bad2(): b = a - 1
  109. def bad3(): a + Decimal(1)
  110. def bad4(): b = a + 0.0
  111. def bad5(): b = a - 0.0
  112. def bad1r(): b = 1 + a
  113. def bad2r(): b = 3 - a
  114. def bad3r(): Decimal(1) + a
  115. def bad4r(): b = 0.0 + a
  116. def bad5r(): b = 0.0 - a
  117. def bad10(): b = coin_amt('1') - a
  118. def bad11(): b = a * -2
  119. def bad12(): b = a / -2
  120. def bad13(): b = a - coin_amt('2')
  121. def bad14(): b = -2 * a
  122. def bad15(): b = -2 / a
  123. def bad16(): b = coin_amt(a)
  124. vmsg('Testing error handling:')
  125. ut.process_bad_data(
  126. (
  127. ('addition with int', 'TypeError', 'incorrect type', bad1),
  128. ('subtraction with int', 'TypeError', 'incorrect type', bad2),
  129. ('addition with Decimal', 'TypeError', 'incorrect type', bad3),
  130. ('addition with float', 'TypeError', 'incorrect type', bad4),
  131. ('subtraction with float', 'TypeError', 'incorrect type', bad5),
  132. ('addition with int', 'TypeError', 'incorrect type', bad1r),
  133. ('subtraction with int', 'TypeError', 'incorrect type', bad2r),
  134. ('addition with Decimal', 'TypeError', 'incorrect type', bad3r),
  135. ('addition with float', 'TypeError', 'incorrect type', bad4r),
  136. ('subtraction with float', 'TypeError', 'incorrect type', bad5r),
  137. ('negative result', 'ObjectInitError', 'cannot be negative', bad10),
  138. ('negative result', 'ObjectInitError', 'cannot be negative', bad11),
  139. ('negative result', 'ObjectInitError', 'cannot be negative', bad12),
  140. ('negative result', 'ObjectInitError', 'cannot be negative', bad13),
  141. ('negative result', 'ObjectInitError', 'cannot be negative', bad14),
  142. ('negative result', 'ObjectInitError', 'cannot be negative', bad15),
  143. ('double initialization', 'TypeError', 'is instance', bad16),
  144. ),
  145. pfx = '')
  146. return True
  147. def coinamt_alt2(self, name, ut, desc='CoinAmt class (altcoins)'):
  148. proto = init_proto(cfg, 'etc', network='regtest', need_amt=True)
  149. test_equal(getcontext().prec, proto.decimal_prec)
  150. coin_amt = proto.coin_amt
  151. dev_amt = coin_amt(parity_dev_amt, from_unit='wei')
  152. dev_amt_s = '1606938044258990275541962092341162602522202.993782792835301376'
  153. dev_amt_a1 = '1606938044258990275541962092341162602522203.993782792835301377'
  154. dev_amt_s1 = '1606938044258990275541962092341162602522201.993782792835301375'
  155. dev_amt_d2 = '803469022129495137770981046170581301261101.496891396417650688'
  156. dev_amt_d10 = '160693804425899027554196209234116260252220.299378279283530138'
  157. test_equal(str(dev_amt), dev_amt_s)
  158. addend = coin_amt('1.000000000000000001')
  159. test_equal(dev_amt + addend, coin_amt(dev_amt_a1))
  160. test_equal(dev_amt - addend, coin_amt(dev_amt_s1))
  161. test_equal(dev_amt / coin_amt('2'), coin_amt(dev_amt_d2))
  162. test_equal(dev_amt / coin_amt('10'), coin_amt(dev_amt_d10))
  163. test_equal(2 / coin_amt('0.3456'), coin_amt('5.787037037037037037'))
  164. test_equal(2.345 * coin_amt('2.3456'), coin_amt('5.500432000000000458'))
  165. return True