@@ -4,6 +4,7 @@
# SPDX-License-Identifier: MIT
#
+import copy
import re
import sys
import unittest
@@ -106,8 +107,10 @@ class OETestLoader(unittest.TestLoader):
if isinstance(obj, OETestDecorator):
if not obj.__class__ in decoratorClasses:
raise Exception("Decorator %s isn't registered" \
- " in decoratorClasses." % obj.__name__)
- obj.bind(self.tc._registry, case)
+ " in decoratorClasses." % obj.__class__.__name__)
+ # The decorator instance is shared by every class that has
+ # the method so bind a copy for this test case
+ copy.copy(obj).bind(self.tc._registry, case)
def _walk_closure(obj):
if hasattr(obj, '__closure__') and obj.__closure__:
new file mode 100644
@@ -0,0 +1,20 @@
+#
+# Copyright OpenEmbedded Contributors
+#
+# SPDX-License-Identifier: MIT
+#
+
+from oeqa.core.case import OETestCase
+from oeqa.core.decorator.depends import OETestDepends
+
+class InheritBaseTest(OETestCase):
+
+ def testInheritFirst(self):
+ self.assertTrue(True, msg='How is this possible?')
+
+ @OETestDepends(['testInheritFirst'])
+ def testInheritSecond(self):
+ self.assertTrue(True, msg='How is this possible?')
+
+class InheritSubTest(InheritBaseTest):
+ pass
@@ -126,6 +126,20 @@ class TestDependsDecorator(TestBase):
self.assertEqual(result.skipped, [])
self.assertEqual(len(result.successes), len(tests))
+class TestInheritedDecorators(TestBase):
+ modules = ['inherit']
+
+ def test_inherited_decorators(self):
+ # Each test case needs its own decorator instances, even when the
+ # test method is inherited by another test class
+ tc = self._testLoader(modules=self.modules)
+ cases = tc._registry['cases']
+ self.assertEqual(len(cases), 4)
+ for case in cases.values():
+ for d in case.decorators:
+ self.assertIs(d.case, case)
+ self.assertTrue(tc.runTests().wasSuccessful())
+
class TestTimeoutDecorator(TestBase):
modules = ['timeout']
A decorator instance belongs to the decorated function, so when a test method is inherited by more than one test class the same instance was bound to each test case in turn and ended up referring to whichever case was bound last. Its setUpDecorator() then ran against the wrong case. Bind a copy for each test case instead and add a test. Also fix the unregistered decorator error, which used obj.__name__ on a decorator instance and so raised AttributeError instead. AI-Generated: Uses Claude Opus 5.5 Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org> --- meta/lib/oeqa/core/loader.py | 7 +++++-- meta/lib/oeqa/core/tests/cases/inherit.py | 20 ++++++++++++++++++++ meta/lib/oeqa/core/tests/test_decorators.py | 14 ++++++++++++++ 3 files changed, 39 insertions(+), 2 deletions(-) create mode 100644 meta/lib/oeqa/core/tests/cases/inherit.py