Skip to content

Commit

Permalink
tests: utils: add onedir_only and onefile_only decorators
Browse files Browse the repository at this point in the history
Add `onedir_only` and `onefile_only` decorators to run the `pyi_builder`
fixture in only the specified mode.

Apply these decorators where applicable (i.e., in tests that were
using the equivalent of these decorators, or that were checking
`pyi_builder._mode` and calling `pytest.skip`).
  • Loading branch information
rokm committed Dec 26, 2024
1 parent 74b2d71 commit c99fe9c
Show file tree
Hide file tree
Showing 10 changed files with 60 additions and 71 deletions.
4 changes: 4 additions & 0 deletions PyInstaller/utils/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@
xfail = pytest.mark.xfail
skip = pytest.mark.skip

# Use these decorators to use the `pyi_builder` fixture only in onedir or only in onefile mode instead of both.
onedir_only = pytest.mark.parametrize('pyi_builder', ['onedir'], indirect=True)
onefile_only = pytest.mark.parametrize('pyi_builder', ['onefile'], indirect=True)


def importorskip(package: str):
"""
Expand Down
48 changes: 18 additions & 30 deletions tests/functional/test_basic.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
import pytest

from PyInstaller.compat import is_darwin, is_win
from PyInstaller.utils.tests import importorskip, skipif, xfail
from PyInstaller.utils.tests import importorskip, skipif, xfail, onedir_only, onefile_only


def test_run_from_path_environ(pyi_builder):
Expand Down Expand Up @@ -52,17 +52,14 @@ def _patch_spec(spec_name, symlink_name):
pyi_builder_spec.test_spec(str(spec_file), app_name=symlink_name)


@onedir_only
def test_pyz_as_external_file(pyi_builder, monkeypatch):
# This tests the not well documented and seldom used feature of having the PYZ-archive in a separate file (.pkg).

def MyEXE(*args, **kwargs):
kwargs['append_pkg'] = False
return EXE(*args, **kwargs)

# :todo: find a better way to not even run this test in onefile-mode
if pyi_builder._mode == 'onefile':
pytest.skip('only --onedir')

import PyInstaller.building.build_main
EXE = PyInstaller.building.build_main.EXE
monkeypatch.setattr('PyInstaller.building.build_main.EXE', MyEXE)
Expand Down Expand Up @@ -628,25 +625,25 @@ def test_arbitrary_ext(pyi_builder):
pyi_builder.test_script('pyi_arbitrary_ext.foo')


@onefile_only
def test_option_runtime_tmpdir(pyi_builder):
"""
Test to ensure that option `runtime_tmpdir` can be set and has effect.
Test to ensure that option `runtime_tmpdir` can be set and has effect. Applicable to onefile builds only.
"""

pyi_builder.test_source(
"""
print('test - runtime_tmpdir - custom runtime temporary directory')
import os
import sys
cwd = os.path.abspath(os.getcwd())
runtime_tmpdir = os.path.abspath(sys._MEIPASS)
# for onedir mode, runtime_tmpdir == cwd
# for onefile mode, os.path.dirname(runtime_tmpdir) == cwd
if not runtime_tmpdir == cwd and not os.path.dirname(runtime_tmpdir) == cwd:
raise SystemExit('Expected sys._MEIPASS to be under current working dir.'
' sys._MEIPASS = ' + runtime_tmpdir + ', cwd = ' + cwd)
print('test - done')
# With --runtime-tmpdir=., we expect the application to unpack itself into cwd/_MEIXXXX directory.
if os.path.dirname(runtime_tmpdir) != cwd:
raise SystemExit(
f'Expected sys._MEIPASS ({sys._MEIPASS}) to be under current working dir ({cwd}).'
)
""",
# Set runtime-tmpdir to current working dir
pyi_args=['--runtime-tmpdir', '.']
Expand Down Expand Up @@ -701,14 +698,12 @@ def test_pe_checksum(pyi_builder):
assert header_sum.value == checksum.value


@onefile_only
def test_onefile_longpath(pyi_builder, tmp_path):
"""
Verify that files with paths longer than 260 characters are correctly extracted from the onefile build.
See issue #5615."
"""
# The test is relevant only for onefile builds
if pyi_builder._mode != 'onefile':
pytest.skip('The test is relevant only to onefile builds.')
# Create data file with secret
_SECRET = 'LongDataPath'
src_path = tmp_path / 'data.txt'
Expand Down Expand Up @@ -854,13 +849,11 @@ def test_package_entry_point_name_collision(pyi_builder):
assert re.findall("Running (.*) as (.*)", p.stdout) == expected


@onedir_only
def test_contents_directory(pyi_builder):
"""
Test the --contents-directory option, including changing it without --clean.
"""
if pyi_builder._mode != 'onedir':
pytest.skip('--contents-directory does not affect onefile builds.')

pyi_builder.test_source("", pyi_args=["--contents-directory", "foo"])
exe, = pyi_builder._find_executables("test_source")
bundle = pathlib.Path(exe).parent
Expand All @@ -874,13 +867,11 @@ def test_contents_directory(pyi_builder):
pyi_builder.test_source("", pyi_args=["--contents-directory", "..", "--noconfirm"])


@onedir_only
def test_legacy_onedir_layout(pyi_builder):
"""
Test the --contents-directory=., which re-enables the legacy onedir layout.
"""
if pyi_builder._mode != 'onedir':
pytest.skip('--contents-directory does not affect onefile builds.')

pyi_builder.test_source(
"""
import sys
Expand All @@ -896,24 +887,21 @@ def test_legacy_onedir_layout(pyi_builder):
)


def test_spec_options(pyi_builder, spec_dir, capsys):
if pyi_builder._mode != 'onedir':
pytest.skip('spec file is onedir mode only')

pyi_builder.test_spec(
def test_spec_options(pyi_builder_spec, spec_dir, capsys):
pyi_builder_spec.test_spec(
spec_dir / "pyi_spec_options.spec",
pyi_args=["--", "--optional-dependency", "email", "--optional-dependency", "gzip"]
)
exe, = pyi_builder._find_executables("pyi_spec_options")
exe, = pyi_builder_spec._find_executables("pyi_spec_options")
p = subprocess.run([exe], stdout=subprocess.PIPE, encoding="utf-8")
assert p.stdout == "Available dependencies: email gzip\n"

capsys.readouterr()
with pytest.raises(SystemExit) as ex:
pyi_builder.test_spec(spec_dir / "pyi_spec_options.spec", pyi_args=["--", "--help"])
pyi_builder_spec.test_spec(spec_dir / "pyi_spec_options.spec", pyi_args=["--", "--help"])
assert ex.value.code == 0
assert "help blah blah blah" in capsys.readouterr().out

with pytest.raises(SystemExit) as ex:
pyi_builder.test_spec(spec_dir / "pyi_spec_options.spec", pyi_args=["--", "--onefile"])
pyi_builder_spec.test_spec(spec_dir / "pyi_spec_options.spec", pyi_args=["--", "--onefile"])
assert "pyi_spec_options.spec: error: unrecognized arguments: --onefile" in capsys.readouterr().err
5 changes: 3 additions & 2 deletions tests/functional/test_binary_vs_data_reclassification.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import pytest

import PyInstaller.utils.misc as miscutils
from PyInstaller.utils.tests import onedir_only


# Helpers
Expand Down Expand Up @@ -60,7 +61,7 @@ def _create_test_build(pyi_builder, tmp_path, datas=None, binaries=None):
@pytest.mark.linux
@pytest.mark.win32
@pytest.mark.darwin
@pytest.mark.parametrize('pyi_builder', ['onedir'], indirect=True) # Run only in onedir mode.
@onedir_only
def test_automatic_reclassification_data_file(pyi_builder, tmp_path):
binaries = []

Expand All @@ -86,7 +87,7 @@ def test_automatic_reclassification_data_file(pyi_builder, tmp_path):
@pytest.mark.linux
@pytest.mark.win32
@pytest.mark.darwin
@pytest.mark.parametrize('pyi_builder', ['onedir'], indirect=True) # Run only in onedir mode.
@onedir_only
def test_automatic_reclassification_binary(pyi_builder, tmp_path):
datas = []

Expand Down
34 changes: 17 additions & 17 deletions tests/functional/test_macos_bundle_structure.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

import pytest

from PyInstaller.utils.tests import importorskip
from PyInstaller.utils.tests import importorskip, onedir_only
from PyInstaller.building.osx import DOT_REPLACEMENT

# NOTE: the tests below explicitly enable the following environment variables:
Expand All @@ -29,7 +29,7 @@
# Test that collected metadata is properly relocated to avoid codesign errors due to directory containing dots in name.
@pytest.mark.darwin
@importorskip('psutil')
@pytest.mark.parametrize('pyi_builder', ['onedir'], indirect=True) # Run only in onedir mode.
@onedir_only
def test_macos_bundle_signing_metadata(pyi_builder, monkeypatch):
monkeypatch.setenv("PYINSTALLER_STRICT_BUNDLE_CODESIGN_ERROR", "1")
monkeypatch.setenv("PYINSTALLER_VERIFY_BUNDLE_SIGNATURE", "1")
Expand All @@ -42,7 +42,7 @@ def test_macos_bundle_signing_metadata(pyi_builder, monkeypatch):
# Test that the bundle signing works even if we collect a package as source .py files, which we do not relocate.
@pytest.mark.darwin
@importorskip('psutil')
@pytest.mark.parametrize('pyi_builder', ['onedir'], indirect=True) # Run only in onedir mode.
@onedir_only
def test_macos_bundle_signing_py_files(pyi_builder, monkeypatch):
monkeypatch.setenv("PYINSTALLER_STRICT_BUNDLE_CODESIGN_ERROR", "1")
monkeypatch.setenv("PYINSTALLER_VERIFY_BUNDLE_SIGNATURE", "1")
Expand All @@ -64,7 +64,7 @@ def AnalysisOverride(*args, **kwargs):
# Test that the codesigning works even if we collect a package as .pyc files, which we do not relocate.
@pytest.mark.darwin
@importorskip('psutil')
@pytest.mark.parametrize('pyi_builder', ['onedir'], indirect=True) # Run only in onedir mode.
@onedir_only
def test_macos_bundle_signing_pyc_files(pyi_builder, monkeypatch):
monkeypatch.setenv("PYINSTALLER_STRICT_BUNDLE_CODESIGN_ERROR", "1")
monkeypatch.setenv("PYINSTALLER_VERIFY_BUNDLE_SIGNATURE", "1")
Expand Down Expand Up @@ -148,7 +148,7 @@ def _create_test_framework(bundle_path):

# Test that top-level data file is relocated into `Contents/Resources` and symlinked back into `Contents/Frameworks`.
@pytest.mark.darwin
@pytest.mark.parametrize('pyi_builder', ['onedir'], indirect=True) # Run only in onedir mode.
@onedir_only
def test_macos_bundle_layout_data_file(pyi_builder, monkeypatch, tmp_path):
datas = []

Expand All @@ -172,7 +172,7 @@ def test_macos_bundle_layout_data_file(pyi_builder, monkeypatch, tmp_path):

# Test that top-level binary is kept in `Contents/Frameworks` and symlinked into `Contents/Resources`.
@pytest.mark.darwin
@pytest.mark.parametrize('pyi_builder', ['onedir'], indirect=True) # Run only in onedir mode.
@onedir_only
def test_macos_bundle_layout_binary(pyi_builder, monkeypatch, tmp_path):
binaries = []

Expand All @@ -196,7 +196,7 @@ def test_macos_bundle_layout_binary(pyi_builder, monkeypatch, tmp_path):

# Test that data-only directory is relocated into `Contents/Resources` and symlinked back into `Contents/Frameworks`.
@pytest.mark.darwin
@pytest.mark.parametrize('pyi_builder', ['onedir'], indirect=True) # Run only in onedir mode.
@onedir_only
def test_macos_bundle_layout_data_only_dir(pyi_builder, monkeypatch, tmp_path):
datas = []

Expand Down Expand Up @@ -247,7 +247,7 @@ def test_macos_bundle_layout_data_only_dir(pyi_builder, monkeypatch, tmp_path):

# Test that binary-only directory is kept in `Contents/Frameworks` and symlinked into `Contents/Resources`.
@pytest.mark.darwin
@pytest.mark.parametrize('pyi_builder', ['onedir'], indirect=True) # Run only in onedir mode.
@onedir_only
def test_macos_bundle_layout_binary_only_dir(pyi_builder, monkeypatch, tmp_path):
binaries = []

Expand Down Expand Up @@ -299,7 +299,7 @@ def test_macos_bundle_layout_binary_only_dir(pyi_builder, monkeypatch, tmp_path)
# Test that mxied-content directory is created in both `Contents/Frameworks` and `Contents/Resources`, and that files
# are put into the proper directory and cross-linked into the other directory.
@pytest.mark.darwin
@pytest.mark.parametrize('pyi_builder', ['onedir'], indirect=True) # Run only in onedir mode.
@onedir_only
def test_macos_bundle_layout_mixed_dir(pyi_builder, monkeypatch, tmp_path):
datas = []
binaries = []
Expand Down Expand Up @@ -353,7 +353,7 @@ def test_macos_bundle_layout_mixed_dir(pyi_builder, monkeypatch, tmp_path):
# Repeat the test with mixed-content directory, except that it now contains three sub-directories: a data-only one,
# a binary-only one, and mixed-content one.
@pytest.mark.darwin
@pytest.mark.parametrize('pyi_builder', ['onedir'], indirect=True) # Run only in onedir mode.
@onedir_only
def test_macos_bundle_layout_mixed_dir_with_subdirs(pyi_builder, monkeypatch, tmp_path):
datas = []
binaries = []
Expand Down Expand Up @@ -474,7 +474,7 @@ def test_macos_bundle_layout_mixed_dir_with_subdirs(pyi_builder, monkeypatch, tm
# Repeat the test with mixed-content directory and sub-directories, except that all directories now contain a dot in
# their names.
@pytest.mark.darwin
@pytest.mark.parametrize('pyi_builder', ['onedir'], indirect=True) # Run only in onedir mode.
@onedir_only
def test_macos_bundle_layout_mixed_dir_with_subdirs_and_dots(pyi_builder, monkeypatch, tmp_path):
datas = []
binaries = []
Expand Down Expand Up @@ -632,7 +632,7 @@ def test_macos_bundle_layout_mixed_dir_with_subdirs_and_dots(pyi_builder, monkey

# Test with symlink in top-level directory pointing to a data file in data-only directory.
@pytest.mark.darwin
@pytest.mark.parametrize('pyi_builder', ['onedir'], indirect=True) # Run only in onedir mode.
@onedir_only
def test_macos_bundle_layout_symlink_into_data_dir(pyi_builder, monkeypatch, tmp_path):
datas = []

Expand Down Expand Up @@ -686,7 +686,7 @@ def test_macos_bundle_layout_symlink_into_data_dir(pyi_builder, monkeypatch, tmp

# Test with symlink in top-level directory pointing to a binary in binary-only directory.
@pytest.mark.darwin
@pytest.mark.parametrize('pyi_builder', ['onedir'], indirect=True) # Run only in onedir mode.
@onedir_only
def test_macos_bundle_layout_symlink_into_binary_dir(pyi_builder, monkeypatch, tmp_path):
binaries = []

Expand Down Expand Up @@ -740,7 +740,7 @@ def test_macos_bundle_layout_symlink_into_binary_dir(pyi_builder, monkeypatch, t

# Test with symlinks in top-level directory pointing to files in mixed-content directory.
@pytest.mark.darwin
@pytest.mark.parametrize('pyi_builder', ['onedir'], indirect=True) # Run only in onedir mode.
@onedir_only
def test_macos_bundle_layout_symlink_into_mixed_dir(pyi_builder, monkeypatch, tmp_path):
datas = []
binaries = []
Expand Down Expand Up @@ -829,7 +829,7 @@ def test_macos_bundle_layout_symlink_into_mixed_dir(pyi_builder, monkeypatch, tm
# This implicitly also tests that we do not replace the dot in the .framework bundle's directory name (the .framework
# bundle directories are the only directories in `Contents/Frameworks` that are allowed to have a dot in name).
@pytest.mark.darwin
@pytest.mark.parametrize('pyi_builder', ['onedir'], indirect=True) # Run only in onedir mode.
@onedir_only
def test_macos_bundle_layout_framework_in_top_level(pyi_builder, monkeypatch, tmp_path):
datas = []
binaries = []
Expand Down Expand Up @@ -940,7 +940,7 @@ def test_macos_bundle_layout_framework_in_top_level(pyi_builder, monkeypatch, tm

# Test with .framework bundle in binary-only directory and framework's binary symlinked to top-level directory.
@pytest.mark.darwin
@pytest.mark.parametrize('pyi_builder', ['onedir'], indirect=True) # Run only in onedir mode.
@onedir_only
def test_macos_bundle_layout_framework_in_binary_dir(pyi_builder, monkeypatch, tmp_path):
datas = []
binaries = []
Expand Down Expand Up @@ -1079,7 +1079,7 @@ def test_macos_bundle_layout_framework_in_binary_dir(pyi_builder, monkeypatch, t
# This also tests that a directory is recognized as a mixed-content one if it contains a data file and a .framework
# bundle (i.e., no other binary files).
@pytest.mark.darwin
@pytest.mark.parametrize('pyi_builder', ['onedir'], indirect=True) # Run only in onedir mode.
@onedir_only
def test_macos_bundle_layout_framework_in_mixed_dir(pyi_builder, monkeypatch, tmp_path):
datas = []
binaries = []
Expand Down
5 changes: 2 additions & 3 deletions tests/functional/test_misc.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import pytest

from PyInstaller import compat
from PyInstaller.utils.tests import onefile_only

# Directory with testing modules used in some tests.
_MODULES_DIR = pathlib.Path(__file__).parent / 'modules'
Expand Down Expand Up @@ -178,10 +179,8 @@ def test_disable_hash_randomization(pyi_builder):


# Test that onefile cleanup does not remove contents of a directory that user symlinks into sys._MEIPASS (see #6074).
@onefile_only
def test_onefile_cleanup_symlinked_dir(pyi_builder, tmp_path):
if pyi_builder._mode != 'onefile':
pytest.skip('The test is relevant only to onefile builds.')

# Create output directory with five pre-existing files
output_dir = tmp_path / 'output_dir'
output_dir.mkdir()
Expand Down
13 changes: 4 additions & 9 deletions tests/functional/test_pkgutil.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
import pytest

from PyInstaller.compat import exec_python_rc
from PyInstaller.utils.tests import importable
from PyInstaller.utils.tests import importable, onedir_only, onefile_only

# Directory with testing modules used in some tests.
_MODULES_DIR = pathlib.Path(__file__).parent / 'modules'
Expand Down Expand Up @@ -98,9 +98,8 @@ def test_pkgutil_iter_modules(package, script_dir, tmp_path, pyi_builder, archiv
# ensure proper matching.
# The test is applicable only to macOS in onefile mode.
@pytest.mark.darwin
@onefile_only
def test_pkgutil_iter_modules_resolve_pkg_path(script_dir, tmp_path, pyi_builder):
if pyi_builder._mode != 'onefile':
pytest.skip('The test is applicable only to onefile mode.')
# A single combination (altgraph package, archive mode) is enough to check for proper symlink handling.
test_pkgutil_iter_modules('json', script_dir, tmp_path, pyi_builder, archive=True, resolve_pkg_path=True)

Expand All @@ -118,10 +117,8 @@ def test_pkgutil_iter_modules_resolve_pkg_path(script_dir, tmp_path, pyi_builder
# data and binary files, the directory is created in both Contents/Frameworks and Contents/Resources, and the contents
# are cross-linked between them on file level.
@pytest.mark.darwin
@onedir_only
def test_pkgutil_iter_modules_macos_app_bundle(script_dir, tmp_path, pyi_builder, monkeypatch):
if pyi_builder._mode != 'onedir':
pytest.skip('The test is applicable only to onedir mode.')

pathex = _MODULES_DIR / 'pyi_pkgutil_itermodules' / 'package'
hooks_dir = _MODULES_DIR / 'pyi_pkgutil_itermodules' / 'hooks'
package = 'mypackage'
Expand Down Expand Up @@ -181,10 +178,8 @@ def test_pkgutil_iter_modules_macos_app_bundle(script_dir, tmp_path, pyi_builder
#
# This is more explicit version of test_pkgutil_iter_modules_macos_app_bundle, just in case.
@pytest.mark.darwin
@onedir_only
def test_pkgutil_iter_modules_macos_app_bundle_alternative_search_path(pyi_builder):
if pyi_builder._mode != 'onedir':
pytest.skip('The test is applicable only to onedir mode.')

pyi_builder.test_source(
"""
import os
Expand Down
Loading

0 comments on commit c99fe9c

Please sign in to comment.