blob: 78b4a3fc966e9371637cff23c6c4c2ece742dcfb [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
Hynek Schlawack4813c0e2015-04-16 13:38:01 -040010import os
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -040011import shutil
Rick Dean47262da2009-07-08 16:17:17 -050012import sys
Hynek Schlawack4813c0e2015-04-16 13:38:01 -040013import traceback
14
15from tempfile import mktemp, mkdtemp
16from unittest import TestCase
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -040017
Jean-Paul Calderone6462b072015-03-29 07:03:11 -040018from six import PY3
19
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -050020from OpenSSL._util import exception_from_error_queue
21from OpenSSL.crypto import Error
Jean-Paul Calderone88f38b22009-07-16 16:25:19 -040022
Hynek Schlawack4813c0e2015-04-16 13:38:01 -040023
Konstantinos Koukopoulos410d0422014-01-30 20:15:25 +020024try:
25 import memdbg
26except Exception:
27 class _memdbg(object): heap = None
28 memdbg = _memdbg()
Jean-Paul Calderone68a6f8f2013-03-01 17:56:22 -080029
Jean-Paul Calderone4f0467a2014-01-11 11:58:41 -050030from OpenSSL._util import ffi, lib, byte_string as b
Jean-Paul Calderone9e4eeae2010-08-22 21:32:52 -040031
Jean-Paul Calderone210c0f32015-04-12 09:20:31 -040032
Hynek Schlawack4813c0e2015-04-16 13:38:01 -040033
Jean-Paul Calderone210c0f32015-04-12 09:20:31 -040034# This is the UTF-8 encoding of the SNOWMAN unicode code point.
35NON_ASCII = b("\xe2\x98\x83").decode("utf-8")
36
37
Hynek Schlawack4813c0e2015-04-16 13:38:01 -040038
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -040039class TestCase(TestCase):
40 """
Hynek Schlawack4813c0e2015-04-16 13:38:01 -040041 :py:class:`TestCase` adds useful testing functionality beyond what is
42 available from the standard library :py:class:`unittest.TestCase`.
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -040043 """
Jean-Paul Calderone855331d2013-03-03 10:21:43 -080044 def run(self, result):
Jean-Paul Calderone68703ed2013-03-04 12:23:44 -080045 run = super(TestCase, self).run
46 if memdbg.heap is None:
47 return run(result)
48
Jean-Paul Calderone855331d2013-03-03 10:21:43 -080049 # Run the test as usual
50 before = set(memdbg.heap)
Jean-Paul Calderone68703ed2013-03-04 12:23:44 -080051 run(result)
Jean-Paul Calderone855331d2013-03-03 10:21:43 -080052
53 # Clean up some long-lived allocations so they won't be reported as
54 # memory leaks.
Jean-Paul Calderone9227c472013-12-31 13:47:36 -050055 lib.CRYPTO_cleanup_all_ex_data()
56 lib.ERR_remove_thread_state(ffi.NULL)
Jean-Paul Calderone855331d2013-03-03 10:21:43 -080057 after = set(memdbg.heap)
58
59 if not after - before:
60 # No leaks, fast succeed
61 return
62
63 if result.wasSuccessful():
64 # If it passed, run it again with memory debugging
65 before = set(memdbg.heap)
Jean-Paul Calderone68703ed2013-03-04 12:23:44 -080066 run(result)
Jean-Paul Calderone855331d2013-03-03 10:21:43 -080067
68 # Clean up some long-lived allocations so they won't be reported as
69 # memory leaks.
Jean-Paul Calderone3f93d212014-01-01 12:36:53 -050070 lib.CRYPTO_cleanup_all_ex_data()
71 lib.ERR_remove_thread_state(ffi.NULL)
Jean-Paul Calderone855331d2013-03-03 10:21:43 -080072
73 after = set(memdbg.heap)
74
75 self._reportLeaks(after - before, result)
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -080076
Jean-Paul Calderone40732ff2013-03-01 20:53:50 -080077
Jean-Paul Calderone855331d2013-03-03 10:21:43 -080078 def _reportLeaks(self, leaks, result):
Jean-Paul Calderone68a6f8f2013-03-01 17:56:22 -080079 def format_leak(p):
80 stacks = memdbg.heap[p]
81 # Eventually look at multiple stacks for the realloc() case. For
82 # now just look at the original allocation location.
Jean-Paul Calderonec2e8b412013-03-02 16:27:55 -080083 (size, python_stack, c_stack) = stacks[0]
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -080084
Jean-Paul Calderone68a6f8f2013-03-01 17:56:22 -080085 stack = traceback.format_list(python_stack)[:-1]
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -080086
Jean-Paul Calderone68a6f8f2013-03-01 17:56:22 -080087 # c_stack looks something like this (interesting parts indicated
88 # with inserted arrows not part of the data):
89 #
90 # /home/exarkun/Projects/pyOpenSSL/branches/use-opentls/__pycache__/_cffi__x89095113xb9185b9b.so(+0x12cf) [0x7fe2e20582cf]
91 # /home/exarkun/Projects/cpython/2.7/python(PyCFunction_Call+0x8b) [0x56265a]
92 # /home/exarkun/Projects/cpython/2.7/python() [0x4d5f52]
93 # /home/exarkun/Projects/cpython/2.7/python(PyEval_EvalFrameEx+0x753b) [0x4d0e1e]
94 # /home/exarkun/Projects/cpython/2.7/python() [0x4d6419]
95 # /home/exarkun/Projects/cpython/2.7/python() [0x4d6129]
96 # /home/exarkun/Projects/cpython/2.7/python(PyEval_EvalFrameEx+0x753b) [0x4d0e1e]
97 # /home/exarkun/Projects/cpython/2.7/python(PyEval_EvalCodeEx+0x1043) [0x4d3726]
98 # /home/exarkun/Projects/cpython/2.7/python() [0x55fd51]
99 # /home/exarkun/Projects/cpython/2.7/python(PyObject_Call+0x7e) [0x420ee6]
100 # /home/exarkun/Projects/cpython/2.7/python(PyEval_CallObjectWithKeywords+0x158) [0x4d56ec]
101 # /home/exarkun/.local/lib/python2.7/site-packages/cffi-0.5-py2.7-linux-x86_64.egg/_cffi_backend.so(+0xe96e) [0x7fe2e38be96e]
102 # /usr/lib/x86_64-linux-gnu/libffi.so.6(ffi_closure_unix64_inner+0x1b9) [0x7fe2e36ad819]
103 # /usr/lib/x86_64-linux-gnu/libffi.so.6(ffi_closure_unix64+0x46) [0x7fe2e36adb7c]
104 # /lib/x86_64-linux-gnu/libcrypto.so.1.0.0(CRYPTO_malloc+0x64) [0x7fe2e1cef784] <------ end interesting
105 # /lib/x86_64-linux-gnu/libcrypto.so.1.0.0(lh_insert+0x16b) [0x7fe2e1d6a24b] .
106 # /lib/x86_64-linux-gnu/libcrypto.so.1.0.0(+0x61c18) [0x7fe2e1cf0c18] .
107 # /lib/x86_64-linux-gnu/libcrypto.so.1.0.0(+0x625ec) [0x7fe2e1cf15ec] .
108 # /lib/x86_64-linux-gnu/libcrypto.so.1.0.0(DSA_new_method+0xe6) [0x7fe2e1d524d6] .
109 # /lib/x86_64-linux-gnu/libcrypto.so.1.0.0(DSA_generate_parameters+0x3a) [0x7fe2e1d5364a] <------ begin interesting
110 # /home/exarkun/Projects/opentls/trunk/tls/c/__pycache__/_cffi__x305d4698xb539baaa.so(+0x1f397) [0x7fe2df84d397]
111 # /home/exarkun/Projects/cpython/2.7/python(PyCFunction_Call+0x8b) [0x56265a]
112 # /home/exarkun/Projects/cpython/2.7/python() [0x4d5f52]
113 # /home/exarkun/Projects/cpython/2.7/python(PyEval_EvalFrameEx+0x753b) [0x4d0e1e]
114 # /home/exarkun/Projects/cpython/2.7/python() [0x4d6419]
115 # ...
116 #
117 # Notice the stack is upside down compared to a Python traceback.
118 # Identify the start and end of interesting bits and stuff it into the stack we report.
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -0800119
Jean-Paul Calderonec2e8b412013-03-02 16:27:55 -0800120 saved = list(c_stack)
121
Jean-Paul Calderone68a6f8f2013-03-01 17:56:22 -0800122 # Figure the first interesting frame will be after a the cffi-compiled module
Jean-Paul Calderonec2e8b412013-03-02 16:27:55 -0800123 while c_stack and '/__pycache__/_cffi__' not in c_stack[-1]:
Jean-Paul Calderone68a6f8f2013-03-01 17:56:22 -0800124 c_stack.pop()
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -0800125
Jean-Paul Calderone68a6f8f2013-03-01 17:56:22 -0800126 # Figure the last interesting frame will always be CRYPTO_malloc,
127 # since that's where we hooked in to things.
Jean-Paul Calderonec2e8b412013-03-02 16:27:55 -0800128 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 -0800129 c_stack.pop(0)
130
Jean-Paul Calderonec2e8b412013-03-02 16:27:55 -0800131 if c_stack:
132 c_stack.reverse()
133 else:
134 c_stack = saved[::-1]
Jean-Paul Calderone68a6f8f2013-03-01 17:56:22 -0800135 stack.extend([frame + "\n" for frame in c_stack])
136
Jean-Paul Calderone2beac532013-03-03 17:30:36 -0800137 stack.insert(0, "Leaked (%s) at:\n")
Jean-Paul Calderone68a6f8f2013-03-01 17:56:22 -0800138 return "".join(stack)
139
Jean-Paul Calderone855331d2013-03-03 10:21:43 -0800140 if leaks:
Jean-Paul Calderone2beac532013-03-03 17:30:36 -0800141 unique_leaks = {}
Jean-Paul Calderone855331d2013-03-03 10:21:43 -0800142 for p in leaks:
Jean-Paul Calderone2beac532013-03-03 17:30:36 -0800143 size = memdbg.heap[p][-1][0]
144 new_leak = format_leak(p)
145 if new_leak not in unique_leaks:
146 unique_leaks[new_leak] = [(size, p)]
147 else:
148 unique_leaks[new_leak].append((size, p))
149 memdbg.free(p)
150
151 for (stack, allocs) in unique_leaks.iteritems():
152 allocs_accum = []
153 for (size, pointer) in allocs:
154
Jean-Paul Calderone9227c472013-12-31 13:47:36 -0500155 addr = int(ffi.cast('uintptr_t', pointer))
Jean-Paul Calderone2beac532013-03-03 17:30:36 -0800156 allocs_accum.append("%d@0x%x" % (size, addr))
157 allocs_report = ", ".join(sorted(allocs_accum))
158
Jean-Paul Calderone855331d2013-03-03 10:21:43 -0800159 result.addError(
160 self,
Jean-Paul Calderone2beac532013-03-03 17:30:36 -0800161 (None, Exception(stack % (allocs_report,)), None))
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -0800162
Jean-Paul Calderone855331d2013-03-03 10:21:43 -0800163
Hynek Schlawack4813c0e2015-04-16 13:38:01 -0400164 _tmpdir = None
165
166
167 @property
168 def tmpdir(self):
169 """
170 On demand create a temporary directory.
171 """
172 if self._tmpdir is not None:
173 return self._tmpdir
174
175 self._tmpdir = mkdtemp(dir=".")
176 return self._tmpdir
177
178
Jean-Paul Calderone855331d2013-03-03 10:21:43 -0800179 def tearDown(self):
180 """
Hynek Schlawack4813c0e2015-04-16 13:38:01 -0400181 Clean up any files or directories created using
182 :py:meth:`TestCase.mktemp`. Subclasses must invoke this method if they
183 override it or the cleanup will not occur.
Jean-Paul Calderone855331d2013-03-03 10:21:43 -0800184 """
Hynek Schlawack4813c0e2015-04-16 13:38:01 -0400185 if self._tmpdir is not None:
186 shutil.rmtree(self._tmpdir)
187
Jean-Paul Calderone1206daf2009-07-16 16:07:42 -0400188 try:
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -0500189 exception_from_error_queue(Error)
Jean-Paul Calderone24b64592010-08-12 10:43:09 -0400190 except Error:
191 e = sys.exc_info()[1]
Jean-Paul Calderone1206daf2009-07-16 16:07:42 -0400192 if e.args != ([],):
Hynek Schlawack4813c0e2015-04-16 13:38:01 -0400193 self.fail(
194 "Left over errors in OpenSSL error queue: " + repr(e)
195 )
Jean-Paul Calderone1206daf2009-07-16 16:07:42 -0400196
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400197
Jean-Paul Calderone8fb53182013-12-30 08:35:49 -0500198 def assertIsInstance(self, instance, classOrTuple, message=None):
199 """
200 Fail if C{instance} is not an instance of the given class or of
201 one of the given classes.
202
203 @param instance: the object to test the type (first argument of the
204 C{isinstance} call).
205 @type instance: any.
206 @param classOrTuple: the class or classes to test against (second
207 argument of the C{isinstance} call).
208 @type classOrTuple: class, type, or tuple.
209
210 @param message: Custom text to include in the exception text if the
211 assertion fails.
212 """
213 if not isinstance(instance, classOrTuple):
214 if message is None:
215 suffix = ""
216 else:
217 suffix = ": " + message
218 self.fail("%r is not an instance of %s%s" % (
219 instance, classOrTuple, suffix))
220
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800221
Jean-Paul Calderone060a57e2011-05-04 18:02:49 -0400222 def failUnlessIn(self, containee, container, msg=None):
223 """
Jonathan Ballet648875f2011-07-16 14:14:58 +0900224 Fail the test if :py:data:`containee` is not found in :py:data:`container`.
Jean-Paul Calderone060a57e2011-05-04 18:02:49 -0400225
Jonathan Ballet648875f2011-07-16 14:14:58 +0900226 :param containee: the value that should be in :py:class:`container`
Jonathan Ballet78b92a22011-07-16 08:07:26 +0900227 :param container: a sequence type, or in the case of a mapping type,
Jean-Paul Calderone060a57e2011-05-04 18:02:49 -0400228 will follow semantics of 'if key in dict.keys()'
Jonathan Ballet78b92a22011-07-16 08:07:26 +0900229 :param msg: if msg is None, then the failure message will be
Jean-Paul Calderone060a57e2011-05-04 18:02:49 -0400230 '%r not in %r' % (first, second)
231 """
232 if containee not in container:
233 raise self.failureException(msg or "%r not in %r"
234 % (containee, container))
235 return containee
236 assertIn = failUnlessIn
237
Jean-Paul Calderone15f36442014-05-01 07:58:02 -0400238 def assertNotIn(self, containee, container, msg=None):
239 """
240 Fail the test if C{containee} is found in C{container}.
241
242 @param containee: the value that should not be in C{container}
243 @param container: a sequence type, or in the case of a mapping type,
244 will follow semantics of 'if key in dict.keys()'
245 @param msg: if msg is None, then the failure message will be
246 '%r in %r' % (first, second)
247 """
248 if containee in container:
249 raise self.failureException(msg or "%r in %r"
250 % (containee, container))
251 return containee
252 failIfIn = assertNotIn
253
254
Jean-Paul Calderone77b3d082014-12-12 20:04:35 -0500255 def assertIs(self, first, second, msg=None):
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400256 """
Jonathan Ballet648875f2011-07-16 14:14:58 +0900257 Fail the test if :py:data:`first` is not :py:data:`second`. This is an
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400258 obect-identity-equality test, not an object equality
Jonathan Ballet648875f2011-07-16 14:14:58 +0900259 (i.e. :py:func:`__eq__`) test.
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400260
Jonathan Ballet78b92a22011-07-16 08:07:26 +0900261 :param msg: if msg is None, then the failure message will be
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400262 '%r is not %r' % (first, second)
263 """
264 if first is not second:
265 raise self.failureException(msg or '%r is not %r' % (first, second))
266 return first
Jean-Paul Calderone77b3d082014-12-12 20:04:35 -0500267 assertIdentical = failUnlessIdentical = assertIs
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400268
269
Jean-Paul Calderone77b3d082014-12-12 20:04:35 -0500270 def assertIsNot(self, first, second, msg=None):
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400271 """
Jonathan Ballet648875f2011-07-16 14:14:58 +0900272 Fail the test if :py:data:`first` is :py:data:`second`. This is an
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400273 obect-identity-equality test, not an object equality
Jonathan Ballet648875f2011-07-16 14:14:58 +0900274 (i.e. :py:func:`__eq__`) test.
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400275
Jonathan Ballet78b92a22011-07-16 08:07:26 +0900276 :param msg: if msg is None, then the failure message will be
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400277 '%r is %r' % (first, second)
278 """
279 if first is second:
280 raise self.failureException(msg or '%r is %r' % (first, second))
281 return first
Jean-Paul Calderone77b3d082014-12-12 20:04:35 -0500282 assertNotIdentical = failIfIdentical = assertIsNot
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400283
284
285 def failUnlessRaises(self, exception, f, *args, **kwargs):
286 """
Jonathan Ballet648875f2011-07-16 14:14:58 +0900287 Fail the test unless calling the function :py:data:`f` with the given
288 :py:data:`args` and :py:data:`kwargs` raises :py:data:`exception`. The
289 failure will report the traceback and call stack of the unexpected
290 exception.
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400291
Jonathan Ballet78b92a22011-07-16 08:07:26 +0900292 :param exception: exception type that is to be expected
293 :param f: the function to call
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400294
Jonathan Ballet78b92a22011-07-16 08:07:26 +0900295 :return: The raised exception instance, if it is of the given type.
296 :raise self.failureException: Raised if the function call does
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400297 not raise an exception or if it raises an exception of a
298 different type.
299 """
300 try:
301 result = f(*args, **kwargs)
Jean-Paul Calderone24b64592010-08-12 10:43:09 -0400302 except exception:
303 inst = sys.exc_info()[1]
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400304 return inst
305 except:
Rick Dean47262da2009-07-08 16:17:17 -0500306 raise self.failureException('%s raised instead of %s'
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400307 % (sys.exc_info()[0],
308 exception.__name__,
Rick Dean47262da2009-07-08 16:17:17 -0500309 ))
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400310 else:
311 raise self.failureException('%s not raised (%r returned)'
312 % (exception.__name__, result))
313 assertRaises = failUnlessRaises
314
315
Hynek Schlawack4813c0e2015-04-16 13:38:01 -0400316 def mktemp(self):
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400317 """
Hynek Schlawack4813c0e2015-04-16 13:38:01 -0400318 Return UTF-8-encoded bytes of a path to a tmp file.
319
320 The file will be cleaned up after the test run.
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400321 """
Hynek Schlawack4813c0e2015-04-16 13:38:01 -0400322 return mktemp(dir=self.tmpdir).encode("utf-8")
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400323
324
Jean-Paul Calderone68649052009-07-17 21:14:27 -0400325 # Other stuff
326 def assertConsistentType(self, theType, name, *constructionArgs):
327 """
Jonathan Ballet648875f2011-07-16 14:14:58 +0900328 Perform various assertions about :py:data:`theType` to ensure that it is a
Jean-Paul Calderone68649052009-07-17 21:14:27 -0400329 well-defined type. This is useful for extension types, where it's
330 pretty easy to do something wacky. If something about the type is
331 unusual, an exception will be raised.
332
Jonathan Ballet78b92a22011-07-16 08:07:26 +0900333 :param theType: The type object about which to make assertions.
334 :param name: A string giving the name of the type.
Jonathan Ballet648875f2011-07-16 14:14:58 +0900335 :param constructionArgs: Positional arguments to use with :py:data:`theType` to
Jean-Paul Calderone68649052009-07-17 21:14:27 -0400336 create an instance of it.
337 """
338 self.assertEqual(theType.__name__, name)
339 self.assertTrue(isinstance(theType, type))
340 instance = theType(*constructionArgs)
341 self.assertIdentical(type(instance), theType)
Jean-Paul Calderone9c7f0692014-04-30 18:17:19 -0400342
343
344
345class EqualityTestsMixin(object):
346 """
347 A mixin defining tests for the standard implementation of C{==} and C{!=}.
348 """
349 def anInstance(self):
350 """
351 Return an instance of the class under test. Each call to this method
352 must return a different object. All objects returned must be equal to
353 each other.
354 """
355 raise NotImplementedError()
356
357
358 def anotherInstance(self):
359 """
360 Return an instance of the class under test. Each call to this method
361 must return a different object. The objects must not be equal to the
362 objects returned by C{anInstance}. They may or may not be equal to
363 each other (they will not be compared against each other).
364 """
365 raise NotImplementedError()
366
367
368 def test_identicalEq(self):
369 """
370 An object compares equal to itself using the C{==} operator.
371 """
372 o = self.anInstance()
373 self.assertTrue(o == o)
374
375
376 def test_identicalNe(self):
377 """
378 An object doesn't compare not equal to itself using the C{!=} operator.
379 """
380 o = self.anInstance()
381 self.assertFalse(o != o)
382
383
384 def test_sameEq(self):
385 """
386 Two objects that are equal to each other compare equal to each other
387 using the C{==} operator.
388 """
389 a = self.anInstance()
390 b = self.anInstance()
391 self.assertTrue(a == b)
392
393
394 def test_sameNe(self):
395 """
396 Two objects that are equal to each other do not compare not equal to
397 each other using the C{!=} operator.
398 """
399 a = self.anInstance()
400 b = self.anInstance()
401 self.assertFalse(a != b)
402
403
404 def test_differentEq(self):
405 """
406 Two objects that are not equal to each other do not compare equal to
407 each other using the C{==} operator.
408 """
409 a = self.anInstance()
410 b = self.anotherInstance()
411 self.assertFalse(a == b)
412
413
414 def test_differentNe(self):
415 """
416 Two objects that are not equal to each other compare not equal to each
417 other using the C{!=} operator.
418 """
419 a = self.anInstance()
420 b = self.anotherInstance()
421 self.assertTrue(a != b)
422
423
424 def test_anotherTypeEq(self):
425 """
426 The object does not compare equal to an object of an unrelated type
427 (which does not implement the comparison) using the C{==} operator.
428 """
429 a = self.anInstance()
430 b = object()
431 self.assertFalse(a == b)
432
433
434 def test_anotherTypeNe(self):
435 """
436 The object compares not equal to an object of an unrelated type (which
437 does not implement the comparison) using the C{!=} operator.
438 """
439 a = self.anInstance()
440 b = object()
441 self.assertTrue(a != b)
442
443
444 def test_delegatedEq(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 __eq__(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])
457
458
459 def test_delegateNe(self):
460 """
461 The result of comparison using C{!=} is delegated to the right-hand
462 operand if it is of an unrelated type.
463 """
464 class Delegate(object):
465 def __ne__(self, other):
466 # Do something crazy and obvious.
467 return [self]
468
469 a = self.anInstance()
470 b = Delegate()
471 self.assertEqual(a != b, [b])
Jean-Paul Calderone6462b072015-03-29 07:03:11 -0400472
473
474# The type name expected in warnings about using the wrong string type.
475if PY3:
476 WARNING_TYPE_EXPECTED = "str"
477else:
478 WARNING_TYPE_EXPECTED = "unicode"