Vehicle-Anti-Theft-Face-Rec.../venv/Lib/site-packages/qtconsole/tests/test_00_console_widget.py

611 lines
22 KiB
Python
Raw Normal View History

2020-11-12 16:05:57 +00:00
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)]