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/blocks/test_block.py b/tests/integration/blocks/test_block.py new file mode 100644 index 00000000..3c741b46 --- /dev/null +++ b/tests/integration/blocks/test_block.py @@ -0,0 +1,73 @@ +# OpenCodeBlock an open-source tool for modular visual programing in python +# Copyright (C) 2021 Mathïs FEDERICO + +""" +Integration tests for the OCBBlocks. +""" + +import pytest +import pyautogui +from pytestqt.qtbot import QtBot + +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 tests.integration.utils import apply_function_inapp, CheckingQueue + + +class TestBlocks: + + @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() + 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.block) + + def test_move_blocks(self, qtbot: QtBot): + """ can be dragged around with the mouse. """ + self.ocb_widget.scene.addItem(self.block) + + def testing_drag(msgQueue: CheckingQueue): + expected_move_amount = [70, -30] + pos_block = QPointF(self.block.pos().x(), self.block.pos().y()) + + pos_block.setX( + pos_block.x() + self.block.title_height + self.block.edge_size + ) + 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) + + pyautogui.moveTo(pos_block.x(), pos_block.y()) + pyautogui.mouseDown(button="left") + + iterations = 5 + for i in range(iterations+1): + pyautogui.moveTo( + pos_block.x() + expected_move_amount[0] * i / iterations, + pos_block.y() + expected_move_amount[1] * i / iterations + ) + + pyautogui.mouseUp(button="left") + + 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 + + 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/blocks/test_codeblock.py b/tests/integration/blocks/test_codeblock.py new file mode 100644 index 00000000..42f3cd2f --- /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(3 + 5 * 2) + + 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/test_blocks.py b/tests/integration/test_blocks.py deleted file mode 100644 index 449f7763..00000000 --- a/tests/integration/test_blocks.py +++ /dev/null @@ -1,144 +0,0 @@ -# OpenCodeBlock an open-source tool for modular visual programing in python -# Copyright (C) 2021 Mathïs FEDERICO - -""" -Integration tests for the OCBBlocks. -""" - -# Imports needed for testing -import threading -import queue -import pytest -from pytest_mock import MockerFixture -import pytest_check as check -import pyautogui - -# Packages tested -from opencodeblocks.graphics.blocks.codeblock import OCBCodeBlock -from opencodeblocks.graphics.window import OCBWindow -from opencodeblocks.graphics.widget import OCBWidget - -from qtpy.QtWidgets import QApplication -from PyQt5.QtCore import QPointF - - -class TestBlocks: - - @pytest.fixture(autouse=True) - def setup(self, mocker: MockerFixture): - """ Setup reused variables. """ - self.window = OCBWindow() - self.ocb_widget = OCBWidget() - self.subwindow = self.window.mdiArea.addSubWindow(self.ocb_widget) - - self.block1 = OCBCodeBlock(title="Testing block 1", source="print(1)") - self.block2 = OCBCodeBlock(title="Testing block 2", source="print(2)") - - def test_create_blocks(self, qtbot): - """ can be added to the scene. """ - self.ocb_widget.scene.addItem(self.block1) - - def test_move_blocks(self, qtbot): - """ can be dragged around with the mouse. """ - 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): - pos_block = QPointF(self.block1.pos().x(), self.block1.pos().y()) - - pos_block.setX( - pos_block.x() + self.block1.title_height + self.block1.edge_size - ) - pos_block.setY(pos_block.y() + self.block1.title_height/2) - - pos_block = self.ocb_widget.view.mapFromScene(pos_block) - pos_block = self.ocb_widget.view.mapToGlobal(pos_block) - - pyautogui.moveTo(pos_block.x(), pos_block.y()) - pyautogui.mouseDown(button="left") - - iterations = 5 - for i in range(iterations+1): - pyautogui.moveTo( - pos_block.x() + expected_move_amount[0] * i / iterations, - pos_block.y() + expected_move_amount[1] * i / iterations - ) - - pyautogui.mouseUp(button="left") - - move_amount = [self.block1.pos().x(), self.block1.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 - - 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() - - -""" -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/utils.py b/tests/integration/utils.py new file mode 100644 index 00000000..956a7f7f --- /dev/null +++ b/tests/integration/utils.py @@ -0,0 +1,52 @@ +# OpenCodeBlock an open-source tool for modular visual programing in python +# Copyright (C) 2021 Mathïs FEDERICO + +""" +Utilities functions for integration testing. +""" + +import os +import asyncio +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): + + 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,)) + 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()