2018-10-30 16:23:12 +00:00
|
|
|
#!/usr/bin/env python3
|
2017-08-14 13:04:25 +03:00
|
|
|
#
|
|
|
|
|
# mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
|
2022-01-04 19:51:22 +00:00
|
|
|
# Copyright (C)2013-2022 The MMGen Project <mmgen@tuta.io>
|
2017-08-14 13:04:25 +03:00
|
|
|
#
|
|
|
|
|
# This program is free software: you can redistribute it and/or modify
|
|
|
|
|
# it under the terms of the GNU General Public License as published by
|
|
|
|
|
# the Free Software Foundation, either version 3 of the License, or
|
|
|
|
|
# (at your option) any later version.
|
|
|
|
|
#
|
|
|
|
|
# This program is distributed in the hope that it will be useful,
|
|
|
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
|
# GNU General Public License for more details.
|
|
|
|
|
#
|
|
|
|
|
# You should have received a copy of the GNU General Public License
|
|
|
|
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
test/objtest.py: Test MMGen data objects
|
|
|
|
|
"""
|
|
|
|
|
|
2021-10-03 17:40:02 +00:00
|
|
|
import sys,os,re
|
|
|
|
|
|
|
|
|
|
from include.tests_header import repo_root
|
|
|
|
|
from test.overlay import overlay_setup
|
2021-10-05 11:57:34 +00:00
|
|
|
sys.path.insert(0,overlay_setup(repo_root))
|
2017-08-14 13:04:25 +03:00
|
|
|
|
2019-07-03 17:40:43 +00:00
|
|
|
os.environ['MMGEN_TEST_SUITE'] = '1'
|
|
|
|
|
|
2017-08-14 13:04:25 +03:00
|
|
|
# Import these _after_ local path's been added to sys.path
|
|
|
|
|
from mmgen.common import *
|
|
|
|
|
from mmgen.obj import *
|
2019-05-13 10:09:32 +00:00
|
|
|
from mmgen.altcoins.eth.obj import *
|
2022-01-15 14:00:05 +00:00
|
|
|
from mmgen.seedsplit import *
|
2022-01-15 14:00:08 +00:00
|
|
|
from mmgen.addr import *
|
|
|
|
|
from mmgen.addrlist import *
|
|
|
|
|
from mmgen.addrdata import *
|
2022-01-15 14:00:06 +00:00
|
|
|
from mmgen.amt import *
|
2022-01-15 14:00:08 +00:00
|
|
|
from mmgen.key import *
|
2022-01-15 14:00:09 +00:00
|
|
|
from mmgen.rpc import IPPort
|
2017-08-14 13:04:25 +03:00
|
|
|
|
2019-03-26 12:59:30 +00:00
|
|
|
opts_data = {
|
|
|
|
|
'sets': [('super_silent', True, 'silent', True)],
|
|
|
|
|
'text': {
|
|
|
|
|
'desc': 'Test MMGen data objects',
|
|
|
|
|
'usage':'[options] [object]',
|
|
|
|
|
'options': """
|
2017-08-14 13:04:25 +03:00
|
|
|
-h, --help Print this help message
|
|
|
|
|
--, --longhelp Print help message for long options (common options)
|
2020-06-01 09:59:04 +00:00
|
|
|
-g, --getobj Instantiate objects with get_obj() wrapper
|
2017-08-14 13:04:25 +03:00
|
|
|
-q, --quiet Produce quieter output
|
|
|
|
|
-s, --silent Silence output of tested objects
|
|
|
|
|
-S, --super-silent Silence all output except for errors
|
|
|
|
|
-v, --verbose Produce more verbose output
|
|
|
|
|
"""
|
2019-03-26 12:59:30 +00:00
|
|
|
}
|
2017-08-14 13:04:25 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cmd_args = opts.init(opts_data)
|
|
|
|
|
|
2020-06-01 09:59:04 +00:00
|
|
|
def run_test(test,arg,input_data,arg1,exc_name):
|
2017-08-14 13:04:25 +03:00
|
|
|
arg_copy = arg
|
2020-06-01 09:59:04 +00:00
|
|
|
kwargs = {}
|
2017-08-14 13:04:25 +03:00
|
|
|
ret_chk = arg
|
2020-05-28 09:53:34 +00:00
|
|
|
ret_idx = None
|
|
|
|
|
if input_data == 'good' and type(arg) == tuple:
|
|
|
|
|
arg,ret_chk = arg
|
2017-08-14 13:04:25 +03:00
|
|
|
if type(arg) == dict: # pass one arg + kwargs to constructor
|
|
|
|
|
arg_copy = arg.copy()
|
|
|
|
|
if 'arg' in arg:
|
|
|
|
|
args = [arg['arg']]
|
|
|
|
|
ret_chk = args[0]
|
|
|
|
|
del arg['arg']
|
|
|
|
|
else:
|
|
|
|
|
args = []
|
2018-10-30 16:31:14 +00:00
|
|
|
ret_chk = list(arg.values())[0] # assume only one key present
|
2017-08-14 13:04:25 +03:00
|
|
|
if 'ret' in arg:
|
|
|
|
|
ret_chk = arg['ret']
|
|
|
|
|
del arg['ret']
|
|
|
|
|
del arg_copy['ret']
|
2020-06-02 19:33:15 +00:00
|
|
|
if 'exc_name' in arg:
|
|
|
|
|
exc_name = arg['exc_name']
|
|
|
|
|
del arg['exc_name']
|
|
|
|
|
del arg_copy['exc_name']
|
2020-05-28 09:53:34 +00:00
|
|
|
if 'ret_idx' in arg:
|
|
|
|
|
ret_idx = arg['ret_idx']
|
|
|
|
|
del arg['ret_idx']
|
|
|
|
|
del arg_copy['ret_idx']
|
2017-08-14 13:04:25 +03:00
|
|
|
kwargs.update(arg)
|
2019-05-12 09:24:00 +00:00
|
|
|
elif type(arg) == tuple:
|
|
|
|
|
args = arg
|
2017-08-14 13:04:25 +03:00
|
|
|
else:
|
|
|
|
|
args = [arg]
|
2020-06-01 09:59:04 +00:00
|
|
|
|
|
|
|
|
if opt.getobj:
|
|
|
|
|
if args:
|
|
|
|
|
assert len(args) == 1, 'objtest_chk1: only one positional arg is allowed'
|
|
|
|
|
kwargs.update( { arg1: args[0] } )
|
|
|
|
|
if opt.silent:
|
|
|
|
|
kwargs.update( { 'silent': True } )
|
|
|
|
|
|
2017-08-14 13:04:25 +03:00
|
|
|
try:
|
|
|
|
|
if not opt.super_silent:
|
2019-11-15 10:46:16 +00:00
|
|
|
arg_disp = repr(arg_copy[0] if type(arg_copy) == tuple else arg_copy)
|
2021-10-03 17:40:03 +00:00
|
|
|
if g.test_suite_deterministic and isinstance(arg_copy,dict):
|
|
|
|
|
arg_disp = re.sub(r'object at 0x[0-9a-f]+','object at [SCRUBBED]',arg_disp)
|
2021-09-29 21:17:57 +00:00
|
|
|
msg_r((green if input_data=='good' else orange)(f'{arg_disp+":":<22}'))
|
2017-08-14 13:04:25 +03:00
|
|
|
cls = globals()[test]
|
2020-06-01 09:59:04 +00:00
|
|
|
|
|
|
|
|
if opt.getobj:
|
|
|
|
|
ret = get_obj(globals()[test],**kwargs)
|
|
|
|
|
else:
|
|
|
|
|
ret = cls(*args,**kwargs)
|
|
|
|
|
|
2017-08-14 13:04:25 +03:00
|
|
|
bad_ret = list() if issubclass(cls,list) else None
|
2018-10-31 18:20:59 +00:00
|
|
|
|
2019-07-05 18:21:00 +00:00
|
|
|
if isinstance(ret_chk,str): ret_chk = ret_chk.encode()
|
|
|
|
|
if isinstance(ret,str): ret = ret.encode()
|
2018-10-31 18:20:59 +00:00
|
|
|
|
2020-06-01 09:59:04 +00:00
|
|
|
if opt.getobj:
|
|
|
|
|
if input_data == 'bad':
|
|
|
|
|
assert ret == False, 'non-False return on bad input data'
|
|
|
|
|
else:
|
|
|
|
|
if (opt.silent and input_data=='bad' and ret!=bad_ret) or (not opt.silent and input_data=='bad'):
|
|
|
|
|
raise UserWarning(f"Non-'None' return value {ret!r} with bad input data")
|
|
|
|
|
|
2017-08-14 13:04:25 +03:00
|
|
|
if opt.silent and input_data=='good' and ret==bad_ret:
|
2018-10-30 16:31:14 +00:00
|
|
|
raise UserWarning("'None' returned with good input data")
|
2020-06-01 09:25:56 +00:00
|
|
|
|
2020-05-28 09:53:34 +00:00
|
|
|
if input_data=='good':
|
|
|
|
|
if ret_idx:
|
|
|
|
|
ret_chk = arg[list(arg.keys())[ret_idx]].encode()
|
|
|
|
|
if ret != ret_chk and repr(ret) != repr(ret_chk):
|
2020-06-01 09:59:04 +00:00
|
|
|
raise UserWarning(f"Return value ({ret!r}) doesn't match expected value ({ret_chk!r})")
|
|
|
|
|
|
|
|
|
|
if opt.super_silent:
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
if opt.getobj and (not opt.silent and input_data == 'bad'):
|
|
|
|
|
pass
|
|
|
|
|
else:
|
2019-11-15 10:46:16 +00:00
|
|
|
try: ret_disp = ret.decode()
|
|
|
|
|
except: ret_disp = ret
|
2020-06-01 09:25:56 +00:00
|
|
|
msg(f'==> {ret_disp!r}')
|
|
|
|
|
|
2017-08-14 13:04:25 +03:00
|
|
|
if opt.verbose and issubclass(cls,MMGenObject):
|
2019-10-11 12:37:51 +00:00
|
|
|
ret.pmsg() if hasattr(ret,'pmsg') else pmsg(ret)
|
2020-06-01 09:59:04 +00:00
|
|
|
|
2019-07-03 17:40:43 +00:00
|
|
|
except Exception as e:
|
2021-09-01 16:56:34 +00:00
|
|
|
if input_data == 'good':
|
|
|
|
|
raise ValueError('Error on good input data')
|
2020-06-01 09:59:04 +00:00
|
|
|
if not type(e).__name__ == exc_name:
|
|
|
|
|
msg(f'Incorrect exception: expected {exc_name} but got {type(e).__name__}')
|
2019-07-03 17:40:43 +00:00
|
|
|
raise
|
2020-06-01 09:59:04 +00:00
|
|
|
if opt.super_silent:
|
|
|
|
|
pass
|
|
|
|
|
elif opt.silent:
|
|
|
|
|
msg(f'==> {exc_name}')
|
|
|
|
|
else:
|
|
|
|
|
msg( yellow(f' {exc_name}:') + str(e) )
|
2017-08-14 13:04:25 +03:00
|
|
|
except SystemExit as e:
|
|
|
|
|
if input_data == 'good':
|
2018-10-30 16:31:14 +00:00
|
|
|
raise ValueError('Error on good input data')
|
2017-08-14 13:04:25 +03:00
|
|
|
if opt.verbose:
|
2020-06-01 09:25:56 +00:00
|
|
|
msg(f'exitval: {e.code}')
|
2017-08-14 13:04:25 +03:00
|
|
|
except UserWarning as e:
|
2020-06-01 09:25:56 +00:00
|
|
|
msg(f'==> {ret!r}')
|
|
|
|
|
die(2,red(str(e)))
|
2017-08-14 13:04:25 +03:00
|
|
|
|
|
|
|
|
def do_loop():
|
2019-10-19 10:18:56 +00:00
|
|
|
import importlib
|
2020-05-28 09:53:34 +00:00
|
|
|
modname = f'test.objtest_py_d.ot_{proto.coin.lower()}_{proto.network}'
|
2019-10-19 10:18:56 +00:00
|
|
|
test_data = importlib.import_module(modname).tests
|
2020-05-28 09:53:34 +00:00
|
|
|
gmsg(f'Running data object tests for {proto.coin} {proto.network}')
|
2019-10-19 10:18:56 +00:00
|
|
|
|
2019-10-11 12:37:51 +00:00
|
|
|
clr = None
|
2019-10-19 10:18:56 +00:00
|
|
|
utests = cmd_args
|
|
|
|
|
for test in test_data:
|
2020-06-01 09:59:04 +00:00
|
|
|
arg1 = test_data[test].get('arg1')
|
2017-08-14 13:04:25 +03:00
|
|
|
if utests and test not in utests: continue
|
2019-10-11 12:37:51 +00:00
|
|
|
nl = ('\n','')[bool(opt.super_silent) or clr == None]
|
|
|
|
|
clr = (blue,nocolor)[bool(opt.super_silent)]
|
2020-06-01 09:59:04 +00:00
|
|
|
|
|
|
|
|
if opt.getobj and arg1 is None:
|
|
|
|
|
msg(gray(f'{nl}Skipping {test}'))
|
|
|
|
|
continue
|
|
|
|
|
else:
|
|
|
|
|
msg(clr(f'{nl}Testing {test}'))
|
|
|
|
|
|
2017-08-14 13:04:25 +03:00
|
|
|
for k in ('bad','good'):
|
2020-06-03 12:29:50 +00:00
|
|
|
if not opt.super_silent:
|
2019-10-11 12:37:51 +00:00
|
|
|
msg(purple(capfirst(k)+' input:'))
|
2019-10-19 10:18:56 +00:00
|
|
|
for arg in test_data[test][k]:
|
2020-06-01 09:25:56 +00:00
|
|
|
run_test(
|
|
|
|
|
test,
|
|
|
|
|
arg,
|
|
|
|
|
input_data = k,
|
2020-06-01 09:59:04 +00:00
|
|
|
arg1 = arg1,
|
2021-09-01 16:56:34 +00:00
|
|
|
exc_name = test_data[test].get('exc_name') or ('ObjectInitError','None')[k=='good'],
|
2020-06-01 09:25:56 +00:00
|
|
|
)
|
2017-08-14 13:04:25 +03:00
|
|
|
|
2020-05-28 09:53:34 +00:00
|
|
|
from mmgen.protocol import init_proto_from_opts
|
|
|
|
|
proto = init_proto_from_opts()
|
2017-08-14 13:04:25 +03:00
|
|
|
do_loop()
|