diff options
| -rw-r--r-- | meta/lib/oeqa/core/context.py | 4 | ||||
| -rw-r--r-- | meta/lib/oeqa/core/decorator/__init__.py | 20 | ||||
| -rw-r--r-- | meta/lib/oeqa/core/decorator/oetag.py | 27 | ||||
| -rw-r--r-- | meta/lib/oeqa/core/loader.py | 61 | ||||
| -rw-r--r-- | meta/lib/oeqa/core/tests/cases/data.py | 2 | ||||
| -rw-r--r-- | meta/lib/oeqa/core/tests/cases/oetag.py | 21 | ||||
| -rw-r--r-- | meta/lib/oeqa/core/tests/common.py | 4 | ||||
| -rwxr-xr-x | meta/lib/oeqa/core/tests/test_decorators.py | 77 | ||||
| -rwxr-xr-x | meta/lib/oeqa/core/tests/test_loader.py | 25 |
9 files changed, 103 insertions, 138 deletions
diff --git a/meta/lib/oeqa/core/context.py b/meta/lib/oeqa/core/context.py index 68819cc338..14fc6a54f4 100644 --- a/meta/lib/oeqa/core/context.py +++ b/meta/lib/oeqa/core/context.py | |||
| @@ -64,12 +64,12 @@ class OETestContext(object): | |||
| 64 | setattr(tclass, 'setUpHooker', skipfuncgen('Skip by the command line argument "%s"' % skip)) | 64 | setattr(tclass, 'setUpHooker', skipfuncgen('Skip by the command line argument "%s"' % skip)) |
| 65 | 65 | ||
| 66 | def loadTests(self, module_paths, modules=[], tests=[], | 66 | def loadTests(self, module_paths, modules=[], tests=[], |
| 67 | modules_manifest="", modules_required=[], filters={}): | 67 | modules_manifest="", modules_required=[], **kwargs): |
| 68 | if modules_manifest: | 68 | if modules_manifest: |
| 69 | modules = self._read_modules_from_manifest(modules_manifest) | 69 | modules = self._read_modules_from_manifest(modules_manifest) |
| 70 | 70 | ||
| 71 | self.loader = self.loaderClass(self, module_paths, modules, tests, | 71 | self.loader = self.loaderClass(self, module_paths, modules, tests, |
| 72 | modules_required, filters) | 72 | modules_required, **kwargs) |
| 73 | self.suites = self.loader.discover() | 73 | self.suites = self.loader.discover() |
| 74 | 74 | ||
| 75 | def runTests(self, processes=None, skips=[]): | 75 | def runTests(self, processes=None, skips=[]): |
diff --git a/meta/lib/oeqa/core/decorator/__init__.py b/meta/lib/oeqa/core/decorator/__init__.py index 923b218266..1a5ac40134 100644 --- a/meta/lib/oeqa/core/decorator/__init__.py +++ b/meta/lib/oeqa/core/decorator/__init__.py | |||
| @@ -6,6 +6,7 @@ | |||
| 6 | 6 | ||
| 7 | from functools import wraps | 7 | from functools import wraps |
| 8 | from abc import abstractmethod, ABCMeta | 8 | from abc import abstractmethod, ABCMeta |
| 9 | from oeqa.core.utils.misc import strToList | ||
| 9 | 10 | ||
| 10 | decoratorClasses = set() | 11 | decoratorClasses = set() |
| 11 | 12 | ||
| @@ -63,12 +64,15 @@ class OETestDiscover(OETestDecorator): | |||
| 63 | def discover(registry): | 64 | def discover(registry): |
| 64 | return registry['cases'] | 65 | return registry['cases'] |
| 65 | 66 | ||
| 66 | class OETestFilter(OETestDecorator): | 67 | def OETestTag(*tags): |
| 68 | expandedtags = [] | ||
| 69 | for tag in tags: | ||
| 70 | expandedtags += strToList(tag) | ||
| 71 | def decorator(item): | ||
| 72 | if hasattr(item, "__oeqa_testtags"): | ||
| 73 | item.__oeqa_testtags += expandedtags | ||
| 74 | else: | ||
| 75 | item.__oeqa_testtags = expandedtags | ||
| 76 | return item | ||
| 77 | return decorator | ||
| 67 | 78 | ||
| 68 | # OETestLoader call it while loading the tests | ||
| 69 | # in loadTestsFromTestCase method, it needs to | ||
| 70 | # return a bool, True if needs to be filtered. | ||
| 71 | # This method must consume the filter used. | ||
| 72 | @abstractmethod | ||
| 73 | def filtrate(self, filters): | ||
| 74 | return False | ||
diff --git a/meta/lib/oeqa/core/decorator/oetag.py b/meta/lib/oeqa/core/decorator/oetag.py deleted file mode 100644 index 8c31138dac..0000000000 --- a/meta/lib/oeqa/core/decorator/oetag.py +++ /dev/null | |||
| @@ -1,27 +0,0 @@ | |||
| 1 | # | ||
| 2 | # Copyright (C) 2016 Intel Corporation | ||
| 3 | # | ||
| 4 | # SPDX-License-Identifier: MIT | ||
| 5 | # | ||
| 6 | |||
| 7 | from . import OETestFilter, registerDecorator | ||
| 8 | from oeqa.core.utils.misc import strToList | ||
| 9 | |||
| 10 | def _tagFilter(tags, filters): | ||
| 11 | return False if set(tags) & set(filters) else True | ||
| 12 | |||
| 13 | @registerDecorator | ||
| 14 | class OETestTag(OETestFilter): | ||
| 15 | attrs = ('oetag',) | ||
| 16 | |||
| 17 | def bind(self, registry, case): | ||
| 18 | super(OETestTag, self).bind(registry, case) | ||
| 19 | self.oetag = strToList(self.oetag, 'oetag') | ||
| 20 | |||
| 21 | def filtrate(self, filters): | ||
| 22 | if filters.get('oetag'): | ||
| 23 | filterx = strToList(filters['oetag'], 'oetag') | ||
| 24 | del filters['oetag'] | ||
| 25 | if _tagFilter(self.oetag, filterx): | ||
| 26 | return True | ||
| 27 | return False | ||
diff --git a/meta/lib/oeqa/core/loader.py b/meta/lib/oeqa/core/loader.py index 7fea0585c7..0d7970d49e 100644 --- a/meta/lib/oeqa/core/loader.py +++ b/meta/lib/oeqa/core/loader.py | |||
| @@ -16,7 +16,7 @@ from oeqa.core.utils.test import getSuiteModules, getCaseID | |||
| 16 | from oeqa.core.exception import OEQATestNotFound | 16 | from oeqa.core.exception import OEQATestNotFound |
| 17 | from oeqa.core.case import OETestCase | 17 | from oeqa.core.case import OETestCase |
| 18 | from oeqa.core.decorator import decoratorClasses, OETestDecorator, \ | 18 | from oeqa.core.decorator import decoratorClasses, OETestDecorator, \ |
| 19 | OETestFilter, OETestDiscover | 19 | OETestDiscover |
| 20 | 20 | ||
| 21 | # When loading tests, the unittest framework stores any exceptions and | 21 | # When loading tests, the unittest framework stores any exceptions and |
| 22 | # displays them only when the run method is called. | 22 | # displays them only when the run method is called. |
| @@ -68,7 +68,7 @@ class OETestLoader(unittest.TestLoader): | |||
| 68 | '_top_level_dir'] | 68 | '_top_level_dir'] |
| 69 | 69 | ||
| 70 | def __init__(self, tc, module_paths, modules, tests, modules_required, | 70 | def __init__(self, tc, module_paths, modules, tests, modules_required, |
| 71 | filters, *args, **kwargs): | 71 | *args, **kwargs): |
| 72 | self.tc = tc | 72 | self.tc = tc |
| 73 | 73 | ||
| 74 | self.modules = _built_modules_dict(modules) | 74 | self.modules = _built_modules_dict(modules) |
| @@ -76,13 +76,7 @@ class OETestLoader(unittest.TestLoader): | |||
| 76 | self.tests = tests | 76 | self.tests = tests |
| 77 | self.modules_required = modules_required | 77 | self.modules_required = modules_required |
| 78 | 78 | ||
| 79 | self.filters = filters | 79 | self.tags_filter = kwargs.get("tags_filter", None) |
| 80 | self.decorator_filters = [d for d in decoratorClasses if \ | ||
| 81 | issubclass(d, OETestFilter)] | ||
| 82 | self._validateFilters(self.filters, self.decorator_filters) | ||
| 83 | self.used_filters = [d for d in self.decorator_filters | ||
| 84 | for f in self.filters | ||
| 85 | if f in d.attrs] | ||
| 86 | 80 | ||
| 87 | if isinstance(module_paths, str): | 81 | if isinstance(module_paths, str): |
| 88 | module_paths = [module_paths] | 82 | module_paths = [module_paths] |
| @@ -104,28 +98,6 @@ class OETestLoader(unittest.TestLoader): | |||
| 104 | setattr(testCaseClass, 'td', self.tc.td) | 98 | setattr(testCaseClass, 'td', self.tc.td) |
| 105 | setattr(testCaseClass, 'logger', self.tc.logger) | 99 | setattr(testCaseClass, 'logger', self.tc.logger) |
| 106 | 100 | ||
| 107 | def _validateFilters(self, filters, decorator_filters): | ||
| 108 | # Validate if filter isn't empty | ||
| 109 | for key,value in filters.items(): | ||
| 110 | if not value: | ||
| 111 | raise TypeError("Filter %s specified is empty" % key) | ||
| 112 | |||
| 113 | # Validate unique attributes | ||
| 114 | attr_filters = [attr for clss in decorator_filters \ | ||
| 115 | for attr in clss.attrs] | ||
| 116 | dup_attr = [attr for attr in attr_filters | ||
| 117 | if attr_filters.count(attr) > 1] | ||
| 118 | if dup_attr: | ||
| 119 | raise TypeError('Detected duplicated attribute(s) %s in filter' | ||
| 120 | ' decorators' % ' ,'.join(dup_attr)) | ||
| 121 | |||
| 122 | # Validate if filter is supported | ||
| 123 | for f in filters: | ||
| 124 | if f not in attr_filters: | ||
| 125 | classes = ', '.join([d.__name__ for d in decorator_filters]) | ||
| 126 | raise TypeError('Found "%s" filter but not declared in any of ' | ||
| 127 | '%s decorators' % (f, classes)) | ||
| 128 | |||
| 129 | def _registerTestCase(self, case): | 101 | def _registerTestCase(self, case): |
| 130 | case_id = case.id() | 102 | case_id = case.id() |
| 131 | self.tc._registry['cases'][case_id] = case | 103 | self.tc._registry['cases'][case_id] = case |
| @@ -188,19 +160,20 @@ class OETestLoader(unittest.TestLoader): | |||
| 188 | return True | 160 | return True |
| 189 | 161 | ||
| 190 | # Decorator filters | 162 | # Decorator filters |
| 191 | if self.filters and isinstance(case, OETestCase): | 163 | if self.tags_filter is not None and callable(self.tags_filter): |
| 192 | filters = self.filters.copy() | 164 | alltags = set() |
| 193 | case_decorators = [cd for cd in case.decorators | 165 | # pull tags from the case class |
| 194 | if cd.__class__ in self.used_filters] | 166 | if hasattr(case, "__oeqa_testtags"): |
| 195 | 167 | for t in getattr(case, "__oeqa_testtags"): | |
| 196 | # Iterate over case decorators to check if needs to be filtered. | 168 | alltags.add(t) |
| 197 | for cd in case_decorators: | 169 | # pull tags from the method itself |
| 198 | if cd.filtrate(filters): | 170 | if hasattr(case, test_name): |
| 199 | return True | 171 | method = getattr(case, test_name) |
| 200 | 172 | if hasattr(method, "__oeqa_testtags"): | |
| 201 | # Case is missing one or more decorators for all the filters | 173 | for t in getattr(method, "__oeqa_testtags"): |
| 202 | # being used, so filter test case. | 174 | alltags.add(t) |
| 203 | if filters: | 175 | |
| 176 | if self.tags_filter(alltags): | ||
| 204 | return True | 177 | return True |
| 205 | 178 | ||
| 206 | return False | 179 | return False |
diff --git a/meta/lib/oeqa/core/tests/cases/data.py b/meta/lib/oeqa/core/tests/cases/data.py index 0d8de87ae7..61f88547f7 100644 --- a/meta/lib/oeqa/core/tests/cases/data.py +++ b/meta/lib/oeqa/core/tests/cases/data.py | |||
| @@ -5,7 +5,7 @@ | |||
| 5 | # | 5 | # |
| 6 | 6 | ||
| 7 | from oeqa.core.case import OETestCase | 7 | from oeqa.core.case import OETestCase |
| 8 | from oeqa.core.decorator.oetag import OETestTag | 8 | from oeqa.core.decorator import OETestTag |
| 9 | from oeqa.core.decorator.data import OETestDataDepends | 9 | from oeqa.core.decorator.data import OETestDataDepends |
| 10 | 10 | ||
| 11 | class DataTest(OETestCase): | 11 | class DataTest(OETestCase): |
diff --git a/meta/lib/oeqa/core/tests/cases/oetag.py b/meta/lib/oeqa/core/tests/cases/oetag.py index 4e1d080985..52f97dfda6 100644 --- a/meta/lib/oeqa/core/tests/cases/oetag.py +++ b/meta/lib/oeqa/core/tests/cases/oetag.py | |||
| @@ -5,10 +5,9 @@ | |||
| 5 | # | 5 | # |
| 6 | 6 | ||
| 7 | from oeqa.core.case import OETestCase | 7 | from oeqa.core.case import OETestCase |
| 8 | from oeqa.core.decorator.oetag import OETestTag | 8 | from oeqa.core.decorator import OETestTag |
| 9 | 9 | ||
| 10 | class TagTest(OETestCase): | 10 | class TagTest(OETestCase): |
| 11 | |||
| 12 | @OETestTag('goodTag') | 11 | @OETestTag('goodTag') |
| 13 | def testTagGood(self): | 12 | def testTagGood(self): |
| 14 | self.assertTrue(True, msg='How is this possible?') | 13 | self.assertTrue(True, msg='How is this possible?') |
| @@ -17,5 +16,23 @@ class TagTest(OETestCase): | |||
| 17 | def testTagOther(self): | 16 | def testTagOther(self): |
| 18 | self.assertTrue(True, msg='How is this possible?') | 17 | self.assertTrue(True, msg='How is this possible?') |
| 19 | 18 | ||
| 19 | @OETestTag('otherTag', 'multiTag') | ||
| 20 | def testTagOtherMulti(self): | ||
| 21 | self.assertTrue(True, msg='How is this possible?') | ||
| 22 | |||
| 20 | def testTagNone(self): | 23 | def testTagNone(self): |
| 21 | self.assertTrue(True, msg='How is this possible?') | 24 | self.assertTrue(True, msg='How is this possible?') |
| 25 | |||
| 26 | @OETestTag('classTag') | ||
| 27 | class TagClassTest(OETestCase): | ||
| 28 | @OETestTag('otherTag') | ||
| 29 | def testTagOther(self): | ||
| 30 | self.assertTrue(True, msg='How is this possible?') | ||
| 31 | |||
| 32 | @OETestTag('otherTag', 'multiTag') | ||
| 33 | def testTagOtherMulti(self): | ||
| 34 | self.assertTrue(True, msg='How is this possible?') | ||
| 35 | |||
| 36 | def testTagNone(self): | ||
| 37 | self.assertTrue(True, msg='How is this possible?') | ||
| 38 | |||
diff --git a/meta/lib/oeqa/core/tests/common.py b/meta/lib/oeqa/core/tests/common.py index 39efd504c0..88cc758ad3 100644 --- a/meta/lib/oeqa/core/tests/common.py +++ b/meta/lib/oeqa/core/tests/common.py | |||
| @@ -30,9 +30,9 @@ class TestBase(unittest.TestCase): | |||
| 30 | directory = os.path.dirname(os.path.abspath(__file__)) | 30 | directory = os.path.dirname(os.path.abspath(__file__)) |
| 31 | self.cases_path = os.path.join(directory, 'cases') | 31 | self.cases_path = os.path.join(directory, 'cases') |
| 32 | 32 | ||
| 33 | def _testLoader(self, d={}, modules=[], tests=[], filters={}): | 33 | def _testLoader(self, d={}, modules=[], tests=[], **kwargs): |
| 34 | from oeqa.core.context import OETestContext | 34 | from oeqa.core.context import OETestContext |
| 35 | tc = OETestContext(d, self.logger) | 35 | tc = OETestContext(d, self.logger) |
| 36 | tc.loadTests(self.cases_path, modules=modules, tests=tests, | 36 | tc.loadTests(self.cases_path, modules=modules, tests=tests, |
| 37 | filters=filters) | 37 | **kwargs) |
| 38 | return tc | 38 | return tc |
diff --git a/meta/lib/oeqa/core/tests/test_decorators.py b/meta/lib/oeqa/core/tests/test_decorators.py index 499cd66ff3..b798bf7d33 100755 --- a/meta/lib/oeqa/core/tests/test_decorators.py +++ b/meta/lib/oeqa/core/tests/test_decorators.py | |||
| @@ -14,35 +14,58 @@ setup_sys_path() | |||
| 14 | from oeqa.core.exception import OEQADependency | 14 | from oeqa.core.exception import OEQADependency |
| 15 | from oeqa.core.utils.test import getCaseMethod, getSuiteCasesNames, getSuiteCasesIDs | 15 | from oeqa.core.utils.test import getCaseMethod, getSuiteCasesNames, getSuiteCasesIDs |
| 16 | 16 | ||
| 17 | class TestFilterDecorator(TestBase): | 17 | class TestTagDecorator(TestBase): |
| 18 | 18 | def _runTest(self, modules, filterfn, expect): | |
| 19 | def _runFilterTest(self, modules, filters, expect, msg): | 19 | tc = self._testLoader(modules = modules, tags_filter = filterfn) |
| 20 | tc = self._testLoader(modules=modules, filters=filters) | 20 | test_loaded = set(getSuiteCasesIDs(tc.suites)) |
| 21 | test_loaded = set(getSuiteCasesNames(tc.suites)) | 21 | self.assertEqual(expect, test_loaded) |
| 22 | self.assertEqual(expect, test_loaded, msg=msg) | ||
| 23 | 22 | ||
| 24 | def test_oetag(self): | 23 | def test_oetag(self): |
| 25 | # Get all cases without filtering. | 24 | # get all cases without any filtering |
| 26 | filter_all = {} | 25 | self._runTest(['oetag'], None, { |
| 27 | test_all = {'testTagGood', 'testTagOther', 'testTagNone'} | 26 | 'oetag.TagTest.testTagGood', |
| 28 | msg_all = 'Failed to get all oetag cases without filtering.' | 27 | 'oetag.TagTest.testTagOther', |
| 29 | 28 | 'oetag.TagTest.testTagOtherMulti', | |
| 30 | # Get cases with 'goodTag'. | 29 | 'oetag.TagTest.testTagNone', |
| 31 | filter_good = {'oetag':'goodTag'} | 30 | 'oetag.TagClassTest.testTagOther', |
| 32 | test_good = {'testTagGood'} | 31 | 'oetag.TagClassTest.testTagOtherMulti', |
| 33 | msg_good = 'Failed to get just one test filtering with "goodTag" oetag.' | 32 | 'oetag.TagClassTest.testTagNone', |
| 34 | 33 | }) | |
| 35 | # Get cases with an invalid tag. | 34 | |
| 36 | filter_invalid = {'oetag':'invalidTag'} | 35 | # exclude any case with tags |
| 37 | test_invalid = set() | 36 | self._runTest(['oetag'], lambda tags: tags, { |
| 38 | msg_invalid = 'Failed to filter all test using an invalid oetag.' | 37 | 'oetag.TagTest.testTagNone', |
| 39 | 38 | }) | |
| 40 | tests = ((filter_all, test_all, msg_all), | 39 | |
| 41 | (filter_good, test_good, msg_good), | 40 | # exclude any case with otherTag |
| 42 | (filter_invalid, test_invalid, msg_invalid)) | 41 | self._runTest(['oetag'], lambda tags: "otherTag" in tags, { |
| 43 | 42 | 'oetag.TagTest.testTagGood', | |
| 44 | for test in tests: | 43 | 'oetag.TagTest.testTagNone', |
| 45 | self._runFilterTest(['oetag'], test[0], test[1], test[2]) | 44 | 'oetag.TagClassTest.testTagNone', |
| 45 | }) | ||
| 46 | |||
| 47 | # exclude any case with classTag | ||
| 48 | self._runTest(['oetag'], lambda tags: "classTag" in tags, { | ||
| 49 | 'oetag.TagTest.testTagGood', | ||
| 50 | 'oetag.TagTest.testTagOther', | ||
| 51 | 'oetag.TagTest.testTagOtherMulti', | ||
| 52 | 'oetag.TagTest.testTagNone', | ||
| 53 | }) | ||
| 54 | |||
| 55 | # include any case with classTag | ||
| 56 | self._runTest(['oetag'], lambda tags: "classTag" not in tags, { | ||
| 57 | 'oetag.TagClassTest.testTagOther', | ||
| 58 | 'oetag.TagClassTest.testTagOtherMulti', | ||
| 59 | 'oetag.TagClassTest.testTagNone', | ||
| 60 | }) | ||
| 61 | |||
| 62 | # include any case with classTag or no tags | ||
| 63 | self._runTest(['oetag'], lambda tags: tags and "classTag" not in tags, { | ||
| 64 | 'oetag.TagTest.testTagNone', | ||
| 65 | 'oetag.TagClassTest.testTagOther', | ||
| 66 | 'oetag.TagClassTest.testTagOtherMulti', | ||
| 67 | 'oetag.TagClassTest.testTagNone', | ||
| 68 | }) | ||
| 46 | 69 | ||
| 47 | class TestDependsDecorator(TestBase): | 70 | class TestDependsDecorator(TestBase): |
| 48 | modules = ['depends'] | 71 | modules = ['depends'] |
diff --git a/meta/lib/oeqa/core/tests/test_loader.py b/meta/lib/oeqa/core/tests/test_loader.py index e73c91b141..cb38ac845e 100755 --- a/meta/lib/oeqa/core/tests/test_loader.py +++ b/meta/lib/oeqa/core/tests/test_loader.py | |||
| @@ -15,31 +15,6 @@ from oeqa.core.exception import OEQADependency | |||
| 15 | from oeqa.core.utils.test import getSuiteModules, getSuiteCasesIDs | 15 | from oeqa.core.utils.test import getSuiteModules, getSuiteCasesIDs |
| 16 | 16 | ||
| 17 | class TestLoader(TestBase): | 17 | class TestLoader(TestBase): |
| 18 | |||
| 19 | def test_fail_empty_filter(self): | ||
| 20 | filters = {'oetag' : ''} | ||
| 21 | expect = 'Filter oetag specified is empty' | ||
| 22 | msg = 'Expected TypeError exception for having invalid filter' | ||
| 23 | try: | ||
| 24 | # Must throw TypeError because empty filter | ||
| 25 | tc = self._testLoader(filters=filters) | ||
| 26 | self.fail(msg) | ||
| 27 | except TypeError as e: | ||
| 28 | result = True if expect in str(e) else False | ||
| 29 | self.assertTrue(result, msg=msg) | ||
| 30 | |||
| 31 | def test_fail_invalid_filter(self): | ||
| 32 | filters = {'invalid' : 'good'} | ||
| 33 | expect = 'filter but not declared in any of' | ||
| 34 | msg = 'Expected TypeError exception for having invalid filter' | ||
| 35 | try: | ||
| 36 | # Must throw TypeError because invalid filter | ||
| 37 | tc = self._testLoader(filters=filters) | ||
| 38 | self.fail(msg) | ||
| 39 | except TypeError as e: | ||
| 40 | result = True if expect in str(e) else False | ||
| 41 | self.assertTrue(result, msg=msg) | ||
| 42 | |||
| 43 | @unittest.skip("invalid directory is missing oetag.py") | 18 | @unittest.skip("invalid directory is missing oetag.py") |
| 44 | def test_fail_duplicated_module(self): | 19 | def test_fail_duplicated_module(self): |
| 45 | cases_path = self.cases_path | 20 | cases_path = self.cases_path |
