From 8458d5d6ad6845d81da73d09e05bcbb3c2e9fc87 Mon Sep 17 00:00:00 2001 From: Ayaan Ahmad Date: Fri, 13 Mar 2026 04:37:33 +0530 Subject: [PATCH 1/5] [fix] Handle skipped Selenium tests in Django parallel runner --- openwisp_utils/tests/selenium.py | 11 ++++- .../test_project/tests/test_selenium_mixin.py | 43 +++++++++++++++++++ 2 files changed, 52 insertions(+), 2 deletions(-) create mode 100644 tests/test_project/tests/test_selenium_mixin.py diff --git a/openwisp_utils/tests/selenium.py b/openwisp_utils/tests/selenium.py index 41025048..de39aa75 100644 --- a/openwisp_utils/tests/selenium.py +++ b/openwisp_utils/tests/selenium.py @@ -54,8 +54,15 @@ def _setup_and_call(self, result, debug=False): ) super()._setup_and_call(result, debug) # IMPORTANT: a skip is not a success; propagate it as a skip and stop. - if getattr(result, "skipped", None): - for _, reason in result.skipped: + skip_reasons = [] + if hasattr(result, "events"): + skip_reasons = [ + event[2] for event in result.events if event[0] == "addSkip" + ] + elif getattr(result, "skipped", None): + skip_reasons = [reason for _, reason in result.skipped] + if skip_reasons: + for reason in skip_reasons: original_result.addSkip(self, reason) if hasattr(original_result, "events") and hasattr(result, "events"): original_result.events = result.events diff --git a/tests/test_project/tests/test_selenium_mixin.py b/tests/test_project/tests/test_selenium_mixin.py new file mode 100644 index 00000000..fee46ef7 --- /dev/null +++ b/tests/test_project/tests/test_selenium_mixin.py @@ -0,0 +1,43 @@ +from unittest import TestResult, skip + +from django.test import SimpleTestCase +from django.test.runner import RemoteTestResult +from openwisp_utils.tests.selenium import SeleniumTestMixin + + +class TestSeleniumMixinSkipHandling(SimpleTestCase): + def _run_skipped_test(self, result_class): + class SkippedSeleniumTest(SeleniumTestMixin, SimpleTestCase): + retry_max = 0 + retry_delay = 0 + + @classmethod + def setUpClass(cls): + pass + + @classmethod + def tearDownClass(cls): + pass + + @skip("skip propagation regression test") + def test_skip(self): + pass + + test = SkippedSeleniumTest("test_skip") + if result_class is RemoteTestResult: + result = result_class(stream=None, descriptions=None, verbosity=0) + else: + result = result_class() + test._setup_and_call(result) + return result + + def test_setup_and_call_propagates_skip_to_standard_result(self): + result = self._run_skipped_test(TestResult) + + self.assertEqual(len(result.skipped), 1) + self.assertEqual(result.skipped[0][1], "skip propagation regression test") + + def test_setup_and_call_records_skip_event_for_remote_result(self): + result = self._run_skipped_test(RemoteTestResult) + + self.assertIn(("addSkip", 0, "skip propagation regression test"), result.events) From 99a096aba421691e1f99317ed24246c599d88485 Mon Sep 17 00:00:00 2001 From: Ayaan Ahmad Date: Sat, 14 Mar 2026 17:59:57 +0530 Subject: [PATCH 2/5] [fix] Simplify Selenium skip handling follow-up #619 Related to #619 --- openwisp_utils/tests/selenium.py | 5 ++--- tests/test_project/tests/test_selenium_mixin.py | 8 -------- 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/openwisp_utils/tests/selenium.py b/openwisp_utils/tests/selenium.py index de39aa75..bf985473 100644 --- a/openwisp_utils/tests/selenium.py +++ b/openwisp_utils/tests/selenium.py @@ -54,13 +54,12 @@ def _setup_and_call(self, result, debug=False): ) super()._setup_and_call(result, debug) # IMPORTANT: a skip is not a success; propagate it as a skip and stop. - skip_reasons = [] if hasattr(result, "events"): skip_reasons = [ event[2] for event in result.events if event[0] == "addSkip" ] - elif getattr(result, "skipped", None): - skip_reasons = [reason for _, reason in result.skipped] + else: + skip_reasons = [reason for _, reason in getattr(result, "skipped", [])] if skip_reasons: for reason in skip_reasons: original_result.addSkip(self, reason) diff --git a/tests/test_project/tests/test_selenium_mixin.py b/tests/test_project/tests/test_selenium_mixin.py index fee46ef7..01ca595e 100644 --- a/tests/test_project/tests/test_selenium_mixin.py +++ b/tests/test_project/tests/test_selenium_mixin.py @@ -11,14 +11,6 @@ class SkippedSeleniumTest(SeleniumTestMixin, SimpleTestCase): retry_max = 0 retry_delay = 0 - @classmethod - def setUpClass(cls): - pass - - @classmethod - def tearDownClass(cls): - pass - @skip("skip propagation regression test") def test_skip(self): pass From e89df63d5699b3dd8df404829635619cae9b63da Mon Sep 17 00:00:00 2001 From: Ayaan Ahmad Date: Sun, 15 Mar 2026 03:32:06 +0530 Subject: [PATCH 3/5] [fix] Preserve remote skip events for multiple tests #619 Related to #619 --- openwisp_utils/tests/selenium.py | 4 +- .../test_project/tests/test_selenium_mixin.py | 58 +++++++++++++++---- 2 files changed, 49 insertions(+), 13 deletions(-) diff --git a/openwisp_utils/tests/selenium.py b/openwisp_utils/tests/selenium.py index bf985473..783dcd3d 100644 --- a/openwisp_utils/tests/selenium.py +++ b/openwisp_utils/tests/selenium.py @@ -63,8 +63,8 @@ def _setup_and_call(self, result, debug=False): if skip_reasons: for reason in skip_reasons: original_result.addSkip(self, reason) - if hasattr(original_result, "events") and hasattr(result, "events"): - original_result.events = result.events + if hasattr(original_result, "events"): + original_result.stopTest(self) return if result.wasSuccessful(): if attempt == 0: diff --git a/tests/test_project/tests/test_selenium_mixin.py b/tests/test_project/tests/test_selenium_mixin.py index 01ca595e..0b5c7822 100644 --- a/tests/test_project/tests/test_selenium_mixin.py +++ b/tests/test_project/tests/test_selenium_mixin.py @@ -1,12 +1,12 @@ -from unittest import TestResult, skip +from unittest import TestResult, TestSuite, skip from django.test import SimpleTestCase -from django.test.runner import RemoteTestResult +from django.test.runner import RemoteTestRunner from openwisp_utils.tests.selenium import SeleniumTestMixin class TestSeleniumMixinSkipHandling(SimpleTestCase): - def _run_skipped_test(self, result_class): + def _run_skipped_standard_test(self): class SkippedSeleniumTest(SeleniumTestMixin, SimpleTestCase): retry_max = 0 retry_delay = 0 @@ -16,20 +16,56 @@ def test_skip(self): pass test = SkippedSeleniumTest("test_skip") - if result_class is RemoteTestResult: - result = result_class(stream=None, descriptions=None, verbosity=0) - else: - result = result_class() + result = TestResult() test._setup_and_call(result) return result + def _run_skipped_remote_suite(self): + class DummyWebDriver: + def quit(self): + pass + + class SkippedSeleniumTest(SeleniumTestMixin, SimpleTestCase): + retry_max = 0 + retry_delay = 0 + + @classmethod + def get_webdriver(cls): + return DummyWebDriver() + + @skip("first skip propagation regression test") + def test_first_skip(self): + pass + + @skip("second skip propagation regression test") + def test_second_skip(self): + pass + + suite = TestSuite( + [ + SkippedSeleniumTest("test_first_skip"), + SkippedSeleniumTest("test_second_skip"), + ] + ) + return RemoteTestRunner().run(suite) + def test_setup_and_call_propagates_skip_to_standard_result(self): - result = self._run_skipped_test(TestResult) + result = self._run_skipped_standard_test() self.assertEqual(len(result.skipped), 1) self.assertEqual(result.skipped[0][1], "skip propagation regression test") - def test_setup_and_call_records_skip_event_for_remote_result(self): - result = self._run_skipped_test(RemoteTestResult) + def test_setup_and_call_preserves_remote_skip_events_for_multiple_tests(self): + result = self._run_skipped_remote_suite() - self.assertIn(("addSkip", 0, "skip propagation regression test"), result.events) + self.assertEqual( + result.events, + [ + ("startTest", 0), + ("addSkip", 0, "first skip propagation regression test"), + ("stopTest", 0), + ("startTest", 1), + ("addSkip", 1, "second skip propagation regression test"), + ("stopTest", 1), + ], + ) From a03b7aa3e21187f00afb8c47fb01e18b54fb1151 Mon Sep 17 00:00:00 2001 From: Federico Capoano Date: Mon, 23 Mar 2026 20:32:40 -0300 Subject: [PATCH 4/5] [chores] Always call stopTest if skipped --- openwisp_utils/tests/selenium.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/openwisp_utils/tests/selenium.py b/openwisp_utils/tests/selenium.py index 783dcd3d..70e728e8 100644 --- a/openwisp_utils/tests/selenium.py +++ b/openwisp_utils/tests/selenium.py @@ -63,8 +63,7 @@ def _setup_and_call(self, result, debug=False): if skip_reasons: for reason in skip_reasons: original_result.addSkip(self, reason) - if hasattr(original_result, "events"): - original_result.stopTest(self) + original_result.stopTest(self) return if result.wasSuccessful(): if attempt == 0: From c7a285668507dc100aec7796e8d703b5b3878664 Mon Sep 17 00:00:00 2001 From: Federico Capoano Date: Mon, 23 Mar 2026 20:36:17 -0300 Subject: [PATCH 5/5] [chores] Minor renaming --- .../tests/{test_selenium_mixin.py => test_selenium_skip.py} | 2 -- 1 file changed, 2 deletions(-) rename tests/test_project/tests/{test_selenium_mixin.py => test_selenium_skip.py} (99%) diff --git a/tests/test_project/tests/test_selenium_mixin.py b/tests/test_project/tests/test_selenium_skip.py similarity index 99% rename from tests/test_project/tests/test_selenium_mixin.py rename to tests/test_project/tests/test_selenium_skip.py index 0b5c7822..c414a10b 100644 --- a/tests/test_project/tests/test_selenium_mixin.py +++ b/tests/test_project/tests/test_selenium_skip.py @@ -51,13 +51,11 @@ def test_second_skip(self): def test_setup_and_call_propagates_skip_to_standard_result(self): result = self._run_skipped_standard_test() - self.assertEqual(len(result.skipped), 1) self.assertEqual(result.skipped[0][1], "skip propagation regression test") def test_setup_and_call_preserves_remote_skip_events_for_multiple_tests(self): result = self._run_skipped_remote_suite() - self.assertEqual( result.events, [