diff options
author | Aníbal Limón <anibal.limon@linux.intel.com> | 2016-11-09 10:38:37 -0600 |
---|---|---|
committer | Richard Purdie <richard.purdie@linuxfoundation.org> | 2017-01-23 12:05:18 +0000 |
commit | 13c8c08b95191829705b5a4c8f0d368c251f0174 (patch) | |
tree | 737412ed0d676dcf1982fc6b2cd0046bca760281 /meta/lib/oeqa/core/context.py | |
parent | abb55ab304af91f68a4e09176ce9c6b995d903e0 (diff) | |
download | poky-13c8c08b95191829705b5a4c8f0d368c251f0174.tar.gz |
oeqa/core: Add loader, context and decorator modules
loader: Implements OETestLoader handling OETestDecorator
and filtering support when load tests. The OETestLoader is
responsible to set custom methods, attrs of the OEQA
frameowork.
[YOCTO #10231]
[YOCTO #10317]
[YOCTO #10353]
decorator: Add base class OETestDecorator to provide a common
way to define decorators to be used over OETestCase's, every
decorator has a method to be called when loading tests and
before test execution starts. Special decorators could be
implemented for filter tests on loading phase.
context: Provides HIGH level API for loadTests and runTests
of certain test component (i.e. runtime, sdk, selftest).
[YOCTO #10230]
(From OE-Core rev: 275ef03b77ef5f23b75cb01c55206d1ab0261342)
Signed-off-by: Aníbal Limón <anibal.limon@linux.intel.com>
Signed-off-by: Mariano Lopez <mariano.lopez@linux.intel.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
Diffstat (limited to 'meta/lib/oeqa/core/context.py')
-rw-r--r-- | meta/lib/oeqa/core/context.py | 148 |
1 files changed, 148 insertions, 0 deletions
diff --git a/meta/lib/oeqa/core/context.py b/meta/lib/oeqa/core/context.py new file mode 100644 index 0000000000..cbab2f8f5f --- /dev/null +++ b/meta/lib/oeqa/core/context.py | |||
@@ -0,0 +1,148 @@ | |||
1 | # Copyright (C) 2016 Intel Corporation | ||
2 | # Released under the MIT license (see COPYING.MIT) | ||
3 | |||
4 | import os | ||
5 | import sys | ||
6 | import json | ||
7 | import time | ||
8 | import logging | ||
9 | import collections | ||
10 | import re | ||
11 | |||
12 | from oeqa.core.loader import OETestLoader | ||
13 | from oeqa.core.runner import OETestRunner, OEStreamLogger, xmlEnabled | ||
14 | |||
15 | class OETestContext(object): | ||
16 | loaderClass = OETestLoader | ||
17 | runnerClass = OETestRunner | ||
18 | streamLoggerClass = OEStreamLogger | ||
19 | |||
20 | files_dir = os.path.abspath(os.path.join(os.path.dirname( | ||
21 | os.path.abspath(__file__)), "../files")) | ||
22 | |||
23 | def __init__(self, td=None, logger=None): | ||
24 | if not type(td) is dict: | ||
25 | raise TypeError("td isn't dictionary type") | ||
26 | |||
27 | self.td = td | ||
28 | self.logger = logger | ||
29 | self._registry = {} | ||
30 | self._registry['cases'] = collections.OrderedDict() | ||
31 | self._results = {} | ||
32 | |||
33 | def _read_modules_from_manifest(self, manifest): | ||
34 | if not os.path.exists(manifest): | ||
35 | raise | ||
36 | |||
37 | modules = [] | ||
38 | for line in open(manifest).readlines(): | ||
39 | line = line.strip() | ||
40 | if line and not line.startswith("#"): | ||
41 | modules.append(line) | ||
42 | |||
43 | return modules | ||
44 | |||
45 | def loadTests(self, module_paths, modules=[], tests=[], | ||
46 | modules_manifest="", modules_required=[], filters={}): | ||
47 | if modules_manifest: | ||
48 | modules = self._read_modules_from_manifest(modules_manifest) | ||
49 | |||
50 | self.loader = self.loaderClass(self, module_paths, modules, tests, | ||
51 | modules_required, filters) | ||
52 | self.suites = self.loader.discover() | ||
53 | |||
54 | def runTests(self): | ||
55 | streamLogger = self.streamLoggerClass(self.logger) | ||
56 | self.runner = self.runnerClass(self, stream=streamLogger, verbosity=2) | ||
57 | |||
58 | self._run_start_time = time.time() | ||
59 | result = self.runner.run(self.suites) | ||
60 | self._run_end_time = time.time() | ||
61 | |||
62 | return result | ||
63 | |||
64 | def logSummary(self, result, component, context_msg=''): | ||
65 | self.logger.info("SUMMARY:") | ||
66 | self.logger.info("%s (%s) - Ran %d test%s in %.3fs" % (component, | ||
67 | context_msg, result.testsRun, result.testsRun != 1 and "s" or "", | ||
68 | (self._run_end_time - self._run_start_time))) | ||
69 | |||
70 | if result.wasSuccessful(): | ||
71 | msg = "%s - OK - All required tests passed" % component | ||
72 | else: | ||
73 | msg = "%s - FAIL - Required tests failed" % component | ||
74 | skipped = len(self._results['skipped']) | ||
75 | if skipped: | ||
76 | msg += " (skipped=%d)" % skipped | ||
77 | self.logger.info(msg) | ||
78 | |||
79 | def _getDetailsNotPassed(self, case, type, desc): | ||
80 | found = False | ||
81 | |||
82 | for (scase, msg) in self._results[type]: | ||
83 | # XXX: When XML reporting is enabled scase is | ||
84 | # xmlrunner.result._TestInfo instance instead of | ||
85 | # string. | ||
86 | if xmlEnabled: | ||
87 | if case.id() == scase.test_id: | ||
88 | found = True | ||
89 | break | ||
90 | scase_str = scase.test_id | ||
91 | else: | ||
92 | if case == scase: | ||
93 | found = True | ||
94 | break | ||
95 | scase_str = str(scase) | ||
96 | |||
97 | # When fails at module or class level the class name is passed as string | ||
98 | # so figure out to see if match | ||
99 | m = re.search("^setUpModule \((?P<module_name>.*)\)$", scase_str) | ||
100 | if m: | ||
101 | if case.__class__.__module__ == m.group('module_name'): | ||
102 | found = True | ||
103 | break | ||
104 | |||
105 | m = re.search("^setUpClass \((?P<class_name>.*)\)$", scase_str) | ||
106 | if m: | ||
107 | class_name = "%s.%s" % (case.__class__.__module__, | ||
108 | case.__class__.__name__) | ||
109 | |||
110 | if class_name == m.group('class_name'): | ||
111 | found = True | ||
112 | break | ||
113 | |||
114 | if found: | ||
115 | return (found, msg) | ||
116 | |||
117 | return (found, None) | ||
118 | |||
119 | def logDetails(self): | ||
120 | self.logger.info("RESULTS:") | ||
121 | for case_name in self._registry['cases']: | ||
122 | case = self._registry['cases'][case_name] | ||
123 | |||
124 | result_types = ['failures', 'errors', 'skipped', 'expectedFailures'] | ||
125 | result_desc = ['FAILED', 'ERROR', 'SKIPPED', 'EXPECTEDFAIL'] | ||
126 | |||
127 | fail = False | ||
128 | desc = None | ||
129 | for idx, name in enumerate(result_types): | ||
130 | (fail, msg) = self._getDetailsNotPassed(case, result_types[idx], | ||
131 | result_desc[idx]) | ||
132 | if fail: | ||
133 | desc = result_desc[idx] | ||
134 | break | ||
135 | |||
136 | oeid = -1 | ||
137 | for d in case.decorators: | ||
138 | if hasattr(d, 'oeid'): | ||
139 | oeid = d.oeid | ||
140 | |||
141 | if fail: | ||
142 | self.logger.info("RESULTS - %s - Testcase %s: %s" % (case.id(), | ||
143 | oeid, desc)) | ||
144 | if msg: | ||
145 | self.logger.info(msg) | ||
146 | else: | ||
147 | self.logger.info("RESULTS - %s - Testcase %s: %s" % (case.id(), | ||
148 | oeid, 'PASSED')) | ||