Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rework Package identification tests #229

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
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
2 changes: 2 additions & 0 deletions test/spell_check.words
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ deps
descs
getpid
iterdir
localhost
lstrip
mkdtemp
monkeypatch
Expand Down Expand Up @@ -54,3 +55,4 @@ tuples
unlinking
veyor
wildcards
xfail
256 changes: 192 additions & 64 deletions test/test_package_identification_python.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
# Copyright 2016-2018 Dirk Thomas
# Copyright 2019 Rover Robotics
# Licensed under the Apache License, Version 2.0

from pathlib import Path
from tempfile import TemporaryDirectory
import re

from colcon_core.package_descriptor import PackageDescriptor
from colcon_core.package_identification.python \
Expand All @@ -12,69 +11,198 @@
import pytest


def test_identify():
@pytest.fixture
def package_descriptor(tmp_path):
"""Create package descriptor and fail the test if its path changes."""
desc = PackageDescriptor(tmp_path)
yield desc
assert str(desc.path) == str(tmp_path)


@pytest.fixture
def unchanged_empty_descriptor(package_descriptor):
"""Create package descriptor and fail the test if it changes."""
yield package_descriptor
assert package_descriptor.name is None
assert package_descriptor.type is None


@pytest.mark.xfail
def test_error_in_setup_py(unchanged_empty_descriptor):
setup_py = unchanged_empty_descriptor.path / 'setup.py'
error_text = 'My hovercraft is full of eels'
setup_py.write_text('raise OverflowError({!r})'.format(error_text))

extension = PythonPackageIdentification()
with pytest.raises(RuntimeError) as e:
extension.identify(unchanged_empty_descriptor)

assert e.match('Failure when trying to run setup script')
assert e.match(re.escape(str(setup_py)))

# details of the root cause should be in error string
assert e.match('OverflowError')
assert e.match(error_text)


def test_missing_setup_py(unchanged_empty_descriptor):
extension = PythonPackageIdentification()
# should not raise
extension.identify(unchanged_empty_descriptor)


@pytest.mark.xfail
def test_empty_setup_py(unchanged_empty_descriptor):
extension = PythonPackageIdentification()
(unchanged_empty_descriptor.path / 'setup.py').write_text('')
with pytest.raises(RuntimeError) as e:
extension.identify(unchanged_empty_descriptor)
assert e.match('not a Distutils setup script')


@pytest.mark.xfail
def test_setup_py_no_name(unchanged_empty_descriptor):
extension = PythonPackageIdentification()
(unchanged_empty_descriptor.path / 'setup.py').write_text(
'import setuptools; setuptools.setup(name="")')
with pytest.raises(RuntimeError):
extension.identify(unchanged_empty_descriptor)


def test_re_identify_if_non_python_package(package_descriptor):
package_descriptor.name = 'other-package'
package_descriptor.type = 'other'
extension = PythonPackageIdentification()
extension.identify(package_descriptor)
assert package_descriptor.name == 'other-package'
assert package_descriptor.type == 'other'


def test_re_identify_python_if_same_python_package(package_descriptor):
package_descriptor.name = 'my-package'
package_descriptor.type = 'python'

extension = PythonPackageIdentification()
(package_descriptor.path / 'setup.py').write_text(
'import setuptools; setuptools.setup(name="my-package")')

extension.identify(package_descriptor)
assert package_descriptor.name == 'my-package'
assert package_descriptor.type == 'python'


@pytest.mark.xfail
def test_re_identify_python_if_different_python_package(package_descriptor):
package_descriptor.name = 'other-package'
package_descriptor.type = 'python'

extension = PythonPackageIdentification()
(package_descriptor.path / 'setup.py').write_text(
'import setuptools; setuptools.setup(name="my-package")')

with pytest.raises(RuntimeError):
extension.identify(package_descriptor)

assert package_descriptor.name == 'other-package'
assert package_descriptor.type == 'python'


def test_minimal_cfg(package_descriptor):
extension = PythonPackageIdentification()

(package_descriptor.path / 'setup.py').write_text(
'import setuptools; setuptools.setup()')
(package_descriptor.path / 'setup.cfg').write_text(
'[metadata]\nname = pkg-name')

extension.identify(package_descriptor)

# descriptor should be unchanged
assert package_descriptor.name == 'pkg-name'
assert package_descriptor.type == 'python'


def test_requires(package_descriptor):
extension = PythonPackageIdentification()

(package_descriptor.path / 'setup.py').write_text(
'import setuptools; setuptools.setup()')

(package_descriptor.path / 'setup.cfg').write_text(
'[metadata]\n'
'name = pkg-name\n'
'[options]\n'
'setup_requires =\n'
' setuptools; sys_platform != "imaginary_platform"\n'
' imaginary-package; sys_platform == "imaginary_platform"\n'
'install_requires =\n'
' runA > 1.2.3\n'
' runB\n'
'tests_require = test == 2.0.0\n'
# prevent trying to look for setup_requires in the Package Index
'[easy_install]\n'
'allow_hosts = localhost\n')

extension.identify(package_descriptor)
assert package_descriptor.name == 'pkg-name'
assert package_descriptor.type == 'python'
assert package_descriptor.dependencies.keys() == {'build', 'run', 'test'}
assert package_descriptor.dependencies == {
'build': {'setuptools', 'imaginary-package'},
'run': {'runA', 'runB'},
'test': {'test'}
}
for dep in package_descriptor.dependencies['run']:
if dep == 'runA':
assert dep.metadata['version_gt'] == '1.2.3'

assert package_descriptor.dependencies['run']
assert package_descriptor.dependencies['run'] == {'runA', 'runB'}


def test_metadata_options(package_descriptor):
(package_descriptor.path / 'setup.py').write_text(
'import setuptools; setuptools.setup()')

(package_descriptor.path / 'setup.cfg').write_text(
'[metadata]\n'
'name = pkg-name\n'
'[options]\n'
'zip_safe = false\n'
'packages = find:\n')

(package_descriptor.path / 'my_module').mkdir()
(package_descriptor.path / 'my_module' / '__init__.py').touch()

extension = PythonPackageIdentification()
extension.identify(package_descriptor)

options = package_descriptor.metadata['get_python_setup_options'](None)
assert options['zip_safe'] is False
assert options['packages'] == ['my_module']


@pytest.mark.xfail
def test_metadata_options_dynamic(package_descriptor):
(package_descriptor.path / 'setup.py').write_text(
'import setuptools; setuptools.setup()')
(package_descriptor.path / 'version_helper.py').write_text(
'import os; version = os.environ["version"]'
)

(package_descriptor.path / 'setup.cfg').write_text(
'[metadata]\n'
'name = my-package\n'
'version = attr: version_helper.version\n'
)

extension = PythonPackageIdentification()
extension.identify(package_descriptor)

with TemporaryDirectory(prefix='test_colcon_') as basepath:
desc = PackageDescriptor(basepath)
desc.type = 'other'
assert extension.identify(desc) is None
assert desc.name is None

desc.type = None
assert extension.identify(desc) is None
assert desc.name is None
assert desc.type is None

basepath = Path(basepath)
(basepath / 'setup.py').write_text('setup()')
assert extension.identify(desc) is None
assert desc.name is None
assert desc.type is None

(basepath / 'setup.cfg').write_text('')
assert extension.identify(desc) is None
assert desc.name is None
assert desc.type is None

(basepath / 'setup.cfg').write_text(
'[metadata]\n'
'name = pkg-name\n')
assert extension.identify(desc) is None
assert desc.name == 'pkg-name'
assert desc.type == 'python'

desc.name = 'other-name'
with pytest.raises(RuntimeError) as e:
extension.identify(desc)
assert str(e.value).endswith(
'Package name already set to different value')

(basepath / 'setup.cfg').write_text(
'[metadata]\n'
'name = other-name\n'
'[options]\n'
'setup_requires =\n'
" build; sys_platform != 'win32'\n"
" build-windows; sys_platform == 'win32'\n"
'install_requires =\n'
' runA > 1.2.3\n'
' runB\n'
'tests_require = test == 2.0.0\n'
'zip_safe = false\n')
assert extension.identify(desc) is None
assert desc.name == 'other-name'
assert desc.type == 'python'
assert set(desc.dependencies.keys()) == {'build', 'run', 'test'}
assert desc.dependencies['build'] == {'build', 'build-windows'}
assert desc.dependencies['run'] == {'runA', 'runB'}
dep = next(x for x in desc.dependencies['run'] if x == 'runA')
assert dep.metadata['version_gt'] == '1.2.3'
assert desc.dependencies['test'] == {'test'}

assert callable(desc.metadata['get_python_setup_options'])
options = desc.metadata['get_python_setup_options'](None)
assert 'zip_safe' in options
for version in ('1.0', '1.1'):
options = package_descriptor.metadata['get_python_setup_options'](
{'version': version})
assert options['metadata'].version == version


def test_create_dependency_descriptor():
Expand Down