test_termui.py 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323
  1. # -*- coding: utf-8 -*-
  2. import time
  3. import pytest
  4. import click._termui_impl
  5. from click._compat import WIN
  6. class FakeClock(object):
  7. def __init__(self):
  8. self.now = time.time()
  9. def advance_time(self, seconds=1):
  10. self.now += seconds
  11. def time(self):
  12. return self.now
  13. def _create_progress(length=10, length_known=True, **kwargs):
  14. progress = click.progressbar(tuple(range(length)))
  15. for key, value in kwargs.items():
  16. setattr(progress, key, value)
  17. progress.length_known = length_known
  18. return progress
  19. def test_progressbar_strip_regression(runner, monkeypatch):
  20. fake_clock = FakeClock()
  21. label = " padded line"
  22. @click.command()
  23. def cli():
  24. with _create_progress(label=label) as progress:
  25. for _ in progress:
  26. fake_clock.advance_time()
  27. monkeypatch.setattr(time, "time", fake_clock.time)
  28. monkeypatch.setattr(click._termui_impl, "isatty", lambda _: True)
  29. assert label in runner.invoke(cli, []).output
  30. def test_progressbar_length_hint(runner, monkeypatch):
  31. class Hinted(object):
  32. def __init__(self, n):
  33. self.items = list(range(n))
  34. def __length_hint__(self):
  35. return len(self.items)
  36. def __iter__(self):
  37. return self
  38. def __next__(self):
  39. if self.items:
  40. return self.items.pop()
  41. else:
  42. raise StopIteration
  43. next = __next__
  44. fake_clock = FakeClock()
  45. @click.command()
  46. def cli():
  47. with click.progressbar(Hinted(10), label="test") as progress:
  48. for _ in progress:
  49. fake_clock.advance_time()
  50. monkeypatch.setattr(time, "time", fake_clock.time)
  51. monkeypatch.setattr(click._termui_impl, "isatty", lambda _: True)
  52. result = runner.invoke(cli, [])
  53. assert result.exception is None
  54. def test_progressbar_hidden(runner, monkeypatch):
  55. fake_clock = FakeClock()
  56. label = "whatever"
  57. @click.command()
  58. def cli():
  59. with _create_progress(label=label) as progress:
  60. for _ in progress:
  61. fake_clock.advance_time()
  62. monkeypatch.setattr(time, "time", fake_clock.time)
  63. monkeypatch.setattr(click._termui_impl, "isatty", lambda _: False)
  64. assert runner.invoke(cli, []).output == ""
  65. @pytest.mark.parametrize("avg, expected", [([], 0.0), ([1, 4], 2.5)])
  66. def test_progressbar_time_per_iteration(runner, avg, expected):
  67. with _create_progress(2, avg=avg) as progress:
  68. assert progress.time_per_iteration == expected
  69. @pytest.mark.parametrize("finished, expected", [(False, 5), (True, 0)])
  70. def test_progressbar_eta(runner, finished, expected):
  71. with _create_progress(2, finished=finished, avg=[1, 4]) as progress:
  72. assert progress.eta == expected
  73. @pytest.mark.parametrize(
  74. "eta, expected",
  75. [
  76. (0, "00:00:00"),
  77. (30, "00:00:30"),
  78. (90, "00:01:30"),
  79. (900, "00:15:00"),
  80. (9000, "02:30:00"),
  81. (99999999999, "1157407d 09:46:39"),
  82. (None, ""),
  83. ],
  84. )
  85. def test_progressbar_format_eta(runner, eta, expected):
  86. with _create_progress(1, eta_known=eta is not None, avg=[eta]) as progress:
  87. assert progress.format_eta() == expected
  88. @pytest.mark.parametrize("pos, length", [(0, 5), (-1, 1), (5, 5), (6, 5), (4, 0)])
  89. def test_progressbar_format_pos(runner, pos, length):
  90. with _create_progress(length, length_known=length != 0, pos=pos) as progress:
  91. result = progress.format_pos()
  92. if progress.length_known:
  93. assert result == "{}/{}".format(pos, length)
  94. else:
  95. assert result == str(pos)
  96. @pytest.mark.parametrize(
  97. "length, finished, pos, avg, expected",
  98. [
  99. (8, False, 7, 0, "#######-"),
  100. (0, True, 8, 0, "########"),
  101. (0, False, 8, 0, "--------"),
  102. (0, False, 5, 3, "#-------"),
  103. ],
  104. )
  105. def test_progressbar_format_bar(runner, length, finished, pos, avg, expected):
  106. with _create_progress(
  107. length, length_known=length != 0, width=8, pos=pos, finished=finished, avg=[avg]
  108. ) as progress:
  109. assert progress.format_bar() == expected
  110. @pytest.mark.parametrize(
  111. "length, length_known, show_percent, show_pos, pos, expected",
  112. [
  113. (0, True, True, True, 0, " [--------] 0/0 0%"),
  114. (0, True, False, True, 0, " [--------] 0/0"),
  115. (0, True, False, False, 0, " [--------]"),
  116. (0, False, False, False, 0, " [--------]"),
  117. (8, True, True, True, 8, " [########] 8/8 100%"),
  118. ],
  119. )
  120. def test_progressbar_format_progress_line(
  121. runner, length, length_known, show_percent, show_pos, pos, expected
  122. ):
  123. with _create_progress(
  124. length,
  125. length_known,
  126. width=8,
  127. show_percent=show_percent,
  128. pos=pos,
  129. show_pos=show_pos,
  130. ) as progress:
  131. assert progress.format_progress_line() == expected
  132. @pytest.mark.parametrize("test_item", ["test", None])
  133. def test_progressbar_format_progress_line_with_show_func(runner, test_item):
  134. def item_show_func(item):
  135. return item
  136. with _create_progress(
  137. item_show_func=item_show_func, current_item=test_item
  138. ) as progress:
  139. if test_item:
  140. assert progress.format_progress_line().endswith(test_item)
  141. else:
  142. assert progress.format_progress_line().endswith(progress.format_pct())
  143. def test_progressbar_init_exceptions(runner):
  144. with pytest.raises(TypeError, match="iterable or length is required"):
  145. click.progressbar()
  146. def test_progressbar_iter_outside_with_exceptions(runner):
  147. with pytest.raises(RuntimeError, match="with block"):
  148. progress = click.progressbar(length=2)
  149. iter(progress)
  150. def test_progressbar_is_iterator(runner, monkeypatch):
  151. fake_clock = FakeClock()
  152. @click.command()
  153. def cli():
  154. with click.progressbar(range(10), label="test") as progress:
  155. while True:
  156. try:
  157. next(progress)
  158. fake_clock.advance_time()
  159. except StopIteration:
  160. break
  161. monkeypatch.setattr(time, "time", fake_clock.time)
  162. monkeypatch.setattr(click._termui_impl, "isatty", lambda _: True)
  163. result = runner.invoke(cli, [])
  164. assert result.exception is None
  165. def test_choices_list_in_prompt(runner, monkeypatch):
  166. @click.command()
  167. @click.option(
  168. "-g", type=click.Choice(["none", "day", "week", "month"]), prompt=True
  169. )
  170. def cli_with_choices(g):
  171. pass
  172. @click.command()
  173. @click.option(
  174. "-g",
  175. type=click.Choice(["none", "day", "week", "month"]),
  176. prompt=True,
  177. show_choices=False,
  178. )
  179. def cli_without_choices(g):
  180. pass
  181. result = runner.invoke(cli_with_choices, [], input="none")
  182. assert "(none, day, week, month)" in result.output
  183. result = runner.invoke(cli_without_choices, [], input="none")
  184. assert "(none, day, week, month)" not in result.output
  185. @pytest.mark.skip
  186. @pytest.mark.parametrize(
  187. "file_kwargs", [{"mode": "rt"}, {"mode": "rb"}, {"lazy": True}]
  188. )
  189. def test_file_prompt_default_format(runner, file_kwargs):
  190. @click.command()
  191. @click.option("-f", default=__file__, prompt="file", type=click.File(**file_kwargs))
  192. def cli(f):
  193. click.echo(f.name)
  194. result = runner.invoke(cli)
  195. assert result.output == "file [{0}]: \n{0}\n".format(__file__)
  196. def test_secho(runner):
  197. with runner.isolation() as outstreams:
  198. click.secho(None, nl=False)
  199. bytes = outstreams[0].getvalue()
  200. assert bytes == b""
  201. def test_progressbar_yields_all_items(runner):
  202. with click.progressbar(range(3)) as progress:
  203. assert len(list(progress)) == 3
  204. def test_progressbar_update(runner, monkeypatch):
  205. fake_clock = FakeClock()
  206. @click.command()
  207. def cli():
  208. with click.progressbar(range(4)) as progress:
  209. for _ in progress:
  210. fake_clock.advance_time()
  211. print("")
  212. monkeypatch.setattr(time, "time", fake_clock.time)
  213. monkeypatch.setattr(click._termui_impl, "isatty", lambda _: True)
  214. output = runner.invoke(cli, []).output
  215. lines = [line for line in output.split("\n") if "[" in line]
  216. assert " 25% 00:00:03" in lines[0]
  217. assert " 50% 00:00:02" in lines[1]
  218. assert " 75% 00:00:01" in lines[2]
  219. assert "100% " in lines[3]
  220. @pytest.mark.parametrize("key_char", (u"h", u"H", u"é", u"À", u" ", u"字", u"àH", u"àR"))
  221. @pytest.mark.parametrize("echo", [True, False])
  222. @pytest.mark.skipif(not WIN, reason="Tests user-input using the msvcrt module.")
  223. def test_getchar_windows(runner, monkeypatch, key_char, echo):
  224. monkeypatch.setattr(click._termui_impl.msvcrt, "getwche", lambda: key_char)
  225. monkeypatch.setattr(click._termui_impl.msvcrt, "getwch", lambda: key_char)
  226. monkeypatch.setattr(click.termui, "_getchar", None)
  227. assert click.getchar(echo) == key_char
  228. @pytest.mark.parametrize(
  229. "special_key_char, key_char", [(u"\x00", "a"), (u"\x00", "b"), (u"\xe0", "c")]
  230. )
  231. @pytest.mark.skipif(
  232. not WIN, reason="Tests special character inputs using the msvcrt module."
  233. )
  234. def test_getchar_special_key_windows(runner, monkeypatch, special_key_char, key_char):
  235. ordered_inputs = [key_char, special_key_char]
  236. monkeypatch.setattr(
  237. click._termui_impl.msvcrt, "getwch", lambda: ordered_inputs.pop()
  238. )
  239. monkeypatch.setattr(click.termui, "_getchar", None)
  240. assert click.getchar() == special_key_char + key_char
  241. @pytest.mark.parametrize(
  242. ("key_char", "exc"), [(u"\x03", KeyboardInterrupt), (u"\x1a", EOFError)],
  243. )
  244. @pytest.mark.skipif(not WIN, reason="Tests user-input using the msvcrt module.")
  245. def test_getchar_windows_exceptions(runner, monkeypatch, key_char, exc):
  246. monkeypatch.setattr(click._termui_impl.msvcrt, "getwch", lambda: key_char)
  247. monkeypatch.setattr(click.termui, "_getchar", None)
  248. with pytest.raises(exc):
  249. click.getchar()