blob: b69e538b1dc317e48d2466694f1c4163a6cebdb4 [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 Calderone6462b072015-03-29 07:03:11 -040017from six import PY3
18
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -050019from OpenSSL._util import exception_from_error_queue
20from OpenSSL.crypto import Error
Jean-Paul Calderone88f38b22009-07-16 16:25:19 -040021
Konstantinos Koukopoulos410d0422014-01-30 20:15:25 +020022try:
23 import memdbg
24except Exception:
25 class _memdbg(object): heap = None
26 memdbg = _memdbg()
Jean-Paul Calderone68a6f8f2013-03-01 17:56:22 -080027
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -050028from OpenSSL._util import ffi, lib, byte_string as b
Jean-Paul Calderone9e4eeae2010-08-22 21:32:52 -040029
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -040030class TestCase(TestCase):
31 """
Jonathan Ballet648875f2011-07-16 14:14:58 +090032 :py:class:`TestCase` adds useful testing functionality beyond what is available
33 from the standard library :py:class:`unittest.TestCase`.
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -040034 """
Jean-Paul Calderone855331d2013-03-03 10:21:43 -080035 def run(self, result):
Jean-Paul Calderone68703ed2013-03-04 12:23:44 -080036 run = super(TestCase, self).run
37 if memdbg.heap is None:
38 return run(result)
39
Jean-Paul Calderone855331d2013-03-03 10:21:43 -080040 # Run the test as usual
41 before = set(memdbg.heap)
Jean-Paul Calderone68703ed2013-03-04 12:23:44 -080042 run(result)
Jean-Paul Calderone855331d2013-03-03 10:21:43 -080043
44 # Clean up some long-lived allocations so they won't be reported as
45 # memory leaks.
Jean-Paul Calderone9227c472013-12-31 13:47:36 -050046 lib.CRYPTO_cleanup_all_ex_data()
47 lib.ERR_remove_thread_state(ffi.NULL)
Jean-Paul Calderone855331d2013-03-03 10:21:43 -080048 after = set(memdbg.heap)
49
50 if not after - before:
51 # No leaks, fast succeed
52 return
53
54 if result.wasSuccessful():
55 # If it passed, run it again with memory debugging
56 before = set(memdbg.heap)
Jean-Paul Calderone68703ed2013-03-04 12:23:44 -080057 run(result)
Jean-Paul Calderone855331d2013-03-03 10:21:43 -080058
59 # Clean up some long-lived allocations so they won't be reported as
60 # memory leaks.
Jean-Paul Calderone3f93d212014-01-01 12:36:53 -050061 lib.CRYPTO_cleanup_all_ex_data()
62 lib.ERR_remove_thread_state(ffi.NULL)
Jean-Paul Calderone855331d2013-03-03 10:21:43 -080063
64 after = set(memdbg.heap)
65
66 self._reportLeaks(after - before, result)
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -080067
Jean-Paul Calderone40732ff2013-03-01 20:53:50 -080068
Jean-Paul Calderone855331d2013-03-03 10:21:43 -080069 def _reportLeaks(self, leaks, result):
Jean-Paul Calderone68a6f8f2013-03-01 17:56:22 -080070 def format_leak(p):
71 stacks = memdbg.heap[p]
72 # Eventually look at multiple stacks for the realloc() case. For
73 # now just look at the original allocation location.
Jean-Paul Calderonec2e8b412013-03-02 16:27:55 -080074 (size, python_stack, c_stack) = stacks[0]
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -080075
Jean-Paul Calderone68a6f8f2013-03-01 17:56:22 -080076 stack = traceback.format_list(python_stack)[:-1]
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -080077
Jean-Paul Calderone68a6f8f2013-03-01 17:56:22 -080078 # c_stack looks something like this (interesting parts indicated
79 # with inserted arrows not part of the data):
80 #
81 # /home/exarkun/Projects/pyOpenSSL/branches/use-opentls/__pycache__/_cffi__x89095113xb9185b9b.so(+0x12cf) [0x7fe2e20582cf]
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 # /home/exarkun/Projects/cpython/2.7/python() [0x4d6129]
87 # /home/exarkun/Projects/cpython/2.7/python(PyEval_EvalFrameEx+0x753b) [0x4d0e1e]
88 # /home/exarkun/Projects/cpython/2.7/python(PyEval_EvalCodeEx+0x1043) [0x4d3726]
89 # /home/exarkun/Projects/cpython/2.7/python() [0x55fd51]
90 # /home/exarkun/Projects/cpython/2.7/python(PyObject_Call+0x7e) [0x420ee6]
91 # /home/exarkun/Projects/cpython/2.7/python(PyEval_CallObjectWithKeywords+0x158) [0x4d56ec]
92 # /home/exarkun/.local/lib/python2.7/site-packages/cffi-0.5-py2.7-linux-x86_64.egg/_cffi_backend.so(+0xe96e) [0x7fe2e38be96e]
93 # /usr/lib/x86_64-linux-gnu/libffi.so.6(ffi_closure_unix64_inner+0x1b9) [0x7fe2e36ad819]
94 # /usr/lib/x86_64-linux-gnu/libffi.so.6(ffi_closure_unix64+0x46) [0x7fe2e36adb7c]
95 # /lib/x86_64-linux-gnu/libcrypto.so.1.0.0(CRYPTO_malloc+0x64) [0x7fe2e1cef784] <------ end interesting
96 # /lib/x86_64-linux-gnu/libcrypto.so.1.0.0(lh_insert+0x16b) [0x7fe2e1d6a24b] .
97 # /lib/x86_64-linux-gnu/libcrypto.so.1.0.0(+0x61c18) [0x7fe2e1cf0c18] .
98 # /lib/x86_64-linux-gnu/libcrypto.so.1.0.0(+0x625ec) [0x7fe2e1cf15ec] .
99 # /lib/x86_64-linux-gnu/libcrypto.so.1.0.0(DSA_new_method+0xe6) [0x7fe2e1d524d6] .
100 # /lib/x86_64-linux-gnu/libcrypto.so.1.0.0(DSA_generate_parameters+0x3a) [0x7fe2e1d5364a] <------ begin interesting
101 # /home/exarkun/Projects/opentls/trunk/tls/c/__pycache__/_cffi__x305d4698xb539baaa.so(+0x1f397) [0x7fe2df84d397]
102 # /home/exarkun/Projects/cpython/2.7/python(PyCFunction_Call+0x8b) [0x56265a]
103 # /home/exarkun/Projects/cpython/2.7/python() [0x4d5f52]
104 # /home/exarkun/Projects/cpython/2.7/python(PyEval_EvalFrameEx+0x753b) [0x4d0e1e]
105 # /home/exarkun/Projects/cpython/2.7/python() [0x4d6419]
106 # ...
107 #
108 # Notice the stack is upside down compared to a Python traceback.
109 # Identify the start and end of interesting bits and stuff it into the stack we report.
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -0800110
Jean-Paul Calderonec2e8b412013-03-02 16:27:55 -0800111 saved = list(c_stack)
112
Jean-Paul Calderone68a6f8f2013-03-01 17:56:22 -0800113 # Figure the first interesting frame will be after a the cffi-compiled module
Jean-Paul Calderonec2e8b412013-03-02 16:27:55 -0800114 while c_stack and '/__pycache__/_cffi__' not in c_stack[-1]:
Jean-Paul Calderone68a6f8f2013-03-01 17:56:22 -0800115 c_stack.pop()
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -0800116
Jean-Paul Calderone68a6f8f2013-03-01 17:56:22 -0800117 # Figure the last interesting frame will always be CRYPTO_malloc,
118 # since that's where we hooked in to things.
Jean-Paul Calderonec2e8b412013-03-02 16:27:55 -0800119 while c_stack and 'CRYPTO_malloc' not in c_stack[0] and 'CRYPTO_realloc' not in c_stack[0]:
Jean-Paul Calderone68a6f8f2013-03-01 17:56:22 -0800120 c_stack.pop(0)
121
Jean-Paul Calderonec2e8b412013-03-02 16:27:55 -0800122 if c_stack:
123 c_stack.reverse()
124 else:
125 c_stack = saved[::-1]
Jean-Paul Calderone68a6f8f2013-03-01 17:56:22 -0800126 stack.extend([frame + "\n" for frame in c_stack])
127
Jean-Paul Calderone2beac532013-03-03 17:30:36 -0800128 stack.insert(0, "Leaked (%s) at:\n")
Jean-Paul Calderone68a6f8f2013-03-01 17:56:22 -0800129 return "".join(stack)
130
Jean-Paul Calderone855331d2013-03-03 10:21:43 -0800131 if leaks:
Jean-Paul Calderone2beac532013-03-03 17:30:36 -0800132 unique_leaks = {}
Jean-Paul Calderone855331d2013-03-03 10:21:43 -0800133 for p in leaks:
Jean-Paul Calderone2beac532013-03-03 17:30:36 -0800134 size = memdbg.heap[p][-1][0]
135 new_leak = format_leak(p)
136 if new_leak not in unique_leaks:
137 unique_leaks[new_leak] = [(size, p)]
138 else:
139 unique_leaks[new_leak].append((size, p))
140 memdbg.free(p)
141
142 for (stack, allocs) in unique_leaks.iteritems():
143 allocs_accum = []
144 for (size, pointer) in allocs:
145
Jean-Paul Calderone9227c472013-12-31 13:47:36 -0500146 addr = int(ffi.cast('uintptr_t', pointer))
Jean-Paul Calderone2beac532013-03-03 17:30:36 -0800147 allocs_accum.append("%d@0x%x" % (size, addr))
148 allocs_report = ", ".join(sorted(allocs_accum))
149
Jean-Paul Calderone855331d2013-03-03 10:21:43 -0800150 result.addError(
151 self,
Jean-Paul Calderone2beac532013-03-03 17:30:36 -0800152 (None, Exception(stack % (allocs_report,)), None))
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -0800153
Jean-Paul Calderone855331d2013-03-03 10:21:43 -0800154
155 def tearDown(self):
156 """
157 Clean up any files or directories created using :py:meth:`TestCase.mktemp`.
158 Subclasses must invoke this method if they override it or the
159 cleanup will not occur.
160 """
Jean-Paul Calderonebf37f0f2010-07-31 14:56:20 -0400161 if False and self._temporaryFiles is not None:
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400162 for temp in self._temporaryFiles:
163 if os.path.isdir(temp):
164 shutil.rmtree(temp)
165 elif os.path.exists(temp):
166 os.unlink(temp)
Jean-Paul Calderone1206daf2009-07-16 16:07:42 -0400167 try:
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -0500168 exception_from_error_queue(Error)
Jean-Paul Calderone24b64592010-08-12 10:43:09 -0400169 except Error:
170 e = sys.exc_info()[1]
Jean-Paul Calderone1206daf2009-07-16 16:07:42 -0400171 if e.args != ([],):
172 self.fail("Left over errors in OpenSSL error queue: " + repr(e))
173
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400174
Jean-Paul Calderone8fb53182013-12-30 08:35:49 -0500175 def assertIsInstance(self, instance, classOrTuple, message=None):
176 """
177 Fail if C{instance} is not an instance of the given class or of
178 one of the given classes.
179
180 @param instance: the object to test the type (first argument of the
181 C{isinstance} call).
182 @type instance: any.
183 @param classOrTuple: the class or classes to test against (second
184 argument of the C{isinstance} call).
185 @type classOrTuple: class, type, or tuple.
186
187 @param message: Custom text to include in the exception text if the
188 assertion fails.
189 """
190 if not isinstance(instance, classOrTuple):
191 if message is None:
192 suffix = ""
193 else:
194 suffix = ": " + message
195 self.fail("%r is not an instance of %s%s" % (
196 instance, classOrTuple, suffix))
197
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800198
Jean-Paul Calderone060a57e2011-05-04 18:02:49 -0400199 def failUnlessIn(self, containee, container, msg=None):
200 """
Jonathan Ballet648875f2011-07-16 14:14:58 +0900201 Fail the test if :py:data:`containee` is not found in :py:data:`container`.
Jean-Paul Calderone060a57e2011-05-04 18:02:49 -0400202
Jonathan Ballet648875f2011-07-16 14:14:58 +0900203 :param containee: the value that should be in :py:class:`container`
Jonathan Ballet78b92a22011-07-16 08:07:26 +0900204 :param container: a sequence type, or in the case of a mapping type,
Jean-Paul Calderone060a57e2011-05-04 18:02:49 -0400205 will follow semantics of 'if key in dict.keys()'
Jonathan Ballet78b92a22011-07-16 08:07:26 +0900206 :param msg: if msg is None, then the failure message will be
Jean-Paul Calderone060a57e2011-05-04 18:02:49 -0400207 '%r not in %r' % (first, second)
208 """
209 if containee not in container:
210 raise self.failureException(msg or "%r not in %r"
211 % (containee, container))
212 return containee
213 assertIn = failUnlessIn
214
Jean-Paul Calderone15f36442014-05-01 07:58:02 -0400215 def assertNotIn(self, containee, container, msg=None):
216 """
217 Fail the test if C{containee} is found in C{container}.
218
219 @param containee: the value that should not be in C{container}
220 @param container: a sequence type, or in the case of a mapping type,
221 will follow semantics of 'if key in dict.keys()'
222 @param msg: if msg is None, then the failure message will be
223 '%r in %r' % (first, second)
224 """
225 if containee in container:
226 raise self.failureException(msg or "%r in %r"
227 % (containee, container))
228 return containee
229 failIfIn = assertNotIn
230
231
Jean-Paul Calderone77b3d082014-12-12 20:04:35 -0500232 def assertIs(self, first, second, msg=None):
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400233 """
Jonathan Ballet648875f2011-07-16 14:14:58 +0900234 Fail the test if :py:data:`first` is not :py:data:`second`. This is an
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400235 obect-identity-equality test, not an object equality
Jonathan Ballet648875f2011-07-16 14:14:58 +0900236 (i.e. :py:func:`__eq__`) test.
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400237
Jonathan Ballet78b92a22011-07-16 08:07:26 +0900238 :param msg: if msg is None, then the failure message will be
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400239 '%r is not %r' % (first, second)
240 """
241 if first is not second:
242 raise self.failureException(msg or '%r is not %r' % (first, second))
243 return first
Jean-Paul Calderone77b3d082014-12-12 20:04:35 -0500244 assertIdentical = failUnlessIdentical = assertIs
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400245
246
Jean-Paul Calderone77b3d082014-12-12 20:04:35 -0500247 def assertIsNot(self, first, second, msg=None):
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400248 """
Jonathan Ballet648875f2011-07-16 14:14:58 +0900249 Fail the test if :py:data:`first` is :py:data:`second`. This is an
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400250 obect-identity-equality test, not an object equality
Jonathan Ballet648875f2011-07-16 14:14:58 +0900251 (i.e. :py:func:`__eq__`) test.
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400252
Jonathan Ballet78b92a22011-07-16 08:07:26 +0900253 :param msg: if msg is None, then the failure message will be
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400254 '%r is %r' % (first, second)
255 """
256 if first is second:
257 raise self.failureException(msg or '%r is %r' % (first, second))
258 return first
Jean-Paul Calderone77b3d082014-12-12 20:04:35 -0500259 assertNotIdentical = failIfIdentical = assertIsNot
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400260
261
262 def failUnlessRaises(self, exception, f, *args, **kwargs):
263 """
Jonathan Ballet648875f2011-07-16 14:14:58 +0900264 Fail the test unless calling the function :py:data:`f` with the given
265 :py:data:`args` and :py:data:`kwargs` raises :py:data:`exception`. The
266 failure will report the traceback and call stack of the unexpected
267 exception.
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400268
Jonathan Ballet78b92a22011-07-16 08:07:26 +0900269 :param exception: exception type that is to be expected
270 :param f: the function to call
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400271
Jonathan Ballet78b92a22011-07-16 08:07:26 +0900272 :return: The raised exception instance, if it is of the given type.
273 :raise self.failureException: Raised if the function call does
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400274 not raise an exception or if it raises an exception of a
275 different type.
276 """
277 try:
278 result = f(*args, **kwargs)
Jean-Paul Calderone24b64592010-08-12 10:43:09 -0400279 except exception:
280 inst = sys.exc_info()[1]
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400281 return inst
282 except:
Rick Dean47262da2009-07-08 16:17:17 -0500283 raise self.failureException('%s raised instead of %s'
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400284 % (sys.exc_info()[0],
285 exception.__name__,
Rick Dean47262da2009-07-08 16:17:17 -0500286 ))
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400287 else:
288 raise self.failureException('%s not raised (%r returned)'
289 % (exception.__name__, result))
290 assertRaises = failUnlessRaises
291
292
293 _temporaryFiles = None
294 def mktemp(self):
295 """
296 Pathetic substitute for twisted.trial.unittest.TestCase.mktemp.
297 """
298 if self._temporaryFiles is None:
299 self._temporaryFiles = []
Jean-Paul Calderonef9fb8922014-01-11 08:35:54 -0500300 temp = b(mktemp(dir="."))
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400301 self._temporaryFiles.append(temp)
302 return temp
303
304
Jean-Paul Calderone68649052009-07-17 21:14:27 -0400305 # Other stuff
306 def assertConsistentType(self, theType, name, *constructionArgs):
307 """
Jonathan Ballet648875f2011-07-16 14:14:58 +0900308 Perform various assertions about :py:data:`theType` to ensure that it is a
Jean-Paul Calderone68649052009-07-17 21:14:27 -0400309 well-defined type. This is useful for extension types, where it's
310 pretty easy to do something wacky. If something about the type is
311 unusual, an exception will be raised.
312
Jonathan Ballet78b92a22011-07-16 08:07:26 +0900313 :param theType: The type object about which to make assertions.
314 :param name: A string giving the name of the type.
Jonathan Ballet648875f2011-07-16 14:14:58 +0900315 :param constructionArgs: Positional arguments to use with :py:data:`theType` to
Jean-Paul Calderone68649052009-07-17 21:14:27 -0400316 create an instance of it.
317 """
318 self.assertEqual(theType.__name__, name)
319 self.assertTrue(isinstance(theType, type))
320 instance = theType(*constructionArgs)
321 self.assertIdentical(type(instance), theType)
Jean-Paul Calderone9c7f0692014-04-30 18:17:19 -0400322
323
324
325class EqualityTestsMixin(object):
326 """
327 A mixin defining tests for the standard implementation of C{==} and C{!=}.
328 """
329 def anInstance(self):
330 """
331 Return an instance of the class under test. Each call to this method
332 must return a different object. All objects returned must be equal to
333 each other.
334 """
335 raise NotImplementedError()
336
337
338 def anotherInstance(self):
339 """
340 Return an instance of the class under test. Each call to this method
341 must return a different object. The objects must not be equal to the
342 objects returned by C{anInstance}. They may or may not be equal to
343 each other (they will not be compared against each other).
344 """
345 raise NotImplementedError()
346
347
348 def test_identicalEq(self):
349 """
350 An object compares equal to itself using the C{==} operator.
351 """
352 o = self.anInstance()
353 self.assertTrue(o == o)
354
355
356 def test_identicalNe(self):
357 """
358 An object doesn't compare not equal to itself using the C{!=} operator.
359 """
360 o = self.anInstance()
361 self.assertFalse(o != o)
362
363
364 def test_sameEq(self):
365 """
366 Two objects that are equal to each other compare equal to each other
367 using the C{==} operator.
368 """
369 a = self.anInstance()
370 b = self.anInstance()
371 self.assertTrue(a == b)
372
373
374 def test_sameNe(self):
375 """
376 Two objects that are equal to each other do not compare not equal to
377 each other using the C{!=} operator.
378 """
379 a = self.anInstance()
380 b = self.anInstance()
381 self.assertFalse(a != b)
382
383
384 def test_differentEq(self):
385 """
386 Two objects that are not equal to each other do not compare equal to
387 each other using the C{==} operator.
388 """
389 a = self.anInstance()
390 b = self.anotherInstance()
391 self.assertFalse(a == b)
392
393
394 def test_differentNe(self):
395 """
396 Two objects that are not equal to each other compare not equal to each
397 other using the C{!=} operator.
398 """
399 a = self.anInstance()
400 b = self.anotherInstance()
401 self.assertTrue(a != b)
402
403
404 def test_anotherTypeEq(self):
405 """
406 The object does not compare equal to an object of an unrelated type
407 (which does not implement the comparison) using the C{==} operator.
408 """
409 a = self.anInstance()
410 b = object()
411 self.assertFalse(a == b)
412
413
414 def test_anotherTypeNe(self):
415 """
416 The object compares not equal to an object of an unrelated type (which
417 does not implement the comparison) using the C{!=} operator.
418 """
419 a = self.anInstance()
420 b = object()
421 self.assertTrue(a != b)
422
423
424 def test_delegatedEq(self):
425 """
426 The result of comparison using C{==} is delegated to the right-hand
427 operand if it is of an unrelated type.
428 """
429 class Delegate(object):
430 def __eq__(self, other):
431 # Do something crazy and obvious.
432 return [self]
433
434 a = self.anInstance()
435 b = Delegate()
436 self.assertEqual(a == b, [b])
437
438
439 def test_delegateNe(self):
440 """
441 The result of comparison using C{!=} is delegated to the right-hand
442 operand if it is of an unrelated type.
443 """
444 class Delegate(object):
445 def __ne__(self, other):
446 # Do something crazy and obvious.
447 return [self]
448
449 a = self.anInstance()
450 b = Delegate()
451 self.assertEqual(a != b, [b])
Jean-Paul Calderone6462b072015-03-29 07:03:11 -0400452
453
454# The type name expected in warnings about using the wrong string type.
455if PY3:
456 WARNING_TYPE_EXPECTED = "str"
457else:
458 WARNING_TYPE_EXPECTED = "unicode"