1# Copyright (c) 2017 Mark D. Hill and David A. Wood 2# All rights reserved. 3# 4# Redistribution and use in source and binary forms, with or without 5# modification, are permitted provided that the following conditions are 6# met: redistributions of source code must retain the above copyright 7# notice, this list of conditions and the following disclaimer; 8# redistributions in binary form must reproduce the above copyright 9# notice, this list of conditions and the following disclaimer in the 10# documentation and/or other materials provided with the distribution; 11# neither the name of the copyright holders nor the names of its 12# contributors may be used to endorse or promote products derived from 13# this software without specific prior written permission. 14# 15# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 16# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 17# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 18# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 19# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 20# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 21# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 22# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 23# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26# 27# Authors: Sean Wilson 28 29import functools 30 31import helper 32import runner as runner_mod 33 34class TestingException(Exception): 35 '''Common ancestor for manual Testing Exceptions.''' 36class TestFailException(TestingException): 37 '''Signals that a test has failed.''' 38class TestSkipException(TestingException): 39 '''Signals that a test has been skipped.''' 40 41def fail(message): 42 '''Cause the current test to fail with the given message.''' 43 raise TestFailException(message) 44 45def skip(message): 46 '''Cause the current test to skip with the given message.''' 47 raise TestSkipException(message) 48 49class TestCase(object): 50 ''' 51 Base class for all tests. 52 53 ..note:: 54 The :func:`__new__` method enables collection of test cases, it must 55 be called in order for test cases to be collected. 56 ''' 57 fixtures = [] 58 59 # TODO, remove explicit dependency. Use the loader to set the 60 # default runner 61 runner = runner_mod.TestRunner 62 collector = helper.InstanceCollector() 63 64 def __new__(cls, *args, **kwargs): 65 obj = super(TestCase, cls).__new__(cls, *args, **kwargs) 66 TestCase.collector.collect(obj) 67 return obj 68 69 def __init__(self, name=None, fixtures=tuple(), **kwargs): 70 self.fixtures = self.fixtures + list(fixtures) 71 if name is None: 72 name = self.__class__.__name__ 73 self.name = name 74 75class TestFunction(TestCase): 76 ''' 77 TestCase implementation which uses a callable object as a test. 78 ''' 79 def __init__(self, function, name=None, **kwargs): 80 self.test_function = function 81 if name is None: 82 name = function.__name__ 83 TestCase.__init__(self, name=name, **kwargs) 84 85 def test(self, *args, **kwargs): 86 self.test_function(*args, **kwargs) 87 88# TODO Change the decorator to make this easier to create copy tests. 89# Good way to do so might be return by reference. 90def testfunction(function=None, name=None, fixtures=tuple()): 91 ''' 92 A decorator used to wrap a function as a TestFunction. 93 ''' 94 def testfunctiondecorator(function): 95 '''Decorator used to mark a function as a test case.''' 96 kwargs = {} 97 if name is not None: 98 kwargs['name'] = name 99 if fixtures is not None: 100 kwargs['fixtures'] = fixtures 101 TestFunction(function, **kwargs) 102 return function 103 if function is not None: 104 return testfunctiondecorator(function) 105 else: 106 return testfunctiondecorator 107