Uploaded Test files

This commit is contained in:
Batuhan Berk Başoğlu 2020-11-12 11:05:57 -05:00
parent f584ad9d97
commit 2e81cb7d99
16627 changed files with 2065359 additions and 102444 deletions

View file

@ -0,0 +1,6 @@
import os
import sys
no_display = (sys.platform not in ('darwin', 'win32') and
os.environ.get('DISPLAY', '') == '')

View file

@ -0,0 +1,610 @@
import sys
import unittest
from flaky import flaky
import pytest
from qtpy import QtCore, QtGui, QtWidgets
from qtpy.QtTest import QTest
from qtconsole.console_widget import ConsoleWidget
from qtconsole.qtconsoleapp import JupyterQtConsoleApp
from . import no_display
if sys.version[0] == '2': # Python 2
from IPython.core.inputsplitter import InputSplitter as TransformerManager
else:
from IPython.core.inputtransformer2 import TransformerManager
SHELL_TIMEOUT = 20000
@pytest.fixture
def qtconsole(qtbot):
"""Qtconsole fixture."""
# Create a console
console = JupyterQtConsoleApp()
console.initialize(argv=[])
qtbot.addWidget(console.window)
console.window.confirm_exit = False
console.window.show()
return console
@flaky(max_runs=3)
@pytest.mark.parametrize(
"debug", [True, False])
def test_scroll(qtconsole, qtbot, debug):
"""
Make sure the scrolling works.
"""
window = qtconsole.window
shell = window.active_frontend
control = shell._control
scroll_bar = control.verticalScrollBar()
# Wait until the console is fully up
qtbot.waitUntil(lambda: shell._prompt_html is not None,
timeout=SHELL_TIMEOUT)
assert scroll_bar.value() == 0
# Define a function with loads of output
# Check the outputs are working as well
code = ["import time",
"def print_numbers():",
" for i in range(1000):",
" print(i)",
" time.sleep(.01)"]
for line in code:
qtbot.keyClicks(control, line)
qtbot.keyClick(control, QtCore.Qt.Key_Enter)
with qtbot.waitSignal(shell.executed):
qtbot.keyClick(control, QtCore.Qt.Key_Enter,
modifier=QtCore.Qt.ShiftModifier)
def run_line(line, block=True):
qtbot.keyClicks(control, line)
if block:
with qtbot.waitSignal(shell.executed):
qtbot.keyClick(control, QtCore.Qt.Key_Enter,
modifier=QtCore.Qt.ShiftModifier)
else:
qtbot.keyClick(control, QtCore.Qt.Key_Enter,
modifier=QtCore.Qt.ShiftModifier)
if debug:
# Enter debug
run_line('%debug print()', block=False)
qtbot.keyClick(control, QtCore.Qt.Key_Enter)
# redefine run_line
def run_line(line, block=True):
qtbot.keyClicks(control, '!' + line)
qtbot.keyClick(control, QtCore.Qt.Key_Enter,
modifier=QtCore.Qt.ShiftModifier)
if block:
qtbot.waitUntil(
lambda: control.toPlainText().strip(
).split()[-1] == "ipdb>")
prev_position = scroll_bar.value()
# Create a bunch of inputs
for i in range(20):
run_line('a = 1')
assert scroll_bar.value() > prev_position
# Put the scroll bar higher and check it doesn't move
prev_position = scroll_bar.value() + scroll_bar.pageStep() // 2
scroll_bar.setValue(prev_position)
for i in range(2):
run_line('a')
assert scroll_bar.value() == prev_position
# add more input and check it moved
for i in range(10):
run_line('a')
assert scroll_bar.value() > prev_position
prev_position = scroll_bar.value()
# Run the printing function
run_line('print_numbers()', block=False)
qtbot.wait(1000)
# Check everything advances
assert scroll_bar.value() > prev_position
# move up
prev_position = scroll_bar.value() - scroll_bar.pageStep()
scroll_bar.setValue(prev_position)
qtbot.wait(1000)
# Check position stayed the same
assert scroll_bar.value() == prev_position
# reset position
prev_position = scroll_bar.maximum() - (scroll_bar.pageStep() * 8) // 10
scroll_bar.setValue(prev_position)
qtbot.wait(1000)
assert scroll_bar.value() > prev_position
@flaky(max_runs=3)
def test_input(qtconsole, qtbot):
"""
Test input function
"""
window = qtconsole.window
shell = window.active_frontend
control = shell._control
# Wait until the console is fully up
qtbot.waitUntil(lambda: shell._prompt_html is not None,
timeout=SHELL_TIMEOUT)
with qtbot.waitSignal(shell.executed):
shell.execute("import time")
if sys.version[0] == '2':
input_function = 'raw_input'
else:
input_function = 'input'
shell.execute("print(" + input_function + "('name: ')); time.sleep(3)")
qtbot.waitUntil(lambda: control.toPlainText().split()[-1] == 'name:')
qtbot.keyClicks(control, 'test')
qtbot.keyClick(control, QtCore.Qt.Key_Enter)
qtbot.waitUntil(lambda: not shell._reading)
qtbot.keyClick(control, 'z', modifier=QtCore.Qt.ControlModifier)
for i in range(10):
qtbot.keyClick(control, QtCore.Qt.Key_Backspace)
qtbot.waitUntil(lambda: shell._prompt_html is not None,
timeout=SHELL_TIMEOUT)
assert 'name: test\ntest' in control.toPlainText()
@flaky(max_runs=3)
def test_debug(qtconsole, qtbot):
"""
Make sure the cursor works while debugging
It might not because the console is "_executing"
"""
window = qtconsole.window
shell = window.active_frontend
control = shell._control
# Wait until the console is fully up
qtbot.waitUntil(lambda: shell._prompt_html is not None,
timeout=SHELL_TIMEOUT)
# Enter execution
code = "%debug range(1)"
qtbot.keyClicks(control, code)
qtbot.keyClick(control, QtCore.Qt.Key_Enter,
modifier=QtCore.Qt.ShiftModifier)
qtbot.waitUntil(
lambda: control.toPlainText().strip().split()[-1] == "ipdb>",
timeout=SHELL_TIMEOUT)
# We should be able to move the cursor while debugging
qtbot.keyClicks(control, "abd")
qtbot.wait(100)
qtbot.keyClick(control, QtCore.Qt.Key_Left)
qtbot.keyClick(control, 'c')
qtbot.wait(100)
assert control.toPlainText().strip().split()[-1] == "abcd"
@pytest.mark.skipif(no_display, reason="Doesn't work without a display")
class TestConsoleWidget(unittest.TestCase):
@classmethod
def setUpClass(cls):
""" Create the application for the test case.
"""
cls._app = QtWidgets.QApplication.instance()
if cls._app is None:
cls._app = QtWidgets.QApplication([])
cls._app.setQuitOnLastWindowClosed(False)
@classmethod
def tearDownClass(cls):
""" Exit the application.
"""
QtWidgets.QApplication.quit()
def assert_text_equal(self, cursor, text):
cursor.select(cursor.Document)
selection = cursor.selectedText()
self.assertEqual(selection, text)
def test_special_characters(self):
""" Are special characters displayed correctly?
"""
w = ConsoleWidget()
cursor = w._get_prompt_cursor()
test_inputs = ['xyz\b\b=\n',
'foo\b\nbar\n',
'foo\b\nbar\r\n',
'abc\rxyz\b\b=']
expected_outputs = [u'x=z\u2029',
u'foo\u2029bar\u2029',
u'foo\u2029bar\u2029',
'x=z']
for i, text in enumerate(test_inputs):
w._insert_plain_text(cursor, text)
self.assert_text_equal(cursor, expected_outputs[i])
# clear all the text
cursor.insertText('')
def test_link_handling(self):
noKeys = QtCore.Qt
noButton = QtCore.Qt.MouseButton(0)
noButtons = QtCore.Qt.MouseButtons(0)
noModifiers = QtCore.Qt.KeyboardModifiers(0)
MouseMove = QtCore.QEvent.MouseMove
QMouseEvent = QtGui.QMouseEvent
w = ConsoleWidget()
cursor = w._get_prompt_cursor()
w._insert_html(cursor, '<a href="http://python.org">written in</a>')
obj = w._control
tip = QtWidgets.QToolTip
self.assertEqual(tip.text(), u'')
# should be somewhere else
elsewhereEvent = QMouseEvent(MouseMove, QtCore.QPoint(50,50),
noButton, noButtons, noModifiers)
w.eventFilter(obj, elsewhereEvent)
self.assertEqual(tip.isVisible(), False)
self.assertEqual(tip.text(), u'')
# should be over text
overTextEvent = QMouseEvent(MouseMove, QtCore.QPoint(1,5),
noButton, noButtons, noModifiers)
w.eventFilter(obj, overTextEvent)
self.assertEqual(tip.isVisible(), True)
self.assertEqual(tip.text(), "http://python.org")
# should still be over text
stillOverTextEvent = QMouseEvent(MouseMove, QtCore.QPoint(1,5),
noButton, noButtons, noModifiers)
w.eventFilter(obj, stillOverTextEvent)
self.assertEqual(tip.isVisible(), True)
self.assertEqual(tip.text(), "http://python.org")
def test_width_height(self):
# width()/height() QWidget properties should not be overridden.
w = ConsoleWidget()
self.assertEqual(w.width(), QtWidgets.QWidget.width(w))
self.assertEqual(w.height(), QtWidgets.QWidget.height(w))
def test_prompt_cursors(self):
"""Test the cursors that keep track of where the prompt begins and
ends"""
w = ConsoleWidget()
w._prompt = 'prompt>'
doc = w._control.document()
# Fill up the QTextEdit area with the maximum number of blocks
doc.setMaximumBlockCount(10)
for _ in range(9):
w._append_plain_text('line\n')
# Draw the prompt, this should cause the first lines to be deleted
w._show_prompt()
self.assertEqual(doc.blockCount(), 10)
# _prompt_pos should be at the end of the document
self.assertEqual(w._prompt_pos, w._get_end_pos())
# _append_before_prompt_pos should be at the beginning of the prompt
self.assertEqual(w._append_before_prompt_pos,
w._prompt_pos - len(w._prompt))
# insert some more text without drawing a new prompt
w._append_plain_text('line\n')
self.assertEqual(w._prompt_pos,
w._get_end_pos() - len('line\n'))
self.assertEqual(w._append_before_prompt_pos,
w._prompt_pos - len(w._prompt))
# redraw the prompt
w._show_prompt()
self.assertEqual(w._prompt_pos, w._get_end_pos())
self.assertEqual(w._append_before_prompt_pos,
w._prompt_pos - len(w._prompt))
# insert some text before the prompt
w._append_plain_text('line', before_prompt=True)
self.assertEqual(w._prompt_pos, w._get_end_pos())
self.assertEqual(w._append_before_prompt_pos,
w._prompt_pos - len(w._prompt))
def test_select_all(self):
w = ConsoleWidget()
w._append_plain_text('Header\n')
w._prompt = 'prompt>'
w._show_prompt()
control = w._control
app = QtWidgets.QApplication.instance()
cursor = w._get_cursor()
w._insert_plain_text_into_buffer(cursor, "if:\n pass")
cursor.clearSelection()
control.setTextCursor(cursor)
# "select all" action selects cell first
w.select_all_smart()
QTest.keyClick(control, QtCore.Qt.Key_C, QtCore.Qt.ControlModifier)
copied = app.clipboard().text()
self.assertEqual(copied, 'if:\n> pass')
# # "select all" action triggered a second time selects whole document
w.select_all_smart()
QTest.keyClick(control, QtCore.Qt.Key_C, QtCore.Qt.ControlModifier)
copied = app.clipboard().text()
self.assertEqual(copied, 'Header\nprompt>if:\n> pass')
def test_keypresses(self):
"""Test the event handling code for keypresses."""
w = ConsoleWidget()
w._append_plain_text('Header\n')
w._prompt = 'prompt>'
w._show_prompt()
app = QtWidgets.QApplication.instance()
control = w._control
# Test setting the input buffer
w._set_input_buffer('test input')
self.assertEqual(w._get_input_buffer(), 'test input')
# Ctrl+K kills input until EOL
w._set_input_buffer('test input')
c = control.textCursor()
c.setPosition(c.position() - 3)
control.setTextCursor(c)
QTest.keyClick(control, QtCore.Qt.Key_K, QtCore.Qt.ControlModifier)
self.assertEqual(w._get_input_buffer(), 'test in')
# Ctrl+V pastes
w._set_input_buffer('test input ')
app.clipboard().setText('pasted text')
QTest.keyClick(control, QtCore.Qt.Key_V, QtCore.Qt.ControlModifier)
self.assertEqual(w._get_input_buffer(), 'test input pasted text')
self.assertEqual(control.document().blockCount(), 2)
# Paste should strip indentation
w._set_input_buffer('test input ')
app.clipboard().setText(' pasted text')
QTest.keyClick(control, QtCore.Qt.Key_V, QtCore.Qt.ControlModifier)
self.assertEqual(w._get_input_buffer(), 'test input pasted text')
self.assertEqual(control.document().blockCount(), 2)
# Multiline paste, should also show continuation marks
w._set_input_buffer('test input ')
app.clipboard().setText('line1\nline2\nline3')
QTest.keyClick(control, QtCore.Qt.Key_V, QtCore.Qt.ControlModifier)
self.assertEqual(w._get_input_buffer(),
'test input line1\nline2\nline3')
self.assertEqual(control.document().blockCount(), 4)
self.assertEqual(control.document().findBlockByNumber(1).text(),
'prompt>test input line1')
self.assertEqual(control.document().findBlockByNumber(2).text(),
'> line2')
self.assertEqual(control.document().findBlockByNumber(3).text(),
'> line3')
# Multiline paste should strip indentation intelligently
# in the case where pasted text has leading whitespace on first line
# and we're pasting into indented position
w._set_input_buffer(' ')
app.clipboard().setText(' If 1:\n pass')
QTest.keyClick(control, QtCore.Qt.Key_V, QtCore.Qt.ControlModifier)
self.assertEqual(w._get_input_buffer(),
' If 1:\n pass')
# Ctrl+Backspace should intelligently remove the last word
w._set_input_buffer("foo = ['foo', 'foo', 'foo', \n"
" 'bar', 'bar', 'bar']")
QTest.keyClick(control, QtCore.Qt.Key_Backspace,
QtCore.Qt.ControlModifier)
self.assertEqual(w._get_input_buffer(),
("foo = ['foo', 'foo', 'foo', \n"
" 'bar', 'bar', '"))
QTest.keyClick(control, QtCore.Qt.Key_Backspace,
QtCore.Qt.ControlModifier)
QTest.keyClick(control, QtCore.Qt.Key_Backspace,
QtCore.Qt.ControlModifier)
self.assertEqual(w._get_input_buffer(),
("foo = ['foo', 'foo', 'foo', \n"
" '"))
QTest.keyClick(control, QtCore.Qt.Key_Backspace,
QtCore.Qt.ControlModifier)
self.assertEqual(w._get_input_buffer(),
("foo = ['foo', 'foo', 'foo', \n"
""))
QTest.keyClick(control, QtCore.Qt.Key_Backspace,
QtCore.Qt.ControlModifier)
self.assertEqual(w._get_input_buffer(),
"foo = ['foo', 'foo', 'foo',")
# Ctrl+Delete should intelligently remove the next word
w._set_input_buffer("foo = ['foo', 'foo', 'foo', \n"
" 'bar', 'bar', 'bar']")
c = control.textCursor()
c.setPosition(35)
control.setTextCursor(c)
QTest.keyClick(control, QtCore.Qt.Key_Delete,
QtCore.Qt.ControlModifier)
self.assertEqual(w._get_input_buffer(),
("foo = ['foo', 'foo', ', \n"
" 'bar', 'bar', 'bar']"))
QTest.keyClick(control, QtCore.Qt.Key_Delete,
QtCore.Qt.ControlModifier)
self.assertEqual(w._get_input_buffer(),
("foo = ['foo', 'foo', \n"
" 'bar', 'bar', 'bar']"))
QTest.keyClick(control, QtCore.Qt.Key_Delete,
QtCore.Qt.ControlModifier)
self.assertEqual(w._get_input_buffer(),
"foo = ['foo', 'foo', 'bar', 'bar', 'bar']")
w._set_input_buffer("foo = ['foo', 'foo', 'foo', \n"
" 'bar', 'bar', 'bar']")
c = control.textCursor()
c.setPosition(48)
control.setTextCursor(c)
QTest.keyClick(control, QtCore.Qt.Key_Delete,
QtCore.Qt.ControlModifier)
self.assertEqual(w._get_input_buffer(),
("foo = ['foo', 'foo', 'foo', \n"
"'bar', 'bar', 'bar']"))
# Left and right keys should respect the continuation prompt
w._set_input_buffer("line 1\n"
"line 2\n"
"line 3")
c = control.textCursor()
c.setPosition(20) # End of line 1
control.setTextCursor(c)
QTest.keyClick(control, QtCore.Qt.Key_Right)
# Cursor should have moved after the continuation prompt
self.assertEqual(control.textCursor().position(), 23)
QTest.keyClick(control, QtCore.Qt.Key_Left)
# Cursor should have moved to the end of the previous line
self.assertEqual(control.textCursor().position(), 20)
# TODO: many more keybindings
def test_indent(self):
"""Test the event handling code for indent/dedent keypresses ."""
w = ConsoleWidget()
w._append_plain_text('Header\n')
w._prompt = 'prompt>'
w._show_prompt()
control = w._control
# TAB with multiline selection should block-indent
w._set_input_buffer("")
c = control.textCursor()
pos=c.position()
w._set_input_buffer("If 1:\n pass")
c.setPosition(pos, QtGui.QTextCursor.KeepAnchor)
control.setTextCursor(c)
QTest.keyClick(control, QtCore.Qt.Key_Tab)
self.assertEqual(w._get_input_buffer()," If 1:\n pass")
# TAB with multiline selection, should block-indent to next multiple
# of 4 spaces, if first line has 0 < indent < 4
w._set_input_buffer("")
c = control.textCursor()
pos=c.position()
w._set_input_buffer(" If 2:\n pass")
c.setPosition(pos, QtGui.QTextCursor.KeepAnchor)
control.setTextCursor(c)
QTest.keyClick(control, QtCore.Qt.Key_Tab)
self.assertEqual(w._get_input_buffer()," If 2:\n pass")
# Shift-TAB with multiline selection should block-dedent
w._set_input_buffer("")
c = control.textCursor()
pos=c.position()
w._set_input_buffer(" If 3:\n pass")
c.setPosition(pos, QtGui.QTextCursor.KeepAnchor)
control.setTextCursor(c)
QTest.keyClick(control, QtCore.Qt.Key_Backtab)
self.assertEqual(w._get_input_buffer(),"If 3:\n pass")
def test_complete(self):
class TestKernelClient(object):
def is_complete(self, source):
calls.append(source)
return msg_id
w = ConsoleWidget()
cursor = w._get_prompt_cursor()
w._execute = lambda *args: calls.append(args)
w.kernel_client = TestKernelClient()
msg_id = object()
calls = []
# test incomplete statement (no _execute called, but indent added)
w.execute("thing", interactive=True)
self.assertEqual(calls, ["thing"])
calls = []
w._handle_is_complete_reply(
dict(parent_header=dict(msg_id=msg_id),
content=dict(status="incomplete", indent="!!!")))
self.assert_text_equal(cursor, u"thing\u2029> !!!")
self.assertEqual(calls, [])
# test complete statement (_execute called)
msg_id = object()
w.execute("else", interactive=True)
self.assertEqual(calls, ["else"])
calls = []
w._handle_is_complete_reply(
dict(parent_header=dict(msg_id=msg_id),
content=dict(status="complete", indent="###")))
self.assertEqual(calls, [("else", False)])
calls = []
self.assert_text_equal(cursor, u"thing\u2029> !!!else\u2029")
# test missing answer from is_complete
msg_id = object()
w.execute("done", interactive=True)
self.assertEqual(calls, ["done"])
calls = []
self.assert_text_equal(cursor, u"thing\u2029> !!!else\u2029")
w._trigger_is_complete_callback()
self.assert_text_equal(cursor, u"thing\u2029> !!!else\u2029\u2029> ")
# assert that late answer isn't destroying anything
w._handle_is_complete_reply(
dict(parent_header=dict(msg_id=msg_id),
content=dict(status="complete", indent="###")))
self.assertEqual(calls, [])
def test_complete_python(self):
"""Test that is_complete is working correctly for Python."""
# Kernel client to test the responses of is_complete
class TestIPyKernelClient(object):
def is_complete(self, source):
tm = TransformerManager()
check_complete = tm.check_complete(source)
responses.append(check_complete)
# Initialize widget
responses = []
w = ConsoleWidget()
w._append_plain_text('Header\n')
w._prompt = 'prompt>'
w._show_prompt()
w.kernel_client = TestIPyKernelClient()
# Execute incomplete statement inside a block
code = '\n'.join(["if True:", " a = 1"])
w._set_input_buffer(code)
w.execute(interactive=True)
assert responses == [('incomplete', 4)]
# Execute complete statement inside a block
responses = []
code = '\n'.join(["if True:", " a = 1\n\n"])
w._set_input_buffer(code)
w.execute(interactive=True)
assert responses == [('complete', None)]

View file

@ -0,0 +1,179 @@
# Standard library imports
import unittest
# Local imports
from qtconsole.ansi_code_processor import AnsiCodeProcessor
class TestAnsiCodeProcessor(unittest.TestCase):
def setUp(self):
self.processor = AnsiCodeProcessor()
def test_clear(self):
""" Do control sequences for clearing the console work?
"""
string = '\x1b[2J\x1b[K'
i = -1
for i, substring in enumerate(self.processor.split_string(string)):
if i == 0:
self.assertEqual(len(self.processor.actions), 1)
action = self.processor.actions[0]
self.assertEqual(action.action, 'erase')
self.assertEqual(action.area, 'screen')
self.assertEqual(action.erase_to, 'all')
elif i == 1:
self.assertEqual(len(self.processor.actions), 1)
action = self.processor.actions[0]
self.assertEqual(action.action, 'erase')
self.assertEqual(action.area, 'line')
self.assertEqual(action.erase_to, 'end')
else:
self.fail('Too many substrings.')
self.assertEqual(i, 1, 'Too few substrings.')
def test_colors(self):
""" Do basic controls sequences for colors work?
"""
string = 'first\x1b[34mblue\x1b[0mlast'
i = -1
for i, substring in enumerate(self.processor.split_string(string)):
if i == 0:
self.assertEqual(substring, 'first')
self.assertEqual(self.processor.foreground_color, None)
elif i == 1:
self.assertEqual(substring, 'blue')
self.assertEqual(self.processor.foreground_color, 4)
elif i == 2:
self.assertEqual(substring, 'last')
self.assertEqual(self.processor.foreground_color, None)
else:
self.fail('Too many substrings.')
self.assertEqual(i, 2, 'Too few substrings.')
def test_colors_xterm(self):
""" Do xterm-specific control sequences for colors work?
"""
string = '\x1b]4;20;rgb:ff/ff/ff\x1b' \
'\x1b]4;25;rgbi:1.0/1.0/1.0\x1b'
substrings = list(self.processor.split_string(string))
desired = { 20 : (255, 255, 255),
25 : (255, 255, 255) }
self.assertEqual(self.processor.color_map, desired)
string = '\x1b[38;5;20m\x1b[48;5;25m'
substrings = list(self.processor.split_string(string))
self.assertEqual(self.processor.foreground_color, 20)
self.assertEqual(self.processor.background_color, 25)
def test_true_color(self):
"""Do 24bit True Color control sequences?
"""
string = '\x1b[38;2;255;100;0m\x1b[48;2;100;100;100m'
substrings = list(self.processor.split_string(string))
self.assertEqual(self.processor.foreground_color, [255, 100, 0])
self.assertEqual(self.processor.background_color, [100, 100, 100])
def test_scroll(self):
""" Do control sequences for scrolling the buffer work?
"""
string = '\x1b[5S\x1b[T'
i = -1
for i, substring in enumerate(self.processor.split_string(string)):
if i == 0:
self.assertEqual(len(self.processor.actions), 1)
action = self.processor.actions[0]
self.assertEqual(action.action, 'scroll')
self.assertEqual(action.dir, 'up')
self.assertEqual(action.unit, 'line')
self.assertEqual(action.count, 5)
elif i == 1:
self.assertEqual(len(self.processor.actions), 1)
action = self.processor.actions[0]
self.assertEqual(action.action, 'scroll')
self.assertEqual(action.dir, 'down')
self.assertEqual(action.unit, 'line')
self.assertEqual(action.count, 1)
else:
self.fail('Too many substrings.')
self.assertEqual(i, 1, 'Too few substrings.')
def test_formfeed(self):
""" Are formfeed characters processed correctly?
"""
string = '\f' # form feed
self.assertEqual(list(self.processor.split_string(string)), [''])
self.assertEqual(len(self.processor.actions), 1)
action = self.processor.actions[0]
self.assertEqual(action.action, 'scroll')
self.assertEqual(action.dir, 'down')
self.assertEqual(action.unit, 'page')
self.assertEqual(action.count, 1)
def test_carriage_return(self):
""" Are carriage return characters processed correctly?
"""
string = 'foo\rbar' # carriage return
splits = []
actions = []
for split in self.processor.split_string(string):
splits.append(split)
actions.append([action.action for action in self.processor.actions])
self.assertEqual(splits, ['foo', None, 'bar'])
self.assertEqual(actions, [[], ['carriage-return'], []])
def test_carriage_return_newline(self):
"""transform CRLF to LF"""
string = 'foo\rbar\r\ncat\r\n\n' # carriage return and newline
# only one CR action should occur, and '\r\n' should transform to '\n'
splits = []
actions = []
for split in self.processor.split_string(string):
splits.append(split)
actions.append([action.action for action in self.processor.actions])
self.assertEqual(splits, ['foo', None, 'bar', '\r\n', 'cat', '\r\n', '\n'])
self.assertEqual(actions, [[], ['carriage-return'], [], ['newline'], [], ['newline'], ['newline']])
def test_beep(self):
""" Are beep characters processed correctly?
"""
string = 'foo\abar' # bell
splits = []
actions = []
for split in self.processor.split_string(string):
splits.append(split)
actions.append([action.action for action in self.processor.actions])
self.assertEqual(splits, ['foo', None, 'bar'])
self.assertEqual(actions, [[], ['beep'], []])
def test_backspace(self):
""" Are backspace characters processed correctly?
"""
string = 'foo\bbar' # backspace
splits = []
actions = []
for split in self.processor.split_string(string):
splits.append(split)
actions.append([action.action for action in self.processor.actions])
self.assertEqual(splits, ['foo', None, 'bar'])
self.assertEqual(actions, [[], ['backspace'], []])
def test_combined(self):
""" Are CR and BS characters processed correctly in combination?
BS is treated as a change in print position, rather than a
backwards character deletion. Therefore a BS at EOL is
effectively ignored.
"""
string = 'abc\rdef\b' # CR and backspace
splits = []
actions = []
for split in self.processor.split_string(string):
splits.append(split)
actions.append([action.action for action in self.processor.actions])
self.assertEqual(splits, ['abc', None, 'def', None])
self.assertEqual(actions, [[], ['carriage-return'], [], ['backspace']])
if __name__ == '__main__':
unittest.main()

View file

@ -0,0 +1,31 @@
"""Test QtConsoleApp"""
# Copyright (c) Jupyter Development Team.
# Distributed under the terms of the Modified BSD License.
import os
import sys
from subprocess import check_output
from jupyter_core import paths
import pytest
from traitlets.tests.utils import check_help_all_output
from . import no_display
@pytest.mark.skipif(no_display, reason="Doesn't work without a display")
def test_help_output():
"""jupyter qtconsole --help-all works"""
check_help_all_output('qtconsole')
@pytest.mark.skipif(no_display, reason="Doesn't work without a display")
@pytest.mark.skipif(os.environ.get('CI', None) is None,
reason="Doesn't work outside of our CIs")
def test_generate_config():
"""jupyter qtconsole --generate-config"""
config_dir = paths.jupyter_config_dir()
check_output([sys.executable, '-m', 'qtconsole', '--generate-config'])
assert os.path.isfile(os.path.join(config_dir,
'jupyter_qtconsole_config.py'))

View file

@ -0,0 +1,162 @@
import time
import sys
import unittest
from ipython_genutils.py3compat import PY3
from jupyter_client.blocking.channels import Empty
from qtconsole.manager import QtKernelManager
PY2 = sys.version[0] == '2'
if PY2:
TimeoutError = RuntimeError
class Tests(unittest.TestCase):
def setUp(self):
"""Open a kernel."""
self.kernel_manager = QtKernelManager()
self.kernel_manager.start_kernel()
self.kernel_client = self.kernel_manager.client()
self.kernel_client.start_channels(shell=True, iopub=True)
self.blocking_client = self.kernel_client.blocking_client()
self.blocking_client.start_channels(shell=True, iopub=True)
self.comm_manager = self.kernel_client.comm_manager
# Check if client is working
self.blocking_client.execute('print(0)')
try:
self._get_next_msg()
self._get_next_msg()
except TimeoutError:
# Maybe it works now?
self.blocking_client.execute('print(0)')
self._get_next_msg()
self._get_next_msg()
def tearDown(self):
"""Close the kernel."""
if self.kernel_manager:
self.kernel_manager.shutdown_kernel(now=True)
if self.kernel_client:
self.kernel_client.shutdown()
def _get_next_msg(self, timeout=10):
# Get status messages
timeout_time = time.time() + timeout
msg_type = 'status'
while msg_type == 'status':
if timeout_time < time.time():
raise TimeoutError
try:
msg = self.blocking_client.get_iopub_msg(timeout=3)
msg_type = msg['header']['msg_type']
except Empty:
pass
return msg
def test_kernel_to_frontend(self):
"""Communicate from the kernel to the frontend."""
comm_manager = self.comm_manager
blocking_client = self.blocking_client
class DummyCommHandler():
def __init__(self):
comm_manager.register_target('test_api', self.comm_open)
self.last_msg = None
def comm_open(self, comm, msg):
comm.on_msg(self.comm_message)
comm.on_close(self.comm_message)
self.last_msg = msg['content']['data']
self.comm = comm
def comm_message(self, msg):
self.last_msg = msg['content']['data']
handler = DummyCommHandler()
blocking_client.execute(
"from ipykernel.comm import Comm\n"
"comm = Comm(target_name='test_api', data='open')\n"
"comm.send('message')\n"
"comm.close('close')\n"
"del comm\n"
"print('Done')\n"
)
# Get input
msg = self._get_next_msg()
assert msg['header']['msg_type'] == 'execute_input'
# Open comm
msg = self._get_next_msg()
assert msg['header']['msg_type'] == 'comm_open'
comm_manager._dispatch(msg)
assert handler.last_msg == 'open'
assert handler.comm.comm_id == msg['content']['comm_id']
# Get message
msg = self._get_next_msg()
assert msg['header']['msg_type'] == 'comm_msg'
comm_manager._dispatch(msg)
assert handler.last_msg == 'message'
assert handler.comm.comm_id == msg['content']['comm_id']
# Get close
msg = self._get_next_msg()
assert msg['header']['msg_type'] == 'comm_close'
comm_manager._dispatch(msg)
assert handler.last_msg == 'close'
assert handler.comm.comm_id == msg['content']['comm_id']
# Get close
msg = self._get_next_msg()
assert msg['header']['msg_type'] == 'stream'
def test_frontend_to_kernel(self):
"""Communicate from the frontend to the kernel."""
comm_manager = self.comm_manager
blocking_client = self.blocking_client
blocking_client.execute(
"class DummyCommHandler():\n"
" def __init__(self):\n"
" get_ipython().kernel.comm_manager.register_target(\n"
" 'test_api', self.comm_open)\n"
" def comm_open(self, comm, msg):\n"
" comm.on_msg(self.comm_message)\n"
" comm.on_close(self.comm_message)\n"
" print(msg['content']['data'])\n"
" def comm_message(self, msg):\n"
" print(msg['content']['data'])\n"
"dummy = DummyCommHandler()\n"
)
# Get input
msg = self._get_next_msg()
assert msg['header']['msg_type'] == 'execute_input'
# Open comm
comm = comm_manager.new_comm('test_api', data='open')
msg = self._get_next_msg()
assert msg['header']['msg_type'] == 'stream'
assert msg['content']['text'] == 'open\n'
# Get message
comm.send('message')
msg = self._get_next_msg()
assert msg['header']['msg_type'] == 'stream'
assert msg['content']['text'] == 'message\n'
# Get close
comm.close('close')
msg = self._get_next_msg()
# Received message has a header and parent header. The parent header has
# the info about the close message type in Python 3
if PY3:
assert msg['parent_header']['msg_type'] == 'comm_close'
assert msg['msg_type'] == 'stream'
assert msg['content']['text'] == 'close\n'
else:
# For some reason ipykernel notifies me that it is closing,
# even though I closed the comm
assert msg['header']['msg_type'] == 'comm_close'
assert comm.comm_id == msg['content']['comm_id']
msg = self._get_next_msg()
assert msg['header']['msg_type'] == 'stream'
if __name__ == "__main__":
unittest.main()

View file

@ -0,0 +1,98 @@
# -*- coding: utf-8 -*-
import os
import tempfile
import shutil
import unittest
import pytest
from qtpy import QtCore, QtWidgets
from qtpy.QtTest import QTest
from qtconsole.console_widget import ConsoleWidget
from qtconsole.completion_widget import CompletionWidget
from . import no_display
class TemporaryDirectory(object):
"""
Context manager for tempfile.mkdtemp().
This class is available in python +v3.2.
See: https://gist.github.com/cpelley/10e2eeaf60dacc7956bb
"""
def __enter__(self):
self.dir_name = tempfile.mkdtemp()
return self.dir_name
def __exit__(self, exc_type, exc_value, traceback):
shutil.rmtree(self.dir_name)
TemporaryDirectory = getattr(tempfile, 'TemporaryDirectory',
TemporaryDirectory)
@pytest.mark.skipif(no_display, reason="Doesn't work without a display")
class TestCompletionWidget(unittest.TestCase):
@classmethod
def setUpClass(cls):
""" Create the application for the test case.
"""
cls._app = QtWidgets.QApplication.instance()
if cls._app is None:
cls._app = QtWidgets.QApplication([])
cls._app.setQuitOnLastWindowClosed(False)
@classmethod
def tearDownClass(cls):
""" Exit the application.
"""
QtWidgets.QApplication.quit()
def setUp(self):
""" Create the main widgets (ConsoleWidget)
"""
self.console = ConsoleWidget()
self.text_edit = self.console._control
def test_droplist_completer_shows(self):
w = CompletionWidget(self.console)
w.show_items(self.text_edit.textCursor(), ["item1", "item2", "item3"])
self.assertTrue(w.isVisible())
def test_droplist_completer_keyboard(self):
w = CompletionWidget(self.console)
w.show_items(self.text_edit.textCursor(), ["item1", "item2", "item3"])
QTest.keyClick(w, QtCore.Qt.Key_PageDown)
QTest.keyClick(w, QtCore.Qt.Key_Enter)
self.assertEqual(self.text_edit.toPlainText(), "item3")
def test_droplist_completer_mousepick(self):
leftButton = QtCore.Qt.LeftButton
w = CompletionWidget(self.console)
w.show_items(self.text_edit.textCursor(), ["item1", "item2", "item3"])
QTest.mouseClick(w.viewport(), leftButton, pos=QtCore.QPoint(19, 8))
QTest.mouseRelease(w.viewport(), leftButton, pos=QtCore.QPoint(19, 8))
QTest.mouseDClick(w.viewport(), leftButton, pos=QtCore.QPoint(19, 8))
self.assertEqual(self.text_edit.toPlainText(), "item1")
self.assertFalse(w.isVisible())
def test_common_path_complete(self):
with TemporaryDirectory() as tmpdir:
items = [
os.path.join(tmpdir, "common/common1/item1"),
os.path.join(tmpdir, "common/common1/item2"),
os.path.join(tmpdir, "common/common1/item3")]
for item in items:
os.makedirs(item)
w = CompletionWidget(self.console)
w.show_items(self.text_edit.textCursor(), items)
self.assertEqual(w.currentItem().text(), '/item1')
QTest.keyClick(w, QtCore.Qt.Key_Down)
self.assertEqual(w.currentItem().text(), '/item2')
QTest.keyClick(w, QtCore.Qt.Key_Down)
self.assertEqual(w.currentItem().text(), '/item3')

View file

@ -0,0 +1,96 @@
import unittest
import pytest
from qtpy import QtWidgets
from qtconsole.frontend_widget import FrontendWidget
from qtpy.QtTest import QTest
from . import no_display
@pytest.mark.skipif(no_display, reason="Doesn't work without a display")
class TestFrontendWidget(unittest.TestCase):
@classmethod
def setUpClass(cls):
""" Create the application for the test case.
"""
cls._app = QtWidgets.QApplication.instance()
if cls._app is None:
cls._app = QtWidgets.QApplication([])
cls._app.setQuitOnLastWindowClosed(False)
@classmethod
def tearDownClass(cls):
""" Exit the application.
"""
QtWidgets.QApplication.quit()
def test_transform_classic_prompt(self):
""" Test detecting classic prompts.
"""
w = FrontendWidget(kind='rich')
t = w._highlighter.transform_classic_prompt
# Base case
self.assertEqual(t('>>> test'), 'test')
self.assertEqual(t(' >>> test'), 'test')
self.assertEqual(t('\t >>> test'), 'test')
# No prompt
self.assertEqual(t(''), '')
self.assertEqual(t('test'), 'test')
# Continuation prompt
self.assertEqual(t('... test'), 'test')
self.assertEqual(t(' ... test'), 'test')
self.assertEqual(t(' ... test'), 'test')
self.assertEqual(t('\t ... test'), 'test')
# Prompts that don't match the 'traditional' prompt
self.assertEqual(t('>>>test'), '>>>test')
self.assertEqual(t('>> test'), '>> test')
self.assertEqual(t('...test'), '...test')
self.assertEqual(t('.. test'), '.. test')
# Prefix indicating input from other clients
self.assertEqual(t('[remote] >>> test'), 'test')
# Random other prefix
self.assertEqual(t('[foo] >>> test'), '[foo] >>> test')
def test_transform_ipy_prompt(self):
""" Test detecting IPython prompts.
"""
w = FrontendWidget(kind='rich')
t = w._highlighter.transform_ipy_prompt
# In prompt
self.assertEqual(t('In [1]: test'), 'test')
self.assertEqual(t('In [2]: test'), 'test')
self.assertEqual(t('In [10]: test'), 'test')
self.assertEqual(t(' In [1]: test'), 'test')
self.assertEqual(t('\t In [1]: test'), 'test')
# No prompt
self.assertEqual(t(''), '')
self.assertEqual(t('test'), 'test')
# Continuation prompt
self.assertEqual(t(' ...: test'), 'test')
self.assertEqual(t(' ...: test'), 'test')
self.assertEqual(t(' ...: test'), 'test')
self.assertEqual(t('\t ...: test'), 'test')
# Prompts that don't match the in-prompt
self.assertEqual(t('In [1]:test'), 'In [1]:test')
self.assertEqual(t('[1]: test'), '[1]: test')
self.assertEqual(t('In: test'), 'In: test')
self.assertEqual(t(': test'), ': test')
self.assertEqual(t('...: test'), '...: test')
# Prefix indicating input from other clients
self.assertEqual(t('[remote] In [1]: test'), 'test')
# Random other prefix
self.assertEqual(t('[foo] In [1]: test'), '[foo] In [1]: test')

View file

@ -0,0 +1,81 @@
import unittest
import pytest
from qtpy import QtWidgets
from qtconsole.client import QtKernelClient
from qtconsole.jupyter_widget import JupyterWidget
from . import no_display
from qtpy.QtTest import QTest
@pytest.mark.skipif(no_display, reason="Doesn't work without a display")
class TestJupyterWidget(unittest.TestCase):
@classmethod
def setUpClass(cls):
""" Create the application for the test case.
"""
cls._app = QtWidgets.QApplication.instance()
if cls._app is None:
cls._app = QtWidgets.QApplication([])
cls._app.setQuitOnLastWindowClosed(False)
@classmethod
def tearDownClass(cls):
""" Exit the application.
"""
QtWidgets.QApplication.quit()
def test_stylesheet_changed(self):
""" Test changing stylesheets.
"""
w = JupyterWidget(kind='rich')
# By default, the background is light. White text is rendered as black
self.assertEqual(w._ansi_processor.get_color(15).name(), '#000000')
# Change to a dark colorscheme. White text is rendered as white
w.syntax_style = 'monokai'
self.assertEqual(w._ansi_processor.get_color(15).name(), '#ffffff')
def test_other_output(self):
""" Test displaying output from other clients.
"""
w = JupyterWidget(kind='rich')
w._append_plain_text('Header\n')
w._show_interpreter_prompt(1)
w.other_output_prefix = '[other] '
w.syntax_style = 'default'
control = w._control
document = control.document()
msg = dict(
execution_count=1,
code='a = 1 + 1\nb = range(10)',
)
w._append_custom(w._insert_other_input, msg, before_prompt=True)
self.assertEqual(document.blockCount(), 6)
self.assertEqual(document.toPlainText(), (
u'Header\n'
u'\n'
u'[other] In [1]: a = 1 + 1\n'
u' ...: b = range(10)\n'
u'\n'
u'In [2]: '
))
# Check proper syntax highlighting
self.assertEqual(document.toHtml(), (
u'<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">\n'
u'<html><head><meta name="qrichtext" content="1" /><style type="text/css">\n'
u'p, li { white-space: pre-wrap; }\n'
u'</style></head><body style=" font-family:\'Monospace\'; font-size:9pt; font-weight:400; font-style:normal;">\n'
u'<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Header</p>\n'
u'<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p>\n'
u'<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" color:#000080;">[other] In [</span><span style=" font-weight:600; color:#000080;">1</span><span style=" color:#000080;">]:</span> a = 1 + 1</p>\n'
u'<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" color:#000080;">\xa0\xa0\xa0\xa0\xa0\xa0\xa0\xa0\xa0\xa0\xa0...:</span> b = range(10)</p>\n'
u'<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p>\n'
u'<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" color:#000080;">In [</span><span style=" font-weight:600; color:#000080;">2</span><span style=" color:#000080;">]:</span> </p></body></html>'
))

View file

@ -0,0 +1,85 @@
import unittest
import pytest
from qtpy import QtGui, QtWidgets
from qtconsole.kill_ring import KillRing, QtKillRing
from . import no_display
@pytest.mark.skipif(no_display, reason="Doesn't work without a display")
class TestKillRing(unittest.TestCase):
@classmethod
def setUpClass(cls):
""" Create the application for the test case.
"""
cls._app = QtWidgets.QApplication.instance()
if cls._app is None:
cls._app = QtWidgets.QApplication([])
cls._app.setQuitOnLastWindowClosed(False)
@classmethod
def tearDownClass(cls):
""" Exit the application.
"""
QtWidgets.QApplication.quit()
def test_generic(self):
""" Does the generic kill ring work?
"""
ring = KillRing()
self.assertTrue(ring.yank() is None)
self.assertTrue(ring.rotate() is None)
ring.kill('foo')
self.assertEqual(ring.yank(), 'foo')
self.assertTrue(ring.rotate() is None)
self.assertEqual(ring.yank(), 'foo')
ring.kill('bar')
self.assertEqual(ring.yank(), 'bar')
self.assertEqual(ring.rotate(), 'foo')
ring.clear()
self.assertTrue(ring.yank() is None)
self.assertTrue(ring.rotate() is None)
def test_qt_basic(self):
""" Does the Qt kill ring work?
"""
text_edit = QtWidgets.QPlainTextEdit()
ring = QtKillRing(text_edit)
ring.kill('foo')
ring.kill('bar')
ring.yank()
ring.rotate()
ring.yank()
self.assertEqual(text_edit.toPlainText(), 'foobar')
text_edit.clear()
ring.kill('baz')
ring.yank()
ring.rotate()
ring.rotate()
ring.rotate()
self.assertEqual(text_edit.toPlainText(), 'foo')
def test_qt_cursor(self):
""" Does the Qt kill ring maintain state with cursor movement?
"""
text_edit = QtWidgets.QPlainTextEdit()
ring = QtKillRing(text_edit)
ring.kill('foo')
ring.kill('bar')
ring.yank()
text_edit.moveCursor(QtGui.QTextCursor.Left)
ring.rotate()
self.assertEqual(text_edit.toPlainText(), 'bar')
if __name__ == '__main__':
import pytest
pytest.main()

View file

@ -0,0 +1,15 @@
import unittest
from qtconsole.styles import dark_color, dark_style
class TestStyles(unittest.TestCase):
def test_dark_color(self):
self.assertTrue(dark_color('#000000')) # black
self.assertTrue(not dark_color('#ffff66')) # bright yellow
self.assertTrue(dark_color('#80807f')) # < 50% gray
self.assertTrue(not dark_color('#808080')) # = 50% gray
def test_dark_style(self):
self.assertTrue(dark_style('monokai'))
self.assertTrue(not dark_style('default'))