blob: 7f6f38c7ceae94fd198d2615d424ebb47a51bb1d [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
Rick Dean47262da2009-07-08 16:17:17 -050011import sys
Hynek Schlawack4813c0e2015-04-16 13:38:01 -040012import traceback
13
14from tempfile import mktemp, mkdtemp
15from unittest import TestCase
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -040016
Alex Gaynord0e83ad2015-09-05 13:19:43 -040017import pytest
18
Jean-Paul Calderone6462b072015-03-29 07:03:11 -040019from six import PY3
20
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -050021from OpenSSL._util import exception_from_error_queue
22from OpenSSL.crypto import Error
Jean-Paul Calderone88f38b22009-07-16 16:25:19 -040023
Hynek Schlawack4813c0e2015-04-16 13:38:01 -040024
Alex Gaynor7e9de8e2015-09-04 08:34:07 -040025import memdbg
Jean-Paul Calderone68a6f8f2013-03-01 17:56:22 -080026
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -050027from OpenSSL._util import ffi, lib, byte_string as b
Jean-Paul Calderone9e4eeae2010-08-22 21:32:52 -040028
Jean-Paul Calderone210c0f32015-04-12 09:20:31 -040029
30# This is the UTF-8 encoding of the SNOWMAN unicode code point.
31NON_ASCII = b("\xe2\x98\x83").decode("utf-8")
32
33
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -040034class TestCase(TestCase):
35 """
Hynek Schlawack4813c0e2015-04-16 13:38:01 -040036 :py:class:`TestCase` adds useful testing functionality beyond what is
37 available from the standard library :py:class:`unittest.TestCase`.
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -040038 """
Jean-Paul Calderone855331d2013-03-03 10:21:43 -080039 def run(self, result):
Jean-Paul Calderone68703ed2013-03-04 12:23:44 -080040 run = super(TestCase, self).run
41 if memdbg.heap is None:
42 return run(result)
43
Jean-Paul Calderone855331d2013-03-03 10:21:43 -080044 # Run the test as usual
45 before = set(memdbg.heap)
Jean-Paul Calderone68703ed2013-03-04 12:23:44 -080046 run(result)
Jean-Paul Calderone855331d2013-03-03 10:21:43 -080047
48 # Clean up some long-lived allocations so they won't be reported as
49 # memory leaks.
Jean-Paul Calderone9227c472013-12-31 13:47:36 -050050 lib.CRYPTO_cleanup_all_ex_data()
51 lib.ERR_remove_thread_state(ffi.NULL)
Jean-Paul Calderone855331d2013-03-03 10:21:43 -080052 after = set(memdbg.heap)
53
54 if not after - before:
55 # No leaks, fast succeed
56 return
57
58 if result.wasSuccessful():
59 # If it passed, run it again with memory debugging
60 before = set(memdbg.heap)
Jean-Paul Calderone68703ed2013-03-04 12:23:44 -080061 run(result)
Jean-Paul Calderone855331d2013-03-03 10:21:43 -080062
63 # Clean up some long-lived allocations so they won't be reported as
64 # memory leaks.
Jean-Paul Calderone3f93d212014-01-01 12:36:53 -050065 lib.CRYPTO_cleanup_all_ex_data()
66 lib.ERR_remove_thread_state(ffi.NULL)
Jean-Paul Calderone855331d2013-03-03 10:21:43 -080067
68 after = set(memdbg.heap)
69
70 self._reportLeaks(after - before, result)
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -080071
Jean-Paul Calderone855331d2013-03-03 10:21:43 -080072 def _reportLeaks(self, leaks, result):
Jean-Paul Calderone68a6f8f2013-03-01 17:56:22 -080073 def format_leak(p):
74 stacks = memdbg.heap[p]
75 # Eventually look at multiple stacks for the realloc() case. For
76 # now just look at the original allocation location.
Jean-Paul Calderonec2e8b412013-03-02 16:27:55 -080077 (size, python_stack, c_stack) = stacks[0]
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -080078
Jean-Paul Calderone68a6f8f2013-03-01 17:56:22 -080079 stack = traceback.format_list(python_stack)[:-1]
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -080080
Jean-Paul Calderone68a6f8f2013-03-01 17:56:22 -080081 # c_stack looks something like this (interesting parts indicated
82 # with inserted arrows not part of the data):
83 #
84 # /home/exarkun/Projects/pyOpenSSL/branches/use-opentls/__pycache__/_cffi__x89095113xb9185b9b.so(+0x12cf) [0x7fe2e20582cf]
85 # /home/exarkun/Projects/cpython/2.7/python(PyCFunction_Call+0x8b) [0x56265a]
86 # /home/exarkun/Projects/cpython/2.7/python() [0x4d5f52]
87 # /home/exarkun/Projects/cpython/2.7/python(PyEval_EvalFrameEx+0x753b) [0x4d0e1e]
88 # /home/exarkun/Projects/cpython/2.7/python() [0x4d6419]
89 # /home/exarkun/Projects/cpython/2.7/python() [0x4d6129]
90 # /home/exarkun/Projects/cpython/2.7/python(PyEval_EvalFrameEx+0x753b) [0x4d0e1e]
91 # /home/exarkun/Projects/cpython/2.7/python(PyEval_EvalCodeEx+0x1043) [0x4d3726]
92 # /home/exarkun/Projects/cpython/2.7/python() [0x55fd51]
93 # /home/exarkun/Projects/cpython/2.7/python(PyObject_Call+0x7e) [0x420ee6]
94 # /home/exarkun/Projects/cpython/2.7/python(PyEval_CallObjectWithKeywords+0x158) [0x4d56ec]
95 # /home/exarkun/.local/lib/python2.7/site-packages/cffi-0.5-py2.7-linux-x86_64.egg/_cffi_backend.so(+0xe96e) [0x7fe2e38be96e]
96 # /usr/lib/x86_64-linux-gnu/libffi.so.6(ffi_closure_unix64_inner+0x1b9) [0x7fe2e36ad819]
97 # /usr/lib/x86_64-linux-gnu/libffi.so.6(ffi_closure_unix64+0x46) [0x7fe2e36adb7c]
98 # /lib/x86_64-linux-gnu/libcrypto.so.1.0.0(CRYPTO_malloc+0x64) [0x7fe2e1cef784] <------ end interesting
99 # /lib/x86_64-linux-gnu/libcrypto.so.1.0.0(lh_insert+0x16b) [0x7fe2e1d6a24b] .
100 # /lib/x86_64-linux-gnu/libcrypto.so.1.0.0(+0x61c18) [0x7fe2e1cf0c18] .
101 # /lib/x86_64-linux-gnu/libcrypto.so.1.0.0(+0x625ec) [0x7fe2e1cf15ec] .
102 # /lib/x86_64-linux-gnu/libcrypto.so.1.0.0(DSA_new_method+0xe6) [0x7fe2e1d524d6] .
103 # /lib/x86_64-linux-gnu/libcrypto.so.1.0.0(DSA_generate_parameters+0x3a) [0x7fe2e1d5364a] <------ begin interesting
104 # /home/exarkun/Projects/opentls/trunk/tls/c/__pycache__/_cffi__x305d4698xb539baaa.so(+0x1f397) [0x7fe2df84d397]
105 # /home/exarkun/Projects/cpython/2.7/python(PyCFunction_Call+0x8b) [0x56265a]
106 # /home/exarkun/Projects/cpython/2.7/python() [0x4d5f52]
107 # /home/exarkun/Projects/cpython/2.7/python(PyEval_EvalFrameEx+0x753b) [0x4d0e1e]
108 # /home/exarkun/Projects/cpython/2.7/python() [0x4d6419]
109 # ...
110 #
111 # Notice the stack is upside down compared to a Python traceback.
Alex Gaynor75690d92015-09-05 10:14:52 -0400112 # Identify the start and end of interesting bits and stuff it into
113 # the stack we report.
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -0800114
Jean-Paul Calderonec2e8b412013-03-02 16:27:55 -0800115 saved = list(c_stack)
116
Alex Gaynor75690d92015-09-05 10:14:52 -0400117 # Figure the first interesting frame will be after a the
118 # 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
Hynek Schlawack4813c0e2015-04-16 13:38:01 -0400159 _tmpdir = None
160
Hynek Schlawack4813c0e2015-04-16 13:38:01 -0400161 @property
162 def tmpdir(self):
163 """
164 On demand create a temporary directory.
165 """
166 if self._tmpdir is not None:
167 return self._tmpdir
168
169 self._tmpdir = mkdtemp(dir=".")
170 return self._tmpdir
171
Jean-Paul Calderone855331d2013-03-03 10:21:43 -0800172 def tearDown(self):
173 """
Hynek Schlawack4813c0e2015-04-16 13:38:01 -0400174 Clean up any files or directories created using
175 :py:meth:`TestCase.mktemp`. Subclasses must invoke this method if they
176 override it or the cleanup will not occur.
Jean-Paul Calderone855331d2013-03-03 10:21:43 -0800177 """
Hynek Schlawack4813c0e2015-04-16 13:38:01 -0400178 if self._tmpdir is not None:
179 shutil.rmtree(self._tmpdir)
180
Jean-Paul Calderone1206daf2009-07-16 16:07:42 -0400181 try:
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -0500182 exception_from_error_queue(Error)
Jean-Paul Calderone24b64592010-08-12 10:43:09 -0400183 except Error:
184 e = sys.exc_info()[1]
Jean-Paul Calderone1206daf2009-07-16 16:07:42 -0400185 if e.args != ([],):
Hynek Schlawack4813c0e2015-04-16 13:38:01 -0400186 self.fail(
187 "Left over errors in OpenSSL error queue: " + repr(e)
188 )
Jean-Paul Calderone1206daf2009-07-16 16:07:42 -0400189
Jean-Paul Calderone8fb53182013-12-30 08:35:49 -0500190 def assertIsInstance(self, instance, classOrTuple, message=None):
191 """
192 Fail if C{instance} is not an instance of the given class or of
193 one of the given classes.
194
195 @param instance: the object to test the type (first argument of the
196 C{isinstance} call).
197 @type instance: any.
198 @param classOrTuple: the class or classes to test against (second
199 argument of the C{isinstance} call).
200 @type classOrTuple: class, type, or tuple.
201
202 @param message: Custom text to include in the exception text if the
203 assertion fails.
204 """
Alex Gaynord0e83ad2015-09-05 13:19:43 -0400205 assert isinstance(instance, classOrTuple)
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800206
Jean-Paul Calderone060a57e2011-05-04 18:02:49 -0400207 def failUnlessIn(self, containee, container, msg=None):
208 """
Jonathan Ballet648875f2011-07-16 14:14:58 +0900209 Fail the test if :py:data:`containee` is not found in :py:data:`container`.
Jean-Paul Calderone060a57e2011-05-04 18:02:49 -0400210
Jonathan Ballet648875f2011-07-16 14:14:58 +0900211 :param containee: the value that should be in :py:class:`container`
Jonathan Ballet78b92a22011-07-16 08:07:26 +0900212 :param container: a sequence type, or in the case of a mapping type,
Jean-Paul Calderone060a57e2011-05-04 18:02:49 -0400213 will follow semantics of 'if key in dict.keys()'
Jonathan Ballet78b92a22011-07-16 08:07:26 +0900214 :param msg: if msg is None, then the failure message will be
Jean-Paul Calderone060a57e2011-05-04 18:02:49 -0400215 '%r not in %r' % (first, second)
216 """
Alex Gaynord0e83ad2015-09-05 13:19:43 -0400217 assert containee in container
Jean-Paul Calderone060a57e2011-05-04 18:02:49 -0400218 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 """
Alex Gaynord0e83ad2015-09-05 13:19:43 -0400230 assert containee not in container
Jean-Paul Calderone15f36442014-05-01 07:58:02 -0400231 failIfIn = assertNotIn
232
Jean-Paul Calderone77b3d082014-12-12 20:04:35 -0500233 def assertIs(self, first, second, msg=None):
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400234 """
Jonathan Ballet648875f2011-07-16 14:14:58 +0900235 Fail the test if :py:data:`first` is not :py:data:`second`. This is an
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400236 obect-identity-equality test, not an object equality
Jonathan Ballet648875f2011-07-16 14:14:58 +0900237 (i.e. :py:func:`__eq__`) test.
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400238
Jonathan Ballet78b92a22011-07-16 08:07:26 +0900239 :param msg: if msg is None, then the failure message will be
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400240 '%r is not %r' % (first, second)
241 """
Alex Gaynord0e83ad2015-09-05 13:19:43 -0400242 assert first is second
Jean-Paul Calderone77b3d082014-12-12 20:04:35 -0500243 assertIdentical = failUnlessIdentical = assertIs
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400244
Jean-Paul Calderone77b3d082014-12-12 20:04:35 -0500245 def assertIsNot(self, first, second, msg=None):
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400246 """
Jonathan Ballet648875f2011-07-16 14:14:58 +0900247 Fail the test if :py:data:`first` is :py:data:`second`. This is an
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400248 obect-identity-equality test, not an object equality
Jonathan Ballet648875f2011-07-16 14:14:58 +0900249 (i.e. :py:func:`__eq__`) test.
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400250
Jonathan Ballet78b92a22011-07-16 08:07:26 +0900251 :param msg: if msg is None, then the failure message will be
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400252 '%r is %r' % (first, second)
253 """
Alex Gaynord0e83ad2015-09-05 13:19:43 -0400254 assert first is not second
Jean-Paul Calderone77b3d082014-12-12 20:04:35 -0500255 assertNotIdentical = failIfIdentical = assertIsNot
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400256
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400257 def failUnlessRaises(self, exception, f, *args, **kwargs):
258 """
Jonathan Ballet648875f2011-07-16 14:14:58 +0900259 Fail the test unless calling the function :py:data:`f` with the given
260 :py:data:`args` and :py:data:`kwargs` raises :py:data:`exception`. The
261 failure will report the traceback and call stack of the unexpected
262 exception.
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400263
Jonathan Ballet78b92a22011-07-16 08:07:26 +0900264 :param exception: exception type that is to be expected
265 :param f: the function to call
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400266
Jonathan Ballet78b92a22011-07-16 08:07:26 +0900267 :return: The raised exception instance, if it is of the given type.
268 :raise self.failureException: Raised if the function call does
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400269 not raise an exception or if it raises an exception of a
270 different type.
271 """
Alex Gaynord0e83ad2015-09-05 13:19:43 -0400272 with pytest.raises(exception) as cm:
273 f(*args, **kwargs)
274 return cm.value
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400275 assertRaises = failUnlessRaises
276
Hynek Schlawack4813c0e2015-04-16 13:38:01 -0400277 def mktemp(self):
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400278 """
Hynek Schlawack4813c0e2015-04-16 13:38:01 -0400279 Return UTF-8-encoded bytes of a path to a tmp file.
280
281 The file will be cleaned up after the test run.
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400282 """
Hynek Schlawack4813c0e2015-04-16 13:38:01 -0400283 return mktemp(dir=self.tmpdir).encode("utf-8")
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400284
Jean-Paul Calderone68649052009-07-17 21:14:27 -0400285 # Other stuff
286 def assertConsistentType(self, theType, name, *constructionArgs):
287 """
Jonathan Ballet648875f2011-07-16 14:14:58 +0900288 Perform various assertions about :py:data:`theType` to ensure that it is a
Jean-Paul Calderone68649052009-07-17 21:14:27 -0400289 well-defined type. This is useful for extension types, where it's
290 pretty easy to do something wacky. If something about the type is
291 unusual, an exception will be raised.
292
Jonathan Ballet78b92a22011-07-16 08:07:26 +0900293 :param theType: The type object about which to make assertions.
294 :param name: A string giving the name of the type.
Jonathan Ballet648875f2011-07-16 14:14:58 +0900295 :param constructionArgs: Positional arguments to use with :py:data:`theType` to
Jean-Paul Calderone68649052009-07-17 21:14:27 -0400296 create an instance of it.
297 """
298 self.assertEqual(theType.__name__, name)
299 self.assertTrue(isinstance(theType, type))
300 instance = theType(*constructionArgs)
301 self.assertIdentical(type(instance), theType)
Jean-Paul Calderone9c7f0692014-04-30 18:17:19 -0400302
303
Jean-Paul Calderone9c7f0692014-04-30 18:17:19 -0400304class EqualityTestsMixin(object):
305 """
306 A mixin defining tests for the standard implementation of C{==} and C{!=}.
307 """
308 def anInstance(self):
309 """
310 Return an instance of the class under test. Each call to this method
311 must return a different object. All objects returned must be equal to
312 each other.
313 """
314 raise NotImplementedError()
315
Jean-Paul Calderone9c7f0692014-04-30 18:17:19 -0400316 def anotherInstance(self):
317 """
318 Return an instance of the class under test. Each call to this method
319 must return a different object. The objects must not be equal to the
320 objects returned by C{anInstance}. They may or may not be equal to
321 each other (they will not be compared against each other).
322 """
323 raise NotImplementedError()
324
Jean-Paul Calderone9c7f0692014-04-30 18:17:19 -0400325 def test_identicalEq(self):
326 """
327 An object compares equal to itself using the C{==} operator.
328 """
329 o = self.anInstance()
330 self.assertTrue(o == o)
331
Jean-Paul Calderone9c7f0692014-04-30 18:17:19 -0400332 def test_identicalNe(self):
333 """
334 An object doesn't compare not equal to itself using the C{!=} operator.
335 """
336 o = self.anInstance()
337 self.assertFalse(o != o)
338
Jean-Paul Calderone9c7f0692014-04-30 18:17:19 -0400339 def test_sameEq(self):
340 """
341 Two objects that are equal to each other compare equal to each other
342 using the C{==} operator.
343 """
344 a = self.anInstance()
345 b = self.anInstance()
346 self.assertTrue(a == b)
347
Jean-Paul Calderone9c7f0692014-04-30 18:17:19 -0400348 def test_sameNe(self):
349 """
350 Two objects that are equal to each other do not compare not equal to
351 each other using the C{!=} operator.
352 """
353 a = self.anInstance()
354 b = self.anInstance()
355 self.assertFalse(a != b)
356
Jean-Paul Calderone9c7f0692014-04-30 18:17:19 -0400357 def test_differentEq(self):
358 """
359 Two objects that are not equal to each other do not compare equal to
360 each other using the C{==} operator.
361 """
362 a = self.anInstance()
363 b = self.anotherInstance()
364 self.assertFalse(a == b)
365
Jean-Paul Calderone9c7f0692014-04-30 18:17:19 -0400366 def test_differentNe(self):
367 """
368 Two objects that are not equal to each other compare not equal to each
369 other using the C{!=} operator.
370 """
371 a = self.anInstance()
372 b = self.anotherInstance()
373 self.assertTrue(a != b)
374
Jean-Paul Calderone9c7f0692014-04-30 18:17:19 -0400375 def test_anotherTypeEq(self):
376 """
377 The object does not compare equal to an object of an unrelated type
378 (which does not implement the comparison) using the C{==} operator.
379 """
380 a = self.anInstance()
381 b = object()
382 self.assertFalse(a == b)
383
Jean-Paul Calderone9c7f0692014-04-30 18:17:19 -0400384 def test_anotherTypeNe(self):
385 """
386 The object compares not equal to an object of an unrelated type (which
387 does not implement the comparison) using the C{!=} operator.
388 """
389 a = self.anInstance()
390 b = object()
391 self.assertTrue(a != b)
392
Jean-Paul Calderone9c7f0692014-04-30 18:17:19 -0400393 def test_delegatedEq(self):
394 """
395 The result of comparison using C{==} is delegated to the right-hand
396 operand if it is of an unrelated type.
397 """
398 class Delegate(object):
399 def __eq__(self, other):
400 # Do something crazy and obvious.
401 return [self]
402
403 a = self.anInstance()
404 b = Delegate()
405 self.assertEqual(a == b, [b])
406
Jean-Paul Calderone9c7f0692014-04-30 18:17:19 -0400407 def test_delegateNe(self):
408 """
409 The result of comparison using C{!=} is delegated to the right-hand
410 operand if it is of an unrelated type.
411 """
412 class Delegate(object):
413 def __ne__(self, other):
414 # Do something crazy and obvious.
415 return [self]
416
417 a = self.anInstance()
418 b = Delegate()
419 self.assertEqual(a != b, [b])
Jean-Paul Calderone6462b072015-03-29 07:03:11 -0400420
421
422# The type name expected in warnings about using the wrong string type.
423if PY3:
424 WARNING_TYPE_EXPECTED = "str"
425else:
426 WARNING_TYPE_EXPECTED = "unicode"