objtest.py 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. #!/usr/bin/env python3
  2. #
  3. # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
  4. # Copyright (C)2013-2020 The MMGen Project <mmgen@tuta.io>
  5. #
  6. # This program is free software: you can redistribute it and/or modify
  7. # it under the terms of the GNU General Public License as published by
  8. # the Free Software Foundation, either version 3 of the License, or
  9. # (at your option) any later version.
  10. #
  11. # This program is distributed in the hope that it will be useful,
  12. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. # GNU General Public License for more details.
  15. #
  16. # You should have received a copy of the GNU General Public License
  17. # along with this program. If not, see <http://www.gnu.org/licenses/>.
  18. """
  19. test/objtest.py: Test MMGen data objects
  20. """
  21. import sys,os
  22. pn = os.path.dirname(sys.argv[0])
  23. os.chdir(os.path.join(pn,os.pardir))
  24. sys.path.__setitem__(0,os.path.abspath(os.curdir))
  25. os.environ['MMGEN_TEST_SUITE'] = '1'
  26. # Import these _after_ local path's been added to sys.path
  27. from mmgen.common import *
  28. from mmgen.obj import *
  29. from mmgen.altcoins.eth.obj import *
  30. from mmgen.seed import *
  31. opts_data = {
  32. 'sets': [('super_silent', True, 'silent', True)],
  33. 'text': {
  34. 'desc': 'Test MMGen data objects',
  35. 'usage':'[options] [object]',
  36. 'options': """
  37. -h, --help Print this help message
  38. --, --longhelp Print help message for long options (common options)
  39. -q, --quiet Produce quieter output
  40. -s, --silent Silence output of tested objects
  41. -S, --super-silent Silence all output except for errors
  42. -v, --verbose Produce more verbose output
  43. """
  44. }
  45. }
  46. cmd_args = opts.init(opts_data)
  47. def run_test(test,arg,input_data):
  48. arg_copy = arg
  49. kwargs = {'on_fail':'silent'} if opt.silent else {'on_fail':'die'}
  50. ret_chk = arg
  51. ret_idx = None
  52. exc_type = None
  53. if input_data == 'good' and type(arg) == tuple:
  54. arg,ret_chk = arg
  55. if type(arg) == dict: # pass one arg + kwargs to constructor
  56. arg_copy = arg.copy()
  57. if 'arg' in arg:
  58. args = [arg['arg']]
  59. ret_chk = args[0]
  60. del arg['arg']
  61. else:
  62. args = []
  63. ret_chk = list(arg.values())[0] # assume only one key present
  64. if 'ret' in arg:
  65. ret_chk = arg['ret']
  66. del arg['ret']
  67. del arg_copy['ret']
  68. if 'ret_idx' in arg:
  69. ret_idx = arg['ret_idx']
  70. del arg['ret_idx']
  71. del arg_copy['ret_idx']
  72. if 'ExcType' in arg:
  73. exc_type = arg['ExcType']
  74. del arg['ExcType']
  75. del arg_copy['ExcType']
  76. kwargs.update(arg)
  77. elif type(arg) == tuple:
  78. args = arg
  79. else:
  80. args = [arg]
  81. try:
  82. if not opt.super_silent:
  83. arg_disp = repr(arg_copy[0] if type(arg_copy) == tuple else arg_copy)
  84. msg_r((orange,green)[input_data=='good']('{:<22}'.format(arg_disp+':')))
  85. cls = globals()[test]
  86. ret = cls(*args,**kwargs)
  87. bad_ret = list() if issubclass(cls,list) else None
  88. if isinstance(ret_chk,str): ret_chk = ret_chk.encode()
  89. if isinstance(ret,str): ret = ret.encode()
  90. if (opt.silent and input_data=='bad' and ret!=bad_ret) or (not opt.silent and input_data=='bad'):
  91. raise UserWarning("Non-'None' return value {} with bad input data".format(repr(ret)))
  92. if opt.silent and input_data=='good' and ret==bad_ret:
  93. raise UserWarning("'None' returned with good input data")
  94. if input_data=='good':
  95. if ret_idx:
  96. ret_chk = arg[list(arg.keys())[ret_idx]].encode()
  97. if ret != ret_chk and repr(ret) != repr(ret_chk):
  98. raise UserWarning("Return value ({!r}) doesn't match expected value ({!r})".format(ret,ret_chk))
  99. if not opt.super_silent:
  100. try: ret_disp = ret.decode()
  101. except: ret_disp = ret
  102. msg('==> {!r}'.format(ret_disp))
  103. if opt.verbose and issubclass(cls,MMGenObject):
  104. ret.pmsg() if hasattr(ret,'pmsg') else pmsg(ret)
  105. except Exception as e:
  106. if not type(e).__name__ == exc_type:
  107. raise
  108. if not opt.super_silent:
  109. msg_r(' {}'.format(yellow(exc_type+':')))
  110. msg(e.args[0])
  111. except SystemExit as e:
  112. if input_data == 'good':
  113. raise ValueError('Error on good input data')
  114. if opt.verbose:
  115. msg('exitval: {}'.format(e.code))
  116. except UserWarning as e:
  117. msg('==> {!r}'.format(ret))
  118. die(2,red('{}'.format(e.args[0])))
  119. def do_loop():
  120. import importlib
  121. modname = f'test.objtest_py_d.ot_{proto.coin.lower()}_{proto.network}'
  122. test_data = importlib.import_module(modname).tests
  123. gmsg(f'Running data object tests for {proto.coin} {proto.network}')
  124. clr = None
  125. utests = cmd_args
  126. for test in test_data:
  127. if utests and test not in utests: continue
  128. nl = ('\n','')[bool(opt.super_silent) or clr == None]
  129. clr = (blue,nocolor)[bool(opt.super_silent)]
  130. msg(clr('{}Testing {}'.format(nl,test)))
  131. for k in ('bad','good'):
  132. if not opt.silent:
  133. msg(purple(capfirst(k)+' input:'))
  134. for arg in test_data[test][k]:
  135. run_test(test,arg,input_data=k)
  136. from mmgen.protocol import init_proto_from_opts
  137. proto = init_proto_from_opts()
  138. do_loop()