From ebe21cf8d888badcf38a427786bccc9385be2426 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Sun, 8 Dec 2019 18:36:40 +0000 Subject: [PATCH] TestDaemon: support ETH; test.py: start/stop parity automatically --- mmgen/test_daemon.py | 58 +++++++++++++++++++++++++++++++++++-- test/test_py_d/ts_ethdev.py | 49 +++---------------------------- 2 files changed, 60 insertions(+), 47 deletions(-) diff --git a/mmgen/test_daemon.py b/mmgen/test_daemon.py index 2868d849..40d5a97d 100755 --- a/mmgen/test_daemon.py +++ b/mmgen/test_daemon.py @@ -20,6 +20,7 @@ test_daemon.py: Daemon control classes for MMGen test suite and regtest mode """ +import shutil from subprocess import run,PIPE from collections import namedtuple from mmgen.exception import * @@ -30,7 +31,7 @@ class TestDaemon(MMGenObject): subclasses_must_implement = ('state','stop_cmd') - network_ids = ('btc','btc_tn','bch','bch_tn','ltc','ltc_tn','xmr') + network_ids = ('btc','btc_tn','bch','bch_tn','ltc','ltc_tn','xmr','eth','etc') cd = namedtuple('coin_data',['coin','coind_exec','cli_exec','conf_file','dfl_rpc','dfl_rpc_tn']) coins = { @@ -52,6 +53,7 @@ class TestDaemon(MMGenObject): coind_args = [] cli_args = [] shared_args = [] + coind_cmd = [] coin_specific_coind_args = [] coin_specific_cli_args = [] @@ -81,6 +83,7 @@ class TestDaemon(MMGenObject): me = MMGenObject.__new__( MoneroTestDaemon if coinsym == 'xmr' + else EthereumTestDaemon if coinsym in ('eth','etc') else BitcoinTestDaemon ) me.network_id = network_id @@ -158,7 +161,8 @@ class TestDaemon(MMGenObject): + self.coin_specific_coind_args + self.coin_specific_shared_args + self.usr_coind_args - + self.usr_shared_args) + + self.usr_shared_args + + self.coind_cmd ) def cli_cmd(self,*cmds): return ([self.cli_exec] @@ -193,6 +197,10 @@ class TestDaemon(MMGenObject): os.makedirs(self.datadir,exist_ok=True) if self.conf_file: open('{}/{}'.format(self.datadir,self.conf_file),'w').write(self.cfg_file_hdr) + if self.use_pidfile and os.path.exists(self.pidfile): + # Parity just overwrites the data in an existing pidfile, leading to + # interesting consequences. + os.unlink(self.pidfile) ret = self.do_start(silent=silent) if self.wait: self.wait_for_state('ready') @@ -305,4 +313,50 @@ class MoneroTestDaemon(TestDaemon): def stop_cmd(self): return [self.coind_exec] + self.shared_args + ['exit'] +class EthereumTestDaemon(TestDaemon): + + def subclass_init(self): + # defaults: + # linux: $HOME/.local/share/io.parity.ethereum/chains/DevelopmentChain + # win: $LOCALAPPDATA/Parity/Ethereum/chains/DevelopmentChain + self.chaindir = os.path.join(self.datadir,'devchain') + shutil.rmtree(self.chaindir,ignore_errors=True) + + @property + def coind_cmd(self): + return ['daemon',self.pidfile] if self.platform == 'linux' else [] + + @property + def coind_args(self): + return ['--ports-shift={}'.format(self.port_shift), + '--base-path={}'.format(self.chaindir), + '--config=dev', + '--log-file={}'.format(os.path.join(self.datadir,'parity.log')) ] + + @property + def state(self): + from mmgen.rpc import EthereumRPCConnection + try: + conn = EthereumRPCConnection('localhost',self.rpc_port,socket_timeout=0.2) + except: + return 'stopped' + + ret = conn.eth_chainId(on_fail='return') + + return ('stopped','ready')[ret == '0x11'] + + @property + def stop_cmd(self): + return ['kill','-Wf',self.pid] if g.platform == 'win' else ['kill',self.pid] + + @property + def pid(self): # TODO: distinguish between ETH and ETC + if g.platform == 'win': + cp = self.run_cmd(['ps','-Wl'],silent=True,check=False) + for line in cp.stdout.decode().splitlines(): + if 'parity.exe' in line: + return line.split()[3] # use Windows, not Cygwin, PID + else: + return super().pid + TestDaemon.check_implement() diff --git a/test/test_py_d/ts_ethdev.py b/test/test_py_d/ts_ethdev.py index 69e8dc68..7640f29f 100755 --- a/test/test_py_d/ts_ethdev.py +++ b/test/test_py_d/ts_ethdev.py @@ -302,43 +302,11 @@ class TestSuiteEthdev(TestSuiteBase,TestSuiteShared): def setup(self): self.spawn('',msg_only=True) - os.environ['MMGEN_BOGUS_WALLET_DATA'] = '' - opts = ['--ports-shift=4','--config=dev'] - lf_arg = '--log-file=' + joinpath(self.tr.data_dir,'parity.log') if g.platform == 'win': - dc_dir = joinpath(os.environ['LOCALAPPDATA'],'Parity','Ethereum','chains','DevelopmentChain') - shutil.rmtree(dc_dir,ignore_errors=True) m1 = 'Please copy precompiled contract data to {d}/mm1 and {d}/mm2\n'.format(d=self.tmpdir) - m2 = 'Then start parity on another terminal as follows:\n' - m3 = ['parity',lf_arg] + opts - m4 = '\nPress ENTER to continue: ' - my_raw_input(m1 + m2 + ' '.join(m3) + m4) - elif run(['which','parity'],stdout=DEVNULL).returncode == 0: - ss = 'parity.*--log-file=test/data_dir.*/parity.log' # allow for UTF8_DEBUG - try: - pid = run(['pgrep','-af',ss],stdout=PIPE).stdout.split()[0] - os.kill(int(pid),9) - except: pass - # '--base-path' doesn't work together with daemon mode, so we have to clobber the main dev chain - dc_dir = joinpath(os.environ['HOME'],'.local/share/io.parity.ethereum/chains/DevelopmentChain') - shutil.rmtree(dc_dir,ignore_errors=True) - bdir = joinpath(self.tr.data_dir,'parity') - try: os.mkdir(bdir) - except: pass - redir = None if opt.exact_output else PIPE - pidfile = joinpath(self.tmpdir,parity_pid_fn) - run(['parity',lf_arg] + opts + ['daemon',pidfile],stderr=redir,stdout=redir,check=True) - time.sleep(3) # race condition - pid = self.read_from_tmpfile(parity_pid_fn) - elif run('netstat -tnl | grep -q 127.0.0.1:8549',shell=True).returncode == 0: - m1 = 'No parity executable found on system, but port 8549 is active!' - m2 = 'Before continuing, you should probably run the command' - m3 = 'test/test.py -X setup ethdev' - m4 = 'on the remote host.' - sys.stderr.write('{}\n{}\n{} {}\n'.format(m1,m2,cyan(m3),m4)) - confirm_continue() - else: - die(1,'No parity executable found!') + m2 = '\nPress ENTER to continue: ' + my_raw_input(m1+m2) + start_test_daemons(g.coin) return 'ok' def wallet_upgrade(self,src_file): @@ -950,14 +918,5 @@ class TestSuiteEthdev(TestSuiteBase,TestSuiteShared): def stop(self): self.spawn('',msg_only=True) - if g.platform == 'win': - my_raw_input('Please stop parity and Press ENTER to continue: ') - elif run(['which','parity'],stdout=DEVNULL).returncode == 0: - pid = self.read_from_tmpfile(parity_pid_fn) - if opt.no_daemon_stop: - msg_r('(leaving daemon running by user request)') - else: - run(['kill',pid],check=True) - else: - imsg('No parity executable found on system. Ignoring') + stop_test_daemons(g.coin) return 'ok'