Browse Source

modtest.py seedsplit, subseed: refactor

The MMGen Project 2 months ago
parent
commit
d54ed9d427
2 changed files with 290 additions and 298 deletions
  1. 154 158
      test/modtest_d/seedsplit.py
  2. 136 140
      test/modtest_d/subseed.py

+ 154 - 158
test/modtest_d/seedsplit.py

@@ -1,182 +1,178 @@
 #!/usr/bin/env python3
 
 """
-test.modtest_d.seedsplit: seed splitting unit test for the MMGen suite
+test.modtest_d.seedsplit: seed splitting unit tests for the MMGen suite
 """
 
 from mmgen.util import msg, msg_r
+from mmgen.color import pink
+from mmgen.seed import Seed
+from mmgen.seedsplit import SeedShareList, SeedShareIdx
 
 from ..include.common import cfg, vmsg, vmsg_r
 
-class unit_test:
-
-	def run_test(self, name, ut):
-		from mmgen.seed import Seed
-		from mmgen.seedsplit import SeedShareList, SeedShareIdx
-
-		cfg.debug_subseed = bool(cfg.verbose)
-
-		def basic_ops(master_idx):
-			test_data = {
-				'default': (
-					(8, '4710FBF0', 'B3D9411B', '2670E83D', 'D1FC57ED', 'AE49CABE', '63FFBA62', 0, 0),
-					(6, '9D07ABBD', 'AF5DC2F6', '1A3BBDAC', '2548AEE9', 'B94F7450', '1F4E5A12', 0, 0),
-					(4, '43670520', '1F72C066', 'E5AA8DA1', 'A33966A0', 'D2BCE0A5', 'A568C315', 0, 0),
-				),
-				'φυβαρ': (
-					(8, '4710FBF0', '269D658C', '9D25889E', '6D730ECB', 'C61A963F', '9FE99C05', 0, 0),
-					(6, '9D07ABBD', '4998B33E', 'F00CE041', 'C612BEE5', '35CD3675', '41B3BE61', 0, 0),
-					(4, '43670520', '77140076', 'EA82CB30', '80F7AEDE', 'D168D768', '77BE57AA', 0, 0),
-				)
-			}
-			test_data_master = {
-				'1': {
-					'default': (
-						(8, '4710FBF0', '6AE6177F', 'AC12090C', '6AE6177F',
-							'3E87A907', '7D1FEA56', 'BFEBFFFF', '629A9808'),
-						(4, '43670520', '6739535C', 'ABF4DD38', '6739535C',
-							'778E9C60', '89CBCFD2', '689FABF5', '70BED76B'),
-					),
-					'φυβαρ': (
-						(8, '4710FBF0', '6AE6177F', 'AC5FA32E', '6AE6177F',
-							'9777A750', 'C7CF2AFC', '035AAACB', 'C777FBE4'),
-						(4, '43670520', '6739535C', '37EBA2F5', '6739535C',
-							'927549D2', '29BADEE7', '9CA73A03', '313F5528'))
-				},
-				'5': {
-					'default': (
-						(8, '4710FBF0', '5EFAC3D6', 'B489167D', '5EFAC3D6',
-							'BB004DC5', '1A0381C0', '4EA182E3', '547FB2DC'),
-						(4, '43670520', 'EE93DB0E', '44962A7D', 'EE93DB0E',
-							'07339882', '376A05B1', 'CE51D022', '00149CA3'),
-					),
-					'φυβαρ': (
-						(8, '4710FBF0', '5EFAC3D6', 'A6E27EE3', '5EFAC3D6',
-							'32C24668', 'B4C54297', '1EC9B71B', '8C5C6B1C'),
-						(4, '43670520', 'EE93DB0E', 'B584E963', 'EE93DB0E',
-							'4BEA2AB2', '4BEA65C7', '140FC43F', 'BBD19461'))
-				}
-			}
-			if master_idx:
-				test_data = test_data_master[str(master_idx)]
-
-			for id_str in (None, 'default', 'φυβαρ'):
-				msg_r(f'Testing basic ops (id_str={id_str!r}, master_idx={master_idx})...')
-				vmsg('')
-
-				for a, b, c, d, e, f, h, i, p in test_data[id_str if id_str is not None else 'default']:
-					seed_bin = bytes.fromhex('deadbeef' * a)
-					seed = Seed(cfg, seed_bin=seed_bin)
-					assert seed.sid == b, seed.sid
-
-					for share_count, j, k, l, m in (
-							(2, c, c, d, i),
-							(5, e, f, h, p)):
-
-						shares = seed.split(share_count, id_str=id_str, master_idx=master_idx)
-						A = len(shares)
-						assert A == share_count, A
-
-						s = shares.format()
-						vmsg_r(f'\n{s}')
-						assert len(s.strip().split('\n')) == share_count+6, s
-
-						if master_idx:
-							A = shares.get_share_by_idx(1, base_seed=False).sid
-							B = shares.get_share_by_seed_id(j, base_seed=False).sid
-							assert A == B == m, A
-
-						A = shares.get_share_by_idx(1, base_seed=True).sid
-						B = shares.get_share_by_seed_id(j, base_seed=True).sid
-						assert A == B == j, A
-
-						A = shares.get_share_by_idx(share_count-1, base_seed=True).sid
-						B = shares.get_share_by_seed_id(k, base_seed=True).sid
-						assert A == B == k, A
-
-						A = shares.get_share_by_idx(share_count).sid
-						B = shares.get_share_by_seed_id(l).sid
-						assert A == B == l, A
-
-						A = shares.join().sid
-						assert A == b, A
-
-						if master_idx:
-							slist = [shares.get_share_by_idx(i+1, base_seed=True) for i in range(len(shares))]
-							A = Seed.join_shares(cfg, slist, master_idx=master_idx, id_str=id_str).sid
-							assert A == b, A
-
-				msg('OK')
-
-		def defaults_and_limits():
-			msg_r('Testing defaults and limits...')
-
-			seed_bin = bytes.fromhex('deadbeef' * 8)
+cfg.debug_subseed = bool(cfg.verbose)
+
+def basic_ops(master_idx):
+	test_data = {
+		'default': (
+			(8, '4710FBF0', 'B3D9411B', '2670E83D', 'D1FC57ED', 'AE49CABE', '63FFBA62', 0, 0),
+			(6, '9D07ABBD', 'AF5DC2F6', '1A3BBDAC', '2548AEE9', 'B94F7450', '1F4E5A12', 0, 0),
+			(4, '43670520', '1F72C066', 'E5AA8DA1', 'A33966A0', 'D2BCE0A5', 'A568C315', 0, 0),
+		),
+		'φυβαρ': (
+			(8, '4710FBF0', '269D658C', '9D25889E', '6D730ECB', 'C61A963F', '9FE99C05', 0, 0),
+			(6, '9D07ABBD', '4998B33E', 'F00CE041', 'C612BEE5', '35CD3675', '41B3BE61', 0, 0),
+			(4, '43670520', '77140076', 'EA82CB30', '80F7AEDE', 'D168D768', '77BE57AA', 0, 0),
+		)
+	}
+	test_data_master = {
+		'1': {
+			'default': (
+				(8, '4710FBF0', '6AE6177F', 'AC12090C', '6AE6177F',
+					'3E87A907', '7D1FEA56', 'BFEBFFFF', '629A9808'),
+				(4, '43670520', '6739535C', 'ABF4DD38', '6739535C',
+					'778E9C60', '89CBCFD2', '689FABF5', '70BED76B'),
+			),
+			'φυβαρ': (
+				(8, '4710FBF0', '6AE6177F', 'AC5FA32E', '6AE6177F',
+					'9777A750', 'C7CF2AFC', '035AAACB', 'C777FBE4'),
+				(4, '43670520', '6739535C', '37EBA2F5', '6739535C',
+					'927549D2', '29BADEE7', '9CA73A03', '313F5528'))
+		},
+		'5': {
+			'default': (
+				(8, '4710FBF0', '5EFAC3D6', 'B489167D', '5EFAC3D6',
+					'BB004DC5', '1A0381C0', '4EA182E3', '547FB2DC'),
+				(4, '43670520', 'EE93DB0E', '44962A7D', 'EE93DB0E',
+					'07339882', '376A05B1', 'CE51D022', '00149CA3'),
+			),
+			'φυβαρ': (
+				(8, '4710FBF0', '5EFAC3D6', 'A6E27EE3', '5EFAC3D6',
+					'32C24668', 'B4C54297', '1EC9B71B', '8C5C6B1C'),
+				(4, '43670520', 'EE93DB0E', 'B584E963', 'EE93DB0E',
+					'4BEA2AB2', '4BEA65C7', '140FC43F', 'BBD19461'))
+		}
+	}
+	if master_idx:
+		test_data = test_data_master[str(master_idx)]
+
+	for id_str in (None, 'default', 'φυβαρ'):
+		vmsg(f'Testing ID str: {pink(str(id_str))}')
+
+		for a, b, c, d, e, f, h, i, p in test_data[id_str if id_str is not None else 'default']:
+			seed_bin = bytes.fromhex('deadbeef' * a)
 			seed = Seed(cfg, seed_bin=seed_bin)
+			assert seed.sid == b, seed.sid
 
-			shares = seed.split(SeedShareIdx.max_val)
-			s = shares.format()
-#			vmsg_r(f'\n{s}')
-			assert len(s.strip().split('\n')) == 1030, s
+			for share_count, j, k, l, m in (
+					(2, c, c, d, i),
+					(5, e, f, h, p)):
 
-			A = shares.get_share_by_idx(1024).sid
-			B = shares.get_share_by_seed_id('4BA23728').sid
-			assert A == '4BA23728', A
-			assert B == '4BA23728', B
+				shares = seed.split(share_count, id_str=id_str, master_idx=master_idx)
+				A = len(shares)
+				assert A == share_count, A
 
-			A = shares.join().sid
-			B = seed.sid
-			assert A == B, A
+				s = shares.format()
+				vmsg_r(f'\n{s}')
+				assert len(s.strip().split('\n')) == share_count+6, s
 
-			msg('OK')
+				if master_idx:
+					A = shares.get_share_by_idx(1, base_seed=False).sid
+					B = shares.get_share_by_seed_id(j, base_seed=False).sid
+					assert A == B == m, A
 
-		def collisions(seed_hex, ss_count, last_sid, collisions_chk, master_idx):
+				A = shares.get_share_by_idx(1, base_seed=True).sid
+				B = shares.get_share_by_seed_id(j, base_seed=True).sid
+				assert A == B == j, A
 
-			msg_r(f'Testing Seed ID collisions ({ss_count} seed shares, master_idx={master_idx})...')
-			vmsg('')
+				A = shares.get_share_by_idx(share_count-1, base_seed=True).sid
+				B = shares.get_share_by_seed_id(k, base_seed=True).sid
+				assert A == B == k, A
 
-			seed_bin = bytes.fromhex(seed_hex)
-			seed = Seed(cfg, seed_bin=seed_bin)
+				A = shares.get_share_by_idx(share_count).sid
+				B = shares.get_share_by_seed_id(l).sid
+				assert A == B == l, A
 
-			SeedShareIdx.max_val = ss_count
-			shares = seed.split(ss_count, master_idx=master_idx)
-			A = shares.get_share_by_idx(ss_count).sid
-			B = shares.get_share_by_seed_id(last_sid).sid
-			assert A == last_sid, A
-			assert B == last_sid, B
+				A = shares.join().sid
+				assert A == b, A
 
-			assert shares.nonce_start == 0, shares.nonce_start
+				if master_idx:
+					slist = [shares.get_share_by_idx(i+1, base_seed=True) for i in range(len(shares))]
+					A = Seed.join_shares(cfg, slist, master_idx=master_idx, id_str=id_str).sid
+					assert A == b, A
 
-			collisions = 0
-			for sid in shares.data['long']:
-				collisions += shares.data['long'][sid][1]
+		return True
 
-			assert collisions == collisions_chk, collisions
-			vmsg_r(f'{collisions} collisions, last_sid {last_sid}')
-			msg('OK')
+def collisions(seed_hex, ss_count, last_sid, collisions_chk, master_idx):
 
-		def last_share_collisions():
-			msg_r('Testing last share collisions with shortened Seed IDs')
-			vmsg('')
-			seed_bin = bytes.fromhex('2eadbeef'*8)
-			seed = Seed(cfg, seed_bin=seed_bin)
-			ssm_save = SeedShareIdx.max_val
-			ssm = SeedShareIdx.max_val = 2048
-			shares = SeedShareList(seed, count=ssm, id_str='foo', master_idx=1, debug_last_share=True)
-			lsid = shares.last_share.sid
-			collisions = shares.data['long'][lsid][1]
-			assert collisions == 2, collisions
-			assert lsid == 'B5B8AD09', lsid
-			SeedShareIdx.max_val = ssm_save
-			vmsg_r(f'{collisions} collisions, last_share sid {lsid}')
-			msg('..OK')
-
-		basic_ops(master_idx=None)
-		basic_ops(master_idx=1)
-		basic_ops(master_idx=5)
-		defaults_and_limits()
-		last_share_collisions()
-		collisions('1dabcdef'*4, 65535, 'B5CBCE0A', 3, master_idx=None)
-		collisions('18abcdef'*4, 65535, 'FF03CE82', 3, master_idx=1)
+	seed_bin = bytes.fromhex(seed_hex)
+	seed = Seed(cfg, seed_bin=seed_bin)
+
+	SeedShareIdx.max_val = ss_count
+	shares = seed.split(ss_count, master_idx=master_idx)
+	A = shares.get_share_by_idx(ss_count).sid
+	B = shares.get_share_by_seed_id(last_sid).sid
+	assert A == last_sid, A
+	assert B == last_sid, B
+
+	assert shares.nonce_start == 0, shares.nonce_start
+
+	collisions = 0
+	for sid in shares.data['long']:
+		collisions += shares.data['long'][sid][1]
+
+	assert collisions == collisions_chk, collisions
+	vmsg(f'{collisions} collisions, last_sid {last_sid}')
+
+	return True
+
+class unit_tests:
+
+	def ops1(self, name, ut, desc='basic ops (master_idx=None)'):
+		return basic_ops(master_idx=None)
 
+	def ops2(self, name, ut, desc='basic ops (master_idx=1)'):
+		return basic_ops(master_idx=1)
+
+	def ops3(self, name, ut, desc='basic ops (master_idx=5)'):
+		return basic_ops(master_idx=5)
+
+	def limits(self, name, ut, desc='defaults and limits...'):
+		seed_bin = bytes.fromhex('deadbeef' * 8)
+		seed = Seed(cfg, seed_bin=seed_bin)
+
+		shares = seed.split(SeedShareIdx.max_val)
+		s = shares.format()
+		assert len(s.strip().split('\n')) == 1030, s
+
+		A = shares.get_share_by_idx(1024).sid
+		B = shares.get_share_by_seed_id('4BA23728').sid
+		assert A == '4BA23728', A
+		assert B == '4BA23728', B
+
+		A = shares.join().sid
+		B = seed.sid
+		assert A == B, A
+
+		return True
+
+	def collisions1(self, name, ut, desc='last share collisions with shortened Seed IDs'):
+		seed_bin = bytes.fromhex('2eadbeef'*8)
+		seed = Seed(cfg, seed_bin=seed_bin)
+		ssm_save = SeedShareIdx.max_val
+		ssm = SeedShareIdx.max_val = 2048
+		shares = SeedShareList(seed, count=ssm, id_str='foo', master_idx=1, debug_last_share=True)
+		lsid = shares.last_share.sid
+		collisions = shares.data['long'][lsid][1]
+		assert collisions == 2, collisions
+		assert lsid == 'B5B8AD09', lsid
+		SeedShareIdx.max_val = ssm_save
+		vmsg(f'{collisions} collisions, last_share sid {lsid}')
 		return True
+
+	def collisions2(self, name, ut, desc='Seed ID collisions (65535 seed shares, master_idx=None)'):
+		return collisions('1dabcdef'*4, 65535, 'B5CBCE0A', 3, master_idx=None)
+
+	def collisions3(self, name, ut, desc='Seed ID collisions (65535 seed shares, master_idx=1)'):
+		return collisions('18abcdef'*4, 65535, 'FF03CE82', 3, master_idx=1)

+ 136 - 140
test/modtest_d/subseed.py

@@ -1,196 +1,192 @@
 #!/usr/bin/env python3
 
 """
-test.modtest_d.subseed: subseed unit test for the MMGen suite
+test.modtest_d.subseed: subseed unit tests for the MMGen suite
 """
 
-from mmgen.util import msg, msg_r
+from mmgen.util import msg
+from mmgen.seed import Seed
+from mmgen.subseed import SubSeedList, SubSeedIdxRange
 
-from ..include.common import cfg, vmsg_r
+from ..include.common import cfg, vmsg
 
-class unit_test:
+nSubseeds = SubSeedList.dfl_len
 
-	def run_test(self, name, ut):
-		from mmgen.seed import Seed
-		from mmgen.subseed import SubSeedList, SubSeedIdxRange
+class unit_tests:
 
-		nSubseeds = SubSeedList.dfl_len
+	def ops(self, name, ut, desc='basic ops...'):
+		for a, b, c, d, e, f, h in (
+				(8, '4710FBF0', '0C1B0615', '803B165C', '2669AC64', 256, '10L'),
+				(6, '9D07ABBD', 'EBA9C33F', '20787E6A', '192E2AA2', 192, '10L'),
+				(4, '43670520', '04A4CCB3', 'B5F21D7B', 'C1934CFF', 128, '10L'),
+			):
 
-		def basic_ops():
-			msg_r('Testing basic ops...')
-			for a, b, c, d, e, f, h in (
-					(8, '4710FBF0', '0C1B0615', '803B165C', '2669AC64', 256, '10L'),
-					(6, '9D07ABBD', 'EBA9C33F', '20787E6A', '192E2AA2', 192, '10L'),
-					(4, '43670520', '04A4CCB3', 'B5F21D7B', 'C1934CFF', 128, '10L'),
-				):
+			seed_bin = bytes.fromhex('deadbeef' * a)
+			seed = Seed(cfg, seed_bin=seed_bin)
+			assert seed.sid == b, seed.sid
 
-				seed_bin = bytes.fromhex('deadbeef' * a)
-				seed = Seed(cfg, seed_bin=seed_bin)
-				assert seed.sid == b, seed.sid
+			subseed = seed.subseed('2s')
+			assert subseed.sid == c, subseed.sid
 
-				subseed = seed.subseed('2s')
-				assert subseed.sid == c, subseed.sid
+			subseed = seed.subseed('3')
+			assert subseed.sid == d, subseed.sid
 
-				subseed = seed.subseed('3')
-				assert subseed.sid == d, subseed.sid
+			subseed = seed.subseed_by_seed_id(e)
+			assert subseed.bitlen == f, subseed.bitlen
+			assert subseed.sid == e, subseed.sid
+			assert subseed.idx == 10, subseed.idx
+			assert subseed.ss_idx == h, subseed.ss_idx
 
-				subseed = seed.subseed_by_seed_id(e)
-				assert subseed.bitlen == f, subseed.bitlen
-				assert subseed.sid == e, subseed.sid
-				assert subseed.idx == 10, subseed.idx
-				assert subseed.ss_idx == h, subseed.ss_idx
+			seed2 = Seed(cfg, seed_bin=seed_bin)
+			ss2_list = seed2.subseeds
 
-				seed2 = Seed(cfg, seed_bin=seed_bin)
-				ss2_list = seed2.subseeds
+			seed2.subseeds._generate(1)
+			assert len(ss2_list) == 1, len(ss2_list)
 
-				seed2.subseeds._generate(1)
-				assert len(ss2_list) == 1, len(ss2_list)
+			seed2.subseeds._generate(1) # do nothing
+			seed2.subseeds._generate(2) # append one item
 
-				seed2.subseeds._generate(1) # do nothing
-				seed2.subseeds._generate(2) # append one item
+			seed2.subseeds._generate(5)
+			assert len(ss2_list) == 5, len(ss2_list)
 
-				seed2.subseeds._generate(5)
-				assert len(ss2_list) == 5, len(ss2_list)
+			seed2.subseeds._generate(3) # do nothing
+			assert len(ss2_list) == 5, len(ss2_list)
 
-				seed2.subseeds._generate(3) # do nothing
-				assert len(ss2_list) == 5, len(ss2_list)
+			seed2.subseeds._generate(10)
+			assert len(ss2_list) == 10, len(ss2_list)
 
-				seed2.subseeds._generate(10)
-				assert len(ss2_list) == 10, len(ss2_list)
+			assert seed.pfmt() == seed2.pfmt()
+			assert seed.subseeds.pfmt() == seed2.subseeds.pfmt()
 
-				assert seed.pfmt() == seed2.pfmt()
-				assert seed.subseeds.pfmt() == seed2.subseeds.pfmt()
+			s = seed.subseeds.format(1, nSubseeds)
+			s_lines = s.strip().split('\n')
+			assert len(s_lines) == nSubseeds + 4, s
 
-				s = seed.subseeds.format(1, nSubseeds)
-				s_lines = s.strip().split('\n')
-				assert len(s_lines) == nSubseeds + 4, s
+			a = seed.subseed('2L').sid
+			b = [e for e in s_lines if ' 2L:' in e][0].strip().split()[1]
+			assert a == b, b
 
-				a = seed.subseed('2L').sid
-				b = [e for e in s_lines if ' 2L:' in e][0].strip().split()[1]
-				assert a == b, b
+			c = seed.subseed('2').sid
+			assert c == a, c
 
-				c = seed.subseed('2').sid
-				assert c == a, c
+			a = seed.subseed('5S').sid
+			b = [e for e in s_lines if ' 5S:' in e][0].strip().split()[3]
+			assert a == b, b
 
-				a = seed.subseed('5S').sid
-				b = [e for e in s_lines if ' 5S:' in e][0].strip().split()[3]
-				assert a == b, b
+			s = seed.subseeds.format(nSubseeds+1, nSubseeds+2)
+			s_lines = s.strip().split('\n')
+			assert len(s_lines) == 6, s
 
-				s = seed.subseeds.format(nSubseeds+1, nSubseeds+2)
-				s_lines = s.strip().split('\n')
-				assert len(s_lines) == 6, s
+			ss_idx = str(nSubseeds+2) + 'S'
+			a = seed.subseed(ss_idx).sid
+			b = [e for e in s_lines if f' {ss_idx}:' in e][0].strip().split()[3]
+			assert a == b, b
 
-				ss_idx = str(nSubseeds+2) + 'S'
-				a = seed.subseed(ss_idx).sid
-				b = [e for e in s_lines if f' {ss_idx}:' in e][0].strip().split()[3]
-				assert a == b, b
+			s = seed.subseeds.format(1, 10)
+			s_lines = s.strip().split('\n')
+			assert len(s_lines) == 14, s
 
-				s = seed.subseeds.format(1, 10)
-				s_lines = s.strip().split('\n')
-				assert len(s_lines) == 14, s
+			vmsg(f'{s}')
 
-				vmsg_r(f'\n{s}')
+		return True
 
-			msg('OK')
+	def limits(self, name, ut, desc='limits and ranges'):
 
-		def defaults_and_limits():
-			msg_r('Testing defaults and limits...')
+		seed_bin = bytes.fromhex('deadbeef' * 8)
 
-			seed_bin = bytes.fromhex('deadbeef' * 8)
+		vmsg('Testing limits')
 
-			seed = Seed(cfg, seed_bin=seed_bin, nSubseeds=11)
-			seed.subseeds._generate()
-			ss = seed.subseeds
-			assert len(ss.data['long']) == len(ss.data['short']), len(ss.data['short'])
-			assert len(ss) == 11, len(ss)
+		seed = Seed(cfg, seed_bin=seed_bin, nSubseeds=11)
+		seed.subseeds._generate()
+		ss = seed.subseeds
+		assert len(ss.data['long']) == len(ss.data['short']), len(ss.data['short'])
+		assert len(ss) == 11, len(ss)
 
-			seed = Seed(cfg, seed_bin=seed_bin)
-			seed.subseeds._generate()
-			ss = seed.subseeds
-			assert len(ss.data['long']) == len(ss.data['short']), len(ss.data['short'])
-			assert len(ss) == nSubseeds, len(ss)
+		seed = Seed(cfg, seed_bin=seed_bin)
+		seed.subseeds._generate()
+		ss = seed.subseeds
+		assert len(ss.data['long']) == len(ss.data['short']), len(ss.data['short'])
+		assert len(ss) == nSubseeds, len(ss)
 
-			seed = Seed(cfg, seed_bin=seed_bin)
-			seed.subseed_by_seed_id('EEEEEEEE')
-			ss = seed.subseeds
-			assert len(ss.data['long']) == len(ss.data['short']), len(ss.data['short'])
-			assert len(ss) == nSubseeds, len(ss)
+		seed = Seed(cfg, seed_bin=seed_bin)
+		seed.subseed_by_seed_id('EEEEEEEE')
+		ss = seed.subseeds
+		assert len(ss.data['long']) == len(ss.data['short']), len(ss.data['short'])
+		assert len(ss) == nSubseeds, len(ss)
 
-			seed = Seed(cfg, seed_bin=seed_bin)
-			subseed = seed.subseed_by_seed_id('803B165C')
-			assert len(ss.data['long']) == len(ss.data['short']), len(ss.data['short'])
-			assert subseed.sid == '803B165C', subseed.sid
-			assert subseed.idx == 3, subseed.idx
+		seed = Seed(cfg, seed_bin=seed_bin)
+		subseed = seed.subseed_by_seed_id('803B165C')
+		assert len(ss.data['long']) == len(ss.data['short']), len(ss.data['short'])
+		assert subseed.sid == '803B165C', subseed.sid
+		assert subseed.idx == 3, subseed.idx
 
-			seed = Seed(cfg, seed_bin=seed_bin)
-			subseed = seed.subseed_by_seed_id('803B165C', last_idx=1)
-			assert len(ss.data['long']) == len(ss.data['short']), len(ss.data['short'])
-			assert subseed is None, subseed
+		seed = Seed(cfg, seed_bin=seed_bin)
+		subseed = seed.subseed_by_seed_id('803B165C', last_idx=1)
+		assert len(ss.data['long']) == len(ss.data['short']), len(ss.data['short'])
+		assert subseed is None, subseed
+
+		vmsg('Testing SubSeedIdxRange()')
 
-			r = SubSeedIdxRange('1-5')
-			r2 = SubSeedIdxRange(1, 5)
-			assert r2 == r, r2
-			assert r == (r.first, r.last), r
-			assert r.first == 1, r.first
-			assert r.last == 5, r.last
-			assert r.items == [1, 2, 3, 4, 5], r.items
-			assert list(r.iterate()) == r.items, list(r.iterate())
+		r = SubSeedIdxRange('1-5')
+		r2 = SubSeedIdxRange(1, 5)
+		assert r2 == r, r2
+		assert r == (r.first, r.last), r
+		assert r.first == 1, r.first
+		assert r.last == 5, r.last
+		assert r.items == [1, 2, 3, 4, 5], r.items
+		assert list(r.iterate()) == r.items, list(r.iterate())
 
-			r = SubSeedIdxRange('22')
-			r2 = SubSeedIdxRange(22, 22)
-			assert r2 == r, r2
-			assert r == (r.first, r.last), r
-			assert r.first == 22, r.first
-			assert r.last == 22, r.last
-			assert r.items == [22], r
-			assert list(r.iterate()) == r.items, list(r.iterate())
+		r = SubSeedIdxRange('22')
+		r2 = SubSeedIdxRange(22, 22)
+		assert r2 == r, r2
+		assert r == (r.first, r.last), r
+		assert r.first == 22, r.first
+		assert r.last == 22, r.last
+		assert r.items == [22], r
+		assert list(r.iterate()) == r.items, list(r.iterate())
 
-			r = SubSeedIdxRange('3-3')
-			assert r.items == [3], r.items
+		r = SubSeedIdxRange('3-3')
+		assert r.items == [3], r.items
 
-			r = SubSeedIdxRange(f'{nSubseeds-1}-{nSubseeds}')
-			assert r.items == [nSubseeds-1, nSubseeds], r.items
+		r = SubSeedIdxRange(f'{nSubseeds-1}-{nSubseeds}')
+		assert r.items == [nSubseeds-1, nSubseeds], r.items
 
-			for n, e in enumerate(SubSeedIdxRange('1-5').iterate(), 1):
-				assert n == e, e
+		for n, e in enumerate(SubSeedIdxRange('1-5').iterate(), 1):
+			assert n == e, e
 
-			assert n == 5, n
+		assert n == 5, n
 
-			msg('OK')
+		return True
 
-		def collisions():
-			ss_count, ltr, last_sid, collisions_chk = (
-				(SubSeedIdxRange.max_idx, 'S', '2788F26B', 470),
-				(49509, 'L', '8D1FE500', 2)
-			)[bool(cfg.fast)]
+	def collisions(self, name, ut, desc='Seed ID collisions'):
 
-			last_idx = str(ss_count) + ltr
+		ss_count, ltr, last_sid, collisions_chk, mode_desc = (
+			(SubSeedIdxRange.max_idx, 'S', '2788F26B', 470, 'normal'),
+			(49509, 'L', '8D1FE500', 2, 'fast')
+		)[bool(cfg.fast)]
 
-			msg_r(f'Testing Seed ID collisions ({ss_count} subseed pairs)...')
+		last_idx = str(ss_count) + ltr
 
-			seed_bin = bytes.fromhex('12abcdef' * 8) # 95B3D78D
-			seed = Seed(cfg, seed_bin=seed_bin)
+		vmsg(f'Using {ss_count} subseed pairs ({mode_desc} mode)')
 
-			seed.subseeds._generate(ss_count)
-			ss = seed.subseeds
+		seed_bin = bytes.fromhex('12abcdef' * 8) # 95B3D78D
+		seed = Seed(cfg, seed_bin=seed_bin)
 
-			assert seed.subseed(last_idx).sid == last_sid, seed.subseed(last_idx).sid
+		seed.subseeds._generate(ss_count)
+		ss = seed.subseeds
 
-			for sid in ss.data['long']:
-				# msg(sid)
-				assert sid not in ss.data['short']
+		assert seed.subseed(last_idx).sid == last_sid, seed.subseed(last_idx).sid
 
-			collisions = 0
-			for k in ('short', 'long'):
-				for sid in ss.data[k]:
-					collisions += ss.data[k][sid][1]
+		for sid in ss.data['long']:
+			# msg(sid)
+			assert sid not in ss.data['short']
 
-			assert collisions == collisions_chk, collisions
-			vmsg_r(f'\n{collisions} collisions, last_sid {last_sid}')
-			msg('OK')
+		collisions = 0
+		for k in ('short', 'long'):
+			for sid in ss.data[k]:
+				collisions += ss.data[k][sid][1]
 
-		basic_ops()
-		defaults_and_limits()
-		collisions()
+		assert collisions == collisions_chk, collisions
+		vmsg(f'{collisions} collisions, last_sid {last_sid}')
 
 		return True