runner.py revision 12882
112882Sspwilson2@wisc.edu# Copyright (c) 2017 Mark D. Hill and David A. Wood 212882Sspwilson2@wisc.edu# All rights reserved. 312882Sspwilson2@wisc.edu# 412882Sspwilson2@wisc.edu# Redistribution and use in source and binary forms, with or without 512882Sspwilson2@wisc.edu# modification, are permitted provided that the following conditions are 612882Sspwilson2@wisc.edu# met: redistributions of source code must retain the above copyright 712882Sspwilson2@wisc.edu# notice, this list of conditions and the following disclaimer; 812882Sspwilson2@wisc.edu# redistributions in binary form must reproduce the above copyright 912882Sspwilson2@wisc.edu# notice, this list of conditions and the following disclaimer in the 1012882Sspwilson2@wisc.edu# documentation and/or other materials provided with the distribution; 1112882Sspwilson2@wisc.edu# neither the name of the copyright holders nor the names of its 1212882Sspwilson2@wisc.edu# contributors may be used to endorse or promote products derived from 1312882Sspwilson2@wisc.edu# this software without specific prior written permission. 1412882Sspwilson2@wisc.edu# 1512882Sspwilson2@wisc.edu# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 1612882Sspwilson2@wisc.edu# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 1712882Sspwilson2@wisc.edu# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 1812882Sspwilson2@wisc.edu# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 1912882Sspwilson2@wisc.edu# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 2012882Sspwilson2@wisc.edu# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 2112882Sspwilson2@wisc.edu# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 2212882Sspwilson2@wisc.edu# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 2312882Sspwilson2@wisc.edu# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 2412882Sspwilson2@wisc.edu# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 2512882Sspwilson2@wisc.edu# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 2612882Sspwilson2@wisc.edu# 2712882Sspwilson2@wisc.edu# Authors: Sean Wilson 2812882Sspwilson2@wisc.edu 2912882Sspwilson2@wisc.eduimport multiprocessing.dummy 3012882Sspwilson2@wisc.eduimport threading 3112882Sspwilson2@wisc.eduimport traceback 3212882Sspwilson2@wisc.edu 3312882Sspwilson2@wisc.eduimport helper 3412882Sspwilson2@wisc.eduimport state 3512882Sspwilson2@wisc.eduimport log 3612882Sspwilson2@wisc.eduimport sandbox 3712882Sspwilson2@wisc.edu 3812882Sspwilson2@wisc.edufrom state import Status, Result 3912882Sspwilson2@wisc.edufrom fixture import SkipException 4012882Sspwilson2@wisc.edu 4112882Sspwilson2@wisc.edudef compute_aggregate_result(iterable): 4212882Sspwilson2@wisc.edu ''' 4312882Sspwilson2@wisc.edu Status of the test suite by default is: 4412882Sspwilson2@wisc.edu * Passed if all contained tests passed 4512882Sspwilson2@wisc.edu * Errored if any contained tests errored 4612882Sspwilson2@wisc.edu * Failed if no tests errored, but one or more failed. 4712882Sspwilson2@wisc.edu * Skipped if all contained tests were skipped 4812882Sspwilson2@wisc.edu ''' 4912882Sspwilson2@wisc.edu failed = [] 5012882Sspwilson2@wisc.edu skipped = [] 5112882Sspwilson2@wisc.edu for testitem in iterable: 5212882Sspwilson2@wisc.edu result = testitem.result 5312882Sspwilson2@wisc.edu 5412882Sspwilson2@wisc.edu if result.value == Result.Errored: 5512882Sspwilson2@wisc.edu return Result(result.value, result.reason) 5612882Sspwilson2@wisc.edu elif result.value == Result.Failed: 5712882Sspwilson2@wisc.edu failed.append(result.reason) 5812882Sspwilson2@wisc.edu elif result.value == result.Skipped: 5912882Sspwilson2@wisc.edu skipped.append(result.reason) 6012882Sspwilson2@wisc.edu if failed: 6112882Sspwilson2@wisc.edu return Result(Result.Failed, failed) 6212882Sspwilson2@wisc.edu elif skipped: 6312882Sspwilson2@wisc.edu return Result(Result.Skipped, skipped) 6412882Sspwilson2@wisc.edu else: 6512882Sspwilson2@wisc.edu return Result(Result.Passed) 6612882Sspwilson2@wisc.edu 6712882Sspwilson2@wisc.educlass TestParameters(object): 6812882Sspwilson2@wisc.edu def __init__(self, test, suite): 6912882Sspwilson2@wisc.edu self.test = test 7012882Sspwilson2@wisc.edu self.suite = suite 7112882Sspwilson2@wisc.edu self.log = log.TestLogWrapper(log.test_log, test, suite) 7212882Sspwilson2@wisc.edu 7312882Sspwilson2@wisc.edu @helper.cacheresult 7412882Sspwilson2@wisc.edu def _fixtures(self): 7512882Sspwilson2@wisc.edu fixtures = {fixture.name:fixture for fixture in self.suite.fixtures} 7612882Sspwilson2@wisc.edu for fixture in self.test.fixtures: 7712882Sspwilson2@wisc.edu fixtures[fixture.name] = fixture 7812882Sspwilson2@wisc.edu return fixtures 7912882Sspwilson2@wisc.edu 8012882Sspwilson2@wisc.edu @property 8112882Sspwilson2@wisc.edu def fixtures(self): 8212882Sspwilson2@wisc.edu return self._fixtures() 8312882Sspwilson2@wisc.edu 8412882Sspwilson2@wisc.edu 8512882Sspwilson2@wisc.educlass RunnerPattern: 8612882Sspwilson2@wisc.edu def __init__(self, loaded_testable): 8712882Sspwilson2@wisc.edu self.testable = loaded_testable 8812882Sspwilson2@wisc.edu self.builder = FixtureBuilder(self.testable.fixtures) 8912882Sspwilson2@wisc.edu 9012882Sspwilson2@wisc.edu def handle_error(self, trace): 9112882Sspwilson2@wisc.edu self.testable.result = Result(Result.Errored, trace) 9212882Sspwilson2@wisc.edu self.avoid_children(trace) 9312882Sspwilson2@wisc.edu 9412882Sspwilson2@wisc.edu def handle_skip(self, trace): 9512882Sspwilson2@wisc.edu self.testable.result = Result(Result.Skipped, trace) 9612882Sspwilson2@wisc.edu self.avoid_children(trace) 9712882Sspwilson2@wisc.edu 9812882Sspwilson2@wisc.edu def avoid_children(self, reason): 9912882Sspwilson2@wisc.edu for testable in self.testable: 10012882Sspwilson2@wisc.edu testable.result = Result(self.testable.result.value, reason) 10112882Sspwilson2@wisc.edu testable.status = Status.Avoided 10212882Sspwilson2@wisc.edu 10312882Sspwilson2@wisc.edu def test(self): 10412882Sspwilson2@wisc.edu pass 10512882Sspwilson2@wisc.edu 10612882Sspwilson2@wisc.edu def run(self): 10712882Sspwilson2@wisc.edu avoided = False 10812882Sspwilson2@wisc.edu try: 10912882Sspwilson2@wisc.edu self.testable.status = Status.Building 11012882Sspwilson2@wisc.edu self.builder.setup(self.testable) 11112882Sspwilson2@wisc.edu except SkipException: 11212882Sspwilson2@wisc.edu self.handle_skip(traceback.format_exc()) 11312882Sspwilson2@wisc.edu avoided = True 11412882Sspwilson2@wisc.edu except BrokenFixtureException: 11512882Sspwilson2@wisc.edu self.handle_error(traceback.format_exc()) 11612882Sspwilson2@wisc.edu avoided = True 11712882Sspwilson2@wisc.edu else: 11812882Sspwilson2@wisc.edu self.testable.status = Status.Running 11912882Sspwilson2@wisc.edu self.test() 12012882Sspwilson2@wisc.edu finally: 12112882Sspwilson2@wisc.edu self.testable.status = Status.TearingDown 12212882Sspwilson2@wisc.edu self.builder.teardown(self.testable) 12312882Sspwilson2@wisc.edu 12412882Sspwilson2@wisc.edu if avoided: 12512882Sspwilson2@wisc.edu self.testable.status = Status.Avoided 12612882Sspwilson2@wisc.edu else: 12712882Sspwilson2@wisc.edu self.testable.status = Status.Complete 12812882Sspwilson2@wisc.edu 12912882Sspwilson2@wisc.educlass TestRunner(RunnerPattern): 13012882Sspwilson2@wisc.edu def test(self): 13112882Sspwilson2@wisc.edu self.sandbox_test() 13212882Sspwilson2@wisc.edu 13312882Sspwilson2@wisc.edu def sandbox_test(self): 13412882Sspwilson2@wisc.edu try: 13512882Sspwilson2@wisc.edu sandbox.Sandbox(TestParameters( 13612882Sspwilson2@wisc.edu self.testable, 13712882Sspwilson2@wisc.edu self.testable.parent_suite)) 13812882Sspwilson2@wisc.edu except sandbox.SubprocessException: 13912882Sspwilson2@wisc.edu self.testable.result = Result(Result.Failed, 14012882Sspwilson2@wisc.edu traceback.format_exc()) 14112882Sspwilson2@wisc.edu else: 14212882Sspwilson2@wisc.edu self.testable.result = Result(Result.Passed) 14312882Sspwilson2@wisc.edu 14412882Sspwilson2@wisc.edu 14512882Sspwilson2@wisc.educlass SuiteRunner(RunnerPattern): 14612882Sspwilson2@wisc.edu def test(self): 14712882Sspwilson2@wisc.edu for test in self.testable: 14812882Sspwilson2@wisc.edu test.runner(test).run() 14912882Sspwilson2@wisc.edu self.testable.result = compute_aggregate_result( 15012882Sspwilson2@wisc.edu iter(self.testable)) 15112882Sspwilson2@wisc.edu 15212882Sspwilson2@wisc.edu 15312882Sspwilson2@wisc.educlass LibraryRunner(SuiteRunner): 15412882Sspwilson2@wisc.edu pass 15512882Sspwilson2@wisc.edu 15612882Sspwilson2@wisc.edu 15712882Sspwilson2@wisc.educlass LibraryParallelRunner(RunnerPattern): 15812882Sspwilson2@wisc.edu def set_threads(self, threads): 15912882Sspwilson2@wisc.edu self.threads = threads 16012882Sspwilson2@wisc.edu 16112882Sspwilson2@wisc.edu def _entrypoint(self, suite): 16212882Sspwilson2@wisc.edu suite.runner(suite).run() 16312882Sspwilson2@wisc.edu 16412882Sspwilson2@wisc.edu def test(self): 16512882Sspwilson2@wisc.edu pool = multiprocessing.dummy.Pool(self.threads) 16612882Sspwilson2@wisc.edu pool.map(lambda suite : suite.runner(suite).run(), self.testable) 16712882Sspwilson2@wisc.edu self.testable.result = compute_aggregate_result( 16812882Sspwilson2@wisc.edu iter(self.testable)) 16912882Sspwilson2@wisc.edu 17012882Sspwilson2@wisc.edu 17112882Sspwilson2@wisc.educlass BrokenFixtureException(Exception): 17212882Sspwilson2@wisc.edu def __init__(self, fixture, testitem, trace): 17312882Sspwilson2@wisc.edu self.fixture = fixture 17412882Sspwilson2@wisc.edu self.testitem = testitem 17512882Sspwilson2@wisc.edu self.trace = trace 17612882Sspwilson2@wisc.edu 17712882Sspwilson2@wisc.edu self.msg = ('%s\n' 17812882Sspwilson2@wisc.edu 'Exception raised building "%s" raised SkipException' 17912882Sspwilson2@wisc.edu ' for "%s".' % 18012882Sspwilson2@wisc.edu (trace, fixture.name, testitem.name) 18112882Sspwilson2@wisc.edu ) 18212882Sspwilson2@wisc.edu super(BrokenFixtureException, self).__init__(self.msg) 18312882Sspwilson2@wisc.edu 18412882Sspwilson2@wisc.educlass FixtureBuilder(object): 18512882Sspwilson2@wisc.edu def __init__(self, fixtures): 18612882Sspwilson2@wisc.edu self.fixtures = fixtures 18712882Sspwilson2@wisc.edu self.built_fixtures = [] 18812882Sspwilson2@wisc.edu 18912882Sspwilson2@wisc.edu def setup(self, testitem): 19012882Sspwilson2@wisc.edu for fixture in self.fixtures: 19112882Sspwilson2@wisc.edu # Mark as built before, so if the build fails 19212882Sspwilson2@wisc.edu # we still try to tear it down. 19312882Sspwilson2@wisc.edu self.built_fixtures.append(fixture) 19412882Sspwilson2@wisc.edu try: 19512882Sspwilson2@wisc.edu fixture.setup(testitem) 19612882Sspwilson2@wisc.edu except SkipException: 19712882Sspwilson2@wisc.edu raise 19812882Sspwilson2@wisc.edu except Exception as e: 19912882Sspwilson2@wisc.edu exc = traceback.format_exc() 20012882Sspwilson2@wisc.edu msg = 'Exception raised while setting up fixture for %s' %\ 20112882Sspwilson2@wisc.edu testitem.uid 20212882Sspwilson2@wisc.edu log.test_log.warn('%s\n%s' % (exc, msg)) 20312882Sspwilson2@wisc.edu 20412882Sspwilson2@wisc.edu raise BrokenFixtureException(fixture, testitem, 20512882Sspwilson2@wisc.edu traceback.format_exc()) 20612882Sspwilson2@wisc.edu 20712882Sspwilson2@wisc.edu def teardown(self, testitem): 20812882Sspwilson2@wisc.edu for fixture in self.built_fixtures: 20912882Sspwilson2@wisc.edu try: 21012882Sspwilson2@wisc.edu fixture.teardown(testitem) 21112882Sspwilson2@wisc.edu except Exception: 21212882Sspwilson2@wisc.edu # Log exception but keep cleaning up. 21312882Sspwilson2@wisc.edu exc = traceback.format_exc() 21412882Sspwilson2@wisc.edu msg = 'Exception raised while tearing down fixture for %s' %\ 21512882Sspwilson2@wisc.edu testitem.uid 21612882Sspwilson2@wisc.edu log.test_log.warn('%s\n%s' % (exc, msg)) 217