comparison python-daemon-2.0.5/test/scaffold.py @ 33:7ceb967147c3

start xena with no gui add library files
author jingchunzhu <jingchunzhu@gmail.com>
date Wed, 22 Jul 2015 13:24:44 -0700
parents
children
comparison
equal deleted inserted replaced
32:63b1ba1e3424 33:7ceb967147c3
1 # -*- coding: utf-8 -*-
2
3 # test/scaffold.py
4 # Part of ‘python-daemon’, an implementation of PEP 3143.
5 #
6 # Copyright © 2007–2015 Ben Finney <ben+python@benfinney.id.au>
7 #
8 # This is free software: you may copy, modify, and/or distribute this work
9 # under the terms of the Apache License, version 2.0 as published by the
10 # Apache Software Foundation.
11 # No warranty expressed or implied. See the file ‘LICENSE.ASF-2’ for details.
12
13 """ Scaffolding for unit test modules.
14 """
15
16 from __future__ import (absolute_import, unicode_literals)
17
18 import unittest
19 import doctest
20 import logging
21 import os
22 import sys
23 import operator
24 import textwrap
25 from copy import deepcopy
26 import functools
27
28 try:
29 # Python 2 has both ‘str’ (bytes) and ‘unicode’ (text).
30 basestring = basestring
31 unicode = unicode
32 except NameError:
33 # Python 3 names the Unicode data type ‘str’.
34 basestring = str
35 unicode = str
36
37 import testscenarios
38 import testtools.testcase
39
40
41 test_dir = os.path.dirname(os.path.abspath(__file__))
42 parent_dir = os.path.dirname(test_dir)
43 if not test_dir in sys.path:
44 sys.path.insert(1, test_dir)
45 if not parent_dir in sys.path:
46 sys.path.insert(1, parent_dir)
47
48 # Disable all but the most critical logging messages.
49 logging.disable(logging.CRITICAL)
50
51
52 def get_function_signature(func):
53 """ Get the function signature as a mapping of attributes.
54
55 :param func: The function object to interrogate.
56 :return: A mapping of the components of a function signature.
57
58 The signature is constructed as a mapping:
59
60 * 'name': The function's defined name.
61 * 'arg_count': The number of arguments expected by the function.
62 * 'arg_names': A sequence of the argument names, as strings.
63 * 'arg_defaults': A sequence of the default values for the arguments.
64 * 'va_args': The name bound to remaining positional arguments.
65 * 'va_kw_args': The name bound to remaining keyword arguments.
66
67 """
68 try:
69 # Python 3 function attributes.
70 func_code = func.__code__
71 func_defaults = func.__defaults__
72 except AttributeError:
73 # Python 2 function attributes.
74 func_code = func.func_code
75 func_defaults = func.func_defaults
76
77 arg_count = func_code.co_argcount
78 arg_names = func_code.co_varnames[:arg_count]
79
80 arg_defaults = {}
81 if func_defaults is not None:
82 arg_defaults = dict(
83 (name, value)
84 for (name, value) in
85 zip(arg_names[::-1], func_defaults[::-1]))
86
87 signature = {
88 'name': func.__name__,
89 'arg_count': arg_count,
90 'arg_names': arg_names,
91 'arg_defaults': arg_defaults,
92 }
93
94 non_pos_names = list(func_code.co_varnames[arg_count:])
95 COLLECTS_ARBITRARY_POSITIONAL_ARGS = 0x04
96 if func_code.co_flags & COLLECTS_ARBITRARY_POSITIONAL_ARGS:
97 signature['var_args'] = non_pos_names.pop(0)
98 COLLECTS_ARBITRARY_KEYWORD_ARGS = 0x08
99 if func_code.co_flags & COLLECTS_ARBITRARY_KEYWORD_ARGS:
100 signature['var_kw_args'] = non_pos_names.pop(0)
101
102 return signature
103
104
105 def format_function_signature(func):
106 """ Format the function signature as printable text.
107
108 :param func: The function object to interrogate.
109 :return: A formatted text representation of the function signature.
110
111 The signature is rendered a text; for example::
112
113 foo(spam, eggs, ham=True, beans=None, *args, **kwargs)
114
115 """
116 signature = get_function_signature(func)
117
118 args_text = []
119 for arg_name in signature['arg_names']:
120 if arg_name in signature['arg_defaults']:
121 arg_text = "{name}={value!r}".format(
122 name=arg_name, value=signature['arg_defaults'][arg_name])
123 else:
124 arg_text = "{name}".format(
125 name=arg_name)
126 args_text.append(arg_text)
127 if 'var_args' in signature:
128 args_text.append("*{var_args}".format(signature))
129 if 'var_kw_args' in signature:
130 args_text.append("**{var_kw_args}".format(signature))
131 signature_args_text = ", ".join(args_text)
132
133 func_name = signature['name']
134 signature_text = "{name}({args})".format(
135 name=func_name, args=signature_args_text)
136
137 return signature_text
138
139
140 class TestCase(testtools.testcase.TestCase):
141 """ Test case behaviour. """
142
143 def failUnlessOutputCheckerMatch(self, want, got, msg=None):
144 """ Fail unless the specified string matches the expected.
145
146 :param want: The desired output pattern.
147 :param got: The actual text to match.
148 :param msg: A message to prefix on the failure message.
149 :return: ``None``.
150 :raises self.failureException: If the text does not match.
151
152 Fail the test unless ``want`` matches ``got``, as determined by
153 a ``doctest.OutputChecker`` instance. This is not an equality
154 check, but a pattern match according to the ``OutputChecker``
155 rules.
156
157 """
158 checker = doctest.OutputChecker()
159 want = textwrap.dedent(want)
160 source = ""
161 example = doctest.Example(source, want)
162 got = textwrap.dedent(got)
163 checker_optionflags = functools.reduce(operator.or_, [
164 doctest.ELLIPSIS,
165 ])
166 if not checker.check_output(want, got, checker_optionflags):
167 if msg is None:
168 diff = checker.output_difference(
169 example, got, checker_optionflags)
170 msg = "\n".join([
171 "Output received did not match expected output",
172 "{diff}",
173 ]).format(
174 diff=diff)
175 raise self.failureException(msg)
176
177 assertOutputCheckerMatch = failUnlessOutputCheckerMatch
178
179 def failUnlessFunctionInTraceback(self, traceback, function, msg=None):
180 """ Fail if the function is not in the traceback.
181
182 :param traceback: The traceback object to interrogate.
183 :param function: The function object to match.
184 :param msg: A message to prefix on the failure message.
185 :return: ``None``.
186
187 :raises self.failureException: If the function is not in the
188 traceback.
189
190 Fail the test if the function ``function`` is not at any of the
191 levels in the traceback object ``traceback``.
192
193 """
194 func_in_traceback = False
195 expected_code = function.func_code
196 current_traceback = traceback
197 while current_traceback is not None:
198 if expected_code is current_traceback.tb_frame.f_code:
199 func_in_traceback = True
200 break
201 current_traceback = current_traceback.tb_next
202
203 if not func_in_traceback:
204 if msg is None:
205 msg = (
206 "Traceback did not lead to original function"
207 " {function}"
208 ).format(
209 function=function)
210 raise self.failureException(msg)
211
212 assertFunctionInTraceback = failUnlessFunctionInTraceback
213
214 def failUnlessFunctionSignatureMatch(self, first, second, msg=None):
215 """ Fail if the function signatures do not match.
216
217 :param first: The first function to compare.
218 :param second: The second function to compare.
219 :param msg: A message to prefix to the failure message.
220 :return: ``None``.
221
222 :raises self.failureException: If the function signatures do
223 not match.
224
225 Fail the test if the function signature does not match between
226 the ``first`` function and the ``second`` function.
227
228 The function signature includes:
229
230 * function name,
231
232 * count of named parameters,
233
234 * sequence of named parameters,
235
236 * default values of named parameters,
237
238 * collector for arbitrary positional arguments,
239
240 * collector for arbitrary keyword arguments.
241
242 """
243 first_signature = get_function_signature(first)
244 second_signature = get_function_signature(second)
245
246 if first_signature != second_signature:
247 if msg is None:
248 first_signature_text = format_function_signature(first)
249 second_signature_text = format_function_signature(second)
250 msg = (textwrap.dedent("""\
251 Function signatures do not match:
252 {first!r} != {second!r}
253 Expected:
254 {first_text}
255 Got:
256 {second_text}""")
257 ).format(
258 first=first_signature,
259 first_text=first_signature_text,
260 second=second_signature,
261 second_text=second_signature_text,
262 )
263 raise self.failureException(msg)
264
265 assertFunctionSignatureMatch = failUnlessFunctionSignatureMatch
266
267
268 class TestCaseWithScenarios(testscenarios.WithScenarios, TestCase):
269 """ Test cases run per scenario. """
270
271
272 class Exception_TestCase(TestCaseWithScenarios):
273 """ Test cases for exception classes. """
274
275 def test_exception_instance(self):
276 """ Exception instance should be created. """
277 self.assertIsNot(self.instance, None)
278
279 def test_exception_types(self):
280 """ Exception instance should match expected types. """
281 for match_type in self.types:
282 self.assertIsInstance(self.instance, match_type)
283
284
285 def make_exception_scenarios(scenarios):
286 """ Make test scenarios for exception classes.
287
288 :param scenarios: Sequence of scenarios.
289 :return: List of scenarios with additional mapping entries.
290
291 Use this with `testscenarios` to adapt `Exception_TestCase`_ for
292 any exceptions that need testing.
293
294 Each scenario is a tuple (`name`, `map`) where `map` is a mapping
295 of attributes to be applied to each test case. Attributes map must
296 contain items for:
297
298 :key exc_type:
299 The exception type to be tested.
300 :key min_args:
301 The minimum argument count for the exception instance
302 initialiser.
303 :key types:
304 Sequence of types that should be superclasses of each
305 instance of the exception type.
306
307 """
308 updated_scenarios = deepcopy(scenarios)
309 for (name, scenario) in updated_scenarios:
310 args = (None,) * scenario['min_args']
311 scenario['args'] = args
312 instance = scenario['exc_type'](*args)
313 scenario['instance'] = instance
314
315 return updated_scenarios
316
317
318 # Local variables:
319 # coding: utf-8
320 # mode: python
321 # End:
322 # vim: fileencoding=utf-8 filetype=python :