blob: 073aa6c0fe3ca69f2a19fc78ba8beae599166966 [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
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
Hynek Schlawack4813c0e2015-04-16 13:38:01 -040022
Alex Gaynor7e9de8e2015-09-04 08:34:07 -040023import memdbg
Jean-Paul Calderone68a6f8f2013-03-01 17:56:22 -080024
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -050025from OpenSSL._util import ffi, lib, byte_string as b
Jean-Paul Calderone9e4eeae2010-08-22 21:32:52 -040026
Jean-Paul Calderone210c0f32015-04-12 09:20:31 -040027
Hynek Schlawack4813c0e2015-04-16 13:38:01 -040028
Jean-Paul Calderone210c0f32015-04-12 09:20:31 -040029# This is the UTF-8 encoding of the SNOWMAN unicode code point.
30NON_ASCII = b("\xe2\x98\x83").decode("utf-8")
31
32
Hynek Schlawack4813c0e2015-04-16 13:38:01 -040033
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 Calderone40732ff2013-03-01 20:53:50 -080072
Jean-Paul Calderone855331d2013-03-03 10:21:43 -080073 def _reportLeaks(self, leaks, result):
Jean-Paul Calderone68a6f8f2013-03-01 17:56:22 -080074 def format_leak(p):
75 stacks = memdbg.heap[p]
76 # Eventually look at multiple stacks for the realloc() case. For
77 # now just look at the original allocation location.
Jean-Paul Calderonec2e8b412013-03-02 16:27:55 -080078 (size, python_stack, c_stack) = stacks[0]
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -080079
Jean-Paul Calderone68a6f8f2013-03-01 17:56:22 -080080 stack = traceback.format_list(python_stack)[:-1]
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -080081
Jean-Paul Calderone68a6f8f2013-03-01 17:56:22 -080082 # c_stack looks something like this (interesting parts indicated
83 # with inserted arrows not part of the data):
84 #
85 # /home/exarkun/Projects/pyOpenSSL/branches/use-opentls/__pycache__/_cffi__x89095113xb9185b9b.so(+0x12cf) [0x7fe2e20582cf]
86 # /home/exarkun/Projects/cpython/2.7/python(PyCFunction_Call+0x8b) [0x56265a]
87 # /home/exarkun/Projects/cpython/2.7/python() [0x4d5f52]
88 # /home/exarkun/Projects/cpython/2.7/python(PyEval_EvalFrameEx+0x753b) [0x4d0e1e]
89 # /home/exarkun/Projects/cpython/2.7/python() [0x4d6419]
90 # /home/exarkun/Projects/cpython/2.7/python() [0x4d6129]
91 # /home/exarkun/Projects/cpython/2.7/python(PyEval_EvalFrameEx+0x753b) [0x4d0e1e]
92 # /home/exarkun/Projects/cpython/2.7/python(PyEval_EvalCodeEx+0x1043) [0x4d3726]
93 # /home/exarkun/Projects/cpython/2.7/python() [0x55fd51]
94 # /home/exarkun/Projects/cpython/2.7/python(PyObject_Call+0x7e) [0x420ee6]
95 # /home/exarkun/Projects/cpython/2.7/python(PyEval_CallObjectWithKeywords+0x158) [0x4d56ec]
96 # /home/exarkun/.local/lib/python2.7/site-packages/cffi-0.5-py2.7-linux-x86_64.egg/_cffi_backend.so(+0xe96e) [0x7fe2e38be96e]
97 # /usr/lib/x86_64-linux-gnu/libffi.so.6(ffi_closure_unix64_inner+0x1b9) [0x7fe2e36ad819]
98 # /usr/lib/x86_64-linux-gnu/libffi.so.6(ffi_closure_unix64+0x46) [0x7fe2e36adb7c]
99 # /lib/x86_64-linux-gnu/libcrypto.so.1.0.0(CRYPTO_malloc+0x64) [0x7fe2e1cef784] <------ end interesting
100 # /lib/x86_64-linux-gnu/libcrypto.so.1.0.0(lh_insert+0x16b) [0x7fe2e1d6a24b] .
101 # /lib/x86_64-linux-gnu/libcrypto.so.1.0.0(+0x61c18) [0x7fe2e1cf0c18] .
102 # /lib/x86_64-linux-gnu/libcrypto.so.1.0.0(+0x625ec) [0x7fe2e1cf15ec] .
103 # /lib/x86_64-linux-gnu/libcrypto.so.1.0.0(DSA_new_method+0xe6) [0x7fe2e1d524d6] .
104 # /lib/x86_64-linux-gnu/libcrypto.so.1.0.0(DSA_generate_parameters+0x3a) [0x7fe2e1d5364a] <------ begin interesting
105 # /home/exarkun/Projects/opentls/trunk/tls/c/__pycache__/_cffi__x305d4698xb539baaa.so(+0x1f397) [0x7fe2df84d397]
106 # /home/exarkun/Projects/cpython/2.7/python(PyCFunction_Call+0x8b) [0x56265a]
107 # /home/exarkun/Projects/cpython/2.7/python() [0x4d5f52]
108 # /home/exarkun/Projects/cpython/2.7/python(PyEval_EvalFrameEx+0x753b) [0x4d0e1e]
109 # /home/exarkun/Projects/cpython/2.7/python() [0x4d6419]
110 # ...
111 #
112 # Notice the stack is upside down compared to a Python traceback.
113 # Identify the start and end of interesting bits and stuff it into 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
Jean-Paul Calderone68a6f8f2013-03-01 17:56:22 -0800117 # Figure the first interesting frame will be after a the cffi-compiled module
Jean-Paul Calderonec2e8b412013-03-02 16:27:55 -0800118 while c_stack and '/__pycache__/_cffi__' not in c_stack[-1]:
Jean-Paul Calderone68a6f8f2013-03-01 17:56:22 -0800119 c_stack.pop()
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -0800120
Jean-Paul Calderone68a6f8f2013-03-01 17:56:22 -0800121 # Figure the last interesting frame will always be CRYPTO_malloc,
122 # since that's where we hooked in to things.
Jean-Paul Calderonec2e8b412013-03-02 16:27:55 -0800123 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 -0800124 c_stack.pop(0)
125
Jean-Paul Calderonec2e8b412013-03-02 16:27:55 -0800126 if c_stack:
127 c_stack.reverse()
128 else:
129 c_stack = saved[::-1]
Jean-Paul Calderone68a6f8f2013-03-01 17:56:22 -0800130 stack.extend([frame + "\n" for frame in c_stack])
131
Jean-Paul Calderone2beac532013-03-03 17:30:36 -0800132 stack.insert(0, "Leaked (%s) at:\n")
Jean-Paul Calderone68a6f8f2013-03-01 17:56:22 -0800133 return "".join(stack)
134
Jean-Paul Calderone855331d2013-03-03 10:21:43 -0800135 if leaks:
Jean-Paul Calderone2beac532013-03-03 17:30:36 -0800136 unique_leaks = {}
Jean-Paul Calderone855331d2013-03-03 10:21:43 -0800137 for p in leaks:
Jean-Paul Calderone2beac532013-03-03 17:30:36 -0800138 size = memdbg.heap[p][-1][0]
139 new_leak = format_leak(p)
140 if new_leak not in unique_leaks:
141 unique_leaks[new_leak] = [(size, p)]
142 else:
143 unique_leaks[new_leak].append((size, p))
144 memdbg.free(p)
145
146 for (stack, allocs) in unique_leaks.iteritems():
147 allocs_accum = []
148 for (size, pointer) in allocs:
149
Jean-Paul Calderone9227c472013-12-31 13:47:36 -0500150 addr = int(ffi.cast('uintptr_t', pointer))
Jean-Paul Calderone2beac532013-03-03 17:30:36 -0800151 allocs_accum.append("%d@0x%x" % (size, addr))
152 allocs_report = ", ".join(sorted(allocs_accum))
153
Jean-Paul Calderone855331d2013-03-03 10:21:43 -0800154 result.addError(
155 self,
Jean-Paul Calderone2beac532013-03-03 17:30:36 -0800156 (None, Exception(stack % (allocs_report,)), None))
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -0800157
Jean-Paul Calderone855331d2013-03-03 10:21:43 -0800158
Hynek Schlawack4813c0e2015-04-16 13:38:01 -0400159 _tmpdir = None
160
161
162 @property
163 def tmpdir(self):
164 """
165 On demand create a temporary directory.
166 """
167 if self._tmpdir is not None:
168 return self._tmpdir
169
170 self._tmpdir = mkdtemp(dir=".")
171 return self._tmpdir
172
173
Jean-Paul Calderone855331d2013-03-03 10:21:43 -0800174 def tearDown(self):
175 """
Hynek Schlawack4813c0e2015-04-16 13:38:01 -0400176 Clean up any files or directories created using
177 :py:meth:`TestCase.mktemp`. Subclasses must invoke this method if they
178 override it or the cleanup will not occur.
Jean-Paul Calderone855331d2013-03-03 10:21:43 -0800179 """
Hynek Schlawack4813c0e2015-04-16 13:38:01 -0400180 if self._tmpdir is not None:
181 shutil.rmtree(self._tmpdir)
182
Jean-Paul Calderone1206daf2009-07-16 16:07:42 -0400183 try:
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -0500184 exception_from_error_queue(Error)
Jean-Paul Calderone24b64592010-08-12 10:43:09 -0400185 except Error:
186 e = sys.exc_info()[1]
Jean-Paul Calderone1206daf2009-07-16 16:07:42 -0400187 if e.args != ([],):
Hynek Schlawack4813c0e2015-04-16 13:38:01 -0400188 self.fail(
189 "Left over errors in OpenSSL error queue: " + repr(e)
190 )
Jean-Paul Calderone1206daf2009-07-16 16:07:42 -0400191
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400192
Jean-Paul Calderone8fb53182013-12-30 08:35:49 -0500193 def assertIsInstance(self, instance, classOrTuple, message=None):
194 """
195 Fail if C{instance} is not an instance of the given class or of
196 one of the given classes.
197
198 @param instance: the object to test the type (first argument of the
199 C{isinstance} call).
200 @type instance: any.
201 @param classOrTuple: the class or classes to test against (second
202 argument of the C{isinstance} call).
203 @type classOrTuple: class, type, or tuple.
204
205 @param message: Custom text to include in the exception text if the
206 assertion fails.
207 """
208 if not isinstance(instance, classOrTuple):
209 if message is None:
210 suffix = ""
211 else:
212 suffix = ": " + message
213 self.fail("%r is not an instance of %s%s" % (
214 instance, classOrTuple, suffix))
215
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800216
Jean-Paul Calderone060a57e2011-05-04 18:02:49 -0400217 def failUnlessIn(self, containee, container, msg=None):
218 """
Jonathan Ballet648875f2011-07-16 14:14:58 +0900219 Fail the test if :py:data:`containee` is not found in :py:data:`container`.
Jean-Paul Calderone060a57e2011-05-04 18:02:49 -0400220
Jonathan Ballet648875f2011-07-16 14:14:58 +0900221 :param containee: the value that should be in :py:class:`container`
Jonathan Ballet78b92a22011-07-16 08:07:26 +0900222 :param container: a sequence type, or in the case of a mapping type,
Jean-Paul Calderone060a57e2011-05-04 18:02:49 -0400223 will follow semantics of 'if key in dict.keys()'
Jonathan Ballet78b92a22011-07-16 08:07:26 +0900224 :param msg: if msg is None, then the failure message will be
Jean-Paul Calderone060a57e2011-05-04 18:02:49 -0400225 '%r not in %r' % (first, second)
226 """
227 if containee not in container:
228 raise self.failureException(msg or "%r not in %r"
229 % (containee, container))
230 return containee
231 assertIn = failUnlessIn
232
Jean-Paul Calderone15f36442014-05-01 07:58:02 -0400233 def assertNotIn(self, containee, container, msg=None):
234 """
235 Fail the test if C{containee} is found in C{container}.
236
237 @param containee: the value that should not be in C{container}
238 @param container: a sequence type, or in the case of a mapping type,
239 will follow semantics of 'if key in dict.keys()'
240 @param msg: if msg is None, then the failure message will be
241 '%r in %r' % (first, second)
242 """
243 if containee in container:
244 raise self.failureException(msg or "%r in %r"
245 % (containee, container))
246 return containee
247 failIfIn = assertNotIn
248
249
Jean-Paul Calderone77b3d082014-12-12 20:04:35 -0500250 def assertIs(self, first, second, msg=None):
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400251 """
Jonathan Ballet648875f2011-07-16 14:14:58 +0900252 Fail the test if :py:data:`first` is not :py:data:`second`. This is an
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400253 obect-identity-equality test, not an object equality
Jonathan Ballet648875f2011-07-16 14:14:58 +0900254 (i.e. :py:func:`__eq__`) test.
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400255
Jonathan Ballet78b92a22011-07-16 08:07:26 +0900256 :param msg: if msg is None, then the failure message will be
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400257 '%r is not %r' % (first, second)
258 """
259 if first is not second:
260 raise self.failureException(msg or '%r is not %r' % (first, second))
261 return first
Jean-Paul Calderone77b3d082014-12-12 20:04:35 -0500262 assertIdentical = failUnlessIdentical = assertIs
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400263
264
Jean-Paul Calderone77b3d082014-12-12 20:04:35 -0500265 def assertIsNot(self, first, second, msg=None):
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400266 """
Jonathan Ballet648875f2011-07-16 14:14:58 +0900267 Fail the test if :py:data:`first` is :py:data:`second`. This is an
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400268 obect-identity-equality test, not an object equality
Jonathan Ballet648875f2011-07-16 14:14:58 +0900269 (i.e. :py:func:`__eq__`) test.
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400270
Jonathan Ballet78b92a22011-07-16 08:07:26 +0900271 :param msg: if msg is None, then the failure message will be
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400272 '%r is %r' % (first, second)
273 """
274 if first is second:
275 raise self.failureException(msg or '%r is %r' % (first, second))
276 return first
Jean-Paul Calderone77b3d082014-12-12 20:04:35 -0500277 assertNotIdentical = failIfIdentical = assertIsNot
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400278
279
280 def failUnlessRaises(self, exception, f, *args, **kwargs):
281 """
Jonathan Ballet648875f2011-07-16 14:14:58 +0900282 Fail the test unless calling the function :py:data:`f` with the given
283 :py:data:`args` and :py:data:`kwargs` raises :py:data:`exception`. The
284 failure will report the traceback and call stack of the unexpected
285 exception.
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400286
Jonathan Ballet78b92a22011-07-16 08:07:26 +0900287 :param exception: exception type that is to be expected
288 :param f: the function to call
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400289
Jonathan Ballet78b92a22011-07-16 08:07:26 +0900290 :return: The raised exception instance, if it is of the given type.
291 :raise self.failureException: Raised if the function call does
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400292 not raise an exception or if it raises an exception of a
293 different type.
294 """
295 try:
296 result = f(*args, **kwargs)
Jean-Paul Calderone24b64592010-08-12 10:43:09 -0400297 except exception:
298 inst = sys.exc_info()[1]
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400299 return inst
300 except:
Rick Dean47262da2009-07-08 16:17:17 -0500301 raise self.failureException('%s raised instead of %s'
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400302 % (sys.exc_info()[0],
303 exception.__name__,
Rick Dean47262da2009-07-08 16:17:17 -0500304 ))
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400305 else:
306 raise self.failureException('%s not raised (%r returned)'
307 % (exception.__name__, result))
308 assertRaises = failUnlessRaises
309
310
Hynek Schlawack4813c0e2015-04-16 13:38:01 -0400311 def mktemp(self):
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400312 """
Hynek Schlawack4813c0e2015-04-16 13:38:01 -0400313 Return UTF-8-encoded bytes of a path to a tmp file.
314
315 The file will be cleaned up after the test run.
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400316 """
Hynek Schlawack4813c0e2015-04-16 13:38:01 -0400317 return mktemp(dir=self.tmpdir).encode("utf-8")
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400318
319
Jean-Paul Calderone68649052009-07-17 21:14:27 -0400320 # Other stuff
321 def assertConsistentType(self, theType, name, *constructionArgs):
322 """
Jonathan Ballet648875f2011-07-16 14:14:58 +0900323 Perform various assertions about :py:data:`theType` to ensure that it is a
Jean-Paul Calderone68649052009-07-17 21:14:27 -0400324 well-defined type. This is useful for extension types, where it's
325 pretty easy to do something wacky. If something about the type is
326 unusual, an exception will be raised.
327
Jonathan Ballet78b92a22011-07-16 08:07:26 +0900328 :param theType: The type object about which to make assertions.
329 :param name: A string giving the name of the type.
Jonathan Ballet648875f2011-07-16 14:14:58 +0900330 :param constructionArgs: Positional arguments to use with :py:data:`theType` to
Jean-Paul Calderone68649052009-07-17 21:14:27 -0400331 create an instance of it.
332 """
333 self.assertEqual(theType.__name__, name)
334 self.assertTrue(isinstance(theType, type))
335 instance = theType(*constructionArgs)
336 self.assertIdentical(type(instance), theType)
Jean-Paul Calderone9c7f0692014-04-30 18:17:19 -0400337
338
339
340class EqualityTestsMixin(object):
341 """
342 A mixin defining tests for the standard implementation of C{==} and C{!=}.
343 """
344 def anInstance(self):
345 """
346 Return an instance of the class under test. Each call to this method
347 must return a different object. All objects returned must be equal to
348 each other.
349 """
350 raise NotImplementedError()
351
352
353 def anotherInstance(self):
354 """
355 Return an instance of the class under test. Each call to this method
356 must return a different object. The objects must not be equal to the
357 objects returned by C{anInstance}. They may or may not be equal to
358 each other (they will not be compared against each other).
359 """
360 raise NotImplementedError()
361
362
363 def test_identicalEq(self):
364 """
365 An object compares equal to itself using the C{==} operator.
366 """
367 o = self.anInstance()
368 self.assertTrue(o == o)
369
370
371 def test_identicalNe(self):
372 """
373 An object doesn't compare not equal to itself using the C{!=} operator.
374 """
375 o = self.anInstance()
376 self.assertFalse(o != o)
377
378
379 def test_sameEq(self):
380 """
381 Two objects that are equal to each other compare equal to each other
382 using the C{==} operator.
383 """
384 a = self.anInstance()
385 b = self.anInstance()
386 self.assertTrue(a == b)
387
388
389 def test_sameNe(self):
390 """
391 Two objects that are equal to each other do not compare not equal to
392 each other using the C{!=} operator.
393 """
394 a = self.anInstance()
395 b = self.anInstance()
396 self.assertFalse(a != b)
397
398
399 def test_differentEq(self):
400 """
401 Two objects that are not equal to each other do not compare equal to
402 each other using the C{==} operator.
403 """
404 a = self.anInstance()
405 b = self.anotherInstance()
406 self.assertFalse(a == b)
407
408
409 def test_differentNe(self):
410 """
411 Two objects that are not equal to each other compare not equal to each
412 other using the C{!=} operator.
413 """
414 a = self.anInstance()
415 b = self.anotherInstance()
416 self.assertTrue(a != b)
417
418
419 def test_anotherTypeEq(self):
420 """
421 The object does not compare equal to an object of an unrelated type
422 (which does not implement the comparison) using the C{==} operator.
423 """
424 a = self.anInstance()
425 b = object()
426 self.assertFalse(a == b)
427
428
429 def test_anotherTypeNe(self):
430 """
431 The object compares not equal to an object of an unrelated type (which
432 does not implement the comparison) using the C{!=} operator.
433 """
434 a = self.anInstance()
435 b = object()
436 self.assertTrue(a != b)
437
438
439 def test_delegatedEq(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 __eq__(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])
452
453
454 def test_delegateNe(self):
455 """
456 The result of comparison using C{!=} is delegated to the right-hand
457 operand if it is of an unrelated type.
458 """
459 class Delegate(object):
460 def __ne__(self, other):
461 # Do something crazy and obvious.
462 return [self]
463
464 a = self.anInstance()
465 b = Delegate()
466 self.assertEqual(a != b, [b])
Jean-Paul Calderone6462b072015-03-29 07:03:11 -0400467
468
469# The type name expected in warnings about using the wrong string type.
470if PY3:
471 WARNING_TYPE_EXPECTED = "str"
472else:
473 WARNING_TYPE_EXPECTED = "unicode"