2 # testcase.py: Control of test case execution.
4 # Subversion is a tool for revision control.
5 # See http://subversion.tigris.org for more information.
7 # ====================================================================
8 # Licensed to the Apache Software Foundation (ASF) under one
9 # or more contributor license agreements. See the NOTICE file
10 # distributed with this work for additional information
11 # regarding copyright ownership. The ASF licenses this file
12 # to you under the Apache License, Version 2.0 (the
13 # "License"); you may not use this file except in compliance
14 # with the License. You may obtain a copy of the License at
16 # http://www.apache.org/licenses/LICENSE-2.0
18 # Unless required by applicable law or agreed to in writing,
19 # software distributed under the License is distributed on an
20 # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
21 # KIND, either express or implied. See the License for the
22 # specific language governing permissions and limitations
24 ######################################################################
30 # if somebody does a "from testcase import *", they only get these names
31 __all__
= ['XFail', 'Wimp', 'Skip', 'SkipUnless']
39 '''Some ANSI terminal constants for output color'''
41 FAILURE
= '\033[1;31m'
42 SUCCESS
= '\033[1;32m'
51 def success(cls
, str):
52 return lambda: cls
.SUCCESS
+ str + cls
.ENDC
55 def failure(cls
, str):
56 return lambda: cls
.FAILURE
+ str + cls
.ENDC
59 if not sys
.stdout
.isatty() or sys
.platform
== 'win32':
64 """A thing that can be tested. This is an abstract class with
65 several methods that need to be overridden."""
68 RESULT_OK
: (0, TextColors
.success('PASS: '), True),
69 RESULT_FAIL
: (1, TextColors
.failure('FAIL: '), False),
70 RESULT_SKIP
: (2, TextColors
.success('SKIP: '), True),
73 def __init__(self
, delegate
=None, cond_func
=lambda: True, doc
=None, wip
=None):
74 """Create a test case instance based on DELEGATE.
76 COND_FUNC is a callable that is evaluated at test run time and should
77 return a boolean value that determines how a pass or failure is
78 interpreted: see the specialized kinds of test case such as XFail and
79 Skip for details. The evaluation of COND_FUNC is deferred so that it
80 can base its decision on useful bits of information that are not
81 available at __init__ time (like the fact that we're running over a
88 assert hasattr(cond_func
, '__call__')
90 self
._delegate
= delegate
91 self
._cond
_func
= cond_func
92 self
.description
= doc
or delegate
.description
95 def get_function_name(self
):
96 """Return the name of the python function implementing the test."""
97 return self
._delegate
.get_function_name()
99 def get_sandbox_name(self
):
100 """Return the name that should be used for the sandbox.
102 If a sandbox should not be constructed, this method returns None.
104 return self
._delegate
.get_sandbox_name()
106 def run(self
, sandbox
):
107 """Run the test within the given sandbox."""
108 return self
._delegate
.run(sandbox
)
113 def results(self
, result
):
114 # if our condition applied, then use our result map. otherwise, delegate.
115 if self
._cond
_func
():
116 val
= list(self
._result
_map
[result
])
119 return self
._delegate
.results(result
)
122 class FunctionTestCase(TestCase
):
123 """A TestCase based on a naked Python function object.
125 FUNC should be a function that returns None on success and throws an
126 svntest.Failure exception on failure. It should have a brief
127 docstring describing what it does (and fulfilling certain conditions).
128 FUNC must take one argument, an Sandbox instance. (The sandbox name
129 is derived from the file name in which FUNC was defined)
132 def __init__(self
, func
):
133 # it better be a function that accepts an sbox parameter and has a
135 assert isinstance(func
, types
.FunctionType
)
137 name
= func
.func_name
139 assert func
.func_code
.co_argcount
== 1, \
140 '%s must take an sbox argument' % name
142 doc
= func
.__doc
__.strip()
143 assert doc
, '%s must have a docstring' % name
145 # enforce stylistic guidelines for the function docstrings:
146 # - no longer than 50 characters
147 # - should not end in a period
148 # - should not be capitalized
149 assert len(doc
) <= 50, \
150 "%s's docstring must be 50 characters or less" % name
151 assert doc
[-1] != '.', \
152 "%s's docstring should not end in a period" % name
153 assert doc
[0].lower() == doc
[0], \
154 "%s's docstring should not be capitalized" % name
156 TestCase
.__init
__(self
, doc
=doc
)
159 def get_function_name(self
):
160 return self
.func
.func_name
162 def get_sandbox_name(self
):
163 """Base the sandbox's name on the name of the file in which the
164 function was defined."""
166 filename
= self
.func
.func_code
.co_filename
167 return os
.path
.splitext(os
.path
.basename(filename
))[0]
169 def run(self
, sandbox
):
170 return self
.func(sandbox
)
173 class XFail(TestCase
):
174 """A test that is expected to fail, if its condition is true."""
177 RESULT_OK
: (1, TextColors
.failure('XPASS:'), False),
178 RESULT_FAIL
: (0, TextColors
.success('XFAIL:'), True),
179 RESULT_SKIP
: (2, TextColors
.success('SKIP: '), True),
182 def __init__(self
, test_case
, cond_func
=lambda: True, wip
=None):
183 """Create an XFail instance based on TEST_CASE. COND_FUNC is a
184 callable that is evaluated at test run time and should return a
185 boolean value. If COND_FUNC returns true, then TEST_CASE is
186 expected to fail (and a pass is considered an error); otherwise,
187 TEST_CASE is run normally. The evaluation of COND_FUNC is
188 deferred so that it can base its decision on useful bits of
189 information that are not available at __init__ time (like the fact
190 that we're running over a particular RA layer).
194 TestCase
.__init
__(self
, create_test_case(test_case
), cond_func
, wip
=wip
)
197 # basically, the only possible delegate is a Skip test. favor that mode.
198 return self
._delegate
.list_mode() or 'XFAIL'
202 """Like XFail, but indicates a work-in-progress: an unexpected pass
203 is not considered a test failure."""
206 RESULT_OK
: (0, TextColors
.success('XPASS:'), True),
207 RESULT_FAIL
: (0, TextColors
.success('XFAIL:'), True),
208 RESULT_SKIP
: (2, TextColors
.success('SKIP: '), True),
211 def __init__(self
, wip
, test_case
, cond_func
=lambda: True):
212 XFail
.__init
__(self
, test_case
, cond_func
, wip
)
215 class Skip(TestCase
):
216 """A test that will be skipped if its conditional is true."""
218 def __init__(self
, test_case
, cond_func
=lambda: True):
219 """Create a Skip instance based on TEST_CASE. COND_FUNC is a
220 callable that is evaluated at test run time and should return a
221 boolean value. If COND_FUNC returns true, then TEST_CASE is
222 skipped; otherwise, TEST_CASE is run normally.
223 The evaluation of COND_FUNC is deferred so that it can base its
224 decision on useful bits of information that are not available at
225 __init__ time (like the fact that we're running over a
226 particular RA layer)."""
228 TestCase
.__init
__(self
, create_test_case(test_case
), cond_func
)
231 if self
._cond
_func
():
233 return self
._delegate
.list_mode()
235 def get_sandbox_name(self
):
236 if self
._cond
_func
():
238 return self
._delegate
.get_sandbox_name()
240 def run(self
, sandbox
):
241 if self
._cond
_func
():
243 return self
._delegate
.run(sandbox
)
246 class SkipUnless(Skip
):
247 """A test that will be skipped if its conditional is false."""
249 def __init__(self
, test_case
, cond_func
):
250 Skip
.__init
__(self
, test_case
, lambda c
=cond_func
: not c())
253 def create_test_case(func
):
254 if isinstance(func
, TestCase
):
257 return FunctionTestCase(func
)