# Copyright (c) 2017 Mark D. Hill and David A. Wood # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are # met: redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer; # redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution; # neither the name of the copyright holders nor the names of its # contributors may be used to endorse or promote products derived from # this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # # Authors: Sean Wilson ''' Module contains wrappers for test items that have been loaded by the testlib :class:`testlib.loader.Loader`. ''' import itertools import log import uid from state import Status, Result class TestCaseMetadata(): def __init__(self, name, uid, path, result, status, suite_uid): self.name = name self.uid = uid self.path = path self.status = status self.result = result self.suite_uid = suite_uid class TestSuiteMetadata(): def __init__(self, name, uid, tags, path, status, result): self.name = name self.uid = uid self.tags = tags self.path = path self.status = status self.result = result class LibraryMetadata(): def __init__(self, name, result, status): self.name = name self.result = result self.status = status class LoadedTestable(object): ''' Base class for loaded test items. :property:`result` and :property:`status` setters notify testlib via the :func:`log_result` and :func:`log_status` of the updated status. ''' def __init__(self, obj): self.obj = obj self.metadata = self._generate_metadata() @property def status(self): return self.metadata.status @status.setter def status(self, status): self.log_status(status) self.metadata.status = status @property def result(self): return self.metadata.result @result.setter def result(self, result): self.log_result(result) self.metadata.result = result @property def uid(self): return self.metadata.uid @property def name(self): return self.metadata.name @property def fixtures(self): return self.obj.fixtures @fixtures.setter def fixtures(self, fixtures): self.obj.fixtures = fixtures @property def runner(self): return self.obj.runner # TODO Change log to provide status_update, result_update for all types. def log_status(self, status): log.test_log.status_update(self, status) def log_result(self, result): log.test_log.result_update(self, result) def __iter__(self): return iter(()) class LoadedTest(LoadedTestable): def __init__(self, test_obj, loaded_suite, path): self.parent_suite = loaded_suite self._path = path LoadedTestable.__init__(self, test_obj) def test(self, *args, **kwargs): self.obj.test(*args, **kwargs) def _generate_metadata(self): return TestCaseMetadata( **{ 'name':self.obj.name, 'path': self._path, 'uid': uid.TestUID(self._path, self.obj.name, self.parent_suite.name), 'status': Status.Unscheduled, 'result': Result(Result.NotRun), 'suite_uid': self.parent_suite.metadata.uid }) class LoadedSuite(LoadedTestable): def __init__(self, suite_obj, path): self._path = path LoadedTestable.__init__(self, suite_obj) self.tests = self._wrap_children(suite_obj) def _wrap_children(self, suite_obj): return [LoadedTest(test, self, self.metadata.path) for test in suite_obj] def _generate_metadata(self): return TestSuiteMetadata( **{ 'name': self.obj.name, 'tags':self.obj.tags, 'path': self._path, 'uid': uid.SuiteUID(self._path, self.obj.name), 'status': Status.Unscheduled, 'result': Result(Result.NotRun) }) def __iter__(self): return iter(self.tests) @property def tags(self): return self.metadata.tags class LoadedLibrary(LoadedTestable): ''' Wraps a collection of all loaded test suites and provides utility functions for accessing fixtures. ''' def __init__(self, suites, global_fixtures): LoadedTestable.__init__(self, suites) self.global_fixtures = global_fixtures def _generate_metadata(self): return LibraryMetadata( **{ 'name': 'Test Library', 'status': Status.Unscheduled, 'result': Result(Result.NotRun) }) def __iter__(self): ''' :returns: an iterator over contained :class:`TestSuite` objects. ''' return iter(self.obj) def all_fixture_tuples(self): return itertools.chain( self.global_fixtures, *(suite.fixtures for suite in self.obj)) def all_fixtures(self): ''' :returns: an interator overall all global, suite, and test fixtures ''' return itertools.chain(itertools.chain( self.global_fixtures, *(suite.fixtures for suite in self.obj)), *(self.test_fixtures(suite) for suite in self.obj) ) def test_fixtures(self, suite): ''' :returns: an interator over all fixtures of each test contained in the given suite ''' return itertools.chain(*(test.fixtures for test in suite)) @property def fixtures(self): return self.global_fixtures @property def uid(self): return self.name @property def suites(self): return self.obj @suites.setter def suites(self, suites): self.obj = suites