Browse Source

cmdtest.py: suppress traceback with expected non-zero exit val

The MMGen Project 1 year ago
parent
commit
cf2e308713

+ 1 - 1
mmgen/data/version

@@ -1 +1 @@
-14.1.dev18
+14.1.dev19

+ 2 - 2
scripts/exec_wrapper.py

@@ -69,8 +69,8 @@ def exec_wrapper_write_traceback(e,exit_val):
 		if 'SystemExit' in exc_line:
 			tb_lines.pop()
 
-		if os.getenv('EXEC_WRAPPER_EXIT_OK'):
-			sys.stdout.write(c.red(exc_line))
+		if os.getenv('EXEC_WRAPPER_EXIT_VAL') == str(exit_val):
+			sys.stdout.write(c.yellow(exc_line) + '\n')
 		else:
 			sys.stdout.write('{}\n{}\n'.format(
 				c.yellow('\n'.join(tb_lines)),

+ 5 - 1
test/cmdtest.py

@@ -616,8 +616,11 @@ class CmdTestRunner:
 			direct_exec     = False,
 			no_passthru_opts = False,
 			spawn_env_override = None,
+			exit_val        = None,
 			env             = {}):
 
+		self.exit_val = exit_val
+
 		desc = self.tg.test_name if cfg.names else self.gm.dpy_data[self.tg.test_name][1]
 		if extra_desc:
 			desc += ' ' + extra_desc
@@ -684,6 +687,7 @@ class CmdTestRunner:
 		spawn_env.update({
 			'MMGEN_HOLD_PROTECT_DISABLE': '' if send_delay else '1',
 			'MMGEN_TEST_SUITE_POPEN_SPAWN': '' if pexpect_spawn else '1',
+			'EXEC_WRAPPER_EXIT_VAL': '' if exit_val is None else str(exit_val),
 		})
 		spawn_env.update(env)
 
@@ -963,7 +967,7 @@ class CmdTestRunner:
 
 	def process_retval(self,cmd,ret):
 		if type(ret).__name__ == 'MMGenPexpect':
-			ret.ok()
+			ret.ok(exit_val=self.exit_val)
 			self.cmd_total += 1
 		elif ret == 'ok':
 			ok()

+ 10 - 13
test/cmdtest_py_d/ct_automount.py

@@ -89,11 +89,11 @@ class CmdTestAutosignAutomount(CmdTestAutosignThreaded, CmdTestRegtestBDBWallet)
 			'mmgen-txcreate',
 			opts
 			+ ['--alice', '--autosign']
-			+ [f'{self.burn_addr},1.23456', f'{sid}:{chg_addr}'])
+			+ [f'{self.burn_addr},1.23456', f'{sid}:{chg_addr}'],
+			exit_val = exit_val or None)
 		if exit_val:
 			t.read()
 			self.remove_device_online()
-			t.req_exit_val = exit_val
 			return t
 		t = self.txcreate_ui_common(
 			t,
@@ -121,15 +121,14 @@ class CmdTestAutosignAutomount(CmdTestAutosignThreaded, CmdTestRegtestBDBWallet)
 		self.insert_device_online()
 		t = self.spawn(
 				'mmgen-txsend',
-				['--quiet', '--abort'])
+				['--quiet', '--abort'],
+				exit_val = 2 if err else 1 if user_exit else None)
 		if err:
 			t.expect('No unsent transactions')
-			t.req_exit_val = 2
 		else:
 			t.expect('(y/N): ', 'n' if user_exit else 'y')
 			if user_exit:
 				t.expect('Exiting at user request')
-				t.req_exit_val = 1
 			else:
 				for pat in del_expect:
 					t.expect(pat, regex=True)
@@ -191,11 +190,10 @@ class CmdTestAutosignAutomount(CmdTestAutosignThreaded, CmdTestRegtestBDBWallet)
 		self.insert_device_online()
 		t = self.spawn(
 				'mmgen-txsend',
-				['--alice', '--autosign', '--status', '--verbose'])
+				['--alice', '--autosign', '--status', '--verbose'],
+				exit_val = exit_val)
 		t.expect(expect)
 		self.remove_device_online()
-		if exit_val:
-			t.req_exit_val = exit_val
 		return t
 
 	def alice_txstatus1(self):
@@ -209,7 +207,7 @@ class CmdTestAutosignAutomount(CmdTestAutosignThreaded, CmdTestRegtestBDBWallet)
 		return self._alice_txstatus('in mempool')
 
 	def alice_txstatus4(self):
-		return self._alice_txstatus('1 confirmation')
+		return self._alice_txstatus('1 confirmation', 0)
 
 	def _alice_txsend(self, comment=None, no_wait=False):
 		if not no_wait:
@@ -226,10 +224,9 @@ class CmdTestAutosignAutomount(CmdTestAutosignThreaded, CmdTestRegtestBDBWallet)
 
 	def alice_txsend_bad_no_unsent(self):
 		self.insert_device_online()
-		t = self.spawn('mmgen-txsend', ['--quiet', '--autosign'])
+		t = self.spawn('mmgen-txsend', ['--quiet', '--autosign'], exit_val=2)
 		t.expect('No unsent transactions')
 		t.read()
-		t.req_exit_val = 2
 		self.remove_device_online()
 		return t
 
@@ -239,11 +236,11 @@ class CmdTestAutosignAutomount(CmdTestAutosignThreaded, CmdTestRegtestBDBWallet)
 		self.insert_device_online()
 		t = self.spawn(
 				'mmgen-txbump',
-				['--alice', '--autosign'])
+				['--alice', '--autosign'],
+				exit_val = 1 if bad_tx_desc else None)
 		if bad_tx_desc:
 			t.expect('Only sent transactions')
 			t.expect(bad_tx_desc)
-			t.req_exit_val = 1
 		else:
 			t.expect(f'to deduct the fee from .* change output\): ', '\n', regex=True)
 			t.expect(r'(Y/n): ', 'y')  # output OK?

+ 7 - 6
test/cmdtest_py_d/ct_autosign.py

@@ -636,14 +636,17 @@ class CmdTestAutosign(CmdTestAutosignBase):
 		tx_desc = Signable.transaction.desc
 		t = self.spawn(
 				'mmgen-autosign',
-				self.opts + args)
+				self.opts + args,
+				exit_val = 1
+					if 'wait' in args
+					or self.bad_tx_count
+					or (have_msg and self.bad_msg_count) else None)
 		t.expect(
 			f'{self.tx_count} {tx_desc}{suf(self.tx_count)} signed' if self.tx_count else
 			f'No unsigned {tx_desc}s')
 
 		if self.bad_tx_count:
 			t.expect(f'{self.bad_tx_count} {tx_desc}{suf(self.bad_tx_count)} failed to sign')
-			t.req_exit_val = 1
 
 		if have_msg:
 			t.expect(
@@ -655,13 +658,11 @@ class CmdTestAutosign(CmdTestAutosignBase):
 				t.expect(
 					f'{self.bad_msg_count} message file{suf(self.bad_msg_count)}{{0,1}} failed to sign',
 					regex = True)
-				t.req_exit_val = 1
 
 		if 'wait' in args:
 			t.expect('Waiting')
 			imsg(purple('\nKilling wait loop!'))
 			t.kill(2)
-			t.req_exit_val = 1
 		else:
 			t.read()
 
@@ -805,7 +806,8 @@ class CmdTestAutosignLive(CmdTestAutosignBTC):
 		t = self.spawn(
 			'mmgen-autosign',
 			self.opts + (led_opts or []) + ['--quiet', '--no-summary', 'wait'],
-			no_msg = True)
+			no_msg   = True,
+			exit_val = 1)
 
 		if not cfg.exact_output:
 			omsg('')
@@ -818,7 +820,6 @@ class CmdTestAutosignLive(CmdTestAutosignBTC):
 
 		imsg(purple('\nKilling wait loop!'))
 		t.kill(2) # 2 = SIGINT
-		t.req_exit_val = 1
 
 		if self.simulate_led and led_opts:
 			t.expect('Stopping LED')

+ 7 - 9
test/cmdtest_py_d/ct_cfgfile.py

@@ -44,13 +44,14 @@ class CmdTestCfgFile(CmdTestBase):
 		CmdTestBase.__init__(self,trunner,cfgs,spawn)
 		self.spawn_env['MMGEN_TEST_SUITE_CFGTEST'] = '1'
 
-	def spawn_test(self,args=[],extra_desc='',pexpect_spawn=None):
+	def spawn_test(self,args=[],extra_desc='',pexpect_spawn=None, exit_val=None):
 		return self.spawn(
 			'test/misc/cfg.py',
 			[f'--data-dir={self.path("data_dir")}'] + args,
 			cmd_dir       = '.',
 			extra_desc    = extra_desc,
-			pexpect_spawn = pexpect_spawn)
+			pexpect_spawn = pexpect_spawn,
+			exit_val      = exit_val)
 
 	def path(self,id_str):
 		return {
@@ -111,7 +112,7 @@ class CmdTestCfgFile(CmdTestBase):
 		chk = cfg_file_sample.cls_make_metadata(d)
 		write_to_file(self.path('sample'),'\n'.join(d+chk) + '\n')
 
-		t = self.spawn_test(args=args,pexpect_spawn=pexpect_spawn)
+		t = self.spawn_test(args=args, pexpect_spawn=pexpect_spawn, exit_val=1 if old_set else None)
 
 		t.expect('options have changed')
 		for s in ('have been added','monero_','have been removed','zcash_','foo','bar'):
@@ -136,7 +137,6 @@ class CmdTestCfgFile(CmdTestBase):
 
 		if old_set:
 			t.expect('unrecognized option')
-			t.req_exit_val = 1
 
 		if args == ['parse_test']:
 			t.expect('parsed chunks: 29')
@@ -159,10 +159,10 @@ class CmdTestCfgFile(CmdTestBase):
 			old_set       = True,
 			pexpect_spawn = not sys.platform == 'win32')
 
-	def _autoset_opts(self,args=[],text='rpc_backend aiohttp\n'):
+	def _autoset_opts(self, args=[], text='rpc_backend aiohttp\n', exit_val=None):
 		write_to_file( self.path('usr'), text )
 		imsg(yellow(f'Wrote cfg file:\n  {text}'))
-		return self.spawn_test(args=args)
+		return self.spawn_test(args=args, exit_val=exit_val)
 
 	def autoset_opts(self):
 		return self._autoset_opts(args=['autoset_opts'])
@@ -171,9 +171,7 @@ class CmdTestCfgFile(CmdTestBase):
 		return self._autoset_opts(args=['--rpc-backend=curl','autoset_opts_cmdline'])
 
 	def _autoset_opts_bad(self,kwargs):
-		t = self._autoset_opts(**kwargs)
-		t.req_exit_val = 1
-		return t
+		return self._autoset_opts(exit_val=1, **kwargs)
 
 	def autoset_opts_bad(self):
 		return self._autoset_opts_bad({'text':'rpc_backend foo\n'})

+ 9 - 8
test/cmdtest_py_d/ct_ethdev.py

@@ -601,10 +601,11 @@ class CmdTestEthdev(CmdTestBase,CmdTestShared):
 			ext       = '21-23]{}.regtest.addrs',
 			expect    = '9/9',
 			add_args  = [],
-			bad_input = False):
+			bad_input = False,
+			exit_val  = None):
 		ext = ext.format('-α' if cfg.debug_utf8 else '')
 		fn = self.get_file_with_ext(ext,no_dot=True,delete=False)
-		t = self.spawn('mmgen-addrimport', self.eth_args[1:-1] + add_args + [fn])
+		t = self.spawn('mmgen-addrimport', self.eth_args[1:-1] + add_args + [fn], exit_val=exit_val)
 		if bad_input:
 			return t
 		t.expect('Importing')
@@ -750,11 +751,11 @@ class CmdTestEthdev(CmdTestBase,CmdTestShared):
 		txfile = self.get_file_with_ext(ext,no_dot=True)
 		t = self.spawn(
 				'mmgen-txsend',
-				self.eth_args + add_args + ['--status', txfile])
+				self.eth_args + add_args + ['--status', txfile],
+				exit_val = exit_val)
 		t.expect(expect_str)
 		if expect_str2:
 			t.expect(expect_str2)
-		t.req_exit_val = exit_val
 		return t
 
 	def tx_status1(self):
@@ -1119,18 +1120,18 @@ class CmdTestEthdev(CmdTestBase,CmdTestShared):
 		t = self.addrimport(
 			ext       = '[11-13]{}.regtest.addrs',
 			add_args  = ['--token=abc'],
-			bad_input = True)
+			bad_input = True,
+			exit_val  = 2)
 		t.expect('could not be resolved')
-		t.req_exit_val = 2
 		return t
 
 	def token_addrimport_badaddr2(self):
 		t = self.addrimport(
 			ext       = '[11-13]{}.regtest.addrs',
 			add_args  = ['--token='+'00deadbeef'*4],
-			bad_input = True)
+			bad_input = True,
+			exit_val  = 2)
 		t.expect('could not be resolved')
-		t.req_exit_val = 2
 		return t
 
 	def token_addrimport(self,addr_file,addr_range,expect,extra_args=[]):

+ 6 - 8
test/cmdtest_py_d/ct_main.py

@@ -781,7 +781,8 @@ class CmdTestMain(CmdTestBase,CmdTestShared):
 			'mmgen-addrgen',
 			(['-S'] if stdout else []) +
 			self.segwit_arg +
-			['-i' + in_fmt, '-d', self.tmpdir, wf, self.addr_idx_list])
+			['-i' + in_fmt, '-d', self.tmpdir, wf, self.addr_idx_list],
+			exit_val = None if stdout else 1)
 		t.license()
 		t.expect_getend(f'Valid {wcls.desc} for Seed ID ')
 		vmsg('Comparing generated checksum with checksum from previous address file')
@@ -790,7 +791,6 @@ class CmdTestMain(CmdTestBase,CmdTestShared):
 			t.expect_getend(r'Checksum for address data .*?: ',regex=True) )
 		if not stdout:
 			t.no_overwrite()
-			t.req_exit_val = 1
 		return t
 
 	def addrgen_hex(self,wf,_,in_fmt='mmhex'):
@@ -806,7 +806,8 @@ class CmdTestMain(CmdTestBase,CmdTestShared):
 			+ self.segwit_arg
 			+ ['-i'+in_fmt, '-d', self.tmpdir]
 			+ ([wf] if wf else [])
-			+ [self.addr_idx_list])
+			+ [self.addr_idx_list],
+			exit_val = 1)
 		t.license()
 		t.expect_getend('Incog Wallet ID: ')
 		wcls = get_wallet_cls(fmt_code=in_fmt)
@@ -816,7 +817,6 @@ class CmdTestMain(CmdTestBase,CmdTestShared):
 		chk = t.expect_getend(r'Checksum for address data .*?: ',regex=True)
 		verify_checksum_or_exit(self._get_addrfile_checksum(),chk)
 		t.no_overwrite()
-		t.req_exit_val = 1
 		return t
 
 	def addrgen_incog_hex(self,wf,_):
@@ -973,7 +973,8 @@ class CmdTestMain(CmdTestBase,CmdTestShared):
 		non_mm_file = joinpath(self.tmpdir,non_mmgen_fn)
 		t = self.spawn(
 			'mmgen-txsign',
-			add_args + ['-d', self.tmpdir, '-k', non_mm_file, txf, wf])
+			add_args + ['-d', self.tmpdir, '-k', non_mm_file, txf, wf],
+			exit_val = 2 if bad_vsize else None)
 		t.license()
 		t.view_tx('n')
 		wcls = get_wallet_cls(ext=get_extension(wf))
@@ -981,12 +982,9 @@ class CmdTestMain(CmdTestBase,CmdTestShared):
 		if bad_vsize:
 			t.expect('Estimated transaction vsize')
 			t.expect('1 transaction could not be signed')
-			exit_val = 2
 		else:
 			t.do_comment(False)
 			t.expect('Save signed transaction? (Y/n): ','y')
-			exit_val = 0
-		t.req_exit_val = exit_val
 		return t
 
 	def walletgen6(self,del_dw_run='dummy'):

+ 1 - 2
test/cmdtest_py_d/ct_misc.py

@@ -168,9 +168,8 @@ class CmdTestHelp(CmdTestBase):
 	)
 
 	def usage(self):
-		t = self.spawn('mmgen-walletgen', ['foo'])
+		t = self.spawn('mmgen-walletgen', ['foo'], exit_val=1)
 		t.expect('USAGE: mmgen-walletgen')
-		t.req_exit_val = 1
 		return t
 
 	def version(self):

+ 3 - 4
test/cmdtest_py_d/ct_opts.py

@@ -29,8 +29,8 @@ class CmdTestOpts(CmdTestBase):
 		('opt_bad_autoset',      (41,"invalid autoset value", [])),
 	)
 
-	def spawn_prog(self,args):
-		return self.spawn('test/misc/opts.py',args,cmd_dir='.')
+	def spawn_prog(self, args, exit_val=None):
+		return self.spawn('test/misc/opts.py', args, cmd_dir='.', exit_val=exit_val)
 
 	def check_vals(self,args,vals):
 		t = self.spawn_prog(args)
@@ -39,9 +39,8 @@ class CmdTestOpts(CmdTestBase):
 		return t
 
 	def do_run(self,args,expect,exit_val,regex=False):
-		t = self.spawn_prog(args)
+		t = self.spawn_prog(args, exit_val=exit_val or None)
 		t.expect(expect,regex=regex)
-		t.req_exit_val = exit_val
 		return t
 
 	def opt_helpscreen(self):

+ 25 - 20
test/cmdtest_py_d/ct_regtest.py

@@ -711,13 +711,11 @@ class CmdTestRegtest(CmdTestBase,CmdTestShared):
 		return self.fund_wallet('alice',('L','S')[self.proto.cap('segwit')],rtFundAmt)
 
 	def user_twview(self, user, chk=None, expect=None, cmdline=['twview'], sort='age', exit_val=None):
-		t = self.spawn('mmgen-tool',[f'--{user}'] + cmdline + ['sort='+sort])
+		t = self.spawn('mmgen-tool',[f'--{user}'] + cmdline + ['sort='+sort], exit_val=exit_val)
 		if chk:
 			t.expect(r'{}\b.*\D{}\b'.format(*chk),regex=True)
 		if expect:
 			t.expect(expect)
-		if exit_val:
-			t.req_exit_val = exit_val
 		return t
 
 	def bob_twview_noaddrs(self):
@@ -949,10 +947,12 @@ class CmdTestRegtest(CmdTestBase,CmdTestShared):
 			tx_file,
 			exp1       = '',
 			exp2       = '',
-			extra_args = []):
+			extra_args = [],
+			exit_val   = None):
 		t = self.spawn(
 				'mmgen-txsend',
-				['-d', self.tmpdir, '--'+user, '--status'] + extra_args + [tx_file])
+				['-d', self.tmpdir, '--'+user, '--status'] + extra_args + [tx_file],
+				exit_val = exit_val)
 		if exp1:
 			t.expect(exp1,regex=True)
 		if exp2:
@@ -977,7 +977,8 @@ class CmdTestRegtest(CmdTestBase,CmdTestShared):
 			return_after_send  = False,
 			menu               = ['M'],
 			skip_passphrase    = False,
-			used_chg_addr_resp = None):
+			used_chg_addr_resp = None,
+			exit_val           = None):
 
 		t = self.spawn(
 			'mmgen-txdo',
@@ -985,7 +986,8 @@ class CmdTestRegtest(CmdTestBase,CmdTestShared):
 			+ (['--fee='+fee] if fee else [])
 			+ extra_args
 			+ ([],[wf])[bool(wf)]
-			+ outputs_cl)
+			+ outputs_cl,
+			exit_val = exit_val)
 
 		self.txcreate_ui_common(
 				t,
@@ -1132,12 +1134,12 @@ class CmdTestRegtest(CmdTestBase,CmdTestShared):
 		self.write_to_tmpfile('rbf_txid',mp[0]+'\n')
 		return 'ok'
 
-	def bob_rbf_status(self, fee, exp1, exp2=''):
+	def bob_rbf_status(self, fee, exp1, exp2='', exit_val=None):
 		if not self.proto.cap('rbf'):
 			return 'skip'
 		ext = ',{}]{x}.regtest.sigtx'.format(fee[:-1],x='-α' if cfg.debug_utf8 else '')
 		txfile = self.get_file_with_ext(ext,delete=False,no_dot=True)
-		return self.user_txsend_status('bob', txfile, exp1, exp2)
+		return self.user_txsend_status('bob', txfile, exp1, exp2, exit_val=exit_val)
 
 	def bob_rbf_status1(self):
 		if not self.proto.cap('rbf'):
@@ -1163,7 +1165,8 @@ class CmdTestRegtest(CmdTestBase,CmdTestShared):
 		return self.bob_rbf_status(
 			rtFee[1],
 			'Transaction has been replaced',
-			f'{new_txid} in mempool')
+			f'{new_txid} in mempool',
+			exit_val = 0)
 
 	def bob_rbf_status3(self):
 		if not self.proto.cap('rbf'):
@@ -1514,8 +1517,8 @@ class CmdTestRegtest(CmdTestBase,CmdTestShared):
 				extra_args        = [f'--locktime={locktime}'],
 				return_early      = return_early,
 				add_comment       = False,
-				return_after_send = True)
-		t.req_exit_val = exit_val
+				return_after_send = True,
+				exit_val          = exit_val)
 		if expect:
 			t.expect(expect)
 		return t
@@ -1556,9 +1559,9 @@ class CmdTestRegtest(CmdTestBase,CmdTestShared):
 			return 'skip'
 		t = self.spawn(
 				'mmgen-tool',
-				['--alice','add_label',addr,'(none)'])
+				['--alice','add_label',addr,'(none)'],
+				exit_val = exit_val)
 		t.expect(reply,regex=True)
-		t.req_exit_val = exit_val
 		return t
 
 	def alice_add_comment_badaddr1(self):
@@ -1809,19 +1812,21 @@ class CmdTestRegtest(CmdTestBase,CmdTestShared):
 			addr     = None,
 			ext      = 'sigmsg.json',
 			cmd      = 'verify',
-			msgfile  = None):
+			msgfile  = None,
+			exit_val = None):
 		return self.spawn(
 			'mmgen-msg', [
 				'--bob',
 				f'--outdir={self.tmpdir}',
 				cmd,
 				msgfile or get_file_with_ext(self.tmpdir,ext),
-			] + ([addr] if addr else []) )
+			]
+			+ ([addr] if addr else []),
+			exit_val = exit_val)
 
 	def bob_msgverify_raw(self):
-		t = self.bob_msgverify(ext='rawmsg.json')
+		t = self.bob_msgverify(ext='rawmsg.json', exit_val=1)
 		t.expect('No signatures')
-		t.req_exit_val = 1
 		return t
 
 	def bob_msgverify_single(self):
@@ -1947,8 +1952,8 @@ class CmdTestRegtest(CmdTestBase,CmdTestShared):
 	def _usr_auto_chg_bad(self,user,al_id,expect):
 		t = self.spawn(
 			'mmgen-txcreate',
-			['-d', self.tr.trash_dir, '-B', f'--{user}', self.burn_addr+', 0.01', al_id])
-		t.req_exit_val = 2
+			['-d', self.tr.trash_dir, '-B', f'--{user}', self.burn_addr+', 0.01', al_id],
+			exit_val = 2)
 		t.expect(expect)
 		return t
 

+ 6 - 6
test/cmdtest_py_d/ct_seedsplit.py

@@ -132,7 +132,7 @@ class CmdTestSeedSplit(CmdTestBase):
 			in_exts,
 			add_args = [],
 			sid      = None,
-			bad_invocation = False,
+			exit_val = None,
 			master   = None,
 			id_str   = None):
 		td = self.get_tmp_subdir(tdir)
@@ -145,8 +145,9 @@ class CmdTestSeedSplit(CmdTestBase):
 				+ ([f'--id-str={id_str}'] if id_str else [])
 				+ ['-d',td,'-o',ofmt]
 				+ (['--label','Joined Wallet Label','-r0'] if ofmt == 'w' else [])
-				+ shares)
-		if bad_invocation:
+				+ shares,
+				exit_val = exit_val)
+		if exit_val:
 			return t
 		icls = ( dfl_wcls if 'mmdat' in in_exts
 			else get_wallet_cls('incog') if 'mmincog' in in_exts
@@ -241,8 +242,7 @@ class CmdTestSeedSplit(CmdTestBase):
 							['-H',self.get_hincog_arg(self.tdir2,'-master7')],master=7,id_str='φυβαρ')
 
 	def ss_bad_invocation(self,cmd,args,exit_val,errmsg):
-		t = self.spawn(cmd,args)
-		t.req_exit_val = exit_val
+		t = self.spawn(cmd, args, exit_val=exit_val)
 		t.expect(errmsg)
 		return t
 
@@ -250,7 +250,7 @@ class CmdTestSeedSplit(CmdTestBase):
 		t = self.ss_join('3way_dfl','mmhex',
 			['mmwords','mmincox','bip39'],
 			id_str   = 'foo',
-			bad_invocation = True)
+			exit_val = 1)
 		t.expect('option meaningless')
 		return t
 

+ 3 - 3
test/cmdtest_py_d/ct_shared.py

@@ -195,13 +195,14 @@ class CmdTestShared:
 			view       = 'n',
 			dfl_wallet = False):
 		opts = extra_opts + ['-d',self.tmpdir,txfile] + ([wf] if wf else [])
+		wcls = get_wallet_cls(ext = 'mmdat' if dfl_wallet else get_extension(wf))
 		t = self.spawn(
 			'mmgen-txsign',
 			opts,
-			extra_desc)
+			extra_desc,
+			exit_val = None if save or (wcls.enc and wcls.type != 'brain') else 1)
 		t.license()
 		t.view_tx(view)
-		wcls = get_wallet_cls( ext = 'mmdat' if dfl_wallet else get_extension(wf) )
 		if wcls.enc and wcls.type != 'brain':
 			t.passphrase(wcls.desc,self.wpasswd)
 		if save:
@@ -210,7 +211,6 @@ class CmdTestShared:
 			t.do_comment(False,has_label=has_label)
 			t.expect('Save signed transaction? (Y/n): ','n')
 			t.expect('not saved')
-			t.req_exit_val = 1
 		return t
 
 	def ref_brain_chk(self,bw_file=ref_bw_file):

+ 2 - 2
test/cmdtest_py_d/ct_tool.py

@@ -95,9 +95,9 @@ class CmdTestTool(CmdTestMain,CmdTestBase):
 		t = self.spawn(
 			'mmgen-tool',
 			['twview'],
-			env = { 'MMGEN_BOGUS_UNSPENT_DATA': joinpath(ref_dir,'bad-comment-unspent.json') })
+			env = { 'MMGEN_BOGUS_UNSPENT_DATA': joinpath(ref_dir,'bad-comment-unspent.json') },
+			exit_val = 2)
 		t.expect('cannot be converted to TwComment')
-		t.req_exit_val = 2
 		return t
 
 	def _decrypt_keystore(self,cmd,fn,pw,chk):

+ 1 - 3
test/cmdtest_py_d/ct_xmr_autosign.py

@@ -258,14 +258,12 @@ class CmdTestXMRAutosign(CmdTestXMRWallet,CmdTestAutosignThreaded):
 
 	def _abort_tx(self,expect,send=None,exit_val=None):
 		self.insert_device_online()
-		t = self.spawn('mmgen-xmrwallet', ['--autosign', 'abort'])
+		t = self.spawn('mmgen-xmrwallet', ['--autosign', 'abort'], exit_val=exit_val)
 		t.expect(expect)
 		if send:
 			t.send(send)
 		t.read() # required!
 		self.remove_device_online()
-		if exit_val:
-			t.req_exit_val = exit_val
 		return t
 
 	def abort_tx1(self):

+ 2 - 3
test/include/pexpect.py

@@ -51,7 +51,6 @@ class MMGenPexpect:
 
 		self.pexpect_spawn = pexpect_spawn
 		self.send_delay = send_delay
-		self.req_exit_val = 0
 		self.skip_ok = False
 		self.sent_value = None
 		self.spawn_env = spawn_env
@@ -91,12 +90,12 @@ class MMGenPexpect:
 		if add_comment:
 			self.expect('Comment: ',add_comment+'\n')
 
-	def ok(self):
+	def ok(self, exit_val=None):
 		if not self.pexpect_spawn:
 			self.p.sendeof()
 		self.p.read()
 		ret = self.p.wait()
-		if ret != self.req_exit_val and not cfg.coverage:
+		if ret != (exit_val or 0) and not cfg.coverage:
 			die( 'TestSuiteSpawnedScriptException', f'Spawned script exited with value {ret}' )
 		if cfg.profile:
 			return