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

This commit is contained in:
The MMGen Project 2024-09-29 11:59:54 +00:00
commit 3f1c0b2a93
Signed by: mmgen
GPG key ID: 3F8B1861E32B7DA2
7 changed files with 160 additions and 164 deletions

View file

@ -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()

View file

@ -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

View file

@ -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 = {}

View file

@ -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

View file

@ -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')

View file

@ -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

View file

@ -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):
@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 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
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)
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
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
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
test_fromhex(b)
test_tohex(b)
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