diff options
author | Devtools Arcadia <arcadia-devtools@yandex-team.ru> | 2022-02-07 18:08:42 +0300 |
---|---|---|
committer | Devtools Arcadia <arcadia-devtools@mous.vla.yp-c.yandex.net> | 2022-02-07 18:08:42 +0300 |
commit | 1110808a9d39d4b808aef724c861a2e1a38d2a69 (patch) | |
tree | e26c9fed0de5d9873cce7e00bc214573dc2195b7 /contrib/python/prompt-toolkit/py2/tests | |
download | ydb-1110808a9d39d4b808aef724c861a2e1a38d2a69.tar.gz |
intermediate changes
ref:cde9a383711a11544ce7e107a78147fb96cc4029
Diffstat (limited to 'contrib/python/prompt-toolkit/py2/tests')
15 files changed, 1980 insertions, 0 deletions
diff --git a/contrib/python/prompt-toolkit/py2/tests/test_buffer.py b/contrib/python/prompt-toolkit/py2/tests/test_buffer.py new file mode 100644 index 00000000000..a9cff190240 --- /dev/null +++ b/contrib/python/prompt-toolkit/py2/tests/test_buffer.py @@ -0,0 +1,111 @@ +from __future__ import unicode_literals + +from prompt_toolkit.buffer import Buffer + +import pytest + + +@pytest.fixture +def _buffer(): + return Buffer() + + +def test_initial(_buffer): + assert _buffer.text == '' + assert _buffer.cursor_position == 0 + + +def test_insert_text(_buffer): + _buffer.insert_text('some_text') + assert _buffer.text == 'some_text' + assert _buffer.cursor_position == len('some_text') + + +def test_cursor_movement(_buffer): + _buffer.insert_text('some_text') + _buffer.cursor_left() + _buffer.cursor_left() + _buffer.cursor_left() + _buffer.cursor_right() + _buffer.insert_text('A') + + assert _buffer.text == 'some_teAxt' + assert _buffer.cursor_position == len('some_teA') + + +def test_backspace(_buffer): + _buffer.insert_text('some_text') + _buffer.cursor_left() + _buffer.cursor_left() + _buffer.delete_before_cursor() + + assert _buffer.text == 'some_txt' + assert _buffer.cursor_position == len('some_t') + + +def test_cursor_up(_buffer): + # Cursor up to a line thats longer. + _buffer.insert_text('long line1\nline2') + _buffer.cursor_up() + + assert _buffer.document.cursor_position == 5 + + # Going up when already at the top. + _buffer.cursor_up() + assert _buffer.document.cursor_position == 5 + + # Going up to a line that's shorter. + _buffer.reset() + _buffer.insert_text('line1\nlong line2') + + _buffer.cursor_up() + assert _buffer.document.cursor_position == 5 + + +def test_cursor_down(_buffer): + _buffer.insert_text('line1\nline2') + _buffer.cursor_position = 3 + + # Normally going down + _buffer.cursor_down() + assert _buffer.document.cursor_position == len('line1\nlin') + + # Going down to a line that's storter. + _buffer.reset() + _buffer.insert_text('long line1\na\nb') + _buffer.cursor_position = 3 + + _buffer.cursor_down() + assert _buffer.document.cursor_position == len('long line1\na') + + +def test_join_next_line(_buffer): + _buffer.insert_text('line1\nline2\nline3') + _buffer.cursor_up() + _buffer.join_next_line() + + assert _buffer.text == 'line1\nline2 line3' + + # Test when there is no '\n' in the text + _buffer.reset() + _buffer.insert_text('line1') + _buffer.cursor_position = 0 + _buffer.join_next_line() + + assert _buffer.text == 'line1' + + +def test_newline(_buffer): + _buffer.insert_text('hello world') + _buffer.newline() + + assert _buffer.text == 'hello world\n' + + +def test_swap_characters_before_cursor(_buffer): + _buffer.insert_text('hello world') + _buffer.cursor_left() + _buffer.cursor_left() + _buffer.swap_characters_before_cursor() + + assert _buffer.text == 'hello wrold' diff --git a/contrib/python/prompt-toolkit/py2/tests/test_cli.py b/contrib/python/prompt-toolkit/py2/tests/test_cli.py new file mode 100644 index 00000000000..68ca3d03f05 --- /dev/null +++ b/contrib/python/prompt-toolkit/py2/tests/test_cli.py @@ -0,0 +1,629 @@ +# encoding: utf-8 +""" +These are almost end-to-end tests. They create a CommandLineInterface +instance, feed it with some input and check the result. +""" +from __future__ import unicode_literals +from prompt_toolkit.application import Application +from prompt_toolkit.buffer import Buffer, AcceptAction +from prompt_toolkit.clipboard import InMemoryClipboard, ClipboardData +from prompt_toolkit.enums import DEFAULT_BUFFER, EditingMode +from prompt_toolkit.eventloop.posix import PosixEventLoop +from prompt_toolkit.history import InMemoryHistory +from prompt_toolkit.input import PipeInput +from prompt_toolkit.interface import CommandLineInterface +from prompt_toolkit.key_binding.manager import KeyBindingManager +from prompt_toolkit.output import DummyOutput +from prompt_toolkit.terminal.vt100_input import ANSI_SEQUENCES +from functools import partial +import pytest + + +def _history(): + h = InMemoryHistory() + h.append('line1 first input') + h.append('line2 second input') + h.append('line3 third input') + return h + + +def _feed_cli_with_input(text, editing_mode=EditingMode.EMACS, clipboard=None, + history=None, multiline=False, check_line_ending=True, + pre_run_callback=None): + """ + Create a CommandLineInterface, feed it with the given user input and return + the CLI object. + + This returns a (result, CLI) tuple. + """ + # If the given text doesn't end with a newline, the interface won't finish. + if check_line_ending: + assert text.endswith('\n') + + loop = PosixEventLoop() + try: + inp = PipeInput() + inp.send_text(text) + cli = CommandLineInterface( + application=Application( + buffer=Buffer(accept_action=AcceptAction.RETURN_DOCUMENT, + history=history, is_multiline=multiline), + editing_mode=editing_mode, + clipboard=clipboard or InMemoryClipboard(), + key_bindings_registry=KeyBindingManager.for_prompt().registry, + ), + eventloop=loop, + input=inp, + output=DummyOutput()) + + if pre_run_callback: + pre_run_callback(cli) + + result = cli.run() + return result, cli + finally: + loop.close() + inp.close() + + +def test_simple_text_input(): + # Simple text input, followed by enter. + result, cli = _feed_cli_with_input('hello\n') + assert result.text == 'hello' + assert cli.buffers[DEFAULT_BUFFER].text == 'hello' + + +def test_emacs_cursor_movements(): + """ + Test cursor movements with Emacs key bindings. + """ + # ControlA (beginning-of-line) + result, cli = _feed_cli_with_input('hello\x01X\n') + assert result.text == 'Xhello' + + # ControlE (end-of-line) + result, cli = _feed_cli_with_input('hello\x01X\x05Y\n') + assert result.text == 'XhelloY' + + # ControlH or \b + result, cli = _feed_cli_with_input('hello\x08X\n') + assert result.text == 'hellX' + + # Delete. (Left, left, delete) + result, cli = _feed_cli_with_input('hello\x1b[D\x1b[D\x1b[3~\n') + assert result.text == 'helo' + + # Left. + result, cli = _feed_cli_with_input('hello\x1b[DX\n') + assert result.text == 'hellXo' + + # ControlA, right + result, cli = _feed_cli_with_input('hello\x01\x1b[CX\n') + assert result.text == 'hXello' + + # ControlA, right + result, cli = _feed_cli_with_input('hello\x01\x1b[CX\n') + assert result.text == 'hXello' + + # ControlB (backward-char) + result, cli = _feed_cli_with_input('hello\x02X\n') + assert result.text == 'hellXo' + + # ControlF (forward-char) + result, cli = _feed_cli_with_input('hello\x01\x06X\n') + assert result.text == 'hXello' + + # ControlC: raise KeyboardInterrupt. + with pytest.raises(KeyboardInterrupt): + result, cli = _feed_cli_with_input('hello\x03\n') + assert result.text == 'hello' + + # ControlD without any input: raises EOFError. + with pytest.raises(EOFError): + result, cli = _feed_cli_with_input('\x04\n') + assert result.text == 'hello' + + # ControlD: delete after cursor. + result, cli = _feed_cli_with_input('hello\x01\x04\n') + assert result.text == 'ello' + + # ControlD at the end of the input ssshould not do anything. + result, cli = _feed_cli_with_input('hello\x04\n') + assert result.text == 'hello' + + # Left, Left, ControlK (kill-line) + result, cli = _feed_cli_with_input('hello\x1b[D\x1b[D\x0b\n') + assert result.text == 'hel' + + # Left, Left Esc- ControlK (kill-line, but negative) + result, cli = _feed_cli_with_input('hello\x1b[D\x1b[D\x1b-\x0b\n') + assert result.text == 'lo' + + # ControlL: should not influence the result. + result, cli = _feed_cli_with_input('hello\x0c\n') + assert result.text == 'hello' + + # ControlRight (forward-word) + result, cli = _feed_cli_with_input('hello world\x01X\x1b[1;5CY\n') + assert result.text == 'XhelloY world' + + # ContrlolLeft (backward-word) + result, cli = _feed_cli_with_input('hello world\x1b[1;5DY\n') + assert result.text == 'hello Yworld' + + # <esc>-f with argument. (forward-word) + result, cli = _feed_cli_with_input('hello world abc def\x01\x1b3\x1bfX\n') + assert result.text == 'hello world abcX def' + + # <esc>-f with negative argument. (forward-word) + result, cli = _feed_cli_with_input('hello world abc def\x1b-\x1b3\x1bfX\n') + assert result.text == 'hello Xworld abc def' + + # <esc>-b with argument. (backward-word) + result, cli = _feed_cli_with_input('hello world abc def\x1b3\x1bbX\n') + assert result.text == 'hello Xworld abc def' + + # <esc>-b with negative argument. (backward-word) + result, cli = _feed_cli_with_input('hello world abc def\x01\x1b-\x1b3\x1bbX\n') + assert result.text == 'hello world abc Xdef' + + # ControlW (kill-word / unix-word-rubout) + result, cli = _feed_cli_with_input('hello world\x17\n') + assert result.text == 'hello ' + assert cli.clipboard.get_data().text == 'world' + + result, cli = _feed_cli_with_input('test hello world\x1b2\x17\n') + assert result.text == 'test ' + + # Escape Backspace (unix-word-rubout) + result, cli = _feed_cli_with_input('hello world\x1b\x7f\n') + assert result.text == 'hello ' + assert cli.clipboard.get_data().text == 'world' + + result, cli = _feed_cli_with_input('hello world\x1b\x08\n') + assert result.text == 'hello ' + assert cli.clipboard.get_data().text == 'world' + + # Backspace (backward-delete-char) + result, cli = _feed_cli_with_input('hello world\x7f\n') + assert result.text == 'hello worl' + assert result.cursor_position == len('hello worl') + + result, cli = _feed_cli_with_input('hello world\x08\n') + assert result.text == 'hello worl' + assert result.cursor_position == len('hello worl') + + # Delete (delete-char) + result, cli = _feed_cli_with_input('hello world\x01\x1b[3~\n') + assert result.text == 'ello world' + assert result.cursor_position == 0 + + # Escape-\\ (delete-horizontal-space) + result, cli = _feed_cli_with_input('hello world\x1b8\x02\x1b\\\n') + assert result.text == 'helloworld' + assert result.cursor_position == len('hello') + + +def test_emacs_yank(): + # ControlY (yank) + c = InMemoryClipboard(ClipboardData('XYZ')) + result, cli = _feed_cli_with_input('hello\x02\x19\n', clipboard=c) + assert result.text == 'hellXYZo' + assert result.cursor_position == len('hellXYZ') + + +def test_quoted_insert(): + # ControlQ - ControlB (quoted-insert) + result, cli = _feed_cli_with_input('hello\x11\x02\n') + assert result.text == 'hello\x02' + + +def test_transformations(): + # Meta-c (capitalize-word) + result, cli = _feed_cli_with_input('hello world\01\x1bc\n') + assert result.text == 'Hello world' + assert result.cursor_position == len('Hello') + + # Meta-u (uppercase-word) + result, cli = _feed_cli_with_input('hello world\01\x1bu\n') + assert result.text == 'HELLO world' + assert result.cursor_position == len('Hello') + + # Meta-u (downcase-word) + result, cli = _feed_cli_with_input('HELLO WORLD\01\x1bl\n') + assert result.text == 'hello WORLD' + assert result.cursor_position == len('Hello') + + # ControlT (transpose-chars) + result, cli = _feed_cli_with_input('hello\x14\n') + assert result.text == 'helol' + assert result.cursor_position == len('hello') + + # Left, Left, Control-T (transpose-chars) + result, cli = _feed_cli_with_input('abcde\x1b[D\x1b[D\x14\n') + assert result.text == 'abdce' + assert result.cursor_position == len('abcd') + + +def test_emacs_other_bindings(): + # Transpose characters. + result, cli = _feed_cli_with_input('abcde\x14X\n') # Ctrl-T + assert result.text == 'abcedX' + + # Left, Left, Transpose. (This is slightly different.) + result, cli = _feed_cli_with_input('abcde\x1b[D\x1b[D\x14X\n') + assert result.text == 'abdcXe' + + # Clear before cursor. + result, cli = _feed_cli_with_input('hello\x1b[D\x1b[D\x15X\n') + assert result.text == 'Xlo' + + # unix-word-rubout: delete word before the cursor. + # (ControlW). + result, cli = _feed_cli_with_input('hello world test\x17X\n') + assert result.text == 'hello world X' + + result, cli = _feed_cli_with_input('hello world /some/very/long/path\x17X\n') + assert result.text == 'hello world X' + + # (with argument.) + result, cli = _feed_cli_with_input('hello world test\x1b2\x17X\n') + assert result.text == 'hello X' + + result, cli = _feed_cli_with_input('hello world /some/very/long/path\x1b2\x17X\n') + assert result.text == 'hello X' + + # backward-kill-word: delete word before the cursor. + # (Esc-ControlH). + result, cli = _feed_cli_with_input('hello world /some/very/long/path\x1b\x08X\n') + assert result.text == 'hello world /some/very/long/X' + + # (with arguments.) + result, cli = _feed_cli_with_input('hello world /some/very/long/path\x1b3\x1b\x08X\n') + assert result.text == 'hello world /some/very/X' + + +def test_controlx_controlx(): + # At the end: go to the start of the line. + result, cli = _feed_cli_with_input('hello world\x18\x18X\n') + assert result.text == 'Xhello world' + assert result.cursor_position == 1 + + # At the start: go to the end of the line. + result, cli = _feed_cli_with_input('hello world\x01\x18\x18X\n') + assert result.text == 'hello worldX' + + # Left, Left Control-X Control-X: go to the end of the line. + result, cli = _feed_cli_with_input('hello world\x1b[D\x1b[D\x18\x18X\n') + assert result.text == 'hello worldX' + + +def test_emacs_history_bindings(): + # Adding a new item to the history. + history = _history() + result, cli = _feed_cli_with_input('new input\n', history=history) + assert result.text == 'new input' + history.strings[-1] == 'new input' + + # Go up in history, and accept the last item. + result, cli = _feed_cli_with_input('hello\x1b[A\n', history=history) + assert result.text == 'new input' + + # Esc< (beginning-of-history) + result, cli = _feed_cli_with_input('hello\x1b<\n', history=history) + assert result.text == 'line1 first input' + + # Esc> (end-of-history) + result, cli = _feed_cli_with_input('another item\x1b[A\x1b[a\x1b>\n', history=history) + assert result.text == 'another item' + + # ControlUp (previous-history) + result, cli = _feed_cli_with_input('\x1b[1;5A\n', history=history) + assert result.text == 'another item' + + # Esc< ControlDown (beginning-of-history, next-history) + result, cli = _feed_cli_with_input('\x1b<\x1b[1;5B\n', history=history) + assert result.text == 'line2 second input' + + +def test_emacs_reverse_search(): + history = _history() + + # ControlR (reverse-search-history) + result, cli = _feed_cli_with_input('\x12input\n\n', history=history) + assert result.text == 'line3 third input' + + # Hitting ControlR twice. + result, cli = _feed_cli_with_input('\x12input\x12\n\n', history=history) + assert result.text == 'line2 second input' + + +def test_emacs_arguments(): + """ + Test various combinations of arguments in Emacs mode. + """ + # esc 4 + result, cli = _feed_cli_with_input('\x1b4x\n') + assert result.text == 'xxxx' + + # esc 4 4 + result, cli = _feed_cli_with_input('\x1b44x\n') + assert result.text == 'x' * 44 + + # esc 4 esc 4 + result, cli = _feed_cli_with_input('\x1b4\x1b4x\n') + assert result.text == 'x' * 44 + + # esc - right (-1 position to the right, equals 1 to the left.) + result, cli = _feed_cli_with_input('aaaa\x1b-\x1b[Cbbbb\n') + assert result.text == 'aaabbbba' + + # esc - 3 right + result, cli = _feed_cli_with_input('aaaa\x1b-3\x1b[Cbbbb\n') + assert result.text == 'abbbbaaa' + + # esc - - - 3 right + result, cli = _feed_cli_with_input('aaaa\x1b---3\x1b[Cbbbb\n') + assert result.text == 'abbbbaaa' + + +def test_emacs_arguments_for_all_commands(): + """ + Test all Emacs commands with Meta-[0-9] arguments (both positive and + negative). No one should crash. + """ + for key in ANSI_SEQUENCES: + # Ignore BracketedPaste. This would hang forever, because it waits for + # the end sequence. + if key != '\x1b[200~': + try: + # Note: we add an 'X' after the key, because Ctrl-Q (quoted-insert) + # expects something to follow. We add an additional \n, because + # Ctrl-R and Ctrl-S (reverse-search) expect that. + result, cli = _feed_cli_with_input( + 'hello\x1b4' + key + 'X\n\n') + + result, cli = _feed_cli_with_input( + 'hello\x1b-' + key + 'X\n\n') + except KeyboardInterrupt: + # This exception should only be raised for Ctrl-C + assert key == '\x03' + + +def test_emacs_kill_ring(): + operations = ( + # abc ControlA ControlK + 'abc\x01\x0b' + + # def ControlA ControlK + 'def\x01\x0b' + + # ghi ControlA ControlK + 'ghi\x01\x0b' + + # ControlY (yank) + '\x19' + ) + + result, cli = _feed_cli_with_input(operations + '\n') + assert result.text == 'ghi' + + result, cli = _feed_cli_with_input(operations + '\x1by\n') + assert result.text == 'def' + + result, cli = _feed_cli_with_input(operations + '\x1by\x1by\n') + assert result.text == 'abc' + + result, cli = _feed_cli_with_input(operations + '\x1by\x1by\x1by\n') + assert result.text == 'ghi' + + +def test_emacs_insert_comment(): + # Test insert-comment (M-#) binding. + result, cli = _feed_cli_with_input('hello\x1b#', check_line_ending=False) + assert result.text == '#hello' + + result, cli = _feed_cli_with_input( + 'hello\nworld\x1b#', check_line_ending=False, multiline=True) + assert result.text == '#hello\n#world' + + +def test_emacs_record_macro(): + operations = ( + ' ' + '\x18(' # Start recording macro. C-X( + 'hello' + '\x18)' # Stop recording macro. + ' ' + '\x18e' # Execute macro. + '\x18e' # Execute macro. + '\n' + ) + + result, cli = _feed_cli_with_input(operations) + assert result.text == ' hello hellohello' + + +def test_prefix_meta(): + # Test the prefix-meta command. + def setup_keybindings(cli): + from prompt_toolkit.key_binding.bindings.named_commands import prefix_meta + from prompt_toolkit.filters import ViInsertMode + cli.application.key_bindings_registry.add_binding('j', 'j', filter=ViInsertMode())(prefix_meta) + + result, cli = _feed_cli_with_input( + 'hellojjIX\n', pre_run_callback=setup_keybindings, editing_mode=EditingMode.VI) + assert result.text == 'Xhello' + + +def test_bracketed_paste(): + result, cli = _feed_cli_with_input('\x1b[200~hello world\x1b[201~\n') + assert result.text == 'hello world' + + result, cli = _feed_cli_with_input('\x1b[200~hello\nworld\x1b[201~\x1b\n') + assert result.text == 'hello\nworld' + + # With \r\n endings. + result, cli = _feed_cli_with_input('\x1b[200~hello\r\nworld\x1b[201~\x1b\n') + assert result.text == 'hello\nworld' + + # With \r endings. + result, cli = _feed_cli_with_input('\x1b[200~hello\rworld\x1b[201~\x1b\n') + assert result.text == 'hello\nworld' + + +def test_vi_cursor_movements(): + """ + Test cursor movements with Vi key bindings. + """ + feed = partial(_feed_cli_with_input, editing_mode=EditingMode.VI) + + result, cli = feed('\x1b\n') + assert result.text == '' + assert cli.editing_mode == EditingMode.VI + + # Esc h a X + result, cli = feed('hello\x1bhaX\n') + assert result.text == 'hellXo' + + # Esc I X + result, cli = feed('hello\x1bIX\n') + assert result.text == 'Xhello' + + # Esc I X + result, cli = feed('hello\x1bIX\n') + assert result.text == 'Xhello' + + # Esc 2hiX + result, cli = feed('hello\x1b2hiX\n') + assert result.text == 'heXllo' + + # Esc 2h2liX + result, cli = feed('hello\x1b2h2liX\n') + assert result.text == 'hellXo' + + # Esc \b\b + result, cli = feed('hello\b\b\n') + assert result.text == 'hel' + + # Esc \b\b + result, cli = feed('hello\b\b\n') + assert result.text == 'hel' + + # Esc 2h D + result, cli = feed('hello\x1b2hD\n') + assert result.text == 'he' + + # Esc 2h rX \n + result, cli = feed('hello\x1b2hrX\n') + assert result.text == 'heXlo' + + +def test_vi_operators(): + feed = partial(_feed_cli_with_input, editing_mode=EditingMode.VI) + + # Esc g~0 + result, cli = feed('hello\x1bg~0\n') + assert result.text == 'HELLo' + + # Esc gU0 + result, cli = feed('hello\x1bgU0\n') + assert result.text == 'HELLo' + + # Esc d0 + result, cli = feed('hello\x1bd0\n') + assert result.text == 'o' + + +def test_vi_text_objects(): + feed = partial(_feed_cli_with_input, editing_mode=EditingMode.VI) + + # Esc gUgg + result, cli = feed('hello\x1bgUgg\n') + assert result.text == 'HELLO' + + # Esc gUU + result, cli = feed('hello\x1bgUU\n') + assert result.text == 'HELLO' + + # Esc di( + result, cli = feed('before(inside)after\x1b8hdi(\n') + assert result.text == 'before()after' + + # Esc di[ + result, cli = feed('before[inside]after\x1b8hdi[\n') + assert result.text == 'before[]after' + + # Esc da( + result, cli = feed('before(inside)after\x1b8hda(\n') + assert result.text == 'beforeafter' + + +def test_vi_digraphs(): + feed = partial(_feed_cli_with_input, editing_mode=EditingMode.VI) + + # C-K o/ + result, cli = feed('hello\x0bo/\n') + assert result.text == 'helloø' + + # C-K /o (reversed input.) + result, cli = feed('hello\x0b/o\n') + assert result.text == 'helloø' + + # C-K e: + result, cli = feed('hello\x0be:\n') + assert result.text == 'helloë' + + # C-K xxy (Unknown digraph.) + result, cli = feed('hello\x0bxxy\n') + assert result.text == 'helloy' + + +def test_vi_block_editing(): + " Test Vi Control-V style block insertion. " + feed = partial(_feed_cli_with_input, editing_mode=EditingMode.VI, + multiline=True) + + operations = ( + # Three lines of text. + '-line1\n-line2\n-line3\n-line4\n-line5\n-line6' + # Go to the second character of the second line. + '\x1bkkkkkkkj0l' + # Enter Visual block mode. + '\x16' + # Go down two more lines. + 'jj' + # Go 3 characters to the right. + 'lll' + # Go to insert mode. + 'insert' # (Will be replaced.) + # Insert stars. + '***' + # Escape again. + '\x1b\n') + + # Control-I + result, cli = feed(operations.replace('insert', 'I')) + + assert (result.text == + '-line1\n-***line2\n-***line3\n-***line4\n-line5\n-line6') + + # Control-A + result, cli = feed(operations.replace('insert', 'A')) + + assert (result.text == + '-line1\n-line***2\n-line***3\n-line***4\n-line5\n-line6') + + +def test_vi_character_paste(): + feed = partial(_feed_cli_with_input, editing_mode=EditingMode.VI) + + # Test 'p' character paste. + result, cli = feed('abcde\x1bhhxp\n') + assert result.text == 'abdce' + assert result.cursor_position == 3 + + # Test 'P' character paste. + result, cli = feed('abcde\x1bhhxP\n') + assert result.text == 'abcde' + assert result.cursor_position == 2 diff --git a/contrib/python/prompt-toolkit/py2/tests/test_contrib.py b/contrib/python/prompt-toolkit/py2/tests/test_contrib.py new file mode 100644 index 00000000000..64765d3f1a1 --- /dev/null +++ b/contrib/python/prompt-toolkit/py2/tests/test_contrib.py @@ -0,0 +1,254 @@ +from __future__ import unicode_literals, absolute_import, print_function + +import os +import shutil +import tempfile + +from contextlib import contextmanager + +from six import text_type + +from prompt_toolkit.completion import CompleteEvent +from prompt_toolkit.document import Document +from prompt_toolkit.contrib.completers.filesystem import PathCompleter + + +@contextmanager +def chdir(directory): + """Context manager for current working directory temporary change.""" + orig_dir = os.getcwd() + os.chdir(directory) + + try: + yield + finally: + os.chdir(orig_dir) + + +def write_test_files(test_dir, names=None): + """Write test files in test_dir using the names list.""" + names = names or range(10) + for i in names: + with open(os.path.join(test_dir, str(i)), 'wb') as out: + out.write(''.encode('UTF-8')) + + +def test_pathcompleter_completes_in_current_directory(): + completer = PathCompleter() + doc_text = '' + doc = Document(doc_text, len(doc_text)) + event = CompleteEvent() + completions = list(completer.get_completions(doc, event)) + assert len(completions) > 0 + + +def test_pathcompleter_completes_files_in_current_directory(): + # setup: create a test dir with 10 files + test_dir = tempfile.mkdtemp() + write_test_files(test_dir) + + expected = sorted([str(i) for i in range(10)]) + + if not test_dir.endswith(os.path.sep): + test_dir += os.path.sep + + with chdir(test_dir): + completer = PathCompleter() + # this should complete on the cwd + doc_text = '' + doc = Document(doc_text, len(doc_text)) + event = CompleteEvent() + completions = list(completer.get_completions(doc, event)) + result = sorted(c.text for c in completions) + assert expected == result + + # cleanup + shutil.rmtree(test_dir) + + +def test_pathcompleter_completes_files_in_absolute_directory(): + # setup: create a test dir with 10 files + test_dir = tempfile.mkdtemp() + write_test_files(test_dir) + + expected = sorted([str(i) for i in range(10)]) + + test_dir = os.path.abspath(test_dir) + if not test_dir.endswith(os.path.sep): + test_dir += os.path.sep + + completer = PathCompleter() + # force unicode + doc_text = text_type(test_dir) + doc = Document(doc_text, len(doc_text)) + event = CompleteEvent() + completions = list(completer.get_completions(doc, event)) + result = sorted([c.text for c in completions]) + assert expected == result + + # cleanup + shutil.rmtree(test_dir) + + +def test_pathcompleter_completes_directories_with_only_directories(): + # setup: create a test dir with 10 files + test_dir = tempfile.mkdtemp() + write_test_files(test_dir) + + # create a sub directory there + os.mkdir(os.path.join(test_dir, 'subdir')) + + if not test_dir.endswith(os.path.sep): + test_dir += os.path.sep + + with chdir(test_dir): + completer = PathCompleter(only_directories=True) + doc_text = '' + doc = Document(doc_text, len(doc_text)) + event = CompleteEvent() + completions = list(completer.get_completions(doc, event)) + result = [c.text for c in completions] + assert ['subdir'] == result + + # check that there is no completion when passing a file + with chdir(test_dir): + completer = PathCompleter(only_directories=True) + doc_text = '1' + doc = Document(doc_text, len(doc_text)) + event = CompleteEvent() + completions = list(completer.get_completions(doc, event)) + assert [] == completions + + # cleanup + shutil.rmtree(test_dir) + + +def test_pathcompleter_respects_completions_under_min_input_len(): + # setup: create a test dir with 10 files + test_dir = tempfile.mkdtemp() + write_test_files(test_dir) + + # min len:1 and no text + with chdir(test_dir): + completer = PathCompleter(min_input_len=1) + doc_text = '' + doc = Document(doc_text, len(doc_text)) + event = CompleteEvent() + completions = list(completer.get_completions(doc, event)) + assert [] == completions + + # min len:1 and text of len 1 + with chdir(test_dir): + completer = PathCompleter(min_input_len=1) + doc_text = '1' + doc = Document(doc_text, len(doc_text)) + event = CompleteEvent() + completions = list(completer.get_completions(doc, event)) + result = [c.text for c in completions] + assert [''] == result + + # min len:0 and text of len 2 + with chdir(test_dir): + completer = PathCompleter(min_input_len=0) + doc_text = '1' + doc = Document(doc_text, len(doc_text)) + event = CompleteEvent() + completions = list(completer.get_completions(doc, event)) + result = [c.text for c in completions] + assert [''] == result + + # create 10 files with a 2 char long name + for i in range(10): + with open(os.path.join(test_dir, str(i) * 2), 'wb') as out: + out.write(b'') + + # min len:1 and text of len 1 + with chdir(test_dir): + completer = PathCompleter(min_input_len=1) + doc_text = '2' + doc = Document(doc_text, len(doc_text)) + event = CompleteEvent() + completions = list(completer.get_completions(doc, event)) + result = sorted(c.text for c in completions) + assert ['', '2'] == result + + # min len:2 and text of len 1 + with chdir(test_dir): + completer = PathCompleter(min_input_len=2) + doc_text = '2' + doc = Document(doc_text, len(doc_text)) + event = CompleteEvent() + completions = list(completer.get_completions(doc, event)) + assert [] == completions + + # cleanup + shutil.rmtree(test_dir) + + +def test_pathcompleter_does_not_expanduser_by_default(): + completer = PathCompleter() + doc_text = '~' + doc = Document(doc_text, len(doc_text)) + event = CompleteEvent() + completions = list(completer.get_completions(doc, event)) + assert [] == completions + + +def test_pathcompleter_can_expanduser(monkeypatch): + monkeypatch.setenv('HOME', '/tmp') + completer = PathCompleter(expanduser=True) + doc_text = '~' + doc = Document(doc_text, len(doc_text)) + event = CompleteEvent() + completions = list(completer.get_completions(doc, event)) + assert len(completions) > 0 + + +def test_pathcompleter_can_apply_file_filter(): + # setup: create a test dir with 10 files + test_dir = tempfile.mkdtemp() + write_test_files(test_dir) + + # add a .csv file + with open(os.path.join(test_dir, 'my.csv'), 'wb') as out: + out.write(b'') + + file_filter = lambda f: f and f.endswith('.csv') + + with chdir(test_dir): + completer = PathCompleter(file_filter=file_filter) + doc_text = '' + doc = Document(doc_text, len(doc_text)) + event = CompleteEvent() + completions = list(completer.get_completions(doc, event)) + result = [c.text for c in completions] + assert ['my.csv'] == result + + # cleanup + shutil.rmtree(test_dir) + + +def test_pathcompleter_get_paths_constrains_path(): + # setup: create a test dir with 10 files + test_dir = tempfile.mkdtemp() + write_test_files(test_dir) + + # add a subdir with 10 other files with different names + subdir = os.path.join(test_dir, 'subdir') + os.mkdir(subdir) + write_test_files(subdir, 'abcdefghij') + + get_paths = lambda: ['subdir'] + + with chdir(test_dir): + completer = PathCompleter(get_paths=get_paths) + doc_text = '' + doc = Document(doc_text, len(doc_text)) + event = CompleteEvent() + completions = list(completer.get_completions(doc, event)) + result = [c.text for c in completions] + expected = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'] + assert expected == result + + # cleanup + shutil.rmtree(test_dir) diff --git a/contrib/python/prompt-toolkit/py2/tests/test_document.py b/contrib/python/prompt-toolkit/py2/tests/test_document.py new file mode 100644 index 00000000000..8c0cf5704c1 --- /dev/null +++ b/contrib/python/prompt-toolkit/py2/tests/test_document.py @@ -0,0 +1,74 @@ +from __future__ import unicode_literals + +import pytest + +from prompt_toolkit.document import Document + + +@pytest.fixture +def document(): + return Document( + 'line 1\n' + + 'line 2\n' + + 'line 3\n' + + 'line 4\n', + len('line 1\n' + 'lin') + ) + + +def test_current_char(document): + assert document.current_char == 'e' + + +def test_text_before_cursor(document): + assert document.text_before_cursor == 'line 1\nlin' + + +def test_text_after_cursor(document): + assert document.text_after_cursor == 'e 2\n' + \ + 'line 3\n' + \ + 'line 4\n' + + +def test_lines(document): + assert document.lines == [ + 'line 1', + 'line 2', + 'line 3', + 'line 4', ''] + + +def test_line_count(document): + assert document.line_count == 5 + + +def test_current_line_before_cursor(document): + assert document.current_line_before_cursor == 'lin' + + +def test_current_line_after_cursor(document): + assert document.current_line_after_cursor == 'e 2' + + +def test_current_line(document): + assert document.current_line == 'line 2' + + +def test_cursor_position(document): + assert document.cursor_position_row == 1 + assert document.cursor_position_col == 3 + + d = Document('', 0) + assert d.cursor_position_row == 0 + assert d.cursor_position_col == 0 + + +def test_translate_index_to_position(document): + pos = document.translate_index_to_position( + len('line 1\nline 2\nlin')) + + assert pos[0] == 2 + assert pos[1] == 3 + + pos = document.translate_index_to_position(0) + assert pos == (0, 0) diff --git a/contrib/python/prompt-toolkit/py2/tests/test_filter.py b/contrib/python/prompt-toolkit/py2/tests/test_filter.py new file mode 100644 index 00000000000..5aee62a9a90 --- /dev/null +++ b/contrib/python/prompt-toolkit/py2/tests/test_filter.py @@ -0,0 +1,168 @@ +from __future__ import unicode_literals +from prompt_toolkit.filters import Condition, Never, Always, Filter +from prompt_toolkit.filters.types import CLIFilter, SimpleFilter +from prompt_toolkit.filters.utils import to_cli_filter, to_simple_filter +from prompt_toolkit.filters.cli import HasArg, HasFocus, HasSelection + +import pytest + + +def test_condition_filter_args(): + c = Condition(lambda a, b, c: True) + assert c.test_args('a', 'b', 'c') + assert not c.test_args() + assert not c.test_args('a') + assert not c.test_args('a', 'b') + assert not c.test_args('a', 'b', 'c', 'd') + + c2 = Condition(lambda a, b=1: True) + assert c2.test_args('a') + assert c2.test_args('a', 'b') + assert not c2.test_args('a', 'b', 'c') + assert not c2.test_args() + + c3 = Condition(lambda *a: True) + assert c3.test_args() + assert c3.test_args('a') + assert c3.test_args('a', 'b') + + +def test_and_arg(): + c1 = Condition(lambda a: True) + c2 = Condition(lambda a: True) + c3 = c1 & c2 + + assert c3.test_args('a') + assert not c3.test_args() + assert not c3.test_args('a', 'b') + + +def test_or_arg(): + c1 = Condition(lambda a: True) + c2 = Condition(lambda a: True) + c3 = c1 | c2 + + assert c3.test_args('a') + assert not c3.test_args() + assert not c3.test_args('a', 'b') + + +def test_condition(): + c = Condition(lambda a: a % 2 == 0) + assert c(4) + assert c(6) + assert not c(5) + assert not c(3) + + +def test_never(): + assert not Never()() + + +def test_always(): + assert Always()() + + +def test_invert(): + assert not (~Always())() + assert (~Never()()) + + c = ~Condition(lambda: False) + assert c() + + +def test_or(): + for a in (True, False): + for b in (True, False): + c1 = Condition(lambda: a) + c2 = Condition(lambda: b) + c3 = c1 | c2 + + assert isinstance(c3, Filter) + assert c3() == a or b + + +def test_and(): + for a in (True, False): + for b in (True, False): + c1 = Condition(lambda: a) + c2 = Condition(lambda: b) + c3 = c1 & c2 + + assert isinstance(c3, Filter) + assert c3() == (a and b) + + +def test_cli_filter(): + c1 = Condition(lambda cli: True) + assert isinstance(c1, CLIFilter) + assert not isinstance(c1, SimpleFilter) + + c2 = Condition(lambda: True) + assert not isinstance(c2, CLIFilter) + assert isinstance(c2, SimpleFilter) + + c3 = c1 | c2 + assert not isinstance(c3, CLIFilter) + assert not isinstance(c3, SimpleFilter) + + c4 = Condition(lambda cli: True) + c5 = Condition(lambda cli: True) + c6 = c4 & c5 + c7 = c4 | c5 + assert isinstance(c6, CLIFilter) + assert isinstance(c7, CLIFilter) + assert not isinstance(c6, SimpleFilter) + assert not isinstance(c7, SimpleFilter) + + c8 = Condition(lambda *args: True) + assert isinstance(c8, CLIFilter) + assert isinstance(c8, SimpleFilter) + + +def test_to_cli_filter(): + f1 = to_cli_filter(True) + f2 = to_cli_filter(False) + f3 = to_cli_filter(Condition(lambda cli: True)) + f4 = to_cli_filter(Condition(lambda cli: False)) + + assert isinstance(f1, CLIFilter) + assert isinstance(f2, CLIFilter) + assert isinstance(f3, CLIFilter) + assert isinstance(f4, CLIFilter) + assert f1(None) + assert not f2(None) + assert f3(None) + assert not f4(None) + + with pytest.raises(TypeError): + to_cli_filter(4) + with pytest.raises(TypeError): + to_cli_filter(Condition(lambda: True)) + + +def test_to_simple_filter(): + f1 = to_simple_filter(True) + f2 = to_simple_filter(False) + f3 = to_simple_filter(Condition(lambda: True)) + f4 = to_simple_filter(Condition(lambda: False)) + + assert isinstance(f1, SimpleFilter) + assert isinstance(f2, SimpleFilter) + assert isinstance(f3, SimpleFilter) + assert isinstance(f4, SimpleFilter) + assert f1() + assert not f2() + assert f3() + assert not f4() + + with pytest.raises(TypeError): + to_simple_filter(4) + with pytest.raises(TypeError): + to_simple_filter(Condition(lambda cli: True)) + + +def test_cli_filters(): + assert isinstance(HasArg(), CLIFilter) + assert isinstance(HasFocus('BUFFER_NAME'), CLIFilter) + assert isinstance(HasSelection(), CLIFilter) diff --git a/contrib/python/prompt-toolkit/py2/tests/test_inputstream.py b/contrib/python/prompt-toolkit/py2/tests/test_inputstream.py new file mode 100644 index 00000000000..de10c0f9a91 --- /dev/null +++ b/contrib/python/prompt-toolkit/py2/tests/test_inputstream.py @@ -0,0 +1,142 @@ +from __future__ import unicode_literals + +from prompt_toolkit.terminal.vt100_input import InputStream +from prompt_toolkit.keys import Keys + +import pytest + + +class _ProcessorMock(object): + + def __init__(self): + self.keys = [] + + def feed_key(self, key_press): + self.keys.append(key_press) + + +@pytest.fixture +def processor(): + return _ProcessorMock() + + +@pytest.fixture +def stream(processor): + return InputStream(processor.feed_key) + + +def test_control_keys(processor, stream): + stream.feed('\x01\x02\x10') + + assert len(processor.keys) == 3 + assert processor.keys[0].key == Keys.ControlA + assert processor.keys[1].key == Keys.ControlB + assert processor.keys[2].key == Keys.ControlP + assert processor.keys[0].data == '\x01' + assert processor.keys[1].data == '\x02' + assert processor.keys[2].data == '\x10' + + +def test_arrows(processor, stream): + stream.feed('\x1b[A\x1b[B\x1b[C\x1b[D') + + assert len(processor.keys) == 4 + assert processor.keys[0].key == Keys.Up + assert processor.keys[1].key == Keys.Down + assert processor.keys[2].key == Keys.Right + assert processor.keys[3].key == Keys.Left + assert processor.keys[0].data == '\x1b[A' + assert processor.keys[1].data == '\x1b[B' + assert processor.keys[2].data == '\x1b[C' + assert processor.keys[3].data == '\x1b[D' + + +def test_escape(processor, stream): + stream.feed('\x1bhello') + + assert len(processor.keys) == 1 + len('hello') + assert processor.keys[0].key == Keys.Escape + assert processor.keys[1].key == 'h' + assert processor.keys[0].data == '\x1b' + assert processor.keys[1].data == 'h' + + +def test_special_double_keys(processor, stream): + stream.feed('\x1b[1;3D') # Should both send escape and left. + + assert len(processor.keys) == 2 + assert processor.keys[0].key == Keys.Escape + assert processor.keys[1].key == Keys.Left + assert processor.keys[0].data == '\x1b[1;3D' + assert processor.keys[1].data == '\x1b[1;3D' + + +def test_flush_1(processor, stream): + # Send left key in two parts without flush. + stream.feed('\x1b') + stream.feed('[D') + + assert len(processor.keys) == 1 + assert processor.keys[0].key == Keys.Left + assert processor.keys[0].data == '\x1b[D' + + +def test_flush_2(processor, stream): + # Send left key with a 'Flush' in between. + # The flush should make sure that we process evenything before as-is, + # with makes the first part just an escape character instead. + stream.feed('\x1b') + stream.flush() + stream.feed('[D') + + assert len(processor.keys) == 3 + assert processor.keys[0].key == Keys.Escape + assert processor.keys[1].key == '[' + assert processor.keys[2].key == 'D' + + assert processor.keys[0].data == '\x1b' + assert processor.keys[1].data == '[' + assert processor.keys[2].data == 'D' + + +def test_meta_arrows(processor, stream): + stream.feed('\x1b\x1b[D') + + assert len(processor.keys) == 2 + assert processor.keys[0].key == Keys.Escape + assert processor.keys[1].key == Keys.Left + + +def test_control_square_close(processor, stream): + stream.feed('\x1dC') + + assert len(processor.keys) == 2 + assert processor.keys[0].key == Keys.ControlSquareClose + assert processor.keys[1].key == 'C' + + +def test_invalid(processor, stream): + # Invalid sequence that has at two characters in common with other + # sequences. + stream.feed('\x1b[*') + + assert len(processor.keys) == 3 + assert processor.keys[0].key == Keys.Escape + assert processor.keys[1].key == '[' + assert processor.keys[2].key == '*' + + +def test_cpr_response(processor, stream): + stream.feed('a\x1b[40;10Rb') + assert len(processor.keys) == 3 + assert processor.keys[0].key == 'a' + assert processor.keys[1].key == Keys.CPRResponse + assert processor.keys[2].key == 'b' + + +def test_cpr_response_2(processor, stream): + # Make sure that the newline is not included in the CPR response. + stream.feed('\x1b[40;1R\n') + assert len(processor.keys) == 2 + assert processor.keys[0].key == Keys.CPRResponse + assert processor.keys[1].key == Keys.ControlJ diff --git a/contrib/python/prompt-toolkit/py2/tests/test_key_binding.py b/contrib/python/prompt-toolkit/py2/tests/test_key_binding.py new file mode 100644 index 00000000000..d706c4443b2 --- /dev/null +++ b/contrib/python/prompt-toolkit/py2/tests/test_key_binding.py @@ -0,0 +1,140 @@ +from __future__ import unicode_literals + +from prompt_toolkit.key_binding.input_processor import InputProcessor, KeyPress +from prompt_toolkit.key_binding.registry import Registry +from prompt_toolkit.keys import Keys + +import pytest + + +class Handlers(object): + + def __init__(self): + self.called = [] + + def __getattr__(self, name): + def func(event): + self.called.append(name) + return func + + +@pytest.fixture +def handlers(): + return Handlers() + + +@pytest.fixture +def registry(handlers): + registry = Registry() + registry.add_binding( + Keys.ControlX, Keys.ControlC)(handlers.controlx_controlc) + registry.add_binding(Keys.ControlX)(handlers.control_x) + registry.add_binding(Keys.ControlD)(handlers.control_d) + registry.add_binding( + Keys.ControlSquareClose, Keys.Any)(handlers.control_square_close_any) + + return registry + + +@pytest.fixture +def processor(registry): + return InputProcessor(registry, lambda: None) + + +def test_feed_simple(processor, handlers): + processor.feed(KeyPress(Keys.ControlX, '\x18')) + processor.feed(KeyPress(Keys.ControlC, '\x03')) + processor.process_keys() + + assert handlers.called == ['controlx_controlc'] + + +def test_feed_several(processor, handlers): + # First an unknown key first. + processor.feed(KeyPress(Keys.ControlQ, '')) + processor.process_keys() + + assert handlers.called == [] + + # Followed by a know key sequence. + processor.feed(KeyPress(Keys.ControlX, '')) + processor.feed(KeyPress(Keys.ControlC, '')) + processor.process_keys() + + assert handlers.called == ['controlx_controlc'] + + # Followed by another unknown sequence. + processor.feed(KeyPress(Keys.ControlR, '')) + processor.feed(KeyPress(Keys.ControlS, '')) + + # Followed again by a know key sequence. + processor.feed(KeyPress(Keys.ControlD, '')) + processor.process_keys() + + assert handlers.called == ['controlx_controlc', 'control_d'] + + +def test_control_square_closed_any(processor, handlers): + processor.feed(KeyPress(Keys.ControlSquareClose, '')) + processor.feed(KeyPress('C', 'C')) + processor.process_keys() + + assert handlers.called == ['control_square_close_any'] + + +def test_common_prefix(processor, handlers): + # Sending Control_X should not yet do anything, because there is + # another sequence starting with that as well. + processor.feed(KeyPress(Keys.ControlX, '')) + processor.process_keys() + + assert handlers.called == [] + + # When another key is pressed, we know that we did not meant the longer + # "ControlX ControlC" sequence and the callbacks are called. + processor.feed(KeyPress(Keys.ControlD, '')) + processor.process_keys() + + assert handlers.called == ['control_x', 'control_d'] + + +def test_previous_key_sequence(processor, handlers): + """ + test whether we receive the correct previous_key_sequence. + """ + events = [] + def handler(event): + events.append(event) + + # Build registry. + registry = Registry() + registry.add_binding('a', 'a')(handler) + registry.add_binding('b', 'b')(handler) + processor = InputProcessor(registry, lambda: None) + + # Create processor and feed keys. + processor.feed(KeyPress('a', 'a')) + processor.feed(KeyPress('a', 'a')) + processor.feed(KeyPress('b', 'b')) + processor.feed(KeyPress('b', 'b')) + processor.process_keys() + + # Test. + assert len(events) == 2 + assert len(events[0].key_sequence) == 2 + assert events[0].key_sequence[0].key == 'a' + assert events[0].key_sequence[0].data == 'a' + assert events[0].key_sequence[1].key == 'a' + assert events[0].key_sequence[1].data == 'a' + assert events[0].previous_key_sequence == [] + + assert len(events[1].key_sequence) == 2 + assert events[1].key_sequence[0].key == 'b' + assert events[1].key_sequence[0].data == 'b' + assert events[1].key_sequence[1].key == 'b' + assert events[1].key_sequence[1].data == 'b' + assert len(events[1].previous_key_sequence) == 2 + assert events[1].previous_key_sequence[0].key == 'a' + assert events[1].previous_key_sequence[0].data == 'a' + assert events[1].previous_key_sequence[1].key == 'a' + assert events[1].previous_key_sequence[1].data == 'a' diff --git a/contrib/python/prompt-toolkit/py2/tests/test_layout.py b/contrib/python/prompt-toolkit/py2/tests/test_layout.py new file mode 100644 index 00000000000..6c9e67b4dea --- /dev/null +++ b/contrib/python/prompt-toolkit/py2/tests/test_layout.py @@ -0,0 +1,60 @@ +from __future__ import unicode_literals + +from prompt_toolkit.layout.utils import split_lines +from prompt_toolkit.token import Token + + +def test_split_lines(): + lines = list(split_lines([(Token.A, 'line1\nline2\nline3')])) + + assert lines == [ + [(Token.A, 'line1')], + [(Token.A, 'line2')], + [(Token.A, 'line3')], + ] + + +def test_split_lines_2(): + lines = list(split_lines([ + (Token.A, 'line1'), + (Token.B, 'line2\nline3\nline4') + ])) + + assert lines == [ + [(Token.A, 'line1'), (Token.B, 'line2')], + [(Token.B, 'line3')], + [(Token.B, 'line4')], + ] + + +def test_split_lines_3(): + " Edge cases: inputs ending with newlines. " + # -1- + lines = list(split_lines([ + (Token.A, 'line1\nline2\n') + ])) + + assert lines == [ + [(Token.A, 'line1')], + [(Token.A, 'line2')], + [(Token.A, '')], + ] + + # -2- + lines = list(split_lines([ + (Token.A, '\n'), + ])) + + assert lines == [ + [], + [(Token.A, '')], + ] + + # -3- + lines = list(split_lines([ + (Token.A, ''), + ])) + + assert lines == [ + [(Token.A, '')], + ] diff --git a/contrib/python/prompt-toolkit/py2/tests/test_print_tokens.py b/contrib/python/prompt-toolkit/py2/tests/test_print_tokens.py new file mode 100644 index 00000000000..c3b5826b36c --- /dev/null +++ b/contrib/python/prompt-toolkit/py2/tests/test_print_tokens.py @@ -0,0 +1,50 @@ +""" +Test `shortcuts.print_tokens`. +""" +from __future__ import unicode_literals +from prompt_toolkit.shortcuts import print_tokens +from prompt_toolkit.token import Token +from prompt_toolkit.styles import style_from_dict + + +class _Capture: + " Emulate an stdout object. " + encoding = 'utf-8' + + def __init__(self): + self._data = [] + + def write(self, data): + self._data.append(data) + + @property + def data(self): + return b''.join(self._data) + + def flush(self): + pass + + def isatty(self): + return True + + +def test_print_tokens(): + f = _Capture() + print_tokens([(Token, 'hello'), (Token, 'world')], file=f) + assert b'hello' in f.data + assert b'world' in f.data + + +def test_with_style(): + f = _Capture() + style = style_from_dict({ + Token.Hello: '#ff0066', + Token.World: '#44ff44 italic', + }) + tokens = [ + (Token.Hello, 'Hello '), + (Token.World, 'world'), + ] + print_tokens(tokens, style=style, file=f) + assert b'\x1b[0;38;5;197mHello' in f.data + assert b'\x1b[0;38;5;83;3mworld' in f.data diff --git a/contrib/python/prompt-toolkit/py2/tests/test_regular_languages.py b/contrib/python/prompt-toolkit/py2/tests/test_regular_languages.py new file mode 100644 index 00000000000..6ece5cdeb53 --- /dev/null +++ b/contrib/python/prompt-toolkit/py2/tests/test_regular_languages.py @@ -0,0 +1,110 @@ +from __future__ import unicode_literals + +from prompt_toolkit.completion import CompleteEvent, Completer, Completion +from prompt_toolkit.contrib.regular_languages import compile +from prompt_toolkit.contrib.regular_languages.compiler import Match, Variables +from prompt_toolkit.contrib.regular_languages.completion import \ + GrammarCompleter +from prompt_toolkit.document import Document + + +def test_simple_match(): + g = compile('hello|world') + + m = g.match('hello') + assert isinstance(m, Match) + + m = g.match('world') + assert isinstance(m, Match) + + m = g.match('somethingelse') + assert m is None + + +def test_variable_varname(): + """ + Test `Variable` with varname. + """ + g = compile('((?P<varname>hello|world)|test)') + + m = g.match('hello') + variables = m.variables() + assert isinstance(variables, Variables) + assert variables.get('varname') == 'hello' + assert variables['varname'] == 'hello' + + m = g.match('world') + variables = m.variables() + assert isinstance(variables, Variables) + assert variables.get('varname') == 'world' + assert variables['varname'] == 'world' + + m = g.match('test') + variables = m.variables() + assert isinstance(variables, Variables) + assert variables.get('varname') is None + assert variables['varname'] is None + + +def test_prefix(): + """ + Test `match_prefix`. + """ + g = compile(r'(hello\ world|something\ else)') + + m = g.match_prefix('hello world') + assert isinstance(m, Match) + + m = g.match_prefix('he') + assert isinstance(m, Match) + + m = g.match_prefix('') + assert isinstance(m, Match) + + m = g.match_prefix('som') + assert isinstance(m, Match) + + m = g.match_prefix('hello wor') + assert isinstance(m, Match) + + m = g.match_prefix('no-match') + assert m.trailing_input().start == 0 + assert m.trailing_input().stop == len('no-match') + + m = g.match_prefix('hellotest') + assert m.trailing_input().start == len('hello') + assert m.trailing_input().stop == len('hellotest') + + +def test_completer(): + class completer1(Completer): + + def get_completions(self, document, complete_event): + yield Completion( + 'before-%s-after' % document.text, -len(document.text)) + yield Completion( + 'before-%s-after-B' % document.text, -len(document.text)) + + class completer2(Completer): + + def get_completions(self, document, complete_event): + yield Completion( + 'before2-%s-after2' % document.text, -len(document.text)) + yield Completion( + 'before2-%s-after2-B' % document.text, -len(document.text)) + + # Create grammar. "var1" + "whitespace" + "var2" + g = compile(r'(?P<var1>[a-z]*) \s+ (?P<var2>[a-z]*)') + + # Test 'get_completions()' + completer = GrammarCompleter( + g, {'var1': completer1(), 'var2': completer2()}) + completions = list(completer.get_completions( + Document('abc def', len('abc def')), + CompleteEvent())) + + assert len(completions) == 2 + assert completions[0].text == 'before2-def-after2' + assert completions[0].start_position == -3 + assert completions[1].text == 'before2-def-after2-B' + assert completions[1].start_position == -3 diff --git a/contrib/python/prompt-toolkit/py2/tests/test_shortcuts.py b/contrib/python/prompt-toolkit/py2/tests/test_shortcuts.py new file mode 100644 index 00000000000..8c13510ea83 --- /dev/null +++ b/contrib/python/prompt-toolkit/py2/tests/test_shortcuts.py @@ -0,0 +1,50 @@ +from prompt_toolkit.shortcuts import _split_multiline_prompt +from prompt_toolkit.token import Token + + +def test_split_multiline_prompt(): + # Test 1: no newlines: + tokens = [(Token, 'ab')] + has_before_tokens, before, first_input_line = _split_multiline_prompt(lambda cli: tokens) + assert has_before_tokens(None) is False + assert before(None) == [] + assert first_input_line(None) == [ + (Token, 'a'), + (Token, 'b'), + ] + + # Test 1: multiple lines. + tokens = [(Token, 'ab\ncd\nef')] + has_before_tokens, before, first_input_line = _split_multiline_prompt(lambda cli: tokens) + assert has_before_tokens(None) is True + assert before(None) == [ + (Token, 'a'), + (Token, 'b'), + (Token, '\n'), + (Token, 'c'), + (Token, 'd'), + ] + assert first_input_line(None) == [ + (Token, 'e'), + (Token, 'f'), + ] + + # Edge case 1: starting with a newline. + tokens = [(Token, '\nab')] + has_before_tokens, before, first_input_line = _split_multiline_prompt(lambda cli: tokens) + assert has_before_tokens(None) is True + assert before(None) == [] + assert first_input_line(None) == [ + (Token, 'a'), + (Token, 'b') + ] + + # Edge case 2: starting with two newlines. + tokens = [(Token, '\n\nab')] + has_before_tokens, before, first_input_line = _split_multiline_prompt(lambda cli: tokens) + assert has_before_tokens(None) is True + assert before(None) == [(Token, '\n')] + assert first_input_line(None) == [ + (Token, 'a'), + (Token, 'b') + ] diff --git a/contrib/python/prompt-toolkit/py2/tests/test_style.py b/contrib/python/prompt-toolkit/py2/tests/test_style.py new file mode 100644 index 00000000000..fc839ec95f2 --- /dev/null +++ b/contrib/python/prompt-toolkit/py2/tests/test_style.py @@ -0,0 +1,40 @@ +from __future__ import unicode_literals + +from prompt_toolkit.styles import Attrs, style_from_dict +from prompt_toolkit.token import Token + + +def test_style_from_dict(): + style = style_from_dict({ + Token.A: '#ff0000 bold underline italic', + Token.B: 'bg:#00ff00 blink reverse', + }) + + expected = Attrs(color='ff0000', bgcolor=None, bold=True, + underline=True, italic=True, blink=False, reverse=False) + assert style.get_attrs_for_token(Token.A) == expected + + expected = Attrs(color=None, bgcolor='00ff00', bold=False, + underline=False, italic=False, blink=True, reverse=True) + assert style.get_attrs_for_token(Token.B) == expected + + +def test_style_inheritance(): + style = style_from_dict({ + Token: '#ff0000', + Token.A.B.C: 'bold', + Token.A.B.C.D: '#ansired', + Token.A.B.C.D.E: 'noinherit blink' + }) + + expected = Attrs(color='ff0000', bgcolor=None, bold=True, + underline=False, italic=False, blink=False, reverse=False) + assert style.get_attrs_for_token(Token.A.B.C) == expected + + expected = Attrs(color='ansired', bgcolor=None, bold=True, + underline=False, italic=False, blink=False, reverse=False) + assert style.get_attrs_for_token(Token.A.B.C.D) == expected + + expected = Attrs(color=None, bgcolor=None, bold=False, + underline=False, italic=False, blink=True, reverse=False) + assert style.get_attrs_for_token(Token.A.B.C.D.E) == expected diff --git a/contrib/python/prompt-toolkit/py2/tests/test_utils.py b/contrib/python/prompt-toolkit/py2/tests/test_utils.py new file mode 100644 index 00000000000..1e3d92cda69 --- /dev/null +++ b/contrib/python/prompt-toolkit/py2/tests/test_utils.py @@ -0,0 +1,39 @@ +from __future__ import unicode_literals + +from prompt_toolkit.utils import take_using_weights + +import itertools + + +def test_using_weights(): + def take(generator, count): + return list(itertools.islice(generator, 0, count)) + + # Check distribution. + data = take(take_using_weights(['A', 'B', 'C'], [5, 10, 20]), 35) + assert data.count('A') == 5 + assert data.count('B') == 10 + assert data.count('C') == 20 + + assert data == [ + 'A', 'B', 'C', 'C', 'B', 'C', 'C', 'A', 'B', 'C', 'C', 'B', 'C', + 'C', 'A', 'B', 'C', 'C', 'B', 'C', 'C', 'A', 'B', 'C', 'C', + 'B', 'C', 'C', 'A', 'B', 'C', 'C', 'B', 'C', 'C'] + + # Another order. + data = take(take_using_weights(['A', 'B', 'C'], [20, 10, 5]), 35) + assert data.count('A') == 20 + assert data.count('B') == 10 + assert data.count('C') == 5 + + # Bigger numbers. + data = take(take_using_weights(['A', 'B', 'C'], [20, 10, 5]), 70) + assert data.count('A') == 40 + assert data.count('B') == 20 + assert data.count('C') == 10 + + # Negative numbers. + data = take(take_using_weights(['A', 'B', 'C'], [-20, 10, 0]), 70) + assert data.count('A') == 0 + assert data.count('B') == 70 + assert data.count('C') == 0 diff --git a/contrib/python/prompt-toolkit/py2/tests/test_yank_nth_arg.py b/contrib/python/prompt-toolkit/py2/tests/test_yank_nth_arg.py new file mode 100644 index 00000000000..1ccec7bf7b1 --- /dev/null +++ b/contrib/python/prompt-toolkit/py2/tests/test_yank_nth_arg.py @@ -0,0 +1,85 @@ +from __future__ import unicode_literals +from prompt_toolkit.buffer import Buffer +from prompt_toolkit.history import InMemoryHistory + +import pytest + + +@pytest.fixture +def _history(): + " Prefilled history. " + history = InMemoryHistory() + history.append('alpha beta gamma delta') + history.append('one two three four') + return history + + +# Test yank_last_arg. + + +def test_empty_history(): + buf = Buffer() + buf.yank_last_arg() + assert buf.document.current_line == '' + + +def test_simple_search(_history): + buff = Buffer(history=_history) + buff.yank_last_arg() + assert buff.document.current_line == 'four' + + +def test_simple_search_with_quotes(_history): + _history.append("""one two "three 'x' four"\n""") + buff = Buffer(history=_history) + buff.yank_last_arg() + assert buff.document.current_line == '''"three 'x' four"''' + + +def test_simple_search_with_arg(_history): + buff = Buffer(history=_history) + buff.yank_last_arg(n=2) + assert buff.document.current_line == 'three' + + +def test_simple_search_with_arg_out_of_bounds(_history): + buff = Buffer(history=_history) + buff.yank_last_arg(n=8) + assert buff.document.current_line == '' + + +def test_repeated_search(_history): + buff = Buffer(history=_history) + buff.yank_last_arg() + buff.yank_last_arg() + assert buff.document.current_line == 'delta' + + +def test_repeated_search_with_wraparound(_history): + buff = Buffer(history=_history) + buff.yank_last_arg() + buff.yank_last_arg() + buff.yank_last_arg() + assert buff.document.current_line == 'four' + + +# Test yank_last_arg. + + +def test_yank_nth_arg(_history): + buff = Buffer(history=_history) + buff.yank_nth_arg() + assert buff.document.current_line == 'two' + + +def test_repeated_yank_nth_arg(_history): + buff = Buffer(history=_history) + buff.yank_nth_arg() + buff.yank_nth_arg() + assert buff.document.current_line == 'beta' + + +def test_yank_nth_arg_with_arg(_history): + buff = Buffer(history=_history) + buff.yank_nth_arg(n=2) + assert buff.document.current_line == 'three' diff --git a/contrib/python/prompt-toolkit/py2/tests/ya.make b/contrib/python/prompt-toolkit/py2/tests/ya.make new file mode 100644 index 00000000000..afd26fbfe72 --- /dev/null +++ b/contrib/python/prompt-toolkit/py2/tests/ya.make @@ -0,0 +1,28 @@ +PY2TEST() + +OWNER(g:python-contrib) + +PEERDIR( + contrib/python/prompt-toolkit +) + +TEST_SRCS( + test_buffer.py + test_cli.py + test_contrib.py + test_document.py + test_filter.py + test_inputstream.py + test_key_binding.py + test_layout.py + test_print_tokens.py + test_regular_languages.py + test_shortcuts.py + test_style.py + test_utils.py + test_yank_nth_arg.py +) + +NO_LINT() + +END() |