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 |