term.py 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. #!/usr/bin/env python3
  2. import sys,os
  3. pn = os.path.abspath(os.path.dirname(sys.argv[0]))
  4. parpar = os.path.dirname(os.path.dirname(pn))
  5. os.chdir(parpar)
  6. sys.path[0] = os.curdir
  7. from mmgen.common import *
  8. commands = [
  9. 'start',
  10. 'get_terminal_size',
  11. 'color',
  12. 'license',
  13. 'line_input',
  14. 'urand',
  15. 'txview',
  16. 'get_char_one',
  17. 'get_char_one_raw',
  18. ]
  19. if gc.platform == 'linux':
  20. commands.extend([
  21. 'get_char',
  22. 'get_char_immed_chars',
  23. 'get_char_raw',
  24. ])
  25. elif gc.platform == 'win':
  26. commands.extend([
  27. 'get_char_one_char_immed_chars',
  28. ])
  29. opts_data = {
  30. 'text': {
  31. 'desc': 'Interactively test MMGen terminal functionality',
  32. 'usage': 'command',
  33. 'options': """
  34. -h, --help Print this help message
  35. """,
  36. 'notes': f"""
  37. available commands for platform {gc.platform!r}:
  38. {fmt_list(commands,fmt='col',indent=' ')}
  39. """
  40. }
  41. }
  42. cfg = Config(opts_data=opts_data)
  43. from mmgen.term import get_char,get_char_raw,get_terminal_size,get_term
  44. from mmgen.ui import line_input,keypress_confirm,do_license_msg
  45. import mmgen.term as term_mod
  46. def cmsg(m):
  47. msg('\n'+cyan(m))
  48. def confirm(m):
  49. if not keypress_confirm( cfg, m ):
  50. if keypress_confirm( cfg, 'Are you sure you want to exit test?' ):
  51. die(1,'Exiting test at user request')
  52. else:
  53. msg('Continuing...')
  54. def tt_start():
  55. m = fmt("""
  56. We will now test MMGen’s terminal capabilities.
  57. This is a non-automated test and requires user interaction.
  58. Continue?
  59. """)
  60. confirm(m.strip())
  61. def tt_get_terminal_size():
  62. cmsg('Testing get_terminal_size():')
  63. msg('X' * get_terminal_size().width)
  64. confirm('Do the X’s exactly fill the width of the screen?')
  65. def tt_color():
  66. cmsg('Testing color:')
  67. confirm(blue('THIS TEXT') + ' should be blue. Is it?')
  68. def tt_license():
  69. cmsg('Testing do_license_msg() with pager')
  70. ymsg('Press "w" to test the pager, then "c" to continue')
  71. do_license_msg(cfg)
  72. def tt_line_input():
  73. set_vt100()
  74. cmsg('Testing line_input():')
  75. msg(fmt("""
  76. At the Ready? prompt type and hold down "y".
  77. Then Enter some text, followed by held-down ENTER.
  78. The held-down "y" and ENTER keys should be blocked, not affecting the output
  79. on screen or entered text.
  80. """))
  81. get_char_raw('Ready? ',num_bytes=1)
  82. reply = line_input( cfg, '\nEnter text: ' )
  83. confirm(f'Did you enter the text {reply!r}?')
  84. def _tt_get_char(raw=False,one_char=False,immed_chars=''):
  85. funcname = ('get_char','get_char_raw')[raw]
  86. fs = fmt("""
  87. Press some keys in quick succession.
  88. {}{}
  89. {}
  90. When you’re finished, use Ctrl-C to exit.
  91. """).strip()
  92. m1 = (
  93. 'You should experience a delay with quickly repeated entry.',
  94. 'Your entry should be repeated back to you immediately.'
  95. )[raw]
  96. m2 = (
  97. '',
  98. f'\nThe characters {immed_chars!r} will be repeated immediately, the others with delay.'
  99. )[bool(immed_chars)]
  100. m3 = 'The F1-F12 keys will be ' + (
  101. 'blocked entirely.'
  102. if one_char and not raw else
  103. "echoed AS A SINGLE character '\\x1b'."
  104. if one_char else
  105. 'echoed as a FULL CONTROL SEQUENCE.'
  106. )
  107. if gc.platform == 'win':
  108. if raw:
  109. m3 = 'The Escape and F1-F12 keys will be returned as two-character strings.'
  110. else:
  111. m3 = 'The Escape and F1-F12 keys will be returned as single characters.'
  112. kwargs = {}
  113. if one_char:
  114. kwargs.update({'num_bytes':1})
  115. if immed_chars:
  116. kwargs.update({'immed_chars':immed_chars})
  117. cmsg('Testing {}({}):'.format(
  118. funcname,
  119. ','.join(f'{a}={b!r}' for a,b in kwargs.items())
  120. ))
  121. msg(fs.format( m1, yellow(m2), yellow(m3) ))
  122. try:
  123. while True:
  124. ret = getattr( term_mod, funcname )('Enter a letter: ',**kwargs)
  125. msg(f'You typed {ret!r}')
  126. except KeyboardInterrupt:
  127. msg('\nDone')
  128. def tt_urand():
  129. cmsg('Testing _get_random_data_from_user():')
  130. from mmgen.crypto import Crypto
  131. ret = Crypto(cfg)._get_random_data_from_user(uchars=10,desc='data').decode()
  132. msg(f'USER ENTROPY (user input + keystroke timings):\n\n{fmt(ret," ")}')
  133. times = ret.splitlines()[1:]
  134. avg_prec = sum(len(t.split('.')[1]) for t in times) // len(times)
  135. if avg_prec < gc.min_time_precision:
  136. ymsg(f'WARNING: Avg. time precision of only {avg_prec} decimal points. User entropy quality is degraded!')
  137. else:
  138. msg(f'Average time precision: {avg_prec} decimal points - OK')
  139. line_input( cfg, 'Press ENTER to continue: ' )
  140. def tt_txview():
  141. cmsg('Testing tx.info.view_with_prompt() (try each viewing option)')
  142. from mmgen.tx import UnsignedTX
  143. fn = 'test/ref/0B8D5A[15.31789,14,tl=1320969600].rawtx'
  144. tx = UnsignedTX(cfg=cfg,filename=fn,quiet_open=True)
  145. while True:
  146. tx.info.view_with_prompt('View data for transaction?',pause=False)
  147. set_vt100()
  148. if not keypress_confirm( cfg, 'Continue testing transaction view?', default_yes=True ):
  149. break
  150. def tt_get_char_one():
  151. _tt_get_char(one_char=True)
  152. def tt_get_char_one_raw():
  153. _tt_get_char(one_char=True,raw=True)
  154. def tt_get_char():
  155. _tt_get_char(one_char=False)
  156. def tt_get_char_immed_chars():
  157. _tt_get_char(one_char=False,immed_chars='asdf')
  158. def tt_get_char_raw():
  159. _tt_get_char(one_char=False,raw=True)
  160. def tt_get_char_one_char_immed_chars():
  161. _tt_get_char(one_char=True,immed_chars='asdf')
  162. get_term().register_cleanup()
  163. if cfg._args:
  164. locals()['tt_'+cfg._args[0]]()
  165. else:
  166. for command in commands:
  167. locals()['tt_'+command]()
  168. gmsg('\nTest completed')