import os import random import subprocess import sys import unittest try: import _ssl except ImportError: _ssl = None try: import ssl except ImportError: ssl = None try: import ctypes except ImportError: # Python 2.4 ctypes = None import mitogen import testlib import plain_old_module def _find_ssl_linux(): proc = subprocess.Popen( ['ldd', _ssl.__file__], stdout=subprocess.PIPE, stderr=subprocess.PIPE, ) b_stdout, b_stderr = proc.communicate() assert proc.returncode == 0 assert b_stderr.decode() == '' for line in b_stdout.decode().splitlines(): bits = line.split() if bits[0].startswith('libssl'): return bits[2] def _find_ssl_darwin(): proc = subprocess.Popen( ['otool', '-l', _ssl.__file__], stdout=subprocess.PIPE, stderr=subprocess.PIPE, ) b_stdout, b_stderr = proc.communicate() assert proc.returncode == 0 assert b_stderr.decode() == '' for line in b_stdout.decode().splitlines(): bits = line.split() if bits[0] == 'name' and 'libssl' in bits[1]: return bits[1] if ctypes and sys.platform.startswith('linux'): LIBSSL_PATH = _find_ssl_linux() elif ctypes and sys.platform == 'darwin': LIBSSL_PATH = _find_ssl_darwin() else: LIBSSL_PATH = None if ctypes and LIBSSL_PATH: c_ssl = ctypes.CDLL(LIBSSL_PATH) c_ssl.RAND_pseudo_bytes.argtypes = [ctypes.c_char_p, ctypes.c_int] c_ssl.RAND_pseudo_bytes.restype = ctypes.c_int def ping(): return 123 def random_random(): return random.random() def RAND_pseudo_bytes(n=32): buf = ctypes.create_string_buffer(n) assert 1 == c_ssl.RAND_pseudo_bytes(buf, n) return buf[:] def exercise_importer(n): """ Ensure the forked child has a sensible importer. """ sys.path.remove(testlib.DATA_DIR) import simple_pkg.a return simple_pkg.a.subtract_one_add_two(n) skipIfUnsupported = unittest.skipIf( condition=(not mitogen.fork.FORK_SUPPORTED), reason="mitogen.fork unsupported on this platform" ) class ForkTest(testlib.RouterMixin, testlib.TestCase): def test_okay(self): context = self.router.fork() self.assertNotEqual(context.call(os.getpid), os.getpid()) self.assertEqual(context.call(os.getppid), os.getpid()) def test_random_module_diverges(self): context = self.router.fork() self.assertNotEqual(context.call(random_random), random_random()) @unittest.skipIf( condition=LIBSSL_PATH is None or ctypes is None, reason='cant test libssl on this platform', ) def test_ssl_module_diverges(self): # Ensure generator state is initialized. RAND_pseudo_bytes() context = self.router.fork() self.assertNotEqual(context.call(RAND_pseudo_bytes), RAND_pseudo_bytes()) def test_importer(self): context = self.router.fork() self.assertEqual(2, context.call(exercise_importer, 1)) def test_on_start(self): recv = mitogen.core.Receiver(self.router) def on_start(econtext): sender = mitogen.core.Sender(econtext.parent, recv.handle) sender.send(123) context = self.router.fork(on_start=on_start) self.assertEqual(123, recv.get().unpickle()) ForkTest = skipIfUnsupported(ForkTest) class DoubleChildTest(testlib.RouterMixin, testlib.TestCase): def test_okay(self): # When forking from the master process, Mitogen had nothing to do with # setting up stdio -- that was inherited wherever the Master is running # (supervisor, iTerm, etc). When forking from a Mitogen child context # however, Mitogen owns all of fd 0, 1, and 2, and during the fork # procedure, it deletes all of these descriptors. That leaves the # process in a weird state that must be handled by some combination of # fork.py and ExternalContext.main(). # Below we simply test whether ExternalContext.main() managed to boot # successfully. In future, we need lots more tests. c1 = self.router.fork() c2 = self.router.fork(via=c1) self.assertEqual(123, c2.call(ping)) def test_importer(self): c1 = self.router.fork(name='c1') c2 = self.router.fork(name='c2', via=c1) self.assertEqual(2, c2.call(exercise_importer, 1)) DoubleChildTest = skipIfUnsupported(DoubleChildTest)