In the command-line client, forbid
[svn.git] / subversion / tests / cmdline / svntest / testcase.py
blob70df3d5f649e9ce3efdff4bd5619b1f0e9b92d80
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 # Copyright (c) 2000-2004 CollabNet. All rights reserved.
10 # This software is licensed as described in the file COPYING, which
11 # you should have received as part of this distribution. The terms
12 # are also available at http://subversion.tigris.org/license-1.html.
13 # If newer versions of this license are posted there, you may use a
14 # newer version instead, at your option.
16 ######################################################################
18 import os, types
20 import svntest
22 __all__ = ['XFail', 'Skip']
25 class TestCase:
26 """A thing that can be tested. This is an abstract class with
27 several methods that need to be overridden."""
29 def __init__(self):
30 # Each element is a tuple whose second element indicates benignity:
31 # e.g., True means "can be ignored when in quiet_mode". See also
32 # XFail.run_test().
33 self._result_text = [('PASS: ', True),
34 ('FAIL: ', False),
35 ('SKIP: ', True)]
36 self._list_mode_text = ''
38 def get_description(self):
39 raise NotImplementedError()
41 def check_description(self):
42 description = self.get_description()
44 if len(description) > 50:
45 print 'WARNING: Test doc string exceeds 50 characters'
46 if description[-1] == '.':
47 print 'WARNING: Test doc string ends in a period (.)'
48 if not description[0].lower() == description[0]:
49 print 'WARNING: Test doc string is capitalized'
51 def need_sandbox(self):
52 """Return True iff this test needs a Sandbox for its execution."""
54 return 0
56 def get_sandbox_name(self):
57 """Return the name that should be used for the sandbox.
59 This method is only called if self.need_sandbox() returns True."""
61 return 'sandbox'
63 def run(self, sandbox=None):
64 """Run the test.
66 If self.need_sandbox() returns True, then a Sandbox instance is
67 passed to this method as the SANDBOX keyword argument; otherwise,
68 no argument is passed to this method."""
70 raise NotImplementedError()
72 def list_mode(self):
73 return self._list_mode_text
75 def run_text(self, result=0):
76 return self._result_text[result]
78 def convert_result(self, result):
79 return result
82 class FunctionTestCase(TestCase):
83 """A TestCase based on a naked Python function object.
85 FUNC should be a function that returns None on success and throws an
86 svntest.Failure exception on failure. It should have a brief
87 docstring describing what it does (and fulfilling the conditions
88 enforced by TestCase.check_description()). FUNC may take zero or
89 one argument. It it takes an argument, it will be invoked with an
90 svntest.main.Sandbox instance as argument. (The sandbox's name is
91 derived from the file name in which FUNC was defined.)"""
93 def __init__(self, func):
94 TestCase.__init__(self)
95 self.func = func
96 assert type(self.func) is types.FunctionType
98 def get_description(self):
99 """Use the function's docstring as a description."""
101 description = self.func.__doc__
102 if not description:
103 raise Exception(self.func.__name__ + ' lacks required doc string')
104 return description
106 def need_sandbox(self):
107 """If the function requires an argument, then we need to pass it a
108 sandbox."""
110 return self.func.func_code.co_argcount != 0
112 def get_sandbox_name(self):
113 """Base the sandbox's name on the name of the file in which the
114 function was defined."""
116 filename = self.func.func_code.co_filename
117 return os.path.splitext(os.path.basename(filename))[0]
119 def run(self, sandbox=None):
120 if self.need_sandbox():
121 return self.func(sandbox)
122 else:
123 return self.func()
126 class XFail(TestCase):
127 "A test that is expected to fail."
129 def __init__(self, test_case, cond_func=lambda:1):
130 """Create an XFail instance based on TEST_CASE. COND_FUNC is a
131 callable that is evaluated at test run time and should return a
132 boolean value. If COND_FUNC returns true, then TEST_CASE is
133 expected to fail (and a pass is considered an error); otherwise,
134 TEST_CASE is run normally. The evaluation of COND_FUNC is
135 deferred so that it can base its decision on useful bits of
136 information that are not available at __init__ time (like the fact
137 that we're running over a particular RA layer)."""
139 TestCase.__init__(self)
140 self.test_case = create_test_case(test_case)
141 self._list_mode_text = self.test_case.list_mode() or 'XFAIL'
142 # Delegate most methods to self.test_case:
143 self.get_description = self.test_case.get_description
144 self.need_sandbox = self.test_case.need_sandbox
145 self.get_sandbox_name = self.test_case.get_sandbox_name
146 self.run = self.test_case.run
147 self.cond_func = cond_func
149 def convert_result(self, result):
150 if self.cond_func():
151 # Conditions are reversed here: a failure is expected, therefore
152 # it isn't an error; a pass is an error; but a skip remains a skip.
153 return {0:1, 1:0, 2:2}[self.test_case.convert_result(result)]
154 else:
155 return self.test_case.convert_result(result)
157 def run_text(self, result=0):
158 if self.cond_func():
159 # Tuple elements mean same as in TestCase._result_text.
160 return [('XFAIL:', True),
161 ('XPASS:', False),
162 self.test_case.run_text(2)][result]
163 else:
164 return self.test_case.run_text(result)
167 class Skip(TestCase):
168 """A test that will be skipped if its conditional is true."""
170 def __init__(self, test_case, cond_func=lambda:1):
171 """Create an Skip instance based on TEST_CASE. COND_FUNC is a
172 callable that is evaluated at test run time and should return a
173 boolean value. If COND_FUNC returns true, then TEST_CASE is
174 skipped; otherwise, TEST_CASE is run normally.
175 The evaluation of COND_FUNC is deferred so that it can base its
176 decision on useful bits of information that are not available at
177 __init__ time (like the fact that we're running over a
178 particular RA layer)."""
180 TestCase.__init__(self)
181 self.test_case = create_test_case(test_case)
182 self.cond_func = cond_func
183 try:
184 if self.conditional():
185 self._list_mode_text = 'SKIP'
186 except svntest.Failure:
187 pass
188 # Delegate most methods to self.test_case:
189 self.get_description = self.test_case.get_description
190 self.get_sandbox_name = self.test_case.get_sandbox_name
191 self.convert_result = self.test_case.convert_result
193 def need_sandbox(self):
194 if self.conditional():
195 return 0
196 else:
197 return self.test_case.need_sandbox()
199 def run(self, sandbox=None):
200 if self.conditional():
201 raise svntest.Skip
202 elif self.need_sandbox():
203 return self.test_case.run(sandbox=sandbox)
204 else:
205 return self.test_case.run()
207 def conditional(self):
208 """Invoke SELF.cond_func(), and return the result evaluated
209 against the expected value."""
210 return self.cond_func()
213 class SkipUnless(Skip):
214 """A test that will be skipped if its conditional is false."""
216 def __init__(self, test_case, cond_func):
217 Skip.__init__(self, test_case, cond_func)
219 def conditional(self):
220 "Return the negation of SELF.cond_func()."
221 return not self.cond_func()
224 def create_test_case(func):
225 if isinstance(func, TestCase):
226 return func
227 else:
228 return FunctionTestCase(func)
231 ### End of file.