diff options
Diffstat (limited to 'python/macholib/macholib_tests/test_dyld.py')
-rw-r--r-- | python/macholib/macholib_tests/test_dyld.py | 450 |
1 files changed, 450 insertions, 0 deletions
diff --git a/python/macholib/macholib_tests/test_dyld.py b/python/macholib/macholib_tests/test_dyld.py new file mode 100644 index 000000000..7b41758d9 --- /dev/null +++ b/python/macholib/macholib_tests/test_dyld.py @@ -0,0 +1,450 @@ +from macholib import dyld + +import sys +import os +import functools + +if sys.version_info[:2] <= (2,6): + import unittest2 as unittest +else: + import unittest + +class DyldPatcher (object): + def __init__(self): + self.calls = [] + self.patched = {} + + def clear_calls(self): + self.calls = [] + + def cleanup(self): + for name in self.patched: + setattr(dyld, name, self.patched[name]) + + def log_calls(self, name): + if name in self.patched: + return + + self.patched[name] = getattr(dyld, name) + + + @functools.wraps(self.patched[name]) + def wrapper(*args, **kwds): + self.calls.append((name, args, kwds)) + return self.patched[name](*args, **kwds) + + setattr(dyld, name, wrapper) + + +class TestDyld (unittest.TestCase): + if not hasattr(unittest.TestCase, 'assertIsInstance'): + def assertIsInstance(self, value, types, message=None): + self.assertTrue(isinstance(value, types), + message or "%r is not an instance of %r"%(value, types)) + + def setUp(self): + self._environ = os.environ + os.environ = dict([(k, os.environ[k]) for k in os.environ if 'DYLD' not in k]) + self._dyld_env = dyld._dyld_env + self._dyld_image_suffix = dyld.dyld_image_suffix + + def tearDown(self): + dyld._dyld_env = self._dyld_env + dyld.dyld_image_suffix = self._dyld_image_suffix + os.environ = self._environ + + if sys.version_info[0] == 2: + def test_ensure_utf8(self): + self.assertEqual(dyld._ensure_utf8("hello"), "hello") + self.assertEqual(dyld._ensure_utf8("hello".decode('utf-8')), "hello") + self.assertEqual(dyld._ensure_utf8(None), None) + + else: + def test_ensure_utf8(self): + self.assertEqual(dyld._ensure_utf8("hello"), "hello") + self.assertEqual(dyld._ensure_utf8(None), None) + self.assertRaises(ValueError, dyld._ensure_utf8, b"hello") + + def test__dyld_env(self): + new = os.environ + + self.assertEqual(dyld._dyld_env(None, 'DYLD_FOO'), []) + self.assertEqual(dyld._dyld_env({'DYLD_FOO':'bar'}, 'DYLD_FOO'), ['bar']) + self.assertEqual(dyld._dyld_env({'DYLD_FOO':'bar:baz'}, 'DYLD_FOO'), ['bar', 'baz']) + self.assertEqual(dyld._dyld_env({}, 'DYLD_FOO'), []) + self.assertEqual(dyld._dyld_env({'DYLD_FOO':''}, 'DYLD_FOO'), []) + os.environ['DYLD_FOO'] = 'foobar' + self.assertEqual(dyld._dyld_env(None, 'DYLD_FOO'), ['foobar']) + os.environ['DYLD_FOO'] = 'foobar:nowhere' + self.assertEqual(dyld._dyld_env(None, 'DYLD_FOO'), ['foobar', 'nowhere']) + self.assertEqual(dyld._dyld_env({'DYLD_FOO':'bar'}, 'DYLD_FOO'), ['bar']) + self.assertEqual(dyld._dyld_env({}, 'DYLD_FOO'), []) + + + self.assertEqual(dyld.dyld_image_suffix(), None) + self.assertEqual(dyld.dyld_image_suffix(None), None) + self.assertEqual(dyld.dyld_image_suffix({'DYLD_IMAGE_SUFFIX':'bar'}), 'bar') + os.environ['DYLD_IMAGE_SUFFIX'] = 'foobar' + self.assertEqual(dyld.dyld_image_suffix(), 'foobar') + self.assertEqual(dyld.dyld_image_suffix(None), 'foobar') + + def test_dyld_helpers(self): + record = [] + def fake__dyld_env(env, key): + record.append((env, key)) + return ['hello'] + + dyld._dyld_env = fake__dyld_env + self.assertEqual(dyld.dyld_framework_path(), ['hello']) + self.assertEqual(dyld.dyld_framework_path({}), ['hello']) + + self.assertEqual(dyld.dyld_library_path(), ['hello']) + self.assertEqual(dyld.dyld_library_path({}), ['hello']) + + self.assertEqual(dyld.dyld_fallback_framework_path(), ['hello']) + self.assertEqual(dyld.dyld_fallback_framework_path({}), ['hello']) + + self.assertEqual(dyld.dyld_fallback_library_path(), ['hello']) + self.assertEqual(dyld.dyld_fallback_library_path({}), ['hello']) + + self.assertEqual(record, [ + (None, 'DYLD_FRAMEWORK_PATH'), + ({}, 'DYLD_FRAMEWORK_PATH'), + (None, 'DYLD_LIBRARY_PATH'), + ({}, 'DYLD_LIBRARY_PATH'), + (None, 'DYLD_FALLBACK_FRAMEWORK_PATH'), + ({}, 'DYLD_FALLBACK_FRAMEWORK_PATH'), + (None, 'DYLD_FALLBACK_LIBRARY_PATH'), + ({}, 'DYLD_FALLBACK_LIBRARY_PATH'), + ]) + + def test_dyld_suffix_search(self): + envs = [object()] + def fake_suffix(env): + envs[0] = env + return None + dyld.dyld_image_suffix = fake_suffix + + iterator = [ + '/usr/lib/foo', + '/usr/lib/foo.dylib', + ] + result = dyld.dyld_image_suffix_search(iter(iterator)) + self.assertEqual(list(result), iterator) + self.assertEqual(envs[0], None) + + result = dyld.dyld_image_suffix_search(iter(iterator), {}) + self.assertEqual(list(result), iterator) + self.assertEqual(envs[0], {}) + + envs = [object()] + def fake_suffix(env): + envs[0] = env + return '_profile' + dyld.dyld_image_suffix = fake_suffix + + iterator = [ + '/usr/lib/foo', + '/usr/lib/foo.dylib', + ] + result = dyld.dyld_image_suffix_search(iter(iterator)) + self.assertEqual(list(result), [ + '/usr/lib/foo_profile', + '/usr/lib/foo', + '/usr/lib/foo_profile.dylib', + '/usr/lib/foo.dylib', + ]) + self.assertEqual(envs[0], None) + + result = dyld.dyld_image_suffix_search(iter(iterator), {}) + self.assertEqual(list(result), [ + '/usr/lib/foo_profile', + '/usr/lib/foo', + '/usr/lib/foo_profile.dylib', + '/usr/lib/foo.dylib', + ]) + self.assertEqual(envs[0], {}) + + def test_override_search(self): + os.environ['DYLD_FRAMEWORK_PATH'] = '' + os.environ['DYLD_LIBRARY_PATH'] = '' + + self.assertEqual( + list(dyld.dyld_override_search("foo.dyld", None)), []) + self.assertEqual( + list(dyld.dyld_override_search("/usr/lib/libfoo.dyld", None)), []) + self.assertEqual( + list(dyld.dyld_override_search("/Library/Frameworks/Python.framework/Versions/Current/Python", None)), []) + + + os.environ['DYLD_FRAMEWORK_PATH'] = '/Foo/Frameworks:/Bar/Frameworks' + os.environ['DYLD_LIBRARY_PATH'] = '' + self.assertEqual( + list(dyld.dyld_override_search("foo.dyld", None)), []) + self.assertEqual( + list(dyld.dyld_override_search("/usr/lib/libfoo.dyld", None)), []) + self.assertEqual( + list(dyld.dyld_override_search("/Library/Frameworks/Python.framework/Versions/Current/Python", None)), [ + '/Foo/Frameworks/Python.framework/Versions/Current/Python', + '/Bar/Frameworks/Python.framework/Versions/Current/Python', + ]) + + os.environ['DYLD_FRAMEWORK_PATH'] = '' + os.environ['DYLD_LIBRARY_PATH'] = '/local/lib:/remote/lib' + self.assertEqual( + list(dyld.dyld_override_search("foo.dyld", None)), [ + '/local/lib/foo.dyld', + '/remote/lib/foo.dyld', + ]) + self.assertEqual( + list(dyld.dyld_override_search("/usr/lib/libfoo.dyld", None)), [ + '/local/lib/libfoo.dyld', + '/remote/lib/libfoo.dyld', + ]) + self.assertEqual( + list(dyld.dyld_override_search("/Library/Frameworks/Python.framework/Versions/Current/Python", None)), [ + '/local/lib/Python', + '/remote/lib/Python', + ]) + + os.environ['DYLD_FRAMEWORK_PATH'] = '/Foo/Frameworks:/Bar/Frameworks' + os.environ['DYLD_LIBRARY_PATH'] = '/local/lib:/remote/lib' + self.assertEqual( + list(dyld.dyld_override_search("foo.dyld", None)), [ + '/local/lib/foo.dyld', + '/remote/lib/foo.dyld', + ]) + self.assertEqual( + list(dyld.dyld_override_search("/usr/lib/libfoo.dyld", None)), [ + '/local/lib/libfoo.dyld', + '/remote/lib/libfoo.dyld', + ]) + self.assertEqual( + list(dyld.dyld_override_search("/Library/Frameworks/Python.framework/Versions/Current/Python", None)), [ + '/Foo/Frameworks/Python.framework/Versions/Current/Python', + '/Bar/Frameworks/Python.framework/Versions/Current/Python', + '/local/lib/Python', + '/remote/lib/Python', + ]) + + def test_executable_path_search(self): + self.assertEqual(list(dyld.dyld_executable_path_search("/usr/lib/foo.dyld", "/usr/bin")), []) + self.assertEqual( + list(dyld.dyld_executable_path_search("@executable_path/foo.dyld", "/usr/bin")), + ['/usr/bin/foo.dyld']) + self.assertEqual( + list(dyld.dyld_executable_path_search("@executable_path/../../lib/foo.dyld", "/usr/bin")), + ['/usr/bin/../../lib/foo.dyld']) + + def test_default_search(self): + self.assertEqual( + list(dyld.dyld_default_search('/usr/lib/mylib.dylib', None)), [ + '/usr/lib/mylib.dylib', + os.path.join(os.path.expanduser('~/lib'), 'mylib.dylib'), + '/usr/local/lib/mylib.dylib', + '/lib/mylib.dylib', + '/usr/lib/mylib.dylib', + + ]) + + self.assertEqual( + list(dyld.dyld_default_search('/Library/Frameworks/Python.framework/Versions/2.7/Python', None)), [ + '/Library/Frameworks/Python.framework/Versions/2.7/Python', + os.path.join(os.path.expanduser('~/Library/Frameworks'), 'Python.framework/Versions/2.7/Python'), + '/Library/Frameworks/Python.framework/Versions/2.7/Python', + '/Network/Library/Frameworks/Python.framework/Versions/2.7/Python', + '/System/Library/Frameworks/Python.framework/Versions/2.7/Python', + os.path.join(os.path.expanduser('~/lib'), 'Python'), + '/usr/local/lib/Python', + '/lib/Python', + '/usr/lib/Python', + ]) + + + + + os.environ['DYLD_FALLBACK_LIBRARY_PATH'] = '/local/lib:/network/lib' + os.environ['DYLD_FALLBACK_FRAMEWORK_PATH'] = '' + + self.assertEqual( + list(dyld.dyld_default_search('/usr/lib/mylib.dylib', None)), [ + '/usr/lib/mylib.dylib', + '/local/lib/mylib.dylib', + '/network/lib/mylib.dylib', + ]) + + + self.assertEqual( + list(dyld.dyld_default_search('/Library/Frameworks/Python.framework/Versions/2.7/Python', None)), [ + '/Library/Frameworks/Python.framework/Versions/2.7/Python', + os.path.join(os.path.expanduser('~/Library/Frameworks'), 'Python.framework/Versions/2.7/Python'), + '/Library/Frameworks/Python.framework/Versions/2.7/Python', + '/Network/Library/Frameworks/Python.framework/Versions/2.7/Python', + '/System/Library/Frameworks/Python.framework/Versions/2.7/Python', + '/local/lib/Python', + '/network/lib/Python', + ]) + + + os.environ['DYLD_FALLBACK_LIBRARY_PATH'] = '' + os.environ['DYLD_FALLBACK_FRAMEWORK_PATH'] = '/MyFrameworks:/OtherFrameworks' + + + self.assertEqual( + list(dyld.dyld_default_search('/usr/lib/mylib.dylib', None)), [ + '/usr/lib/mylib.dylib', + os.path.join(os.path.expanduser('~/lib'), 'mylib.dylib'), + '/usr/local/lib/mylib.dylib', + '/lib/mylib.dylib', + '/usr/lib/mylib.dylib', + + ]) + + self.assertEqual( + list(dyld.dyld_default_search('/Library/Frameworks/Python.framework/Versions/2.7/Python', None)), [ + '/Library/Frameworks/Python.framework/Versions/2.7/Python', + '/MyFrameworks/Python.framework/Versions/2.7/Python', + '/OtherFrameworks/Python.framework/Versions/2.7/Python', + os.path.join(os.path.expanduser('~/lib'), 'Python'), + '/usr/local/lib/Python', + '/lib/Python', + '/usr/lib/Python', + ]) + + os.environ['DYLD_FALLBACK_LIBRARY_PATH'] = '/local/lib:/network/lib' + os.environ['DYLD_FALLBACK_FRAMEWORK_PATH'] = '/MyFrameworks:/OtherFrameworks' + + + self.assertEqual( + list(dyld.dyld_default_search('/usr/lib/mylib.dylib', None)), [ + '/usr/lib/mylib.dylib', + '/local/lib/mylib.dylib', + '/network/lib/mylib.dylib', + + ]) + + self.assertEqual( + list(dyld.dyld_default_search('/Library/Frameworks/Python.framework/Versions/2.7/Python', None)), [ + '/Library/Frameworks/Python.framework/Versions/2.7/Python', + '/MyFrameworks/Python.framework/Versions/2.7/Python', + '/OtherFrameworks/Python.framework/Versions/2.7/Python', + '/local/lib/Python', + '/network/lib/Python', + ]) + + def test_dyld_find(self): + result = dyld.dyld_find('/usr/lib/libSystem.dylib') + self.assertEqual(result, '/usr/lib/libSystem.dylib') + self.assertIsInstance(result, str) # bytes on 2.x, unicode on 3.x + + result = dyld.dyld_find(b'/usr/lib/libSystem.dylib'.decode('ascii')) + self.assertEqual(result, '/usr/lib/libSystem.dylib') + self.assertIsInstance(result, str) # bytes on 2.x, unicode on 3.x + + patcher = DyldPatcher() + try: + patcher.log_calls('dyld_image_suffix_search') + patcher.log_calls('dyld_override_search') + patcher.log_calls('dyld_executable_path_search') + patcher.log_calls('dyld_default_search') + + result = dyld.dyld_find('/usr/lib/libSystem.dylib') + self.assertEqual(patcher.calls[:-1], [ + ('dyld_override_search', ('/usr/lib/libSystem.dylib', None), {}), + ('dyld_executable_path_search', ('/usr/lib/libSystem.dylib', None), {}), + ('dyld_default_search', ('/usr/lib/libSystem.dylib', None), {}), + ]) + self.assertEqual(patcher.calls[-1][0], 'dyld_image_suffix_search') + patcher.clear_calls() + + result = dyld.dyld_find('/usr/lib/libSystem.dylib', env=None) + self.assertEqual(patcher.calls[:-1], [ + ('dyld_override_search', ('/usr/lib/libSystem.dylib', None), {}), + ('dyld_executable_path_search', ('/usr/lib/libSystem.dylib', None), {}), + ('dyld_default_search', ('/usr/lib/libSystem.dylib', None), {}), + ]) + self.assertEqual(patcher.calls[-1][0], 'dyld_image_suffix_search') + patcher.clear_calls() + + result = dyld.dyld_find('/usr/lib/libSystem.dylib', env={}) + self.assertEqual(patcher.calls[:-1], [ + ('dyld_override_search', ('/usr/lib/libSystem.dylib', {}), {}), + ('dyld_executable_path_search', ('/usr/lib/libSystem.dylib', None), {}), + ('dyld_default_search', ('/usr/lib/libSystem.dylib', {}), {}), + ]) + self.assertEqual(patcher.calls[-1][0], 'dyld_image_suffix_search') + patcher.clear_calls() + + result = dyld.dyld_find('/usr/lib/libSystem.dylib', executable_path="/opt/py2app/bin", env={}) + self.assertEqual(patcher.calls[:-1], [ + ('dyld_override_search', ('/usr/lib/libSystem.dylib', {}), {}), + ('dyld_executable_path_search', ('/usr/lib/libSystem.dylib', "/opt/py2app/bin"), {}), + ('dyld_default_search', ('/usr/lib/libSystem.dylib', {}), {}), + ]) + self.assertEqual(patcher.calls[-1][0], 'dyld_image_suffix_search') + patcher.clear_calls() + + finally: + patcher.cleanup() + + def test_framework_find(self): + result = dyld.framework_find('/System/Library/Frameworks/Cocoa.framework/Versions/Current/Cocoa') + self.assertEqual(result, '/System/Library/Frameworks/Cocoa.framework/Versions/Current/Cocoa') + self.assertIsInstance(result, str) # bytes on 2.x, unicode on 3.x + + result = dyld.framework_find(b'/System/Library/Frameworks/Cocoa.framework/Versions/Current/Cocoa'.decode('latin1')) + self.assertEqual(result, '/System/Library/Frameworks/Cocoa.framework/Versions/Current/Cocoa') + self.assertIsInstance(result, str) # bytes on 2.x, unicode on 3.x + + result = dyld.framework_find('Cocoa.framework') + self.assertEqual(result, '/System/Library/Frameworks/Cocoa.framework/Cocoa') + self.assertIsInstance(result, str) # bytes on 2.x, unicode on 3.x + + result = dyld.framework_find('Cocoa') + self.assertEqual(result, '/System/Library/Frameworks/Cocoa.framework/Cocoa') + self.assertIsInstance(result, str) # bytes on 2.x, unicode on 3.x + + patcher = DyldPatcher() + try: + patcher.log_calls('dyld_find') + + result = dyld.framework_find('/System/Library/Frameworks/Cocoa.framework/Versions/Current/Cocoa') + self.assertEqual(patcher.calls, [ + ('dyld_find', ('/System/Library/Frameworks/Cocoa.framework/Versions/Current/Cocoa',), + {'env':None, 'executable_path': None}), + ]) + patcher.clear_calls() + + result = dyld.framework_find('Cocoa') + self.assertEqual(patcher.calls, [ + ('dyld_find', ('Cocoa',), + {'env':None, 'executable_path': None}), + ('dyld_find', ('Cocoa.framework/Cocoa',), + {'env':None, 'executable_path': None}), + ]) + patcher.clear_calls() + + result = dyld.framework_find('Cocoa', '/my/sw/bin', {}) + self.assertEqual(patcher.calls, [ + ('dyld_find', ('Cocoa',), + {'env':{}, 'executable_path': '/my/sw/bin'}), + ('dyld_find', ('Cocoa.framework/Cocoa',), + {'env':{}, 'executable_path': '/my/sw/bin'}), + ]) + patcher.clear_calls() + + + finally: + patcher.cleanup() + + + + +class TestTrivialDyld (unittest.TestCase): + # Tests ported from the implementation file + def testBasic(self): + self.assertEqual(dyld.dyld_find('libSystem.dylib'), '/usr/lib/libSystem.dylib') + self.assertEqual(dyld.dyld_find('System.framework/System'), '/System/Library/Frameworks/System.framework/System') + +if __name__ == "__main__": + unittest.main() |