2018-10-30 16:23:12 +00:00
|
|
|
#!/usr/bin/env python3
|
2018-11-17 20:34:04 +00:00
|
|
|
#
|
2016-02-28 16:41:43 +03:00
|
|
|
# mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
|
2024-01-19 11:05:10 +00:00
|
|
|
# Copyright (C)2013-2024 The MMGen Project <mmgen@tuta.io>
|
2016-02-28 16:41:43 +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/>.
|
|
|
|
|
|
|
|
|
|
"""
|
2023-10-13 09:51:14 +00:00
|
|
|
test/cmdtest.py: Command test runner for the MMGen wallet system
|
2016-02-28 16:41:43 +03:00
|
|
|
"""
|
2015-01-03 00:14:40 +03:00
|
|
|
|
2019-03-02 18:27:53 +00:00
|
|
|
def check_segwit_opts():
|
|
|
|
|
for k,m in (('segwit','S'),('segwit_random','S'),('bech32','B')):
|
2023-03-28 18:14:37 +00:00
|
|
|
if getattr(cfg,k) and m not in proto.mmtypes:
|
2020-05-28 09:53:34 +00:00
|
|
|
die(1,f'--{k.replace("_","-")} option incompatible with {proto.cls_name}')
|
2016-02-28 16:41:43 +03:00
|
|
|
|
2019-03-02 18:27:53 +00:00
|
|
|
def create_shm_dir(data_dir,trash_dir):
|
|
|
|
|
# Laggy flash media can cause pexpect to fail, so create a temporary directory
|
|
|
|
|
# under '/dev/shm' and put datadir and tmpdirs here.
|
|
|
|
|
import shutil
|
2019-10-23 09:31:21 +00:00
|
|
|
from subprocess import run
|
2023-10-13 09:51:12 +00:00
|
|
|
if sys.platform == 'win32':
|
2018-05-12 15:26:54 +00:00
|
|
|
for tdir in (data_dir,trash_dir):
|
2023-10-11 12:58:51 +00:00
|
|
|
try:
|
|
|
|
|
os.listdir(tdir)
|
|
|
|
|
except:
|
|
|
|
|
pass
|
2018-05-08 10:44:12 +00:00
|
|
|
else:
|
2023-10-11 12:58:51 +00:00
|
|
|
try:
|
|
|
|
|
shutil.rmtree(tdir)
|
2018-05-08 10:44:12 +00:00
|
|
|
except: # we couldn't remove data dir - perhaps regtest daemon is running
|
2019-10-23 09:31:21 +00:00
|
|
|
try:
|
|
|
|
|
run(['python3',os.path.join('cmds','mmgen-regtest'),'stop'],check=True)
|
|
|
|
|
except:
|
2022-02-05 13:32:56 +00:00
|
|
|
die(4,f'Unable to remove {tdir!r}!')
|
2018-05-08 10:44:12 +00:00
|
|
|
else:
|
|
|
|
|
time.sleep(2)
|
|
|
|
|
shutil.rmtree(tdir)
|
2018-10-30 16:31:14 +00:00
|
|
|
os.mkdir(tdir,0o755)
|
2019-03-25 10:07:04 +00:00
|
|
|
shm_dir = 'test'
|
2016-11-23 17:17:08 +03:00
|
|
|
else:
|
2019-03-02 18:27:53 +00:00
|
|
|
tdir,pfx = '/dev/shm','mmgen-test-'
|
2016-12-12 00:30:23 +03:00
|
|
|
try:
|
2021-09-29 21:17:57 +00:00
|
|
|
run(f'rm -rf {tdir}/{pfx}*',shell=True,check=True)
|
2016-12-12 00:30:23 +03:00
|
|
|
except Exception as e:
|
2021-09-29 21:17:57 +00:00
|
|
|
die(2,f'Unable to delete directory tree {tdir}/{pfx}* ({e.args[0]})')
|
2016-12-12 00:30:23 +03:00
|
|
|
try:
|
|
|
|
|
import tempfile
|
2019-03-02 18:27:53 +00:00
|
|
|
shm_dir = str(tempfile.mkdtemp('',pfx,tdir))
|
2016-12-12 00:30:23 +03:00
|
|
|
except Exception as e:
|
2021-09-29 21:17:57 +00:00
|
|
|
die(2,f'Unable to create temporary directory in {tdir} ({e.args[0]})')
|
2019-03-02 18:27:53 +00:00
|
|
|
|
|
|
|
|
dest = os.path.join(shm_dir,os.path.basename(trash_dir))
|
|
|
|
|
os.mkdir(dest,0o755)
|
2020-05-10 13:39:53 +00:00
|
|
|
|
|
|
|
|
run(f'rm -rf {trash_dir}',shell=True,check=True)
|
2019-03-02 18:27:53 +00:00
|
|
|
os.symlink(dest,trash_dir)
|
|
|
|
|
|
|
|
|
|
dest = os.path.join(shm_dir,os.path.basename(data_dir))
|
2023-04-04 16:04:10 +00:00
|
|
|
shutil.move(data_dir,dest) # data_dir was created by Config()
|
2019-03-02 18:27:53 +00:00
|
|
|
os.symlink(dest,data_dir)
|
|
|
|
|
|
|
|
|
|
return shm_dir
|
|
|
|
|
|
2024-03-06 11:05:22 +00:00
|
|
|
import sys, os, time, asyncio
|
2021-10-03 17:40:02 +00:00
|
|
|
|
2022-10-05 19:22:29 +00:00
|
|
|
if sys.argv[-1] == 'clean':
|
2023-09-26 09:40:12 +00:00
|
|
|
os.environ['MMGEN_TEST_SUITE'] = '1'
|
|
|
|
|
repo_root = os.path.normpath(os.path.abspath(os.path.join(os.path.dirname(sys.argv[0]),os.pardir)))
|
|
|
|
|
os.chdir(repo_root)
|
|
|
|
|
sys.path[0] = repo_root
|
|
|
|
|
|
2022-10-05 19:22:29 +00:00
|
|
|
from shutil import rmtree
|
2023-09-26 09:40:12 +00:00
|
|
|
from test.overlay import get_overlay_tree_dir
|
|
|
|
|
overlay_tree_dir = get_overlay_tree_dir(repo_root)
|
2022-10-24 16:50:04 +00:00
|
|
|
rmtree(overlay_tree_dir,ignore_errors=True)
|
2023-09-26 09:40:12 +00:00
|
|
|
print(f'Removed {os.path.relpath(overlay_tree_dir)!r}')
|
2022-10-05 19:22:29 +00:00
|
|
|
else:
|
|
|
|
|
# overlay must be set up before importing mmgen mods!
|
2023-10-13 09:51:14 +00:00
|
|
|
try:
|
|
|
|
|
from include.test_init import repo_root
|
|
|
|
|
except ImportError:
|
|
|
|
|
from test.include.test_init import repo_root
|
2019-03-02 18:27:53 +00:00
|
|
|
|
2023-10-03 14:27:56 +00:00
|
|
|
from mmgen.cfg import Config,gc
|
2023-11-21 15:43:52 +00:00
|
|
|
from mmgen.color import red,yellow,green,blue,cyan,gray,nocolor,init_color
|
2024-03-06 11:05:22 +00:00
|
|
|
from mmgen.util import msg, Msg, bmsg, die, suf, make_timestr
|
2023-10-03 14:27:56 +00:00
|
|
|
|
|
|
|
|
from test.include.common import (
|
|
|
|
|
set_globals,
|
2023-10-13 09:51:14 +00:00
|
|
|
cmdtest_py_log_fn,
|
|
|
|
|
cmdtest_py_error_fn,
|
2023-10-03 14:27:56 +00:00
|
|
|
mk_tmpdir,
|
|
|
|
|
cleandir,
|
|
|
|
|
iqmsg,
|
|
|
|
|
omsg,
|
|
|
|
|
omsg_r,
|
|
|
|
|
ok,
|
|
|
|
|
start_test_daemons,
|
|
|
|
|
stop_test_daemons,
|
2023-10-03 14:27:57 +00:00
|
|
|
init_coverage,
|
2023-10-03 14:27:56 +00:00
|
|
|
)
|
2023-09-29 12:24:21 +00:00
|
|
|
|
2022-02-07 21:08:08 +00:00
|
|
|
try:
|
2023-10-13 09:51:14 +00:00
|
|
|
os.unlink(os.path.join(repo_root,cmdtest_py_error_fn))
|
2022-02-07 21:08:08 +00:00
|
|
|
except:
|
|
|
|
|
pass
|
2019-03-02 18:27:53 +00:00
|
|
|
|
|
|
|
|
os.environ['MMGEN_QUIET'] = '0' # for this script and spawned scripts
|
2016-11-21 19:59:03 +03:00
|
|
|
|
2019-03-26 12:59:30 +00:00
|
|
|
opts_data = {
|
2022-10-29 20:10:23 +00:00
|
|
|
'sets': [
|
|
|
|
|
('list_current_cmd_groups',True,'list_cmd_groups',True),
|
|
|
|
|
('demo',True,'exact_output',True),
|
|
|
|
|
('demo',True,'buf_keypress',True),
|
|
|
|
|
('demo',True,'pexpect_spawn',True),
|
|
|
|
|
],
|
2019-03-26 12:59:30 +00:00
|
|
|
'text': {
|
2023-11-27 09:15:07 +00:00
|
|
|
'desc': 'High-level tests for the MMGen Wallet suite',
|
2022-07-29 16:45:31 +00:00
|
|
|
'usage':'[options] [command [..command]] | [command_group[.command_subgroup][:command]]',
|
2022-02-03 20:40:42 +00:00
|
|
|
'options': """
|
2018-10-13 15:00:03 +00:00
|
|
|
-h, --help Print this help message
|
|
|
|
|
--, --longhelp Print help message for long options (common options)
|
2022-05-03 21:01:05 +00:00
|
|
|
-a, --no-altcoin Skip altcoin tests (WIP)
|
2019-12-07 12:45:04 +00:00
|
|
|
-A, --no-daemon-autostart Don't start and stop daemons automatically
|
2018-10-13 15:00:03 +00:00
|
|
|
-B, --bech32 Generate and use Bech32 addresses
|
|
|
|
|
-b, --buf-keypress Use buffered keypresses as with real human input
|
2019-02-06 11:05:37 +00:00
|
|
|
(often required on slow systems, or under emulation)
|
2018-10-13 15:00:03 +00:00
|
|
|
-c, --print-cmdline Print the command line of each spawned command
|
|
|
|
|
-C, --coverage Produce code coverage info using trace module
|
|
|
|
|
-x, --debug-pexpect Produce debugging output for pexpect calls
|
2022-10-29 20:10:23 +00:00
|
|
|
--, --demo Add extra delay after each send to make input visible.
|
|
|
|
|
Implies --exact-output --pexpect-spawn --buf-keypress
|
2022-12-01 12:32:31 +00:00
|
|
|
-d, --deps-only Run a command or command subgroup’s dependencies without
|
|
|
|
|
running the command or command group itself.
|
2018-10-13 15:00:03 +00:00
|
|
|
-D, --no-daemon-stop Don't stop auto-started daemons after running tests
|
|
|
|
|
-E, --direct-exec Bypass pexpect and execute a command directly (for
|
|
|
|
|
debugging only)
|
|
|
|
|
-e, --exact-output Show the exact output of the MMGen script(s) being run
|
2019-03-02 18:27:53 +00:00
|
|
|
-G, --exclude-groups=G Exclude the specified command groups (comma-separated)
|
2022-07-29 16:45:30 +00:00
|
|
|
-l, --list-cmds List the test script’s available commands
|
2022-07-29 16:45:31 +00:00
|
|
|
-L, --list-cmd-groups List the test script’s command groups and subgroups
|
2019-12-07 12:20:21 +00:00
|
|
|
-g, --list-current-cmd-groups List command groups for current configuration
|
2018-10-13 15:00:03 +00:00
|
|
|
-n, --names Display command names instead of descriptions
|
2021-09-29 21:17:56 +00:00
|
|
|
-N, --no-timings Suppress display of timing information
|
2022-02-03 20:40:42 +00:00
|
|
|
-o, --log Log commands to file {lf!r}
|
2019-03-02 18:27:53 +00:00
|
|
|
-O, --pexpect-spawn Use pexpect.spawn instead of popen_spawn (much slower,
|
|
|
|
|
kut does real terminal emulation)
|
2018-10-13 15:00:03 +00:00
|
|
|
-p, --pause Pause between tests, resuming on keypress
|
|
|
|
|
-P, --profile Record the execution time of each script
|
|
|
|
|
-q, --quiet Produce minimal output. Suppress dependency info
|
|
|
|
|
-r, --resume=c Resume at command 'c' after interrupted run
|
2020-05-10 13:39:53 +00:00
|
|
|
-R, --resume-after=c Same, but resume at command following 'c'
|
2022-02-07 21:08:09 +00:00
|
|
|
-t, --step After resuming, execute one command and stop
|
2018-10-13 15:00:03 +00:00
|
|
|
-S, --skip-deps Skip dependency checking for command
|
|
|
|
|
-u, --usr-random Get random data interactively from user
|
2019-03-23 14:30:47 +00:00
|
|
|
-T, --pexpect-timeout=T Set the timeout for pexpect
|
2018-10-13 15:00:03 +00:00
|
|
|
-v, --verbose Produce more verbose output
|
2022-07-29 16:45:30 +00:00
|
|
|
-W, --no-dw-delete Don't remove default wallet from data dir after dw tests
|
|
|
|
|
are done
|
2018-10-13 15:00:03 +00:00
|
|
|
-X, --exit-after=C Exit after command 'C'
|
2019-02-19 09:02:49 +00:00
|
|
|
-y, --segwit Generate and use Segwit addresses
|
|
|
|
|
-Y, --segwit-random Generate and use a random mix of Segwit and Legacy addrs
|
2019-03-26 12:59:30 +00:00
|
|
|
""",
|
|
|
|
|
'notes': """
|
2016-11-21 19:59:03 +03:00
|
|
|
|
2022-07-29 16:45:30 +00:00
|
|
|
If no command is given, the whole test suite is run for the currently
|
|
|
|
|
specified coin (default BTC).
|
2022-07-29 16:45:31 +00:00
|
|
|
|
|
|
|
|
For traceback output and error file support, set the EXEC_WRAPPER_TRACEBACK
|
|
|
|
|
environment var
|
2016-11-21 19:59:03 +03:00
|
|
|
"""
|
2019-03-26 12:59:30 +00:00
|
|
|
},
|
2022-02-03 20:40:42 +00:00
|
|
|
'code': {
|
|
|
|
|
'options': lambda proto,help_notes,s: s.format(
|
2023-10-13 09:51:14 +00:00
|
|
|
lf = cmdtest_py_log_fn
|
2022-02-03 20:40:42 +00:00
|
|
|
)
|
|
|
|
|
}
|
2016-11-21 19:59:03 +03:00
|
|
|
}
|
|
|
|
|
|
2020-05-26 14:53:44 +00:00
|
|
|
# we need some opt values before running opts.init, so parse without initializing:
|
2023-04-04 16:04:10 +00:00
|
|
|
po = Config(opts_data=opts_data,parse_only=True)._parsed_opts
|
2022-02-03 20:40:42 +00:00
|
|
|
|
2023-09-29 12:24:22 +00:00
|
|
|
data_dir = Config.test_datadir
|
2016-11-21 19:59:03 +03:00
|
|
|
|
2019-03-02 18:27:53 +00:00
|
|
|
# step 1: delete data_dir symlink in ./test;
|
2024-02-22 12:48:12 +00:00
|
|
|
if not po.user_opts.get('skip_deps'):
|
2023-10-11 12:58:51 +00:00
|
|
|
try:
|
|
|
|
|
os.unlink(data_dir)
|
|
|
|
|
except:
|
|
|
|
|
pass
|
2017-10-28 00:11:00 +03:00
|
|
|
|
2024-02-22 12:48:12 +00:00
|
|
|
# step 2: opts.init will create new data_dir in ./test (if not po.user_opts['skip_deps'])
|
2023-04-04 16:04:10 +00:00
|
|
|
cfg = Config(opts_data=opts_data)
|
2023-03-28 18:14:37 +00:00
|
|
|
|
2023-11-21 15:48:10 +00:00
|
|
|
if cfg.no_altcoin and cfg.coin != 'BTC':
|
|
|
|
|
die(1,f'--no-altcoin incompatible with --coin={cfg.coin}')
|
|
|
|
|
|
2023-03-28 18:14:37 +00:00
|
|
|
set_globals(cfg)
|
|
|
|
|
|
2023-10-13 09:51:14 +00:00
|
|
|
from test.cmdtest_py_d.common import ( # this must be loaded after set_globals()
|
2023-10-03 14:27:57 +00:00
|
|
|
get_file_with_ext,
|
|
|
|
|
confirm_continue
|
|
|
|
|
)
|
2023-03-28 18:14:37 +00:00
|
|
|
|
|
|
|
|
type(cfg)._reset_ok += (
|
2022-08-04 13:44:31 +00:00
|
|
|
'no_daemon_autostart',
|
|
|
|
|
'names',
|
|
|
|
|
'no_timings',
|
|
|
|
|
'exit_after',
|
|
|
|
|
'resuming',
|
|
|
|
|
'skipping_deps' )
|
2020-06-01 09:25:56 +00:00
|
|
|
|
2024-02-15 09:28:07 +00:00
|
|
|
logging = cfg.log or os.getenv('MMGEN_EXEC_WRAPPER')
|
|
|
|
|
|
2023-03-28 18:14:37 +00:00
|
|
|
cfg.resuming = any(k in po.user_opts for k in ('resume','resume_after'))
|
|
|
|
|
cfg.skipping_deps = cfg.resuming or 'skip_deps' in po.user_opts
|
|
|
|
|
|
|
|
|
|
cmd_args = cfg._args
|
2017-11-13 22:50:35 +03:00
|
|
|
|
2023-10-13 09:51:12 +00:00
|
|
|
if cfg.pexpect_spawn and sys.platform == 'win32':
|
2022-11-01 14:37:06 +00:00
|
|
|
die(1,'--pexpect-spawn option not supported on Windows platform, exiting')
|
|
|
|
|
|
2023-03-28 18:14:37 +00:00
|
|
|
if cfg.daemon_id and cfg.daemon_id in cfg.blacklisted_daemons.split():
|
2023-10-13 09:51:14 +00:00
|
|
|
die(1,f'cmdtest.py: daemon {cfg.daemon_id!r} blacklisted, exiting')
|
2022-06-13 17:30:22 +00:00
|
|
|
|
2023-03-28 18:14:37 +00:00
|
|
|
network_id = cfg.coin.lower() + ('_tn' if cfg.testnet else '')
|
2020-05-28 09:53:34 +00:00
|
|
|
|
2023-03-28 18:14:37 +00:00
|
|
|
proto = cfg._proto
|
2020-05-28 09:53:34 +00:00
|
|
|
|
2019-03-02 18:27:53 +00:00
|
|
|
# step 3: move data_dir to /dev/shm and symlink it back to ./test:
|
|
|
|
|
trash_dir = os.path.join('test','trash')
|
2023-05-19 16:16:52 +00:00
|
|
|
trash_dir2 = os.path.join('test','trash2')
|
2020-05-28 09:53:34 +00:00
|
|
|
|
2023-03-28 18:14:37 +00:00
|
|
|
if not cfg.skipping_deps:
|
2019-03-02 18:27:53 +00:00
|
|
|
shm_dir = create_shm_dir(data_dir,trash_dir)
|
2017-10-28 00:11:00 +03:00
|
|
|
|
2019-03-02 18:27:53 +00:00
|
|
|
check_segwit_opts()
|
2017-10-03 22:26:24 +03:00
|
|
|
|
2023-03-28 18:14:37 +00:00
|
|
|
testing_segwit = cfg.segwit or cfg.segwit_random or cfg.bech32
|
2021-10-08 16:44:56 +00:00
|
|
|
|
2023-03-28 18:14:37 +00:00
|
|
|
if cfg.test_suite_deterministic:
|
|
|
|
|
cfg.no_timings = True
|
2021-10-03 17:40:03 +00:00
|
|
|
init_color(num_colors=0)
|
2023-05-23 12:12:33 +00:00
|
|
|
os.environ['MMGEN_DISABLE_COLOR'] = '1' # for this script and spawned scripts
|
2021-10-03 17:40:03 +00:00
|
|
|
|
2023-03-28 18:14:37 +00:00
|
|
|
if cfg.profile:
|
|
|
|
|
cfg.names = True
|
2018-03-09 11:29:58 +00:00
|
|
|
|
2023-03-28 18:14:37 +00:00
|
|
|
if cfg.exact_output:
|
2023-09-29 12:24:22 +00:00
|
|
|
qmsg = qmsg_r = lambda s: None
|
2023-03-28 18:14:37 +00:00
|
|
|
else:
|
|
|
|
|
qmsg = cfg._util.qmsg
|
|
|
|
|
qmsg_r = cfg._util.qmsg_r
|
2017-07-27 22:55:52 +03:00
|
|
|
|
2023-03-28 18:14:37 +00:00
|
|
|
if cfg.skipping_deps:
|
|
|
|
|
cfg.no_daemon_autostart = True
|
2020-05-10 13:39:53 +00:00
|
|
|
|
2023-10-13 09:51:14 +00:00
|
|
|
from test.cmdtest_py_d.cfg import cfgs,fixup_cfgs
|
2019-10-17 11:42:13 +00:00
|
|
|
|
2022-07-26 14:09:44 +00:00
|
|
|
for k in cfgs:
|
|
|
|
|
cfgs[k]['tmpdir'] = os.path.join('test','tmp',str(k))
|
2019-10-19 10:18:56 +00:00
|
|
|
|
|
|
|
|
fixup_cfgs()
|
2018-03-09 13:16:10 +00:00
|
|
|
|
2019-03-02 18:27:53 +00:00
|
|
|
utils = {
|
|
|
|
|
# 'check_deps': 'check dependencies for specified command (WIP)', # TODO
|
|
|
|
|
'clean': 'clean specified tmp dir(s) (specify by integer, no arg = all dirs)',
|
|
|
|
|
}
|
2018-07-28 13:52:43 +00:00
|
|
|
|
2019-03-02 18:27:53 +00:00
|
|
|
def list_cmds():
|
2022-07-29 16:45:30 +00:00
|
|
|
|
|
|
|
|
def gen_output():
|
|
|
|
|
|
|
|
|
|
gm = CmdGroupMgr()
|
|
|
|
|
cw,d = 0,[]
|
|
|
|
|
|
|
|
|
|
yield green('AVAILABLE COMMANDS:')
|
|
|
|
|
|
|
|
|
|
for gname in gm.cmd_groups:
|
2023-10-13 09:51:14 +00:00
|
|
|
tg = gm.gm_init_group(None,gname,None,None)
|
|
|
|
|
desc = tg.__doc__.strip() if tg.__doc__ else type(tg).__name__
|
2022-07-29 16:45:30 +00:00
|
|
|
d.append( (gname,desc,gm.cmd_list,gm.dpy_data) )
|
|
|
|
|
cw = max(max(len(k) for k in gm.dpy_data),cw)
|
|
|
|
|
|
|
|
|
|
for gname,gdesc,clist,dpdata in d:
|
|
|
|
|
yield '\n'+green(f'{gname!r} - {gdesc}:')
|
|
|
|
|
for cmd in clist:
|
|
|
|
|
data = dpdata[cmd]
|
|
|
|
|
yield ' {:{w}} - {}'.format(
|
|
|
|
|
cmd,
|
2023-10-11 12:58:51 +00:00
|
|
|
(data if isinstance(data,str) else data[1]),
|
2022-07-29 16:45:30 +00:00
|
|
|
w = cw )
|
|
|
|
|
|
|
|
|
|
w = max(map(len,utils))
|
|
|
|
|
|
|
|
|
|
yield '\n'+green('AVAILABLE UTILITIES:')
|
|
|
|
|
|
|
|
|
|
for cmd in sorted(utils):
|
2023-10-11 12:58:52 +00:00
|
|
|
yield f' {cmd:{w}} - {utils[cmd]}'
|
2022-07-29 16:45:30 +00:00
|
|
|
|
2022-10-17 18:37:22 +00:00
|
|
|
from mmgen.ui import do_pager
|
2022-07-29 16:45:30 +00:00
|
|
|
do_pager('\n'.join(gen_output()))
|
ERC20 token support (create/deploy, TX create/sign/send)
This feature is EXPERIMENTAL. Until v0.9.9 is released, mainnet use is
strictly at your own risk!
To test on dev chain, run 'test/test.py -e ethdev'
To test on Kovan, add '--testnet=1' option to all commands below
Transaction example:
Generate some ETH addresses with your default wallet:
$ mmgen-addrgen --coin=eth 1-5
Create an EOS token tracking wallet and import the addresses into it:
$ mmgen-addrimport --coin=eth --token=86fa049857e0209aa7d9e616f7eb3b3b78ecfdb0 ABCDABCD-ETH[1-5].addrs
Send 10+ EOS from an exchange or another wallet to address ABCDABCD:E:1
Create a TX sending 10 EOS to address aabbccdd..., with change to ABCDABCD:E:2:
$ mmgen-txcreate --coin=eth --token=eos aabbccddaabbccddaabbccddaabbccddaabbccdd,10 ABCDABCD:E:2
On your offline machine, sign the TX:
$ mmgen-txsign --coin=eth --token=eos ABC123-EOS[10,50000].rawtx
On your online machine, send the TX:
$ mmgen-txsend --coin=eth --token=eos ABC123-EOS[10,50000].sigtx
View your EOS tracking wallet:
$ mmgen-tool --coin=eth --token=eos twview
Token creation/deployment example:
Install the Solidity compiler ('solc') on your system.
Create a token 'MFT' with default parameters, owned by ddeeff... (ABCDABCD:E:1):
$ scripts/create-token.py --symbol=MFT --name='My First Token' ddeeffddeeffddeeffddeeffddeeffddeeffddee
Deploy the token on the ETH blockchain:
$ mmgen-txdo --coin=eth --tx-gas=200000 --contract-data=SafeMath.bin
$ mmgen-txdo --coin=eth --tx-gas=250000 --contract-data=Owned.bin
$ mmgen-txdo --coin=eth --tx-gas=1100000 --contract-data=Token.bin
...
Token address: abcd1234abcd1234abcd1234abcd1234abcd1234
Create an MFT token tracking wallet and import your ETH addresses into it:
$ mmgen-addrimport --coin=eth --token=abcd1234abcd1234abcd1234abcd1234abcd1234 ABCDABCD-ETH[1-5].addrs
View your MFT tracking wallet:
$ mmgen-tool --coin=eth --token=mft twview
2018-07-25 12:57:04 +00:00
|
|
|
|
2019-03-02 18:27:53 +00:00
|
|
|
sys.exit(0)
|
ERC20 token support (create/deploy, TX create/sign/send)
This feature is EXPERIMENTAL. Until v0.9.9 is released, mainnet use is
strictly at your own risk!
To test on dev chain, run 'test/test.py -e ethdev'
To test on Kovan, add '--testnet=1' option to all commands below
Transaction example:
Generate some ETH addresses with your default wallet:
$ mmgen-addrgen --coin=eth 1-5
Create an EOS token tracking wallet and import the addresses into it:
$ mmgen-addrimport --coin=eth --token=86fa049857e0209aa7d9e616f7eb3b3b78ecfdb0 ABCDABCD-ETH[1-5].addrs
Send 10+ EOS from an exchange or another wallet to address ABCDABCD:E:1
Create a TX sending 10 EOS to address aabbccdd..., with change to ABCDABCD:E:2:
$ mmgen-txcreate --coin=eth --token=eos aabbccddaabbccddaabbccddaabbccddaabbccdd,10 ABCDABCD:E:2
On your offline machine, sign the TX:
$ mmgen-txsign --coin=eth --token=eos ABC123-EOS[10,50000].rawtx
On your online machine, send the TX:
$ mmgen-txsend --coin=eth --token=eos ABC123-EOS[10,50000].sigtx
View your EOS tracking wallet:
$ mmgen-tool --coin=eth --token=eos twview
Token creation/deployment example:
Install the Solidity compiler ('solc') on your system.
Create a token 'MFT' with default parameters, owned by ddeeff... (ABCDABCD:E:1):
$ scripts/create-token.py --symbol=MFT --name='My First Token' ddeeffddeeffddeeffddeeffddeeffddeeffddee
Deploy the token on the ETH blockchain:
$ mmgen-txdo --coin=eth --tx-gas=200000 --contract-data=SafeMath.bin
$ mmgen-txdo --coin=eth --tx-gas=250000 --contract-data=Owned.bin
$ mmgen-txdo --coin=eth --tx-gas=1100000 --contract-data=Token.bin
...
Token address: abcd1234abcd1234abcd1234abcd1234abcd1234
Create an MFT token tracking wallet and import your ETH addresses into it:
$ mmgen-addrimport --coin=eth --token=abcd1234abcd1234abcd1234abcd1234abcd1234 ABCDABCD-ETH[1-5].addrs
View your MFT tracking wallet:
$ mmgen-tool --coin=eth --token=mft twview
2018-07-25 12:57:04 +00:00
|
|
|
|
2019-03-02 18:27:53 +00:00
|
|
|
def do_between():
|
2023-03-28 18:14:37 +00:00
|
|
|
if cfg.pause:
|
2019-03-02 18:27:53 +00:00
|
|
|
confirm_continue()
|
2023-03-28 18:14:37 +00:00
|
|
|
elif (cfg.verbose or cfg.exact_output) and not cfg.skipping_deps:
|
2019-03-02 18:27:53 +00:00
|
|
|
sys.stderr.write('\n')
|
2018-10-20 19:13:51 +00:00
|
|
|
|
2019-03-02 18:27:53 +00:00
|
|
|
def list_tmpdirs():
|
2019-03-16 17:45:44 +00:00
|
|
|
return {k:cfgs[k]['tmpdir'] for k in cfgs}
|
2018-10-02 18:09:48 +00:00
|
|
|
|
2021-10-03 17:40:02 +00:00
|
|
|
def clean(usr_dirs=None,clean_overlay=True):
|
2023-03-28 18:14:37 +00:00
|
|
|
if cfg.skipping_deps:
|
2020-03-12 16:38:02 +00:00
|
|
|
return
|
2019-03-02 18:27:53 +00:00
|
|
|
all_dirs = list_tmpdirs()
|
|
|
|
|
dirnums = map(int,(usr_dirs if usr_dirs is not None else all_dirs))
|
|
|
|
|
dirlist = list(map(str,sorted(dirnums)))
|
|
|
|
|
for d in dirlist:
|
|
|
|
|
if d in all_dirs:
|
|
|
|
|
cleandir(all_dirs[d])
|
|
|
|
|
else:
|
2021-09-29 21:17:57 +00:00
|
|
|
die(1,f'{d}: invalid directory number')
|
2019-03-02 18:27:53 +00:00
|
|
|
if dirlist:
|
2021-09-29 21:17:57 +00:00
|
|
|
iqmsg(green('Cleaned tmp director{} {}'.format(
|
|
|
|
|
suf(dirlist,'ies'),
|
|
|
|
|
' '.join(dirlist))
|
|
|
|
|
))
|
2019-03-02 18:27:53 +00:00
|
|
|
cleandir(data_dir)
|
|
|
|
|
cleandir(trash_dir)
|
2023-05-19 16:16:52 +00:00
|
|
|
cleandir(trash_dir2)
|
|
|
|
|
iqmsg(green(f'Cleaned directories {data_dir!r} {trash_dir!r} {trash_dir2!r}'))
|
ERC20 token support (create/deploy, TX create/sign/send)
This feature is EXPERIMENTAL. Until v0.9.9 is released, mainnet use is
strictly at your own risk!
To test on dev chain, run 'test/test.py -e ethdev'
To test on Kovan, add '--testnet=1' option to all commands below
Transaction example:
Generate some ETH addresses with your default wallet:
$ mmgen-addrgen --coin=eth 1-5
Create an EOS token tracking wallet and import the addresses into it:
$ mmgen-addrimport --coin=eth --token=86fa049857e0209aa7d9e616f7eb3b3b78ecfdb0 ABCDABCD-ETH[1-5].addrs
Send 10+ EOS from an exchange or another wallet to address ABCDABCD:E:1
Create a TX sending 10 EOS to address aabbccdd..., with change to ABCDABCD:E:2:
$ mmgen-txcreate --coin=eth --token=eos aabbccddaabbccddaabbccddaabbccddaabbccdd,10 ABCDABCD:E:2
On your offline machine, sign the TX:
$ mmgen-txsign --coin=eth --token=eos ABC123-EOS[10,50000].rawtx
On your online machine, send the TX:
$ mmgen-txsend --coin=eth --token=eos ABC123-EOS[10,50000].sigtx
View your EOS tracking wallet:
$ mmgen-tool --coin=eth --token=eos twview
Token creation/deployment example:
Install the Solidity compiler ('solc') on your system.
Create a token 'MFT' with default parameters, owned by ddeeff... (ABCDABCD:E:1):
$ scripts/create-token.py --symbol=MFT --name='My First Token' ddeeffddeeffddeeffddeeffddeeffddeeffddee
Deploy the token on the ETH blockchain:
$ mmgen-txdo --coin=eth --tx-gas=200000 --contract-data=SafeMath.bin
$ mmgen-txdo --coin=eth --tx-gas=250000 --contract-data=Owned.bin
$ mmgen-txdo --coin=eth --tx-gas=1100000 --contract-data=Token.bin
...
Token address: abcd1234abcd1234abcd1234abcd1234abcd1234
Create an MFT token tracking wallet and import your ETH addresses into it:
$ mmgen-addrimport --coin=eth --token=abcd1234abcd1234abcd1234abcd1234abcd1234 ABCDABCD-ETH[1-5].addrs
View your MFT tracking wallet:
$ mmgen-tool --coin=eth --token=mft twview
2018-07-25 12:57:04 +00:00
|
|
|
|
2021-10-03 17:40:02 +00:00
|
|
|
if clean_overlay:
|
2022-10-24 16:50:04 +00:00
|
|
|
cleandir(overlay_tree_dir)
|
|
|
|
|
iqmsg(green(f'Cleaned directory {os.path.relpath(overlay_tree_dir)!r}'))
|
2021-10-03 17:40:02 +00:00
|
|
|
|
2019-03-02 18:27:53 +00:00
|
|
|
def create_tmp_dirs(shm_dir):
|
2023-10-13 09:51:12 +00:00
|
|
|
if sys.platform == 'win32':
|
2019-03-02 18:27:53 +00:00
|
|
|
for cfg in sorted(cfgs):
|
|
|
|
|
mk_tmpdir(cfgs[cfg]['tmpdir'])
|
|
|
|
|
else:
|
2023-05-19 16:16:52 +00:00
|
|
|
os.makedirs( os.path.join('test','tmp'), mode=0o755, exist_ok=True )
|
2019-03-02 18:27:53 +00:00
|
|
|
for cfg in sorted(cfgs):
|
|
|
|
|
src = os.path.join(shm_dir,cfgs[cfg]['tmpdir'].split('/')[-1])
|
|
|
|
|
mk_tmpdir(src)
|
|
|
|
|
try:
|
|
|
|
|
os.unlink(cfgs[cfg]['tmpdir'])
|
|
|
|
|
except OSError as e:
|
2022-02-05 13:32:56 +00:00
|
|
|
if e.errno != 2:
|
|
|
|
|
raise
|
2019-03-02 18:27:53 +00:00
|
|
|
finally:
|
|
|
|
|
os.symlink(src,cfgs[cfg]['tmpdir'])
|
ERC20 token support (create/deploy, TX create/sign/send)
This feature is EXPERIMENTAL. Until v0.9.9 is released, mainnet use is
strictly at your own risk!
To test on dev chain, run 'test/test.py -e ethdev'
To test on Kovan, add '--testnet=1' option to all commands below
Transaction example:
Generate some ETH addresses with your default wallet:
$ mmgen-addrgen --coin=eth 1-5
Create an EOS token tracking wallet and import the addresses into it:
$ mmgen-addrimport --coin=eth --token=86fa049857e0209aa7d9e616f7eb3b3b78ecfdb0 ABCDABCD-ETH[1-5].addrs
Send 10+ EOS from an exchange or another wallet to address ABCDABCD:E:1
Create a TX sending 10 EOS to address aabbccdd..., with change to ABCDABCD:E:2:
$ mmgen-txcreate --coin=eth --token=eos aabbccddaabbccddaabbccddaabbccddaabbccdd,10 ABCDABCD:E:2
On your offline machine, sign the TX:
$ mmgen-txsign --coin=eth --token=eos ABC123-EOS[10,50000].rawtx
On your online machine, send the TX:
$ mmgen-txsend --coin=eth --token=eos ABC123-EOS[10,50000].sigtx
View your EOS tracking wallet:
$ mmgen-tool --coin=eth --token=eos twview
Token creation/deployment example:
Install the Solidity compiler ('solc') on your system.
Create a token 'MFT' with default parameters, owned by ddeeff... (ABCDABCD:E:1):
$ scripts/create-token.py --symbol=MFT --name='My First Token' ddeeffddeeffddeeffddeeffddeeffddeeffddee
Deploy the token on the ETH blockchain:
$ mmgen-txdo --coin=eth --tx-gas=200000 --contract-data=SafeMath.bin
$ mmgen-txdo --coin=eth --tx-gas=250000 --contract-data=Owned.bin
$ mmgen-txdo --coin=eth --tx-gas=1100000 --contract-data=Token.bin
...
Token address: abcd1234abcd1234abcd1234abcd1234abcd1234
Create an MFT token tracking wallet and import your ETH addresses into it:
$ mmgen-addrimport --coin=eth --token=abcd1234abcd1234abcd1234abcd1234abcd1234 ABCDABCD-ETH[1-5].addrs
View your MFT tracking wallet:
$ mmgen-tool --coin=eth --token=mft twview
2018-07-25 12:57:04 +00:00
|
|
|
|
2019-03-02 18:27:53 +00:00
|
|
|
def set_restore_term_at_exit():
|
|
|
|
|
import termios,atexit
|
|
|
|
|
fd = sys.stdin.fileno()
|
|
|
|
|
old = termios.tcgetattr(fd)
|
|
|
|
|
def at_exit():
|
|
|
|
|
termios.tcsetattr(fd, termios.TCSADRAIN, old)
|
|
|
|
|
atexit.register(at_exit)
|
ERC20 token support (create/deploy, TX create/sign/send)
This feature is EXPERIMENTAL. Until v0.9.9 is released, mainnet use is
strictly at your own risk!
To test on dev chain, run 'test/test.py -e ethdev'
To test on Kovan, add '--testnet=1' option to all commands below
Transaction example:
Generate some ETH addresses with your default wallet:
$ mmgen-addrgen --coin=eth 1-5
Create an EOS token tracking wallet and import the addresses into it:
$ mmgen-addrimport --coin=eth --token=86fa049857e0209aa7d9e616f7eb3b3b78ecfdb0 ABCDABCD-ETH[1-5].addrs
Send 10+ EOS from an exchange or another wallet to address ABCDABCD:E:1
Create a TX sending 10 EOS to address aabbccdd..., with change to ABCDABCD:E:2:
$ mmgen-txcreate --coin=eth --token=eos aabbccddaabbccddaabbccddaabbccddaabbccdd,10 ABCDABCD:E:2
On your offline machine, sign the TX:
$ mmgen-txsign --coin=eth --token=eos ABC123-EOS[10,50000].rawtx
On your online machine, send the TX:
$ mmgen-txsend --coin=eth --token=eos ABC123-EOS[10,50000].sigtx
View your EOS tracking wallet:
$ mmgen-tool --coin=eth --token=eos twview
Token creation/deployment example:
Install the Solidity compiler ('solc') on your system.
Create a token 'MFT' with default parameters, owned by ddeeff... (ABCDABCD:E:1):
$ scripts/create-token.py --symbol=MFT --name='My First Token' ddeeffddeeffddeeffddeeffddeeffddeeffddee
Deploy the token on the ETH blockchain:
$ mmgen-txdo --coin=eth --tx-gas=200000 --contract-data=SafeMath.bin
$ mmgen-txdo --coin=eth --tx-gas=250000 --contract-data=Owned.bin
$ mmgen-txdo --coin=eth --tx-gas=1100000 --contract-data=Token.bin
...
Token address: abcd1234abcd1234abcd1234abcd1234abcd1234
Create an MFT token tracking wallet and import your ETH addresses into it:
$ mmgen-addrimport --coin=eth --token=abcd1234abcd1234abcd1234abcd1234abcd1234 ABCDABCD-ETH[1-5].addrs
View your MFT tracking wallet:
$ mmgen-tool --coin=eth --token=mft twview
2018-07-25 12:57:04 +00:00
|
|
|
|
2023-10-11 12:58:51 +00:00
|
|
|
class CmdGroupMgr:
|
2019-03-02 18:27:53 +00:00
|
|
|
|
2023-10-13 09:51:13 +00:00
|
|
|
dpy_data = None
|
|
|
|
|
|
2023-10-13 09:51:14 +00:00
|
|
|
from test.cmdtest_py_d.cfg import cmd_groups_dfl,cmd_groups_extra
|
ERC20 token support (create/deploy, TX create/sign/send)
This feature is EXPERIMENTAL. Until v0.9.9 is released, mainnet use is
strictly at your own risk!
To test on dev chain, run 'test/test.py -e ethdev'
To test on Kovan, add '--testnet=1' option to all commands below
Transaction example:
Generate some ETH addresses with your default wallet:
$ mmgen-addrgen --coin=eth 1-5
Create an EOS token tracking wallet and import the addresses into it:
$ mmgen-addrimport --coin=eth --token=86fa049857e0209aa7d9e616f7eb3b3b78ecfdb0 ABCDABCD-ETH[1-5].addrs
Send 10+ EOS from an exchange or another wallet to address ABCDABCD:E:1
Create a TX sending 10 EOS to address aabbccdd..., with change to ABCDABCD:E:2:
$ mmgen-txcreate --coin=eth --token=eos aabbccddaabbccddaabbccddaabbccddaabbccdd,10 ABCDABCD:E:2
On your offline machine, sign the TX:
$ mmgen-txsign --coin=eth --token=eos ABC123-EOS[10,50000].rawtx
On your online machine, send the TX:
$ mmgen-txsend --coin=eth --token=eos ABC123-EOS[10,50000].sigtx
View your EOS tracking wallet:
$ mmgen-tool --coin=eth --token=eos twview
Token creation/deployment example:
Install the Solidity compiler ('solc') on your system.
Create a token 'MFT' with default parameters, owned by ddeeff... (ABCDABCD:E:1):
$ scripts/create-token.py --symbol=MFT --name='My First Token' ddeeffddeeffddeeffddeeffddeeffddeeffddee
Deploy the token on the ETH blockchain:
$ mmgen-txdo --coin=eth --tx-gas=200000 --contract-data=SafeMath.bin
$ mmgen-txdo --coin=eth --tx-gas=250000 --contract-data=Owned.bin
$ mmgen-txdo --coin=eth --tx-gas=1100000 --contract-data=Token.bin
...
Token address: abcd1234abcd1234abcd1234abcd1234abcd1234
Create an MFT token tracking wallet and import your ETH addresses into it:
$ mmgen-addrimport --coin=eth --token=abcd1234abcd1234abcd1234abcd1234abcd1234 ABCDABCD-ETH[1-5].addrs
View your MFT tracking wallet:
$ mmgen-tool --coin=eth --token=mft twview
2018-07-25 12:57:04 +00:00
|
|
|
|
2020-03-12 16:38:02 +00:00
|
|
|
cmd_groups = cmd_groups_dfl.copy()
|
|
|
|
|
cmd_groups.update(cmd_groups_extra)
|
2019-03-02 18:27:53 +00:00
|
|
|
|
2022-07-29 16:45:31 +00:00
|
|
|
@staticmethod
|
|
|
|
|
def create_cmd_group(cls,sg_name=None):
|
|
|
|
|
|
|
|
|
|
cmd_group_in = dict(cls.cmd_group_in)
|
|
|
|
|
|
|
|
|
|
if sg_name and 'subgroup.' + sg_name not in cmd_group_in:
|
|
|
|
|
die(1,f'{sg_name!r}: no such subgroup in test group {cls.__name__}')
|
|
|
|
|
|
2022-12-01 12:32:30 +00:00
|
|
|
def add_entries(key,add_deps=True,added_subgroups=[]):
|
|
|
|
|
|
2022-07-29 16:45:31 +00:00
|
|
|
if add_deps:
|
|
|
|
|
for dep in cmd_group_in['subgroup.'+key]:
|
|
|
|
|
for e in add_entries(dep):
|
|
|
|
|
yield e
|
2022-08-04 13:44:31 +00:00
|
|
|
|
|
|
|
|
assert isinstance(cls.cmd_subgroups[key][0],str), f'header for subgroup {key!r} missing!'
|
|
|
|
|
|
2022-12-01 12:32:30 +00:00
|
|
|
if not key in added_subgroups:
|
|
|
|
|
for e in cls.cmd_subgroups[key][1:]:
|
|
|
|
|
yield e
|
|
|
|
|
added_subgroups.append(key)
|
2022-07-29 16:45:31 +00:00
|
|
|
|
|
|
|
|
def gen():
|
|
|
|
|
for name,data in cls.cmd_group_in:
|
|
|
|
|
if name.startswith('subgroup.'):
|
2024-02-10 15:10:43 +00:00
|
|
|
sg_key = name.removeprefix('subgroup.')
|
2022-07-29 16:45:31 +00:00
|
|
|
if sg_name in (None,sg_key):
|
|
|
|
|
for e in add_entries(
|
|
|
|
|
sg_key,
|
2023-03-28 18:14:37 +00:00
|
|
|
add_deps = sg_name and not cfg.skipping_deps,
|
|
|
|
|
added_subgroups = [sg_name] if cfg.deps_only else [] ):
|
2022-07-29 16:45:31 +00:00
|
|
|
yield e
|
2023-03-28 18:14:37 +00:00
|
|
|
if cfg.deps_only and sg_key == sg_name:
|
2022-12-01 12:32:31 +00:00
|
|
|
return
|
2023-03-28 18:14:37 +00:00
|
|
|
elif not cfg.skipping_deps:
|
2022-07-29 16:45:31 +00:00
|
|
|
yield (name,data)
|
|
|
|
|
|
|
|
|
|
return tuple(gen())
|
|
|
|
|
|
2019-03-02 18:27:53 +00:00
|
|
|
def load_mod(self,gname,modname=None):
|
|
|
|
|
clsname,kwargs = self.cmd_groups[gname]
|
2023-10-11 12:58:52 +00:00
|
|
|
if modname is None and 'modname' in kwargs:
|
2019-03-02 18:27:53 +00:00
|
|
|
modname = kwargs['modname']
|
2019-10-19 10:15:31 +00:00
|
|
|
import importlib
|
2023-10-13 09:51:14 +00:00
|
|
|
modpath = f'test.cmdtest_py_d.ct_{modname or gname}'
|
2019-10-19 10:15:31 +00:00
|
|
|
return getattr(importlib.import_module(modpath),clsname)
|
2019-03-02 18:27:53 +00:00
|
|
|
|
2022-07-29 16:45:31 +00:00
|
|
|
def create_group(self,gname,sg_name,full_data=False,modname=None,is3seed=False,add_dpy=False):
|
2019-03-02 18:27:53 +00:00
|
|
|
"""
|
|
|
|
|
Initializes the list 'cmd_list' and dict 'dpy_data' from module's cmd_group data.
|
|
|
|
|
Alternatively, if called with 'add_dpy=True', updates 'dpy_data' from module data
|
|
|
|
|
without touching 'cmd_list'
|
|
|
|
|
"""
|
|
|
|
|
|
2019-10-19 10:15:31 +00:00
|
|
|
cls = self.load_mod(gname,modname)
|
2019-03-02 18:27:53 +00:00
|
|
|
cdata = []
|
2019-10-19 10:15:31 +00:00
|
|
|
|
2019-10-31 10:46:46 +00:00
|
|
|
def get_shared_deps(cmdname,tmpdir_idx):
|
|
|
|
|
"""
|
|
|
|
|
shared_deps are "implied" dependencies for all cmds in cmd_group that don't appear in
|
|
|
|
|
the cmd_group data or cmds' argument lists. Supported only for 3seed tests at present.
|
|
|
|
|
"""
|
2020-05-10 13:39:53 +00:00
|
|
|
if not hasattr(cls,'shared_deps'):
|
|
|
|
|
return []
|
|
|
|
|
|
2019-10-31 10:46:46 +00:00
|
|
|
return [k for k,v in cfgs[str(tmpdir_idx)]['dep_generators'].items()
|
|
|
|
|
if k in cls.shared_deps and v != cmdname]
|
|
|
|
|
|
2022-07-29 16:45:31 +00:00
|
|
|
if not hasattr(cls,'cmd_group'):
|
|
|
|
|
cls.cmd_group = self.create_cmd_group(cls,sg_name)
|
|
|
|
|
|
2019-10-19 10:15:31 +00:00
|
|
|
for a,b in cls.cmd_group:
|
2019-03-02 18:27:53 +00:00
|
|
|
if is3seed:
|
2019-10-19 10:15:31 +00:00
|
|
|
for n,(i,j) in enumerate(zip(cls.tmpdir_nums,(128,192,256))):
|
2021-09-29 21:17:57 +00:00
|
|
|
k = f'{a}_{n+1}'
|
2020-02-12 10:36:15 +00:00
|
|
|
if hasattr(cls,'skip_cmds') and k in cls.skip_cmds:
|
|
|
|
|
continue
|
2019-10-31 10:46:46 +00:00
|
|
|
sdeps = get_shared_deps(k,i)
|
2023-10-11 12:58:51 +00:00
|
|
|
if isinstance(b,str):
|
2021-09-29 21:17:57 +00:00
|
|
|
cdata.append( (k, (i,f'{b} ({j}-bit)',[[[]+sdeps,i]])) )
|
2019-03-02 18:27:53 +00:00
|
|
|
else:
|
2021-09-29 21:17:57 +00:00
|
|
|
cdata.append( (k, (i,f'{b[1]} ({j}-bit)',[[b[0]+sdeps,i]])) )
|
2019-03-02 18:27:53 +00:00
|
|
|
else:
|
2019-10-19 10:15:31 +00:00
|
|
|
cdata.append( (a, b if full_data else (cls.tmpdir_nums[0],b,[[[],cls.tmpdir_nums[0]]])) )
|
ERC20 token support (create/deploy, TX create/sign/send)
This feature is EXPERIMENTAL. Until v0.9.9 is released, mainnet use is
strictly at your own risk!
To test on dev chain, run 'test/test.py -e ethdev'
To test on Kovan, add '--testnet=1' option to all commands below
Transaction example:
Generate some ETH addresses with your default wallet:
$ mmgen-addrgen --coin=eth 1-5
Create an EOS token tracking wallet and import the addresses into it:
$ mmgen-addrimport --coin=eth --token=86fa049857e0209aa7d9e616f7eb3b3b78ecfdb0 ABCDABCD-ETH[1-5].addrs
Send 10+ EOS from an exchange or another wallet to address ABCDABCD:E:1
Create a TX sending 10 EOS to address aabbccdd..., with change to ABCDABCD:E:2:
$ mmgen-txcreate --coin=eth --token=eos aabbccddaabbccddaabbccddaabbccddaabbccdd,10 ABCDABCD:E:2
On your offline machine, sign the TX:
$ mmgen-txsign --coin=eth --token=eos ABC123-EOS[10,50000].rawtx
On your online machine, send the TX:
$ mmgen-txsend --coin=eth --token=eos ABC123-EOS[10,50000].sigtx
View your EOS tracking wallet:
$ mmgen-tool --coin=eth --token=eos twview
Token creation/deployment example:
Install the Solidity compiler ('solc') on your system.
Create a token 'MFT' with default parameters, owned by ddeeff... (ABCDABCD:E:1):
$ scripts/create-token.py --symbol=MFT --name='My First Token' ddeeffddeeffddeeffddeeffddeeffddeeffddee
Deploy the token on the ETH blockchain:
$ mmgen-txdo --coin=eth --tx-gas=200000 --contract-data=SafeMath.bin
$ mmgen-txdo --coin=eth --tx-gas=250000 --contract-data=Owned.bin
$ mmgen-txdo --coin=eth --tx-gas=1100000 --contract-data=Token.bin
...
Token address: abcd1234abcd1234abcd1234abcd1234abcd1234
Create an MFT token tracking wallet and import your ETH addresses into it:
$ mmgen-addrimport --coin=eth --token=abcd1234abcd1234abcd1234abcd1234abcd1234 ABCDABCD-ETH[1-5].addrs
View your MFT tracking wallet:
$ mmgen-tool --coin=eth --token=mft twview
2018-07-25 12:57:04 +00:00
|
|
|
|
2019-03-02 18:27:53 +00:00
|
|
|
if add_dpy:
|
|
|
|
|
self.dpy_data.update(dict(cdata))
|
|
|
|
|
else:
|
|
|
|
|
self.cmd_list = tuple(e[0] for e in cdata)
|
|
|
|
|
self.dpy_data = dict(cdata)
|
|
|
|
|
|
2019-10-19 10:15:31 +00:00
|
|
|
return cls
|
2019-03-02 18:27:53 +00:00
|
|
|
|
2022-07-29 16:45:31 +00:00
|
|
|
def gm_init_group(self,trunner,gname,sg_name,spawn_prog):
|
2019-10-19 10:15:31 +00:00
|
|
|
kwargs = self.cmd_groups[gname][1]
|
2022-07-29 16:45:31 +00:00
|
|
|
cls = self.create_group(gname,sg_name,**kwargs)
|
2020-05-10 13:39:53 +00:00
|
|
|
cls.group_name = gname
|
2019-10-19 10:15:31 +00:00
|
|
|
return cls(trunner,cfgs,spawn_prog)
|
2019-03-02 18:27:53 +00:00
|
|
|
|
2022-08-04 13:44:31 +00:00
|
|
|
def get_cls_by_gname(self,gname):
|
|
|
|
|
return self.load_mod( gname, self.cmd_groups[gname][1].get('modname') )
|
|
|
|
|
|
2019-10-24 16:21:09 +00:00
|
|
|
def list_cmd_groups(self):
|
2019-12-07 12:20:21 +00:00
|
|
|
ginfo = []
|
2019-10-24 16:21:09 +00:00
|
|
|
for gname in self.cmd_groups:
|
2022-08-04 13:44:31 +00:00
|
|
|
ginfo.append(( gname, self.get_cls_by_gname(gname) ))
|
2019-12-07 12:20:21 +00:00
|
|
|
|
2023-03-28 18:14:37 +00:00
|
|
|
if cfg.list_current_cmd_groups:
|
|
|
|
|
exclude = (cfg.exclude_groups or '').split(',')
|
2019-12-07 12:20:21 +00:00
|
|
|
ginfo = [g for g in ginfo
|
|
|
|
|
if network_id in g[1].networks
|
|
|
|
|
and not g[0] in exclude
|
2023-03-28 18:14:37 +00:00
|
|
|
and g[0] in tuple(self.cmd_groups_dfl) + tuple(cmd_args) ]
|
2022-07-29 16:45:31 +00:00
|
|
|
desc = 'CONFIGURED'
|
|
|
|
|
else:
|
|
|
|
|
desc = 'AVAILABLE'
|
|
|
|
|
|
|
|
|
|
def gen_output():
|
|
|
|
|
yield green(f'{desc} COMMAND GROUPS AND SUBGROUPS:')
|
|
|
|
|
yield ''
|
|
|
|
|
for name,cls in ginfo:
|
|
|
|
|
yield ' {} - {}'.format(
|
|
|
|
|
yellow(name.ljust(13)),
|
|
|
|
|
(cls.__doc__.strip() if cls.__doc__ else cls.__name__) )
|
|
|
|
|
if hasattr(cls,'cmd_subgroups'):
|
2023-01-29 16:16:22 +00:00
|
|
|
subgroups = {k:v for k,v in cls.cmd_subgroups.items() if not k.startswith('_')}
|
|
|
|
|
max_w = max(len(k) for k in subgroups)
|
|
|
|
|
for k,v in subgroups.items():
|
2022-07-29 16:45:31 +00:00
|
|
|
yield ' + {} · {}'.format( cyan(k.ljust(max_w+1)), v[0] )
|
|
|
|
|
|
2022-10-17 18:37:22 +00:00
|
|
|
from mmgen.ui import do_pager
|
2022-07-29 16:45:31 +00:00
|
|
|
do_pager('\n'.join(gen_output()))
|
2019-12-07 12:20:21 +00:00
|
|
|
|
2022-02-06 13:28:44 +00:00
|
|
|
Msg( '\n' + ' '.join(e[0] for e in ginfo) )
|
|
|
|
|
sys.exit(0)
|
2019-10-24 16:21:09 +00:00
|
|
|
|
2019-03-02 18:27:53 +00:00
|
|
|
def find_cmd_in_groups(self,cmd,group=None):
|
|
|
|
|
"""
|
|
|
|
|
Search for a test command in specified group or all configured command groups
|
|
|
|
|
and return it as a string. Loads modules but alters no global variables.
|
|
|
|
|
"""
|
|
|
|
|
if group:
|
|
|
|
|
if not group in [e[0] for e in self.cmd_groups]:
|
2021-09-29 21:17:57 +00:00
|
|
|
die(1,f'{group!r}: unrecognized group')
|
2019-03-02 18:27:53 +00:00
|
|
|
groups = [self.cmd_groups[group]]
|
|
|
|
|
else:
|
|
|
|
|
groups = self.cmd_groups
|
|
|
|
|
|
|
|
|
|
for gname in groups:
|
2022-08-04 13:44:31 +00:00
|
|
|
cls = self.get_cls_by_gname(gname)
|
2022-07-29 16:45:30 +00:00
|
|
|
|
2022-07-29 16:45:31 +00:00
|
|
|
if not hasattr(cls,'cmd_group'):
|
|
|
|
|
cls.cmd_group = self.create_cmd_group(cls)
|
|
|
|
|
|
2019-10-19 10:15:31 +00:00
|
|
|
if cmd in cls.cmd_group: # first search the class
|
2019-03-02 18:27:53 +00:00
|
|
|
return gname
|
2022-07-29 16:45:30 +00:00
|
|
|
|
2019-10-19 10:15:31 +00:00
|
|
|
if cmd in dir(cls(None,None,None)): # then a throwaway instance
|
|
|
|
|
return gname # cmd might exist in more than one group - we'll go with the first
|
2022-07-29 16:45:30 +00:00
|
|
|
|
2019-03-02 18:27:53 +00:00
|
|
|
return None
|
|
|
|
|
|
2023-10-13 09:51:14 +00:00
|
|
|
class CmdTestRunner:
|
|
|
|
|
'cmdtest.py test runner'
|
2019-03-02 18:27:53 +00:00
|
|
|
|
2022-01-06 20:24:22 +00:00
|
|
|
def __del__(self):
|
2024-02-15 09:28:07 +00:00
|
|
|
if logging:
|
2022-01-06 20:24:22 +00:00
|
|
|
self.log_fd.close()
|
|
|
|
|
|
2019-03-02 18:27:53 +00:00
|
|
|
def __init__(self,data_dir,trash_dir):
|
2021-09-29 21:17:57 +00:00
|
|
|
|
2019-03-02 18:27:53 +00:00
|
|
|
self.data_dir = data_dir
|
|
|
|
|
self.trash_dir = trash_dir
|
|
|
|
|
self.cmd_total = 0
|
2020-04-09 19:34:24 +00:00
|
|
|
self.rebuild_list = {}
|
2019-03-02 18:27:53 +00:00
|
|
|
self.gm = CmdGroupMgr()
|
2019-12-08 18:38:31 +00:00
|
|
|
self.repo_root = repo_root
|
2020-02-22 19:54:03 +00:00
|
|
|
self.skipped_warnings = []
|
2022-02-07 21:08:09 +00:00
|
|
|
self.resume_cmd = None
|
2022-12-01 12:32:31 +00:00
|
|
|
self.deps_only = None
|
2019-03-02 18:27:53 +00:00
|
|
|
|
2024-02-15 09:28:07 +00:00
|
|
|
if logging:
|
2023-10-13 09:51:14 +00:00
|
|
|
self.log_fd = open(cmdtest_py_log_fn,'a')
|
2021-09-29 21:17:57 +00:00
|
|
|
self.log_fd.write(f'\nLog started: {make_timestr()} UTC\n')
|
2023-10-13 09:51:14 +00:00
|
|
|
omsg(f'INFO → Logging to file {cmdtest_py_log_fn!r}')
|
2019-03-02 18:27:53 +00:00
|
|
|
else:
|
|
|
|
|
self.log_fd = None
|
ERC20 token support (create/deploy, TX create/sign/send)
This feature is EXPERIMENTAL. Until v0.9.9 is released, mainnet use is
strictly at your own risk!
To test on dev chain, run 'test/test.py -e ethdev'
To test on Kovan, add '--testnet=1' option to all commands below
Transaction example:
Generate some ETH addresses with your default wallet:
$ mmgen-addrgen --coin=eth 1-5
Create an EOS token tracking wallet and import the addresses into it:
$ mmgen-addrimport --coin=eth --token=86fa049857e0209aa7d9e616f7eb3b3b78ecfdb0 ABCDABCD-ETH[1-5].addrs
Send 10+ EOS from an exchange or another wallet to address ABCDABCD:E:1
Create a TX sending 10 EOS to address aabbccdd..., with change to ABCDABCD:E:2:
$ mmgen-txcreate --coin=eth --token=eos aabbccddaabbccddaabbccddaabbccddaabbccdd,10 ABCDABCD:E:2
On your offline machine, sign the TX:
$ mmgen-txsign --coin=eth --token=eos ABC123-EOS[10,50000].rawtx
On your online machine, send the TX:
$ mmgen-txsend --coin=eth --token=eos ABC123-EOS[10,50000].sigtx
View your EOS tracking wallet:
$ mmgen-tool --coin=eth --token=eos twview
Token creation/deployment example:
Install the Solidity compiler ('solc') on your system.
Create a token 'MFT' with default parameters, owned by ddeeff... (ABCDABCD:E:1):
$ scripts/create-token.py --symbol=MFT --name='My First Token' ddeeffddeeffddeeffddeeffddeeffddeeffddee
Deploy the token on the ETH blockchain:
$ mmgen-txdo --coin=eth --tx-gas=200000 --contract-data=SafeMath.bin
$ mmgen-txdo --coin=eth --tx-gas=250000 --contract-data=Owned.bin
$ mmgen-txdo --coin=eth --tx-gas=1100000 --contract-data=Token.bin
...
Token address: abcd1234abcd1234abcd1234abcd1234abcd1234
Create an MFT token tracking wallet and import your ETH addresses into it:
$ mmgen-addrimport --coin=eth --token=abcd1234abcd1234abcd1234abcd1234abcd1234 ABCDABCD-ETH[1-5].addrs
View your MFT tracking wallet:
$ mmgen-tool --coin=eth --token=mft twview
2018-07-25 12:57:04 +00:00
|
|
|
|
2023-03-28 18:14:37 +00:00
|
|
|
if cfg.coverage:
|
2021-10-10 20:18:13 +00:00
|
|
|
coverdir,accfile = init_coverage()
|
|
|
|
|
omsg(f'INFO → Writing coverage files to {coverdir!r}')
|
|
|
|
|
self.pre_args = ['python3','-m','trace','--count','--coverdir='+coverdir,'--file='+accfile]
|
|
|
|
|
else:
|
2023-10-13 09:51:12 +00:00
|
|
|
self.pre_args = ['python3'] if sys.platform == 'win32' else []
|
2018-07-28 13:52:43 +00:00
|
|
|
|
2023-03-28 18:14:37 +00:00
|
|
|
if cfg.pexpect_spawn:
|
2023-10-11 12:58:52 +00:00
|
|
|
omsg('INFO → Using pexpect.spawn() for real terminal emulation')
|
2021-10-07 13:12:10 +00:00
|
|
|
|
2023-05-23 12:12:33 +00:00
|
|
|
self.set_spawn_env()
|
|
|
|
|
|
|
|
|
|
def set_spawn_env(self):
|
|
|
|
|
|
|
|
|
|
self.spawn_env = dict(os.environ)
|
|
|
|
|
self.spawn_env.update({
|
|
|
|
|
'MMGEN_NO_LICENSE': '1',
|
|
|
|
|
'MMGEN_BOGUS_SEND': '1',
|
|
|
|
|
'MMGEN_TEST_SUITE_PEXPECT': '1',
|
2024-02-22 12:48:13 +00:00
|
|
|
'EXEC_WRAPPER_DO_RUNTIME_MSG':'1',
|
2023-10-13 09:51:14 +00:00
|
|
|
# if cmdtest.py itself is running under exec_wrapper, disable writing of traceback file for spawned script
|
2023-05-23 12:12:33 +00:00
|
|
|
'EXEC_WRAPPER_TRACEBACK': '' if os.getenv('MMGEN_EXEC_WRAPPER') else '1',
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
if cfg.exact_output:
|
|
|
|
|
from mmgen.term import get_terminal_size
|
|
|
|
|
self.spawn_env['MMGEN_COLUMNS'] = str(get_terminal_size().width)
|
|
|
|
|
else:
|
|
|
|
|
self.spawn_env['MMGEN_COLUMNS'] = '120'
|
|
|
|
|
|
2023-10-11 12:58:51 +00:00
|
|
|
def spawn_wrapper(
|
|
|
|
|
self,
|
|
|
|
|
cmd,
|
|
|
|
|
args = [],
|
|
|
|
|
extra_desc = '',
|
|
|
|
|
no_output = False,
|
|
|
|
|
msg_only = False,
|
|
|
|
|
no_msg = False,
|
|
|
|
|
cmd_dir = 'cmds',
|
2022-10-28 11:35:13 +00:00
|
|
|
no_exec_wrapper = False,
|
2023-10-11 12:58:51 +00:00
|
|
|
timeout = None,
|
|
|
|
|
pexpect_spawn = None,
|
|
|
|
|
direct_exec = False,
|
2024-02-22 12:48:13 +00:00
|
|
|
no_passthru_opts = False,
|
2024-02-22 12:48:13 +00:00
|
|
|
spawn_env_override = None,
|
2024-03-08 14:13:23 +00:00
|
|
|
exit_val = None,
|
2023-10-11 12:58:51 +00:00
|
|
|
env = {}):
|
2018-07-28 13:52:43 +00:00
|
|
|
|
2024-03-08 14:13:23 +00:00
|
|
|
self.exit_val = exit_val
|
|
|
|
|
|
2023-10-13 09:51:14 +00:00
|
|
|
desc = self.tg.test_name if cfg.names else self.gm.dpy_data[self.tg.test_name][1]
|
2021-10-08 16:44:56 +00:00
|
|
|
if extra_desc:
|
|
|
|
|
desc += ' ' + extra_desc
|
2018-07-28 13:52:43 +00:00
|
|
|
|
2021-10-10 20:18:13 +00:00
|
|
|
cmd_path = (
|
2023-03-28 18:14:37 +00:00
|
|
|
cmd if cfg.system # cfg.system is broken for main test group with overlay tree
|
2021-10-10 20:18:13 +00:00
|
|
|
else os.path.relpath(os.path.join(repo_root,cmd_dir,cmd)) )
|
2018-10-13 15:00:03 +00:00
|
|
|
|
2021-10-10 20:18:13 +00:00
|
|
|
args = (
|
|
|
|
|
self.pre_args +
|
|
|
|
|
([] if no_exec_wrapper else ['scripts/exec_wrapper.py']) +
|
|
|
|
|
[cmd_path] +
|
2024-02-22 12:48:13 +00:00
|
|
|
([] if no_passthru_opts else self.passthru_opts) +
|
2023-10-13 09:51:14 +00:00
|
|
|
self.tg.extra_spawn_args +
|
2021-10-10 20:18:13 +00:00
|
|
|
args )
|
2018-10-13 15:00:03 +00:00
|
|
|
|
2024-02-15 09:28:07 +00:00
|
|
|
try:
|
|
|
|
|
qargs = ['{q}{}{q}'.format( a, q = "'" if ' ' in a else '' ) for a in args]
|
|
|
|
|
except:
|
|
|
|
|
msg(f'args: {args}')
|
|
|
|
|
raise
|
|
|
|
|
|
2022-11-13 15:36:34 +00:00
|
|
|
cmd_disp = ' '.join(qargs).replace('\\','/') # for mingw
|
|
|
|
|
|
2024-02-15 09:28:07 +00:00
|
|
|
if logging:
|
2022-11-13 15:36:34 +00:00
|
|
|
self.log_fd.write('[{}][{}:{}] {}\n'.format(
|
|
|
|
|
proto.coin.lower(),
|
2023-10-13 09:51:14 +00:00
|
|
|
self.tg.group_name,
|
|
|
|
|
self.tg.test_name,
|
2022-11-13 15:36:34 +00:00
|
|
|
cmd_disp))
|
|
|
|
|
|
|
|
|
|
for i in args: # die only after writing log entry
|
2021-06-08 11:46:05 +00:00
|
|
|
if not isinstance(i,str):
|
2021-10-08 16:44:56 +00:00
|
|
|
die(2,'Error: missing input files in cmd line?:\nName: {}\nCmdline: {!r}'.format(
|
2023-10-13 09:51:14 +00:00
|
|
|
self.tg.test_name,
|
2021-10-08 16:44:56 +00:00
|
|
|
args ))
|
2018-10-13 15:00:03 +00:00
|
|
|
|
2019-03-02 18:27:53 +00:00
|
|
|
if not no_msg:
|
2023-03-28 18:14:37 +00:00
|
|
|
t_pfx = '' if cfg.no_timings else f'[{time.time() - self.start_time:08.2f}] '
|
|
|
|
|
if cfg.verbose or cfg.print_cmdline or cfg.exact_output:
|
2021-09-29 21:17:56 +00:00
|
|
|
omsg(green(f'{t_pfx}Testing: {desc}'))
|
2019-03-02 18:27:53 +00:00
|
|
|
if not msg_only:
|
2023-03-28 18:14:37 +00:00
|
|
|
clr1,clr2 = (nocolor,nocolor) if cfg.print_cmdline else (green,cyan)
|
2021-10-08 16:44:56 +00:00
|
|
|
omsg(
|
|
|
|
|
clr1('Executing: ') +
|
2023-10-13 09:51:12 +00:00
|
|
|
clr2(repr(cmd_disp) if sys.platform == 'win32' else cmd_disp)
|
2021-10-08 16:44:56 +00:00
|
|
|
)
|
2019-03-02 18:27:53 +00:00
|
|
|
else:
|
2023-05-06 15:14:06 +00:00
|
|
|
omsg_r('{a}Testing {b}: {c}'.format(
|
2023-05-06 15:14:06 +00:00
|
|
|
a = t_pfx,
|
2023-05-06 15:14:06 +00:00
|
|
|
b = desc,
|
|
|
|
|
c = 'OK\n' if direct_exec or cfg.direct_exec else ''))
|
2018-06-02 13:03:12 +00:00
|
|
|
|
2020-05-10 13:39:53 +00:00
|
|
|
if msg_only:
|
|
|
|
|
return
|
2017-10-29 15:06:16 +03:00
|
|
|
|
2022-10-29 20:10:24 +00:00
|
|
|
# NB: the `pexpect_spawn` arg enables hold_protect and send_delay while the corresponding cmdline
|
|
|
|
|
# option does not. For performance reasons, this is the desired behavior. For full emulation of
|
|
|
|
|
# the user experience with hold protect enabled, specify --buf-keypress or --demo.
|
2023-03-28 18:14:37 +00:00
|
|
|
send_delay = 0.4 if pexpect_spawn is True or cfg.buf_keypress else None
|
|
|
|
|
pexpect_spawn = pexpect_spawn if pexpect_spawn is not None else bool(cfg.pexpect_spawn)
|
2022-10-28 11:35:13 +00:00
|
|
|
|
2024-02-22 12:48:13 +00:00
|
|
|
spawn_env = dict(spawn_env_override or self.tg.spawn_env)
|
2023-05-23 12:12:33 +00:00
|
|
|
spawn_env.update({
|
|
|
|
|
'MMGEN_HOLD_PROTECT_DISABLE': '' if send_delay else '1',
|
|
|
|
|
'MMGEN_TEST_SUITE_POPEN_SPAWN': '' if pexpect_spawn else '1',
|
2024-03-08 14:13:23 +00:00
|
|
|
'EXEC_WRAPPER_EXIT_VAL': '' if exit_val is None else str(exit_val),
|
2023-05-23 12:12:33 +00:00
|
|
|
})
|
|
|
|
|
spawn_env.update(env)
|
2021-10-03 17:40:02 +00:00
|
|
|
|
2020-03-16 10:45:00 +00:00
|
|
|
from test.include.pexpect import MMGenPexpect
|
2022-10-29 20:10:23 +00:00
|
|
|
return MMGenPexpect(
|
|
|
|
|
args = args,
|
|
|
|
|
no_output = no_output,
|
2023-05-23 12:12:33 +00:00
|
|
|
spawn_env = spawn_env,
|
2022-10-29 20:10:24 +00:00
|
|
|
pexpect_spawn = pexpect_spawn,
|
2022-12-07 10:40:53 +00:00
|
|
|
timeout = timeout,
|
2023-05-06 15:14:06 +00:00
|
|
|
send_delay = send_delay,
|
|
|
|
|
direct_exec = direct_exec )
|
2018-05-16 15:36:51 +00:00
|
|
|
|
2019-03-02 18:27:53 +00:00
|
|
|
def end_msg(self):
|
2021-06-28 19:04:25 +00:00
|
|
|
t = int(time.time() - self.start_time)
|
2021-09-29 21:17:56 +00:00
|
|
|
sys.stderr.write(green(
|
2021-10-06 13:22:33 +00:00
|
|
|
f'{self.cmd_total} test{suf(self.cmd_total)} performed' +
|
2023-03-28 18:14:37 +00:00
|
|
|
('\n' if cfg.no_timings else f'. Elapsed time: {t//60:02d}:{t%60:02d}\n')
|
2021-09-29 21:17:56 +00:00
|
|
|
))
|
2018-05-16 15:36:51 +00:00
|
|
|
|
2022-07-29 16:45:31 +00:00
|
|
|
def init_group(self,gname,sg_name=None,cmd=None,quiet=False,do_clean=True):
|
2021-09-26 21:16:54 +00:00
|
|
|
|
2023-11-21 15:48:10 +00:00
|
|
|
from test.cmdtest_py_d.cfg import cmd_groups_altcoin
|
|
|
|
|
if cfg.no_altcoin and gname in cmd_groups_altcoin:
|
|
|
|
|
omsg(gray(f'INFO → skipping test group {gname!r} (--no-altcoin)'))
|
|
|
|
|
return None
|
|
|
|
|
|
2023-10-13 09:51:14 +00:00
|
|
|
ct_cls = CmdGroupMgr().load_mod(gname)
|
2017-12-16 09:31:00 +03:00
|
|
|
|
2023-10-13 09:51:14 +00:00
|
|
|
if sys.platform == 'win32' and ct_cls.win_skip:
|
2023-11-21 15:43:52 +00:00
|
|
|
omsg(gray(f'INFO → skipping test {gname!r} (platform=win32)'))
|
2024-02-22 12:48:42 +00:00
|
|
|
return None
|
2023-05-19 16:16:53 +00:00
|
|
|
|
2019-03-02 18:27:53 +00:00
|
|
|
for k in ('segwit','segwit_random','bech32'):
|
2023-03-28 18:14:37 +00:00
|
|
|
if getattr(cfg,k):
|
2019-03-02 18:27:53 +00:00
|
|
|
segwit_opt = k
|
|
|
|
|
break
|
|
|
|
|
else:
|
|
|
|
|
segwit_opt = None
|
|
|
|
|
|
2020-05-16 16:07:28 +00:00
|
|
|
def gen_msg():
|
|
|
|
|
yield ('{g}:{c}' if cmd else 'test group {g!r}').format(g=gname,c=cmd)
|
2023-10-13 09:51:14 +00:00
|
|
|
if len(ct_cls.networks) != 1:
|
2021-09-29 21:17:57 +00:00
|
|
|
yield f' for {proto.coin} {proto.network}'
|
2020-05-16 16:07:28 +00:00
|
|
|
if segwit_opt:
|
2021-09-29 21:17:57 +00:00
|
|
|
yield ' (--{})'.format( segwit_opt.replace('_','-') )
|
2020-05-16 16:07:28 +00:00
|
|
|
|
|
|
|
|
m = ''.join(gen_msg())
|
2019-03-02 18:27:53 +00:00
|
|
|
|
2023-10-13 09:51:14 +00:00
|
|
|
if segwit_opt and not ct_cls.segwit_opts_ok:
|
2023-11-21 15:43:52 +00:00
|
|
|
iqmsg(gray(f'INFO → skipping {m}'))
|
2024-02-22 12:48:42 +00:00
|
|
|
return None
|
2019-03-02 18:27:53 +00:00
|
|
|
|
|
|
|
|
# 'networks = ()' means all networks allowed
|
2023-10-13 09:51:14 +00:00
|
|
|
nws = [(e.split('_')[0],'testnet') if '_' in e else (e,'mainnet') for e in ct_cls.networks]
|
2019-03-02 18:27:53 +00:00
|
|
|
if nws:
|
2020-05-28 09:53:34 +00:00
|
|
|
coin = proto.coin.lower()
|
|
|
|
|
nw = ('mainnet','testnet')[proto.testnet]
|
2019-03-02 18:27:53 +00:00
|
|
|
for a,b in nws:
|
|
|
|
|
if a == coin and b == nw:
|
|
|
|
|
break
|
|
|
|
|
else:
|
2023-11-21 15:43:52 +00:00
|
|
|
iqmsg(gray(f'INFO → skipping {m} (network={nw})'))
|
2024-02-22 12:48:42 +00:00
|
|
|
return None
|
2017-10-28 00:11:00 +03:00
|
|
|
|
2021-10-08 16:44:56 +00:00
|
|
|
if do_clean:
|
2023-10-13 09:51:14 +00:00
|
|
|
clean(ct_cls.tmpdir_nums,clean_overlay=False)
|
2021-10-08 16:44:56 +00:00
|
|
|
|
2019-10-31 10:46:46 +00:00
|
|
|
if not quiet:
|
|
|
|
|
bmsg('Executing ' + m)
|
2018-06-04 21:15:52 +00:00
|
|
|
|
2022-08-04 13:44:31 +00:00
|
|
|
if (not self.daemon_started) and self.gm.get_cls_by_gname(gname).need_daemon:
|
2021-10-03 17:40:02 +00:00
|
|
|
start_test_daemons(network_id,remove_datadir=True)
|
2022-08-04 13:44:31 +00:00
|
|
|
self.daemon_started = True
|
2021-10-03 17:40:02 +00:00
|
|
|
|
2024-02-22 12:48:42 +00:00
|
|
|
if hasattr(self,'tg'):
|
|
|
|
|
del self.tg
|
|
|
|
|
|
2023-10-13 09:51:14 +00:00
|
|
|
self.tg = self.gm.gm_init_group(self,gname,sg_name,self.spawn_wrapper)
|
|
|
|
|
self.ct_clsname = type(self.tg).__name__
|
2017-10-28 00:11:00 +03:00
|
|
|
|
2023-03-27 10:48:21 +00:00
|
|
|
# pass through opts from cmdline (po.user_opts)
|
2021-10-08 16:44:56 +00:00
|
|
|
self.passthru_opts = ['--{}{}'.format(
|
|
|
|
|
k.replace('_','-'),
|
2023-03-28 18:14:37 +00:00
|
|
|
'' if cfg._uopts[k] is True else '=' + cfg._uopts[k]
|
2023-10-13 09:51:14 +00:00
|
|
|
) for k in cfg._uopts if k in self.tg.base_passthru_opts + self.tg.passthru_opts]
|
2021-10-08 16:44:56 +00:00
|
|
|
|
2023-03-28 18:14:37 +00:00
|
|
|
if cfg.resuming:
|
|
|
|
|
rc = cfg.resume or cfg.resume_after
|
|
|
|
|
offset = 1 if cfg.resume_after else 0
|
2022-02-07 21:08:09 +00:00
|
|
|
self.resume_cmd = self.gm.cmd_list[self.gm.cmd_list.index(rc)+offset]
|
|
|
|
|
omsg(f'INFO → Resuming at command {self.resume_cmd!r}')
|
2023-03-28 18:14:37 +00:00
|
|
|
if cfg.step:
|
|
|
|
|
cfg.exit_after = self.resume_cmd
|
2020-05-10 13:39:53 +00:00
|
|
|
|
2023-03-28 18:14:37 +00:00
|
|
|
if cfg.exit_after and cfg.exit_after not in self.gm.cmd_list:
|
|
|
|
|
die(1,f'{cfg.exit_after!r}: command not recognized')
|
2015-10-25 13:04:30 +03:00
|
|
|
|
2024-02-22 12:48:42 +00:00
|
|
|
return self.tg
|
2015-10-25 13:04:30 +03:00
|
|
|
|
2023-03-28 18:14:37 +00:00
|
|
|
def run_tests(self,cmd_args):
|
2021-06-28 19:04:25 +00:00
|
|
|
self.start_time = time.time()
|
2022-08-04 13:44:31 +00:00
|
|
|
self.daemon_started = False
|
2019-10-31 10:46:46 +00:00
|
|
|
gname_save = None
|
2024-02-22 12:48:42 +00:00
|
|
|
|
|
|
|
|
def parse_arg(arg):
|
|
|
|
|
if '.' in arg:
|
|
|
|
|
a,b = arg.split('.')
|
|
|
|
|
return [a] + b.split(':') if ':' in b else [a,b,None]
|
|
|
|
|
elif ':' in arg:
|
|
|
|
|
a,b = arg.split(':')
|
|
|
|
|
return [a,None,b]
|
|
|
|
|
else:
|
|
|
|
|
return [self.gm.find_cmd_in_groups(arg),None,arg]
|
|
|
|
|
|
2023-03-28 18:14:37 +00:00
|
|
|
if cmd_args:
|
|
|
|
|
for arg in cmd_args:
|
2019-03-02 18:27:53 +00:00
|
|
|
if arg in self.gm.cmd_groups:
|
2024-02-22 12:48:42 +00:00
|
|
|
if self.init_group(arg):
|
|
|
|
|
for cmd in self.gm.cmd_list:
|
|
|
|
|
self.check_needs_rerun(cmd,build=True)
|
|
|
|
|
do_between()
|
2019-03-02 18:27:53 +00:00
|
|
|
else:
|
2022-07-29 16:45:31 +00:00
|
|
|
gname,sg_name,cmdname = parse_arg(arg)
|
2019-03-02 18:27:53 +00:00
|
|
|
if gname:
|
2019-10-31 10:46:46 +00:00
|
|
|
same_grp = gname == gname_save # same group as previous cmd: don't clean, suppress blue msg
|
2024-02-22 12:48:42 +00:00
|
|
|
if self.init_group(gname,sg_name,cmdname,quiet=same_grp,do_clean=not same_grp):
|
|
|
|
|
if cmdname:
|
|
|
|
|
if cfg.deps_only:
|
|
|
|
|
self.deps_only = cmdname
|
|
|
|
|
try:
|
|
|
|
|
self.check_needs_rerun(cmdname,build=True)
|
|
|
|
|
except Exception as e: # allow calling of functions not in cmd_group
|
|
|
|
|
if isinstance(e,KeyError) and e.args[0] == cmdname:
|
|
|
|
|
ret = getattr(self.tg,cmdname)()
|
|
|
|
|
if type(ret).__name__ == 'coroutine':
|
2024-03-06 11:05:22 +00:00
|
|
|
asyncio.run(ret)
|
2024-02-22 12:48:42 +00:00
|
|
|
else:
|
|
|
|
|
raise
|
2022-07-29 16:45:31 +00:00
|
|
|
do_between()
|
2024-02-22 12:48:42 +00:00
|
|
|
else:
|
|
|
|
|
for cmd in self.gm.cmd_list:
|
|
|
|
|
self.check_needs_rerun(cmd,build=True)
|
|
|
|
|
do_between()
|
|
|
|
|
gname_save = gname
|
2019-03-02 18:27:53 +00:00
|
|
|
else:
|
2021-09-29 21:17:57 +00:00
|
|
|
die(1,f'{arg!r}: command not recognized')
|
2019-03-02 18:27:53 +00:00
|
|
|
else:
|
2023-03-28 18:14:37 +00:00
|
|
|
if cfg.exclude_groups:
|
|
|
|
|
exclude = cfg.exclude_groups.split(',')
|
2019-03-02 18:27:53 +00:00
|
|
|
for e in exclude:
|
2020-03-12 16:38:02 +00:00
|
|
|
if e not in self.gm.cmd_groups_dfl:
|
2021-09-29 21:17:57 +00:00
|
|
|
die(1,f'{e!r}: group not recognized')
|
2020-03-12 16:38:02 +00:00
|
|
|
for gname in self.gm.cmd_groups_dfl:
|
2023-03-28 18:14:37 +00:00
|
|
|
if cfg.exclude_groups and gname in exclude:
|
2020-05-10 13:39:53 +00:00
|
|
|
continue
|
2024-02-22 12:48:42 +00:00
|
|
|
if self.init_group(gname):
|
|
|
|
|
for cmd in self.gm.cmd_list:
|
|
|
|
|
self.check_needs_rerun(cmd,build=True)
|
|
|
|
|
do_between()
|
2019-03-02 18:27:53 +00:00
|
|
|
|
|
|
|
|
self.end_msg()
|
|
|
|
|
|
2023-10-11 12:58:51 +00:00
|
|
|
def check_needs_rerun(
|
|
|
|
|
self,
|
|
|
|
|
cmd,
|
2021-09-29 21:17:57 +00:00
|
|
|
build = False,
|
|
|
|
|
root = True,
|
|
|
|
|
force_delete = False,
|
2023-10-11 12:58:51 +00:00
|
|
|
dpy = False):
|
2015-04-25 19:39:25 +03:00
|
|
|
|
2023-10-13 09:51:14 +00:00
|
|
|
self.tg.test_name = cmd
|
2021-10-08 16:44:56 +00:00
|
|
|
|
2023-10-13 09:51:14 +00:00
|
|
|
if self.ct_clsname == 'CmdTestMain' and testing_segwit and cmd not in self.tg.segwit_do:
|
2021-10-08 16:44:56 +00:00
|
|
|
return False
|
|
|
|
|
|
2019-03-02 18:27:53 +00:00
|
|
|
rerun = root # force_delete is not passed to recursive call
|
|
|
|
|
|
|
|
|
|
fns = []
|
|
|
|
|
if force_delete or not root:
|
|
|
|
|
# does cmd produce a needed dependency(ies)?
|
2023-10-11 12:58:52 +00:00
|
|
|
ret = self.get_num_exts_for_cmd(cmd)
|
2019-03-02 18:27:53 +00:00
|
|
|
if ret:
|
|
|
|
|
for ext in ret[1]:
|
|
|
|
|
fn = get_file_with_ext(cfgs[ret[0]]['tmpdir'],ext,delete=build)
|
|
|
|
|
if fn:
|
2023-10-11 12:58:51 +00:00
|
|
|
if force_delete:
|
|
|
|
|
os.unlink(fn)
|
2019-03-02 18:27:53 +00:00
|
|
|
else: fns.append(fn)
|
|
|
|
|
else: rerun = True
|
|
|
|
|
|
|
|
|
|
fdeps = self.generate_file_deps(cmd)
|
|
|
|
|
cdeps = self.generate_cmd_deps(fdeps)
|
|
|
|
|
|
|
|
|
|
for fn in fns:
|
|
|
|
|
my_age = os.stat(fn).st_mtime
|
|
|
|
|
for num,ext in fdeps:
|
|
|
|
|
f = get_file_with_ext(cfgs[num]['tmpdir'],ext,delete=build)
|
|
|
|
|
if f and os.stat(f).st_mtime > my_age:
|
|
|
|
|
rerun = True
|
|
|
|
|
|
|
|
|
|
for cdep in cdeps:
|
|
|
|
|
if self.check_needs_rerun(cdep,build=build,root=False,dpy=cmd):
|
|
|
|
|
rerun = True
|
2015-04-25 19:39:25 +03:00
|
|
|
|
2019-03-02 18:27:53 +00:00
|
|
|
if build:
|
|
|
|
|
if rerun:
|
|
|
|
|
for fn in fns:
|
2022-01-06 20:24:22 +00:00
|
|
|
if not root:
|
|
|
|
|
os.unlink(fn)
|
2023-03-28 18:14:37 +00:00
|
|
|
if not (dpy and cfg.skipping_deps):
|
2019-03-02 18:27:53 +00:00
|
|
|
self.run_test(cmd)
|
2022-01-06 20:24:22 +00:00
|
|
|
if not root:
|
|
|
|
|
do_between()
|
2019-03-02 18:27:53 +00:00
|
|
|
else:
|
|
|
|
|
# If prog produces multiple files:
|
2023-10-11 12:58:52 +00:00
|
|
|
if cmd not in self.rebuild_list or rerun is True:
|
2019-03-02 18:27:53 +00:00
|
|
|
self.rebuild_list[cmd] = (rerun,fns[0] if fns else '') # FIX
|
2015-04-25 19:39:25 +03:00
|
|
|
|
2019-03-02 18:27:53 +00:00
|
|
|
return rerun
|
2015-04-25 19:39:25 +03:00
|
|
|
|
2019-03-02 18:27:53 +00:00
|
|
|
def run_test(self,cmd):
|
2017-09-19 22:14:00 +03:00
|
|
|
|
2022-12-01 12:32:31 +00:00
|
|
|
if self.deps_only and cmd == self.deps_only:
|
|
|
|
|
sys.exit(0)
|
|
|
|
|
|
2019-03-02 18:27:53 +00:00
|
|
|
d = [(str(num),ext) for exts,num in self.gm.dpy_data[cmd][2] for ext in exts]
|
2017-10-29 15:06:16 +03:00
|
|
|
|
2019-03-02 18:27:53 +00:00
|
|
|
# delete files depended on by this cmd
|
|
|
|
|
arg_list = [get_file_with_ext(cfgs[num]['tmpdir'],ext) for num,ext in d]
|
2019-02-08 12:10:35 +00:00
|
|
|
|
2019-10-31 10:46:46 +00:00
|
|
|
# remove shared_deps from arg list
|
2023-10-13 09:51:14 +00:00
|
|
|
if hasattr(self.tg,'shared_deps'):
|
|
|
|
|
arg_list = arg_list[:-len(self.tg.shared_deps)]
|
2019-10-31 10:46:46 +00:00
|
|
|
|
2022-02-07 21:08:09 +00:00
|
|
|
if self.resume_cmd:
|
|
|
|
|
if cmd != self.resume_cmd:
|
2019-03-02 18:27:53 +00:00
|
|
|
return
|
2022-02-07 21:08:09 +00:00
|
|
|
bmsg(f'Resuming at {self.resume_cmd!r}')
|
|
|
|
|
self.resume_cmd = None
|
2023-03-28 18:14:37 +00:00
|
|
|
cfg.skipping_deps = False
|
|
|
|
|
cfg.resuming = False
|
2017-12-16 09:31:00 +03:00
|
|
|
|
2023-03-28 18:14:37 +00:00
|
|
|
if cfg.profile:
|
2021-09-29 21:17:57 +00:00
|
|
|
start = time.time()
|
2015-01-03 00:14:40 +03:00
|
|
|
|
2023-10-13 09:51:14 +00:00
|
|
|
self.tg.test_name = cmd # NB: Do not remove, this needs to be set twice
|
2019-03-02 18:27:53 +00:00
|
|
|
cdata = self.gm.dpy_data[cmd]
|
2023-10-13 09:51:14 +00:00
|
|
|
# self.tg.test_dpydata = cdata
|
|
|
|
|
self.tg.tmpdir_num = cdata[0]
|
|
|
|
|
# self.tg.cfg = cfgs[str(cdata[0])] # will remove this eventually
|
2023-03-28 18:14:37 +00:00
|
|
|
test_cfg = cfgs[str(cdata[0])]
|
2019-03-02 18:27:53 +00:00
|
|
|
for k in ( 'seed_len', 'seed_id',
|
|
|
|
|
'wpasswd', 'kapasswd',
|
|
|
|
|
'segwit', 'hash_preset',
|
|
|
|
|
'bw_filename', 'bw_params', 'ref_bw_seed_id',
|
|
|
|
|
'addr_idx_list', 'pass_idx_list' ):
|
2023-03-28 18:14:37 +00:00
|
|
|
if k in test_cfg:
|
2023-10-13 09:51:14 +00:00
|
|
|
setattr(self.tg,k,test_cfg[k])
|
2019-03-02 18:27:53 +00:00
|
|
|
|
2023-10-13 09:51:14 +00:00
|
|
|
ret = getattr(self.tg,cmd)(*arg_list) # run the test
|
asyncio/aiohttp support
Asynchronous HTTP significantly speeds up operations involving multiple
JSON-RPC calls to the server, such as tracking wallet views for wallets
with a large number of outputs.
This patch adds base-level asyncio infrastructure plus aiohttp support to all
applicable MMGen commands.
The aiohttp package is not currently supported by MSYS2, so Windows users will
have to choose one of the other backends ('curl' is the default).
Tested on: Linux, Armbian, Windows; Python 3.6, 3.7, 3.8
New user features:
- configurable RPC backends via the 'rpc_backend' option. Supported
options are 'aiohttp' (Linux-only), 'httplib', 'requests' and 'curl'
- configurable RPC queue size via the 'aiohttp_rpc_queue_len' option
The patch also includes a rewrite/redesign of large parts of the MMGen code
base, most importantly:
- rpc.py - full rewrite of RPC library, new RPCBackends class
- main_addrimport.py - full rewrite
- main_autosign.py - LED code now handled by new LEDControl class
- eth/tw.py, eth/tx.py - reworked logic for resolving token symbols and
addresses
- eth/tx.py - separate classes for signed and unsigned transactions
Testing:
# Set a backend (choose one):
$ export MMGEN_RPC_BACKEND='aiohttp' # Linux-only
$ export MMGEN_RPC_BACKEND='curl' # Windows
$ export MMGEN_RPC_BACKEND='httplib' # compare performance with 'aiohttp'
# Bitcoin:
$ test/unit_tests.py rpc btc
$ test/test.py main regtest autosign
# Ethereum:
$ test/unit_tests.py rpc eth
$ test/tooltest2.py --coin=eth --testnet=1 txview
$ test/test.py --coin=eth ethdev
# Monero wallet:
$ test/unit_tests.py rpc xmr_wallet
$ test/test-release.sh -F xmr
2020-05-10 14:07:54 +00:00
|
|
|
if type(ret).__name__ == 'coroutine':
|
2024-03-06 11:05:22 +00:00
|
|
|
ret = asyncio.run(ret)
|
asyncio/aiohttp support
Asynchronous HTTP significantly speeds up operations involving multiple
JSON-RPC calls to the server, such as tracking wallet views for wallets
with a large number of outputs.
This patch adds base-level asyncio infrastructure plus aiohttp support to all
applicable MMGen commands.
The aiohttp package is not currently supported by MSYS2, so Windows users will
have to choose one of the other backends ('curl' is the default).
Tested on: Linux, Armbian, Windows; Python 3.6, 3.7, 3.8
New user features:
- configurable RPC backends via the 'rpc_backend' option. Supported
options are 'aiohttp' (Linux-only), 'httplib', 'requests' and 'curl'
- configurable RPC queue size via the 'aiohttp_rpc_queue_len' option
The patch also includes a rewrite/redesign of large parts of the MMGen code
base, most importantly:
- rpc.py - full rewrite of RPC library, new RPCBackends class
- main_addrimport.py - full rewrite
- main_autosign.py - LED code now handled by new LEDControl class
- eth/tw.py, eth/tx.py - reworked logic for resolving token symbols and
addresses
- eth/tx.py - separate classes for signed and unsigned transactions
Testing:
# Set a backend (choose one):
$ export MMGEN_RPC_BACKEND='aiohttp' # Linux-only
$ export MMGEN_RPC_BACKEND='curl' # Windows
$ export MMGEN_RPC_BACKEND='httplib' # compare performance with 'aiohttp'
# Bitcoin:
$ test/unit_tests.py rpc btc
$ test/test.py main regtest autosign
# Ethereum:
$ test/unit_tests.py rpc eth
$ test/tooltest2.py --coin=eth --testnet=1 txview
$ test/test.py --coin=eth ethdev
# Monero wallet:
$ test/unit_tests.py rpc xmr_wallet
$ test/test-release.sh -F xmr
2020-05-10 14:07:54 +00:00
|
|
|
self.process_retval(cmd,ret)
|
2015-01-03 20:45:01 +03:00
|
|
|
|
2023-03-28 18:14:37 +00:00
|
|
|
if cfg.profile:
|
2021-09-29 21:17:57 +00:00
|
|
|
omsg('\r\033[50C{:.4f}'.format( time.time() - start ))
|
2015-04-25 19:39:25 +03:00
|
|
|
|
2023-03-28 18:14:37 +00:00
|
|
|
if cmd == cfg.exit_after:
|
2019-03-02 18:27:53 +00:00
|
|
|
sys.exit(0)
|
2015-04-25 19:39:25 +03:00
|
|
|
|
2020-02-22 19:54:03 +00:00
|
|
|
def warn_skipped(self):
|
|
|
|
|
if self.skipped_warnings:
|
|
|
|
|
print(yellow('The following tests were skipped and may require attention:'))
|
|
|
|
|
r = '-' * 72 + '\n'
|
|
|
|
|
print(r+('\n'+r).join(self.skipped_warnings))
|
|
|
|
|
|
2019-03-02 18:27:53 +00:00
|
|
|
def process_retval(self,cmd,ret):
|
|
|
|
|
if type(ret).__name__ == 'MMGenPexpect':
|
2024-03-08 14:13:23 +00:00
|
|
|
ret.ok(exit_val=self.exit_val)
|
2019-03-02 18:27:53 +00:00
|
|
|
self.cmd_total += 1
|
|
|
|
|
elif ret == 'ok':
|
|
|
|
|
ok()
|
|
|
|
|
self.cmd_total += 1
|
2023-03-27 10:48:20 +00:00
|
|
|
elif ret == 'error':
|
2023-10-13 09:51:14 +00:00
|
|
|
die(2,red(f'\nTest {self.tg.test_name!r} failed'))
|
2024-02-22 12:48:13 +00:00
|
|
|
elif ret in ('skip','skip_msg','silent'):
|
2023-05-06 15:14:06 +00:00
|
|
|
if ret == 'silent':
|
|
|
|
|
self.cmd_total += 1
|
2024-02-22 12:48:13 +00:00
|
|
|
elif ret == 'skip_msg':
|
|
|
|
|
ok('SKIP')
|
2023-10-11 12:58:51 +00:00
|
|
|
elif isinstance(ret,tuple) and ret[0] == 'skip_warn':
|
2020-02-22 19:54:03 +00:00
|
|
|
self.skipped_warnings.append(
|
|
|
|
|
'Test {!r} was skipped:\n {}'.format(cmd,'\n '.join(ret[1].split('\n'))))
|
2019-03-02 18:27:53 +00:00
|
|
|
else:
|
2022-02-05 13:32:56 +00:00
|
|
|
die(2,f'{cmd!r} returned {ret}')
|
2015-04-25 19:39:25 +03:00
|
|
|
|
2019-03-02 18:27:53 +00:00
|
|
|
def check_deps(self,cmds): # TODO: broken
|
|
|
|
|
if len(cmds) != 1:
|
2023-03-28 18:14:36 +00:00
|
|
|
die(1,f'Usage: {gc.prog_name} check_deps <command>')
|
2015-01-03 00:14:40 +03:00
|
|
|
|
2019-03-02 18:27:53 +00:00
|
|
|
cmd = cmds[0]
|
2015-10-25 13:04:30 +03:00
|
|
|
|
2019-03-02 18:27:53 +00:00
|
|
|
if cmd not in self.gm.cmd_list:
|
2021-09-29 21:17:57 +00:00
|
|
|
die(1,f'{cmd!r}: unrecognized command')
|
2018-07-25 12:34:50 +00:00
|
|
|
|
2023-03-28 18:14:37 +00:00
|
|
|
if not cfg.quiet:
|
2021-09-29 21:17:57 +00:00
|
|
|
omsg(f'Checking dependencies for {cmd!r}')
|
2015-01-03 00:14:40 +03:00
|
|
|
|
2023-10-13 09:51:14 +00:00
|
|
|
self.check_needs_rerun(self.tg,cmd)
|
2016-07-27 22:55:47 +03:00
|
|
|
|
2019-03-02 18:27:53 +00:00
|
|
|
w = max(map(len,self.rebuild_list)) + 1
|
|
|
|
|
for cmd in self.rebuild_list:
|
|
|
|
|
c = self.rebuild_list[cmd]
|
|
|
|
|
m = 'Rebuild' if (c[0] and c[1]) else 'Build' if c[0] else 'OK'
|
2021-09-29 21:17:57 +00:00
|
|
|
omsg('cmd {:<{w}} {}'.format( cmd+':', m, w=w ))
|
2017-07-07 16:28:07 +03:00
|
|
|
|
2019-03-02 18:27:53 +00:00
|
|
|
def generate_file_deps(self,cmd):
|
|
|
|
|
return [(str(n),e) for exts,n in self.gm.dpy_data[cmd][2] for e in exts]
|
2018-07-23 21:17:05 +00:00
|
|
|
|
2019-03-02 18:27:53 +00:00
|
|
|
def generate_cmd_deps(self,fdeps):
|
|
|
|
|
return [cfgs[str(n)]['dep_generators'][ext] for n,ext in fdeps]
|
2015-01-03 00:14:40 +03:00
|
|
|
|
2023-10-11 12:58:52 +00:00
|
|
|
def get_num_exts_for_cmd(self,cmd):
|
2019-03-02 18:27:53 +00:00
|
|
|
try:
|
|
|
|
|
num = str(self.gm.dpy_data[cmd][0])
|
|
|
|
|
except KeyError:
|
2021-09-29 21:17:57 +00:00
|
|
|
qmsg_r(f'Missing dependency {cmd!r}')
|
2019-03-02 18:27:53 +00:00
|
|
|
gname = self.gm.find_cmd_in_groups(cmd)
|
|
|
|
|
if gname:
|
|
|
|
|
kwargs = self.gm.cmd_groups[gname][1]
|
|
|
|
|
kwargs.update({'add_dpy':True})
|
2022-07-29 16:45:31 +00:00
|
|
|
self.gm.create_group(gname,None,**kwargs)
|
2019-03-02 18:27:53 +00:00
|
|
|
num = str(self.gm.dpy_data[cmd][0])
|
2021-09-29 21:17:57 +00:00
|
|
|
qmsg(f' found in group {gname!r}')
|
2019-03-02 18:27:53 +00:00
|
|
|
else:
|
|
|
|
|
qmsg(' not found in any command group!')
|
|
|
|
|
raise
|
|
|
|
|
dgl = cfgs[num]['dep_generators']
|
|
|
|
|
if cmd in dgl.values():
|
|
|
|
|
exts = [k for k in dgl if dgl[k] == cmd]
|
|
|
|
|
return (num,exts)
|
|
|
|
|
else:
|
|
|
|
|
return None
|
2015-01-03 00:14:40 +03:00
|
|
|
|
2023-10-13 09:51:14 +00:00
|
|
|
if __name__ == '__main__':
|
2015-01-03 00:14:40 +03:00
|
|
|
|
2023-10-13 09:51:14 +00:00
|
|
|
if not cfg.skipping_deps: # do this before list cmds exit, so we stay in sync with shm_dir
|
|
|
|
|
create_tmp_dirs(shm_dir)
|
2015-01-03 00:14:40 +03:00
|
|
|
|
2023-10-13 09:51:14 +00:00
|
|
|
if cfg.list_cmd_groups:
|
|
|
|
|
CmdGroupMgr().list_cmd_groups()
|
|
|
|
|
elif cfg.list_cmds:
|
|
|
|
|
list_cmds()
|
|
|
|
|
elif cmd_args and cmd_args[0] in utils:
|
|
|
|
|
globals()[cmd_args[0]](*cmd_args[1:])
|
|
|
|
|
sys.exit(0)
|
2015-04-25 19:39:25 +03:00
|
|
|
|
2023-10-13 09:51:14 +00:00
|
|
|
if cfg.pause:
|
|
|
|
|
set_restore_term_at_exit()
|
2015-01-03 00:14:40 +03:00
|
|
|
|
2023-10-13 09:51:14 +00:00
|
|
|
from mmgen.exception import TestSuiteException,TestSuiteFatalException,TestSuiteSpawnedScriptException
|
2022-04-28 11:00:50 +00:00
|
|
|
|
2023-10-13 09:51:14 +00:00
|
|
|
try:
|
|
|
|
|
tr = CmdTestRunner(data_dir,trash_dir)
|
|
|
|
|
tr.run_tests(cmd_args)
|
|
|
|
|
tr.warn_skipped()
|
|
|
|
|
if tr.daemon_started:
|
|
|
|
|
stop_test_daemons(network_id)
|
|
|
|
|
except KeyboardInterrupt:
|
|
|
|
|
if tr.daemon_started:
|
|
|
|
|
stop_test_daemons(network_id)
|
|
|
|
|
tr.warn_skipped()
|
|
|
|
|
die(1,'\ntest.py exiting at user request')
|
|
|
|
|
except TestSuiteException as e:
|
|
|
|
|
die(2,e.args[0])
|
|
|
|
|
except TestSuiteFatalException as e:
|
|
|
|
|
die(4,e.args[0])
|
|
|
|
|
except TestSuiteSpawnedScriptException as e:
|
|
|
|
|
# if spawned script is not running under exec_wrapper, output brief error msg:
|
|
|
|
|
if os.getenv('MMGEN_EXEC_WRAPPER'):
|
|
|
|
|
Msg(red(str(e)))
|
|
|
|
|
Msg(blue('cmdtest.py: spawned script exited with error'))
|
|
|
|
|
except Exception:
|
|
|
|
|
# if cmdtest.py itself is running under exec_wrapper, re-raise so exec_wrapper can handle exception:
|
|
|
|
|
if os.getenv('MMGEN_EXEC_WRAPPER') or not os.getenv('MMGEN_IGNORE_TEST_PY_EXCEPTION'):
|
|
|
|
|
raise
|
|
|
|
|
die(1,red('Test script exited with error'))
|