diff mbox series

[28/41] oeqa/core/case: Tear down decorators if setUp() fails

Message ID 20261115084845.701136-28-richard.purdie@linuxfoundation.org
State New
Headers show
Series [01/41] oeqa/loader: Drop code for pre python 3.5 | expand

Commit Message

Richard Purdie Nov. 15, 2026, 8:48 a.m. UTC
unittest doesn't call tearDown() when setUp() raises, so if a test's own
setUp() failed after the decorators were set up, they were never torn
down. For OETimeout this left the alarm running so it could fire during
a later test. Tear the decorators down in that case too and add a test.

AI-Generated: Uses Claude Opus 5.5
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
---
 meta/lib/oeqa/core/case.py                  | 4 +++-
 meta/lib/oeqa/core/tests/cases/timeout.py   | 9 +++++++++
 meta/lib/oeqa/core/tests/test_decorators.py | 9 +++++++++
 3 files changed, 21 insertions(+), 1 deletion(-)
diff mbox series

Patch

diff --git a/meta/lib/oeqa/core/case.py b/meta/lib/oeqa/core/case.py
index ad5524a7142..e71cab5028c 100644
--- a/meta/lib/oeqa/core/case.py
+++ b/meta/lib/oeqa/core/case.py
@@ -44,14 +44,16 @@  class OETestCase(unittest.TestCase):
         clss.tearDownClassMethod()
 
     def _oeSetUp(self):
+        # unittest doesn't call tearDown() if setUp() fails so the decorators
+        # need tearing down here in that case too
         try:
             for d in self.decorators:
                 d.setUpDecorator()
+            self.setUpMethod()
         except:
             for d in self.decorators:
                 d.tearDownDecorator()
             raise
-        self.setUpMethod()
 
     def _oeTearDown(self):
         for d in self.decorators:
diff --git a/meta/lib/oeqa/core/tests/cases/timeout.py b/meta/lib/oeqa/core/tests/cases/timeout.py
index 69cf969a67b..2d7229c18fd 100644
--- a/meta/lib/oeqa/core/tests/cases/timeout.py
+++ b/meta/lib/oeqa/core/tests/cases/timeout.py
@@ -32,3 +32,12 @@  class TimeoutTest(OETestCase):
 
     def testTimeoutUnrelated(self):
         sleep(6)
+
+class TimeoutSetUpFailTest(OETestCase):
+
+    def setUp(self):
+        raise Exception("setUp failed")
+
+    @OETimeout(10)
+    def testTimeoutSetUpFail(self):
+        self.assertTrue(True, msg='How is this possible?')
diff --git a/meta/lib/oeqa/core/tests/test_decorators.py b/meta/lib/oeqa/core/tests/test_decorators.py
index 2c668a9871d..d3305b1e534 100755
--- a/meta/lib/oeqa/core/tests/test_decorators.py
+++ b/meta/lib/oeqa/core/tests/test_decorators.py
@@ -147,6 +147,15 @@  class TestTimeoutDecorator(TestBase):
         msg = "OETestTimeout didn't restore SIGALRM"
         self.assertIs(alarm_signal, signal.getsignal(signal.SIGALRM), msg=msg)
 
+    def test_timeout_setup_fail(self):
+        tests = ['timeout.TimeoutSetUpFailTest.testTimeoutSetUpFail']
+        alarm_signal = signal.getsignal(signal.SIGALRM)
+        tc = self._testLoader(modules=self.modules, tests=tests)
+        self.assertFalse(tc.runTests().wasSuccessful())
+        # signal.alarm() returns the time left on any previous alarm
+        self.assertEqual(signal.alarm(0), 0, msg="OETestTimeout alarm left running after setUp failed")
+        self.assertIs(alarm_signal, signal.getsignal(signal.SIGALRM), msg="OETestTimeout didn't restore SIGALRM")
+
     def test_timeout_cancel(self):
         tests = ['timeout.TimeoutTest.testTimeoutSkip', 'timeout.TimeoutTest.testTimeoutDepends', 'timeout.TimeoutTest.testTimeoutUnrelated']
         msg = 'Unrelated test failed to complete'