From e90c5f61376cfbaea01e715fbb342d52b17008e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Math=C3=AFs=20F=C3=A9d=C3=A9rico?= Date: Sun, 28 Nov 2021 17:44:04 +0100 Subject: [PATCH 1/4] :hammer: :umbrella: Add integration.utils Refactor inapp integration tests --- tests/integration/test_blocks.py | 40 ++++++--------------------- tests/integration/utils.py | 46 ++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 32 deletions(-) create mode 100644 tests/integration/utils.py diff --git a/tests/integration/test_blocks.py b/tests/integration/test_blocks.py index 449f7763..5757b8c9 100644 --- a/tests/integration/test_blocks.py +++ b/tests/integration/test_blocks.py @@ -6,8 +6,6 @@ """ # Imports needed for testing -import threading -import queue import pytest from pytest_mock import MockerFixture import pytest_check as check @@ -18,9 +16,10 @@ from opencodeblocks.graphics.window import OCBWindow from opencodeblocks.graphics.widget import OCBWidget -from qtpy.QtWidgets import QApplication from PyQt5.QtCore import QPointF +from tests.integration.utils import apply_function_inapp, CheckingQueue + class TestBlocks: @@ -43,15 +42,9 @@ def test_move_blocks(self, qtbot): self.ocb_widget.scene.addItem(self.block1) self.subwindow.show() - QApplication.processEvents() - expected_move_amount = [70, -30] - STOP_MSG = "stop" - CHECK_MSG = "check" - msgQueue = queue.Queue() - - def testing_drag(msgQueue): + def testing_drag(msgQueue: CheckingQueue): pos_block = QPointF(self.block1.pos().x(), self.block1.pos().y()) pos_block.setX( @@ -79,28 +72,11 @@ def testing_drag(msgQueue): move_amount[0] = move_amount[0] * self.ocb_widget.view.zoom move_amount[1] = move_amount[1] * self.ocb_widget.view.zoom - msgQueue.put([ - CHECK_MSG, - move_amount, - expected_move_amount, - "Block moved by the correct amound" - ]) - - msgQueue.put([STOP_MSG]) - - t = threading.Thread(target=testing_drag, args=(msgQueue,)) - t.start() - - while True: - QApplication.processEvents() - if not msgQueue.empty(): - msg = msgQueue.get() - if msg[0] == STOP_MSG: - break - elif msg[0] == CHECK_MSG: - check.equal(msg[1], msg[2], msg[3]) - t.join() - self.window.close() + msgQueue.check_equal( + move_amount, expected_move_amount, "Block moved by the correct amound") + msgQueue.stop() + + apply_function_inapp(self.window, testing_drag) """ diff --git a/tests/integration/utils.py b/tests/integration/utils.py new file mode 100644 index 00000000..83bf1d7c --- /dev/null +++ b/tests/integration/utils.py @@ -0,0 +1,46 @@ +# OpenCodeBlock an open-source tool for modular visual programing in python +# Copyright (C) 2021 Mathïs FEDERICO + +""" +Utilities functions for integration testing. +""" + +from typing import Callable + +import threading +from queue import Queue +from qtpy.QtWidgets import QApplication +import pytest_check as check + +from opencodeblocks.graphics.window import OCBWindow + +STOP_MSG = "stop" +CHECK_MSG = "check" + + +class CheckingQueue(Queue): + + def check_equal(self, a, b, msg=""): + self.put([CHECK_MSG, a, b, msg]) + + def stop(self): + self.put([STOP_MSG]) + + +def apply_function_inapp(window: OCBWindow, run_func: Callable): + QApplication.processEvents() + msgQueue = CheckingQueue() + t = threading.Thread(target=run_func, args=(msgQueue,)) + t.start() + + stop = False + while not stop: + QApplication.processEvents() + if not msgQueue.empty(): + msg = msgQueue.get() + if msg[0] == CHECK_MSG: + check.equal(msg[1], msg[2], msg[3]) + elif msg[0] == STOP_MSG: + stop = True + t.join() + window.close() From 130b2fa16a20f386cb2d74592de1c779255af924 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Math=C3=AFs=20F=C3=A9d=C3=A9rico?= Date: Mon, 29 Nov 2021 00:38:45 +0100 Subject: [PATCH 2/4] :umbrella: Add test for CodeBlock run button --- main.py | 2 +- opencodeblocks/graphics/blocks/codeblock.py | 3 +- tests/integration/blocks/__init__.py | 6 ++ .../{test_blocks.py => blocks/test_block.py} | 67 +++--------------- tests/integration/blocks/test_codeblock.py | 68 +++++++++++++++++++ tests/integration/utils.py | 6 ++ 6 files changed, 93 insertions(+), 59 deletions(-) create mode 100644 tests/integration/blocks/__init__.py rename tests/integration/{test_blocks.py => blocks/test_block.py} (55%) create mode 100644 tests/integration/blocks/test_codeblock.py diff --git a/main.py b/main.py index 2df8be1e..71e706d2 100644 --- a/main.py +++ b/main.py @@ -5,7 +5,7 @@ import sys import asyncio -if os.name == "nt": # If on windows +if os.name == "nt": # If on windows asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy()) from qtpy.QtWidgets import QApplication diff --git a/opencodeblocks/graphics/blocks/codeblock.py b/opencodeblocks/graphics/blocks/codeblock.py index 782c8875..ea2271f1 100644 --- a/opencodeblocks/graphics/blocks/codeblock.py +++ b/opencodeblocks/graphics/blocks/codeblock.py @@ -141,8 +141,7 @@ def init_run_button(self): def run_code(self): """Run the code in the block""" - code = self.source_editor.text() - self.source = code + self.source = self.source_editor.text() # Create a worker to handle execution worker = Worker(self.source_editor.kernel, self.source) worker.signals.stdout.connect(self.handle_stdout) diff --git a/tests/integration/blocks/__init__.py b/tests/integration/blocks/__init__.py new file mode 100644 index 00000000..da40e082 --- /dev/null +++ b/tests/integration/blocks/__init__.py @@ -0,0 +1,6 @@ +# OpenCodeBlock an open-source tool for modular visual programing in python +# Copyright (C) 2021 Mathïs FEDERICO + +""" +Integration tests for blocks behavior. +""" diff --git a/tests/integration/test_blocks.py b/tests/integration/blocks/test_block.py similarity index 55% rename from tests/integration/test_blocks.py rename to tests/integration/blocks/test_block.py index 5757b8c9..c8120bbc 100644 --- a/tests/integration/test_blocks.py +++ b/tests/integration/blocks/test_block.py @@ -5,46 +5,42 @@ Integration tests for the OCBBlocks. """ -# Imports needed for testing import pytest -from pytest_mock import MockerFixture -import pytest_check as check import pyautogui +from pytestqt.qtbot import QtBot -# Packages tested -from opencodeblocks.graphics.blocks.codeblock import OCBCodeBlock +from PyQt5.QtCore import QPointF + +from opencodeblocks.graphics.blocks.codeblock import OCBBlock from opencodeblocks.graphics.window import OCBWindow from opencodeblocks.graphics.widget import OCBWidget -from PyQt5.QtCore import QPointF - from tests.integration.utils import apply_function_inapp, CheckingQueue class TestBlocks: @pytest.fixture(autouse=True) - def setup(self, mocker: MockerFixture): + def setup(self): """ Setup reused variables. """ self.window = OCBWindow() self.ocb_widget = OCBWidget() self.subwindow = self.window.mdiArea.addSubWindow(self.ocb_widget) + self.subwindow.show() - self.block1 = OCBCodeBlock(title="Testing block 1", source="print(1)") - self.block2 = OCBCodeBlock(title="Testing block 2", source="print(2)") + self.block1 = OCBBlock(title="Testing block 1") + self.block2 = OCBBlock(title="Testing block 2") - def test_create_blocks(self, qtbot): + def test_create_blocks(self, qtbot: QtBot): """ can be added to the scene. """ self.ocb_widget.scene.addItem(self.block1) - def test_move_blocks(self, qtbot): + def test_move_blocks(self, qtbot: QtBot): """ can be dragged around with the mouse. """ self.ocb_widget.scene.addItem(self.block1) - self.subwindow.show() - - expected_move_amount = [70, -30] def testing_drag(msgQueue: CheckingQueue): + expected_move_amount = [70, -30] pos_block = QPointF(self.block1.pos().x(), self.block1.pos().y()) pos_block.setX( @@ -77,44 +73,3 @@ def testing_drag(msgQueue: CheckingQueue): msgQueue.stop() apply_function_inapp(self.window, testing_drag) - - -""" -def test_running_python(qtbot): - # The blocks should run arbitrary python when unfocused - wnd = OCBWindow() - - EXPRESSION = "3 + 5 * 2" - SOURCE_TEST = \ - ''' - print(%s) - ''' % EXPRESSION - expected_result = str(eval(EXPRESSION)) - - # Let's add a block with the source to the window ! - ocb_widget = OCBWidget() - test_block = OCBCodeBlock(title="Testing block", source=SOURCE_TEST) - ocb_widget.scene.addItem(test_block) - wnd.mdiArea.addSubWindow(ocb_widget) - - # Let's run the block ! - pyeditor = test_block.source_editor.widget() - # pyeditor.setModified(True) - # test_block._source = "" - QApplication.processEvents() - QtTest.QTest.mouseClick(pyeditor,Qt.MouseButton.LeftButton) - QApplication.processEvents() - QtTest.QTest.keyPress(pyeditor," ") - QApplication.processEvents() - - # Click outside the block to lose focus of the previous block. - # This will need to be changed by the click to the run button. - QtTest.QTest.mouseClick(ocb_widget,Qt.MouseButton.LeftButton) - QApplication.processEvents() - - # When the execution becomes non-blocking for the UI, a refactor will be needed here. - result = test_block.stdout.strip() - - check.equal(expected_result,result) - wnd.close() -""" diff --git a/tests/integration/blocks/test_codeblock.py b/tests/integration/blocks/test_codeblock.py new file mode 100644 index 00000000..3c7d3bd3 --- /dev/null +++ b/tests/integration/blocks/test_codeblock.py @@ -0,0 +1,68 @@ +# OpenCodeBlock an open-source tool for modular visual programing in python +# Copyright (C) 2021 Mathïs FEDERICO + +""" +Integration tests for the OCBCodeBlocks. +""" + +import pyautogui +import pytest +from pytestqt.qtbot import QtBot + +from PyQt5.QtCore import QPointF + +from opencodeblocks.graphics.blocks.codeblock import OCBCodeBlock +from opencodeblocks.graphics.window import OCBWindow +from opencodeblocks.graphics.widget import OCBWidget + +from tests.integration.utils import apply_function_inapp, CheckingQueue + + +class TestCodeBlocks: + + @pytest.fixture(autouse=True) + def setup(self): + """ Setup reused variables. """ + self.window = OCBWindow() + self.ocb_widget = OCBWidget() + self.subwindow = self.window.mdiArea.addSubWindow(self.ocb_widget) + self.subwindow.show() + + def test_run_python(self, qtbot: QtBot): + """ run source code when run button is pressed. """ + + # Add a block with the source to the window + EXPRESSION = "3 + 5 * 2" + SOURCE_TEST = f'''print({EXPRESSION})''' + expected_result = str(eval(EXPRESSION)) + + test_block = OCBCodeBlock(title="CodeBlock test", source=SOURCE_TEST) + self.ocb_widget.scene.addItem(test_block) + + def testing_run(msgQueue: CheckingQueue): + + msgQueue.check_equal(test_block.stdout.strip(), "") + + pos_run_button = test_block.run_button.pos() + pos_run_button = QPointF( + pos_run_button.x() + test_block.run_button.width() / 2, + pos_run_button.y() + test_block.run_button.height() / 2, + ) + pos_run_button = self.ocb_widget.view.mapFromScene(pos_run_button) + pos_run_button = self.ocb_widget.view.mapToGlobal(pos_run_button) + + # Run the block by pressung the run button + pyautogui.moveTo(pos_run_button.x(), pos_run_button.y()) + pyautogui.mouseDown(button="left") + pyautogui.mouseUp(button="left") + + # qtbot.mouseMove(test_block.run_button) + # qtbot.mousePress(test_block.run_button, + # Qt.MouseButton.LeftButton, delay=1) + # qtbot.mouseRelease(test_block.run_button, Qt.MouseButton.LeftButton) + + # When the execution becomes non-blocking for the UI, a refactor will be needed here. + msgQueue.check_equal(test_block.stdout.strip(), expected_result) + msgQueue.stop() + + apply_function_inapp(self.window, testing_run) diff --git a/tests/integration/utils.py b/tests/integration/utils.py index 83bf1d7c..956a7f7f 100644 --- a/tests/integration/utils.py +++ b/tests/integration/utils.py @@ -5,6 +5,8 @@ Utilities functions for integration testing. """ +import os +import asyncio from typing import Callable import threading @@ -28,6 +30,10 @@ def stop(self): def apply_function_inapp(window: OCBWindow, run_func: Callable): + + if os.name == "nt": # If on windows + asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy()) + QApplication.processEvents() msgQueue = CheckingQueue() t = threading.Thread(target=run_func, args=(msgQueue,)) From d56c2c5608e656cff50c41d9d594041441586d0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Math=C3=AFs=20F=C3=A9d=C3=A9rico?= Date: Mon, 29 Nov 2021 00:58:15 +0100 Subject: [PATCH 3/4] :beetle: Fix eval usage --- tests/integration/blocks/test_codeblock.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/integration/blocks/test_codeblock.py b/tests/integration/blocks/test_codeblock.py index 3c7d3bd3..42f3cd2f 100644 --- a/tests/integration/blocks/test_codeblock.py +++ b/tests/integration/blocks/test_codeblock.py @@ -34,7 +34,7 @@ def test_run_python(self, qtbot: QtBot): # Add a block with the source to the window EXPRESSION = "3 + 5 * 2" SOURCE_TEST = f'''print({EXPRESSION})''' - expected_result = str(eval(EXPRESSION)) + expected_result = str(3 + 5 * 2) test_block = OCBCodeBlock(title="CodeBlock test", source=SOURCE_TEST) self.ocb_widget.scene.addItem(test_block) From bb8fd94a0a501a0c9c85882d03b8944163a9e8c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Math=C3=AFs=20F=C3=A9d=C3=A9rico?= Date: Mon, 29 Nov 2021 21:02:22 +0100 Subject: [PATCH 4/4] :fire: Remove self.block2 --- tests/integration/blocks/test_block.py | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/tests/integration/blocks/test_block.py b/tests/integration/blocks/test_block.py index c8120bbc..3c741b46 100644 --- a/tests/integration/blocks/test_block.py +++ b/tests/integration/blocks/test_block.py @@ -27,26 +27,24 @@ def setup(self): self.ocb_widget = OCBWidget() self.subwindow = self.window.mdiArea.addSubWindow(self.ocb_widget) self.subwindow.show() - - self.block1 = OCBBlock(title="Testing block 1") - self.block2 = OCBBlock(title="Testing block 2") + self.block = OCBBlock(title="Testing block") def test_create_blocks(self, qtbot: QtBot): """ can be added to the scene. """ - self.ocb_widget.scene.addItem(self.block1) + self.ocb_widget.scene.addItem(self.block) def test_move_blocks(self, qtbot: QtBot): """ can be dragged around with the mouse. """ - self.ocb_widget.scene.addItem(self.block1) + self.ocb_widget.scene.addItem(self.block) def testing_drag(msgQueue: CheckingQueue): expected_move_amount = [70, -30] - pos_block = QPointF(self.block1.pos().x(), self.block1.pos().y()) + pos_block = QPointF(self.block.pos().x(), self.block.pos().y()) pos_block.setX( - pos_block.x() + self.block1.title_height + self.block1.edge_size + pos_block.x() + self.block.title_height + self.block.edge_size ) - pos_block.setY(pos_block.y() + self.block1.title_height/2) + pos_block.setY(pos_block.y() + self.block.title_height/2) pos_block = self.ocb_widget.view.mapFromScene(pos_block) pos_block = self.ocb_widget.view.mapToGlobal(pos_block) @@ -63,7 +61,7 @@ def testing_drag(msgQueue: CheckingQueue): pyautogui.mouseUp(button="left") - move_amount = [self.block1.pos().x(), self.block1.pos().y()] + move_amount = [self.block.pos().x(), self.block.pos().y()] # rectify because the scene can be zoomed : move_amount[0] = move_amount[0] * self.ocb_widget.view.zoom move_amount[1] = move_amount[1] * self.ocb_widget.view.zoom