Skip to content

Commit

Permalink
☔🪲 Change properly catch error in tests. Remove code that was wrongfu…
Browse files Browse the repository at this point in the history
…lly added by git during a merge.
  • Loading branch information
vanyle committed Dec 20, 2021
1 parent 6649b32 commit 76c5786
Show file tree
Hide file tree
Showing 4 changed files with 56 additions and 102 deletions.
82 changes: 0 additions & 82 deletions opencodeblocks/blocks/codeblock.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,88 +126,6 @@ def execution_finished(self):
self.run_button.setText(">")
self.run_all_button.setText(">>")

def has_input(self) -> bool:
"""Checks whether a block has connected input blocks"""
for input_socket in self.sockets_in:
if len(input_socket.edges) != 0:
return True
return False

def has_output(self) -> bool:
"""Checks whether a block has connected output blocks"""
for output_socket in self.sockets_out:
if len(output_socket.edges) != 0:
return True
return False

def _interrupt_execution(self):
"""Interrupt an execution, reset the blocks in the queue"""
for block, _ in self.source_editor.kernel.execution_queue:
# Reset the blocks that have not been run
block.reset_buttons()
block.has_been_run = False
# Clear the queue
self.source_editor.kernel.execution_queue = []
# Interrupt the kernel
self.source_editor.kernel.kernel_manager.interrupt_kernel()

def run_left(self, in_right_button=False):
"""
Run all of the block's dependencies and then run the block
"""
# If the user presses left run when running, cancel the execution
if self.run_button.text() == "..." and not in_right_button:
self._interrupt_execution()
return

# If no dependencies
if not self.has_input():
return self.run_code()

# Create the graph from the scene
graph = self.scene().create_graph()
# BFS through the input graph
edges = bfs_edges(graph, self, reverse=True)
# Run the blocks found except self
blocks_to_run: List["OCBCodeBlock"] = [v for _, v in edges]
for block in blocks_to_run[::-1]:
if not block.has_been_run:
block.run_code()

if in_right_button:
# If run_left was called inside of run_right
# self is not necessarily the block that was clicked
# which means that self does not need to be run
if not self.has_been_run:
self.run_code()
else:
# On the contrary if run_left was called outside of run_right
# self is the block that was clicked
# so self needs to be run
self.run_code()

def run_right(self):
"""Run all of the output blocks and all their dependencies"""
# If the user presses right run when running, cancel the execution
if self.run_all_button.text() == "...":
self._interrupt_execution()
return

# If no output, run left
if not self.has_output():
return self.run_left(in_right_button=True)

# Same as run_left but instead of running the blocks, we'll use run_left
graph = self.scene().create_graph()
edges = bfs_edges(graph, self)
blocks_to_run: List["OCBCodeBlock"] = [self] + [v for _, v in edges]
for block in blocks_to_run[::-1]:
block.run_left(in_right_button=True)

def reset_has_been_run(self):
"""Reset has_been_run, is called when the output is an error"""
self.has_been_run = False

def update_title(self):
"""Change the geometry of the title widget"""
self.title_widget.setGeometry(
Expand Down
26 changes: 14 additions & 12 deletions tests/integration/blocks/test_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,31 +9,26 @@
import time

from opencodeblocks.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
from tests.integration.utils import apply_function_inapp, CheckingQueue, start_app


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()
start_app(self)

self.ocb_widget.scene.load("tests/assets/flow_test.ipyg")

titles = ["Test flow 5", "Test flow 4", "Test no connection 1",
self.titles = ["Test flow 5", "Test flow 4", "Test no connection 1",
"Test input only 2", "Test output only 1"]
self.blocks_to_run = [None]*5
for item in self.ocb_widget.scene.items():
if isinstance(item, OCBCodeBlock):
if item.title in titles:
self.blocks_to_run[titles.index(item.title)] = item
if item.title in self.titles:
self.blocks_to_run[self.titles.index(item.title)] = item

def test_duplicated_run(self):
""" Don't run a block twice when the execution flows """
Expand Down Expand Up @@ -95,8 +90,13 @@ def testing_run(msgQueue: CheckingQueue):
def run_block():
block_to_run.run_left()

print("About to run !")

msgQueue.run_lambda(run_block)
time.sleep(0.5)
time.sleep(0.1)
while block_to_run.is_running:
print("wait ...")
time.sleep(0.1)

msgQueue.check_equal(block_to_run.stdout.strip(), "1")
msgQueue.stop()
Expand All @@ -116,7 +116,9 @@ def run_block():
block_to_run.run_right()

msgQueue.run_lambda(run_block)
time.sleep(0.5)
time.sleep(0.1)
while block_to_run.is_running:
time.sleep(0.1)

# Just check that it doesn't crash
msgQueue.stop()
Expand Down
10 changes: 5 additions & 5 deletions tests/integration/test_window.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,14 @@ def setup(self, mocker: MockerFixture):
"""Setup reused variables."""
self.window = OCBWindow()

def test_window_close(self, qtbot):
"""closes"""
self.window.close()

def test_open_file(self):
def test_open_file(self, qtbot):
"""loads files"""
wnd = OCBWindow()
file_example_path = "./tests/assets/example_graph1.ipyg"
subwnd = wnd.createNewMdiChild(os.path.abspath(file_example_path))
subwnd.show()
wnd.close()

def test_window_close(self, qtbot):
"""closes"""
self.window.close()
40 changes: 37 additions & 3 deletions tests/integration/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,17 @@
Utilities functions for integration testing.
"""

import os
import asyncio
from typing import Callable

import os
import asyncio
import threading
import time
from queue import Queue

from qtpy.QtWidgets import QApplication
import pytest_check as check
import warnings
from opencodeblocks.graphics.widget import OCBWidget

from opencodeblocks.graphics.window import OCBWindow
Expand All @@ -32,6 +35,27 @@ def run_lambda(self, func: Callable, *args, **kwargs):
def stop(self):
self.put([STOP_MSG])

class ExceptionForwardingThread(threading.Thread):
""" A Thread class that forwards the exceptions to the calling thread """
def __init__(self, *args, **kwargs):
""" Create an exception forwarding thread """
super().__init__(*args, **kwargs)
self.e = None

def run(self):
""" Code ran in another thread """
try:
super().run()
except Exception as e:
self.e = e

def join(self):
""" Used to sync the thread with the caller """
super().join()
print("except: ",self.e)
if self.e != None:
raise self.e


def start_app(obj):
""" Create a new app for testing """
Expand All @@ -47,11 +71,14 @@ def apply_function_inapp(window: OCBWindow, run_func: Callable):

QApplication.processEvents()
msgQueue = CheckingQueue()
t = threading.Thread(target=run_func, args=(msgQueue,))
t = ExceptionForwardingThread(target=run_func, args=(msgQueue,))
t.start()

stop = False
deadCounter = 0

while not stop:
time.sleep(1 / 30) # 30 fps
QApplication.processEvents()
if not msgQueue.empty():
msg = msgQueue.get()
Expand All @@ -61,4 +88,11 @@ def apply_function_inapp(window: OCBWindow, run_func: Callable):
stop = True
elif msg[0] == RUN_MSG:
msg[1](*msg[2], **msg[3])

if not t.is_alive() and not stop:
deadCounter += 1
if deadCounter >= 3:
# Test failed, close was not called
warnings.warn("Warning: you need to call CheckingQueue.stop() at the end of your test !")
break
t.join()

0 comments on commit 76c5786

Please sign in to comment.