async def test_include_ros_base(): pkg = PackageDescriptor('package/path') pkg.name = 'MyPackageName' pkg.dependencies['run'] = {} installers = { 'apt': MagicMock(), } top_level_args = MagicMock(build_base='build/base', install_base='install/base', bundle_base='bundle/base') args = BundlePackageArguments(pkg, installers, top_level_args) args.ros_distribution = 'kinetic' args.exclude_ros_base = False context = TaskContext(pkg=pkg, args=args, dependencies={}) task = RosBundle() task.set_context(context=context) # Concise read on why it's patched this way. # http://www.voidspace.org.uk/python/mock/patch.html#where-to-patch with patch('colcon_ros_bundle.task.ros_bundle.RosdepWrapper' ) as wrapper: # noqa: E501 with patch('os.environ') as environ: environ.__getitem__.side_effect = access_var wrapper().get_rule.side_effect = _get_rule_side_effect wrapper().resolve.side_effect = _resolve_side_effect await task.bundle() installers['apt'].add_to_install_list.assert_called_with( 'ros-kinetic-ros-base')
def test_identify(): extension = PythonPackageIdentification() 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('') 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).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
async def test_rosdistro_not_defined(): pkg = PackageDescriptor('package/path') pkg.name = 'MyPackageName' pkg.dependencies['run'] = {} installers = { 'apt': MagicMock(), } top_level_args = MagicMock(build_base='build/base', install_base='install/base', bundle_base='bundle/base') args = BundlePackageArguments(pkg, installers, top_level_args) context = TaskContext(pkg=pkg, args=args, dependencies={}) task = RosBundle() task.set_context(context=context) # Concise read on why it's patched this way. # http://www.voidspace.org.uk/python/mock/patch.html#where-to-patch with patch('colcon_ros_bundle.task.ros_bundle.RosdepWrapper') as wrapper: # noqa: E501 with pytest.raises(RuntimeError): wrapper().get_rule.side_effect = _get_rule_side_effect wrapper().resolve.side_effect = _resolve_side_effect await task.bundle() installers['apt'].add_to_install_list.assert_not_called()
async def test_bundle(): descriptor = PackageDescriptor('some/path') descriptor.name = 'python_package' deps = [DependencyDescriptor('pkg1', metadata={'version_eq': '1.3.2'}), DependencyDescriptor('pkg_in_workspace'), DependencyDescriptor('pkg2', metadata={'version_lt': '1.2'}), 'ignored_pkg'] descriptor.dependencies['run'] = deps workspace = {'pkg_in_workspace': 'path/to/pkg'} task_args = MagicMock(build_base='build/base', install_base='install/base', bundle_base='bundle/base') pip_installer = MagicMock() apt_installer = MagicMock() args = BundlePackageArguments( descriptor, {'pip3': pip_installer, 'apt': apt_installer}, task_args) context = TaskContext(pkg=descriptor, args=args, dependencies=workspace) task = PythonBundleTask() task.set_context(context=context) await task.bundle() pip_installer.add_to_install_list.assert_called_once_with('pkg1==1.3.2') apt_calls = apt_installer.add_to_install_list.call_args_list assert len(apt_calls) == 2 assert apt_calls[0][0][0] == 'libpython3-dev' assert apt_calls[1][0][0] == 'python3-pip'
def test_no_augmentation(): augmentation_extension = DirhashPackageAugmentation() with TemporaryDirectory(prefix='test_colcon_') as basepath: desc = PackageDescriptor(basepath) desc.metadata['vcs_type'] = 'default' augmentation_extension.augment_package(desc) assert desc.metadata['vcs_type'] == 'default'
def test_identifies_package(): d = PackageDescriptor('/some/path') assert not d.identifies_package() d.type = 'type' assert not d.identifies_package() d.type = None d.name = 'name' assert not d.identifies_package() d.type = 'type' assert d.identifies_package()
def test_augment_packages(): desc1 = PackageDescriptor('/some/path') desc2 = PackageDescriptor('/other/path') descs = {desc1, desc2} with EntryPointContext(extension1=Extension1, extension2=Extension2): extensions = get_package_augmentation_extensions() extensions['extension1'].augment_package = Mock( side_effect=augment_package_metadata_with_data) extensions['extension2'].augment_package = Mock( side_effect=augment_package_metadata_with_path) augment_packages(descs) assert len(desc1.metadata) == 2 assert set(desc1.metadata.keys()) == {'key', 'path'} assert desc1.path == desc1.metadata['path'] assert len(desc2.metadata) == 1 assert set(desc2.metadata.keys()) == {'path'} assert desc2.path == desc2.metadata['path'] # raise exception desc1 = PackageDescriptor('/some/path') desc2 = PackageDescriptor('/other/path') descs = {desc1, desc2} with EntryPointContext(extension1=Extension1, extension2=Extension2): extensions = get_package_augmentation_extensions() extensions['extension1'].augment_package = Mock( side_effect=augment_package_with_hook) with patch('colcon_core.package_augmentation.logger.error') as error: augment_packages( descs, additional_argument_names=['arg1', 'arg2'], augmentation_extensions=extensions) assert desc1.hooks == ['arg1', 'arg2'] assert desc2.hooks == ['arg1', 'arg2'] # the raised exception is catched and results in an error message assert error.call_count == 1 assert len(error.call_args[0]) == 1 assert error.call_args[0][0].startswith( "Exception in package augmentation extension 'extension2': \n") # invalid return value desc1.hooks = [] desc2.hooks = [] extensions['extension2'].augment_packages = Mock(return_value=False) with patch('colcon_core.package_augmentation.logger.error') as error: augment_packages( descs, additional_argument_names=['arg1', 'arg2'], augmentation_extensions=extensions) assert desc1.hooks == ['arg1', 'arg2'] assert desc2.hooks == ['arg1', 'arg2'] # the raised assertion is catched and results in an error message assert error.call_count == 1 assert len(error.call_args[0]) == 1 assert error.call_args[0][0].startswith( "Exception in package augmentation extension 'extension2': ")
def test_dependencies_not_changed(self): package_1_desc = PackageDescriptor('fake/path') package_1_desc.name = 'baz' package_1_desc.dependencies['run'] = ['foo'] package_1_desc.dependencies['test'] = ['bar'] package_1 = PackageDecorator(package_1_desc) decorators = [package_1] self.assertTrue( package_dependencies_changed(self.path_context, decorators)) update_dependencies_cache(self.path_context) self.assertFalse( package_dependencies_changed(self.path_context, decorators))
def test_build_package(): event_loop = new_event_loop() asyncio.set_event_loop(event_loop) try: with TemporaryDirectory(prefix='test_colcon_') as tmp_path_str: tmp_path = Path(tmp_path_str) python_build_task = PythonBuildTask() package = PackageDescriptor(tmp_path / 'src') package.name = 'test_package' package.type = 'python' context = TaskContext( pkg=package, args=SimpleNamespace( path=str(tmp_path / 'src'), build_base=str(tmp_path / 'build'), install_base=str(tmp_path / 'install'), symlink_install=False, ), dependencies={} ) python_build_task.set_context(context=context) pkg = python_build_task.context.pkg pkg.path.mkdir() (pkg.path / 'setup.py').write_text( 'from setuptools import setup\n' 'setup(\n' ' name="test_package",\n' ' packages=["my_module"],\n' ')\n' ) (pkg.path / 'my_module').mkdir() (pkg.path / 'my_module' / '__init__.py').touch() src_base = Path(python_build_task.context.args.path) source_files_before = set(src_base.rglob('*')) event_loop.run_until_complete(python_build_task.build()) source_files_after = set(src_base.rglob('*')) assert source_files_before == source_files_after build_base = Path(python_build_task.context.args.build_base) assert 1 == len(list(build_base.rglob('my_module/__init__.py'))) install_base = Path(python_build_task.context.args.install_base) assert 1 == len(list(install_base.rglob('my_module/__init__.py'))) pkg_info, = install_base.rglob('PKG-INFO') assert 'Name: test-package' in pkg_info.read_text().splitlines() finally: event_loop.close()
def test__discover_packages(): descs = _discover_packages(None, None, {}) assert descs == set() with EntryPointContext( extension1=Extension1, extension2=Extension2, extension3=Extension3, extension4=Extension4, ): extensions = get_package_discovery_extensions() # mock the discover method in the order the extensions are being called extensions['extension2'].discover = Mock( side_effect=ValueError('exception in discover')) extensions['extension4'].discover = Mock( side_effect=extensions['extension4'].discover) extensions['extension3'].discover = Mock( return_value={ PackageDescriptor('/extension3/pkg1'), PackageDescriptor('/extension3/pkg2') }) # returns None instead of a set extensions['extension1'].discover = Mock() with patch('colcon_core.package_discovery.logger.error') as error: descs = _discover_packages(Mock(), None, extensions) # in the order the extensions are being called assert extensions['extension2'].discover.call_count == 1 assert extensions['extension4'].discover.call_count == 1 assert extensions['extension3'].discover.call_count == 1 assert extensions['extension1'].discover.call_count == 1 # the raised exceptions are catched and result in error messages assert error.call_count == 2 assert len(error.call_args_list[0][0]) == 1 assert error.call_args_list[0][0][0].startswith( "Exception in package discovery extension 'extension2': " 'exception in discover\n') assert len(error.call_args_list[1][0]) == 1 assert error.call_args_list[1][0][0].startswith( "Exception in package discovery extension 'extension1': " 'discover() should return a set\n') assert len(descs) == 2 expected_path = '/extension3/pkg1'.replace('/', os.sep) assert expected_path in (str(d.path) for d in descs) expected_path = '/extension3/pkg2'.replace('/', os.sep) assert expected_path in (str(d.path) for d in descs)
def test_update_metadata(): desc = PackageDescriptor('/some/path') desc.name = 'name' assert len(desc.metadata) == 0 update_metadata(desc, 'd', {1: 'one', 2: 'two'}) assert len(desc.metadata) == 1 assert 'd' in desc.metadata.keys() assert desc.metadata['d'] == {1: 'one', 2: 'two'} update_metadata(desc, 'd', {2: 'TWO', 3: 'THREE'}) assert len(desc.metadata) == 1 assert 'd' in desc.metadata.keys() assert desc.metadata['d'] == {1: 'one', 2: 'TWO', 3: 'THREE'} update_metadata(desc, 'l', [1, 2]) assert len(desc.metadata) == 2 assert 'l' in desc.metadata.keys() assert desc.metadata['l'] == [1, 2] update_metadata(desc, 'l', [2, 3]) assert len(desc.metadata) == 2 assert 'l' in desc.metadata.keys() assert desc.metadata['l'] == [1, 2, 2, 3] update_metadata(desc, 's', {1, 2}) assert len(desc.metadata) == 3 assert 's' in desc.metadata.keys() assert desc.metadata['s'] == {1, 2} update_metadata(desc, 's', {2, 3}) assert len(desc.metadata) == 3 assert 's' in desc.metadata.keys() assert desc.metadata['s'] == {1, 2, 3} with patch('colcon_core.package_augmentation.logger.warn') as warn: update_metadata(desc, 's', 'different type') warn.assert_called_once_with( "update package 'name' metadata 's' from value '{1, 2, 3}' to " "'different type'") assert len(desc.metadata) == 3 assert 's' in desc.metadata.keys() assert desc.metadata['s'] == 'different type' with patch('colcon_core.package_augmentation.logger.warn') as warn: update_metadata(desc, 's', 'same type') assert warn.call_count == 0 assert len(desc.metadata) == 3 assert 's' in desc.metadata.keys() assert desc.metadata['s'] == 'same type'
def test_discover_packages(): # check without any extensions with patch( 'colcon_core.package_discovery.get_package_discovery_extensions', return_value={}, ) as get_extensions: with patch('colcon_core.package_discovery.logger.warning') as warn: descs = discover_packages(None, None) assert get_extensions.call_count == 1 warn.assert_called_once_with('No package discovery extensions found') assert descs == set() with EntryPointContext( extension1=Extension1, extension2=Extension2, extension3=Extension3, extension4=Extension4, ): extensions = get_package_discovery_extensions() assert len(extensions) == 4 # check without any parameters extensions['extension1'].discover = Mock( return_value={PackageDescriptor('/extension1/pkg1')}) extensions['extension2'].discover = Mock( return_value={PackageDescriptor('/extension2/pkg1')}) descs = discover_packages(None, None, discovery_extensions=extensions) assert len(descs) == 2 expected_path = '/extension1/pkg1'.replace('/', os.sep) assert expected_path in (str(d.path) for d in descs) expected_path = '/extension2/pkg1'.replace('/', os.sep) assert expected_path in (str(d.path) for d in descs) # check with parameters extensions['extension3'].has_parameters = Mock(return_value=True) extensions['extension3'].discover = Mock( return_value={ PackageDescriptor('/extension3/pkg1'), PackageDescriptor('/extension3/pkg2') }) descs = discover_packages(None, None, discovery_extensions=extensions) assert len(descs) == 2 expected_path = '/extension3/pkg1'.replace('/', os.sep) assert expected_path in (str(d.path) for d in descs) expected_path = '/extension3/pkg2'.replace('/', os.sep) assert expected_path in (str(d.path) for d in descs)
def test_dependencies_changed(self): package_1_desc = PackageDescriptor('fake/path') package_1_desc.name = 'package1' package_1_desc.dependencies['run'] = [DependencyDescriptor('foo')] package_1 = PackageDecorator(package_1_desc) decorators = [package_1] self.assertTrue( package_dependencies_changed(self.path_context, decorators)) update_dependencies_cache(self.path_context) package_1_desc.dependencies['run'] = [ DependencyDescriptor('foo'), 'baz' ] package_1 = PackageDecorator(package_1_desc) decorators = [package_1] self.assertTrue( package_dependencies_changed(self.path_context, decorators))
def test_discover(): extension = PathPackageDiscovery() args = Mock() args.paths = None assert extension.discover(args=args, identification_extensions={}) == set() args.paths = [ '/empty/path', '/skip/path', '/same/path', '/same/path/../path', '/other/path' ] with patch('colcon_core.package_discovery.path.identify', side_effect=identify): descs = extension.discover(args=args, identification_extensions={}) assert descs == { PackageDescriptor(os.path.realpath('/same/path')), PackageDescriptor(os.path.realpath('/other/path')) }
def test_constructor(): d = PackageDescriptor('/some/path') assert d.path == Path('/some/path') assert d.type is None assert d.name is None assert len(d.dependencies.keys()) == 0 assert len(d.hooks) == 0 assert len(d.metadata.keys()) == 0
def test_no_augmentation(): augmentation_extension = GitPackageAugmentation() with TemporaryDirectory(prefix='test_colcon_') as basepath: desc = PackageDescriptor(basepath) assert not desc.metadata augmentation_extension.augment_package(desc) assert 'vcs_type' not in desc.metadata
def test_update_descriptor(): desc = PackageDescriptor('/some/path') assert len(desc.dependencies) == 0 assert len(desc.hooks) == 0 assert len(desc.metadata) == 0 data = { 'build-dependencies': {'b1', 'b2'}, 'test-dependencies': {'t1'}, } update_descriptor(desc, data) assert len(desc.dependencies) == 2 assert 'build' in desc.dependencies.keys() assert desc.dependencies['build'] == {'b1', 'b2'} assert 'test' in desc.dependencies.keys() assert desc.dependencies['test'] == {'t1'} data = { 'dependencies': {'d1'}, 'hooks': ['hook1', 'hook2'], 'key': 'value', } update_descriptor(desc, data, additional_argument_names=['*']) assert len(desc.dependencies) == 3 assert 'build' in desc.dependencies.keys() assert desc.dependencies['build'] == {'d1', 'b1', 'b2'} assert 'run' in desc.dependencies.keys() assert desc.dependencies['run'] == {'d1'} assert 'test' in desc.dependencies.keys() assert desc.dependencies['test'] == {'d1', 't1'} assert len(desc.hooks) == 2 assert desc.hooks == ['hook1', 'hook2'] assert len(desc.metadata) == 1 assert 'key' in desc.metadata assert desc.metadata['key'] == 'value' data = { 'other': 'value', 'some': 'value', } update_descriptor( desc, data, additional_argument_names=['some', 'unknown']) assert len(desc.metadata) == 2 assert 'other' not in desc.metadata assert 'some' in desc.metadata assert desc.metadata['some'] == 'value' data = { 'name': 'foo', 'type': 'cmake', } update_descriptor(desc, data) assert desc.name == 'foo' assert desc.type == 'cmake'
async def test_bundle(): pkg = PackageDescriptor('package/path') pkg.name = 'MyPackageName' pkg.dependencies['run'] = { DependencyDescriptor('source_pkg'), DependencyDescriptor('other_pkg'), DependencyDescriptor('system_pkg') } installers = { 'rdmanifest': MagicMock(), 'system': MagicMock(), 'other': MagicMock() } top_level_args = MagicMock(build_base='build/base', install_base='install/base', bundle_base='bundle/base') args = BundlePackageArguments(pkg, installers, top_level_args) args.ros_distribution = 'kinetic' args.exclude_ros_base = True context = TaskContext(pkg=pkg, args=args, dependencies={}) task = RosBundle() task.set_context(context=context) # Concise read on why it's patched this way. # http://www.voidspace.org.uk/python/mock/patch.html#where-to-patch with patch('colcon_ros_bundle.task.ros_bundle.RosdepWrapper' ) as wrapper: # noqa: E501 wrapper().get_rule.side_effect = _get_rule_side_effect wrapper().resolve.side_effect = _resolve_side_effect await task.bundle() installers['rdmanifest'].add_to_install_list.assert_called_with( 'source_pkg', { 's': 'ource', 'uri': 'rdmanifest' }) installers['other'].add_to_install_list.assert_called_with( 'other_pkg_name') installers['system'].add_to_install_list.assert_called_with( 'system_pkg_name')
def test_discover_with_wildcards(): with TemporaryDirectory(prefix='test_colcon_') as prefix_path: prefix_path = Path(prefix_path) path_one = prefix_path / 'one' / 'path' path_two = prefix_path / 'two' / 'path' path_three = prefix_path / 'three' / 'path' path_one.mkdir(parents=True) path_two.mkdir(parents=True) path_three.mkdir(parents=True) extension = PathPackageDiscovery() args = Mock() args.paths = [str(prefix_path / '*' / 'path')] with patch('colcon_core.package_discovery.path.identify', side_effect=identify): descs = extension.discover(args=args, identification_extensions={}) assert descs == { PackageDescriptor(os.path.realpath(str(path_one))), PackageDescriptor(os.path.realpath(str(path_two))), PackageDescriptor(os.path.realpath(str(path_three))) }
def test_augmentation(): augmentation_extension = GitPackageAugmentation() with TemporaryDirectory(prefix='test_colcon_') as basepath: repo = Repo.init(basepath) assert repo desc = PackageDescriptor(basepath) assert not desc.metadata augmentation_extension.augment_package(desc) assert desc.metadata['vcs_type'] == 'git'
async def test_task_test(): with TemporaryDirectory(prefix='test_colcon_') as basepath: extension = BazelTestTask() desc = PackageDescriptor(basepath) desc.name = "test" args_verb = MockArgs(basepath) args_pkg = TestPackageArguments(desc, args_verb) args_pkg.path = basepath args_pkg.build_base = args_verb.build_base args_pkg.install_base = args_verb.install_base args_pkg.merge_install = args_verb.merge_install args_pkg.test_result_base = args_verb.test_result_base context = TaskContext(pkg=desc, args=args_pkg, dependencies=set()) extension.set_context(context=context) ret = await extension.test() assert ret
def test_str(): d = PackageDescriptor('/some/path') d.type = 'custom-type' d.name = 'custom-name' d.dependencies['build'].add('build-depend') d.dependencies['run'].add('run-depend') d.hooks += ('hook-a', 'hook-b') d.metadata['key'] = 'value' s = str(d) assert s.startswith('{') assert s.endswith('}') assert 'path: ' in s assert '/some/path'.replace('/', os.sep) in s assert 'type: ' in s assert 'custom-type' in s assert 'name: ' in s assert 'custom-name' in s assert 'dependencies: ' in s assert 'build-depend' in s assert 'run-depend' in s assert 'hooks: ' in s assert 'hook-a' in s assert 'metadata: ' in s assert 'value' in s
def test_pytest_match(): extension = PytestPythonTestingStep() env = {} desc = PackageDescriptor('/dev/null') context = TaskContext(pkg=desc, args=None, dependencies=None) desc.name = 'pkg-name' desc.type = 'python' # no test requirements desc.metadata['get_python_setup_options'] = lambda env: {} assert not extension.match(context, env, get_setup_data(desc, env)) # pytest not in tests_require desc.metadata['get_python_setup_options'] = lambda env: { 'tests_require': ['nose'], } assert not extension.match(context, env, get_setup_data(desc, env)) # pytest not in extras_require.test desc.metadata['get_python_setup_options'] = lambda env: { 'extras_require': { 'test': ['nose'] }, } assert not extension.match(context, env, get_setup_data(desc, env)) # pytest in tests_require desc.metadata['get_python_setup_options'] = lambda env: { 'tests_require': ['pytest'], } assert extension.match(context, env, get_setup_data(desc, env)) # pytest in extras_require.test desc.metadata['get_python_setup_options'] = lambda env: { 'extras_require': { 'test': ['pytest'] }, } assert extension.match(context, env, get_setup_data(desc, env))
def test_get_dependencies(): d1 = PackageDescriptor('/some/path') d1.name = 'self' d1.dependencies['build'].add('build-depend') d1.dependencies['build'].add('depend') d1.dependencies['run'].add('run-depend') d1.dependencies['run'].add('depend') assert d1.get_dependencies() == {'build-depend', 'run-depend', 'depend'} d1.dependencies['test'].add('self') assert d1.get_dependencies(categories=('build', )) == \ {'build-depend', 'depend'} with pytest.raises(AssertionError) as e: d1.get_dependencies() assert "'self'" in str(e.value)
def identify( extensions: Dict[ # actually an OrderedDict int, # priority Dict[str, PackageIdentificationExtensionPoint], # an OrderedDict ], path: str, ) -> ReturnType: """ Identify the package in the given path. :param extensions: dict of extensions :param path: The path """ desc = PackageDescriptor(path) for extensions_same_prio in extensions.values(): result = _identify(extensions_same_prio, desc) # continue with next priority level if no information was contributed if result is None: continue # skip location since identification is ambiguous if result is False: raise IgnoreLocationException() assert isinstance(result, PackageDescriptor), result if result.identifies_package(): return result # use incrementally populated descriptor for next priority level desc = result if getattr(desc, 'type', None) or getattr(desc, 'name', None): logger.warning( "package '{desc.path}' has type or name but is incomplete". format_map(locals())) return None
def test_get_packages(): args = Namespace() d1 = PackageDescriptor('/some/path') d1.name = 'one' d2 = PackageDescriptor('/other/path') d2.name = 'two' with patch('colcon_core.package_selection.discover_packages', return_value=[d1, d2]): decos = get_packages(args) assert len(decos) == 2 assert decos[0].descriptor.name == 'one' assert decos[0].selected is True assert decos[1].descriptor.name == 'two' assert decos[1].selected is True d2.name = 'one' with patch('colcon_core.package_selection.discover_packages', return_value=[d1, d2]): with pytest.raises(RuntimeError) as e: get_packages(args) assert 'Duplicate package names not supported:' in str(e.value) assert '- one:' in str(e.value) assert '- {sep}some{sep}path'.format(sep=os.sep) in str(e.value) assert '- {sep}other{sep}path'.format(sep=os.sep) in str(e.value)
def test__identify(): desc_path_only = PackageDescriptor('/some/path') with EntryPointContext( extension1=Extension1, extension2=Extension2, extension3=Extension3, extension4=Extension4, ): # valid result extensions = get_package_identification_extensions()[100] extensions['extension2'].identify = Mock() extensions['extension4'].identify = identify_name_and_type desc = _identify(extensions, desc_path_only) assert isinstance(desc, PackageDescriptor) assert str(desc.path) == '/some/path'.replace('/', os.sep) assert desc.name == 'name' assert desc.type == 'type' # no results extensions = get_package_identification_extensions()[100] extensions['extension2'].identify = Mock() extensions['extension4'].identify = Mock() desc = _identify(extensions, desc_path_only) assert desc is None # multiple different results extensions = get_package_identification_extensions()[100] extensions['extension2'].identify = identify_name extensions['extension4'].identify = identify_type with patch( 'colcon_core.package_identification.logger.warning') as warn: desc = _identify(extensions, desc_path_only) assert desc is False # the raised exception is catched and results in a warn message assert warn.call_count == 1 assert len(warn.call_args[0]) == 1 assert 'multiple matches' in warn.call_args[0][0] # invalid return value extensions = get_package_identification_extensions()[90] extensions['extension3'].identify = Mock(return_value=True) with patch('colcon_core.package_identification.logger.error') as error: desc = _identify(extensions, desc_path_only) assert desc is None # the raised exception is catched and results in an error message assert error.call_count == 1 assert len(error.call_args[0]) == 1 assert error.call_args[0][0].startswith( "Exception in package identification extension 'extension3' " "in '/some/path': identify() should return None\n".replace( '/', os.sep)) # skip location extensions = get_package_identification_extensions()[90] extensions['extension3'].identify = Mock( side_effect=IgnoreLocationException()) with pytest.raises(IgnoreLocationException): _identify(extensions, desc_path_only) # raise exception extensions = get_package_identification_extensions()[90] extensions['extension3'].identify = Mock( side_effect=RuntimeError('custom exception')) with patch('colcon_core.package_identification.logger.error') as error: desc = _identify(extensions, desc_path_only) assert desc is None # the raised exception is catched and results in an error message assert error.call_count == 1 assert len(error.call_args[0]) == 1 assert error.call_args[0][0].startswith( "Exception in package identification extension 'extension3' " "in '/some/path': custom exception\n".replace('/', os.sep))
def test_magic_methods(): d1 = PackageDescriptor('/some/path') d1.type = 'custom-type' d1.name = 'custom-name' d2 = PackageDescriptor('/some/path') d2.type = 'custom-type' d2.name = 'other-name' assert d1 != d2 assert hash(d1) != hash(d2) d2.name = 'custom-name' assert d1 == d2 assert hash(d1) == hash(d2) d1.dependencies['build'].add('build-depend') d2.hooks.append('hook') d2.metadata['key'] = 'value' assert d1 == d2 assert hash(d1) == hash(d2) d2.type = 'other-type' assert d1 != d2 assert hash(d1) != hash(d2) d2.type = 'custom-type' assert d1 == d2 assert hash(d1) == hash(d2) d2.path = Path('/other/path') assert d1 != d2 # comparing with other types always returns False assert d1 != []
def test_get_recursive_dependencies(): d = PackageDescriptor('/some/path') d.name = 'A' d.dependencies['build'].add('B') d.dependencies['run'].add('c') d.dependencies['test'].add('d') d1 = PackageDescriptor('/other/path') d1.name = 'B' d1.dependencies['build'].add('e') d1.dependencies['run'].add('F') d1.dependencies['test'].add('G') d2 = PackageDescriptor('/another/path') d2.name = 'd' d3 = PackageDescriptor('/yet-another/path') d3.name = 'F' d3.dependencies['build'].add('h') d3.dependencies['test'].add('G') d3.dependencies['test'].add('I') d4 = PackageDescriptor('/more/path') d4.name = 'G' d4.dependencies['test'].add('I') d5 = PackageDescriptor('/yet-more/path') d5.name = 'I' # circular dependencies should be ignored d5.dependencies['run'].add('A') rec_deps = d.get_recursive_dependencies({d, d1, d2, d3, d4, d5}, direct_categories=('build', 'run'), recursive_categories=('run', 'test')) assert rec_deps == { # direct dependencies 'B', # recursive dependencies 'F', 'G', 'I', }
def update_descriptor(desc: PackageDescriptor, data: dict, *, additional_argument_names=None): """ Update the package descriptor with additional information. For the keys `name` and `type` the values from `data` are set on the descriptor attributes with the same names. For the key `dependencies` the values are being added to the dependencies in each of the following categories: `build`, `run`, and `test`. For the keys `build_dependencies`, `run_dependencies`, and `test_dependencies` the values are being added to the dependencies in the category with the same name. For the key `hooks` the values are being added to the list of hooks. Any key-value pair not explicitly mentioned above is being used to update the metadata if the key is in the list of additional argument names. See :function:`update_metadata` for details how the metadata is updated. If the additional argument names is a list with the single value `*` all keys not explicitly mentioned above are being used to update the metadata. :param desc: the package descriptor :param data: The dictionary with additional information :param additional_argument_names: A dict of option names to destination names or a list of argument names """ # explicit key updates try: desc.name = data['name'] except KeyError: pass try: desc.type = data['type'] except KeyError: pass dep_types = ('build', 'run', 'test') # transfer generic dependencies to each specific type if 'dependencies' in data: for d in data['dependencies']: for dep_type in dep_types: desc.dependencies[dep_type].add(d) # transfer type specific dependencies for dep_type in dep_types: key = '{dep_type}-dependencies'.format_map(locals()) if key in data: for d in data[key]: desc.dependencies[dep_type].add(d) # transfer hooks if 'hooks' in data: for d in data['hooks']: desc.hooks.append(d) # transfer any other metadata if additional_argument_names == ['*']: additional_argument_names = [] # skip any of the already explicitly handled names ignored_names = ['name', 'type', 'dependencies', 'hooks'] for dep_type in dep_types: ignored_names.append('{dep_type}-dependencies'.format_map( locals())) for name in data.keys(): if name in ignored_names: continue additional_argument_names.append(name) if isinstance(additional_argument_names, list): additional_argument_names = OrderedDict([ (name, name) for name in additional_argument_names ]) for option, dest in (additional_argument_names or {}).items(): if option in data: update_metadata(desc, dest, data[option])