blob: 88dc79b905c1a09af0dd471af257e1627bd9903d [file] [log] [blame]
Jean-Paul Calderone8671c852011-03-02 19:26:20 -05001# Copyright (C) Jean-Paul Calderone
2# Copyright (C) Twisted Matrix Laboratories.
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -04003# See LICENSE for details.
4
5"""
6Helpers for the OpenSSL test suite, largely copied from
7U{Twisted<http://twistedmatrix.com/>}.
8"""
9
10import shutil
Jean-Paul Calderone68a6f8f2013-03-01 17:56:22 -080011import traceback
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -040012import os, os.path
13from tempfile import mktemp
14from unittest import TestCase
Rick Dean47262da2009-07-08 16:17:17 -050015import sys
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -040016
Jean-Paul Calderone88f38b22009-07-16 16:25:19 -040017from OpenSSL.crypto import Error, _exception_from_error_queue
18
Jean-Paul Calderone68a6f8f2013-03-01 17:56:22 -080019import memdbg
20
Jean-Paul Calderoneea9c8a32011-04-01 18:26:37 -040021if sys.version_info < (3, 0):
Jean-Paul Calderone9e4eeae2010-08-22 21:32:52 -040022 def b(s):
23 return s
24 bytes = str
25else:
26 def b(s):
Jean-Paul Calderone77769602011-04-06 18:20:10 -040027 return s.encode("charmap")
Jean-Paul Calderoneea9c8a32011-04-01 18:26:37 -040028 bytes = bytes
Jean-Paul Calderone9e4eeae2010-08-22 21:32:52 -040029
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -080030from tls.c import api
Jean-Paul Calderone9e4eeae2010-08-22 21:32:52 -040031
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -040032class TestCase(TestCase):
33 """
Jonathan Ballet648875f2011-07-16 14:14:58 +090034 :py:class:`TestCase` adds useful testing functionality beyond what is available
35 from the standard library :py:class:`unittest.TestCase`.
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -040036 """
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -080037 def setUp(self):
38 super(TestCase, self).setUp()
Jean-Paul Calderone68a6f8f2013-03-01 17:56:22 -080039 self._before = set(memdbg.heap)
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -080040
Jean-Paul Calderone40732ff2013-03-01 20:53:50 -080041
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -040042 def tearDown(self):
43 """
Jonathan Ballet648875f2011-07-16 14:14:58 +090044 Clean up any files or directories created using :py:meth:`TestCase.mktemp`.
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -040045 Subclasses must invoke this method if they override it or the
46 cleanup will not occur.
47 """
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -080048 import gc
49 gc.collect(); gc.collect(); gc.collect()
50
Jean-Paul Calderone68a6f8f2013-03-01 17:56:22 -080051 def format_leak(p):
52 stacks = memdbg.heap[p]
53 # Eventually look at multiple stacks for the realloc() case. For
54 # now just look at the original allocation location.
55 (python_stack, c_stack) = stacks[0]
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -080056
Jean-Paul Calderone68a6f8f2013-03-01 17:56:22 -080057 stack = traceback.format_list(python_stack)[:-1]
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -080058
Jean-Paul Calderone68a6f8f2013-03-01 17:56:22 -080059 # c_stack looks something like this (interesting parts indicated
60 # with inserted arrows not part of the data):
61 #
62 # /home/exarkun/Projects/pyOpenSSL/branches/use-opentls/__pycache__/_cffi__x89095113xb9185b9b.so(+0x12cf) [0x7fe2e20582cf]
63 # /home/exarkun/Projects/cpython/2.7/python(PyCFunction_Call+0x8b) [0x56265a]
64 # /home/exarkun/Projects/cpython/2.7/python() [0x4d5f52]
65 # /home/exarkun/Projects/cpython/2.7/python(PyEval_EvalFrameEx+0x753b) [0x4d0e1e]
66 # /home/exarkun/Projects/cpython/2.7/python() [0x4d6419]
67 # /home/exarkun/Projects/cpython/2.7/python() [0x4d6129]
68 # /home/exarkun/Projects/cpython/2.7/python(PyEval_EvalFrameEx+0x753b) [0x4d0e1e]
69 # /home/exarkun/Projects/cpython/2.7/python(PyEval_EvalCodeEx+0x1043) [0x4d3726]
70 # /home/exarkun/Projects/cpython/2.7/python() [0x55fd51]
71 # /home/exarkun/Projects/cpython/2.7/python(PyObject_Call+0x7e) [0x420ee6]
72 # /home/exarkun/Projects/cpython/2.7/python(PyEval_CallObjectWithKeywords+0x158) [0x4d56ec]
73 # /home/exarkun/.local/lib/python2.7/site-packages/cffi-0.5-py2.7-linux-x86_64.egg/_cffi_backend.so(+0xe96e) [0x7fe2e38be96e]
74 # /usr/lib/x86_64-linux-gnu/libffi.so.6(ffi_closure_unix64_inner+0x1b9) [0x7fe2e36ad819]
75 # /usr/lib/x86_64-linux-gnu/libffi.so.6(ffi_closure_unix64+0x46) [0x7fe2e36adb7c]
76 # /lib/x86_64-linux-gnu/libcrypto.so.1.0.0(CRYPTO_malloc+0x64) [0x7fe2e1cef784] <------ end interesting
77 # /lib/x86_64-linux-gnu/libcrypto.so.1.0.0(lh_insert+0x16b) [0x7fe2e1d6a24b] .
78 # /lib/x86_64-linux-gnu/libcrypto.so.1.0.0(+0x61c18) [0x7fe2e1cf0c18] .
79 # /lib/x86_64-linux-gnu/libcrypto.so.1.0.0(+0x625ec) [0x7fe2e1cf15ec] .
80 # /lib/x86_64-linux-gnu/libcrypto.so.1.0.0(DSA_new_method+0xe6) [0x7fe2e1d524d6] .
81 # /lib/x86_64-linux-gnu/libcrypto.so.1.0.0(DSA_generate_parameters+0x3a) [0x7fe2e1d5364a] <------ begin interesting
82 # /home/exarkun/Projects/opentls/trunk/tls/c/__pycache__/_cffi__x305d4698xb539baaa.so(+0x1f397) [0x7fe2df84d397]
83 # /home/exarkun/Projects/cpython/2.7/python(PyCFunction_Call+0x8b) [0x56265a]
84 # /home/exarkun/Projects/cpython/2.7/python() [0x4d5f52]
85 # /home/exarkun/Projects/cpython/2.7/python(PyEval_EvalFrameEx+0x753b) [0x4d0e1e]
86 # /home/exarkun/Projects/cpython/2.7/python() [0x4d6419]
87 # ...
88 #
89 # Notice the stack is upside down compared to a Python traceback.
90 # Identify the start and end of interesting bits and stuff it into the stack we report.
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -080091
Jean-Paul Calderone68a6f8f2013-03-01 17:56:22 -080092 # Figure the first interesting frame will be after a the cffi-compiled module
93 while '/__pycache__/_cffi__' not in c_stack[-1]:
94 c_stack.pop()
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -080095
Jean-Paul Calderone68a6f8f2013-03-01 17:56:22 -080096 # Figure the last interesting frame will always be CRYPTO_malloc,
97 # since that's where we hooked in to things.
98 while 'CRYPTO_malloc' not in c_stack[0]:
99 c_stack.pop(0)
100
101 c_stack.reverse()
102 stack.extend([frame + "\n" for frame in c_stack])
103
104 # XXX :(
105 ptr = int(str(p).split()[-1][:-1], 16)
106 stack.insert(0, "Leaked 0x%x at:\n" % (ptr,))
107 return "".join(stack)
108
Jean-Paul Calderone40732ff2013-03-01 20:53:50 -0800109 # Clean up some long-lived allocations so they won't be reported as
110 # memory leaks.
111 api.CRYPTO_cleanup_all_ex_data()
112
Jean-Paul Calderone68a6f8f2013-03-01 17:56:22 -0800113 after = set(memdbg.heap)
114 leak = after - self._before
115 if leak:
116 reasons = []
117 for p in leak:
118 reasons.append(format_leak(p))
119 del memdbg.heap[p]
120 self.fail('\n'.join(reasons))
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -0800121
Jean-Paul Calderonebf37f0f2010-07-31 14:56:20 -0400122 if False and self._temporaryFiles is not None:
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400123 for temp in self._temporaryFiles:
124 if os.path.isdir(temp):
125 shutil.rmtree(temp)
126 elif os.path.exists(temp):
127 os.unlink(temp)
Jean-Paul Calderone1206daf2009-07-16 16:07:42 -0400128 try:
129 _exception_from_error_queue()
Jean-Paul Calderone24b64592010-08-12 10:43:09 -0400130 except Error:
131 e = sys.exc_info()[1]
Jean-Paul Calderone1206daf2009-07-16 16:07:42 -0400132 if e.args != ([],):
133 self.fail("Left over errors in OpenSSL error queue: " + repr(e))
134
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400135
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800136
Jean-Paul Calderone060a57e2011-05-04 18:02:49 -0400137 def failUnlessIn(self, containee, container, msg=None):
138 """
Jonathan Ballet648875f2011-07-16 14:14:58 +0900139 Fail the test if :py:data:`containee` is not found in :py:data:`container`.
Jean-Paul Calderone060a57e2011-05-04 18:02:49 -0400140
Jonathan Ballet648875f2011-07-16 14:14:58 +0900141 :param containee: the value that should be in :py:class:`container`
Jonathan Ballet78b92a22011-07-16 08:07:26 +0900142 :param container: a sequence type, or in the case of a mapping type,
Jean-Paul Calderone060a57e2011-05-04 18:02:49 -0400143 will follow semantics of 'if key in dict.keys()'
Jonathan Ballet78b92a22011-07-16 08:07:26 +0900144 :param msg: if msg is None, then the failure message will be
Jean-Paul Calderone060a57e2011-05-04 18:02:49 -0400145 '%r not in %r' % (first, second)
146 """
147 if containee not in container:
148 raise self.failureException(msg or "%r not in %r"
149 % (containee, container))
150 return containee
151 assertIn = failUnlessIn
152
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400153 def failUnlessIdentical(self, first, second, msg=None):
154 """
Jonathan Ballet648875f2011-07-16 14:14:58 +0900155 Fail the test if :py:data:`first` is not :py:data:`second`. This is an
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400156 obect-identity-equality test, not an object equality
Jonathan Ballet648875f2011-07-16 14:14:58 +0900157 (i.e. :py:func:`__eq__`) test.
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400158
Jonathan Ballet78b92a22011-07-16 08:07:26 +0900159 :param msg: if msg is None, then the failure message will be
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400160 '%r is not %r' % (first, second)
161 """
162 if first is not second:
163 raise self.failureException(msg or '%r is not %r' % (first, second))
164 return first
165 assertIdentical = failUnlessIdentical
166
167
168 def failIfIdentical(self, first, second, msg=None):
169 """
Jonathan Ballet648875f2011-07-16 14:14:58 +0900170 Fail the test if :py:data:`first` is :py:data:`second`. This is an
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400171 obect-identity-equality test, not an object equality
Jonathan Ballet648875f2011-07-16 14:14:58 +0900172 (i.e. :py:func:`__eq__`) test.
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400173
Jonathan Ballet78b92a22011-07-16 08:07:26 +0900174 :param msg: if msg is None, then the failure message will be
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400175 '%r is %r' % (first, second)
176 """
177 if first is second:
178 raise self.failureException(msg or '%r is %r' % (first, second))
179 return first
180 assertNotIdentical = failIfIdentical
181
182
183 def failUnlessRaises(self, exception, f, *args, **kwargs):
184 """
Jonathan Ballet648875f2011-07-16 14:14:58 +0900185 Fail the test unless calling the function :py:data:`f` with the given
186 :py:data:`args` and :py:data:`kwargs` raises :py:data:`exception`. The
187 failure will report the traceback and call stack of the unexpected
188 exception.
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400189
Jonathan Ballet78b92a22011-07-16 08:07:26 +0900190 :param exception: exception type that is to be expected
191 :param f: the function to call
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400192
Jonathan Ballet78b92a22011-07-16 08:07:26 +0900193 :return: The raised exception instance, if it is of the given type.
194 :raise self.failureException: Raised if the function call does
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400195 not raise an exception or if it raises an exception of a
196 different type.
197 """
198 try:
199 result = f(*args, **kwargs)
Jean-Paul Calderone24b64592010-08-12 10:43:09 -0400200 except exception:
201 inst = sys.exc_info()[1]
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400202 return inst
203 except:
Rick Dean47262da2009-07-08 16:17:17 -0500204 raise self.failureException('%s raised instead of %s'
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400205 % (sys.exc_info()[0],
206 exception.__name__,
Rick Dean47262da2009-07-08 16:17:17 -0500207 ))
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400208 else:
209 raise self.failureException('%s not raised (%r returned)'
210 % (exception.__name__, result))
211 assertRaises = failUnlessRaises
212
213
214 _temporaryFiles = None
215 def mktemp(self):
216 """
217 Pathetic substitute for twisted.trial.unittest.TestCase.mktemp.
218 """
219 if self._temporaryFiles is None:
220 self._temporaryFiles = []
221 temp = mktemp(dir=".")
222 self._temporaryFiles.append(temp)
223 return temp
224
225
226 # Python 2.3 compatibility.
227 def assertTrue(self, *a, **kw):
228 return self.failUnless(*a, **kw)
229
230
231 def assertFalse(self, *a, **kw):
232 return self.failIf(*a, **kw)
Jean-Paul Calderone68649052009-07-17 21:14:27 -0400233
234
235 # Other stuff
236 def assertConsistentType(self, theType, name, *constructionArgs):
237 """
Jonathan Ballet648875f2011-07-16 14:14:58 +0900238 Perform various assertions about :py:data:`theType` to ensure that it is a
Jean-Paul Calderone68649052009-07-17 21:14:27 -0400239 well-defined type. This is useful for extension types, where it's
240 pretty easy to do something wacky. If something about the type is
241 unusual, an exception will be raised.
242
Jonathan Ballet78b92a22011-07-16 08:07:26 +0900243 :param theType: The type object about which to make assertions.
244 :param name: A string giving the name of the type.
Jonathan Ballet648875f2011-07-16 14:14:58 +0900245 :param constructionArgs: Positional arguments to use with :py:data:`theType` to
Jean-Paul Calderone68649052009-07-17 21:14:27 -0400246 create an instance of it.
247 """
248 self.assertEqual(theType.__name__, name)
249 self.assertTrue(isinstance(theType, type))
250 instance = theType(*constructionArgs)
251 self.assertIdentical(type(instance), theType)