def test_overwrite_file(self, tmpcwd):
     '''
     Test file overwrites
     '''
     create_project(project1) # Note this template is such that each of the files that be overwritten will require an overwrite
     overwritten_files = [path for path, requirements in project_file_requirements.items() if requirements.permission == Permission.overwrite]
     with assert_file_access(*overwritten_files, written=True, contents_changed=True):
         mkproject & pb.FG
     
     for path in overwritten_files:
         requirements = project_file_requirements[path]
         content = read_file(path)
         requirements.verify_default_content(content, project1.format_kwargs)
 def test_new(self, tmpcwd):
     '''
     When project.py missing, ask for a name and generate template
     '''
     # When project.py is missing and git repo exists but has no commits
     create_project()
     remove_file(Path('project.py'))
     (mkproject << '{name}\n{pkg_name}\n{human_friendly_name}\n'.format(**project1.format_kwargs))()
     
     # Then project.py is created and contains the defaults we want
     project_py_path = Path('project.py')
     assert project_py_path.exists()
     assert read_file(project_py_path) == spec.project_py.format(**project1.format_kwargs)
     assert eval_file(project_py_path)['project'] == project_defaults  # double-check
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_update_file(self, tmpcwd):
     '''
     Test updates to files:
     
     - file is updated to match requirements
     - file also still contains original contents
     
     Specifically:
     
     - $project_name/__init__.py: leaves all intact, just inserts/overwrites __version__
     - .gitignore: leaves previous patterns intact, does insert the new patterns
     - setup.cfg: leaves previous intact, except for some lines designated to be overwritten
     - ...
     '''
     create_project(project1) # Note this template is such that each of the files will require an update
     updated_files = [path for path, requirements in project_file_requirements.items() if requirements.permission == Permission.update]
     with assert_file_access(*updated_files, written=True, contents_changed=True):
         mkproject & pb.FG
     
     for path in updated_files:
         requirements = project_file_requirements[path]
         content = read_file(path)
         requirements.verify_updated_content(content, project1.files[path], project1.format_kwargs)
 def test_missing_file(self, tmpcwd, missing_path, missing_requirements):
     '''
     Test handling of missing files:
     
     - When files are missing, create them if allowed, error otherwise
     - When files are present and may not be updated, they are left untouched
     '''
     create_project()
     remove_file(missing_path)
     if missing_requirements.permission == Permission.none:
         # When can't create, must raise error
         with assert_process_fails(stderr_matches=missing_path.name):
             mkproject()
     else:
         # When can create, create
         with ExitStack() as contexts:
             for file, requirements in project_file_requirements.items():
                 if missing_path != file and missing_path not in file.parents and requirements.permission <= Permission.create:
                     contexts.enter_context(assert_file_access(file, written=False, contents_changed=False))
             mkproject & pb.FG
         assert missing_path.exists()
         content = read_file(missing_path)
         missing_requirements.verify_default_content(content, project1.format_kwargs)
 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