Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 7 additions & 59 deletions testing/cffi0/test_zintegration.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,72 +4,20 @@
import textwrap
from pathlib import Path
from testing.udir import udir
from testing.support import create_bridged_venv
import pytest

if sys.platform == 'win32':
pytestmark = pytest.mark.skip('snippets do not run on win32')
if sys.version_info < (2, 7):
pytestmark = pytest.mark.skip(
'fails e.g. on a Debian/Ubuntu which patches virtualenv'
' in a non-2.6-friendly way')
try:
import setuptools
except ImportError:
pytestmark = pytest.mark.skip('the snippets need setuptools at build time')

def create_venv(name):
tmpdir = udir / name
try:
# FUTURE: we should probably update this to use venv for at least more modern Pythons, and
# install setuptools/pip/etc explicitly for the tests that require them (as venv has stopped including
# setuptools by default for newer versions).
subprocess.check_call(['virtualenv',
#'--never-download', <= could be added, but causes failures
# in random cases on random machines
'-p', os.path.abspath(sys.executable),
str(tmpdir)])

# Newer venv/virtualenv no longer include setuptools by default, which
# breaks a number of these tests; ensure they're always present
subprocess.check_call([
str(tmpdir / 'bin/python'),
'-m',
'pip',
'install',
'setuptools',
'--upgrade'
])

except OSError as e:
pytest.skip("Cannot execute virtualenv: %s" % (e,))

site_packages = None
for dirpath, dirnames, filenames in os.walk(tmpdir):
if Path(dirpath).name == 'site-packages':
site_packages = dirpath
break
paths = ""
if site_packages:
try:
from cffi import _pycparser
modules = ('cffi', '_cffi_backend')
except ImportError:
modules = ('cffi', '_cffi_backend', 'pycparser')
try:
import ply
except ImportError:
pass
else:
modules += ('ply',) # needed for older versions of pycparser
paths = []
for module in modules:
target = __import__(module, None, None, [])
if not hasattr(target, '__file__'): # for _cffi_backend on pypy
continue
src = os.path.abspath(target.__file__)
for end in ['__init__.pyc', '__init__.pyo', '__init__.py']:
if src.lower().endswith(end):
src = src[:-len(end)-1]
break
paths.append(os.path.dirname(src))
paths = os.pathsep.join(paths)
return tmpdir, paths
create_bridged_venv(tmpdir)
return tmpdir, ''

SNIPPET_DIR = Path(__file__).absolute().parent / 'snippets'

Expand Down
45 changes: 17 additions & 28 deletions testing/cffi1/test_cffi_gen_src_meson.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
"""End-to-end test: build a self-contained CFFI extension with meson-python.

The test provisions a fresh nested venv under ``tmp_path`` using the
stdlib :mod:`venv` module, installs ``cffi`` (from the current source
tree) and ``meson-python`` into it, installs one of the small example
projects that live under ``testing/cffi1/cffi_gen_src_examples/``, and then
imports the built extension to confirm it works.
stdlib :mod:`venv` module, bridged so that it can import this
environment's ``cffi`` and ``meson-python`` (see
``testing.support.create_bridged_venv``), installs one of the small
example projects that live under ``testing/cffi1/cffi_gen_src_examples/``,
and then imports the built extension to confirm it works.

"""

Expand All @@ -19,6 +20,8 @@

import cffi

from testing.support import create_bridged_venv

pytestmark = [
pytest.mark.thread_unsafe(reason="spawns subprocesses, slow"),
]
Expand All @@ -28,11 +31,15 @@
except ImportError:
pytest.skip("Test requires meson-python", allow_module_level=True)

# meson needs the ninja binary at build time; it is not a Python-level
# dependency of meson-python.
if not (shutil.which("ninja") or shutil.which("ninja-build")):
pytest.skip("Test requires ninja", allow_module_level=True)


HERE = Path(__file__).resolve().parent
EXAMPLE_PROJECT = HERE / "cffi_gen_src_examples" / "exec_python_example"
EXAMPLE_PROJECT2 = HERE / "cffi_gen_src_examples" / "read_sources_example"
CFFI_DIR = HERE.parent.parent


def _venv_python(venv_dir):
Expand All @@ -44,39 +51,21 @@ def _venv_python(venv_dir):
@pytest.mark.parametrize("project", [EXAMPLE_PROJECT, EXAMPLE_PROJECT2])
def test_meson_python_build(tmp_path, project):
venv_dir = tmp_path / "venv"
subprocess.check_call([sys.executable, "-m", "venv", str(venv_dir)])
create_bridged_venv(venv_dir)
venv_python = _venv_python(venv_dir)
assert venv_python.exists(), venv_python

# Upgrade pip so --no-build-isolation behaves consistently with recent
# resolver behaviour on older base images.
subprocess.check_call([
str(venv_python), "-m", "pip", "install", "--upgrade", "pip",
])

# Install build-time deps into the nested venv.
subprocess.check_call([
str(venv_python), "-m", "pip", "install", "meson-python", CFFI_DIR
])

# Copy the example project so nothing is written back into the
# source tree
project_dir = tmp_path / "project"
shutil.copytree(project, project_dir)

# The example meson.build files locate the codegen tool with
# find_program('cffi-gen-src'), which searches PATH. pip only puts
# an environment's scripts directory on PATH for isolated builds,
# so with --no-build-isolation the nested venv's script must be
# made findable by hand.
env = os.environ.copy()
env["PATH"] = str(venv_python.parent) + os.pathsep + env.get("PATH", "")

# --no-build-isolation to ensure the test runs against the CFFI build we want to test
# --no-build-isolation so the build uses the bridged environment;
# --no-index/--no-deps so pip cannot touch the network.
proc = subprocess.run([
str(venv_python), "-m", "pip", "install", "-v",
"--no-build-isolation", str(project_dir),
], env=env, capture_output=True, text=True)
"--no-index", "--no-deps", "--no-build-isolation", str(project_dir),
], capture_output=True, text=True)
assert proc.returncode == 0, proc.stdout + proc.stderr

# Confirm the built extension imports and behaves as expected.
Expand Down
28 changes: 28 additions & 0 deletions testing/support.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import sys, os
import site, subprocess
from pathlib import Path
import pytest
from cffi._imp_emulation import load_dynamic

if sys.version_info < (3,):
Expand Down Expand Up @@ -134,3 +137,28 @@ def typeof_disabled(*args, **kwds):
extra_compile_args.append('-Wno-error=sign-conversion')

del platform_tags


def create_bridged_venv(tmpdir):
"""Create a venv at *tmpdir* that can import this environment's
packages, so tests can build and install projects into it without
downloading anything.
"""
try:
subprocess.check_call([sys.executable, '-m', 'venv', str(tmpdir)])
except OSError as e:
pytest.skip("Cannot execute venv: %s" % (e,))

site_packages = None
for dirpath, dirnames, filenames in os.walk(tmpdir):
if Path(dirpath).name == 'site-packages':
site_packages = dirpath
break
if site_packages:
site_dirs = list(site.getsitepackages())
user_site = site.getusersitepackages()
if user_site:
site_dirs.append(user_site)
pth = Path(site_packages) / '_outer_test_env.pth'
pth.write_text(''.join(
'import site; site.addsitedir(%r)\n' % (d,) for d in site_dirs))