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