1# Copyright (c) 2019 ARM Limited
2# All rights reserved
3#
4# The license below extends only to copyright in the software and shall
5# not be construed as granting a license to any other intellectual
6# property including but not limited to intellectual property relating
7# to a hardware implementation of the functionality of the software
8# licensed hereunder. You may use the software subject to the license
9# terms below provided that you ensure that this notice is replicated
10# unmodified and in its entirety in all distributions of the software,
11# modified or unmodified, in source code or in binary form.
12#
13# Copyright (c) 2017 Mark D. Hill and David A. Wood
14# All rights reserved.
15#
16# Redistribution and use in source and binary forms, with or without
17# modification, are permitted provided that the following conditions are
18# met: redistributions of source code must retain the above copyright
19# notice, this list of conditions and the following disclaimer;
20# redistributions in binary form must reproduce the above copyright
21# notice, this list of conditions and the following disclaimer in the
22# documentation and/or other materials provided with the distribution;
23# neither the name of the copyright holders nor the names of its
24# contributors may be used to endorse or promote products derived from
25# this software without specific prior written permission.
26#
27# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
28# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
29# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
30# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
31# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
32# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
33# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
34# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
35# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
36# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
37# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38#
39# Authors: Sean Wilson
40
41'''
42Module contains wrappers for test items that have been
43loaded by the testlib :class:`testlib.loader.Loader`.
44'''
45import itertools
46
47import log
48import uid
49from state import Status, Result
50
51class TestCaseMetadata():
52 def __init__(self, name, uid, path, result, status, suite_uid):
53 self.name = name
54 self.uid = uid
55 self.path = path
56 self.status = status
57 self.result = result
58 self.suite_uid = suite_uid
59
60
61class TestSuiteMetadata():
62 def __init__(self, name, uid, tags, path, status, result):
63 self.name = name
64 self.uid = uid
65 self.tags = tags
66 self.path = path
67 self.status = status
68 self.result = result
69
70
71class LibraryMetadata():
72 def __init__(self, name, result, status):
73 self.name = name
74 self.result = result
75 self.status = status
76
77
78class LoadedTestable(object):
79 '''
80 Base class for loaded test items.
81
82 :property:`result` and :property:`status` setters
83 notify testlib via the :func:`log_result` and :func:`log_status`
84 of the updated status.
85 '''
86 def __init__(self, obj):
87 self.obj = obj
88 self.metadata = self._generate_metadata()
89
90 @property
91 def status(self):
92 return self.metadata.status
93
94 @status.setter
95 def status(self, status):
96 self.log_status(status)
97 self.metadata.status = status
98
99 @property
100 def result(self):
101 return self.metadata.result
102
103 @result.setter
104 def result(self, result):
105 self.log_result(result)
106 self.metadata.result = result
107
108 @property
109 def uid(self):
110 return self.metadata.uid
111
112 @property
113 def name(self):
114 return self.metadata.name
115
116 @property
117 def fixtures(self):
118 return self.obj.fixtures
119
120 @fixtures.setter
121 def fixtures(self, fixtures):
122 self.obj.fixtures = fixtures
123
124 @property
125 def runner(self):
126 return self.obj.runner
127
128 # TODO Change log to provide status_update, result_update for all types.
129 def log_status(self, status):
130 log.test_log.status_update(self, status)
131
132 def log_result(self, result):
133 log.test_log.result_update(self, result)
134
135 def __iter__(self):
136 return iter(())
137
138
139class LoadedTest(LoadedTestable):
140 def __init__(self, test_obj, loaded_suite, path):
141 self.parent_suite = loaded_suite
142 self._path = path
143 LoadedTestable.__init__(self, test_obj)
144
145 def test(self, *args, **kwargs):
146 self.obj.test(*args, **kwargs)
147
148 def _generate_metadata(self):
149 return TestCaseMetadata( **{
150 'name':self.obj.name,
151 'path': self._path,
152 'uid': uid.TestUID(self._path,
153 self.obj.name,
154 self.parent_suite.name),
155 'status': Status.Unscheduled,
156 'result': Result(Result.NotRun),
157 'suite_uid': self.parent_suite.metadata.uid
158 })
159
160
161class LoadedSuite(LoadedTestable):
162 def __init__(self, suite_obj, path):
163 self._path = path
164 LoadedTestable.__init__(self, suite_obj)
165 self.tests = self._wrap_children(suite_obj)
166
167 def _wrap_children(self, suite_obj):
168 return [LoadedTest(test, self, self.metadata.path)
169 for test in suite_obj]
170
171 def _generate_metadata(self):
172 return TestSuiteMetadata( **{
173 'name': self.obj.name,
174 'tags':self.obj.tags,
175 'path': self._path,
176 'uid': uid.SuiteUID(self._path, self.obj.name),
177 'status': Status.Unscheduled,
178 'result': Result(Result.NotRun)
179 })
180
181 def __iter__(self):
182 return iter(self.tests)
183
184 @property
185 def tags(self):
186 return self.metadata.tags
187
188
189class LoadedLibrary(LoadedTestable):
190 '''
191 Wraps a collection of all loaded test suites and
192 provides utility functions for accessing fixtures.
193 '''
182 def __init__(self, suites, global_fixtures):
194 def __init__(self, suites):
195 LoadedTestable.__init__(self, suites)
184 self.global_fixtures = global_fixtures
196
197 def _generate_metadata(self):
198 return LibraryMetadata( **{
199 'name': 'Test Library',
200 'status': Status.Unscheduled,
201 'result': Result(Result.NotRun)
202 })
203
204 def __iter__(self):
205 '''
206 :returns: an iterator over contained :class:`TestSuite` objects.
207 '''
208 return iter(self.obj)
209
199 def all_fixture_tuples(self):
200 return itertools.chain(
201 self.global_fixtures,
202 *(suite.fixtures for suite in self.obj))
203
210 def all_fixtures(self):
211 '''
212 :returns: an interator overall all global, suite,
213 and test fixtures
214 '''
215 return itertools.chain(itertools.chain(
210 self.global_fixtures,
211 *(suite.fixtures for suite in self.obj)),
216 *(suite.fixtures for suite in self.obj)),
217 *(self.test_fixtures(suite) for suite in self.obj)
218 )
219
220 def test_fixtures(self, suite):
221 '''
222 :returns: an interator over all fixtures of each
223 test contained in the given suite
224 '''
225 return itertools.chain(*(test.fixtures for test in suite))
226
227 @property
228 def fixtures(self):
224 return self.global_fixtures
229 global_fixtures = []
230 for fixture in self.all_fixtures():
231 if fixture.is_global():
232 global_fixtures.append(fixture)
233 return global_fixtures
234
235 @property
236 def uid(self):
237 return self.name
238
239 @property
240 def suites(self):
241 return self.obj
242
243 @suites.setter
244 def suites(self, suites):
245 self.obj = suites