diff --git a/tests/buffer/test_buffer.py b/tests/buffer/test_buffer.py new file mode 100644 index 000000000..a9cff1902 --- /dev/null +++ b/tests/buffer/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/tests/buffer_tests/__init__.py b/tests/buffer_tests/__init__.py deleted file mode 100644 index 048925b1b..000000000 --- a/tests/buffer_tests/__init__.py +++ /dev/null @@ -1,102 +0,0 @@ -from __future__ import unicode_literals - -from prompt_toolkit.buffer import Buffer - -import unittest - - -class BufferTest(unittest.TestCase): - def setUp(self): - self.buffer = Buffer() - - def test_initial(self): - self.assertEqual(self.buffer.text, '') - self.assertEqual(self.buffer.cursor_position, 0) - - def test_insert_text(self): - self.buffer.insert_text('some_text') - self.assertEqual(self.buffer.text, 'some_text') - self.assertEqual(self.buffer.cursor_position, len('some_text')) - - def test_cursor_movement(self): - self.buffer.insert_text('some_text') - self.buffer.cursor_left() - self.buffer.cursor_left() - self.buffer.cursor_left() - self.buffer.cursor_right() - self.buffer.insert_text('A') - - self.assertEqual(self.buffer.text, 'some_teAxt') - self.assertEqual(self.buffer.cursor_position, len('some_teA')) - - def test_backspace(self): - self.buffer.insert_text('some_text') - self.buffer.cursor_left() - self.buffer.cursor_left() - self.buffer.delete_before_cursor() - - self.assertEqual(self.buffer.text, 'some_txt') - self.assertEqual(self.buffer.cursor_position, len('some_t')) - - def test_cursor_up(self): - # Cursor up to a line thats longer. - self.buffer.insert_text('long line1\nline2') - self.buffer.cursor_up() - - self.assertEqual(self.buffer.document.cursor_position, 5) - - # Going up when already at the top. - self.buffer.cursor_up() - self.assertEqual(self.buffer.document.cursor_position, 5) - - # Going up to a line that's shorter. - self.buffer.reset() - self.buffer.insert_text('line1\nlong line2') - - self.buffer.cursor_up() - self.assertEqual(self.buffer.document.cursor_position, 5) - - def test_cursor_down(self): - self.buffer.insert_text('line1\nline2') - self.buffer.cursor_position = 3 - - # Normally going down - self.buffer.cursor_down() - self.assertEqual(self.buffer.document.cursor_position, len('line1\nlin')) - - # Going down to a line that's storter. - self.buffer.reset() - self.buffer.insert_text('long line1\na\nb') - self.buffer.cursor_position = 3 - - self.buffer.cursor_down() - self.assertEqual(self.buffer.document.cursor_position, len('long line1\na')) - - def test_join_next_line(self): - self.buffer.insert_text('line1\nline2\nline3') - self.buffer.cursor_up() - self.buffer.join_next_line() - - self.assertEqual(self.buffer.text, 'line1\nline2 line3') - - # Test when there is no '\n' in the text - self.buffer.reset() - self.buffer.insert_text('line1') - self.buffer.cursor_position = 0 - self.buffer.join_next_line() - - self.assertEqual(self.buffer.text, 'line1') - - def test_newline(self): - self.buffer.insert_text('hello world') - self.buffer.newline() - - self.assertEqual(self.buffer.text, 'hello world\n') - - def test_swap_characters_before_cursor(self): - self.buffer.insert_text('hello world') - self.buffer.cursor_left() - self.buffer.cursor_left() - self.buffer.swap_characters_before_cursor() - - self.assertEqual(self.buffer.text, 'hello wrold') diff --git a/tests/cli_tests.py b/tests/cli_tests.py deleted file mode 100644 index 4976e0a7d..000000000 --- a/tests/cli_tests.py +++ /dev/null @@ -1,208 +0,0 @@ -# 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.enums import DEFAULT_BUFFER, EditingMode -from prompt_toolkit.eventloop.posix import PosixEventLoop -from prompt_toolkit.input import PipeInput -from prompt_toolkit.interface import CommandLineInterface -from prompt_toolkit.output import DummyOutput -from functools import partial - -import unittest - -def _feed_cli_with_input(text, editing_mode=EditingMode.EMACS): - """ - 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. - assert text.endswith('\n') - - loop = PosixEventLoop() - try: - inp = PipeInput() - inp.send_text(text) - cli = CommandLineInterface( - application=Application(editing_mode=editing_mode), - eventloop=loop, - input=inp, - output=DummyOutput()) - result = cli.run() - return result, cli - finally: - loop.close() - inp.close() - - -class FeedCliTest(unittest.TestCase): - def test_simple_text_input(self): - # Simple text input, followed by enter. - result, cli = _feed_cli_with_input('hello\n') - self.assertEqual(result.text, 'hello') - self.assertEqual(cli.buffers[DEFAULT_BUFFER].text, 'hello') - - def test_emacs_cursor_movements(self): - """ - Test cursor movements with Emacs key bindings. - """ - # ControlA - result, cli = _feed_cli_with_input('hello\x01X\n') - self.assertEqual(result.text, 'Xhello') - - # ControlH or \b - result, cli = _feed_cli_with_input('hello\x08X\n') - self.assertEqual(result.text, 'hellX') - - # Delete. (Left, left, delete) - result, cli = _feed_cli_with_input('hello\x1b[D\x1b[D\x1b[3~\n') - self.assertEqual(result.text, 'helo') - - # Left. - result, cli = _feed_cli_with_input('hello\x1b[DX\n') - self.assertEqual(result.text, 'hellXo') - - # ControlA, right - result, cli = _feed_cli_with_input('hello\x01\x1b[CX\n') - self.assertEqual(result.text, 'hXello') - - # ControlA, right - result, cli = _feed_cli_with_input('hello\x01\x1b[CX\n') - self.assertEqual(result.text, 'hXello') - - # ControlB (Emacs cursor left.) - result, cli = _feed_cli_with_input('hello\x02X\n') - self.assertEqual(result.text, 'hellXo') - - # ControlC: ignored by default, unless the prompt-bindings are loaded. - result, cli = _feed_cli_with_input('hello\x03\n') - self.assertEqual(result.text, 'hello') - - # ControlD: ignored by default, unless the prompt-bindings are loaded. - result, cli = _feed_cli_with_input('hello\x04\n') - self.assertEqual(result.text, 'hello') - - # Left, Left, ControlK - result, cli = _feed_cli_with_input('hello\x1b[D\x1b[D\x0b\n') - self.assertEqual(result.text, 'hel') - - # ControlL: should not influence the result. - result, cli = _feed_cli_with_input('hello\x0c\n') - self.assertEqual(result.text, 'hello') - - def test_emacs_other_bindings(self): - # Transpose characters. - result, cli = _feed_cli_with_input('abcde\x14X\n') # Ctrl-T - self.assertEqual(result.text, 'abcedX') - - # Left, Left, Transpose. (This is slightly different.) - result, cli = _feed_cli_with_input('abcde\x1b[D\x1b[D\x14X\n') - self.assertEqual(result.text, 'abdcXe') - - # Clear before cursor. - result, cli = _feed_cli_with_input('hello\x1b[D\x1b[D\x15X\n') - self.assertEqual(result.text, 'Xlo') - - # Delete word before the cursor. - result, cli = _feed_cli_with_input('hello world test\x17X\n') - self.assertEqual(result.text, 'hello world X') - - # (with argument.) - result, cli = _feed_cli_with_input('hello world test\x1b2\x17X\n') - self.assertEqual(result.text, 'hello X') - - def test_vi_cursor_movements(self): - """ - Test cursor movements with Vi key bindings. - """ - feed = partial(_feed_cli_with_input, editing_mode=EditingMode.VI) - - result, cli = feed('\x1b\n') - self.assertEqual(result.text, '') - self.assertEqual(cli.editing_mode, EditingMode.VI) - - # Esc h a X - result, cli = feed('hello\x1bhaX\n') - self.assertEqual(result.text, 'hellXo') - - # Esc I X - result, cli = feed('hello\x1bIX\n') - self.assertEqual(result.text, 'Xhello') - - # Esc I X - result, cli = feed('hello\x1bIX\n') - self.assertEqual(result.text, 'Xhello') - - # Esc 2hiX - result, cli = feed('hello\x1b2hiX\n') - self.assertEqual(result.text, 'heXllo') - - # Esc 2h2liX - result, cli = feed('hello\x1b2h2liX\n') - self.assertEqual(result.text, 'hellXo') - - # Esc \b\b - result, cli = feed('hello\b\b\n') - self.assertEqual(result.text, 'hel') - - # Esc \b\b - result, cli = feed('hello\b\b\n') - self.assertEqual(result.text, 'hel') - - # Esc 2h D - result, cli = feed('hello\x1b2hD\n') - self.assertEqual(result.text, 'he') - - # Esc 2h rX \n - result, cli = feed('hello\x1b2hrX\n') - self.assertEqual(result.text, 'heXlo') - - def test_vi_operators(self): - feed = partial(_feed_cli_with_input, editing_mode=EditingMode.VI) - - # Esc g~0 - result, cli = feed('hello\x1bg~0\n') - self.assertEqual(result.text, 'HELLo') - - # Esc gU0 - result, cli = feed('hello\x1bgU0\n') - self.assertEqual(result.text, 'HELLo') - - # Esc d0 - result, cli = feed('hello\x1bd0\n') - self.assertEqual(result.text, 'o') - - def test_vi_text_objects(self): - feed = partial(_feed_cli_with_input, editing_mode=EditingMode.VI) - - # Esc gUgg - result, cli = feed('hello\x1bgUgg\n') - self.assertEqual(result.text, 'HELLO') - - # Esc di( - result, cli = feed('before(inside)after\x1b8hdi(\n') - self.assertEqual(result.text, 'before()after') - - # Esc di[ - result, cli = feed('before[inside]after\x1b8hdi[\n') - self.assertEqual(result.text, 'before[]after') - - # Esc da( - result, cli = feed('before(inside)after\x1b8hda(\n') - self.assertEqual(result.text, 'beforeafter') - - def test_vi_digraphs(self): - feed = partial(_feed_cli_with_input, editing_mode=EditingMode.VI) - - # C-K o/ - result, cli = feed('hello\x0bo/\n') - self.assertEqual(result.text, 'helloø') - - # C-K e: - result, cli = feed('hello\x0be:\n') - self.assertEqual(result.text, 'helloë') diff --git a/tests/contrib_tests.py b/tests/contrib_tests.py deleted file mode 100644 index 2262d5633..000000000 --- a/tests/contrib_tests.py +++ /dev/null @@ -1,249 +0,0 @@ -from __future__ import unicode_literals -from __future__ import absolute_import -from __future__ import print_function - -import os -import shutil -import tempfile -import unittest - -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 - - -class chdir(object): - """Context manager for current working directory temporary change.""" - def __init__(self, directory): - self.new_dir = directory - self.orig_dir = os.getcwd() - - def __enter__(self): - os.chdir(self.new_dir) - - def __exit__(self, *args): - os.chdir(self.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')) - - -class PathCompleterTest(unittest.TestCase): - - def test_pathcompleter_completes_in_current_directory(self): - completer = PathCompleter() - doc_text = '' - doc = Document(doc_text, len(doc_text)) - event = CompleteEvent() - completions = list(completer.get_completions(doc, event)) - self.assertTrue(len(completions) > 0) - - def test_pathcompleter_completes_files_in_current_directory(self): - # 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) - self.assertEqual(expected, result) - - # cleanup - shutil.rmtree(test_dir) - - def test_pathcompleter_completes_files_in_absolute_directory(self): - # 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]) - self.assertEqual(expected, result) - - # cleanup - shutil.rmtree(test_dir) - - def test_pathcompleter_completes_directories_with_only_directories(self): - # 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] - self.assertEqual(['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)) - self.assertEqual([], completions) - - # cleanup - shutil.rmtree(test_dir) - - def test_pathcompleter_respects_completions_under_min_input_len(self): - # 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)) - self.assertEqual([], 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] - self.assertEqual([''], 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] - self.assertEqual([''], 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) - self.assertEqual(['', '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)) - self.assertEqual([], completions) - - # cleanup - shutil.rmtree(test_dir) - - def test_pathcompleter_does_not_expanduser_by_default(self): - completer = PathCompleter() - doc_text = '~' - doc = Document(doc_text, len(doc_text)) - event = CompleteEvent() - completions = list(completer.get_completions(doc, event)) - self.assertEqual([], completions) - - def test_pathcompleter_can_expanduser(self): - completer = PathCompleter(expanduser=True) - doc_text = '~' - doc = Document(doc_text, len(doc_text)) - event = CompleteEvent() - completions = list(completer.get_completions(doc, event)) - self.assertTrue(len(completions) > 0) - - def test_pathcompleter_can_apply_file_filter(self): - # 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] - self.assertEqual(['my.csv'], result) - - # cleanup - shutil.rmtree(test_dir) - - def test_pathcompleter_get_paths_constrains_path(self): - # 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'] - self.assertEqual(expected, result) - - # cleanup - shutil.rmtree(test_dir) diff --git a/tests/document/test_document.py b/tests/document/test_document.py new file mode 100644 index 000000000..8c0cf5704 --- /dev/null +++ b/tests/document/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/tests/document_tests/__init__.py b/tests/document_tests/__init__.py deleted file mode 100644 index 9ed173780..000000000 --- a/tests/document_tests/__init__.py +++ /dev/null @@ -1,65 +0,0 @@ -from __future__ import unicode_literals - -from prompt_toolkit.document import Document - -import unittest - - -class DocumentTest(unittest.TestCase): - def setUp(self): - self.document = Document( - 'line 1\n' + - 'line 2\n' + - 'line 3\n' + - 'line 4\n', - len('line 1\n' + 'lin') - ) - - def test_current_char(self): - self.assertEqual(self.document.current_char, 'e') - - def test_text_before_cursor(self): - self.assertEqual(self.document.text_before_cursor, 'line 1\nlin') - - def test_text_after_cursor(self): - self.assertEqual(self.document.text_after_cursor, - 'e 2\n' + - 'line 3\n' + - 'line 4\n') - - def test_lines(self): - self.assertEqual(self.document.lines, [ - 'line 1', - 'line 2', - 'line 3', - 'line 4', '']) - - def test_line_count(self): - self.assertEqual(self.document.line_count, 5) - - def test_current_line_before_cursor(self): - self.assertEqual(self.document.current_line_before_cursor, 'lin') - - def test_current_line_after_cursor(self): - self.assertEqual(self.document.current_line_after_cursor, 'e 2') - - def test_current_line(self): - self.assertEqual(self.document.current_line, 'line 2') - - def test_cursor_position(self): - self.assertEqual(self.document.cursor_position_row, 1) - self.assertEqual(self.document.cursor_position_col, 3) - - d = Document('', 0) - self.assertEqual(d.cursor_position_row, 0) - self.assertEqual(d.cursor_position_col, 0) - - def test_translate_index_to_position(self): - pos = self.document.translate_index_to_position( - len('line 1\nline 2\nlin')) - - self.assertEqual(pos[0], 2) - self.assertEqual(pos[1], 3) - - pos = self.document.translate_index_to_position(0) - self.assertEqual(pos, (0, 0)) diff --git a/tests/filter_tests.py b/tests/filter_tests.py deleted file mode 100644 index 483c082f3..000000000 --- a/tests/filter_tests.py +++ /dev/null @@ -1,153 +0,0 @@ -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 unittest - - -class FilterTest(unittest.TestCase): - def test_condition_filter_args(self): - c = Condition(lambda a, b, c:True) - self.assertTrue(c.test_args('a', 'b', 'c')) - self.assertFalse(c.test_args()) - self.assertFalse(c.test_args('a')) - self.assertFalse(c.test_args('a', 'b')) - self.assertFalse(c.test_args('a', 'b', 'c', 'd')) - - c2 = Condition(lambda a, b=1:True) - self.assertTrue(c2.test_args('a')) - self.assertTrue(c2.test_args('a', 'b')) - self.assertFalse(c2.test_args('a', 'b', 'c')) - self.assertFalse(c2.test_args()) - - c3 = Condition(lambda *a: True) - self.assertTrue(c3.test_args()) - self.assertTrue(c3.test_args('a')) - self.assertTrue(c3.test_args('a', 'b')) - - def test_and_arg(self): - c1 = Condition(lambda a: True) - c2 = Condition(lambda a: True) - c3 = c1 & c2 - - self.assertTrue(c3.test_args('a')) - self.assertFalse(c3.test_args()) - self.assertFalse(c3.test_args('a', 'b')) - - def test_or_arg(self): - c1 = Condition(lambda a: True) - c2 = Condition(lambda a: True) - c3 = c1 | c2 - - self.assertTrue(c3.test_args('a')) - self.assertFalse(c3.test_args()) - self.assertFalse(c3.test_args('a', 'b')) - - def test_condition(self): - c = Condition(lambda a: a % 2 == 0) - self.assertTrue(c(4)) - self.assertTrue(c(6)) - self.assertFalse(c(5)) - self.assertFalse(c(3)) - - def test_never(self): - self.assertFalse(Never()()) - - def test_always(self): - self.assertTrue(Always()()) - - def test_invert(self): - self.assertFalse((~Always())()) - self.assertTrue((~Never()())) - - c = ~Condition(lambda: False) - self.assertTrue(c()) - - def test_or(self): - for a in (True, False): - for b in (True, False): - c1 = Condition(lambda: a) - c2 = Condition(lambda: b) - c3 = c1 | c2 - - self.assertTrue(isinstance(c3, Filter)) - self.assertEqual(c3(), a or b) - - def test_and(self): - for a in (True, False): - for b in (True, False): - c1 = Condition(lambda: a) - c2 = Condition(lambda: b) - c3 = c1 & c2 - - self.assertTrue(isinstance(c3, Filter)) - self.assertEqual(c3(), a and b) - - def test_cli_filter(self): - c1 = Condition(lambda cli: True) - self.assertTrue(isinstance(c1, CLIFilter)) - self.assertFalse(isinstance(c1, SimpleFilter)) - - c2 = Condition(lambda: True) - self.assertFalse(isinstance(c2, CLIFilter)) - self.assertTrue(isinstance(c2, SimpleFilter)) - - c3 = c1 | c2 - self.assertFalse(isinstance(c3, CLIFilter)) - self.assertFalse(isinstance(c3, SimpleFilter)) - - c4 = Condition(lambda cli: True) - c5 = Condition(lambda cli: True) - c6 = c4 & c5 - c7 = c4 | c5 - self.assertTrue(isinstance(c6, CLIFilter)) - self.assertTrue(isinstance(c7, CLIFilter)) - self.assertFalse(isinstance(c6, SimpleFilter)) - self.assertFalse(isinstance(c7, SimpleFilter)) - - c8 = Condition(lambda *args: True) - self.assertTrue(isinstance(c8, CLIFilter)) - self.assertTrue(isinstance(c8, SimpleFilter)) - - def test_to_cli_filter(self): - 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)) - - self.assertTrue(isinstance(f1, CLIFilter)) - self.assertTrue(isinstance(f2, CLIFilter)) - self.assertTrue(isinstance(f3, CLIFilter)) - self.assertTrue(isinstance(f4, CLIFilter)) - self.assertTrue(f1(None)) - self.assertFalse(f2(None)) - self.assertTrue(f3(None)) - self.assertFalse(f4(None)) - - self.assertRaises(TypeError, to_cli_filter, 4) - self.assertRaises(TypeError, to_cli_filter, Condition(lambda: True)) - - def test_to_simple_filter(self): - f1 = to_simple_filter(True) - f2 = to_simple_filter(False) - f3 = to_simple_filter(Condition(lambda: True)) - f4 = to_simple_filter(Condition(lambda: False)) - - self.assertTrue(isinstance(f1, SimpleFilter)) - self.assertTrue(isinstance(f2, SimpleFilter)) - self.assertTrue(isinstance(f3, SimpleFilter)) - self.assertTrue(isinstance(f4, SimpleFilter)) - self.assertTrue(f1()) - self.assertFalse(f2()) - self.assertTrue(f3()) - self.assertFalse(f4()) - - self.assertRaises(TypeError, to_simple_filter, 4) - self.assertRaises(TypeError, to_simple_filter, Condition(lambda cli: True)) - - def test_cli_filters(self): - self.assertTrue(isinstance(HasArg(), CLIFilter)) - self.assertTrue(isinstance(HasFocus('BUFFER_NAME'), CLIFilter)) - self.assertTrue(isinstance(HasSelection(), CLIFilter)) diff --git a/tests/inputstream/test_inputstream.py b/tests/inputstream/test_inputstream.py new file mode 100644 index 000000000..de10c0f9a --- /dev/null +++ b/tests/inputstream/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/tests/inputstream_tests/__init__.py b/tests/inputstream_tests/__init__.py deleted file mode 100644 index 27a3198d6..000000000 --- a/tests/inputstream_tests/__init__.py +++ /dev/null @@ -1,125 +0,0 @@ -from __future__ import unicode_literals - -from prompt_toolkit.terminal.vt100_input import InputStream -from prompt_toolkit.keys import Keys - -import unittest - - -class InputStreamTest(unittest.TestCase): - def setUp(self): - class _ProcessorMock(object): - def __init__(self): - self.keys = [] - - def feed_key(self, key_press): - self.keys.append(key_press) - - self.processor = _ProcessorMock() - self.stream = InputStream(self.processor.feed_key) - - def test_control_keys(self): - self.stream.feed('\x01\x02\x10') - - self.assertEqual(len(self.processor.keys), 3) - self.assertEqual(self.processor.keys[0].key, Keys.ControlA) - self.assertEqual(self.processor.keys[1].key, Keys.ControlB) - self.assertEqual(self.processor.keys[2].key, Keys.ControlP) - self.assertEqual(self.processor.keys[0].data, '\x01') - self.assertEqual(self.processor.keys[1].data, '\x02') - self.assertEqual(self.processor.keys[2].data, '\x10') - - def test_arrows(self): - self.stream.feed('\x1b[A\x1b[B\x1b[C\x1b[D') - - self.assertEqual(len(self.processor.keys), 4) - self.assertEqual(self.processor.keys[0].key, Keys.Up) - self.assertEqual(self.processor.keys[1].key, Keys.Down) - self.assertEqual(self.processor.keys[2].key, Keys.Right) - self.assertEqual(self.processor.keys[3].key, Keys.Left) - self.assertEqual(self.processor.keys[0].data, '\x1b[A') - self.assertEqual(self.processor.keys[1].data, '\x1b[B') - self.assertEqual(self.processor.keys[2].data, '\x1b[C') - self.assertEqual(self.processor.keys[3].data, '\x1b[D') - - def test_escape(self): - self.stream.feed('\x1bhello') - - self.assertEqual(len(self.processor.keys), 1 + len('hello')) - self.assertEqual(self.processor.keys[0].key, Keys.Escape) - self.assertEqual(self.processor.keys[1].key, 'h') - self.assertEqual(self.processor.keys[0].data, '\x1b') - self.assertEqual(self.processor.keys[1].data, 'h') - - def test_special_double_keys(self): - self.stream.feed('\x1b[1;3D') # Should both send escape and left. - - self.assertEqual(len(self.processor.keys), 2) - self.assertEqual(self.processor.keys[0].key, Keys.Escape) - self.assertEqual(self.processor.keys[1].key, Keys.Left) - self.assertEqual(self.processor.keys[0].data, '\x1b[1;3D') - self.assertEqual(self.processor.keys[1].data, '\x1b[1;3D') - - def test_flush_1(self): - # Send left key in two parts without flush. - self.stream.feed('\x1b') - self.stream.feed('[D') - - self.assertEqual(len(self.processor.keys), 1) - self.assertEqual(self.processor.keys[0].key, Keys.Left) - self.assertEqual(self.processor.keys[0].data, '\x1b[D') - - def test_flush_2(self): - # 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. - self.stream.feed('\x1b') - self.stream.flush() - self.stream.feed('[D') - - self.assertEqual(len(self.processor.keys), 3) - self.assertEqual(self.processor.keys[0].key, Keys.Escape) - self.assertEqual(self.processor.keys[1].key, '[') - self.assertEqual(self.processor.keys[2].key, 'D') - - self.assertEqual(self.processor.keys[0].data, '\x1b') - self.assertEqual(self.processor.keys[1].data, '[') - self.assertEqual(self.processor.keys[2].data, 'D') - - def test_meta_arrows(self): - self.stream.feed('\x1b\x1b[D') - - self.assertEqual(len(self.processor.keys), 2) - self.assertEqual(self.processor.keys[0].key, Keys.Escape) - self.assertEqual(self.processor.keys[1].key, Keys.Left) - - def test_control_square_close(self): - self.stream.feed('\x1dC') - - self.assertEqual(len(self.processor.keys), 2) - self.assertEqual(self.processor.keys[0].key, Keys.ControlSquareClose) - self.assertEqual(self.processor.keys[1].key, 'C') - - def test_invalid(self): - # Invalid sequence that has at two characters in common with other - # sequences. - self.stream.feed('\x1b[*') - - self.assertEqual(len(self.processor.keys), 3) - self.assertEqual(self.processor.keys[0].key, Keys.Escape) - self.assertEqual(self.processor.keys[1].key, '[') - self.assertEqual(self.processor.keys[2].key, '*') - - def test_cpr_response(self): - self.stream.feed('a\x1b[40;10Rb') - self.assertEqual(len(self.processor.keys), 3) - self.assertEqual(self.processor.keys[0].key, 'a') - self.assertEqual(self.processor.keys[1].key, Keys.CPRResponse) - self.assertEqual(self.processor.keys[2].key, 'b') - - def test_cpr_response_2(self): - # Make sure that the newline is not included in the CPR response. - self.stream.feed('\x1b[40;1R\n') - self.assertEqual(len(self.processor.keys), 2) - self.assertEqual(self.processor.keys[0].key, Keys.CPRResponse) - self.assertEqual(self.processor.keys[1].key, Keys.ControlJ) diff --git a/tests/key_binding/test_key_binding.py b/tests/key_binding/test_key_binding.py new file mode 100644 index 000000000..04f6b5f82 --- /dev/null +++ b/tests/key_binding/test_key_binding.py @@ -0,0 +1,98 @@ +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'] diff --git a/tests/key_binding_tests/__init__.py b/tests/key_binding_tests/__init__.py deleted file mode 100644 index 5892fd580..000000000 --- a/tests/key_binding_tests/__init__.py +++ /dev/null @@ -1,82 +0,0 @@ -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 unittest - - -class KeyBindingTest(unittest.TestCase): - def setUp(self): - class Handlers(object): - def __init__(self): - self.called = [] - - def __getattr__(self, name): - def func(event): - self.called.append(name) - return func - - self.handlers = Handlers() - - self.registry = Registry() - self.registry.add_binding(Keys.ControlX, Keys.ControlC)(self.handlers.controlx_controlc) - self.registry.add_binding(Keys.ControlX)(self.handlers.control_x) - self.registry.add_binding(Keys.ControlD)(self.handlers.control_d) - self.registry.add_binding(Keys.ControlSquareClose, Keys.Any)(self.handlers.control_square_close_any) - - self.processor = InputProcessor(self.registry, lambda: None) - - def test_feed_simple(self): - self.processor.feed(KeyPress(Keys.ControlX, '\x18')) - self.processor.feed(KeyPress(Keys.ControlC, '\x03')) - self.processor.process_keys() - - self.assertEqual(self.handlers.called, ['controlx_controlc']) - - def test_feed_several(self): - # First an unknown key first. - self.processor.feed(KeyPress(Keys.ControlQ, '')) - self.processor.process_keys() - - self.assertEqual(self.handlers.called, []) - - # Followed by a know key sequence. - self.processor.feed(KeyPress(Keys.ControlX, '')) - self.processor.feed(KeyPress(Keys.ControlC, '')) - self.processor.process_keys() - - self.assertEqual(self.handlers.called, ['controlx_controlc']) - - # Followed by another unknown sequence. - self.processor.feed(KeyPress(Keys.ControlR, '')) - self.processor.feed(KeyPress(Keys.ControlS, '')) - - # Followed again by a know key sequence. - self.processor.feed(KeyPress(Keys.ControlD, '')) - self.processor.process_keys() - - self.assertEqual(self.handlers.called, ['controlx_controlc', 'control_d']) - - def test_control_square_closed_any(self): - self.processor.feed(KeyPress(Keys.ControlSquareClose, '')) - self.processor.feed(KeyPress('C', 'C')) - self.processor.process_keys() - - self.assertEqual(self.handlers.called, ['control_square_close_any']) - - def test_common_prefix(self): - # Sending Control_X should not yet do anything, because there is - # another sequence starting with that as well. - self.processor.feed(KeyPress(Keys.ControlX, '')) - self.processor.process_keys() - - self.assertEqual(self.handlers.called, []) - - # When another key is pressed, we know that we did not meant the longer - # "ControlX ControlC" sequence and the callbacks are called. - self.processor.feed(KeyPress(Keys.ControlD, '')) - self.processor.process_keys() - - self.assertEqual(self.handlers.called, ['control_x', 'control_d']) diff --git a/tests/layout/test_layout.py b/tests/layout/test_layout.py new file mode 100644 index 000000000..4f6cdedc5 --- /dev/null +++ b/tests/layout/test_layout.py @@ -0,0 +1,27 @@ +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')], + ] diff --git a/tests/layout_tests/__init__.py b/tests/layout_tests/__init__.py deleted file mode 100644 index 360481714..000000000 --- a/tests/layout_tests/__init__.py +++ /dev/null @@ -1,29 +0,0 @@ -from __future__ import unicode_literals - -from prompt_toolkit.layout.utils import split_lines -from prompt_toolkit.token import Token - -import unittest - - -class SplitLinesTest(unittest.TestCase): - def test_split_lines(self): - lines = list(split_lines([(Token.A, 'line1\nline2\nline3')])) - - self.assertEqual(lines, [ - [(Token.A, 'line1')], - [(Token.A, 'line2')], - [(Token.A, 'line3')], - ]) - - def test_split_lines_2(self): - lines = list(split_lines([ - (Token.A, 'line1'), - (Token.B, 'line2\nline3\nline4') - ])) - - self.assertEqual(lines, [ - [(Token.A, 'line1'), (Token.B, 'line2')], - [(Token.B, 'line3')], - [(Token.B, 'line4')], - ]) diff --git a/tests/regular_languages/test_regular_languages.py b/tests/regular_languages/test_regular_languages.py new file mode 100644 index 000000000..6ece5cdeb --- /dev/null +++ b/tests/regular_languages/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('((?Phello|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[a-z]*) \s+ (?P[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/tests/regular_languages_tests/__init__.py b/tests/regular_languages_tests/__init__.py deleted file mode 100644 index 0cfa01702..000000000 --- a/tests/regular_languages_tests/__init__.py +++ /dev/null @@ -1,102 +0,0 @@ -from __future__ import unicode_literals - -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.completion import Completer, Completion, CompleteEvent -from prompt_toolkit.document import Document - -import unittest - - -class GrammarTest(unittest.TestCase): - def test_simple_match(self): - g = compile('hello|world') - - m = g.match('hello') - self.assertTrue(isinstance(m, Match)) - - m = g.match('world') - self.assertTrue(isinstance(m, Match)) - - m = g.match('somethingelse') - self.assertEqual(m, None) - - def test_variable_varname(self): - """ - Test `Variable` with varname. - """ - g = compile('((?Phello|world)|test)') - - m = g.match('hello') - variables = m.variables() - self.assertTrue(isinstance(variables, Variables)) - self.assertEqual(variables.get('varname'), 'hello') - self.assertEqual(variables['varname'], 'hello') - - m = g.match('world') - variables = m.variables() - self.assertTrue(isinstance(variables, Variables)) - self.assertEqual(variables.get('varname'), 'world') - self.assertEqual(variables['varname'], 'world') - - m = g.match('test') - variables = m.variables() - self.assertTrue(isinstance(variables, Variables)) - self.assertEqual(variables.get('varname'), None) - self.assertEqual(variables['varname'], None) - - def test_prefix(self): - """ - Test `match_prefix`. - """ - g = compile(r'(hello\ world|something\ else)') - - m = g.match_prefix('hello world') - self.assertTrue(isinstance(m, Match)) - - m = g.match_prefix('he') - self.assertTrue(isinstance(m, Match)) - - m = g.match_prefix('') - self.assertTrue(isinstance(m, Match)) - - m = g.match_prefix('som') - self.assertTrue(isinstance(m, Match)) - - m = g.match_prefix('hello wor') - self.assertTrue(isinstance(m, Match)) - - m = g.match_prefix('no-match') - self.assertEqual(m.trailing_input().start, 0) - self.assertEqual(m.trailing_input().stop, len('no-match')) - - m = g.match_prefix('hellotest') - self.assertEqual(m.trailing_input().start, len('hello')) - self.assertEqual(m.trailing_input().stop, len('hellotest')) - - def test_completer(self): - 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[a-z]*) \s+ (?P[a-z]*)') - - # Test 'get_completions()' - completer = GrammarCompleter(g, {'var1': completer1(), 'var2': completer2()}) - completions = list(completer.get_completions( - Document('abc def', len('abc def')), - CompleteEvent())) - - self.assertEqual(len(completions), 2) - self.assertEqual(completions[0].text, 'before2-def-after2') - self.assertEqual(completions[0].start_position, -3) - self.assertEqual(completions[1].text, 'before2-def-after2-B') - self.assertEqual(completions[1].start_position, -3) diff --git a/tests/run_tests.py b/tests/run_tests.py deleted file mode 100755 index e550abffe..000000000 --- a/tests/run_tests.py +++ /dev/null @@ -1,61 +0,0 @@ -#!/usr/bin/env python -from __future__ import unicode_literals - -from buffer_tests import * -from contrib_tests import * -from document_tests import * -from inputstream_tests import * -from key_binding_tests import * -from layout_tests import * -from regular_languages_tests import * -from style_tests import * -from utils_tests import * -from filter_tests import * -from cli_tests import * - -import unittest - -# Import modules for syntax checking. -import prompt_toolkit -import prompt_toolkit.application -import prompt_toolkit.auto_suggest -import prompt_toolkit.buffer -import prompt_toolkit.buffer_mapping -import prompt_toolkit.clipboard -import prompt_toolkit.completion -import prompt_toolkit.contrib.completers -import prompt_toolkit.contrib.regular_languages -import prompt_toolkit.contrib.telnet -import prompt_toolkit.contrib.validators -import prompt_toolkit.document -import prompt_toolkit.enums -import prompt_toolkit.eventloop.base -import prompt_toolkit.eventloop.inputhook -import prompt_toolkit.eventloop.posix -import prompt_toolkit.eventloop.posix_utils -import prompt_toolkit.eventloop.utils -import prompt_toolkit.filters.base -import prompt_toolkit.filters.cli -import prompt_toolkit.filters.types -import prompt_toolkit.filters.utils -import prompt_toolkit.history -import prompt_toolkit.input -import prompt_toolkit.interface -import prompt_toolkit.key_binding -import prompt_toolkit.keys -import prompt_toolkit.layout -import prompt_toolkit.output -import prompt_toolkit.reactive -import prompt_toolkit.renderer -import prompt_toolkit.search_state -import prompt_toolkit.selection -import prompt_toolkit.shortcuts -import prompt_toolkit.styles -import prompt_toolkit.terminal -import prompt_toolkit.terminal.vt100_input -import prompt_toolkit.terminal.vt100_output -import prompt_toolkit.utils -import prompt_toolkit.validation - -if __name__ == '__main__': - unittest.main() diff --git a/tests/style_tests.py b/tests/style_tests.py deleted file mode 100644 index cd92143e4..000000000 --- a/tests/style_tests.py +++ /dev/null @@ -1,41 +0,0 @@ -from __future__ import unicode_literals -from prompt_toolkit.styles import Attrs, style_from_dict -from prompt_toolkit.token import Token - -import unittest - - -class StyleFromDictTest(unittest.TestCase): - def test_style_from_dict(self): - 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(self): - 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) - self.assertEqual(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) - self.assertEqual(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) - self.assertEqual(style.get_attrs_for_token(Token.A.B.C.D.E), expected) diff --git a/tests/test_cli.py b/tests/test_cli.py new file mode 100644 index 000000000..e9ba9f4d7 --- /dev/null +++ b/tests/test_cli.py @@ -0,0 +1,212 @@ +# 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.enums import DEFAULT_BUFFER, EditingMode +from prompt_toolkit.eventloop.posix import PosixEventLoop +from prompt_toolkit.input import PipeInput +from prompt_toolkit.interface import CommandLineInterface +from prompt_toolkit.output import DummyOutput +from functools import partial + + +def _feed_cli_with_input(text, editing_mode=EditingMode.EMACS): + """ + 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. + assert text.endswith('\n') + + loop = PosixEventLoop() + try: + inp = PipeInput() + inp.send_text(text) + cli = CommandLineInterface( + application=Application(editing_mode=editing_mode), + eventloop=loop, + input=inp, + output=DummyOutput()) + 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 + result, cli = _feed_cli_with_input('hello\x01X\n') + assert result.text == 'Xhello' + + # 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 (Emacs cursor left.) + result, cli = _feed_cli_with_input('hello\x02X\n') + assert result.text == 'hellXo' + + # ControlC: ignored by default, unless the prompt-bindings are loaded. + result, cli = _feed_cli_with_input('hello\x03\n') + assert result.text == 'hello' + + # ControlD: ignored by default, unless the prompt-bindings are loaded. + result, cli = _feed_cli_with_input('hello\x04\n') + assert result.text == 'hello' + + # Left, Left, ControlK + result, cli = _feed_cli_with_input('hello\x1b[D\x1b[D\x0b\n') + assert result.text == 'hel' + + # ControlL: should not influence the result. + result, cli = _feed_cli_with_input('hello\x0c\n') + assert result.text == 'hello' + + +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' + + # Delete word before the cursor. + result, cli = _feed_cli_with_input('hello world test\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' + + +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 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 e: + result, cli = feed('hello\x0be:\n') + assert result.text == 'helloë' diff --git a/tests/test_contrib.py b/tests/test_contrib.py new file mode 100644 index 000000000..cce640ade --- /dev/null +++ b/tests/test_contrib.py @@ -0,0 +1,253 @@ +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(): + 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/tests/test_filter.py b/tests/test_filter.py new file mode 100644 index 000000000..5aee62a9a --- /dev/null +++ b/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/tests/test_style.py b/tests/test_style.py new file mode 100644 index 000000000..fc839ec95 --- /dev/null +++ b/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/tests/utils/test_utils.py b/tests/utils/test_utils.py new file mode 100644 index 000000000..1e3d92cda --- /dev/null +++ b/tests/utils/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/tests/utils_tests/__init__.py b/tests/utils_tests/__init__.py deleted file mode 100644 index 7cafc0a6a..000000000 --- a/tests/utils_tests/__init__.py +++ /dev/null @@ -1,41 +0,0 @@ -from __future__ import unicode_literals - -from prompt_toolkit.utils import take_using_weights - -import unittest -import itertools - - -class SplitLinesTest(unittest.TestCase): - def test_using_weights(self): - 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) - self.assertEqual(data.count('A'), 5) - self.assertEqual(data.count('B'), 10) - self.assertEqual(data.count('C'), 20) - - self.assertEqual(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) - self.assertEqual(data.count('A'), 20) - self.assertEqual(data.count('B'), 10) - self.assertEqual(data.count('C'), 5) - - # Bigger numbers. - data = take(take_using_weights(['A', 'B', 'C'], [20, 10, 5]), 70) - self.assertEqual(data.count('A'), 40) - self.assertEqual(data.count('B'), 20) - self.assertEqual(data.count('C'), 10) - - # Negative numbers. - data = take(take_using_weights(['A', 'B', 'C'], [-20, 10, 0]), 70) - self.assertEqual(data.count('A'), 0) - self.assertEqual(data.count('B'), 70) - self.assertEqual(data.count('C'), 0) diff --git a/tox.ini b/tox.ini index bc4112a35..016beedb9 100644 --- a/tox.ini +++ b/tox.ini @@ -7,4 +7,7 @@ envlist = py26, py27, py33, py34, py35, pypy, pypy3 [testenv] -commands = {toxinidir}/tests/run_tests.py +commands = py.test [] + +deps= + pytest