blob: c26a57a8d49c3923dd45b7b6d8ec20ee3760c8f7 [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 Calderone210c0f32015-04-12 09:20:31 -040030
31# This is the UTF-8 encoding of the SNOWMAN unicode code point.
32NON_ASCII = b("\xe2\x98\x83").decode("utf-8")
33
34
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -040035class TestCase(TestCase):
36 """
Jonathan Ballet648875f2011-07-16 14:14:58 +090037 :py:class:`TestCase` adds useful testing functionality beyond what is available
38 from the standard library :py:class:`unittest.TestCase`.
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -040039 """
Jean-Paul Calderone855331d2013-03-03 10:21:43 -080040 def run(self, result):
Jean-Paul Calderone68703ed2013-03-04 12:23:44 -080041 run = super(TestCase, self).run
42 if memdbg.heap is None:
43 return run(result)
44
Jean-Paul Calderone855331d2013-03-03 10:21:43 -080045 # Run the test as usual
46 before = set(memdbg.heap)
Jean-Paul Calderone68703ed2013-03-04 12:23:44 -080047 run(result)
Jean-Paul Calderone855331d2013-03-03 10:21:43 -080048
49 # Clean up some long-lived allocations so they won't be reported as
50 # memory leaks.
Jean-Paul Calderone9227c472013-12-31 13:47:36 -050051 lib.CRYPTO_cleanup_all_ex_data()
52 lib.ERR_remove_thread_state(ffi.NULL)
Jean-Paul Calderone855331d2013-03-03 10:21:43 -080053 after = set(memdbg.heap)
54
55 if not after - before:
56 # No leaks, fast succeed
57 return
58
59 if result.wasSuccessful():
60 # If it passed, run it again with memory debugging
61 before = set(memdbg.heap)
Jean-Paul Calderone68703ed2013-03-04 12:23:44 -080062 run(result)
Jean-Paul Calderone855331d2013-03-03 10:21:43 -080063
64 # Clean up some long-lived allocations so they won't be reported as
65 # memory leaks.
Jean-Paul Calderone3f93d212014-01-01 12:36:53 -050066 lib.CRYPTO_cleanup_all_ex_data()
67 lib.ERR_remove_thread_state(ffi.NULL)
Jean-Paul Calderone855331d2013-03-03 10:21:43 -080068
69 after = set(memdbg.heap)
70
71 self._reportLeaks(after - before, result)
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -080072
Jean-Paul Calderone40732ff2013-03-01 20:53:50 -080073
Jean-Paul Calderone855331d2013-03-03 10:21:43 -080074 def _reportLeaks(self, leaks, result):
Jean-Paul Calderone68a6f8f2013-03-01 17:56:22 -080075 def format_leak(p):
76 stacks = memdbg.heap[p]
77 # Eventually look at multiple stacks for the realloc() case. For
78 # now just look at the original allocation location.
Jean-Paul Calderonec2e8b412013-03-02 16:27:55 -080079 (size, python_stack, c_stack) = stacks[0]
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -080080
Jean-Paul Calderone68a6f8f2013-03-01 17:56:22 -080081 stack = traceback.format_list(python_stack)[:-1]
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -080082
Jean-Paul Calderone68a6f8f2013-03-01 17:56:22 -080083 # c_stack looks something like this (interesting parts indicated
84 # with inserted arrows not part of the data):
85 #
86 # /home/exarkun/Projects/pyOpenSSL/branches/use-opentls/__pycache__/_cffi__x89095113xb9185b9b.so(+0x12cf) [0x7fe2e20582cf]
87 # /home/exarkun/Projects/cpython/2.7/python(PyCFunction_Call+0x8b) [0x56265a]
88 # /home/exarkun/Projects/cpython/2.7/python() [0x4d5f52]
89 # /home/exarkun/Projects/cpython/2.7/python(PyEval_EvalFrameEx+0x753b) [0x4d0e1e]
90 # /home/exarkun/Projects/cpython/2.7/python() [0x4d6419]
91 # /home/exarkun/Projects/cpython/2.7/python() [0x4d6129]
92 # /home/exarkun/Projects/cpython/2.7/python(PyEval_EvalFrameEx+0x753b) [0x4d0e1e]
93 # /home/exarkun/Projects/cpython/2.7/python(PyEval_EvalCodeEx+0x1043) [0x4d3726]
94 # /home/exarkun/Projects/cpython/2.7/python() [0x55fd51]
95 # /home/exarkun/Projects/cpython/2.7/python(PyObject_Call+0x7e) [0x420ee6]
96 # /home/exarkun/Projects/cpython/2.7/python(PyEval_CallObjectWithKeywords+0x158) [0x4d56ec]
97 # /home/exarkun/.local/lib/python2.7/site-packages/cffi-0.5-py2.7-linux-x86_64.egg/_cffi_backend.so(+0xe96e) [0x7fe2e38be96e]
98 # /usr/lib/x86_64-linux-gnu/libffi.so.6(ffi_closure_unix64_inner+0x1b9) [0x7fe2e36ad819]
99 # /usr/lib/x86_64-linux-gnu/libffi.so.6(ffi_closure_unix64+0x46) [0x7fe2e36adb7c]
100 # /lib/x86_64-linux-gnu/libcrypto.so.1.0.0(CRYPTO_malloc+0x64) [0x7fe2e1cef784] <------ end interesting
101 # /lib/x86_64-linux-gnu/libcrypto.so.1.0.0(lh_insert+0x16b) [0x7fe2e1d6a24b] .
102 # /lib/x86_64-linux-gnu/libcrypto.so.1.0.0(+0x61c18) [0x7fe2e1cf0c18] .
103 # /lib/x86_64-linux-gnu/libcrypto.so.1.0.0(+0x625ec) [0x7fe2e1cf15ec] .
104 # /lib/x86_64-linux-gnu/libcrypto.so.1.0.0(DSA_new_method+0xe6) [0x7fe2e1d524d6] .
105 # /lib/x86_64-linux-gnu/libcrypto.so.1.0.0(DSA_generate_parameters+0x3a) [0x7fe2e1d5364a] <------ begin interesting
106 # /home/exarkun/Projects/opentls/trunk/tls/c/__pycache__/_cffi__x305d4698xb539baaa.so(+0x1f397) [0x7fe2df84d397]
107 # /home/exarkun/Projects/cpython/2.7/python(PyCFunction_Call+0x8b) [0x56265a]
108 # /home/exarkun/Projects/cpython/2.7/python() [0x4d5f52]
109 # /home/exarkun/Projects/cpython/2.7/python(PyEval_EvalFrameEx+0x753b) [0x4d0e1e]
110 # /home/exarkun/Projects/cpython/2.7/python() [0x4d6419]
111 # ...
112 #
113 # Notice the stack is upside down compared to a Python traceback.
114 # Identify the start and end of interesting bits and stuff it into the stack we report.
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -0800115
Jean-Paul Calderonec2e8b412013-03-02 16:27:55 -0800116 saved = list(c_stack)
117
Jean-Paul Calderone68a6f8f2013-03-01 17:56:22 -0800118 # Figure the first interesting frame will be after a the cffi-compiled module
Jean-Paul Calderonec2e8b412013-03-02 16:27:55 -0800119 while c_stack and '/__pycache__/_cffi__' not in c_stack[-1]:
Jean-Paul Calderone68a6f8f2013-03-01 17:56:22 -0800120 c_stack.pop()
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -0800121
Jean-Paul Calderone68a6f8f2013-03-01 17:56:22 -0800122 # Figure the last interesting frame will always be CRYPTO_malloc,
123 # since that's where we hooked in to things.
Jean-Paul Calderonec2e8b412013-03-02 16:27:55 -0800124 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 -0800125 c_stack.pop(0)
126
Jean-Paul Calderonec2e8b412013-03-02 16:27:55 -0800127 if c_stack:
128 c_stack.reverse()
129 else:
130 c_stack = saved[::-1]
Jean-Paul Calderone68a6f8f2013-03-01 17:56:22 -0800131 stack.extend([frame + "\n" for frame in c_stack])
132
Jean-Paul Calderone2beac532013-03-03 17:30:36 -0800133 stack.insert(0, "Leaked (%s) at:\n")
Jean-Paul Calderone68a6f8f2013-03-01 17:56:22 -0800134 return "".join(stack)
135
Jean-Paul Calderone855331d2013-03-03 10:21:43 -0800136 if leaks:
Jean-Paul Calderone2beac532013-03-03 17:30:36 -0800137 unique_leaks = {}
Jean-Paul Calderone855331d2013-03-03 10:21:43 -0800138 for p in leaks:
Jean-Paul Calderone2beac532013-03-03 17:30:36 -0800139 size = memdbg.heap[p][-1][0]
140 new_leak = format_leak(p)
141 if new_leak not in unique_leaks:
142 unique_leaks[new_leak] = [(size, p)]
143 else:
144 unique_leaks[new_leak].append((size, p))
145 memdbg.free(p)
146
147 for (stack, allocs) in unique_leaks.iteritems():
148 allocs_accum = []
149 for (size, pointer) in allocs:
150
Jean-Paul Calderone9227c472013-12-31 13:47:36 -0500151 addr = int(ffi.cast('uintptr_t', pointer))
Jean-Paul Calderone2beac532013-03-03 17:30:36 -0800152 allocs_accum.append("%d@0x%x" % (size, addr))
153 allocs_report = ", ".join(sorted(allocs_accum))
154
Jean-Paul Calderone855331d2013-03-03 10:21:43 -0800155 result.addError(
156 self,
Jean-Paul Calderone2beac532013-03-03 17:30:36 -0800157 (None, Exception(stack % (allocs_report,)), None))
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -0800158
Jean-Paul Calderone855331d2013-03-03 10:21:43 -0800159
160 def tearDown(self):
161 """
162 Clean up any files or directories created using :py:meth:`TestCase.mktemp`.
163 Subclasses must invoke this method if they override it or the
164 cleanup will not occur.
165 """
Hynek Schlawack38eb7482015-04-16 11:35:00 -0400166 if self._temporaryFiles is not None:
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400167 for temp in self._temporaryFiles:
168 if os.path.isdir(temp):
169 shutil.rmtree(temp)
170 elif os.path.exists(temp):
171 os.unlink(temp)
Jean-Paul Calderone1206daf2009-07-16 16:07:42 -0400172 try:
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -0500173 exception_from_error_queue(Error)
Jean-Paul Calderone24b64592010-08-12 10:43:09 -0400174 except Error:
175 e = sys.exc_info()[1]
Jean-Paul Calderone1206daf2009-07-16 16:07:42 -0400176 if e.args != ([],):
177 self.fail("Left over errors in OpenSSL error queue: " + repr(e))
178
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400179
Jean-Paul Calderone8fb53182013-12-30 08:35:49 -0500180 def assertIsInstance(self, instance, classOrTuple, message=None):
181 """
182 Fail if C{instance} is not an instance of the given class or of
183 one of the given classes.
184
185 @param instance: the object to test the type (first argument of the
186 C{isinstance} call).
187 @type instance: any.
188 @param classOrTuple: the class or classes to test against (second
189 argument of the C{isinstance} call).
190 @type classOrTuple: class, type, or tuple.
191
192 @param message: Custom text to include in the exception text if the
193 assertion fails.
194 """
195 if not isinstance(instance, classOrTuple):
196 if message is None:
197 suffix = ""
198 else:
199 suffix = ": " + message
200 self.fail("%r is not an instance of %s%s" % (
201 instance, classOrTuple, suffix))
202
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800203
Jean-Paul Calderone060a57e2011-05-04 18:02:49 -0400204 def failUnlessIn(self, containee, container, msg=None):
205 """
Jonathan Ballet648875f2011-07-16 14:14:58 +0900206 Fail the test if :py:data:`containee` is not found in :py:data:`container`.
Jean-Paul Calderone060a57e2011-05-04 18:02:49 -0400207
Jonathan Ballet648875f2011-07-16 14:14:58 +0900208 :param containee: the value that should be in :py:class:`container`
Jonathan Ballet78b92a22011-07-16 08:07:26 +0900209 :param container: a sequence type, or in the case of a mapping type,
Jean-Paul Calderone060a57e2011-05-04 18:02:49 -0400210 will follow semantics of 'if key in dict.keys()'
Jonathan Ballet78b92a22011-07-16 08:07:26 +0900211 :param msg: if msg is None, then the failure message will be
Jean-Paul Calderone060a57e2011-05-04 18:02:49 -0400212 '%r not in %r' % (first, second)
213 """
214 if containee not in container:
215 raise self.failureException(msg or "%r not in %r"
216 % (containee, container))
217 return containee
218 assertIn = failUnlessIn
219
Jean-Paul Calderone15f36442014-05-01 07:58:02 -0400220 def assertNotIn(self, containee, container, msg=None):
221 """
222 Fail the test if C{containee} is found in C{container}.
223
224 @param containee: the value that should not be in C{container}
225 @param container: a sequence type, or in the case of a mapping type,
226 will follow semantics of 'if key in dict.keys()'
227 @param msg: if msg is None, then the failure message will be
228 '%r in %r' % (first, second)
229 """
230 if containee in container:
231 raise self.failureException(msg or "%r in %r"
232 % (containee, container))
233 return containee
234 failIfIn = assertNotIn
235
236
Jean-Paul Calderone77b3d082014-12-12 20:04:35 -0500237 def assertIs(self, first, second, msg=None):
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400238 """
Jonathan Ballet648875f2011-07-16 14:14:58 +0900239 Fail the test if :py:data:`first` is not :py:data:`second`. This is an
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400240 obect-identity-equality test, not an object equality
Jonathan Ballet648875f2011-07-16 14:14:58 +0900241 (i.e. :py:func:`__eq__`) test.
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400242
Jonathan Ballet78b92a22011-07-16 08:07:26 +0900243 :param msg: if msg is None, then the failure message will be
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400244 '%r is not %r' % (first, second)
245 """
246 if first is not second:
247 raise self.failureException(msg or '%r is not %r' % (first, second))
248 return first
Jean-Paul Calderone77b3d082014-12-12 20:04:35 -0500249 assertIdentical = failUnlessIdentical = assertIs
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400250
251
Jean-Paul Calderone77b3d082014-12-12 20:04:35 -0500252 def assertIsNot(self, first, second, msg=None):
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400253 """
Jonathan Ballet648875f2011-07-16 14:14:58 +0900254 Fail the test if :py:data:`first` is :py:data:`second`. This is an
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400255 obect-identity-equality test, not an object equality
Jonathan Ballet648875f2011-07-16 14:14:58 +0900256 (i.e. :py:func:`__eq__`) test.
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400257
Jonathan Ballet78b92a22011-07-16 08:07:26 +0900258 :param msg: if msg is None, then the failure message will be
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400259 '%r is %r' % (first, second)
260 """
261 if first is second:
262 raise self.failureException(msg or '%r is %r' % (first, second))
263 return first
Jean-Paul Calderone77b3d082014-12-12 20:04:35 -0500264 assertNotIdentical = failIfIdentical = assertIsNot
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400265
266
267 def failUnlessRaises(self, exception, f, *args, **kwargs):
268 """
Jonathan Ballet648875f2011-07-16 14:14:58 +0900269 Fail the test unless calling the function :py:data:`f` with the given
270 :py:data:`args` and :py:data:`kwargs` raises :py:data:`exception`. The
271 failure will report the traceback and call stack of the unexpected
272 exception.
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400273
Jonathan Ballet78b92a22011-07-16 08:07:26 +0900274 :param exception: exception type that is to be expected
275 :param f: the function to call
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400276
Jonathan Ballet78b92a22011-07-16 08:07:26 +0900277 :return: The raised exception instance, if it is of the given type.
278 :raise self.failureException: Raised if the function call does
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400279 not raise an exception or if it raises an exception of a
280 different type.
281 """
282 try:
283 result = f(*args, **kwargs)
Jean-Paul Calderone24b64592010-08-12 10:43:09 -0400284 except exception:
285 inst = sys.exc_info()[1]
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400286 return inst
287 except:
Rick Dean47262da2009-07-08 16:17:17 -0500288 raise self.failureException('%s raised instead of %s'
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400289 % (sys.exc_info()[0],
290 exception.__name__,
Rick Dean47262da2009-07-08 16:17:17 -0500291 ))
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400292 else:
293 raise self.failureException('%s not raised (%r returned)'
294 % (exception.__name__, result))
295 assertRaises = failUnlessRaises
296
297
298 _temporaryFiles = None
Hynek Schlawack72e1e5a2015-04-16 13:05:27 -0400299 def mktemp(self, suffix=""):
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400300 """
301 Pathetic substitute for twisted.trial.unittest.TestCase.mktemp.
302 """
303 if self._temporaryFiles is None:
304 self._temporaryFiles = []
Hynek Schlawack72e1e5a2015-04-16 13:05:27 -0400305 temp = mktemp(suffix=suffix, dir=".").encode("utf-8")
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400306 self._temporaryFiles.append(temp)
307 return temp
308
309
Jean-Paul Calderone68649052009-07-17 21:14:27 -0400310 # Other stuff
311 def assertConsistentType(self, theType, name, *constructionArgs):
312 """
Jonathan Ballet648875f2011-07-16 14:14:58 +0900313 Perform various assertions about :py:data:`theType` to ensure that it is a
Jean-Paul Calderone68649052009-07-17 21:14:27 -0400314 well-defined type. This is useful for extension types, where it's
315 pretty easy to do something wacky. If something about the type is
316 unusual, an exception will be raised.
317
Jonathan Ballet78b92a22011-07-16 08:07:26 +0900318 :param theType: The type object about which to make assertions.
319 :param name: A string giving the name of the type.
Jonathan Ballet648875f2011-07-16 14:14:58 +0900320 :param constructionArgs: Positional arguments to use with :py:data:`theType` to
Jean-Paul Calderone68649052009-07-17 21:14:27 -0400321 create an instance of it.
322 """
323 self.assertEqual(theType.__name__, name)
324 self.assertTrue(isinstance(theType, type))
325 instance = theType(*constructionArgs)
326 self.assertIdentical(type(instance), theType)
Jean-Paul Calderone9c7f0692014-04-30 18:17:19 -0400327
328
329
330class EqualityTestsMixin(object):
331 """
332 A mixin defining tests for the standard implementation of C{==} and C{!=}.
333 """
334 def anInstance(self):
335 """
336 Return an instance of the class under test. Each call to this method
337 must return a different object. All objects returned must be equal to
338 each other.
339 """
340 raise NotImplementedError()
341
342
343 def anotherInstance(self):
344 """
345 Return an instance of the class under test. Each call to this method
346 must return a different object. The objects must not be equal to the
347 objects returned by C{anInstance}. They may or may not be equal to
348 each other (they will not be compared against each other).
349 """
350 raise NotImplementedError()
351
352
353 def test_identicalEq(self):
354 """
355 An object compares equal to itself using the C{==} operator.
356 """
357 o = self.anInstance()
358 self.assertTrue(o == o)
359
360
361 def test_identicalNe(self):
362 """
363 An object doesn't compare not equal to itself using the C{!=} operator.
364 """
365 o = self.anInstance()
366 self.assertFalse(o != o)
367
368
369 def test_sameEq(self):
370 """
371 Two objects that are equal to each other compare equal to each other
372 using the C{==} operator.
373 """
374 a = self.anInstance()
375 b = self.anInstance()
376 self.assertTrue(a == b)
377
378
379 def test_sameNe(self):
380 """
381 Two objects that are equal to each other do not compare not equal to
382 each other using the C{!=} operator.
383 """
384 a = self.anInstance()
385 b = self.anInstance()
386 self.assertFalse(a != b)
387
388
389 def test_differentEq(self):
390 """
391 Two objects that are not equal to each other do not compare equal to
392 each other using the C{==} operator.
393 """
394 a = self.anInstance()
395 b = self.anotherInstance()
396 self.assertFalse(a == b)
397
398
399 def test_differentNe(self):
400 """
401 Two objects that are not equal to each other compare not equal to each
402 other using the C{!=} operator.
403 """
404 a = self.anInstance()
405 b = self.anotherInstance()
406 self.assertTrue(a != b)
407
408
409 def test_anotherTypeEq(self):
410 """
411 The object does not compare equal to an object of an unrelated type
412 (which does not implement the comparison) using the C{==} operator.
413 """
414 a = self.anInstance()
415 b = object()
416 self.assertFalse(a == b)
417
418
419 def test_anotherTypeNe(self):
420 """
421 The object compares not equal to an object of an unrelated type (which
422 does not implement the comparison) using the C{!=} operator.
423 """
424 a = self.anInstance()
425 b = object()
426 self.assertTrue(a != b)
427
428
429 def test_delegatedEq(self):
430 """
431 The result of comparison using C{==} is delegated to the right-hand
432 operand if it is of an unrelated type.
433 """
434 class Delegate(object):
435 def __eq__(self, other):
436 # Do something crazy and obvious.
437 return [self]
438
439 a = self.anInstance()
440 b = Delegate()
441 self.assertEqual(a == b, [b])
442
443
444 def test_delegateNe(self):
445 """
446 The result of comparison using C{!=} is delegated to the right-hand
447 operand if it is of an unrelated type.
448 """
449 class Delegate(object):
450 def __ne__(self, other):
451 # Do something crazy and obvious.
452 return [self]
453
454 a = self.anInstance()
455 b = Delegate()
456 self.assertEqual(a != b, [b])
Jean-Paul Calderone6462b072015-03-29 07:03:11 -0400457
458
459# The type name expected in warnings about using the wrong string type.
460if PY3:
461 WARNING_TYPE_EXPECTED = "str"
462else:
463 WARNING_TYPE_EXPECTED = "unicode"