gr-mcp/grc/tests/test_qtbot.py

947 lines
29 KiB
Python

import pytest
import gettext
import locale
import threading
import sys
from pytestqt.plugin import qapp
import time
import pyautogui as pag
import logging
from qtpy import QtTest, QtCore, QtGui, QtWidgets, QT6
from os import path, remove
from gnuradio import gr
from grc.gui_qt import properties
from grc.gui_qt.grc import Application
from grc.gui_qt.components.window import MainWindow
from grc.gui_qt.Platform import Platform
log = logging.getLogger("grc")
@pytest.fixture(scope="session")
def qapp_cls_():
settings = properties.Properties([])
settings.argv = [""]
""" Translation Support """
# Try to get the current locale. Always add English
lc, encoding = locale.getlocale()
if lc:
languages = [lc]
languages += settings.DEFAULT_LANGUAGE
log.debug("Using locale - %s" % str(languages))
# Still run even if the english translation isn't found
language = gettext.translation(
settings.APP_NAME, settings.path.LANGUAGE, languages=languages, fallback=True
)
if type(language) == gettext.NullTranslations:
log.error("Unable to find any translation")
log.error("Default English translation missing")
else:
log.info("Using translation - %s" % language.info()["language"])
# Still need to install null translation to let the system handle calls to _()
language.install()
model = Platform(
version=gr.version(),
version_parts=(gr.major_version(), gr.api_version(), gr.minor_version()),
prefs=gr.prefs(),
install_prefix=gr.prefix(),
)
model.build_library()
app = Application(settings, model, [])
app.MainWindow.showMaximized()
return app
def global_pos(block, view):
scene_pos = block.mapToScene(block.boundingRect().center())
global_pos = view.viewport().mapToGlobal(view.mapFromScene(scene_pos))
return global_pos
def type_text(qtbot, app, keys):
for key in keys:
# Each sequence contains a single key.
# That's why we use the first element
keycode = QtGui.QKeySequence(key)[0]
if QT6:
qtbot.keyClick(app.focusWidget(), keycode.key(), QtCore.Qt.NoModifier)
else:
qtbot.keyClick(app.focusWidget(), keycode, QtCore.Qt.NoModifier)
def keystroke(qtbot, app, key):
qtbot.keyClick(app.focusWidget(), key, QtCore.Qt.NoModifier)
qtbot.wait(100)
def ctrl_keystroke(qtbot, app, key):
qtbot.keyClick(app.focusWidget(), key, QtCore.Qt.ControlModifier)
qtbot.wait(100)
def gather_menu_items(menu):
ret = {}
for act in menu.actions():
ret[act.text()] = act
return ret
def add_block_from_query(qtbot, app, query):
qtbot.keyClick(app.focusWidget(), QtCore.Qt.Key_F, QtCore.Qt.ControlModifier)
type_text(qtbot, app, query)
qtbot.wait(10)
pag.press('down')
qtbot.wait(10)
pag.press('enter')
qtbot.wait(10)
def find_blocks(flowgraph, block_type):
blocks = []
for b in flowgraph.blocks:
if b.key == block_type:
blocks.append(b)
if len(blocks) == 0:
return None
if len(blocks) == 1:
return blocks[0]
return blocks
def click_on(qtbot, app, item, button="left"):
scaling = app.MainWindow.screen().devicePixelRatio()
view = app.MainWindow.currentView
click_pos = scaling * global_pos(item.gui, view)
pag.click(click_pos.x(), click_pos.y(), button=button)
qtbot.wait(100)
def undo(qtbot, app):
qtbot.keyClick(app.focusWidget(), QtCore.Qt.Key_Z, QtCore.Qt.ControlModifier)
qtbot.wait(100)
def redo(qtbot, app):
qtbot.keyClick(
app.focusWidget(),
QtCore.Qt.Key_Z,
QtCore.Qt.ControlModifier | QtCore.Qt.ShiftModifier,
)
qtbot.wait(100)
def delete_block(qtbot, app, block):
view = app.MainWindow.currentView
scaling = app.MainWindow.screen().devicePixelRatio()
click_pos = scaling * global_pos(block.gui, view)
pag.click(click_pos.x(), click_pos.y(), button="left")
qtbot.wait(100)
qtbot.keyClick(app.focusWidget(), QtCore.Qt.Key_Delete)
qtbot.wait(100)
def menu_shortcut(qtbot, app, menu_name, menu_key, shortcut_key):
menu = app.MainWindow.menus[menu_name]
qtbot.keyClick(app.focusWidget(), menu_key, QtCore.Qt.AltModifier)
qtbot.wait(100)
qtbot.keyClick(menu, shortcut_key)
qtbot.wait(100)
# Start by closing the flowgraph that pops up on start
def test_file_close_init(qtbot, qapp_cls_, monkeypatch):
win = qapp_cls_.MainWindow
monkeypatch.setattr(
QtWidgets.QMessageBox,
"question",
lambda *args: QtWidgets.QMessageBox.Discard,
)
qtbot.wait(100)
assert win.tabWidget.count() == 1
menu_shortcut(qtbot, qapp_cls_, "file", QtCore.Qt.Key_F, QtCore.Qt.Key_L)
assert win.tabWidget.count() == 1
def test_delete_block(qtbot, qapp_cls_):
qtbot.wait(100)
var = find_blocks(qapp_cls_.MainWindow.currentFlowgraph, "variable")
assert var is not None
delete_block(qtbot, qapp_cls_, var)
qtbot.wait(100)
assert len(qapp_cls_.MainWindow.currentFlowgraph.blocks) == 1
undo(qtbot, qapp_cls_)
assert len(qapp_cls_.MainWindow.currentFlowgraph.blocks) == 2
def test_add_null_sink(qtbot, qapp_cls_):
qtbot.wait(100)
add_block_from_query(qtbot, qapp_cls_, "null sin")
n_sink = find_blocks(qapp_cls_.MainWindow.currentFlowgraph, "blocks_null_sink")
assert n_sink is not None
delete_block(qtbot, qapp_cls_, n_sink)
def test_add_null_source(qtbot, qapp_cls_):
qtbot.wait(100)
add_block_from_query(qtbot, qapp_cls_, "null sou")
n_sou = find_blocks(qapp_cls_.MainWindow.currentFlowgraph, "blocks_null_source")
assert n_sou is not None
delete_block(qtbot, qapp_cls_, n_sou)
def test_add_throttle(qtbot, qapp_cls_):
qtbot.wait(100)
add_block_from_query(qtbot, qapp_cls_, "throttle")
throttle = find_blocks(qapp_cls_.MainWindow.currentFlowgraph, "blocks_throttle")
assert throttle is not None
delete_block(qtbot, qapp_cls_, throttle)
def test_right_click(qtbot, qapp_cls_):
qtbot.wait(100)
add_block_from_query(qtbot, qapp_cls_, "throttle")
throttle = find_blocks(qapp_cls_.MainWindow.currentFlowgraph, "blocks_throttle")
assert throttle is not None
qtbot.wait(100)
def close():
qtbot.keyClick(throttle.gui.right_click_menu, QtCore.Qt.Key_Escape)
QtCore.QTimer.singleShot(200, close)
click_on(qtbot, qapp_cls_, throttle, button="right")
qtbot.wait(100)
delete_block(qtbot, qapp_cls_, throttle)
def test_errors(qtbot, qapp_cls_):
menu = qapp_cls_.MainWindow.menus["build"]
def assert_and_close():
assert qapp_cls_.activeWindow() != qapp_cls_.MainWindow
qtbot.keyClick(qapp_cls_.activeWindow(), QtCore.Qt.Key_Escape)
qtbot.wait(100)
add_block_from_query(qtbot, qapp_cls_, "throttle")
qtbot.wait(100)
qtbot.keyClick(qapp_cls_.focusWidget(), QtCore.Qt.Key_B, QtCore.Qt.AltModifier)
qtbot.wait(100)
QtCore.QTimer.singleShot(200, assert_and_close)
# qtbot.keyClick(menu, QtCore.Qt.Key_E) # Not necessary since it's already selected (it's the first item)
qtbot.keyClick(menu, QtCore.Qt.Key_Enter)
qtbot.wait(300)
throttle = find_blocks(qapp_cls_.MainWindow.currentFlowgraph, "blocks_throttle")
assert throttle is not None
delete_block(qtbot, qapp_cls_, throttle)
def test_open_properties(qtbot, qapp_cls_):
qtbot.wait(100)
qtbot.mouseDClick(
qapp_cls_.MainWindow.currentView.viewport(),
QtCore.Qt.LeftButton,
pos=qapp_cls_.MainWindow.currentView.mapFromScene(
qapp_cls_.MainWindow.currentFlowgraph.options_block.gui.pos() +
QtCore.QPointF(15.0, 15.0)
),
)
qtbot.wait(100)
assert qapp_cls_.MainWindow.currentFlowgraph.options_block.gui.props_dialog.isVisible()
keystroke(qtbot, qapp_cls_, QtCore.Qt.Key_Escape)
assert (
not qapp_cls_.MainWindow.currentFlowgraph.options_block.gui.props_dialog.isVisible()
)
def test_change_id(qtbot, qapp_cls_):
opts = find_blocks(qapp_cls_.MainWindow.currentFlowgraph, "options")
assert opts.params["title"].value == "Not titled yet"
qtbot.mouseDClick(
qapp_cls_.MainWindow.currentView.viewport(),
QtCore.Qt.LeftButton,
pos=qapp_cls_.MainWindow.currentView.mapFromScene(
opts.gui.pos() + QtCore.QPointF(15.0, 15.0)
),
)
qtbot.wait(100)
qtbot.mouseDClick(
opts.gui.props_dialog.edit_params[1],
QtCore.Qt.LeftButton,
)
type_text(qtbot, qapp_cls_, "changed")
qtbot.wait(100)
keystroke(qtbot, qapp_cls_, QtCore.Qt.Key_Enter)
assert opts.params["title"].value == "Not changed yet"
qtbot.wait(100)
keystroke(qtbot, qapp_cls_, QtCore.Qt.Key_Enter)
qtbot.wait(100)
undo(qtbot, qapp_cls_)
assert opts.params["title"].value == "Not titled yet"
redo(qtbot, qapp_cls_)
assert opts.params["title"].value == "Not changed yet"
def test_rotate_block(qtbot, qapp_cls_):
qtbot.wait(100)
opts = find_blocks(qapp_cls_.MainWindow.currentFlowgraph, "options")
# Still running into issues with what has focus depending on the order of tests run.
# This is a problem! Tests should be able to run independently without affecting other
# tests. Need to track this down, but for those tests that are failing for this reason,
# something like below seems to be a workaround
click_on(qtbot, qapp_cls_, opts)
qtbot.wait(400)
click_on(qtbot, qapp_cls_, opts)
old_rotation = opts.states["rotation"]
keystroke(qtbot, qapp_cls_, QtCore.Qt.Key_Left)
new_rotation = opts.states["rotation"]
assert new_rotation == (old_rotation - 90) % 360
undo(qtbot, qapp_cls_)
new_rotation = opts.states["rotation"]
assert new_rotation == old_rotation
keystroke(qtbot, qapp_cls_, QtCore.Qt.Key_Right)
new_rotation = opts.states["rotation"]
assert new_rotation == old_rotation + 90
undo(qtbot, qapp_cls_)
new_rotation = opts.states["rotation"]
assert new_rotation == old_rotation
def test_disable_enable(qtbot, qapp_cls_):
qtbot.wait(100)
var = find_blocks(qapp_cls_.MainWindow.currentFlowgraph, "variable")
click_on(qtbot, qapp_cls_, var)
assert var is not None
assert var.state == "enabled"
keystroke(qtbot, qapp_cls_, QtCore.Qt.Key_D)
assert var.state == "disabled"
keystroke(qtbot, qapp_cls_, QtCore.Qt.Key_E)
assert var.state == "enabled"
def test_move_blocks(qtbot, qapp_cls_):
fg = qapp_cls_.MainWindow.currentFlowgraph
view = qapp_cls_.MainWindow.currentView
scaling = qapp_cls_.MainWindow.screen().devicePixelRatio()
qtbot.wait(100)
add_block_from_query(qtbot, qapp_cls_, "throttle")
throttle = find_blocks(fg, "blocks_throttle")
variable = find_blocks(fg, "variable")
assert throttle is not None
click_on(qtbot, qapp_cls_, variable)
qtbot.wait(100)
start_throttle = scaling * global_pos(throttle.gui, view)
start_variable = scaling * global_pos(variable.gui, view)
pag.moveTo(start_throttle.x(), start_throttle.y())
pag.mouseDown()
def drag():
for i in range(20):
pag.move(0, scaling * 10)
drag_t = threading.Thread(target=drag)
drag_t.start()
while drag_t.is_alive():
qtbot.wait(50)
pag.mouseUp()
qtbot.wait(100)
assert scaling * global_pos(throttle.gui, view) != start_throttle
undo(qtbot, qapp_cls_)
assert scaling * global_pos(throttle.gui, view) == start_throttle
redo(qtbot, qapp_cls_)
assert scaling * global_pos(throttle.gui, view) != start_throttle
# Variable shouldn't move
assert scaling * global_pos(variable.gui, view) == start_variable
delete_block(qtbot, qapp_cls_, throttle)
def test_connection(qtbot, qapp_cls_):
fg = qapp_cls_.MainWindow.currentFlowgraph
view = qapp_cls_.MainWindow.currentView
scaling = qapp_cls_.MainWindow.screen().devicePixelRatio()
qtbot.wait(100)
for block in ["null sou", "null sin"]:
add_block_from_query(qtbot, qapp_cls_, block)
n_src = find_blocks(fg, "blocks_null_source")
n_sink = find_blocks(fg, "blocks_null_sink")
assert len(fg.connections) == 0
start = scaling * global_pos(n_sink.gui, view)
pag.moveTo(start.x(), start.y())
pag.mouseDown()
def drag():
for i in range(20):
pag.move(scaling * 10, 0)
drag_t = threading.Thread(target=drag)
drag_t.start()
while drag_t.is_alive():
qtbot.wait(50)
pag.mouseUp()
click_on(qtbot, qapp_cls_, n_src.sources[0])
click_on(qtbot, qapp_cls_, n_sink.sinks[0])
assert len(fg.connections) == 1
undo(qtbot, qapp_cls_)
assert len(fg.connections) == 0
redo(qtbot, qapp_cls_)
assert len(fg.connections) == 1
connection = next(iter(fg.connections)) # get a connection without removing it
# delete connection with delete key press event
assert len(fg.connections) == 1
click_on(qtbot, qapp_cls_, connection)
keystroke(qtbot, qapp_cls_, QtCore.Qt.Key_Delete)
assert len(fg.connections) == 0
qtbot.wait(100)
undo(qtbot, qapp_cls_)
# delete connection with double click
click_on(qtbot, qapp_cls_, connection)
click_on(qtbot, qapp_cls_, connection)
assert len(fg.connections) == 0
qtbot.wait(100)
for block in [n_src, n_sink]:
delete_block(qtbot, qapp_cls_, block)
def test_num_inputs(qtbot, qapp_cls_):
fg = qapp_cls_.MainWindow.currentFlowgraph
view = qapp_cls_.MainWindow.currentView
scaling = qapp_cls_.MainWindow.screen().devicePixelRatio()
qtbot.wait(100)
for block in ["null sou", "null sin"]:
add_block_from_query(qtbot, qapp_cls_, block)
n_src = find_blocks(fg, "blocks_null_source")
n_sink = find_blocks(fg, "blocks_null_sink")
assert len(n_sink.sinks) == 1
start = scaling * global_pos(n_sink.gui, view)
pag.moveTo(start.x(), start.y())
pag.mouseDown()
def drag():
for i in range(20):
pag.move(scaling * 10, 0)
drag_t = threading.Thread(target=drag)
drag_t.start()
while drag_t.is_alive():
qtbot.wait(50)
pag.mouseUp()
click_on(qtbot, qapp_cls_, n_src.sources[0])
click_on(qtbot, qapp_cls_, n_sink.sinks[0])
qtbot.wait(100)
click_pos = scaling * global_pos(n_sink.gui, view)
pag.doubleClick(click_pos.x(), click_pos.y(), button="left")
qtbot.wait(100)
param_index = 0
for i in range(len(n_sink.gui.props_dialog.edit_params)):
if n_sink.gui.props_dialog.edit_params[i].param.key == 'num_inputs':
param_index = i
qtbot.mouseDClick(n_sink.gui.props_dialog.edit_params[param_index], QtCore.Qt.LeftButton)
type_text(qtbot, qapp_cls_, "2")
qtbot.wait(100)
keystroke(qtbot, qapp_cls_, QtCore.Qt.Key_Enter)
assert len(n_sink.sinks) == 2
assert len(fg.connections) == 1
click_pos = scaling * global_pos(n_sink.gui, view)
pag.doubleClick(click_pos.x(), click_pos.y(), button="left")
qtbot.wait(100)
qtbot.mouseDClick(n_sink.gui.props_dialog.edit_params[param_index], QtCore.Qt.LeftButton)
type_text(qtbot, qapp_cls_, "1")
qtbot.wait(100)
keystroke(qtbot, qapp_cls_, QtCore.Qt.Key_Enter)
qtbot.wait(100)
keystroke(qtbot, qapp_cls_, QtCore.Qt.Key_Enter)
qtbot.wait(100)
assert len(n_sink.sinks) == 1
assert len(fg.connections) == 1
# I think loses focus makes delete_fail the first time. This makes it work, but is a hack
click_on(qtbot, qapp_cls_, n_src)
pag.click(click_pos.x() + 50, click_pos.y() + 50, button="left")
for block in [n_src, n_sink]:
delete_block(qtbot, qapp_cls_, block)
qtbot.wait(100)
assert len(fg.blocks) == 2
def test_bus(qtbot, qapp_cls_):
fg = qapp_cls_.MainWindow.currentFlowgraph
view = qapp_cls_.MainWindow.currentView
scaling = qapp_cls_.MainWindow.screen().devicePixelRatio()
qtbot.wait(100)
add_block_from_query(qtbot, qapp_cls_, "null sin")
n_sink = find_blocks(fg, "blocks_null_sink")
assert len(n_sink.sinks) == 1
click_pos = scaling * global_pos(n_sink.gui, view)
pag.doubleClick(click_pos.x(), click_pos.y(), button="left")
qtbot.wait(100)
param_index = 0
for i in range(len(n_sink.gui.props_dialog.edit_params)):
if n_sink.gui.props_dialog.edit_params[i].param.key == 'num_inputs':
param_index = i
qtbot.mouseDClick(n_sink.gui.props_dialog.edit_params[param_index], QtCore.Qt.LeftButton)
type_text(qtbot, qapp_cls_, "2")
qtbot.wait(100)
keystroke(qtbot, qapp_cls_, QtCore.Qt.Key_Enter)
qtbot.wait(100)
keystroke(qtbot, qapp_cls_, QtCore.Qt.Key_Enter)
qtbot.wait(100)
assert len(n_sink.sinks) == 2
# Enable bus port
qtbot.wait(100)
more_menu = qapp_cls_.MainWindow.menus["more"]
menu_shortcut(qtbot, qapp_cls_, "edit", QtCore.Qt.Key_E, QtCore.Qt.Key_M)
qtbot.wait(100)
qtbot.keyClick(more_menu, QtCore.Qt.Key_Up)
qtbot.wait(100)
qtbot.keyClick(more_menu, QtCore.Qt.Key_Enter)
qtbot.wait(100)
assert len(n_sink.sinks) == 3
assert n_sink.sinks[2].dtype == 'bus'
# Disable bus port
qtbot.wait(100)
more_menu = qapp_cls_.MainWindow.menus["more"]
menu_shortcut(qtbot, qapp_cls_, "edit", QtCore.Qt.Key_E, QtCore.Qt.Key_M)
qtbot.wait(100)
qtbot.keyClick(more_menu, QtCore.Qt.Key_Up)
qtbot.wait(100)
qtbot.keyClick(more_menu, QtCore.Qt.Key_Enter)
qtbot.wait(100)
assert len(n_sink.sinks) == 2
# Test undo
undo(qtbot, qapp_cls_)
qtbot.wait(100)
assert len(n_sink.sinks) == 3
qtbot.wait(100)
undo(qtbot, qapp_cls_)
qtbot.wait(100)
assert len(n_sink.sinks) == 2
delete_block(qtbot, qapp_cls_, n_sink)
qtbot.wait(100)
def test_bypass(qtbot, qapp_cls_):
scaling = qapp_cls_.MainWindow.screen().devicePixelRatio()
fg = qapp_cls_.MainWindow.currentFlowgraph
view = qapp_cls_.MainWindow.currentView
qtbot.wait(100)
for block in ["null sou", "throttle"]:
add_block_from_query(qtbot, qapp_cls_, block)
n_src = find_blocks(fg, "blocks_null_source")
throttle = find_blocks(fg, "blocks_throttle")
# Bypass the throttle block
click_on(qtbot, qapp_cls_, throttle)
keystroke(qtbot, qapp_cls_, QtCore.Qt.Key_B)
assert throttle.state == "bypassed"
undo(qtbot, qapp_cls_)
assert throttle.state == "enabled"
redo(qtbot, qapp_cls_)
assert throttle.state == "bypassed"
qtbot.wait(100)
keystroke(qtbot, qapp_cls_, QtCore.Qt.Key_E)
assert throttle.state == "enabled"
# Try to bypass the null source, this shouldn't work
click_on(qtbot, qapp_cls_, n_src)
keystroke(qtbot, qapp_cls_, QtCore.Qt.Key_B)
assert n_src.state == "enabled"
for block in [throttle, n_src]:
delete_block(qtbot, qapp_cls_, block)
def test_file_save(qtbot, qapp_cls_, monkeypatch, tmp_path):
fg_path = tmp_path / "test_save.grc"
monkeypatch.setattr(
QtWidgets.QFileDialog, "selectedFiles", lambda *args, **kargs: (str(fg_path), "")
)
monkeypatch.setattr(
QtWidgets.QFileDialog, "exec_", lambda *args: QtWidgets.QFileDialog.Accepted
)
assert not fg_path.exists(), "File/Save (setup): File already exists"
ctrl_keystroke(qtbot, qapp_cls_, QtCore.Qt.Key_S)
assert fg_path.exists(), "File/Save: Could not save file"
def test_file_save_as(qtbot, qapp_cls_, monkeypatch, tmp_path):
fg_path = tmp_path / "test.grc"
monkeypatch.setattr(
QtWidgets.QFileDialog, "selectedFiles", lambda *args, **kargs: (str(fg_path), "")
)
monkeypatch.setattr(
QtWidgets.QFileDialog, "exec_", lambda *args: QtWidgets.QFileDialog.Accepted
)
qtbot.wait(100)
menu_shortcut(qtbot, qapp_cls_, "file", QtCore.Qt.Key_F, QtCore.Qt.Key_A)
assert fg_path.exists()
def test_file_save_copy(qtbot, qapp_cls_, monkeypatch, tmp_path):
fg_path = tmp_path / "test_copy.grc"
monkeypatch.setattr(
QtWidgets.QFileDialog, "selectedFiles", lambda *args, **kargs: (str(fg_path), "")
)
monkeypatch.setattr(
QtWidgets.QFileDialog, "exec_", lambda *args: QtWidgets.QFileDialog.Accepted
)
qtbot.wait(100)
assert not fg_path.exists(), "File/Save Copy (setup): File already exists"
menu_shortcut(qtbot, qapp_cls_, "file", QtCore.Qt.Key_F, QtCore.Qt.Key_Y)
assert fg_path.exists(), "File/Save Copy: Could not save file"
# TODO: File/Open
def test_file_screen_capture_pdf(qtbot, qapp_cls_, monkeypatch, tmp_path):
fg_path = tmp_path / "test.pdf"
monkeypatch.setattr(
QtWidgets.QFileDialog, "getSaveFileName", lambda *args, **kargs: (str(fg_path), "")
)
qtbot.wait(100)
assert not fg_path.exists(), "File/Screen Capture (setup): PDF already exists"
ctrl_keystroke(qtbot, qapp_cls_, QtCore.Qt.Key_P)
assert fg_path.exists(), "File/Screen Capture: Could not create PDF"
def test_file_screen_capture_png(qtbot, qapp_cls_, monkeypatch, tmp_path):
fg_path = tmp_path / "test.png"
assert not fg_path.exists()
monkeypatch.setattr(
QtWidgets.QFileDialog, "getSaveFileName", lambda *args, **kargs: (str(fg_path), "")
)
qtbot.wait(100)
assert not fg_path.exists(), "File/Screen Capture (setup): PNG already exists"
ctrl_keystroke(qtbot, qapp_cls_, QtCore.Qt.Key_P)
assert fg_path.exists(), "File/Screen Capture: Could not create PNG"
def test_file_screen_capture_svg(qtbot, qapp_cls_, monkeypatch, tmp_path):
fg_path = tmp_path / "test.svg"
assert not fg_path.exists()
monkeypatch.setattr(
QtWidgets.QFileDialog, "getSaveFileName", lambda *args, **kargs: (str(fg_path), "")
)
qtbot.wait(100)
assert not fg_path.exists(), "File/Screen Capture (setup): SVG already exists"
ctrl_keystroke(qtbot, qapp_cls_, QtCore.Qt.Key_P)
assert fg_path.exists(), "File/Screen Capture: Could not create SVG"
def test_file_preferences(qtbot, qapp_cls_):
menu = qapp_cls_.MainWindow.menus["file"]
items = gather_menu_items(menu)
def assert_and_close():
assert qapp_cls_.activeWindow() != qapp_cls_.MainWindow
qtbot.keyClick(qapp_cls_.activeWindow(), QtCore.Qt.Key_Enter)
qtbot.keyClick(qapp_cls_.focusWidget(), QtCore.Qt.Key_F, QtCore.Qt.AltModifier)
qtbot.wait(100)
QtCore.QTimer.singleShot(200, assert_and_close)
qtbot.keyClick(menu, QtCore.Qt.Key_F)
qtbot.wait(600)
assert qapp_cls_.activeWindow() == qapp_cls_.MainWindow
qtbot.wait(100)
def test_file_examples(qtbot, qapp_cls_):
menu = qapp_cls_.MainWindow.menus["file"]
items = gather_menu_items(menu)
def assert_and_close():
assert qapp_cls_.activeWindow() != qapp_cls_.MainWindow
qtbot.keyClick(qapp_cls_.activeWindow(), QtCore.Qt.Key_Escape)
qtbot.keyClick(qapp_cls_.focusWidget(), QtCore.Qt.Key_F, QtCore.Qt.AltModifier)
qtbot.wait(100)
QtCore.QTimer.singleShot(200, assert_and_close)
qtbot.keyClick(menu, QtCore.Qt.Key_E)
qtbot.wait(600)
assert qapp_cls_.activeWindow() == qapp_cls_.MainWindow
qtbot.wait(100)
def test_edit_actions(qtbot, qapp_cls_):
pass
def test_edit_select_all(qtbot, qapp_cls_):
qtbot.keyClick(qapp_cls_.focusWidget(), QtCore.Qt.Key_A, QtCore.Qt.ControlModifier)
qtbot.wait(100)
def test_edit_cut_paste(qtbot, qapp_cls_):
fg = qapp_cls_.MainWindow.currentFlowgraph
qtbot.wait(100)
var = find_blocks(fg, "variable")
assert var is not None, "Edit/Cut and paste (setup): Could not find variable block"
click_on(qtbot, qapp_cls_, var)
ctrl_keystroke(qtbot, qapp_cls_, QtCore.Qt.Key_X)
qtbot.wait(100)
var = find_blocks(fg, "variable")
assert var is None, "Edit/Cut and paste: Could not cut variable block"
ctrl_keystroke(qtbot, qapp_cls_, QtCore.Qt.Key_V)
qtbot.wait(100)
var = find_blocks(fg, "variable")
assert var is not None, "Edit/Cut and paste: Could not paste variable block"
qtbot.wait(100)
def test_edit_copy_paste(qtbot, qapp_cls_):
fg = qapp_cls_.MainWindow.currentFlowgraph
qtbot.wait(100)
var = find_blocks(fg, "variable")
assert var is not None, "Edit/Copy and paste (setup): Could not find variable block"
click_on(qtbot, qapp_cls_, var)
ctrl_keystroke(qtbot, qapp_cls_, QtCore.Qt.Key_C)
qtbot.wait(100)
ctrl_keystroke(qtbot, qapp_cls_, QtCore.Qt.Key_V)
vars = find_blocks(fg, "variable")
assert isinstance(vars, list), "Edit/Copy and paste: Could not paste variable block"
assert len(vars) == 2, "Edit/Copy and paste: Could not paste variable block"
assert (
vars[0].name != vars[1].name
), "Edit/Copy and paste: Newly pasted variable block's ID is the same as the original block's ID"
delete_block(qtbot, qapp_cls_, vars[1])
def test_view_actions(qtbot, qapp_cls_):
pass
def test_build_actions(qtbot, qapp_cls_):
pass
def test_tools_actions(qtbot, qapp_cls_):
pass
def test_tools_oot_browser(qtbot, qapp_cls_):
menu = qapp_cls_.MainWindow.menus["tools"]
items = gather_menu_items(menu)
def assert_open():
assert qapp_cls_.activeWindow() != qapp_cls_.MainWindow
qtbot.keyClick(qapp_cls_.focusWidget(), QtCore.Qt.Key_T, QtCore.Qt.AltModifier)
qtbot.wait(100)
QtCore.QTimer.singleShot(100, assert_open)
qtbot.keyClick(menu, QtCore.Qt.Key_O)
qtbot.wait(200)
qtbot.keyClick(qapp_cls_.activeWindow(), QtCore.Qt.Key_Escape)
qtbot.wait(200)
def test_reports_actions(qtbot, qapp_cls_):
pass
def test_help_windows(qtbot, qapp_cls_):
def assert_and_close():
assert qapp_cls_.activeWindow() != qapp_cls_.MainWindow
qtbot.keyClick(qapp_cls_.activeWindow(), QtCore.Qt.Key_Enter)
def test_help_window(menu_key):
qtbot.keyClick(qapp_cls_.focusWidget(), QtCore.Qt.Key_H, QtCore.Qt.AltModifier)
qtbot.wait(300)
QtCore.QTimer.singleShot(200, assert_and_close)
qtbot.keyClick(menu, menu_key)
qtbot.wait(600)
assert qapp_cls_.activeWindow() == qapp_cls_.MainWindow
qtbot.wait(100)
menu = qapp_cls_.MainWindow.menus["help"]
qtbot.wait(100)
for key in [
QtCore.Qt.Key_H,
QtCore.Qt.Key_T,
QtCore.Qt.Key_K,
QtCore.Qt.Key_G,
QtCore.Qt.Key_A,
QtCore.Qt.Key_Q,
]:
test_help_window(key)
def test_file_new_close(qtbot, qapp_cls_, monkeypatch):
win = qapp_cls_.MainWindow
monkeypatch.setattr(
QtWidgets.QMessageBox,
"question",
lambda *args: QtWidgets.QMessageBox.Discard,
)
qtbot.wait(100)
menu_shortcut(qtbot, qapp_cls_, "file", QtCore.Qt.Key_F, QtCore.Qt.Key_N)
assert win.tabWidget.count() == 2, "File/New"
for i in range(3, 5):
ctrl_keystroke(qtbot, qapp_cls_, QtCore.Qt.Key_N)
assert win.tabWidget.count() == i, "File/New"
for i in range(1, 4):
ctrl_keystroke(qtbot, qapp_cls_, QtCore.Qt.Key_W)
assert win.tabWidget.count() == 4 - i, "File/Close"
def test_generate(qtbot, qapp_cls_, monkeypatch, tmp_path):
fg = qapp_cls_.MainWindow.currentFlowgraph
view = qapp_cls_.MainWindow.currentView
scaling = qapp_cls_.MainWindow.screen().devicePixelRatio()
fg_path = tmp_path / "test_generate.grc"
py_path = tmp_path / "default.py"
monkeypatch.setattr(
QtWidgets.QFileDialog, "selectedFiles", lambda *args, **kargs: (str(fg_path), "")
)
monkeypatch.setattr(
QtWidgets.QFileDialog, "exec_", lambda *args: QtWidgets.QFileDialog.Accepted
)
qtbot.wait(100)
for block in ["null sou", "null sin"]:
add_block_from_query(qtbot, qapp_cls_, block)
n_src = find_blocks(fg, "blocks_null_source")
n_sink = find_blocks(fg, "blocks_null_sink")
assert len(fg.connections) == 0
start = scaling * global_pos(n_sink.gui, view)
pag.moveTo(start.x(), start.y())
pag.mouseDown()
def drag():
for i in range(20):
pag.move(scaling * 10, 0)
drag_t = threading.Thread(target=drag)
drag_t.start()
while drag_t.is_alive():
qtbot.wait(50)
pag.mouseUp()
click_on(qtbot, qapp_cls_, n_src.sources[0])
click_on(qtbot, qapp_cls_, n_sink.sinks[0])
assert not fg_path.exists(), "File/Save (setup): .grc file already exists"
assert not py_path.exists(), "File/Save (setup): Python file already exists"
menu_shortcut(qtbot, qapp_cls_, "build", QtCore.Qt.Key_B, QtCore.Qt.Key_G)
qtbot.wait(500)
assert fg_path.exists(), "File/Save: Could not save .grc file"
assert py_path.exists(), "File/Save: Could not save Python file"
def test_file_close_all(qtbot, qapp_cls_, monkeypatch):
win = qapp_cls_.MainWindow
monkeypatch.setattr(
QtWidgets.QMessageBox,
"question",
lambda *args: QtWidgets.QMessageBox.Discard,
)
qtbot.wait(100)
for i in range(1, 4):
ctrl_keystroke(qtbot, qapp_cls_, QtCore.Qt.Key_N)
assert win.tabWidget.count() == 4, "File/Close All"
menu_shortcut(qtbot, qapp_cls_, "file", QtCore.Qt.Key_F, QtCore.Qt.Key_L)
assert win.tabWidget.count() == 1, "File/Close All"
def test_quit(qtbot, qapp_cls_, monkeypatch):
monkeypatch.setattr(
QtWidgets.QMessageBox,
"question",
lambda *args: QtWidgets.QMessageBox.Discard,
)
qapp_cls_.MainWindow.actions["exit"].trigger()
assert True
time.sleep(1)