You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

427 lines
17 KiB

import sys, os, py
import subprocess
import cffi
from testing.udir import udir
from shutil import rmtree
from tempfile import mkdtemp
def chdir_to_tmp(f):
f.chdir_to_tmp = True
return f
def from_outside(f):
f.chdir_to_tmp = False
return f
class TestDist(object):
def setup_method(self, meth):
self.executable = os.path.abspath(sys.executable)
self.rootdir = os.path.abspath(os.path.dirname(os.path.dirname(
cffi.__file__)))
self.udir = udir.join(meth.__name__)
os.mkdir(str(self.udir))
if meth.chdir_to_tmp:
self.saved_cwd = os.getcwd()
os.chdir(str(self.udir))
def teardown_method(self, meth):
if hasattr(self, 'saved_cwd'):
os.chdir(self.saved_cwd)
def run(self, args, cwd=None):
env = os.environ.copy()
# a horrible hack to prevent distutils from finding ~/.pydistutils.cfg
# (there is the --no-user-cfg option, but not in Python 2.6...)
# NOTE: pointing $HOME to a nonexistent directory can break certain things
# that look there for configuration (like ccache).
tmp_home = mkdtemp()
assert tmp_home != None, "cannot create temporary homedir"
env['HOME'] = tmp_home
if cwd is None:
newpath = self.rootdir
if 'PYTHONPATH' in env:
newpath += os.pathsep + env['PYTHONPATH']
env['PYTHONPATH'] = newpath
try:
subprocess.check_call([self.executable] + args, cwd=cwd, env=env)
finally:
rmtree(tmp_home)
def _prepare_setuptools(self):
if hasattr(TestDist, '_setuptools_ready'):
return
try:
import setuptools
except ImportError:
py.test.skip("setuptools not found")
if os.path.exists(os.path.join(self.rootdir, 'setup.py')):
self.run(['setup.py', 'egg_info'], cwd=self.rootdir)
TestDist._setuptools_ready = True
def check_produced_files(self, content, curdir=None):
if curdir is None:
curdir = str(self.udir)
found_so = None
for name in os.listdir(curdir):
if (name.endswith('.so') or name.endswith('.pyd') or
name.endswith('.dylib') or name.endswith('.dll')):
found_so = os.path.join(curdir, name)
# foo.so => foo
parts = name.split('.')
del parts[-1]
if len(parts) > 1 and parts[-1] != 'bar':
# foo.cpython-34m.so => foo, but foo.bar.so => foo.bar
del parts[-1]
name = '.'.join(parts)
# foo_d => foo (Python 2 debug builds)
if name.endswith('_d') and hasattr(sys, 'gettotalrefcount'):
name = name[:-2]
name += '.SO'
if name.startswith('pycparser') and name.endswith('.egg'):
continue # no clue why this shows up sometimes and not others
if name == '.eggs':
continue # seems new in 3.5, ignore it
assert name in content, "found unexpected file %r" % (
os.path.join(curdir, name),)
value = content.pop(name)
if value is None:
assert name.endswith('.SO') or (
os.path.isfile(os.path.join(curdir, name)))
else:
subdir = os.path.join(curdir, name)
assert os.path.isdir(subdir)
if value == '?':
continue
found_so = self.check_produced_files(value, subdir) or found_so
assert content == {}, "files or dirs not produced in %r: %r" % (
curdir, content.keys())
return found_so
@chdir_to_tmp
def test_empty(self):
self.check_produced_files({})
@chdir_to_tmp
def test_abi_emit_python_code_1(self):
ffi = cffi.FFI()
ffi.set_source("package_name_1.mymod", None)
ffi.emit_python_code('xyz.py')
self.check_produced_files({'xyz.py': None})
@chdir_to_tmp
def test_abi_emit_python_code_2(self):
ffi = cffi.FFI()
ffi.set_source("package_name_1.mymod", None)
py.test.raises(IOError, ffi.emit_python_code, 'unexisting/xyz.py')
@from_outside
def test_abi_emit_python_code_3(self):
ffi = cffi.FFI()
ffi.set_source("package_name_1.mymod", None)
ffi.emit_python_code(str(self.udir.join('xyt.py')))
self.check_produced_files({'xyt.py': None})
@chdir_to_tmp
def test_abi_compile_1(self):
ffi = cffi.FFI()
ffi.set_source("mod_name_in_package.mymod", None)
x = ffi.compile()
self.check_produced_files({'mod_name_in_package': {'mymod.py': None}})
assert x == os.path.join('.', 'mod_name_in_package', 'mymod.py')
@chdir_to_tmp
def test_abi_compile_2(self):
ffi = cffi.FFI()
ffi.set_source("mod_name_in_package.mymod", None)
x = ffi.compile('build2')
self.check_produced_files({'build2': {
'mod_name_in_package': {'mymod.py': None}}})
assert x == os.path.join('build2', 'mod_name_in_package', 'mymod.py')
@from_outside
def test_abi_compile_3(self):
ffi = cffi.FFI()
ffi.set_source("mod_name_in_package.mymod", None)
tmpdir = str(self.udir.join('build3'))
x = ffi.compile(tmpdir)
self.check_produced_files({'build3': {
'mod_name_in_package': {'mymod.py': None}}})
assert x == os.path.join(tmpdir, 'mod_name_in_package', 'mymod.py')
@chdir_to_tmp
def test_api_emit_c_code_1(self):
ffi = cffi.FFI()
ffi.set_source("package_name_1.mymod", "/*code would be here*/")
ffi.emit_c_code('xyz.c')
self.check_produced_files({'xyz.c': None})
@chdir_to_tmp
def test_api_emit_c_code_2(self):
ffi = cffi.FFI()
ffi.set_source("package_name_1.mymod", "/*code would be here*/")
py.test.raises(IOError, ffi.emit_c_code, 'unexisting/xyz.c')
@from_outside
def test_api_emit_c_code_3(self):
ffi = cffi.FFI()
ffi.set_source("package_name_1.mymod", "/*code would be here*/")
ffi.emit_c_code(str(self.udir.join('xyu.c')))
self.check_produced_files({'xyu.c': None})
@chdir_to_tmp
def test_api_compile_1(self):
ffi = cffi.FFI()
ffi.set_source("mod_name_in_package.mymod", "/*code would be here*/")
x = ffi.compile()
if sys.platform != 'win32':
sofile = self.check_produced_files({
'mod_name_in_package': {'mymod.SO': None,
'mymod.c': None,
'mymod.o': None}})
assert os.path.isabs(x) and os.path.samefile(x, sofile)
else:
self.check_produced_files({
'mod_name_in_package': {'mymod.SO': None,
'mymod.c': None},
'Release': '?'})
@chdir_to_tmp
def test_api_compile_2(self):
ffi = cffi.FFI()
ffi.set_source("mod_name_in_package.mymod", "/*code would be here*/")
x = ffi.compile('output')
if sys.platform != 'win32':
sofile = self.check_produced_files({
'output': {'mod_name_in_package': {'mymod.SO': None,
'mymod.c': None,
'mymod.o': None}}})
assert os.path.isabs(x) and os.path.samefile(x, sofile)
else:
self.check_produced_files({
'output': {'mod_name_in_package': {'mymod.SO': None,
'mymod.c': None},
'Release': '?'}})
@from_outside
def test_api_compile_3(self):
ffi = cffi.FFI()
ffi.set_source("mod_name_in_package.mymod", "/*code would be here*/")
x = ffi.compile(str(self.udir.join('foo')))
if sys.platform != 'win32':
sofile = self.check_produced_files({
'foo': {'mod_name_in_package': {'mymod.SO': None,
'mymod.c': None,
'mymod.o': None}}})
assert os.path.isabs(x) and os.path.samefile(x, sofile)
else:
self.check_produced_files({
'foo': {'mod_name_in_package': {'mymod.SO': None,
'mymod.c': None},
'Release': '?'}})
@chdir_to_tmp
def test_api_compile_explicit_target_1(self):
ffi = cffi.FFI()
ffi.set_source("mod_name_in_package.mymod", "/*code would be here*/")
x = ffi.compile(target="foo.bar.*")
if sys.platform != 'win32':
sofile = self.check_produced_files({
'mod_name_in_package': {'foo.bar.SO': None,
'mymod.c': None,
'mymod.o': None}})
assert os.path.isabs(x) and os.path.samefile(x, sofile)
else:
self.check_produced_files({
'mod_name_in_package': {'foo.bar.SO': None,
'mymod.c': None},
'Release': '?'})
@chdir_to_tmp
def test_api_compile_explicit_target_3(self):
ffi = cffi.FFI()
ffi.set_source("mod_name_in_package.mymod", "/*code would be here*/")
x = ffi.compile(target="foo.bar.baz")
if sys.platform != 'win32':
self.check_produced_files({
'mod_name_in_package': {'foo.bar.baz': None,
'mymod.c': None,
'mymod.o': None}})
sofile = os.path.join(str(self.udir),
'mod_name_in_package', 'foo.bar.baz')
assert os.path.isabs(x) and os.path.samefile(x, sofile)
else:
self.check_produced_files({
'mod_name_in_package': {'foo.bar.baz': None,
'mymod.c': None},
'Release': '?'})
@chdir_to_tmp
def test_api_distutils_extension_1(self):
ffi = cffi.FFI()
ffi.set_source("mod_name_in_package.mymod", "/*code would be here*/")
ext = ffi.distutils_extension()
self.check_produced_files({'build': {
'mod_name_in_package': {'mymod.c': None}}})
if hasattr(os.path, 'samefile'):
assert os.path.samefile(ext.sources[0],
'build/mod_name_in_package/mymod.c')
@from_outside
def test_api_distutils_extension_2(self):
ffi = cffi.FFI()
ffi.set_source("mod_name_in_package.mymod", "/*code would be here*/")
ext = ffi.distutils_extension(str(self.udir.join('foo')))
self.check_produced_files({'foo': {
'mod_name_in_package': {'mymod.c': None}}})
if hasattr(os.path, 'samefile'):
assert os.path.samefile(ext.sources[0],
str(self.udir.join('foo/mod_name_in_package/mymod.c')))
def _make_distutils_api(self):
os.mkdir("src")
os.mkdir(os.path.join("src", "pack1"))
with open(os.path.join("src", "pack1", "__init__.py"), "w") as f:
pass
with open("setup.py", "w") as f:
f.write("""if 1:
# https://bugs.python.org/issue23246
import sys
if sys.platform == 'win32':
try:
import setuptools
except ImportError:
pass
import cffi
ffi = cffi.FFI()
ffi.set_source("pack1.mymod", "/*code would be here*/")
from distutils.core import setup
setup(name='example1',
version='0.1',
packages=['pack1'],
package_dir={'': 'src'},
ext_modules=[ffi.distutils_extension()])
""")
@chdir_to_tmp
def test_distutils_api_1(self):
self._make_distutils_api()
self.run(["setup.py", "build"])
self.check_produced_files({'setup.py': None,
'build': '?',
'src': {'pack1': {'__init__.py': None}}})
@chdir_to_tmp
def test_distutils_api_2(self):
self._make_distutils_api()
self.run(["setup.py", "build_ext", "-i"])
self.check_produced_files({'setup.py': None,
'build': '?',
'src': {'pack1': {'__init__.py': None,
'mymod.SO': None}}})
def _make_setuptools_abi(self):
self._prepare_setuptools()
os.mkdir("src0")
os.mkdir(os.path.join("src0", "pack2"))
with open(os.path.join("src0", "pack2", "__init__.py"), "w") as f:
pass
with open(os.path.join("src0", "pack2", "_build.py"), "w") as f:
f.write("""if 1:
import cffi
ffi = cffi.FFI()
ffi.set_source("pack2.mymod", None)
""")
with open("setup.py", "w") as f:
f.write("""if 1:
from setuptools import setup
setup(name='example1',
version='0.1',
packages=['pack2'],
package_dir={'': 'src0'},
cffi_modules=["src0/pack2/_build.py:ffi"])
""")
@chdir_to_tmp
def test_setuptools_abi_1(self):
self._make_setuptools_abi()
self.run(["setup.py", "build"])
self.check_produced_files({'setup.py': None,
'build': '?',
'src0': {'pack2': {'__init__.py': None,
'_build.py': None}}})
@chdir_to_tmp
def test_setuptools_abi_2(self):
self._make_setuptools_abi()
self.run(["setup.py", "build_ext", "-i"])
self.check_produced_files({'setup.py': None,
'src0': {'pack2': {'__init__.py': None,
'_build.py': None,
'mymod.py': None}}})
def _make_setuptools_api(self):
self._prepare_setuptools()
os.mkdir("src1")
os.mkdir(os.path.join("src1", "pack3"))
with open(os.path.join("src1", "pack3", "__init__.py"), "w") as f:
pass
with open(os.path.join("src1", "pack3", "_build.py"), "w") as f:
f.write("""if 1:
import cffi
ffi = cffi.FFI()
ffi.set_source("pack3.mymod", "/*code would be here*/")
ffi._hi_there = 42
""")
with open("setup.py", "w") as f:
f.write("from __future__ import print_function\n"
"""if 1:
from setuptools import setup
from distutils.command.build_ext import build_ext
import os
class TestBuildExt(build_ext):
def pre_run(self, ext, ffi):
print('_make_setuptools_api: in pre_run:', end=" ")
assert ffi._hi_there == 42
assert ext.name == "pack3.mymod"
fn = os.path.join(os.path.dirname(self.build_lib),
'..', 'see_me')
print('creating %r' % (fn,))
open(fn, 'w').close()
setup(name='example1',
version='0.1',
packages=['pack3'],
package_dir={'': 'src1'},
cffi_modules=["src1/pack3/_build.py:ffi"],
cmdclass={'build_ext': TestBuildExt},
)
""")
@chdir_to_tmp
def test_setuptools_api_1(self):
self._make_setuptools_api()
self.run(["setup.py", "build"])
self.check_produced_files({'setup.py': None,
'build': '?',
'see_me': None,
'src1': {'pack3': {'__init__.py': None,
'_build.py': None}}})
@chdir_to_tmp
def test_setuptools_api_2(self):
self._make_setuptools_api()
self.run(["setup.py", "build_ext", "-i"])
self.check_produced_files({'setup.py': None,
'build': '?',
'see_me': None,
'src1': {'pack3': {'__init__.py': None,
'_build.py': None,
'mymod.SO': None}}})