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    '''
194    def __init__(self, suites):
195        LoadedTestable.__init__(self, suites)
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
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(
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):
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
246