tests_main.py 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244
  1. """Test CLI usage."""
  2. import logging
  3. import subprocess # nosec
  4. import sys
  5. from functools import wraps
  6. from os import linesep
  7. from tqdm.cli import TqdmKeyError, TqdmTypeError, main
  8. from tqdm.utils import IS_WIN
  9. from .tests_tqdm import BytesIO, closing, mark, raises
  10. def restore_sys(func):
  11. """Decorates `func(capsysbinary)` to save & restore `sys.(stdin|argv)`."""
  12. @wraps(func)
  13. def inner(capsysbinary):
  14. """function requiring capsysbinary which may alter `sys.(stdin|argv)`"""
  15. _SYS = sys.stdin, sys.argv
  16. try:
  17. res = func(capsysbinary)
  18. finally:
  19. sys.stdin, sys.argv = _SYS
  20. return res
  21. return inner
  22. def norm(bytestr):
  23. """Normalise line endings."""
  24. return bytestr if linesep == "\n" else bytestr.replace(linesep.encode(), b"\n")
  25. @mark.slow
  26. def test_pipes():
  27. """Test command line pipes"""
  28. ls_out = subprocess.check_output(['ls']) # nosec
  29. ls = subprocess.Popen(['ls'], stdout=subprocess.PIPE) # nosec
  30. res = subprocess.Popen( # nosec
  31. [sys.executable, '-c', 'from tqdm.cli import main; main()'],
  32. stdin=ls.stdout, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
  33. out, err = res.communicate()
  34. assert ls.poll() == 0
  35. # actual test:
  36. assert norm(ls_out) == norm(out)
  37. assert b"it/s" in err
  38. assert b"Error" not in err
  39. if sys.version_info[:2] >= (3, 8):
  40. test_pipes = mark.filterwarnings("ignore:unclosed file:ResourceWarning")(
  41. test_pipes)
  42. def test_main_import():
  43. """Test main CLI import"""
  44. N = 123
  45. _SYS = sys.stdin, sys.argv
  46. # test direct import
  47. sys.stdin = [str(i).encode() for i in range(N)]
  48. sys.argv = ['', '--desc', 'Test CLI import',
  49. '--ascii', 'True', '--unit_scale', 'True']
  50. try:
  51. import tqdm.__main__ # NOQA, pylint: disable=unused-variable
  52. finally:
  53. sys.stdin, sys.argv = _SYS
  54. @restore_sys
  55. def test_main_bytes(capsysbinary):
  56. """Test CLI --bytes"""
  57. N = 123
  58. # test --delim
  59. IN_DATA = '\0'.join(map(str, range(N))).encode()
  60. with closing(BytesIO()) as sys.stdin:
  61. sys.stdin.write(IN_DATA)
  62. # sys.stdin.write(b'\xff') # TODO
  63. sys.stdin.seek(0)
  64. main(sys.stderr, ['--desc', 'Test CLI delim', '--ascii', 'True',
  65. '--delim', r'\0', '--buf_size', '64'])
  66. out, err = capsysbinary.readouterr()
  67. assert out == IN_DATA
  68. assert str(N) + "it" in err.decode("U8")
  69. # test --bytes
  70. IN_DATA = IN_DATA.replace(b'\0', b'\n')
  71. with closing(BytesIO()) as sys.stdin:
  72. sys.stdin.write(IN_DATA)
  73. sys.stdin.seek(0)
  74. main(sys.stderr, ['--ascii', '--bytes=True', '--unit_scale', 'False'])
  75. out, err = capsysbinary.readouterr()
  76. assert out == IN_DATA
  77. assert str(len(IN_DATA)) + "B" in err.decode("U8")
  78. def test_main_log(capsysbinary, caplog):
  79. """Test CLI --log"""
  80. _SYS = sys.stdin, sys.argv
  81. N = 123
  82. sys.stdin = [(str(i) + '\n').encode() for i in range(N)]
  83. IN_DATA = b''.join(sys.stdin)
  84. try:
  85. with caplog.at_level(logging.INFO):
  86. main(sys.stderr, ['--log', 'INFO'])
  87. out, err = capsysbinary.readouterr()
  88. assert norm(out) == IN_DATA and b"123/123" in err
  89. assert not caplog.record_tuples
  90. with caplog.at_level(logging.DEBUG):
  91. main(sys.stderr, ['--log', 'DEBUG'])
  92. out, err = capsysbinary.readouterr()
  93. assert norm(out) == IN_DATA and b"123/123" in err
  94. assert caplog.record_tuples
  95. finally:
  96. sys.stdin, sys.argv = _SYS
  97. @restore_sys
  98. def test_main(capsysbinary):
  99. """Test misc CLI options"""
  100. N = 123
  101. sys.stdin = [(str(i) + '\n').encode() for i in range(N)]
  102. IN_DATA = b''.join(sys.stdin)
  103. # test --tee
  104. main(sys.stderr, ['--mininterval', '0', '--miniters', '1'])
  105. out, err = capsysbinary.readouterr()
  106. assert norm(out) == IN_DATA and b"123/123" in err
  107. assert N <= len(err.split(b"\r")) < N + 5
  108. len_err = len(err)
  109. main(sys.stderr, ['--tee', '--mininterval', '0', '--miniters', '1'])
  110. out, err = capsysbinary.readouterr()
  111. assert norm(out) == IN_DATA and b"123/123" in err
  112. # spaces to clear intermediate lines could increase length
  113. assert len_err + len(norm(out)) <= len(err)
  114. # test --null
  115. main(sys.stderr, ['--null'])
  116. out, err = capsysbinary.readouterr()
  117. assert not out and b"123/123" in err
  118. # test integer --update
  119. main(sys.stderr, ['--update'])
  120. out, err = capsysbinary.readouterr()
  121. assert norm(out) == IN_DATA
  122. assert (str(N // 2 * N) + "it").encode() in err, "expected arithmetic sum formula"
  123. # test integer --update_to
  124. main(sys.stderr, ['--update-to'])
  125. out, err = capsysbinary.readouterr()
  126. assert norm(out) == IN_DATA
  127. assert (str(N - 1) + "it").encode() in err
  128. assert (str(N) + "it").encode() not in err
  129. with closing(BytesIO()) as sys.stdin:
  130. sys.stdin.write(IN_DATA.replace(b'\n', b'D'))
  131. # test integer --update --delim
  132. sys.stdin.seek(0)
  133. main(sys.stderr, ['--update', '--delim', 'D'])
  134. out, err = capsysbinary.readouterr()
  135. assert out == IN_DATA.replace(b'\n', b'D')
  136. assert (str(N // 2 * N) + "it").encode() in err, "expected arithmetic sum"
  137. # test integer --update_to --delim
  138. sys.stdin.seek(0)
  139. main(sys.stderr, ['--update-to', '--delim', 'D'])
  140. out, err = capsysbinary.readouterr()
  141. assert out == IN_DATA.replace(b'\n', b'D')
  142. assert (str(N - 1) + "it").encode() in err
  143. assert (str(N) + "it").encode() not in err
  144. # test float --update_to
  145. sys.stdin = [(str(i / 2.0) + '\n').encode() for i in range(N)]
  146. IN_DATA = b''.join(sys.stdin)
  147. main(sys.stderr, ['--update-to'])
  148. out, err = capsysbinary.readouterr()
  149. assert norm(out) == IN_DATA
  150. assert (str((N - 1) / 2.0) + "it").encode() in err
  151. assert (str(N / 2.0) + "it").encode() not in err
  152. @mark.slow
  153. @mark.skipif(IS_WIN, reason="no manpages on windows")
  154. def test_manpath(tmp_path):
  155. """Test CLI --manpath"""
  156. man = tmp_path / "tqdm.1"
  157. assert not man.exists()
  158. with raises(SystemExit):
  159. main(argv=['--manpath', str(tmp_path)])
  160. assert man.is_file()
  161. @mark.slow
  162. @mark.skipif(IS_WIN, reason="no completion on windows")
  163. def test_comppath(tmp_path):
  164. """Test CLI --comppath"""
  165. man = tmp_path / "tqdm_completion.sh"
  166. assert not man.exists()
  167. with raises(SystemExit):
  168. main(argv=['--comppath', str(tmp_path)])
  169. assert man.is_file()
  170. # check most important options appear
  171. script = man.read_text()
  172. opts = {'--help', '--desc', '--total', '--leave', '--ncols', '--ascii',
  173. '--dynamic_ncols', '--position', '--bytes', '--nrows', '--delim',
  174. '--manpath', '--comppath'}
  175. assert all(args in script for args in opts)
  176. @restore_sys
  177. def test_exceptions(capsysbinary):
  178. """Test CLI Exceptions"""
  179. N = 123
  180. sys.stdin = [str(i) + '\n' for i in range(N)]
  181. IN_DATA = ''.join(sys.stdin).encode()
  182. with raises(TqdmKeyError, match="bad_arg_u_ment"):
  183. main(sys.stderr, argv=['-ascii', '-unit_scale', '--bad_arg_u_ment', 'foo'])
  184. out, _ = capsysbinary.readouterr()
  185. assert norm(out) == IN_DATA
  186. with raises(TqdmTypeError, match="invalid_bool_value"):
  187. main(sys.stderr, argv=['-ascii', '-unit_scale', 'invalid_bool_value'])
  188. out, _ = capsysbinary.readouterr()
  189. assert norm(out) == IN_DATA
  190. with raises(TqdmTypeError, match="invalid_int_value"):
  191. main(sys.stderr, argv=['-ascii', '--total', 'invalid_int_value'])
  192. out, _ = capsysbinary.readouterr()
  193. assert norm(out) == IN_DATA
  194. with raises(TqdmKeyError, match="Can only have one of --"):
  195. main(sys.stderr, argv=['--update', '--update_to'])
  196. out, _ = capsysbinary.readouterr()
  197. assert norm(out) == IN_DATA
  198. # test SystemExits
  199. for i in ('-h', '--help', '-v', '--version'):
  200. with raises(SystemExit):
  201. main(argv=[i])