diff --git a/rcl_yaml_param_parser/CMakeLists.txt b/rcl_yaml_param_parser/CMakeLists.txt index 2619eb627..218801b5c 100644 --- a/rcl_yaml_param_parser/CMakeLists.txt +++ b/rcl_yaml_param_parser/CMakeLists.txt @@ -4,6 +4,7 @@ project(rcl_yaml_param_parser) find_package(ament_cmake_ros REQUIRED) find_package(rcutils REQUIRED) +find_package(rmw REQUIRED) find_package(libyaml_vendor REQUIRED) # Default to C++14 @@ -30,7 +31,7 @@ add_library( target_include_directories(${PROJECT_NAME} PUBLIC "$" "$") -ament_target_dependencies(${PROJECT_NAME} "libyaml_vendor" "rcutils") +ament_target_dependencies(${PROJECT_NAME} "libyaml_vendor" "rcutils" "rmw") # Set the visibility to hidden by default if possible if(CMAKE_C_COMPILER_ID STREQUAL "GNU" OR CMAKE_C_COMPILER_ID MATCHES "Clang") @@ -126,6 +127,7 @@ if(BUILD_TESTING) ament_add_gtest(test_parser_multiple_nodes test/test_parser_multiple_nodes.cpp WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" + TIMEOUT 240 # Large timeout to wait for fault injection tests ) if(TARGET test_parser_multiple_nodes) ament_target_dependencies(test_parser_multiple_nodes @@ -181,6 +183,7 @@ if(BUILD_TESTING) endif() ament_export_dependencies(ament_cmake libyaml_vendor) +ament_export_dependencies(rmw) ament_export_include_directories(include) install( DIRECTORY include/ diff --git a/rcl_yaml_param_parser/package.xml b/rcl_yaml_param_parser/package.xml index 7d522bdd1..b4579d381 100644 --- a/rcl_yaml_param_parser/package.xml +++ b/rcl_yaml_param_parser/package.xml @@ -14,6 +14,7 @@ libyaml_vendor yaml + rmw rcutils ament_cmake_gtest diff --git a/rcl_yaml_param_parser/src/parse.c b/rcl_yaml_param_parser/src/parse.c index c60e382f7..c7cd69c7f 100644 --- a/rcl_yaml_param_parser/src/parse.c +++ b/rcl_yaml_param_parser/src/parse.c @@ -18,13 +18,54 @@ #include #include "rcutils/allocator.h" +#include "rcutils/format_string.h" #include "rcutils/strdup.h" +#include "rmw/error_handling.h" +#include "rmw/validate_namespace.h" +#include "rmw/validate_node_name.h" + #include "./impl/add_to_arrays.h" #include "./impl/parse.h" #include "./impl/namespace.h" #include "./impl/node_params.h" #include "rcl_yaml_param_parser/parser.h" +#include "rcl_yaml_param_parser/visibility_control.h" + +/// +/// Check a name space whether it is valid +/// +/// \param[in] namespace the namespace to check +/// \return RCUTILS_RET_OK if namespace is valid, or +/// \return RCUTILS_RET_INVALID_ARGUMENT if namespace is not valid, or +/// \return RCUTILS_RET_ERROR if an unspecified error occurred. +RCL_YAML_PARAM_PARSER_LOCAL +rcutils_ret_t +_validate_namespace(const char * namespace_); + +/// +/// Check a node name whether it is valid +/// +/// \param[in] name the node name to check +/// \return RCUTILS_RET_OK if the node name is valid, or +/// \return RCUTILS_RET_INVALID_ARGUMENT if node name is not valid, or +/// \return RCUTILS_RET_ERROR if an unspecified error occurred. +RCL_YAML_PARAM_PARSER_LOCAL +rcutils_ret_t +_validate_nodename(const char * node_name); + +/// +/// Check a name (namespace/node_name) whether it is valid +/// +/// \param name the name to check +/// \param allocator an allocator to use +/// \return RCUTILS_RET_OK if name is valid, or +/// \return RCUTILS_RET_INVALID_ARGUMENT if name is not valid, or +/// \return RCL_RET_BAD_ALLOC if an allocation failed, or +/// \return RCUTILS_RET_ERROR if an unspecified error occurred. +RCL_YAML_PARAM_PARSER_LOCAL +rcutils_ret_t +_validate_name(const char * name, rcutils_allocator_t allocator); /// /// Determine the type of the value and return the converted value @@ -404,6 +445,138 @@ rcutils_ret_t parse_value( return ret; } +rcutils_ret_t +_validate_namespace(const char * namespace_) +{ + int validation_result = 0; + rmw_ret_t ret; + ret = rmw_validate_namespace(namespace_, &validation_result, NULL); + if (RMW_RET_OK != ret) { + RCUTILS_SET_ERROR_MSG(rmw_get_error_string().str); + return RCUTILS_RET_ERROR; + } + if (RMW_NAMESPACE_VALID != validation_result) { + RCUTILS_SET_ERROR_MSG(rmw_namespace_validation_result_string(validation_result)); + return RCUTILS_RET_INVALID_ARGUMENT; + } + + return RCUTILS_RET_OK; +} + +rcutils_ret_t +_validate_nodename(const char * node_name) +{ + int validation_result = 0; + rmw_ret_t ret; + ret = rmw_validate_node_name(node_name, &validation_result, NULL); + if (RMW_RET_OK != ret) { + RCUTILS_SET_ERROR_MSG(rmw_get_error_string().str); + return RCUTILS_RET_ERROR; + } + if (RMW_NODE_NAME_VALID != validation_result) { + RCUTILS_SET_ERROR_MSG(rmw_node_name_validation_result_string(validation_result)); + return RCUTILS_RET_INVALID_ARGUMENT; + } + + return RCUTILS_RET_OK; +} + +rcutils_ret_t +_validate_name(const char * name, rcutils_allocator_t allocator) +{ + // special rules + if (0 == strcmp(name, "/**") || 0 == strcmp(name, "/*")) { + return RCUTILS_RET_OK; + } + + rcutils_ret_t ret = RCUTILS_RET_OK; + char * separator_pos = strrchr(name, '/'); + char * node_name = NULL; + char * absolute_namespace = NULL; + if (NULL == separator_pos) { + node_name = rcutils_strdup(name, allocator); + if (NULL == node_name) { + ret = RCUTILS_RET_BAD_ALLOC; + goto clean; + } + } else { + // substring namespace including the last '/' + char * namespace_ = rcutils_strndup(name, separator_pos - name + 1, allocator); + if (NULL == namespace_) { + ret = RCUTILS_RET_BAD_ALLOC; + goto clean; + } + if (namespace_[0] != '/') { + absolute_namespace = rcutils_format_string(allocator, "/%s", namespace_); + allocator.deallocate(namespace_, allocator.state); + if (NULL == absolute_namespace) { + ret = RCUTILS_RET_BAD_ALLOC; + goto clean; + } + } else { + absolute_namespace = namespace_; + } + + node_name = rcutils_strdup(separator_pos + 1, allocator); + if (NULL == node_name) { + ret = RCUTILS_RET_BAD_ALLOC; + goto clean; + } + } + + if (absolute_namespace) { + size_t i = 0; + separator_pos = strchr(absolute_namespace + i + 1, '/'); + if (NULL == separator_pos) { + ret = _validate_namespace(absolute_namespace); + if (RCUTILS_RET_OK != ret) { + goto clean; + } + } else { + do { + size_t len = separator_pos - absolute_namespace - i; + char * namespace_ = rcutils_strndup(absolute_namespace + i, len, allocator); + if (NULL == namespace_) { + ret = RCUTILS_RET_BAD_ALLOC; + goto clean; + } + if (0 == strcmp(namespace_, "/")) { + RCUTILS_SET_ERROR_MSG_WITH_FORMAT_STRING( + "%s contains repeated forward slash", absolute_namespace); + allocator.deallocate(namespace_, allocator.state); + ret = RCUTILS_RET_INVALID_ARGUMENT; + goto clean; + } + if (0 != strcmp(namespace_, "/**") && 0 != strcmp(namespace_, "/*")) { + ret = _validate_namespace(namespace_); + if (RCUTILS_RET_OK != ret) { + allocator.deallocate(namespace_, allocator.state); + goto clean; + } + } + allocator.deallocate(namespace_, allocator.state); + i += len; + } while (NULL != (separator_pos = strchr(absolute_namespace + i + 1, '/'))); + } + } + + if (0 != strcmp(node_name, "*") && 0 != strcmp(node_name, "**")) { + ret = _validate_nodename(node_name); + if (RCUTILS_RET_OK != ret) { + goto clean; + } + } + +clean: + if (absolute_namespace) { + allocator.deallocate(absolute_namespace, allocator.state); + } + if (node_name) { + allocator.deallocate(node_name, allocator.state); + } + return ret; +} + /// /// Parse the key part of the pair /// @@ -466,6 +639,12 @@ rcutils_ret_t parse_key( break; } + ret = _validate_name(node_name_ns, allocator); + if (RCUTILS_RET_OK != ret) { + allocator.deallocate(node_name_ns, allocator.state); + break; + } + ret = find_node(node_name_ns, params_st, node_idx); allocator.deallocate(node_name_ns, allocator.state); if (RCUTILS_RET_OK != ret) { diff --git a/rcl_yaml_param_parser/test/empty_name_in_ns.yaml b/rcl_yaml_param_parser/test/empty_name_in_ns.yaml new file mode 100644 index 000000000..34230f409 --- /dev/null +++ b/rcl_yaml_param_parser/test/empty_name_in_ns.yaml @@ -0,0 +1,14 @@ +lidar_ns//lidar_w: + ros__parameters: + id: 10 + name: front_lidar + ports: [2438, 2439, 2440] + driver1: + dx: 4.56 + dy: 2.30 + fr_sensor_specs: [12, 3, 0, 7] + bk_sensor_specs: [12.1, -2.3, 5.2, 9.0] + is_front: true + driver2: + dx: 1.23 + dy: 0.45 diff --git a/rcl_yaml_param_parser/test/test_parse_yaml.cpp b/rcl_yaml_param_parser/test/test_parse_yaml.cpp index 89e1ec9e6..67ba2ebaa 100644 --- a/rcl_yaml_param_parser/test/test_parse_yaml.cpp +++ b/rcl_yaml_param_parser/test/test_parse_yaml.cpp @@ -15,6 +15,8 @@ #include #include +#include +#include #include "osrf_testing_tools_cpp/scope_exit.hpp" @@ -531,6 +533,131 @@ TEST(test_file_parser, special_float_point) { EXPECT_TRUE(std::isinf(param_value->double_array_value->values[6])); } +TEST(test_file_parser, empty_name_in_ns) { + rcutils_reset_error(); + EXPECT_TRUE(rcutils_get_cwd(cur_dir, 1024)); + rcutils_allocator_t allocator = rcutils_get_default_allocator(); + char * test_path = rcutils_join_path(cur_dir, "test", allocator); + ASSERT_TRUE(NULL != test_path) << rcutils_get_error_string().str; + OSRF_TESTING_TOOLS_CPP_SCOPE_EXIT( + { + allocator.deallocate(test_path, allocator.state); + }); + char * path = rcutils_join_path(test_path, "empty_name_in_ns.yaml", allocator); + ASSERT_TRUE(NULL != path) << rcutils_get_error_string().str; + OSRF_TESTING_TOOLS_CPP_SCOPE_EXIT( + { + allocator.deallocate(path, allocator.state); + }); + EXPECT_TRUE(rcutils_exists(path)); + rcl_params_t * params_hdl = rcl_yaml_node_struct_init(allocator); + ASSERT_TRUE(NULL != params_hdl) << rcutils_get_error_string().str; + OSRF_TESTING_TOOLS_CPP_SCOPE_EXIT( + { + rcl_yaml_node_struct_fini(params_hdl); + }); + bool res = rcl_parse_yaml_file(path, params_hdl); + EXPECT_FALSE(res); +} + +TEST(test_file_parser, wildcards) { + rcutils_reset_error(); + EXPECT_TRUE(rcutils_get_cwd(cur_dir, 1024)); + rcutils_allocator_t allocator = rcutils_get_default_allocator(); + char * test_path = rcutils_join_path(cur_dir, "test", allocator); + ASSERT_TRUE(NULL != test_path) << rcutils_get_error_string().str; + OSRF_TESTING_TOOLS_CPP_SCOPE_EXIT( + { + allocator.deallocate(test_path, allocator.state); + }); + char * path = rcutils_join_path(test_path, "wildcards.yaml", allocator); + ASSERT_TRUE(NULL != path) << rcutils_get_error_string().str; + OSRF_TESTING_TOOLS_CPP_SCOPE_EXIT( + { + allocator.deallocate(path, allocator.state); + }); + EXPECT_TRUE(rcutils_exists(path)); + rcl_params_t * params_hdl = rcl_yaml_node_struct_init(allocator); + ASSERT_TRUE(NULL != params_hdl) << rcutils_get_error_string().str; + OSRF_TESTING_TOOLS_CPP_SCOPE_EXIT( + { + rcl_yaml_node_struct_fini(params_hdl); + }); + bool res = rcl_parse_yaml_file(path, params_hdl); + EXPECT_TRUE(res) << rcutils_get_error_string().str; +} + +TEST(test_file_parser, wildcards_node_slash) { + rcutils_reset_error(); + EXPECT_TRUE(rcutils_get_cwd(cur_dir, 1024)); + rcutils_allocator_t allocator = rcutils_get_default_allocator(); + char * test_path = rcutils_join_path(cur_dir, "test", allocator); + ASSERT_TRUE(NULL != test_path) << rcutils_get_error_string().str; + OSRF_TESTING_TOOLS_CPP_SCOPE_EXIT( + { + allocator.deallocate(test_path, allocator.state); + }); + char * path = rcutils_join_path(test_path, "wildcards_node_slash.yaml", allocator); + ASSERT_TRUE(NULL != path) << rcutils_get_error_string().str; + OSRF_TESTING_TOOLS_CPP_SCOPE_EXIT( + { + allocator.deallocate(path, allocator.state); + }); + EXPECT_TRUE(rcutils_exists(path)); + rcl_params_t * params_hdl = rcl_yaml_node_struct_init(allocator); + ASSERT_TRUE(NULL != params_hdl) << rcutils_get_error_string().str; + OSRF_TESTING_TOOLS_CPP_SCOPE_EXIT( + { + rcl_yaml_node_struct_fini(params_hdl); + }); + bool res = rcl_parse_yaml_file(path, params_hdl); + EXPECT_FALSE(res); +} + +TEST(test_file_parser, wildcards_partial) { + rcutils_reset_error(); + EXPECT_TRUE(rcutils_get_cwd(cur_dir, 1024)); + rcutils_allocator_t allocator = rcutils_get_default_allocator(); + char * test_path = rcutils_join_path(cur_dir, "test", allocator); + ASSERT_TRUE(NULL != test_path) << rcutils_get_error_string().str; + OSRF_TESTING_TOOLS_CPP_SCOPE_EXIT( + { + allocator.deallocate(test_path, allocator.state); + }); + const std::vector filenames = { + "wildcards_partial_01.yaml", + "wildcards_partial_02.yaml", + "wildcards_partial_03.yaml", + "wildcards_partial_04.yaml", + "wildcards_partial_05.yaml", + "wildcards_partial_06.yaml", + "wildcards_partial_07.yaml", + "wildcards_partial_08.yaml", + "wildcards_partial_09.yaml", + "wildcards_partial_10.yaml", + "wildcards_partial_11.yaml", + "wildcards_partial_12.yaml" + }; + + for (auto & filename : filenames) { + char * path = rcutils_join_path(test_path, filename.c_str(), allocator); + ASSERT_TRUE(NULL != path) << rcutils_get_error_string().str; + OSRF_TESTING_TOOLS_CPP_SCOPE_EXIT( + { + allocator.deallocate(path, allocator.state); + }); + EXPECT_TRUE(rcutils_exists(path)); + rcl_params_t * params_hdl = rcl_yaml_node_struct_init(allocator); + ASSERT_TRUE(NULL != params_hdl) << rcutils_get_error_string().str; + OSRF_TESTING_TOOLS_CPP_SCOPE_EXIT( + { + rcl_yaml_node_struct_fini(params_hdl); + }); + bool res = rcl_parse_yaml_file(path, params_hdl); + EXPECT_FALSE(res); + } +} + int32_t main(int32_t argc, char ** argv) { ::testing::InitGoogleTest(&argc, argv); diff --git a/rcl_yaml_param_parser/test/wildcards.yaml b/rcl_yaml_param_parser/test/wildcards.yaml new file mode 100644 index 000000000..6507032c4 --- /dev/null +++ b/rcl_yaml_param_parser/test/wildcards.yaml @@ -0,0 +1,80 @@ +# config/test_yaml +--- + +/**: + ros__parameters: + id: 10 + name: "front lidar" +/*: + ros__parameters: + id: 10 + name: "front lidar" +/**/some_node1: + ros__parameters: + id: 10 + name: "front lidar" +/*/some_node2: + ros__parameters: + id: 10 + name: "front lidar" +/**: + some_node3: + ros__parameters: + id: 10 + name: "front lidar" +/*: + some_node4: + ros__parameters: + id: 10 + name: "front lidar" +/foo1/**: + ros__parameters: + id: 10 + name: "front lidar" +/foo2/*: + ros__parameters: + id: 10 + name: "front lidar" +/foo3/**: + some_node5: + ros__parameters: + id: 10 + name: "front lidar" +/foo4/*: + some_node6: + ros__parameters: + id: 10 + name: "front lidar" +/foo5: + "*": + ros__parameters: + id: 10 + name: "front lidar" +/foo6/**/some_node6: + ros__parameters: + id: 10 + name: "front lidar" +/foo7/*/some_node7: + ros__parameters: + id: 10 + name: "front lidar" +/foo8/**/bar: + some_node8: + ros__parameters: + id: 10 + name: "front lidar" +/foo9/*/bar: + some_node9: + ros__parameters: + id: 10 + name: "front lidar" +/foo10/**/bar: + bar1/bar2/**: + ros__parameters: + id: 10 + name: "front lidar" +/foo11/*/bar: + bar1/bar2/*: + ros__parameters: + id: 10 + name: "front lidar" diff --git a/rcl_yaml_param_parser/test/wildcards_node_slash.yaml b/rcl_yaml_param_parser/test/wildcards_node_slash.yaml new file mode 100644 index 000000000..8175f5f50 --- /dev/null +++ b/rcl_yaml_param_parser/test/wildcards_node_slash.yaml @@ -0,0 +1,8 @@ +# config/test_yaml +--- + +/foo: + /*: + ros__parameters: + id: 10 + name: "front lidar" diff --git a/rcl_yaml_param_parser/test/wildcards_partial_01.yaml b/rcl_yaml_param_parser/test/wildcards_partial_01.yaml new file mode 100644 index 000000000..129b382ca --- /dev/null +++ b/rcl_yaml_param_parser/test/wildcards_partial_01.yaml @@ -0,0 +1,7 @@ +# config/test_yaml +--- + +/foo**: + ros__parameters: + id: 10 + name: "front lidar" diff --git a/rcl_yaml_param_parser/test/wildcards_partial_02.yaml b/rcl_yaml_param_parser/test/wildcards_partial_02.yaml new file mode 100644 index 000000000..94dc0ebfe --- /dev/null +++ b/rcl_yaml_param_parser/test/wildcards_partial_02.yaml @@ -0,0 +1,7 @@ +# config/test_yaml +--- + +/**foo: + ros__parameters: + id: 10 + name: "front lidar" diff --git a/rcl_yaml_param_parser/test/wildcards_partial_03.yaml b/rcl_yaml_param_parser/test/wildcards_partial_03.yaml new file mode 100644 index 000000000..073ca5df4 --- /dev/null +++ b/rcl_yaml_param_parser/test/wildcards_partial_03.yaml @@ -0,0 +1,7 @@ +# config/test_yaml +--- + +/f**oo: + ros__parameters: + id: 10 + name: "front lidar" diff --git a/rcl_yaml_param_parser/test/wildcards_partial_04.yaml b/rcl_yaml_param_parser/test/wildcards_partial_04.yaml new file mode 100644 index 000000000..db100762c --- /dev/null +++ b/rcl_yaml_param_parser/test/wildcards_partial_04.yaml @@ -0,0 +1,7 @@ +# config/test_yaml +--- + +/foo*: + ros__parameters: + id: 10 + name: "front lidar" diff --git a/rcl_yaml_param_parser/test/wildcards_partial_05.yaml b/rcl_yaml_param_parser/test/wildcards_partial_05.yaml new file mode 100644 index 000000000..63998422d --- /dev/null +++ b/rcl_yaml_param_parser/test/wildcards_partial_05.yaml @@ -0,0 +1,7 @@ +# config/test_yaml +--- + +/*foo: + ros__parameters: + id: 10 + name: "front lidar" diff --git a/rcl_yaml_param_parser/test/wildcards_partial_06.yaml b/rcl_yaml_param_parser/test/wildcards_partial_06.yaml new file mode 100644 index 000000000..785abd55f --- /dev/null +++ b/rcl_yaml_param_parser/test/wildcards_partial_06.yaml @@ -0,0 +1,7 @@ +# config/test_yaml +--- + +/f*oo: + ros__parameters: + id: 10 + name: "front lidar" diff --git a/rcl_yaml_param_parser/test/wildcards_partial_07.yaml b/rcl_yaml_param_parser/test/wildcards_partial_07.yaml new file mode 100644 index 000000000..5833f75fb --- /dev/null +++ b/rcl_yaml_param_parser/test/wildcards_partial_07.yaml @@ -0,0 +1,7 @@ +# config/test_yaml +--- + +/**/node**: + ros__parameters: + id: 10 + name: "front lidar" diff --git a/rcl_yaml_param_parser/test/wildcards_partial_08.yaml b/rcl_yaml_param_parser/test/wildcards_partial_08.yaml new file mode 100644 index 000000000..b8028c5b7 --- /dev/null +++ b/rcl_yaml_param_parser/test/wildcards_partial_08.yaml @@ -0,0 +1,7 @@ +# config/test_yaml +--- + +/**/**node: + ros__parameters: + id: 10 + name: "front lidar" diff --git a/rcl_yaml_param_parser/test/wildcards_partial_09.yaml b/rcl_yaml_param_parser/test/wildcards_partial_09.yaml new file mode 100644 index 000000000..68ab60ba0 --- /dev/null +++ b/rcl_yaml_param_parser/test/wildcards_partial_09.yaml @@ -0,0 +1,7 @@ +# config/test_yaml +--- + +/**/n**ode: + ros__parameters: + id: 10 + name: "front lidar" diff --git a/rcl_yaml_param_parser/test/wildcards_partial_10.yaml b/rcl_yaml_param_parser/test/wildcards_partial_10.yaml new file mode 100644 index 000000000..4acee370e --- /dev/null +++ b/rcl_yaml_param_parser/test/wildcards_partial_10.yaml @@ -0,0 +1,7 @@ +# config/test_yaml +--- + +/**/*node: + ros__parameters: + id: 10 + name: "front lidar" diff --git a/rcl_yaml_param_parser/test/wildcards_partial_11.yaml b/rcl_yaml_param_parser/test/wildcards_partial_11.yaml new file mode 100644 index 000000000..1fda39672 --- /dev/null +++ b/rcl_yaml_param_parser/test/wildcards_partial_11.yaml @@ -0,0 +1,7 @@ +# config/test_yaml +--- + +/**/node*: + ros__parameters: + id: 10 + name: "front lidar" diff --git a/rcl_yaml_param_parser/test/wildcards_partial_12.yaml b/rcl_yaml_param_parser/test/wildcards_partial_12.yaml new file mode 100644 index 000000000..b3bc972dc --- /dev/null +++ b/rcl_yaml_param_parser/test/wildcards_partial_12.yaml @@ -0,0 +1,7 @@ +# config/test_yaml +--- + +/**/n*ode: + ros__parameters: + id: 10 + name: "front lidar"