451 lines
19 KiB
Python
451 lines
19 KiB
Python
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()
|