From 1d35978228f0145033c363477187932f46f0b971 Mon Sep 17 00:00:00 2001 From: Ales Erjavec Date: Mon, 10 Jul 2017 22:19:40 +0200 Subject: [PATCH 1/5] WidgetTest: Raise a RuntimeError in send_signal if widget is not ready --- Orange/widgets/tests/base.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Orange/widgets/tests/base.py b/Orange/widgets/tests/base.py index afe59043f0e..c04e186fe93 100644 --- a/Orange/widgets/tests/base.py +++ b/Orange/widgets/tests/base.py @@ -215,6 +215,9 @@ def send_signal(self, input, value, *args, widget=None, wait=-1): else: raise ValueError("'{}' is not an input name for widget {}" .format(input, type(widget).__name__)) + if widget.isBlocking(): + raise RuntimeError("'send_signal' called but the widget is in " + "blocking state and does not accept inputs.") getattr(widget, input.handler)(value, *args) widget.handleNewSignals() if wait >= 0 and widget.isBlocking(): From 15d9c3cbcce958b2453d192127309b5af2e9c4d7 Mon Sep 17 00:00:00 2001 From: Ales Erjavec Date: Fri, 4 Aug 2017 19:08:12 +0200 Subject: [PATCH 2/5] WidgetTest: Add `wait` parameter to `get_output` method --- Orange/widgets/tests/base.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/Orange/widgets/tests/base.py b/Orange/widgets/tests/base.py index c04e186fe93..a961ef4d74a 100644 --- a/Orange/widgets/tests/base.py +++ b/Orange/widgets/tests/base.py @@ -224,7 +224,7 @@ def send_signal(self, input, value, *args, widget=None, wait=-1): spy = QSignalSpy(widget.blockingStateChanged) self.assertTrue(spy.wait(timeout=wait)) - def get_output(self, output, widget=None): + def get_output(self, output, widget=None, wait=5000): """Return the last output that has been sent from the widget. Parameters @@ -232,6 +232,8 @@ def get_output(self, output, widget=None): output_name : str widget : Optional[OWWidget] widget whose output is returned. If not set, self.widget is used + wait : int + The amount of time (in milliseconds) to wait for widget to complete. Returns ------- @@ -239,10 +241,16 @@ def get_output(self, output, widget=None): """ if widget is None: widget = self.widget + + if widget.isBlocking() and wait >= 0: + spy = QSignalSpy(widget.blockingStateChanged) + self.assertTrue(spy.wait(wait), + "Failed to get output in the specified timeout") if not isinstance(output, str): output = output.name return self.signal_manager.outputs.get((widget, output), None) + @contextmanager def modifiers(self, modifiers): """ From 94edc257deb871322d174163a00de5d9a3c9ce8e Mon Sep 17 00:00:00 2001 From: Ales Erjavec Date: Fri, 4 Aug 2017 19:09:53 +0200 Subject: [PATCH 3/5] WidgetTest: Better type hinting --- Orange/widgets/tests/base.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/Orange/widgets/tests/base.py b/Orange/widgets/tests/base.py index a961ef4d74a..801b79da843 100644 --- a/Orange/widgets/tests/base.py +++ b/Orange/widgets/tests/base.py @@ -3,6 +3,8 @@ import time import unittest from unittest.mock import Mock +# pylint: disable=unused-import +from typing import List, Optional, Type, TypeVar import numpy as np import sip @@ -28,6 +30,7 @@ from Orange.widgets.utils.annotated_data import ( ANNOTATED_DATA_FEATURE_NAME, ANNOTATED_DATA_SIGNAL_NAME ) +from Orange.widgets.widget import OWWidget from Orange.widgets.utils.owlearnerwidget import OWBaseLearner sip.setdestroyonexit(False) @@ -36,6 +39,9 @@ DEFAULT_TIMEOUT = 5000 +# pylint: disable=invalid-name +T = TypeVar("T") + class DummySignalManager: def __init__(self): @@ -70,8 +76,7 @@ class WidgetTest(GuiTest): will ensure they are created correctly. """ - #: list[OwWidget] - widgets = [] + widgets = [] # type: List[OWWidget] @classmethod def setUpClass(cls): @@ -95,6 +100,7 @@ def tearDown(self): self.process_events() def create_widget(self, cls, stored_settings=None, reset_default_settings=True): + # type: (Type[T], Optional[dict], bool) -> T """Create a widget instance using mock signal_manager. When used with default parameters, it also overrides settings stored From ce2febb7240c47a224e341cda2e3d3ad02d1dde5 Mon Sep 17 00:00:00 2001 From: Ales Erjavec Date: Sun, 9 Jul 2017 22:00:08 +0200 Subject: [PATCH 4/5] tests/owtestlearners: Fix tests w.r.t. asynchronous execution * wait for results where needed * clanup after test --- .../evaluate/tests/test_owtestlearners.py | 23 ++++++++++++------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/Orange/widgets/evaluate/tests/test_owtestlearners.py b/Orange/widgets/evaluate/tests/test_owtestlearners.py index e9e657b38e6..0826260b6b2 100644 --- a/Orange/widgets/evaluate/tests/test_owtestlearners.py +++ b/Orange/widgets/evaluate/tests/test_owtestlearners.py @@ -26,24 +26,28 @@ def setUp(self): super().setUp() self.widget = self.create_widget(OWTestLearners) # type: OWTestLearners + def tearDown(self): + self.widget.onDeleteWidget() + super().tearDown() + def test_basic(self): - data = Table("iris")[::3] + data = Table("iris")[::15] self.send_signal(self.widget.Inputs.train_data, data) - self.send_signal(self.widget.Inputs.learner, MajorityLearner(), 0, wait=5000) - res = self.get_output(self.widget.Outputs.evaluations_results) + self.send_signal(self.widget.Inputs.learner, MajorityLearner(), 0) + res = self.get_output(self.widget.Outputs.evaluations_results, wait=5000) self.assertIsInstance(res, Results) self.assertIsNotNone(res.domain) self.assertIsNotNone(res.data) self.assertIsNotNone(res.probabilities) - self.send_signal(self.widget.Inputs.learner, None, 0, wait=5000) - res = self.get_output(self.widget.Outputs.evaluations_results) + self.send_signal(self.widget.Inputs.learner, None, 0) + res = self.get_output(self.widget.Outputs.evaluations_results, wait=5000) self.assertIsNone(res) data = Table("housing")[::10] self.send_signal(self.widget.Inputs.train_data, data) - self.send_signal(self.widget.Inputs.learner, MeanLearner(), 0, wait=5000) - res = self.get_output(self.widget.Outputs.evaluations_results) + self.send_signal(self.widget.Inputs.learner, MeanLearner(), 0) + res = self.get_output(self.widget.Outputs.evaluations_results, wait=5000) self.assertIsInstance(res, Results) self.assertIsNotNone(res.domain) self.assertIsNotNone(res.data) @@ -65,6 +69,7 @@ def test_CrossValidationByFeature(self): self.send_signal(self.widget.Inputs.train_data, data) self.assertFalse(rb.isEnabled()) self.assertFalse(self.widget.features_combo.isEnabled()) + self.get_output(self.widget.Outputs.evaluations_results, wait=5000) self.send_signal(self.widget.Inputs.train_data, data_with_disc_metas) self.assertTrue(rb.isEnabled()) @@ -73,6 +78,7 @@ def test_CrossValidationByFeature(self): self.assertTrue(self.widget.features_combo.isEnabled()) self.assertEqual(self.widget.features_combo.currentText(), "iris") self.assertEqual(len(self.widget.features_combo.model()), 1) + self.get_output(self.widget.Outputs.evaluations_results, wait=5000) self.send_signal(self.widget.Inputs.train_data, None) self.assertFalse(rb.isEnabled()) @@ -148,7 +154,7 @@ def test_memory_error(self): Handling memory error. GH-2316 """ - data = Table("iris")[::3] + data = Table("iris")[::15] self.send_signal(self.widget.Inputs.train_data, data) self.assertFalse(self.widget.Error.memory_error.is_shown()) @@ -217,6 +223,7 @@ class NewRegressionScore(RegressionScore): del Score.registry["NewClassificationScore"] del Score.registry["NewRegressionScore"] + class TestHelpers(unittest.TestCase): def test_results_one_vs_rest(self): data = Table("lenses") From 51f47e5c15dbabda4082d850d19cd5ab88792427 Mon Sep 17 00:00:00 2001 From: Ales Erjavec Date: Mon, 10 Jul 2017 23:45:26 +0200 Subject: [PATCH 5/5] tests/owmds: Fix tests w.r.t. asynchronous execution * wait for results/ready where needed * cleanup after test --- Orange/widgets/unsupervised/tests/test_owmds.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/Orange/widgets/unsupervised/tests/test_owmds.py b/Orange/widgets/unsupervised/tests/test_owmds.py index 65cbb9ae5b4..ea6da446a1a 100644 --- a/Orange/widgets/unsupervised/tests/test_owmds.py +++ b/Orange/widgets/unsupervised/tests/test_owmds.py @@ -29,6 +29,10 @@ def setUp(self): } ) # type: OWMDS + def tearDown(self): + self.widget.onDeleteWidget() + super().tearDown() + def _select_data(self): random.seed(42) points = random.sample(range(0, len(self.data)), 20) @@ -37,8 +41,8 @@ def _select_data(self): return sorted(points) def test_pca_init(self): - self.send_signal(self.signal_name, self.signal_data, wait=1000) - output = self.get_output(self.widget.Outputs.annotated_data) + self.send_signal(self.signal_name, self.signal_data) + output = self.get_output(self.widget.Outputs.annotated_data, wait=1000) expected = np.array( [[-2.69304803, 0.32676458], [-2.7246721, -0.20921726], @@ -49,7 +53,7 @@ def test_pca_init(self): def test_nan_plot(self): data = datasets.missing_data_1() - self.send_signal(self.widget.Inputs.data, data) + self.send_signal(self.widget.Inputs.data, data, wait=1000) simulate.combobox_run_through_all(self.widget.cb_color_value) simulate.combobox_run_through_all(self.widget.cb_color_value) @@ -63,7 +67,7 @@ def test_nan_plot(self): data.Y[:] = np.nan data.metas[:, 1] = np.nan - self.send_signal("Data", data) + self.send_signal("Data", data, wait=1000) simulate.combobox_run_through_all(self.widget.cb_color_value) simulate.combobox_run_through_all(self.widget.cb_shape_value)