Browse Source

unit_tests.py: cleanups, add `desc` param to subtest methods

The MMGen Project 6 months ago
parent
commit
3f1c0b2a93

+ 12 - 1
test/unit_tests.py

@@ -158,9 +158,20 @@ def run_test(test,subtest=None):
 			getattr(t,'_pre_subtest')(test,subtest,UnitTestHelpers(subtest))
 
 		try:
-			ret = getattr(t,subtest.replace('-','_'))(test,UnitTestHelpers(subtest))
+			func = getattr(t,subtest.replace('-','_'))
+			c = func.__code__
+			do_desc = c.co_varnames[c.co_argcount-1] == 'desc'
+			if do_desc:
+				if cfg.verbose:
+					msg(f'Testing {func.__defaults__[0]}')
+				elif not cfg.quiet:
+					msg_r(f'Testing {func.__defaults__[0]}...')
+
+			ret = func(test, UnitTestHelpers(subtest))
 			if type(ret).__name__ == 'coroutine':
 				ret = asyncio.run(ret)
+			if do_desc and not cfg.quiet:
+				msg('OK\n' if cfg.verbose else 'OK')
 		except:
 			if getattr(t,'silence_output',False):
 				t._end_silence()

+ 70 - 64
test/unit_tests_d/ut_lockable.py

@@ -4,16 +4,12 @@
 test.unit_tests_d.ut_lockable: unit test for the MMGen suite's Lockable class
 """
 
-from ..include.common import qmsg,qmsg_r,vmsg
+from decimal import Decimal
+from mmgen.base_obj import AttrCtrl, Lockable
 
-class unit_test:
+class unit_tests:
 
-	def run_test(self,name,ut):
-
-		from mmgen.base_obj import AttrCtrl,Lockable
-		from decimal import Decimal
-
-		qmsg_r('Testing class AttrCtrl...')
+	def attrctl(self, name, ut, desc='AttrCtrl class'):
 
 		class MyAttrCtrl(AttrCtrl):
 			_autolock = False
@@ -52,8 +48,21 @@ class unit_test:
 		assert acdn.bar is None, f'{acdn.bar}'
 		assert acdn.baz is None, f'{acdn.baz}'
 
-		qmsg('OK')
-		qmsg_r('Testing class Lockable...')
+		def bad1(): ac.x = 1
+		def bad2(): acc.foo = 1
+		def bad3(): aca._lock()
+		def bad4(): acdn.baz = None
+
+		ut.process_bad_data((
+			('attr',            'AttributeError', 'has no attr', bad1),
+			('attr type',       'AttributeError', 'type',        bad2),
+			('call to _lock()', 'AssertionError', 'only once',   bad3),
+			('attr',            'AttributeError', 'has no attr', bad4),
+		))
+
+		return True
+
+	def base(self, name, ut, desc='Lockable class'):
 
 		class MyLockable(Lockable): # class without attrs
 			_autolock = False
@@ -83,6 +92,32 @@ class unit_test:
 
 		lc.epsilon = [0]
 
+		def bad1(): lc.foo = 'fooval3'
+		def bad2(): lc.baz = 'str'
+		def bad3(): lc.qux  = 2
+		def bad4(): lc.x = 1
+		def bad5(): lc.alpha = 0
+		def bad6(): lc.beta = False
+		def bad7(): lc.gamma = Decimal('0')
+		def bad8(): lc.delta = float(0)
+		def bad9(): lc.epsilon = [0]
+
+		ut.process_bad_data((
+			('attr (can’t reset)', 'AttributeError', 'reset',       bad1),
+			('attr type (2)',      'AttributeError', 'type',        bad2),
+			('attr (can’t set)',   'AttributeError', 'read-only',   bad3),
+			('attr (2)',           'AttributeError', 'has no attr', bad4),
+			('attr (can’t reset)', 'AttributeError', 'reset',       bad5),
+			('attr (can’t reset)', 'AttributeError', 'reset',       bad6),
+			('attr (can’t reset)', 'AttributeError', 'reset',       bad7),
+			('attr (can’t reset)', 'AttributeError', 'reset',       bad8),
+			('attr (can’t reset)', 'AttributeError', 'reset',       bad9),
+		))
+
+		return True
+
+	def classattr(self, name, ut, desc='Lockable class with class attrs'):
+
 		class MyLockableClsCheck(Lockable): # class with attrs
 			_autolock = False
 			_use_class_attr = True
@@ -102,8 +137,21 @@ class unit_test:
 		lcc.baz = 3.2
 		lcc.baz = 3.1 # baz is in both lists
 
-		qmsg('OK')
-		qmsg_r('Testing class Lockable with autolock...')
+		def bad1(): lcc.bar = 'str'
+		def bad2(): lcc.qux = 'quxval2'
+		def bad3(): lcc.foo = 'fooval3'
+		def bad4(): lcc.x = 1
+
+		ut.process_bad_data((
+			('attr type (3)',      'AttributeError', 'type',        bad1),
+			('attr (can’t set)',   'AttributeError', 'read-only',   bad2),
+			('attr (can’t reset)', 'AttributeError', 'reset',       bad3),
+			('attr (3)',           'AttributeError', 'has no attr', bad4),
+		))
+
+		return True
+
+	def autolock(self, name, ut, desc='Lockable class with autolock'):
 
 		class MyLockableAutolock(Lockable):
 			def __init__(self):
@@ -126,60 +174,18 @@ class unit_test:
 			_set_ok = ('foo','bar')
 			foo = 1
 
-		qmsg('OK')
-		qmsg_r('Checking error handling...')
-		vmsg('')
-
-		def bad1(): ac.x = 1
-		def bad2(): acc.foo = 1
-		def bad3(): lc.foo = 'fooval3'
-		def bad4(): lc.baz = 'str'
-		def bad5(): lcc.bar = 'str'
-		def bad6(): lc.qux  = 2
-		def bad7(): lcc.qux = 'quxval2'
-		def bad8(): lcc.foo = 'fooval3'
-		def bad9(): lc.x = 1
-		def bad10(): lcc.x = 1
-
-		def bad11(): lc.alpha = 0
-		def bad12(): lc.beta = False
-		def bad13(): lc.gamma = Decimal('0')
-		def bad14(): lc.delta = float(0)
-		def bad15(): lc.epsilon = [0]
-
-		def bad16(): lca.foo = None
-		def bad17(): MyLockableBad()
-		def bad18(): aca._lock()
-
-		def bad19(): acdn.baz = None
-		def bad20(): lcdn.foo = 1
-		def bad21(): lcdn.bar = None
-		def bad22(): del lcdn.foo
+		def bad1(): lca.foo = None
+		def bad2(): MyLockableBad()
+		def bad3(): lcdn.foo = 1
+		def bad4(): lcdn.bar = None
+		def bad5(): del lcdn.foo
 
 		ut.process_bad_data((
-			('attr (1)',           'AttributeError', 'has no attr', bad1 ),
-			('attr (2)',           'AttributeError', 'has no attr', bad9 ),
-			('attr (3)',           'AttributeError', 'has no attr', bad10 ),
-			('attr (4)',           'AttributeError', 'has no attr', bad19 ),
-			('attr (5)',           'AttributeError', 'has no attr', bad21 ),
-			('attr type (1)',      'AttributeError', 'type',        bad2 ),
-			("attr type (2)",      'AttributeError', 'type',        bad4 ),
-			("attr type (3)",      'AttributeError', 'type',        bad5 ),
-			("attr (can't set)",   'AttributeError', 'read-only',   bad6 ),
-			("attr (can't set)",   'AttributeError', 'read-only',   bad7 ),
-			("attr (can't set)",   'AttributeError', 'read-only',   bad20 ),
-			("attr (can't reset)", 'AttributeError', 'reset',       bad3 ),
-			("attr (can't reset)", 'AttributeError', 'reset',       bad8 ),
-			("attr (can't reset)", 'AttributeError', 'reset',       bad11 ),
-			("attr (can't reset)", 'AttributeError', 'reset',       bad12 ),
-			("attr (can't reset)", 'AttributeError', 'reset',       bad13 ),
-			("attr (can't reset)", 'AttributeError', 'reset',       bad14 ),
-			("attr (can't reset)", 'AttributeError', 'reset',       bad15 ),
-			("attr (can't set)",   'AttributeError', 'read-only',   bad16 ),
-			("attr (bad _set_ok)", 'AssertionError', 'not found in',bad17 ),
-			("attr (can’t delete)",'AttributeError', 'not be delet',bad22 ),
-			("call to _lock()",    'AssertionError', 'only once',   bad18 ),
+			('attr (can’t set)',    'AttributeError', 'read-only',    bad1),
+			('attr (bad _set_ok)',  'AssertionError', 'not found in', bad2),
+			('attr (can’t set)',    'AttributeError', 'read-only',    bad3),
+			('attr (5)',            'AttributeError', 'has no attr',  bad4),
+			('attr (can’t delete)', 'AttributeError', 'not be delet', bad5),
 		))
 
-		qmsg('OK')
 		return True

+ 3 - 3
test/unit_tests_d/ut_misc.py

@@ -12,7 +12,7 @@ from ..include.common import vmsg
 
 class unit_tests:
 
-	def format_elapsed_hr(self, name, ut):
+	def format_elapsed_hr(self, name, ut, desc='function util.format_elapsed_hr()'):
 		from mmgen.util2 import format_elapsed_hr
 
 		vectors = (
@@ -63,7 +63,7 @@ class unit_tests:
 
 		return True
 
-	def xmrwallet_uarg_info(self,name,ut): # WIP
+	def xmrwallet_uarg_info(self, name, ut, desc='dict xmrwallet.xmrwallet_uarg_info'): # WIP
 		from mmgen.xmrwallet import xmrwallet_uarg_info as uarg_info
 		vs = namedtuple('vector_data', ['text', 'groups'])
 		fs = '{:16} {}'
@@ -95,7 +95,7 @@ class unit_tests:
 
 		return True
 
-	def pyversion(self,name,ut):
+	def pyversion(self, name, ut, desc='class pyversion.PythonVersion'):
 		from mmgen.pyversion import PythonVersion,python_version
 
 		ver = {}

+ 7 - 16
test/unit_tests_d/ut_mn_entry.py

@@ -4,10 +4,10 @@
 test.unit_tests_d.ut_mn_entry: Mnemonic user entry unit test for the MMGen suite
 """
 
-from mmgen.util import msg,msg_r
-from ..include.common import cfg,qmsg
+from mmgen.mn_entry import mn_entry
+from ..include.common import cfg, vmsg
 
-class unit_test:
+class unit_tests:
 
 	vectors = {
 		'mmgen':   {
@@ -40,26 +40,19 @@ class unit_test:
 		'bip39':   { 'usl': 4, 'sw': 3, 'lw': 8 },
 	}
 
-	def run_test(self,name,ut):
-
-		msg_r('Testing MnemonicEntry methods...')
-
-		from mmgen.mn_entry import mn_entry
-
-		msg_r('\nTesting computed wordlist constants...')
+	def wl(self, name, ut, desc='MnemonicEntry - computed wordlist constants'):
 		for wl_id in self.vectors:
 			for j,k in (('uniq_ss_len','usl'),('shortest_word','sw'),('longest_word','lw')):
 				a = getattr(mn_entry( cfg, wl_id ),j)
 				b = self.vectors[wl_id][k]
 				assert a == b, f'{wl_id}:{j} {a} != {b}'
-		msg('OK')
+		return True
 
-		msg_r('Testing idx()...')
-		qmsg('')
+	def idx(self, name, ut, desc='MnemonicEntry - idx()'):
 		junk = 'a g z aa gg zz aaa ggg zzz aaaa gggg zzzz aaaaaaaaaaaaaa gggggggggggggg zzzzzzzzzzzzzz'
 		for wl_id in self.vectors:
 			m = mn_entry( cfg, wl_id )
-			qmsg('Wordlist: '+wl_id)
+			vmsg('Wordlist: '+wl_id)
 			for entry_mode in ('full','short'):
 				for a,word in enumerate(m.wl):
 					b = m.idx(word,entry_mode)
@@ -78,6 +71,4 @@ class unit_test:
 						assert type(b) is tuple, (type(b),tuple)
 					elif type(chk) is int:
 						assert b == chk, (b,chk)
-		msg('OK')
-
 		return True

+ 8 - 10
test/unit_tests_d/ut_msg.py

@@ -6,7 +6,7 @@ test.unit_tests_d.ut_msg: message signing unit tests for the MMGen suite
 
 import os
 
-from mmgen.util import msg,bmsg,pumsg,suf
+from mmgen.util import msg, pumsg, suf
 from mmgen.protocol import CoinProtocol
 from mmgen.msg import NewMsg,UnsignedMsg,SignedMsg,SignedOnlineMsg,ExportedMsgSigs
 from mmgen.addr import MMGenID
@@ -41,8 +41,6 @@ async def run_test(network_id,chksum,msghash_type='raw'):
 	if not cfg.verbose:
 		silence()
 
-	bmsg(f'\nTesting {coin.upper()} {network.upper()}:\n')
-
 	m = get_obj(coin,network,msghash_type)
 
 	if m.proto.sign_mode == 'daemon':
@@ -131,23 +129,23 @@ class unit_tests:
 
 	altcoin_deps = ('ltc','bch','eth','eth_raw')
 
-	def btc(self,name,ut):
+	def btc(self, name, ut, desc='Bitcoin mainnet'):
 		return run_test('btc','AA0DB5')
 
-	def btc_tn(self,name,ut):
+	def btc_tn(self, name, ut, desc='Bitcoin testnet'):
 		return run_test('btc_tn','A88E1D')
 
-	def btc_rt(self,name,ut):
+	def btc_rt(self, name, ut, desc='Bitcoin regtest'):
 		return run_test('btc_rt','578018')
 
-	def ltc(self,name,ut):
+	def ltc(self, name, ut, desc='Litecoin mainnet'):
 		return run_test('ltc','BA7549')
 
-	def bch(self,name,ut):
+	def bch(self, name, ut, desc='Bitcoin Cash mainnet'):
 		return run_test('bch','1B8065')
 
-	def eth(self,name,ut):
+	def eth(self, name, ut, desc='Ethereum mainnet'):
 		return run_test('eth','35BAD9',msghash_type='eth_sign')
 
-	def eth_raw(self,name,ut):
+	def eth_raw(self, name, ut, desc='Ethereum mainnet (raw message)'):
 		return run_test('eth','9D900C')

+ 6 - 8
test/unit_tests_d/ut_obj.py

@@ -6,11 +6,11 @@ test.unit_tests_d.ut_obj: data object unit tests for the MMGen suite
 
 from decimal import Decimal
 
-from ..include.common import qmsg,qmsg_r,vmsg
+from ..include.common import vmsg
 
 class unit_tests:
 
-	def coinamt(self,name,ut):
+	def coinamt(self, name, ut, desc='BTCAmt, LTCAmt, XMRAmt and ETHAmt classes'):
 
 		from mmgen.amt import BTCAmt,LTCAmt,XMRAmt,ETHAmt
 
@@ -27,8 +27,7 @@ class unit_tests:
 					assert res == chk, f'{res} != {chk}'
 					assert type(res) is cls, f'{type(res).__name__} != {cls.__name__}'
 
-			qmsg_r(f'Testing {cls.__name__} arithmetic operations...')
-			vmsg('')
+			vmsg(f'\nTesting {cls.__name__} arithmetic operations...')
 
 			A,B   = ( Decimal(aa), Decimal(bb) )
 			a,b   = ( cls(aa),  cls(bb) )
@@ -51,9 +50,7 @@ class unit_tests:
 			do('a * b / a', a * b / a, A * B / A)
 			do('a * b / b', a * b / b, A * B / B)
 
-			qmsg('OK')
-			qmsg_r(f'Checking {cls.__name__} error handling...')
-			vmsg('')
+			vmsg(f'\nChecking {cls.__name__} error handling...')
 
 			bad_data = (
 				('negation',          'NotImplementedError', 'not implemented',    lambda: -a ),
@@ -72,5 +69,6 @@ class unit_tests:
 
 			ut.process_bad_data(bad_data)
 
-			qmsg('OK')
+			vmsg('OK')
+
 		return True

+ 54 - 62
test/unit_tests_d/ut_xmrseed.py

@@ -6,11 +6,11 @@ test/unit_tests_d/ut_xmrseed: Monero mnemonic unit test for the MMGen suite
 
 altcoin_dep = True
 
-from mmgen.util import msg,msg_r,ymsg
+from mmgen.util import ymsg
+from mmgen.xmrseed import xmrseed
+from ..include.common import cfg, vmsg
 
-from ..include.common import cfg,qmsg,vmsg
-
-class unit_test:
+class unit_tests:
 
 	vectors = ( # private keys are reduced
 		(   '148d78d2aba7dbca5cd8f6abcfb0b3c009ffbdbea1ff373d50ed94d78286640e', # Monero repo
@@ -55,53 +55,49 @@ class unit_test:
 		),
 	)
 
-	def run_test(self,name,ut):
-
-		def test_fromhex(b):
-			vmsg('')
-			qmsg('Checking seed to mnemonic conversion:')
-			for privhex,chk in self.vectors:
-				vmsg(f'    {chk}')
-				chk = tuple(chk.split())
-				res = b.fromhex(privhex)
-				if use_monero_python:
-					mp_chk = tuple( wl.encode(privhex).split() )
-					assert res == mp_chk, f'check failed:\nres: {res}\nchk: {chk}'
-				assert res == chk, f'check failed:\nres: {res}\nchk: {chk}'
-
-		def test_tohex(b):
-			vmsg('')
-			qmsg('Checking mnemonic to seed conversion:')
-			for chk,words in self.vectors:
-				vmsg(f'    {chk}')
-				res = b.tohex( words.split() )
-				if use_monero_python:
-					mp_chk = wl.decode( words )
-					assert res == mp_chk, f'check failed:\nres: {res}\nchk: {mp_chk}'
-				assert res == chk, f'check failed:\nres: {res}\nchk: {chk}'
-
-		msg_r('Testing xmrseed conversion routines...')
-		qmsg('')
-
-		from mmgen.xmrseed import xmrseed
+	@property
+	def _use_monero_python(self):
+		if not hasattr(self,'_use_monero_python_'):
+			try:
+				from monero.wordlists.english import English
+				self.wl = English()
+			except ImportError:
+				self._use_monero_python_ = False
+				ymsg('Warning: unable to import monero-python, skipping external library checks')
+			else:
+				self._use_monero_python_ = True
+		return self._use_monero_python_
+
+	def wordlist(self, name, ut, desc='Monero wordlist'):
+		xmrseed().check_wordlist(cfg)
+		return True
 
+	def fromhex(self, name, ut, desc='fromhex() method'):
 		b = xmrseed()
-		b.check_wordlist(cfg)
-
-		try:
-			from monero.wordlists.english import English
-			wl = English()
-		except ImportError:
-			use_monero_python = False
-			ymsg('Warning: unable to import monero-python, skipping external library checks')
-		else:
-			use_monero_python = True
+		vmsg('Checking seed to mnemonic conversion:')
+		for privhex, chk in self.vectors:
+			vmsg(f'    {chk}')
+			chk = tuple(chk.split())
+			res = b.fromhex(privhex)
+			if self._use_monero_python:
+				mp_chk = tuple( self.wl.encode(privhex).split() )
+				assert res == mp_chk, f'check failed:\nres: {res}\nchk: {chk}'
+			assert res == chk, f'check failed:\nres: {res}\nchk: {chk}'
+		return True
 
-		test_fromhex(b)
-		test_tohex(b)
+	def tohex(self, name, ut, desc='tohex() method'):
+		b = xmrseed()
+		vmsg('Checking mnemonic to seed conversion:')
+		for chk, words in self.vectors:
+			vmsg(f'    {chk}')
+			res = b.tohex( words.split() )
+			if self._use_monero_python:
+				mp_chk = self.wl.decode( words )
+				assert res == mp_chk, f'check failed:\nres: {res}\nchk: {mp_chk}'
+			assert res == chk, f'check failed:\nres: {res}\nchk: {chk}'
+		return True
 
-		vmsg('')
-		qmsg('Checking error handling:')
+	def errors(self, name, ut, desc='error handling'):
 
 		bad_chksum_mn = ('abbey ' * 21 + 'bamboo jaws jerseys donuts').split()
 		bad_word_mn = "admire zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo".split()
@@ -110,6 +106,7 @@ class unit_test:
 		good_hex = self.vectors[0][0]
 		bad_len_mn = good_mn[:22]
 
+		b = xmrseed()
 		th = b.tohex
 		fh = b.fromhex
 
@@ -118,20 +115,15 @@ class unit_test:
 		ase = 'AssertionError'
 		mne = 'MnemonicError'
 
-		bad_data = (
-			('hex',               hse, 'not a hexadecimal',     lambda:fh('xx')),
-			('seed len',          sle, 'invalid seed byte len', lambda:fh(bad_seed)),
-			('mnemonic type',     ase, 'must be list',          lambda:th('string')),
-			('pad arg (fromhex)', ase, "invalid 'pad' arg",     lambda:fh(good_hex,pad=23)),
-			('pad arg (tohex)',   ase, "invalid 'pad' arg",     lambda:th(good_mn,pad=23)),
-			('word',              mne, "not in Monero",         lambda:th(bad_word_mn)),
-			('checksum',          mne, "checksum",              lambda:th(bad_chksum_mn)),
-			('seed phrase len',   mne, "phrase len",            lambda:th(bad_len_mn)),
-		)
-
-		ut.process_bad_data(bad_data)
-
-		vmsg('')
-		msg('OK')
+		ut.process_bad_data((
+			('hex',               hse, 'not a hexadecimal',     lambda: fh('xx')),
+			('seed len',          sle, 'invalid seed byte len', lambda: fh(bad_seed)),
+			('mnemonic type',     ase, 'must be list',          lambda: th('string')),
+			('pad arg (fromhex)', ase, "invalid 'pad' arg",     lambda: fh(good_hex,pad=23)),
+			('pad arg (tohex)',   ase, "invalid 'pad' arg",     lambda: th(good_mn,pad=23)),
+			('word',              mne, "not in Monero",         lambda: th(bad_word_mn)),
+			('checksum',          mne, "checksum",              lambda: th(bad_chksum_mn)),
+			('seed phrase len',   mne, "phrase len",            lambda: th(bad_len_mn)),
+		))
 
 		return True