blob: 8b034dc478cda7c04393d526f144de4b6419e580 [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
28# This is the UTF-8 encoding of the SNOWMAN unicode code point.
29NON_ASCII = b("\xe2\x98\x83").decode("utf-8")
30
31
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -040032class TestCase(TestCase):
33 """
Hynek Schlawack4813c0e2015-04-16 13:38:01 -040034 :py:class:`TestCase` adds useful testing functionality beyond what is
35 available from the standard library :py:class:`unittest.TestCase`.
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -040036 """
Jean-Paul Calderone855331d2013-03-03 10:21:43 -080037 def run(self, result):
Jean-Paul Calderone68703ed2013-03-04 12:23:44 -080038 run = super(TestCase, self).run
39 if memdbg.heap is None:
40 return run(result)
41
Jean-Paul Calderone855331d2013-03-03 10:21:43 -080042 # Run the test as usual
43 before = set(memdbg.heap)
Jean-Paul Calderone68703ed2013-03-04 12:23:44 -080044 run(result)
Jean-Paul Calderone855331d2013-03-03 10:21:43 -080045
46 # Clean up some long-lived allocations so they won't be reported as
47 # memory leaks.
Jean-Paul Calderone9227c472013-12-31 13:47:36 -050048 lib.CRYPTO_cleanup_all_ex_data()
49 lib.ERR_remove_thread_state(ffi.NULL)
Jean-Paul Calderone855331d2013-03-03 10:21:43 -080050 after = set(memdbg.heap)
51
52 if not after - before:
53 # No leaks, fast succeed
54 return
55
56 if result.wasSuccessful():
57 # If it passed, run it again with memory debugging
58 before = set(memdbg.heap)
Jean-Paul Calderone68703ed2013-03-04 12:23:44 -080059 run(result)
Jean-Paul Calderone855331d2013-03-03 10:21:43 -080060
61 # Clean up some long-lived allocations so they won't be reported as
62 # memory leaks.
Jean-Paul Calderone3f93d212014-01-01 12:36:53 -050063 lib.CRYPTO_cleanup_all_ex_data()
64 lib.ERR_remove_thread_state(ffi.NULL)
Jean-Paul Calderone855331d2013-03-03 10:21:43 -080065
66 after = set(memdbg.heap)
67
68 self._reportLeaks(after - before, result)
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -080069
Jean-Paul Calderone855331d2013-03-03 10:21:43 -080070 def _reportLeaks(self, leaks, result):
Jean-Paul Calderone68a6f8f2013-03-01 17:56:22 -080071 def format_leak(p):
72 stacks = memdbg.heap[p]
73 # Eventually look at multiple stacks for the realloc() case. For
74 # now just look at the original allocation location.
Jean-Paul Calderonec2e8b412013-03-02 16:27:55 -080075 (size, python_stack, c_stack) = stacks[0]
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -080076
Jean-Paul Calderone68a6f8f2013-03-01 17:56:22 -080077 stack = traceback.format_list(python_stack)[:-1]
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -080078
Jean-Paul Calderone68a6f8f2013-03-01 17:56:22 -080079 # c_stack looks something like this (interesting parts indicated
80 # with inserted arrows not part of the data):
81 #
82 # /home/exarkun/Projects/pyOpenSSL/branches/use-opentls/__pycache__/_cffi__x89095113xb9185b9b.so(+0x12cf) [0x7fe2e20582cf]
83 # /home/exarkun/Projects/cpython/2.7/python(PyCFunction_Call+0x8b) [0x56265a]
84 # /home/exarkun/Projects/cpython/2.7/python() [0x4d5f52]
85 # /home/exarkun/Projects/cpython/2.7/python(PyEval_EvalFrameEx+0x753b) [0x4d0e1e]
86 # /home/exarkun/Projects/cpython/2.7/python() [0x4d6419]
87 # /home/exarkun/Projects/cpython/2.7/python() [0x4d6129]
88 # /home/exarkun/Projects/cpython/2.7/python(PyEval_EvalFrameEx+0x753b) [0x4d0e1e]
89 # /home/exarkun/Projects/cpython/2.7/python(PyEval_EvalCodeEx+0x1043) [0x4d3726]
90 # /home/exarkun/Projects/cpython/2.7/python() [0x55fd51]
91 # /home/exarkun/Projects/cpython/2.7/python(PyObject_Call+0x7e) [0x420ee6]
92 # /home/exarkun/Projects/cpython/2.7/python(PyEval_CallObjectWithKeywords+0x158) [0x4d56ec]
93 # /home/exarkun/.local/lib/python2.7/site-packages/cffi-0.5-py2.7-linux-x86_64.egg/_cffi_backend.so(+0xe96e) [0x7fe2e38be96e]
94 # /usr/lib/x86_64-linux-gnu/libffi.so.6(ffi_closure_unix64_inner+0x1b9) [0x7fe2e36ad819]
95 # /usr/lib/x86_64-linux-gnu/libffi.so.6(ffi_closure_unix64+0x46) [0x7fe2e36adb7c]
96 # /lib/x86_64-linux-gnu/libcrypto.so.1.0.0(CRYPTO_malloc+0x64) [0x7fe2e1cef784] <------ end interesting
97 # /lib/x86_64-linux-gnu/libcrypto.so.1.0.0(lh_insert+0x16b) [0x7fe2e1d6a24b] .
98 # /lib/x86_64-linux-gnu/libcrypto.so.1.0.0(+0x61c18) [0x7fe2e1cf0c18] .
99 # /lib/x86_64-linux-gnu/libcrypto.so.1.0.0(+0x625ec) [0x7fe2e1cf15ec] .
100 # /lib/x86_64-linux-gnu/libcrypto.so.1.0.0(DSA_new_method+0xe6) [0x7fe2e1d524d6] .
101 # /lib/x86_64-linux-gnu/libcrypto.so.1.0.0(DSA_generate_parameters+0x3a) [0x7fe2e1d5364a] <------ begin interesting
102 # /home/exarkun/Projects/opentls/trunk/tls/c/__pycache__/_cffi__x305d4698xb539baaa.so(+0x1f397) [0x7fe2df84d397]
103 # /home/exarkun/Projects/cpython/2.7/python(PyCFunction_Call+0x8b) [0x56265a]
104 # /home/exarkun/Projects/cpython/2.7/python() [0x4d5f52]
105 # /home/exarkun/Projects/cpython/2.7/python(PyEval_EvalFrameEx+0x753b) [0x4d0e1e]
106 # /home/exarkun/Projects/cpython/2.7/python() [0x4d6419]
107 # ...
108 #
109 # Notice the stack is upside down compared to a Python traceback.
Alex Gaynor75690d92015-09-05 10:14:52 -0400110 # Identify the start and end of interesting bits and stuff it into
111 # the stack we report.
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -0800112
Jean-Paul Calderonec2e8b412013-03-02 16:27:55 -0800113 saved = list(c_stack)
114
Alex Gaynor75690d92015-09-05 10:14:52 -0400115 # Figure the first interesting frame will be after a the
116 # cffi-compiled module
Jean-Paul Calderonec2e8b412013-03-02 16:27:55 -0800117 while c_stack and '/__pycache__/_cffi__' not in c_stack[-1]:
Jean-Paul Calderone68a6f8f2013-03-01 17:56:22 -0800118 c_stack.pop()
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -0800119
Jean-Paul Calderone68a6f8f2013-03-01 17:56:22 -0800120 # Figure the last interesting frame will always be CRYPTO_malloc,
121 # since that's where we hooked in to things.
Jean-Paul Calderonec2e8b412013-03-02 16:27:55 -0800122 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 -0800123 c_stack.pop(0)
124
Jean-Paul Calderonec2e8b412013-03-02 16:27:55 -0800125 if c_stack:
126 c_stack.reverse()
127 else:
128 c_stack = saved[::-1]
Jean-Paul Calderone68a6f8f2013-03-01 17:56:22 -0800129 stack.extend([frame + "\n" for frame in c_stack])
130
Jean-Paul Calderone2beac532013-03-03 17:30:36 -0800131 stack.insert(0, "Leaked (%s) at:\n")
Jean-Paul Calderone68a6f8f2013-03-01 17:56:22 -0800132 return "".join(stack)
133
Jean-Paul Calderone855331d2013-03-03 10:21:43 -0800134 if leaks:
Jean-Paul Calderone2beac532013-03-03 17:30:36 -0800135 unique_leaks = {}
Jean-Paul Calderone855331d2013-03-03 10:21:43 -0800136 for p in leaks:
Jean-Paul Calderone2beac532013-03-03 17:30:36 -0800137 size = memdbg.heap[p][-1][0]
138 new_leak = format_leak(p)
139 if new_leak not in unique_leaks:
140 unique_leaks[new_leak] = [(size, p)]
141 else:
142 unique_leaks[new_leak].append((size, p))
143 memdbg.free(p)
144
145 for (stack, allocs) in unique_leaks.iteritems():
146 allocs_accum = []
147 for (size, pointer) in allocs:
148
Jean-Paul Calderone9227c472013-12-31 13:47:36 -0500149 addr = int(ffi.cast('uintptr_t', pointer))
Jean-Paul Calderone2beac532013-03-03 17:30:36 -0800150 allocs_accum.append("%d@0x%x" % (size, addr))
151 allocs_report = ", ".join(sorted(allocs_accum))
152
Jean-Paul Calderone855331d2013-03-03 10:21:43 -0800153 result.addError(
154 self,
Jean-Paul Calderone2beac532013-03-03 17:30:36 -0800155 (None, Exception(stack % (allocs_report,)), None))
Jean-Paul Calderonef6745b32013-03-01 15:08:46 -0800156
Hynek Schlawack4813c0e2015-04-16 13:38:01 -0400157 _tmpdir = None
158
Hynek Schlawack4813c0e2015-04-16 13:38:01 -0400159 @property
160 def tmpdir(self):
161 """
162 On demand create a temporary directory.
163 """
164 if self._tmpdir is not None:
165 return self._tmpdir
166
167 self._tmpdir = mkdtemp(dir=".")
168 return self._tmpdir
169
Jean-Paul Calderone855331d2013-03-03 10:21:43 -0800170 def tearDown(self):
171 """
Hynek Schlawack4813c0e2015-04-16 13:38:01 -0400172 Clean up any files or directories created using
173 :py:meth:`TestCase.mktemp`. Subclasses must invoke this method if they
174 override it or the cleanup will not occur.
Jean-Paul Calderone855331d2013-03-03 10:21:43 -0800175 """
Hynek Schlawack4813c0e2015-04-16 13:38:01 -0400176 if self._tmpdir is not None:
177 shutil.rmtree(self._tmpdir)
178
Jean-Paul Calderone1206daf2009-07-16 16:07:42 -0400179 try:
Jean-Paul Calderonec86bb7d2013-12-29 10:25:59 -0500180 exception_from_error_queue(Error)
Jean-Paul Calderone24b64592010-08-12 10:43:09 -0400181 except Error:
182 e = sys.exc_info()[1]
Jean-Paul Calderone1206daf2009-07-16 16:07:42 -0400183 if e.args != ([],):
Hynek Schlawack4813c0e2015-04-16 13:38:01 -0400184 self.fail(
185 "Left over errors in OpenSSL error queue: " + repr(e)
186 )
Jean-Paul Calderone1206daf2009-07-16 16:07:42 -0400187
Jean-Paul Calderone8fb53182013-12-30 08:35:49 -0500188 def assertIsInstance(self, instance, classOrTuple, message=None):
189 """
190 Fail if C{instance} is not an instance of the given class or of
191 one of the given classes.
192
193 @param instance: the object to test the type (first argument of the
194 C{isinstance} call).
195 @type instance: any.
196 @param classOrTuple: the class or classes to test against (second
197 argument of the C{isinstance} call).
198 @type classOrTuple: class, type, or tuple.
199
200 @param message: Custom text to include in the exception text if the
201 assertion fails.
202 """
203 if not isinstance(instance, classOrTuple):
204 if message is None:
205 suffix = ""
206 else:
207 suffix = ": " + message
208 self.fail("%r is not an instance of %s%s" % (
Alex Gaynor75690d92015-09-05 10:14:52 -0400209 instance, classOrTuple, suffix))
Jean-Paul Calderoneabfbab62013-02-09 21:25:02 -0800210
Jean-Paul Calderone060a57e2011-05-04 18:02:49 -0400211 def failUnlessIn(self, containee, container, msg=None):
212 """
Jonathan Ballet648875f2011-07-16 14:14:58 +0900213 Fail the test if :py:data:`containee` is not found in :py:data:`container`.
Jean-Paul Calderone060a57e2011-05-04 18:02:49 -0400214
Jonathan Ballet648875f2011-07-16 14:14:58 +0900215 :param containee: the value that should be in :py:class:`container`
Jonathan Ballet78b92a22011-07-16 08:07:26 +0900216 :param container: a sequence type, or in the case of a mapping type,
Jean-Paul Calderone060a57e2011-05-04 18:02:49 -0400217 will follow semantics of 'if key in dict.keys()'
Jonathan Ballet78b92a22011-07-16 08:07:26 +0900218 :param msg: if msg is None, then the failure message will be
Jean-Paul Calderone060a57e2011-05-04 18:02:49 -0400219 '%r not in %r' % (first, second)
220 """
221 if containee not in container:
222 raise self.failureException(msg or "%r not in %r"
223 % (containee, container))
224 return containee
225 assertIn = failUnlessIn
226
Jean-Paul Calderone15f36442014-05-01 07:58:02 -0400227 def assertNotIn(self, containee, container, msg=None):
228 """
229 Fail the test if C{containee} is found in C{container}.
230
231 @param containee: the value that should not be in C{container}
232 @param container: a sequence type, or in the case of a mapping type,
233 will follow semantics of 'if key in dict.keys()'
234 @param msg: if msg is None, then the failure message will be
235 '%r in %r' % (first, second)
236 """
237 if containee in container:
238 raise self.failureException(msg or "%r in %r"
239 % (containee, container))
240 return containee
241 failIfIn = assertNotIn
242
Jean-Paul Calderone77b3d082014-12-12 20:04:35 -0500243 def assertIs(self, first, second, msg=None):
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400244 """
Jonathan Ballet648875f2011-07-16 14:14:58 +0900245 Fail the test if :py:data:`first` is not :py:data:`second`. This is an
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400246 obect-identity-equality test, not an object equality
Jonathan Ballet648875f2011-07-16 14:14:58 +0900247 (i.e. :py:func:`__eq__`) test.
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400248
Jonathan Ballet78b92a22011-07-16 08:07:26 +0900249 :param msg: if msg is None, then the failure message will be
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400250 '%r is not %r' % (first, second)
251 """
252 if first is not second:
253 raise self.failureException(msg or '%r is not %r' % (first, second))
254 return first
Jean-Paul Calderone77b3d082014-12-12 20:04:35 -0500255 assertIdentical = failUnlessIdentical = assertIs
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400256
Jean-Paul Calderone77b3d082014-12-12 20:04:35 -0500257 def assertIsNot(self, first, second, msg=None):
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400258 """
Jonathan Ballet648875f2011-07-16 14:14:58 +0900259 Fail the test if :py:data:`first` is :py:data:`second`. This is an
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400260 obect-identity-equality test, not an object equality
Jonathan Ballet648875f2011-07-16 14:14:58 +0900261 (i.e. :py:func:`__eq__`) test.
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400262
Jonathan Ballet78b92a22011-07-16 08:07:26 +0900263 :param msg: if msg is None, then the failure message will be
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400264 '%r is %r' % (first, second)
265 """
266 if first is second:
267 raise self.failureException(msg or '%r is %r' % (first, second))
268 return first
Jean-Paul Calderone77b3d082014-12-12 20:04:35 -0500269 assertNotIdentical = failIfIdentical = assertIsNot
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400270
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400271 def failUnlessRaises(self, exception, f, *args, **kwargs):
272 """
Jonathan Ballet648875f2011-07-16 14:14:58 +0900273 Fail the test unless calling the function :py:data:`f` with the given
274 :py:data:`args` and :py:data:`kwargs` raises :py:data:`exception`. The
275 failure will report the traceback and call stack of the unexpected
276 exception.
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400277
Jonathan Ballet78b92a22011-07-16 08:07:26 +0900278 :param exception: exception type that is to be expected
279 :param f: the function to call
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400280
Jonathan Ballet78b92a22011-07-16 08:07:26 +0900281 :return: The raised exception instance, if it is of the given type.
282 :raise self.failureException: Raised if the function call does
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400283 not raise an exception or if it raises an exception of a
284 different type.
285 """
286 try:
287 result = f(*args, **kwargs)
Jean-Paul Calderone24b64592010-08-12 10:43:09 -0400288 except exception:
289 inst = sys.exc_info()[1]
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400290 return inst
291 except:
Rick Dean47262da2009-07-08 16:17:17 -0500292 raise self.failureException('%s raised instead of %s'
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400293 % (sys.exc_info()[0],
294 exception.__name__,
Alex Gaynor75690d92015-09-05 10:14:52 -0400295 ))
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400296 else:
297 raise self.failureException('%s not raised (%r returned)'
298 % (exception.__name__, result))
299 assertRaises = failUnlessRaises
300
Hynek Schlawack4813c0e2015-04-16 13:38:01 -0400301 def mktemp(self):
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400302 """
Hynek Schlawack4813c0e2015-04-16 13:38:01 -0400303 Return UTF-8-encoded bytes of a path to a tmp file.
304
305 The file will be cleaned up after the test run.
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400306 """
Hynek Schlawack4813c0e2015-04-16 13:38:01 -0400307 return mktemp(dir=self.tmpdir).encode("utf-8")
Jean-Paul Calderone0ef63ed2009-07-05 13:05:45 -0400308
Jean-Paul Calderone68649052009-07-17 21:14:27 -0400309 # Other stuff
310 def assertConsistentType(self, theType, name, *constructionArgs):
311 """
Jonathan Ballet648875f2011-07-16 14:14:58 +0900312 Perform various assertions about :py:data:`theType` to ensure that it is a
Jean-Paul Calderone68649052009-07-17 21:14:27 -0400313 well-defined type. This is useful for extension types, where it's
314 pretty easy to do something wacky. If something about the type is
315 unusual, an exception will be raised.
316
Jonathan Ballet78b92a22011-07-16 08:07:26 +0900317 :param theType: The type object about which to make assertions.
318 :param name: A string giving the name of the type.
Jonathan Ballet648875f2011-07-16 14:14:58 +0900319 :param constructionArgs: Positional arguments to use with :py:data:`theType` to
Jean-Paul Calderone68649052009-07-17 21:14:27 -0400320 create an instance of it.
321 """
322 self.assertEqual(theType.__name__, name)
323 self.assertTrue(isinstance(theType, type))
324 instance = theType(*constructionArgs)
325 self.assertIdentical(type(instance), theType)
Jean-Paul Calderone9c7f0692014-04-30 18:17:19 -0400326
327
Jean-Paul Calderone9c7f0692014-04-30 18:17:19 -0400328class EqualityTestsMixin(object):
329 """
330 A mixin defining tests for the standard implementation of C{==} and C{!=}.
331 """
332 def anInstance(self):
333 """
334 Return an instance of the class under test. Each call to this method
335 must return a different object. All objects returned must be equal to
336 each other.
337 """
338 raise NotImplementedError()
339
Jean-Paul Calderone9c7f0692014-04-30 18:17:19 -0400340 def anotherInstance(self):
341 """
342 Return an instance of the class under test. Each call to this method
343 must return a different object. The objects must not be equal to the
344 objects returned by C{anInstance}. They may or may not be equal to
345 each other (they will not be compared against each other).
346 """
347 raise NotImplementedError()
348
Jean-Paul Calderone9c7f0692014-04-30 18:17:19 -0400349 def test_identicalEq(self):
350 """
351 An object compares equal to itself using the C{==} operator.
352 """
353 o = self.anInstance()
354 self.assertTrue(o == o)
355
Jean-Paul Calderone9c7f0692014-04-30 18:17:19 -0400356 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
Jean-Paul Calderone9c7f0692014-04-30 18:17:19 -0400363 def test_sameEq(self):
364 """
365 Two objects that are equal to each other compare equal to each other
366 using the C{==} operator.
367 """
368 a = self.anInstance()
369 b = self.anInstance()
370 self.assertTrue(a == b)
371
Jean-Paul Calderone9c7f0692014-04-30 18:17:19 -0400372 def test_sameNe(self):
373 """
374 Two objects that are equal to each other do not compare not equal to
375 each other using the C{!=} operator.
376 """
377 a = self.anInstance()
378 b = self.anInstance()
379 self.assertFalse(a != b)
380
Jean-Paul Calderone9c7f0692014-04-30 18:17:19 -0400381 def test_differentEq(self):
382 """
383 Two objects that are not equal to each other do not compare equal to
384 each other using the C{==} operator.
385 """
386 a = self.anInstance()
387 b = self.anotherInstance()
388 self.assertFalse(a == b)
389
Jean-Paul Calderone9c7f0692014-04-30 18:17:19 -0400390 def test_differentNe(self):
391 """
392 Two objects that are not equal to each other compare not equal to each
393 other using the C{!=} operator.
394 """
395 a = self.anInstance()
396 b = self.anotherInstance()
397 self.assertTrue(a != b)
398
Jean-Paul Calderone9c7f0692014-04-30 18:17:19 -0400399 def test_anotherTypeEq(self):
400 """
401 The object does not compare equal to an object of an unrelated type
402 (which does not implement the comparison) using the C{==} operator.
403 """
404 a = self.anInstance()
405 b = object()
406 self.assertFalse(a == b)
407
Jean-Paul Calderone9c7f0692014-04-30 18:17:19 -0400408 def test_anotherTypeNe(self):
409 """
410 The object compares not equal to an object of an unrelated type (which
411 does not implement the comparison) using the C{!=} operator.
412 """
413 a = self.anInstance()
414 b = object()
415 self.assertTrue(a != b)
416
Jean-Paul Calderone9c7f0692014-04-30 18:17:19 -0400417 def test_delegatedEq(self):
418 """
419 The result of comparison using C{==} is delegated to the right-hand
420 operand if it is of an unrelated type.
421 """
422 class Delegate(object):
423 def __eq__(self, other):
424 # Do something crazy and obvious.
425 return [self]
426
427 a = self.anInstance()
428 b = Delegate()
429 self.assertEqual(a == b, [b])
430
Jean-Paul Calderone9c7f0692014-04-30 18:17:19 -0400431 def test_delegateNe(self):
432 """
433 The result of comparison using C{!=} is delegated to the right-hand
434 operand if it is of an unrelated type.
435 """
436 class Delegate(object):
437 def __ne__(self, other):
438 # Do something crazy and obvious.
439 return [self]
440
441 a = self.anInstance()
442 b = Delegate()
443 self.assertEqual(a != b, [b])
Jean-Paul Calderone6462b072015-03-29 07:03:11 -0400444
445
446# The type name expected in warnings about using the wrong string type.
447if PY3:
448 WARNING_TYPE_EXPECTED = "str"
449else:
450 WARNING_TYPE_EXPECTED = "unicode"