@@ -32,33 +32,18 @@ class OETestCase(unittest.TestCase):
# OEQAMissingVariable exception is raised
td_vars = None
- @classmethod
- def _oeSetUpClass(clss):
- _validate_td_vars(clss.td, clss.td_vars, "class")
- if hasattr(clss, 'setUpHooker') and callable(getattr(clss, 'setUpHooker')):
- clss.setUpHooker()
- clss.setUpClassMethod()
-
- @classmethod
- def _oeTearDownClass(clss):
- 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
-
- def _oeTearDown(self):
+ # Decorators bound to this test case by OETestLoader
+ decorators = ()
+
+ def _callSetUp(self):
+ # unittest.TestCase.run() calls this to run setUp(). Check the test
+ # data and set up the decorators first. Decorators are torn down as
+ # cleanups, which run after tearDown() and also if setUp() fails.
+ _validate_td_vars(self.td, self.td_vars, "class")
for d in self.decorators:
- d.tearDownDecorator()
- self.tearDownMethod()
+ d.setUpDecorator()
+ self.addCleanup(d.tearDownDecorator)
+ super()._callSetUp()
def assertFileExists(self, filename, msg=None):
"""
@@ -47,24 +47,17 @@ class OETestContext:
return modules
def skipTests(self, skips):
- if not skips:
- return
- def skipfuncgen(skipmsg):
- def func():
- raise unittest.SkipTest(skipmsg)
- return func
- class_ids = {}
+ # Use unittest's skip support so that skipped tests, and the class
+ # setup of skipped classes, don't run
for test in self.suites:
- if test.__class__ not in class_ids:
- class_ids[test.__class__] = '.'.join(test.id().split('.')[:-1])
+ class_id = test.id().rsplit('.', 1)[0]
for skip in skips:
- if (test.id()+'.').startswith(skip+'.'):
- setattr(test, 'setUp', skipfuncgen('Skip by the command line argument "%s"' % skip))
- for tclass in class_ids:
- cid = class_ids[tclass]
- for skip in skips:
- if (cid + '.').startswith(skip + '.'):
- setattr(tclass, 'setUpHooker', skipfuncgen('Skip by the command line argument "%s"' % skip))
+ msg = 'Skip by the command line argument "%s"' % skip
+ if (class_id + '.').startswith(skip + '.'):
+ unittest.skip(msg)(test.__class__)
+ elif (test.id() + '.').startswith(skip + '.'):
+ method = getattr(test, test._testMethodName)
+ setattr(test, test._testMethodName, unittest.skip(msg)(method))
def loadTests(self, module_paths, modules=[], tests=[],
modules_manifest="", modules_required=[], **kwargs):
@@ -171,37 +171,9 @@ class OETestLoader(unittest.TestLoader):
return False
def _getTestCase(self, testCaseClass, tcName):
- if not hasattr(testCaseClass, '__oeqa_loader') and \
- issubclass(testCaseClass, OETestCase):
- # In order to support data_vars validation
- # monkey patch the default setUp/tearDown{Class} to use
- # the ones provided by OETestCase
- setattr(testCaseClass, 'setUpClassMethod',
- getattr(testCaseClass, 'setUpClass'))
- setattr(testCaseClass, 'tearDownClassMethod',
- getattr(testCaseClass, 'tearDownClass'))
- setattr(testCaseClass, 'setUpClass',
- testCaseClass._oeSetUpClass)
- setattr(testCaseClass, 'tearDownClass',
- testCaseClass._oeTearDownClass)
-
- # In order to support decorators initialization
- # monkey patch the default setUp/tearDown to use
- # a setUpDecorators/tearDownDecorators that methods
- # will call setUp/tearDown original methods.
- setattr(testCaseClass, 'setUpMethod',
- getattr(testCaseClass, 'setUp'))
- setattr(testCaseClass, 'tearDownMethod',
- getattr(testCaseClass, 'tearDown'))
- setattr(testCaseClass, 'setUp', testCaseClass._oeSetUp)
- setattr(testCaseClass, 'tearDown', testCaseClass._oeTearDown)
-
- setattr(testCaseClass, '__oeqa_loader', True)
-
case = testCaseClass(tcName)
if isinstance(case, OETestCase):
- setattr(case, 'decorators', [])
-
+ case.decorators = []
return case
def loadTestsFromTestCase(self, testCaseClass):
To run the decorators and the td_vars check, OETestLoader renamed the setUp()/tearDown()/setUpClass()/tearDownClass() methods of every test class it loaded and replaced them with OETestCase wrappers, marking the class as patched. Since the marker was inherited, a subclass which overrode setUp() and was loaded after its parent class was left unpatched and silently lost its decorators. Override TestCase._callSetUp(), which unittest uses to run setUp(), to check td_vars and set up the decorators, and register the decorator teardowns as cleanups so they run after tearDown() and when setUp() fails. The td_vars check now runs per test rather than in setUpClass(), none of the classes using td_vars need the variables in setUpClass(). The class level hook was otherwise only used to skip classes given on the command line, so use unittest.skip() for those and for skipped tests. Tests in a skipped class are now each reported as skipped rather than as a single class level skip. This does mean we use python internal API but we can adapt if python changes that. AI-Generated: Uses Claude Opus 5.5 Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org> --- meta/lib/oeqa/core/case.py | 37 +++++++++++------------------------ meta/lib/oeqa/core/context.py | 25 +++++++++-------------- meta/lib/oeqa/core/loader.py | 30 +--------------------------- 3 files changed, 21 insertions(+), 71 deletions(-)