summaryrefslogtreecommitdiff
path: root/testing/embedding/test_basic.py
diff options
context:
space:
mode:
Diffstat (limited to 'testing/embedding/test_basic.py')
-rw-r--r--testing/embedding/test_basic.py207
1 files changed, 207 insertions, 0 deletions
diff --git a/testing/embedding/test_basic.py b/testing/embedding/test_basic.py
new file mode 100644
index 0000000..8463c3f
--- /dev/null
+++ b/testing/embedding/test_basic.py
@@ -0,0 +1,207 @@
+import py
+import sys, os, re
+import shutil, subprocess, time
+from testing.udir import udir
+import cffi
+
+
+local_dir = os.path.dirname(os.path.abspath(__file__))
+_link_error = '?'
+
+def check_lib_python_found(tmpdir):
+ global _link_error
+ if _link_error == '?':
+ ffi = cffi.FFI()
+ kwds = {}
+ ffi._apply_embedding_fix(kwds)
+ ffi.set_source("_test_lib_python_found", "", **kwds)
+ try:
+ ffi.compile(tmpdir=tmpdir, verbose=True)
+ except cffi.VerificationError as e:
+ _link_error = e
+ else:
+ _link_error = None
+ if _link_error:
+ py.test.skip(str(_link_error))
+
+
+def prefix_pythonpath():
+ cffi_base = os.path.dirname(os.path.dirname(local_dir))
+ pythonpath = org_env.get('PYTHONPATH', '').split(os.pathsep)
+ if cffi_base not in pythonpath:
+ pythonpath.insert(0, cffi_base)
+ return os.pathsep.join(pythonpath)
+
+def copy_away_env():
+ global org_env
+ try:
+ org_env
+ except NameError:
+ org_env = os.environ.copy()
+
+
+class EmbeddingTests:
+ _compiled_modules = {}
+
+ def setup_method(self, meth):
+ check_lib_python_found(str(udir.ensure('embedding', dir=1)))
+ self._path = udir.join('embedding', meth.__name__)
+ if sys.platform == "win32" or sys.platform == "darwin":
+ self._compiled_modules.clear() # workaround
+
+ def get_path(self):
+ return str(self._path.ensure(dir=1))
+
+ def _run_base(self, args, **kwds):
+ print('RUNNING:', args, kwds)
+ return subprocess.Popen(args, **kwds)
+
+ def _run(self, args):
+ popen = self._run_base(args, cwd=self.get_path(),
+ stdout=subprocess.PIPE,
+ universal_newlines=True)
+ output = popen.stdout.read()
+ err = popen.wait()
+ if err:
+ raise OSError("popen failed with exit code %r: %r" % (
+ err, args))
+ print(output.rstrip())
+ return output
+
+ def prepare_module(self, name):
+ self.patch_environment()
+ if name not in self._compiled_modules:
+ path = self.get_path()
+ filename = '%s.py' % name
+ # NOTE: if you have an .egg globally installed with an older
+ # version of cffi, this will not work, because sys.path ends
+ # up with the .egg before the PYTHONPATH entries. I didn't
+ # find a solution to that: we could hack sys.path inside the
+ # script run here, but we can't hack it in the same way in
+ # execute().
+ pathname = os.path.join(path, filename)
+ with open(pathname, 'w') as g:
+ g.write('''
+# https://bugs.python.org/issue23246
+import sys
+if sys.platform == 'win32':
+ try:
+ import setuptools
+ except ImportError:
+ pass
+''')
+ with open(os.path.join(local_dir, filename), 'r') as f:
+ g.write(f.read())
+
+ output = self._run([sys.executable, pathname])
+ match = re.compile(r"\bFILENAME: (.+)").search(output)
+ assert match
+ dynamic_lib_name = match.group(1)
+ if sys.platform == 'win32':
+ assert dynamic_lib_name.endswith('_cffi.dll')
+ elif sys.platform == 'darwin':
+ assert dynamic_lib_name.endswith('_cffi.dylib')
+ else:
+ assert dynamic_lib_name.endswith('_cffi.so')
+ self._compiled_modules[name] = dynamic_lib_name
+ return self._compiled_modules[name]
+
+ def compile(self, name, modules, opt=False, threads=False, defines={}):
+ path = self.get_path()
+ filename = '%s.c' % name
+ shutil.copy(os.path.join(local_dir, filename), path)
+ shutil.copy(os.path.join(local_dir, 'thread-test.h'), path)
+ import distutils.ccompiler
+ curdir = os.getcwd()
+ try:
+ os.chdir(self.get_path())
+ c = distutils.ccompiler.new_compiler()
+ print('compiling %s with %r' % (name, modules))
+ extra_preargs = []
+ debug = True
+ if sys.platform == 'win32':
+ libfiles = []
+ for m in modules:
+ m = os.path.basename(m)
+ assert m.endswith('.dll')
+ libfiles.append('Release\\%s.lib' % m[:-4])
+ modules = libfiles
+ extra_preargs.append('/MANIFEST')
+ debug = False # you need to install extra stuff
+ # for this to work
+ elif threads:
+ extra_preargs.append('-pthread')
+ objects = c.compile([filename], macros=sorted(defines.items()),
+ debug=debug)
+ c.link_executable(objects + modules, name, extra_preargs=extra_preargs)
+ finally:
+ os.chdir(curdir)
+
+ def patch_environment(self):
+ copy_away_env()
+ path = self.get_path()
+ # for libpypy-c.dll or Python27.dll
+ path = os.path.split(sys.executable)[0] + os.path.pathsep + path
+ env_extra = {'PYTHONPATH': prefix_pythonpath()}
+ if sys.platform == 'win32':
+ envname = 'PATH'
+ else:
+ envname = 'LD_LIBRARY_PATH'
+ libpath = org_env.get(envname)
+ if libpath:
+ libpath = path + os.path.pathsep + libpath
+ else:
+ libpath = path
+ env_extra[envname] = libpath
+ for key, value in sorted(env_extra.items()):
+ if os.environ.get(key) != value:
+ print('* setting env var %r to %r' % (key, value))
+ os.environ[key] = value
+
+ def execute(self, name):
+ path = self.get_path()
+ print('running %r in %r' % (name, path))
+ executable_name = name
+ if sys.platform == 'win32':
+ executable_name = os.path.join(path, executable_name + '.exe')
+ else:
+ executable_name = os.path.join('.', executable_name)
+ popen = self._run_base([executable_name], cwd=path,
+ stdout=subprocess.PIPE,
+ universal_newlines=True)
+ result = popen.stdout.read()
+ err = popen.wait()
+ if err:
+ raise OSError("%r failed with exit code %r" % (name, err))
+ return result
+
+
+class TestBasic(EmbeddingTests):
+ def test_empty(self):
+ empty_cffi = self.prepare_module('empty')
+
+ def test_basic(self):
+ add1_cffi = self.prepare_module('add1')
+ self.compile('add1-test', [add1_cffi])
+ output = self.execute('add1-test')
+ assert output == ("preparing...\n"
+ "adding 40 and 2\n"
+ "adding 100 and -5\n"
+ "got: 42 95\n")
+
+ def test_two_modules(self):
+ add1_cffi = self.prepare_module('add1')
+ add2_cffi = self.prepare_module('add2')
+ self.compile('add2-test', [add1_cffi, add2_cffi])
+ output = self.execute('add2-test')
+ assert output == ("preparing...\n"
+ "adding 40 and 2\n"
+ "prepADD2\n"
+ "adding 100 and -5 and -20\n"
+ "got: 42 75\n")
+
+ def test_init_time_error(self):
+ initerror_cffi = self.prepare_module('initerror')
+ self.compile('add1-test', [initerror_cffi])
+ output = self.execute('add1-test')
+ assert output == "got: 0 0\n" # plus lots of info to stderr