def test_sip_install(tmpcwd): ''' When SIP dependencies, install them correctly ''' project = project1.copy() project.project_py['entry_points'] = { 'console_scripts': [ 'mycli = operation.mittens.main:main', ], } project.files[Path( 'requirements.in')] = 'pytest\nsip==4.17\nPyQt5==5.5.1\n' project.files[Path('operation/mittens/main.py')] = dedent('''\ import pytest import PyQt5 def main(): print('meow') ''') create_project(project) pb.local['ct-mkproject']() # When mkvenv, all good result = mkvenv() assert result.exit_code == 0, result.output assert Path('venv/bin/python3.5').exists( ) # project.py requested python version 3.5 # When call mycli, all good stdout = pb.local['venv/bin/mycli']() assert 'meow' in stdout
def test_wrong_readme_file_name(tmpcwd, name): project = project1.copy() project.project_py['readme_file'] = name create_project(project) Path(name).touch() with assert_process_fails(stderr_matches='readme_file'): mkproject()
def test_has_unknown_attr(self, tmpcwd, unknown_attr): ''' When `project` contains an unknown attribute, abort ''' project = project1.copy() project.project_py[unknown_attr] = 'value' create_project(project) with assert_process_fails(stderr_matches=unknown_attr): mkproject()
def test_missing_required_attr(self, tmpcwd, required_attr): ''' When `project` lacks a required attribute, abort ''' project = project1.copy() del project.project_py[required_attr] create_project(project) with assert_process_fails(stderr_matches='Missing.+{}'.format(required_attr)): mkproject()
def test_editable_requirements(tmpcwd): ''' When requirements.txt contains editable dependencies (-e), error ''' project = project1.copy() add_complex_requirements_in(project) create_release_project(project) result = release('1.0.0') assert result.exit_code != 0 assert 'No editable requirements (-e) allowed for release' in result.output
def project(self): project = project1.copy() test_succeed_path = Path('operation/mittens/tests/test_succeed.py') project.files = { Path('requirements.in'): project.files[Path('requirements.in')], Path('LICENSE.txt'): project.files[Path('LICENSE.txt')], Path('README.md'): project.files[Path('README.md')], test_succeed_path: extra_files[test_succeed_path], } return project
def test_unpinned_sip_dependency(self, tmpcwd): ''' When sip based dependency is not pinned, error ''' project = project1.copy() project.files[Path('requirements.in')] = 'pytest\nPyQt5\n' create_project(project) with assert_process_fails(stderr_matches=r"(?i)'PyQt5' .* pin"): mkproject()
def test_attr_has_invalid_value(self, tmpcwd, attr, value): ''' When '\s*' or None as attr values, abort When dashes or whitespace in name, abort ''' project = project1.copy() project.project_py[attr] = value create_project(project) with assert_process_fails(stderr_matches=attr): mkproject()
def test_mkdoc(tmpcwd): '''When happy days and a file with proper docstring, generate ./doc''' # Setup project = project1.copy() description = 'Meow meow n meow meow meow' add_docstring(project, description) create_project(project) # Run mkproject() pb.local['ct-mkdoc']() # Assert content = read_file('docs/build/html/index.html') assert '0.0.0' in content # correct version assert project.project_py['human_friendly_name'] in content # human friendly project name
def test_sip_dependency(self, tmpcwd): ''' When sip based dependency, do not put it in setup.py or requirements.txt ''' project = project1.copy() project.files[Path('requirements.in')] = 'pytest\nsip==4.17\nPyQt5==5.5.1\n' create_project(project) mkproject & pb.FG setup_args = get_setup_args() assert setup_args['install_requires'] == ['pytest'] requirements_txt = read_file('requirements.txt') assert 'sip' not in requirements_txt assert not re.match('(?i)pyqt5', requirements_txt)
def test_setup_py(self, tmpcwd): ''' Test generated setup.py and requirements.txt - install_requires: requirements.in transformed into valid dependency list with version specs maintained - long_description present and nonempty - classifiers: list of str - packages: list of str of packages - package_data: dict of package -> list of str of data file paths - author, author_email, description, entry_points keywords license name, url: exact same as input ''' project = project1.copy() project.project_py['entry_points'] = project_defaults['entry_points'] add_complex_requirements_in(project) project.files[Path('my_extra_requirements.in')] = 'checksumdir\npytest-pep8\n' del project.files[Path('test_requirements.in')] # Create package_data in operation/mittens/tests (it actually may be in non-test as well): project.files[Path('operation/mittens/tests/data/subdir/file1')] = '' project.files[Path('operation/mittens/tests/data/subdir/file2')] = '' project.files[Path('operation/mittens/tests/not_data/file')] = '' project.files[Path('operation/mittens/tests/not_data/data/file')] = '' project.files[Path('operation/mittens/tests/pkg/__init__.py')] = '' project.files[Path('operation/mittens/tests/pkg/data/file')] = '' # create_project(project) # Run mkproject & pb.FG # Assert setup.py args setup_args = get_setup_args() for attr in ('name', 'author', 'author_email', 'description', 'keywords', 'license', 'url'): assert setup_args[attr] == project.project_py[attr].strip() assert setup_args['entry_points'] == project.project_py['entry_points'] assert setup_args['long_description'].strip() assert set(setup_args['classifiers']) == {'Development Status :: 2 - Pre-Alpha', 'Programming Language :: Python :: Implementation :: Stackless'} assert set(setup_args['packages']) == {'operation', 'operation.mittens', 'operation.mittens.tests', 'operation.mittens.tests.pkg'} assert {k:set(v) for k,v in setup_args['package_data'].items()} == { 'operation.mittens.tests' : {'data/subdir/file1', 'data/subdir/file2'}, 'operation.mittens.tests.pkg' : {'data/file'}, } assert set(setup_args['install_requires']) == {'pytest', 'pytest-testmon<5.0.0', 'pytest-env==0.6', 'pkg4', 'pytest-cov'} assert set(setup_args['extras_require'].keys()) == {'my_extra', 'test', 'dev'} assert set(setup_args['extras_require']['my_extra']) == {'checksumdir', 'pytest-pep8'} assert set(setup_args['extras_require']['test']) == set(spec.test_requirements_in) assert setup_args['version'] == '0.0.0' # requirements.txt must contain relevant packages, including optional dependencies requirements_txt_content = read_file('requirements.txt') for name in {'pytest', 'pytest-testmon', 'pytest-env', 'pkg_magic', 'pytest-cov', 'checksumdir', 'pytest-pep8'} | set(spec.test_requirements_in) | set(spec.dev_requirements_in): assert name in requirements_txt_content #TODO may be able to simplify tests a bit as we no longer care about *.in order. In the future we may get rid of *requirements.in files altogether though # Requirements.txt must be sorted like pip-compile #TODO hard to test, exact order used is: https://github.com/nvie/pip-tools/blob/master/piptools/writer.py#L27 # maybe 2 local setup.py and 2 trivial pypi packages that have no dependencies and won't have any in the near/distant future deps_txt = [get_dependency_name(line[0], line[1]) for line in parse_requirements_file(Path('requirements.txt')) if line[1]] # and must contain the dependencies of all *requirements.in files for path in map(Path, ('requirements.in', 'my_extra_requirements.in', 'test_requirements.in')): deps_in = [get_dependency_name(line[0], line[1]) for line in parse_requirements_file(path) if line[1]] assert set(deps_in).issubset(set(deps_txt)) # Multiple runs yield the same setup.py each time with open('setup.py') as f: expected = f.read() for _ in range(5): mkproject() with open('setup.py') as f: actual = f.read() assert actual == expected