ct_opts.py 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  1. #!/usr/bin/env python3
  2. #
  3. # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
  4. # Copyright (C)2013-2024 The MMGen Project <mmgen@tuta.io>
  5. #
  6. # Project source code repository: https://github.com/mmgen/mmgen-wallet
  7. # Licensed according to the terms of GPL Version 3. See LICENSE for details.
  8. """
  9. test.cmdtest_py_d.ct_opts: options processing tests for the MMGen cmdtest.py test suite
  10. """
  11. import os,time
  12. from ..include.common import cfg
  13. from .ct_base import CmdTestBase
  14. class CmdTestOpts(CmdTestBase):
  15. 'command-line options parsing and processing'
  16. networks = ('btc',)
  17. tmpdir_nums = [41]
  18. cmd_group = (
  19. ('opt_helpscreen', (41, 'helpscreen output', [])),
  20. ('opt_noargs', (41, 'invocation with no user options or arguments', [])),
  21. ('opt_good1', (41, 'good opts (long opts only)', [])),
  22. ('opt_good2', (41, 'good opts (mixed short and long opts)', [])),
  23. ('opt_good3', (41, 'good opts (max arg count)', [])),
  24. ('opt_good4', (41, 'good opts (maxlen arg)', [])),
  25. ('opt_good5', (41, 'good opts (long opt substring)', [])),
  26. ('opt_good6', (41, 'good global opt (--coin=xmr)', [])),
  27. ('opt_good7', (41, 'good global opt (--coin xmr)', [])),
  28. ('opt_good8', (41, 'good global opt (--pager)', [])),
  29. ('opt_good9', (41, 'good cmdline arg ‘-’', [])),
  30. ('opt_good10', (41, 'good cmdline arg ‘-’ with arg', [])),
  31. ('opt_good11', (41, 'good cmdline arg ‘-’ with option', [])),
  32. ('opt_bad_param', (41, 'bad global opt (--pager=1)', [])),
  33. ('opt_bad_infile', (41, 'bad infile parameter', [])),
  34. ('opt_bad_outdir', (41, 'bad outdir parameter', [])),
  35. ('opt_bad_incompatible', (41, 'incompatible opts', [])),
  36. ('opt_bad_autoset', (41, 'invalid autoset value', [])),
  37. ('opt_invalid_1', (41, 'invalid cmdline opt ‘--x’', [])),
  38. ('opt_invalid_2', (41, 'invalid cmdline opt ‘---’', [])),
  39. ('opt_invalid_3', (41, 'invalid cmdline opt (combined short opt with param)', [])),
  40. ('opt_invalid_4', (41, 'invalid cmdline opt (combined short opt with param)', [])),
  41. ('opt_invalid_5', (41, 'invalid cmdline opt (missing parameter)', [])),
  42. ('opt_invalid_6', (41, 'invalid cmdline opt (missing parameter)', [])),
  43. ('opt_invalid_7', (41, 'invalid cmdline opt (parameter not required)', [])),
  44. ('opt_invalid_8', (41, 'invalid cmdline opt (non-existent option)', [])),
  45. ('opt_invalid_9', (41, 'invalid cmdline opt (non-existent option)', [])),
  46. ('opt_invalid_10', (41, 'invalid cmdline opt (missing parameter)', [])),
  47. ('opt_invalid_11', (41, 'invalid cmdline opt (missing parameter)', [])),
  48. ('opt_invalid_12', (41, 'invalid cmdline opt (non-existent option)', [])),
  49. ('opt_invalid_13', (41, 'invalid cmdline opt (ambiguous long opt substring)', [])),
  50. ('opt_invalid_14', (41, 'invalid cmdline opt (long opt substring too short)', [])),
  51. ('opt_invalid_15', (41, 'invalid cmdline (too many args)', [])),
  52. ('opt_invalid_16', (41, 'invalid cmdline (overlong arg)', [])),
  53. )
  54. def spawn_prog(self, args, exit_val=None):
  55. return self.spawn('test/misc/opts.py', args, cmd_dir='.', exit_val=exit_val)
  56. def check_vals(self,args,vals):
  57. t = self.spawn_prog(args)
  58. for k,v in vals:
  59. t.expect(rf'{k}:\s+{v}',regex=True)
  60. return t
  61. def do_run(self,args,expect,exit_val,regex=False):
  62. t = self.spawn_prog(args, exit_val=exit_val or None)
  63. t.expect(expect,regex=regex)
  64. return t
  65. def opt_helpscreen(self):
  66. expect = r'OPTS.PY: Opts test.*USAGE:\s+opts.py'
  67. if not cfg.pexpect_spawn:
  68. expect += r'.*--minconf.*NOTES FOR THIS.*a note'
  69. t = self.do_run( ['--help'], expect, 0, regex=True )
  70. if t.pexpect_spawn:
  71. time.sleep(0.4)
  72. t.send('q')
  73. return t
  74. def opt_noargs(self):
  75. return self.check_vals(
  76. [],
  77. (
  78. ('cfg.foo', 'None'), # added opt
  79. ('cfg.print_checksum', 'None'), # sets 'quiet'
  80. ('cfg.quiet', 'False'), # _incompatible_opts
  81. ('cfg.verbose', 'False'), # _incompatible_opts
  82. ('cfg.passwd_file', ''), # _infile_opts - check_infile()
  83. ('cfg.outdir', ''), # check_outdir()
  84. ('cfg.cached_balances', 'False'),
  85. ('cfg.minconf', '1'),
  86. ('cfg.coin', 'BTC'),
  87. ('cfg.pager', 'False'),
  88. ('cfg.fee_estimate_mode', 'conservative'), # _autoset_opts
  89. ))
  90. def opt_good1(self):
  91. pf_base = 'testfile'
  92. pf = os.path.join(self.tmpdir,pf_base)
  93. self.write_to_tmpfile(pf_base,'')
  94. return self.check_vals(
  95. [
  96. '--print-checksum',
  97. '--fee-estimate-mode=E',
  98. '--passwd-file='+pf,
  99. '--outdir='+self.tmpdir,
  100. '--cached-balances',
  101. f'--hidden-incog-input-params={pf},123',
  102. ], (
  103. ('cfg.print_checksum', 'True'),
  104. ('cfg.quiet', 'True'), # set by print_checksum
  105. ('cfg.passwd_file', pf),
  106. ('cfg.outdir', self.tmpdir),
  107. ('cfg.cached_balances', 'True'),
  108. ('cfg.hidden_incog_input_params', pf+',123'),
  109. ('cfg.fee_estimate_mode', 'economical'),
  110. ))
  111. def opt_good2(self):
  112. return self.check_vals(
  113. [
  114. '--print-checksum',
  115. '-qX',
  116. f'--outdir={self.tmpdir}',
  117. '-p5',
  118. '-m', '0',
  119. '--seed-len=256',
  120. '-L--my-label',
  121. '--seed-len', '128',
  122. '--min-temp=-30',
  123. '-T-10',
  124. '--',
  125. 'x', 'y', '12345'
  126. ], (
  127. ('cfg.print_checksum', 'True'),
  128. ('cfg.quiet', 'True'),
  129. ('cfg.outdir', self.tmpdir),
  130. ('cfg.cached_balances', 'True'),
  131. ('cfg.minconf', '0'),
  132. ('cfg.keep_label', 'None'),
  133. ('cfg.seed_len', '128'),
  134. ('cfg.hash_preset', '5'),
  135. ('cfg.label', '--my-label'),
  136. ('cfg.min_temp', '-30'),
  137. ('cfg.max_temp', '-10'),
  138. ('arg1', 'x'),
  139. ('arg2', 'y'),
  140. ('arg3', '12345'),
  141. ))
  142. def opt_good3(self):
  143. return self.check_vals(['m'] * 256, (('arg256', 'm'),))
  144. def opt_good4(self):
  145. return self.check_vals(['e' * 4096], (('arg1', 'e' * 4096),))
  146. def opt_good5(self):
  147. return self.check_vals(['--minc=7'], (('cfg.minconf', '7'),))
  148. def opt_good6(self):
  149. return self.check_vals(['--coin=xmr'], (('cfg.coin', 'XMR'),))
  150. def opt_good7(self):
  151. return self.check_vals(['--coin', 'xmr'], (('cfg.coin', 'XMR'),))
  152. def opt_good8(self):
  153. return self.check_vals(['--pager'], (('cfg.pager', 'True'),))
  154. def opt_good9(self):
  155. return self.check_vals(['-'], (('arg1', '-'),))
  156. def opt_good10(self):
  157. return self.check_vals(['-', '-x'], (('arg1', '-'), ('arg2', '-x')))
  158. def opt_good11(self):
  159. return self.check_vals(['-q', '-', '-x'], (('arg1', '-'), ('arg2', '-x')))
  160. def opt_bad_param(self):
  161. return self.do_run(['--pager=1'], 'no parameter', 1)
  162. def opt_bad_infile(self):
  163. pf = os.path.join(self.tmpdir,'fubar')
  164. return self.do_run(['--passwd-file='+pf],'not found',1)
  165. def opt_bad_outdir(self):
  166. bo = self.tmpdir+'_fubar'
  167. return self.do_run(['--outdir='+bo],'not found',1)
  168. def opt_bad_incompatible(self):
  169. return self.do_run(['--label=Label','--keep-label'],'Conflicting options',1)
  170. def opt_bad_autoset(self):
  171. return self.do_run(['--fee-estimate-mode=Fubar'],'not unique substring',1)
  172. def opt_invalid(self, args, expect, exit_val):
  173. t = self.spawn_prog(args, exit_val=exit_val)
  174. t.expect(expect)
  175. return t
  176. def opt_invalid_1(self):
  177. return self.opt_invalid(['--x'], 'must be at least', 1)
  178. def opt_invalid_2(self):
  179. return self.opt_invalid(['---'], 'must be at least', 1)
  180. def opt_invalid_3(self):
  181. return self.opt_invalid(['-kl3'], 'short option with parameters', 1)
  182. def opt_invalid_4(self):
  183. return self.opt_invalid(['-kl 3'], 'short option with parameters', 1)
  184. def opt_invalid_5(self):
  185. return self.opt_invalid(['-l'], 'missing parameter', 1)
  186. def opt_invalid_6(self):
  187. return self.opt_invalid(['-l', '-k'], 'missing parameter', 1)
  188. def opt_invalid_7(self):
  189. return self.opt_invalid(['--quiet=1'], 'requires no parameter', 1)
  190. def opt_invalid_8(self):
  191. return self.opt_invalid(['-x'], 'unrecognized option', 1)
  192. def opt_invalid_9(self):
  193. return self.opt_invalid(['--frobnicate'], 'unrecognized option', 1)
  194. def opt_invalid_10(self):
  195. return self.opt_invalid(['--label', '-q'], 'missing parameter', 1)
  196. def opt_invalid_11(self):
  197. return self.opt_invalid(['-T', '-10'], 'missing parameter', 1)
  198. def opt_invalid_12(self):
  199. return self.opt_invalid(['-q', '-10'], 'unrecognized option', 1)
  200. def opt_invalid_13(self):
  201. return self.opt_invalid(['--mi=3'], 'ambiguous option', 1)
  202. def opt_invalid_14(self):
  203. return self.opt_invalid(['--m=3'], 'must be at least', 1)
  204. def opt_invalid_15(self):
  205. return self.opt_invalid(['m'] * 257, 'too many', 1)
  206. def opt_invalid_16(self):
  207. return self.opt_invalid(['e' * 4097], 'too long', 1)