summaryrefslogtreecommitdiffstats
path: root/tests/tests_main.py
diff options
context:
space:
mode:
Diffstat (limited to 'tests/tests_main.py')
-rw-r--r--tests/tests_main.py245
1 files changed, 245 insertions, 0 deletions
diff --git a/tests/tests_main.py b/tests/tests_main.py
new file mode 100644
index 0000000..0523cc7
--- /dev/null
+++ b/tests/tests_main.py
@@ -0,0 +1,245 @@
+"""Test CLI usage."""
+import logging
+import subprocess # nosec
+import sys
+from functools import wraps
+from os import linesep
+
+from tqdm.cli import TqdmKeyError, TqdmTypeError, main
+from tqdm.utils import IS_WIN
+
+from .tests_tqdm import BytesIO, _range, closing, mark, raises
+
+
+def restore_sys(func):
+ """Decorates `func(capsysbin)` to save & restore `sys.(stdin|argv)`."""
+ @wraps(func)
+ def inner(capsysbin):
+ """function requiring capsysbin which may alter `sys.(stdin|argv)`"""
+ _SYS = sys.stdin, sys.argv
+ try:
+ res = func(capsysbin)
+ finally:
+ sys.stdin, sys.argv = _SYS
+ return res
+
+ return inner
+
+
+def norm(bytestr):
+ """Normalise line endings."""
+ return bytestr if linesep == "\n" else bytestr.replace(linesep.encode(), b"\n")
+
+
+@mark.slow
+def test_pipes():
+ """Test command line pipes"""
+ ls_out = subprocess.check_output(['ls']) # nosec
+ ls = subprocess.Popen(['ls'], stdout=subprocess.PIPE) # nosec
+ res = subprocess.Popen( # nosec
+ [sys.executable, '-c', 'from tqdm.cli import main; main()'],
+ stdin=ls.stdout, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ out, err = res.communicate()
+ assert ls.poll() == 0
+
+ # actual test:
+ assert norm(ls_out) == norm(out)
+ assert b"it/s" in err
+ assert b"Error" not in err
+
+
+if sys.version_info[:2] >= (3, 8):
+ test_pipes = mark.filterwarnings("ignore:unclosed file:ResourceWarning")(
+ test_pipes)
+
+
+def test_main_import():
+ """Test main CLI import"""
+ N = 123
+ _SYS = sys.stdin, sys.argv
+ # test direct import
+ sys.stdin = [str(i).encode() for i in _range(N)]
+ sys.argv = ['', '--desc', 'Test CLI import',
+ '--ascii', 'True', '--unit_scale', 'True']
+ try:
+ import tqdm.__main__ # NOQA, pylint: disable=unused-variable
+ finally:
+ sys.stdin, sys.argv = _SYS
+
+
+@restore_sys
+def test_main_bytes(capsysbin):
+ """Test CLI --bytes"""
+ N = 123
+
+ # test --delim
+ IN_DATA = '\0'.join(map(str, _range(N))).encode()
+ with closing(BytesIO()) as sys.stdin:
+ sys.stdin.write(IN_DATA)
+ # sys.stdin.write(b'\xff') # TODO
+ sys.stdin.seek(0)
+ main(sys.stderr, ['--desc', 'Test CLI delim', '--ascii', 'True',
+ '--delim', r'\0', '--buf_size', '64'])
+ out, err = capsysbin.readouterr()
+ assert out == IN_DATA
+ assert str(N) + "it" in err.decode("U8")
+
+ # test --bytes
+ IN_DATA = IN_DATA.replace(b'\0', b'\n')
+ with closing(BytesIO()) as sys.stdin:
+ sys.stdin.write(IN_DATA)
+ sys.stdin.seek(0)
+ main(sys.stderr, ['--ascii', '--bytes=True', '--unit_scale', 'False'])
+ out, err = capsysbin.readouterr()
+ assert out == IN_DATA
+ assert str(len(IN_DATA)) + "B" in err.decode("U8")
+
+
+@mark.skipif(sys.version_info[0] == 2, reason="no caplog on py2")
+def test_main_log(capsysbin, caplog):
+ """Test CLI --log"""
+ _SYS = sys.stdin, sys.argv
+ N = 123
+ sys.stdin = [(str(i) + '\n').encode() for i in _range(N)]
+ IN_DATA = b''.join(sys.stdin)
+ try:
+ with caplog.at_level(logging.INFO):
+ main(sys.stderr, ['--log', 'INFO'])
+ out, err = capsysbin.readouterr()
+ assert norm(out) == IN_DATA and b"123/123" in err
+ assert not caplog.record_tuples
+ with caplog.at_level(logging.DEBUG):
+ main(sys.stderr, ['--log', 'DEBUG'])
+ out, err = capsysbin.readouterr()
+ assert norm(out) == IN_DATA and b"123/123" in err
+ assert caplog.record_tuples
+ finally:
+ sys.stdin, sys.argv = _SYS
+
+
+@restore_sys
+def test_main(capsysbin):
+ """Test misc CLI options"""
+ N = 123
+ sys.stdin = [(str(i) + '\n').encode() for i in _range(N)]
+ IN_DATA = b''.join(sys.stdin)
+
+ # test --tee
+ main(sys.stderr, ['--mininterval', '0', '--miniters', '1'])
+ out, err = capsysbin.readouterr()
+ assert norm(out) == IN_DATA and b"123/123" in err
+ assert N <= len(err.split(b"\r")) < N + 5
+
+ len_err = len(err)
+ main(sys.stderr, ['--tee', '--mininterval', '0', '--miniters', '1'])
+ out, err = capsysbin.readouterr()
+ assert norm(out) == IN_DATA and b"123/123" in err
+ # spaces to clear intermediate lines could increase length
+ assert len_err + len(norm(out)) <= len(err)
+
+ # test --null
+ main(sys.stderr, ['--null'])
+ out, err = capsysbin.readouterr()
+ assert not out and b"123/123" in err
+
+ # test integer --update
+ main(sys.stderr, ['--update'])
+ out, err = capsysbin.readouterr()
+ assert norm(out) == IN_DATA
+ assert (str(N // 2 * N) + "it").encode() in err, "expected arithmetic sum formula"
+
+ # test integer --update_to
+ main(sys.stderr, ['--update-to'])
+ out, err = capsysbin.readouterr()
+ assert norm(out) == IN_DATA
+ assert (str(N - 1) + "it").encode() in err
+ assert (str(N) + "it").encode() not in err
+
+ with closing(BytesIO()) as sys.stdin:
+ sys.stdin.write(IN_DATA.replace(b'\n', b'D'))
+
+ # test integer --update --delim
+ sys.stdin.seek(0)
+ main(sys.stderr, ['--update', '--delim', 'D'])
+ out, err = capsysbin.readouterr()
+ assert out == IN_DATA.replace(b'\n', b'D')
+ assert (str(N // 2 * N) + "it").encode() in err, "expected arithmetic sum"
+
+ # test integer --update_to --delim
+ sys.stdin.seek(0)
+ main(sys.stderr, ['--update-to', '--delim', 'D'])
+ out, err = capsysbin.readouterr()
+ assert out == IN_DATA.replace(b'\n', b'D')
+ assert (str(N - 1) + "it").encode() in err
+ assert (str(N) + "it").encode() not in err
+
+ # test float --update_to
+ sys.stdin = [(str(i / 2.0) + '\n').encode() for i in _range(N)]
+ IN_DATA = b''.join(sys.stdin)
+ main(sys.stderr, ['--update-to'])
+ out, err = capsysbin.readouterr()
+ assert norm(out) == IN_DATA
+ assert (str((N - 1) / 2.0) + "it").encode() in err
+ assert (str(N / 2.0) + "it").encode() not in err
+
+
+@mark.slow
+@mark.skipif(IS_WIN, reason="no manpages on windows")
+def test_manpath(tmp_path):
+ """Test CLI --manpath"""
+ man = tmp_path / "tqdm.1"
+ assert not man.exists()
+ with raises(SystemExit):
+ main(argv=['--manpath', str(tmp_path)])
+ assert man.is_file()
+
+
+@mark.slow
+@mark.skipif(IS_WIN, reason="no completion on windows")
+def test_comppath(tmp_path):
+ """Test CLI --comppath"""
+ man = tmp_path / "tqdm_completion.sh"
+ assert not man.exists()
+ with raises(SystemExit):
+ main(argv=['--comppath', str(tmp_path)])
+ assert man.is_file()
+
+ # check most important options appear
+ script = man.read_text()
+ opts = {'--help', '--desc', '--total', '--leave', '--ncols', '--ascii',
+ '--dynamic_ncols', '--position', '--bytes', '--nrows', '--delim',
+ '--manpath', '--comppath'}
+ assert all(args in script for args in opts)
+
+
+@restore_sys
+def test_exceptions(capsysbin):
+ """Test CLI Exceptions"""
+ N = 123
+ sys.stdin = [str(i) + '\n' for i in _range(N)]
+ IN_DATA = ''.join(sys.stdin).encode()
+
+ with raises(TqdmKeyError, match="bad_arg_u_ment"):
+ main(sys.stderr, argv=['-ascii', '-unit_scale', '--bad_arg_u_ment', 'foo'])
+ out, _ = capsysbin.readouterr()
+ assert norm(out) == IN_DATA
+
+ with raises(TqdmTypeError, match="invalid_bool_value"):
+ main(sys.stderr, argv=['-ascii', '-unit_scale', 'invalid_bool_value'])
+ out, _ = capsysbin.readouterr()
+ assert norm(out) == IN_DATA
+
+ with raises(TqdmTypeError, match="invalid_int_value"):
+ main(sys.stderr, argv=['-ascii', '--total', 'invalid_int_value'])
+ out, _ = capsysbin.readouterr()
+ assert norm(out) == IN_DATA
+
+ with raises(TqdmKeyError, match="Can only have one of --"):
+ main(sys.stderr, argv=['--update', '--update_to'])
+ out, _ = capsysbin.readouterr()
+ assert norm(out) == IN_DATA
+
+ # test SystemExits
+ for i in ('-h', '--help', '-v', '--version'):
+ with raises(SystemExit):
+ main(argv=[i])