blob: d58f922ce5c52aee1beebbe72f237b58147df8b1 [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 Calderone0ef63ed2009-07-05 13:05:45 -040041 def tearDown(self):
42 """
Jonathan Ballet648875f2011-07-16 14:14:58 +090043 Clean up any files or directories created using :py:meth:`TestCase.mktemp`.
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -040044 Subclasses must invoke this method if they override it or the
45 cleanup will not occur.
46 """
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -080047 import gc
48 gc.collect(); gc.collect(); gc.collect()
49
Jean-Paul Calderone68a6f8f2013-03-01 17:56:22 -080050 def format_leak(p):
51 stacks = memdbg.heap[p]
52 # Eventually look at multiple stacks for the realloc() case. For
53 # now just look at the original allocation location.
54 (python_stack, c_stack) = stacks[0]
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -080055
Jean-Paul Calderone68a6f8f2013-03-01 17:56:22 -080056 stack = traceback.format_list(python_stack)[:-1]
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -080057
Jean-Paul Calderone68a6f8f2013-03-01 17:56:22 -080058 # c_stack looks something like this (interesting parts indicated
59 # with inserted arrows not part of the data):
60 #
61 # /home/exarkun/Projects/pyOpenSSL/branches/use-opentls/__pycache__/_cffi__x89095113xb9185b9b.so(+0x12cf) [0x7fe2e20582cf]
62 # /home/exarkun/Projects/cpython/2.7/python(PyCFunction_Call+0x8b) [0x56265a]
63 # /home/exarkun/Projects/cpython/2.7/python() [0x4d5f52]
64 # /home/exarkun/Projects/cpython/2.7/python(PyEval_EvalFrameEx+0x753b) [0x4d0e1e]
65 # /home/exarkun/Projects/cpython/2.7/python() [0x4d6419]
66 # /home/exarkun/Projects/cpython/2.7/python() [0x4d6129]
67 # /home/exarkun/Projects/cpython/2.7/python(PyEval_EvalFrameEx+0x753b) [0x4d0e1e]
68 # /home/exarkun/Projects/cpython/2.7/python(PyEval_EvalCodeEx+0x1043) [0x4d3726]
69 # /home/exarkun/Projects/cpython/2.7/python() [0x55fd51]
70 # /home/exarkun/Projects/cpython/2.7/python(PyObject_Call+0x7e) [0x420ee6]
71 # /home/exarkun/Projects/cpython/2.7/python(PyEval_CallObjectWithKeywords+0x158) [0x4d56ec]
72 # /home/exarkun/.local/lib/python2.7/site-packages/cffi-0.5-py2.7-linux-x86_64.egg/_cffi_backend.so(+0xe96e) [0x7fe2e38be96e]
73 # /usr/lib/x86_64-linux-gnu/libffi.so.6(ffi_closure_unix64_inner+0x1b9) [0x7fe2e36ad819]
74 # /usr/lib/x86_64-linux-gnu/libffi.so.6(ffi_closure_unix64+0x46) [0x7fe2e36adb7c]
75 # /lib/x86_64-linux-gnu/libcrypto.so.1.0.0(CRYPTO_malloc+0x64) [0x7fe2e1cef784] <------ end interesting
76 # /lib/x86_64-linux-gnu/libcrypto.so.1.0.0(lh_insert+0x16b) [0x7fe2e1d6a24b] .
77 # /lib/x86_64-linux-gnu/libcrypto.so.1.0.0(+0x61c18) [0x7fe2e1cf0c18] .
78 # /lib/x86_64-linux-gnu/libcrypto.so.1.0.0(+0x625ec) [0x7fe2e1cf15ec] .
79 # /lib/x86_64-linux-gnu/libcrypto.so.1.0.0(DSA_new_method+0xe6) [0x7fe2e1d524d6] .
80 # /lib/x86_64-linux-gnu/libcrypto.so.1.0.0(DSA_generate_parameters+0x3a) [0x7fe2e1d5364a] <------ begin interesting
81 # /home/exarkun/Projects/opentls/trunk/tls/c/__pycache__/_cffi__x305d4698xb539baaa.so(+0x1f397) [0x7fe2df84d397]
82 # /home/exarkun/Projects/cpython/2.7/python(PyCFunction_Call+0x8b) [0x56265a]
83 # /home/exarkun/Projects/cpython/2.7/python() [0x4d5f52]
84 # /home/exarkun/Projects/cpython/2.7/python(PyEval_EvalFrameEx+0x753b) [0x4d0e1e]
85 # /home/exarkun/Projects/cpython/2.7/python() [0x4d6419]
86 # ...
87 #
88 # Notice the stack is upside down compared to a Python traceback.
89 # Identify the start and end of interesting bits and stuff it into the stack we report.
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -080090
Jean-Paul Calderone68a6f8f2013-03-01 17:56:22 -080091 # Figure the first interesting frame will be after a the cffi-compiled module
92 while '/__pycache__/_cffi__' not in c_stack[-1]:
93 c_stack.pop()
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -080094
Jean-Paul Calderone68a6f8f2013-03-01 17:56:22 -080095 # Figure the last interesting frame will always be CRYPTO_malloc,
96 # since that's where we hooked in to things.
97 while 'CRYPTO_malloc' not in c_stack[0]:
98 c_stack.pop(0)
99
100 c_stack.reverse()
101 stack.extend([frame + "\n" for frame in c_stack])
102
103 # XXX :(
104 ptr = int(str(p).split()[-1][:-1], 16)
105 stack.insert(0, "Leaked 0x%x at:\n" % (ptr,))
106 return "".join(stack)
107
108 after = set(memdbg.heap)
109 leak = after - self._before
110 if leak:
111 reasons = []
112 for p in leak:
113 reasons.append(format_leak(p))
114 del memdbg.heap[p]
115 self.fail('\n'.join(reasons))
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -0800116
Jean-Paul Calderonebf37f0f2010-07-31 14:56:20 -0400117 if False and self._temporaryFiles is not None:
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400118 for temp in self._temporaryFiles:
119 if os.path.isdir(temp):
120 shutil.rmtree(temp)
121 elif os.path.exists(temp):
122 os.unlink(temp)
Jean-Paul Calderone1206daf2009-07-16 16:07:42 -0400123 try:
124 _exception_from_error_queue()
Jean-Paul Calderone24b64592010-08-12 10:43:09 -0400125 except Error:
126 e = sys.exc_info()[1]
Jean-Paul Calderone1206daf2009-07-16 16:07:42 -0400127 if e.args != ([],):
128 self.fail("Left over errors in OpenSSL error queue: " + repr(e))
129
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400130
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800131
Jean-Paul Calderone060a57e2011-05-04 18:02:49 -0400132 def failUnlessIn(self, containee, container, msg=None):
133 """
Jonathan Ballet648875f2011-07-16 14:14:58 +0900134 Fail the test if :py:data:`containee` is not found in :py:data:`container`.
Jean-Paul Calderone060a57e2011-05-04 18:02:49 -0400135
Jonathan Ballet648875f2011-07-16 14:14:58 +0900136 :param containee: the value that should be in :py:class:`container`
Jonathan Ballet78b92a22011-07-16 08:07:26 +0900137 :param container: a sequence type, or in the case of a mapping type,
Jean-Paul Calderone060a57e2011-05-04 18:02:49 -0400138 will follow semantics of 'if key in dict.keys()'
Jonathan Ballet78b92a22011-07-16 08:07:26 +0900139 :param msg: if msg is None, then the failure message will be
Jean-Paul Calderone060a57e2011-05-04 18:02:49 -0400140 '%r not in %r' % (first, second)
141 """
142 if containee not in container:
143 raise self.failureException(msg or "%r not in %r"
144 % (containee, container))
145 return containee
146 assertIn = failUnlessIn
147
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400148 def failUnlessIdentical(self, first, second, msg=None):
149 """
Jonathan Ballet648875f2011-07-16 14:14:58 +0900150 Fail the test if :py:data:`first` is not :py:data:`second`. This is an
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400151 obect-identity-equality test, not an object equality
Jonathan Ballet648875f2011-07-16 14:14:58 +0900152 (i.e. :py:func:`__eq__`) test.
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400153
Jonathan Ballet78b92a22011-07-16 08:07:26 +0900154 :param msg: if msg is None, then the failure message will be
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400155 '%r is not %r' % (first, second)
156 """
157 if first is not second:
158 raise self.failureException(msg or '%r is not %r' % (first, second))
159 return first
160 assertIdentical = failUnlessIdentical
161
162
163 def failIfIdentical(self, first, second, msg=None):
164 """
Jonathan Ballet648875f2011-07-16 14:14:58 +0900165 Fail the test if :py:data:`first` is :py:data:`second`. This is an
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400166 obect-identity-equality test, not an object equality
Jonathan Ballet648875f2011-07-16 14:14:58 +0900167 (i.e. :py:func:`__eq__`) test.
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400168
Jonathan Ballet78b92a22011-07-16 08:07:26 +0900169 :param msg: if msg is None, then the failure message will be
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400170 '%r is %r' % (first, second)
171 """
172 if first is second:
173 raise self.failureException(msg or '%r is %r' % (first, second))
174 return first
175 assertNotIdentical = failIfIdentical
176
177
178 def failUnlessRaises(self, exception, f, *args, **kwargs):
179 """
Jonathan Ballet648875f2011-07-16 14:14:58 +0900180 Fail the test unless calling the function :py:data:`f` with the given
181 :py:data:`args` and :py:data:`kwargs` raises :py:data:`exception`. The
182 failure will report the traceback and call stack of the unexpected
183 exception.
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400184
Jonathan Ballet78b92a22011-07-16 08:07:26 +0900185 :param exception: exception type that is to be expected
186 :param f: the function to call
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400187
Jonathan Ballet78b92a22011-07-16 08:07:26 +0900188 :return: The raised exception instance, if it is of the given type.
189 :raise self.failureException: Raised if the function call does
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400190 not raise an exception or if it raises an exception of a
191 different type.
192 """
193 try:
194 result = f(*args, **kwargs)
Jean-Paul Calderone24b64592010-08-12 10:43:09 -0400195 except exception:
196 inst = sys.exc_info()[1]
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400197 return inst
198 except:
Rick Dean47262da2009-07-08 16:17:17 -0500199 raise self.failureException('%s raised instead of %s'
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400200 % (sys.exc_info()[0],
201 exception.__name__,
Rick Dean47262da2009-07-08 16:17:17 -0500202 ))
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400203 else:
204 raise self.failureException('%s not raised (%r returned)'
205 % (exception.__name__, result))
206 assertRaises = failUnlessRaises
207
208
209 _temporaryFiles = None
210 def mktemp(self):
211 """
212 Pathetic substitute for twisted.trial.unittest.TestCase.mktemp.
213 """
214 if self._temporaryFiles is None:
215 self._temporaryFiles = []
216 temp = mktemp(dir=".")
217 self._temporaryFiles.append(temp)
218 return temp
219
220
221 # Python 2.3 compatibility.
222 def assertTrue(self, *a, **kw):
223 return self.failUnless(*a, **kw)
224
225
226 def assertFalse(self, *a, **kw):
227 return self.failIf(*a, **kw)
Jean-Paul Calderone68649052009-07-17 21:14:27 -0400228
229
230 # Other stuff
231 def assertConsistentType(self, theType, name, *constructionArgs):
232 """
Jonathan Ballet648875f2011-07-16 14:14:58 +0900233 Perform various assertions about :py:data:`theType` to ensure that it is a
Jean-Paul Calderone68649052009-07-17 21:14:27 -0400234 well-defined type. This is useful for extension types, where it's
235 pretty easy to do something wacky. If something about the type is
236 unusual, an exception will be raised.
237
Jonathan Ballet78b92a22011-07-16 08:07:26 +0900238 :param theType: The type object about which to make assertions.
239 :param name: A string giving the name of the type.
Jonathan Ballet648875f2011-07-16 14:14:58 +0900240 :param constructionArgs: Positional arguments to use with :py:data:`theType` to
Jean-Paul Calderone68649052009-07-17 21:14:27 -0400241 create an instance of it.
242 """
243 self.assertEqual(theType.__name__, name)
244 self.assertTrue(isinstance(theType, type))
245 instance = theType(*constructionArgs)
246 self.assertIdentical(type(instance), theType)