blob: d12d5a055cce0878b875d41d42bb56f003c4b337 [file] [log] [blame]
Brad Bishopc342db32019-05-15 21:57:59 -04001#
Brad Bishop6e60e8b2018-02-01 10:27:11 -05002# Copyright (C) 2016 Intel Corporation
Brad Bishopc342db32019-05-15 21:57:59 -04003#
4# SPDX-License-Identifier: MIT
5#
Brad Bishop6e60e8b2018-02-01 10:27:11 -05006
7import os
Brad Bishopd7bf8c12018-02-25 22:55:05 -05008import re
Brad Bishop6e60e8b2018-02-01 10:27:11 -05009import sys
10import unittest
Brad Bishopd7bf8c12018-02-25 22:55:05 -050011import inspect
Brad Bishop6e60e8b2018-02-01 10:27:11 -050012
13from oeqa.core.utils.path import findFile
14from oeqa.core.utils.test import getSuiteModules, getCaseID
15
Brad Bishopd7bf8c12018-02-25 22:55:05 -050016from oeqa.core.exception import OEQATestNotFound
Brad Bishop6e60e8b2018-02-01 10:27:11 -050017from oeqa.core.case import OETestCase
18from oeqa.core.decorator import decoratorClasses, OETestDecorator, \
Brad Bishop79641f22019-09-10 07:20:22 -040019 OETestDiscover
Brad Bishop6e60e8b2018-02-01 10:27:11 -050020
Brad Bishopd7bf8c12018-02-25 22:55:05 -050021# When loading tests, the unittest framework stores any exceptions and
22# displays them only when the run method is called.
23#
24# For our purposes, it is better to raise the exceptions in the loading
25# step rather than waiting to run the test suite.
26#
27# Generate the function definition because this differ across python versions
28# Python >= 3.4.4 uses tree parameters instead four but for example Python 3.5.3
29# ueses four parameters so isn't incremental.
Brad Bishopf86d0552018-12-04 14:18:15 -080030_failed_test_args = inspect.getfullargspec(unittest.loader._make_failed_test).args
Brad Bishopd7bf8c12018-02-25 22:55:05 -050031exec("""def _make_failed_test(%s): raise exception""" % ', '.join(_failed_test_args))
Brad Bishop6e60e8b2018-02-01 10:27:11 -050032unittest.loader._make_failed_test = _make_failed_test
33
34def _find_duplicated_modules(suite, directory):
35 for module in getSuiteModules(suite):
36 path = findFile('%s.py' % module, directory)
37 if path:
38 raise ImportError("Duplicated %s module found in %s" % (module, path))
39
Andrew Geisslerfc113ea2023-03-31 09:59:46 -050040def _built_modules_dict(modules, logger):
Brad Bishopd7bf8c12018-02-25 22:55:05 -050041 modules_dict = {}
42
43 if modules == None:
44 return modules_dict
45
46 for module in modules:
47 # Assumption: package and module names do not contain upper case
48 # characters, whereas class names do
Andrew Geissler475cb722020-07-10 16:00:51 -050049 m = re.match(r'^([0-9a-z_.]+)(?:\.(\w[^.]*)(?:\.([^.]+))?)?$', module, flags=re.ASCII)
Brad Bishop1a4b7ee2018-12-16 17:11:34 -080050 if not m:
Andrew Geisslerfc113ea2023-03-31 09:59:46 -050051 logger.warn("module '%s' was skipped from selected modules, "\
52 "because it doesn't match with module name assumptions: "\
53 "package and module names do not contain upper case characters, whereas class names do" % module)
Brad Bishop1a4b7ee2018-12-16 17:11:34 -080054 continue
Brad Bishopd7bf8c12018-02-25 22:55:05 -050055
56 module_name, class_name, test_name = m.groups()
57
58 if module_name and module_name not in modules_dict:
59 modules_dict[module_name] = {}
60 if class_name and class_name not in modules_dict[module_name]:
61 modules_dict[module_name][class_name] = []
62 if test_name and test_name not in modules_dict[module_name][class_name]:
63 modules_dict[module_name][class_name].append(test_name)
Andrew Geisslerfc113ea2023-03-31 09:59:46 -050064 if modules and not modules_dict:
65 raise OEQATestNotFound("All selected modules were skipped, this would trigger selftest with all tests and -r ignored.")
Brad Bishopd7bf8c12018-02-25 22:55:05 -050066
67 return modules_dict
68
Brad Bishop6e60e8b2018-02-01 10:27:11 -050069class OETestLoader(unittest.TestLoader):
70 caseClass = OETestCase
71
72 kwargs_names = ['testMethodPrefix', 'sortTestMethodUsing', 'suiteClass',
73 '_top_level_dir']
74
75 def __init__(self, tc, module_paths, modules, tests, modules_required,
Brad Bishop79641f22019-09-10 07:20:22 -040076 *args, **kwargs):
Brad Bishop6e60e8b2018-02-01 10:27:11 -050077 self.tc = tc
78
Andrew Geisslerfc113ea2023-03-31 09:59:46 -050079 self.modules = _built_modules_dict(modules, tc.logger)
Brad Bishopd7bf8c12018-02-25 22:55:05 -050080
Brad Bishop6e60e8b2018-02-01 10:27:11 -050081 self.tests = tests
82 self.modules_required = modules_required
83
Brad Bishop79641f22019-09-10 07:20:22 -040084 self.tags_filter = kwargs.get("tags_filter", None)
Brad Bishop6e60e8b2018-02-01 10:27:11 -050085
86 if isinstance(module_paths, str):
87 module_paths = [module_paths]
88 elif not isinstance(module_paths, list):
89 raise TypeError('module_paths must be a str or a list of str')
90 self.module_paths = module_paths
91
92 for kwname in self.kwargs_names:
93 if kwname in kwargs:
94 setattr(self, kwname, kwargs[kwname])
95
96 self._patchCaseClass(self.caseClass)
97
Brad Bishopd7bf8c12018-02-25 22:55:05 -050098 super(OETestLoader, self).__init__()
99
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500100 def _patchCaseClass(self, testCaseClass):
101 # Adds custom attributes to the OETestCase class
102 setattr(testCaseClass, 'tc', self.tc)
103 setattr(testCaseClass, 'td', self.tc.td)
104 setattr(testCaseClass, 'logger', self.tc.logger)
105
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500106 def _registerTestCase(self, case):
107 case_id = case.id()
108 self.tc._registry['cases'][case_id] = case
109
110 def _handleTestCaseDecorators(self, case):
111 def _handle(obj):
112 if isinstance(obj, OETestDecorator):
113 if not obj.__class__ in decoratorClasses:
114 raise Exception("Decorator %s isn't registered" \
115 " in decoratorClasses." % obj.__name__)
116 obj.bind(self.tc._registry, case)
117
118 def _walk_closure(obj):
119 if hasattr(obj, '__closure__') and obj.__closure__:
120 for f in obj.__closure__:
121 obj = f.cell_contents
122 _handle(obj)
123 _walk_closure(obj)
124 method = getattr(case, case._testMethodName, None)
125 _walk_closure(method)
126
127 def _filterTest(self, case):
128 """
129 Returns True if test case must be filtered, False otherwise.
130 """
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500131 # XXX; If the module has more than one namespace only use
132 # the first to support run the whole module specifying the
133 # <module_name>.[test_class].[test_name]
134 module_name_small = case.__module__.split('.')[0]
135 module_name = case.__module__
136
137 class_name = case.__class__.__name__
138 test_name = case._testMethodName
139
Brad Bishopd5ae7d92018-06-14 09:52:03 -0700140 # 'auto' is a reserved key word to run test cases automatically
141 # warn users if their test case belong to a module named 'auto'
142 if module_name_small == "auto":
143 bb.warn("'auto' is a reserved key word for TEST_SUITES. "
144 "But test case '%s' is detected to belong to auto module. "
145 "Please condier using a new name for your module." % str(case))
146
147 # check if case belongs to any specified module
148 # if 'auto' is specified, such check is skipped
149 if self.modules and not 'auto' in self.modules:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500150 module = None
151 try:
152 module = self.modules[module_name_small]
153 except KeyError:
154 try:
155 module = self.modules[module_name]
156 except KeyError:
157 return True
158
159 if module:
160 if not class_name in module:
161 return True
162
163 if module[class_name]:
164 if test_name not in module[class_name]:
165 return True
166
167 # Decorator filters
Brad Bishop79641f22019-09-10 07:20:22 -0400168 if self.tags_filter is not None and callable(self.tags_filter):
169 alltags = set()
170 # pull tags from the case class
171 if hasattr(case, "__oeqa_testtags"):
172 for t in getattr(case, "__oeqa_testtags"):
173 alltags.add(t)
174 # pull tags from the method itself
175 if hasattr(case, test_name):
176 method = getattr(case, test_name)
177 if hasattr(method, "__oeqa_testtags"):
178 for t in getattr(method, "__oeqa_testtags"):
179 alltags.add(t)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500180
Brad Bishop79641f22019-09-10 07:20:22 -0400181 if self.tags_filter(alltags):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500182 return True
183
184 return False
185
186 def _getTestCase(self, testCaseClass, tcName):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500187 if not hasattr(testCaseClass, '__oeqa_loader') and \
188 issubclass(testCaseClass, OETestCase):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500189 # In order to support data_vars validation
190 # monkey patch the default setUp/tearDown{Class} to use
191 # the ones provided by OETestCase
192 setattr(testCaseClass, 'setUpClassMethod',
193 getattr(testCaseClass, 'setUpClass'))
194 setattr(testCaseClass, 'tearDownClassMethod',
195 getattr(testCaseClass, 'tearDownClass'))
196 setattr(testCaseClass, 'setUpClass',
197 testCaseClass._oeSetUpClass)
198 setattr(testCaseClass, 'tearDownClass',
199 testCaseClass._oeTearDownClass)
200
201 # In order to support decorators initialization
202 # monkey patch the default setUp/tearDown to use
203 # a setUpDecorators/tearDownDecorators that methods
204 # will call setUp/tearDown original methods.
205 setattr(testCaseClass, 'setUpMethod',
206 getattr(testCaseClass, 'setUp'))
207 setattr(testCaseClass, 'tearDownMethod',
208 getattr(testCaseClass, 'tearDown'))
209 setattr(testCaseClass, 'setUp', testCaseClass._oeSetUp)
210 setattr(testCaseClass, 'tearDown', testCaseClass._oeTearDown)
211
212 setattr(testCaseClass, '__oeqa_loader', True)
213
214 case = testCaseClass(tcName)
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500215 if isinstance(case, OETestCase):
216 setattr(case, 'decorators', [])
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500217
218 return case
219
220 def loadTestsFromTestCase(self, testCaseClass):
221 """
222 Returns a suite of all tests cases contained in testCaseClass.
223 """
224 if issubclass(testCaseClass, unittest.suite.TestSuite):
225 raise TypeError("Test cases should not be derived from TestSuite." \
226 " Maybe you meant to derive %s from TestCase?" \
227 % testCaseClass.__name__)
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500228 if not issubclass(testCaseClass, unittest.case.TestCase):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500229 raise TypeError("Test %s is not derived from %s" % \
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500230 (testCaseClass.__name__, unittest.case.TestCase.__name__))
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500231
232 testCaseNames = self.getTestCaseNames(testCaseClass)
233 if not testCaseNames and hasattr(testCaseClass, 'runTest'):
234 testCaseNames = ['runTest']
235
236 suite = []
237 for tcName in testCaseNames:
238 case = self._getTestCase(testCaseClass, tcName)
239 # Filer by case id
Brad Bishopd5ae7d92018-06-14 09:52:03 -0700240 if not (self.tests and not 'auto' in self.tests
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500241 and not getCaseID(case) in self.tests):
242 self._handleTestCaseDecorators(case)
243
244 # Filter by decorators
245 if not self._filterTest(case):
246 self._registerTestCase(case)
247 suite.append(case)
248
249 return self.suiteClass(suite)
250
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500251 def _required_modules_validation(self):
252 """
253 Search in Test context registry if a required
254 test is found, raise an exception when not found.
255 """
256
257 for module in self.modules_required:
258 found = False
259
260 # The module name is splitted to only compare the
261 # first part of a test case id.
262 comp_len = len(module.split('.'))
263 for case in self.tc._registry['cases']:
264 case_comp = '.'.join(case.split('.')[0:comp_len])
265 if module == case_comp:
266 found = True
267 break
268
269 if not found:
270 raise OEQATestNotFound("Not found %s in loaded test cases" % \
271 module)
272
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500273 def discover(self):
274 big_suite = self.suiteClass()
275 for path in self.module_paths:
276 _find_duplicated_modules(big_suite, path)
277 suite = super(OETestLoader, self).discover(path,
278 pattern='*.py', top_level_dir=path)
279 big_suite.addTests(suite)
280
281 cases = None
282 discover_classes = [clss for clss in decoratorClasses
283 if issubclass(clss, OETestDiscover)]
284 for clss in discover_classes:
285 cases = clss.discover(self.tc._registry)
286
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500287 if self.modules_required:
288 self._required_modules_validation()
289
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500290 return self.suiteClass(cases) if cases else big_suite
291
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500292 def _filterModule(self, module):
293 if module.__name__ in sys.builtin_module_names:
294 msg = 'Tried to import %s test module but is a built-in'
295 raise ImportError(msg % module.__name__)
296
297 # XXX; If the module has more than one namespace only use
298 # the first to support run the whole module specifying the
299 # <module_name>.[test_class].[test_name]
300 module_name_small = module.__name__.split('.')[0]
301 module_name = module.__name__
302
303 # Normal test modules are loaded if no modules were specified,
Brad Bishopd5ae7d92018-06-14 09:52:03 -0700304 # if module is in the specified module list or if 'auto' is in
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500305 # module list.
306 # Underscore modules are loaded only if specified in module list.
307 load_module = True if not module_name.startswith('_') \
308 and (not self.modules \
309 or module_name in self.modules \
310 or module_name_small in self.modules \
Brad Bishopd5ae7d92018-06-14 09:52:03 -0700311 or 'auto' in self.modules) \
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500312 else False
313
314 load_underscore = True if module_name.startswith('_') \
315 and (module_name in self.modules or \
316 module_name_small in self.modules) \
317 else False
318
Patrick Williams8e7b46e2023-05-01 14:19:06 -0500319 if any(c.isupper() for c in module.__name__):
320 raise SystemExit("Module '%s' contains uppercase characters and this isn't supported. Please fix the module name." % module.__name__)
321
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500322 return (load_module, load_underscore)
323
324
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500325 # XXX After Python 3.5, remove backward compatibility hacks for
326 # use_load_tests deprecation via *args and **kws. See issue 16662.
327 if sys.version_info >= (3,5):
328 def loadTestsFromModule(self, module, *args, pattern=None, **kws):
329 """
330 Returns a suite of all tests cases contained in module.
331 """
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500332 load_module, load_underscore = self._filterModule(module)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500333
334 if load_module or load_underscore:
335 return super(OETestLoader, self).loadTestsFromModule(
336 module, *args, pattern=pattern, **kws)
337 else:
338 return self.suiteClass()
339 else:
340 def loadTestsFromModule(self, module, use_load_tests=True):
341 """
342 Returns a suite of all tests cases contained in module.
343 """
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500344 load_module, load_underscore = self._filterModule(module)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500345
346 if load_module or load_underscore:
347 return super(OETestLoader, self).loadTestsFromModule(
348 module, use_load_tests)
349 else:
350 return self.suiteClass()