From 2c16a7e94bd69d6f9ea382f508087270231b0405 Mon Sep 17 00:00:00 2001 From: William Woodall Date: Fri, 19 Jul 2013 15:53:00 -0700 Subject: [PATCH] refuse to release packages with uppercase names Fixes #191 Fixes #76 --- bloom/packages.py | 8 ++ test/unit_tests/__init__.py | 0 test/unit_tests/test_packages.py | 14 ++ .../bad_NAME/CMakeLists.txt | 129 ++++++++++++++++++ .../test_packages_data/bad_NAME/package.xml | 53 +++++++ test/utils/common.py | 48 +++++++ 6 files changed, 252 insertions(+) create mode 100644 test/unit_tests/__init__.py create mode 100644 test/unit_tests/test_packages.py create mode 100644 test/unit_tests/test_packages_data/bad_NAME/CMakeLists.txt create mode 100644 test/unit_tests/test_packages_data/bad_NAME/package.xml diff --git a/bloom/packages.py b/bloom/packages.py index 3408a0f6..ce4803cb 100644 --- a/bloom/packages.py +++ b/bloom/packages.py @@ -91,6 +91,14 @@ def get_package_data(branch_name=None, directory=None, quiet=True): version = verify_equal_package_versions(packages.values()) ignored_packages = get_ignored_packages() for k, v in dict(packages).items(): + # Check for packages with upper case names + if v.name.lower() != v.name: + error("Cowardly refusing to release packages with uppercase characters in the name: " + v.name) + error("See:") + error(" https://github.com/ros-infrastructure/bloom/issues/191") + error(" https://github.com/ros-infrastructure/bloom/issues/76") + error("Invalid package names, aborting.", exit=True) + # Check for ignored packages if v.name in ignored_packages: warning("Explicitly ignoring package '{0}' because it is in the `{1}.ignored` file." .format(v.name, os.environ.get('BLOOM_TRACK', 'packages'))) diff --git a/test/unit_tests/__init__.py b/test/unit_tests/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/test/unit_tests/test_packages.py b/test/unit_tests/test_packages.py new file mode 100644 index 00000000..276686e2 --- /dev/null +++ b/test/unit_tests/test_packages.py @@ -0,0 +1,14 @@ +import os + +from ..utils.common import AssertRaisesContext +from ..utils.common import redirected_stdio + +from bloom.packages import get_package_data + +test_data_dir = os.path.join(os.path.dirname(__file__), 'test_packages_data') + + +def test_get_package_data_fails_on_uppercase(): + with AssertRaisesContext(SystemExit, "Invalid package names, aborting."): + with redirected_stdio(): + get_package_data(directory=test_data_dir) diff --git a/test/unit_tests/test_packages_data/bad_NAME/CMakeLists.txt b/test/unit_tests/test_packages_data/bad_NAME/CMakeLists.txt new file mode 100644 index 00000000..f55acd94 --- /dev/null +++ b/test/unit_tests/test_packages_data/bad_NAME/CMakeLists.txt @@ -0,0 +1,129 @@ +cmake_minimum_required(VERSION 2.8.3) +project(bad_NAME) + +## Find catkin macros and libraries +## if COMPONENTS list like find_package(catkin REQUIRED COMPONENTS xyz) +## is used, also find other catkin packages +find_package(catkin REQUIRED) + +## System dependencies are found with CMake's conventions +# find_package(Boost REQUIRED COMPONENTS system) + + +## Uncomment this if the package has a setup.py. This macro ensures +## modules and global scripts declared therein get installed +## See http://ros.org/doc/api/catkin/html/user_guide/setup_dot_py.html +# catkin_python_setup() + +####################################### +## Declare ROS messages and services ## +####################################### + +## Generate messages in the 'msg' folder +# add_message_files( +# FILES +# Message1.msg +# Message2.msg +# ) + +## Generate services in the 'srv' folder +# add_service_files( +# FILES +# Service1.srv +# Service2.srv +# ) + +## Generate added messages and services with any dependencies listed here +# generate_messages( +# DEPENDENCIES +# std_msgs # Or other packages containing msgs +# ) + +################################### +## catkin specific configuration ## +################################### +## The catkin_package macro generates cmake config files for your package +## Declare things to be passed to dependent projects +## INCLUDE_DIRS: uncomment this if you package contains header files +## LIBRARIES: libraries you create in this project that dependent projects also need +## CATKIN_DEPENDS: catkin_packages dependent projects also need +## DEPENDS: system dependencies of this project that dependent projects also need +catkin_package( +# INCLUDE_DIRS include +# LIBRARIES bad_NAME +# CATKIN_DEPENDS other_catkin_pkg +# DEPENDS system_lib +) + +########### +## Build ## +########### + +## Specify additional locations of header files +## Your package locations should be listed before other locations +# include_directories(include) + +## Declare a cpp library +# add_library(bad_NAME +# src/${PROJECT_NAME}/bad_NAME.cpp +# ) + +## Declare a cpp executable +# add_executable(bad_NAME_node src/bad_NAME_node.cpp) + +## Add cmake target dependencies of the executable/library +## as an example, message headers may need to be generated before nodes +# add_dependencies(bad_NAME_node bad_NAME_generate_messages_cpp) + +## Specify libraries to link a library or executable target against +# target_link_libraries(bad_NAME_node +# ${catkin_LIBRARIES} +# ) + +############# +## Install ## +############# + +# all install targets should use catkin DESTINATION variables +# See http://ros.org/doc/api/catkin/html/adv_user_guide/variables.html + +## Mark executable scripts (Python etc.) for installation +## in contrast to setup.py, you can choose the destination +# install(PROGRAMS +# scripts/my_python_script +# DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION} +# ) + +## Mark executables and/or libraries for installation +# install(TARGETS bad_NAME bad_NAME_node +# ARCHIVE DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION} +# LIBRARY DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION} +# RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION} +# ) + +## Mark cpp header files for installation +# install(DIRECTORY include/${PROJECT_NAME}/ +# DESTINATION ${CATKIN_PACKAGE_INCLUDE_DESTINATION} +# FILES_MATCHING PATTERN "*.h" +# PATTERN ".svn" EXCLUDE +# ) + +## Mark other files for installation (e.g. launch and bag files, etc.) +# install(FILES +# # myfile1 +# # myfile2 +# DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION} +# ) + +############# +## Testing ## +############# + +## Add gtest based cpp test target and link libraries +# catkin_add_gtest(${PROJECT_NAME}-test test/test_bad_NAME.cpp) +# if(TARGET ${PROJECT_NAME}-test) +# target_link_libraries(${PROJECT_NAME}-test ${PROJECT_NAME}) +# endif() + +## Add folders to be run by python nosetests +# catkin_add_nosetests(test) diff --git a/test/unit_tests/test_packages_data/bad_NAME/package.xml b/test/unit_tests/test_packages_data/bad_NAME/package.xml new file mode 100644 index 00000000..d7a29059 --- /dev/null +++ b/test/unit_tests/test_packages_data/bad_NAME/package.xml @@ -0,0 +1,53 @@ + + + bad_NAME + 0.0.0 + The bad_NAME package + + + + + william + + + + + + TODO + + + + + + + + + + + + + + + + + + + + + + + + + + catkin + + + + + + + + + + + \ No newline at end of file diff --git a/test/utils/common.py b/test/utils/common.py index 42b5811b..5f7eeb63 100644 --- a/test/utils/common.py +++ b/test/utils/common.py @@ -6,6 +6,7 @@ import functools import os +import re import shlex import shutil import sys @@ -16,6 +17,53 @@ from subprocess import Popen, PIPE, CalledProcessError +def assert_raises(exception_classes, callable_obj=None, *args, **kwargs): + context = AssertRaisesContext(exception_classes) + if callable_obj is None: + return context + with context: + callable_obj(*args, **kwargs) + + +def assert_raises_regex(exception_classes, expected_regex, callable_obj=None, *args, **kwargs): + context = AssertRaisesContext(exception_classes, expected_regex) + if callable_obj is None: + return context + with context: + callable_obj(*args, **kwargs) + + +class AssertRaisesContext(object): + def __init__(self, expected, expected_regex=None): + self.expected = expected + self.expected_regex = expected_regex + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_value, tb): + if self.expected is None: + if exc_type is None: + return True + else: + raise + if exc_type is None: + try: + exc_name = self.expected.__name__ + except AttributeError: + exc_name = str(self.expected) + raise AssertionError("{0} not raised".format(exc_name)) + if not issubclass(exc_type, self.expected): + raise + if self.expected_regex is None: + return True + expected_regex = self.expected_regex + expected_regex = re.compile(expected_regex) + if not expected_regex.search(str(exc_value)): + raise AssertionError("'{0}' does not match '{1}'".format(expected_regex.pattern, str(exc_value))) + return True + + class bloom_answer(object): ASSERT_NO_QUESTION = -1