diff --git a/rcl_yaml_param_parser/CMakeLists.txt b/rcl_yaml_param_parser/CMakeLists.txt index 397f662ae..5b2a64cfb 100644 --- a/rcl_yaml_param_parser/CMakeLists.txt +++ b/rcl_yaml_param_parser/CMakeLists.txt @@ -22,9 +22,11 @@ endif() set(rcl_yaml_parser_sources src/add_to_arrays.c src/namespace.c + src/node_params_descriptors.c src/node_params.c src/parse.c src/parser.c + src/yaml_descriptor.c src/yaml_variant.c ) @@ -92,6 +94,18 @@ if(BUILD_TESTING) target_link_libraries(test_node_params ${PROJECT_NAME}) endif() + ament_add_gtest(test_node_params_descriptors + test/test_node_params_descriptors.cpp + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" + ) + if(TARGET test_node_params_descriptors) + ament_target_dependencies(test_node_params_descriptors + "rcutils" + "osrf_testing_tools_cpp" + ) + target_link_libraries(test_node_params_descriptors ${PROJECT_NAME}) + endif() + ament_add_gtest(test_parse_yaml test/test_parse_yaml.cpp WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" @@ -166,6 +180,18 @@ if(BUILD_TESTING) target_link_libraries(test_yaml_variant ${PROJECT_NAME}) endif() + ament_add_gtest(test_yaml_descriptor + test/test_yaml_descriptor.cpp + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" + ) + if(TARGET test_yaml_descriptor) + ament_target_dependencies(test_yaml_descriptor + "rcutils" + "osrf_testing_tools_cpp" + ) + target_link_libraries(test_yaml_descriptor ${PROJECT_NAME}) + endif() + add_performance_test(benchmark_parse_yaml test/benchmark/benchmark_parse_yaml.cpp WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}") if(TARGET benchmark_parse_yaml) diff --git a/rcl_yaml_param_parser/include/rcl_yaml_param_parser/parser.h b/rcl_yaml_param_parser/include/rcl_yaml_param_parser/parser.h index 23cb04d06..131a92365 100644 --- a/rcl_yaml_param_parser/include/rcl_yaml_param_parser/parser.h +++ b/rcl_yaml_param_parser/include/rcl_yaml_param_parser/parser.h @@ -119,6 +119,17 @@ rcl_variant_t * rcl_yaml_node_struct_get( const char * param_name, rcl_params_t * params_st); +/// \brief Get the descriptor struct for a given parameter +/// \param[in] node_name is the name of the node to which the parameter belongs +/// \param[in] param_name is the name of the parameter whose descriptor is to be retrieved +/// \param[inout] params_st points to the populated (or to be populated) parameter struct +/// \return parameter descriptor struct on success and NULL on failure +RCL_YAML_PARAM_PARSER_PUBLIC +rcl_param_descriptor_t * rcl_yaml_node_struct_get_descriptor( + const char * node_name, + const char * param_name, + rcl_params_t * params_st); + /// \brief Print the parameter structure to stdout /// \param[in] params_st points to the populated parameter struct RCL_YAML_PARAM_PARSER_PUBLIC diff --git a/rcl_yaml_param_parser/include/rcl_yaml_param_parser/types.h b/rcl_yaml_param_parser/include/rcl_yaml_param_parser/types.h index e0e4b5f1c..60f1f243a 100644 --- a/rcl_yaml_param_parser/include/rcl_yaml_param_parser/types.h +++ b/rcl_yaml_param_parser/include/rcl_yaml_param_parser/types.h @@ -95,6 +95,33 @@ typedef struct rcl_node_params_s size_t capacity_params; ///< Capacity of parameters in the node } rcl_node_params_t; +/// \typedef rcl_param_descriptor_t +/// \brief param_descriptor_t stores the descriptor of a parameter +typedef struct rcl_param_descriptor_s +{ + bool * read_only; + bool * dynamic_typing; + uint8_t * type; + char * description; + char * additional_constraints; + double * min_value_double; + double * max_value_double; + double * step_double; + int64_t * min_value_int; + int64_t * max_value_int; + int64_t * step_int; +} rcl_param_descriptor_t; + +/// \typedef rcl_node_params_descriptors_t +/// \brief node_params_descriptors_t stores all the parameter descriptors of a single node +typedef struct rcl_node_params_descriptors_s +{ + char ** parameter_names; ///< Array of parameter names (keys) + rcl_param_descriptor_t * parameter_descriptors; ///< Array of corresponding parameter descriptors + size_t num_descriptors; ///< Number of parameters in the node + size_t capacity_descriptors; ///< Capacity of parameters in the node +} rcl_node_params_descriptors_t; + /// stores all the parameters of all nodes of a process /* * \typedef rcl_params_t @@ -103,6 +130,7 @@ typedef struct rcl_params_s { char ** node_names; ///< List of names of the node rcl_node_params_t * params; ///< Array of parameters + rcl_node_params_descriptors_t * descriptors; ///< Array of parameter descriptors size_t num_nodes; ///< Number of nodes size_t capacity_nodes; ///< Capacity of nodes rcutils_allocator_t allocator; ///< Allocator used diff --git a/rcl_yaml_param_parser/src/impl/node_params_descriptors.h b/rcl_yaml_param_parser/src/impl/node_params_descriptors.h new file mode 100644 index 000000000..7e50b7cdf --- /dev/null +++ b/rcl_yaml_param_parser/src/impl/node_params_descriptors.h @@ -0,0 +1,69 @@ +// Copyright 2018 Apex.AI, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef IMPL__NODE_PARAMS_DESCRIPTORS_H_ +#define IMPL__NODE_PARAMS_DESCRIPTORS_H_ + +#include "rcl_yaml_param_parser/types.h" +#include "rcl_yaml_param_parser/visibility_control.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +/// +/// Create rcl_node_params_descriptors_t structure +/// +RCL_YAML_PARAM_PARSER_PUBLIC +RCUTILS_WARN_UNUSED +rcutils_ret_t node_params_descriptors_init( + rcl_node_params_descriptors_t * node_descriptors, + const rcutils_allocator_t allocator); + +/// +/// Create rcl_node_params_descriptors_t structure with a capacity +/// +RCL_YAML_PARAM_PARSER_PUBLIC +RCUTILS_WARN_UNUSED +rcutils_ret_t node_params_descriptors_init_with_capacity( + rcl_node_params_descriptors_t * node_descriptors, + size_t capacity, + const rcutils_allocator_t allocator); + +/// +/// Reallocate rcl_node_params_descriptors_t structure with a new capacity +/// \post the address of \p parameter_names in \p node_params might be changed +/// even if the result value is `RCL_RET_BAD_ALLOC`. +/// +RCL_YAML_PARAM_PARSER_PUBLIC +RCUTILS_WARN_UNUSED +rcutils_ret_t node_params_descriptors_reallocate( + rcl_node_params_descriptors_t * node_descriptors, + size_t new_capacity, + const rcutils_allocator_t allocator); + +/// +/// Finalize rcl_node_params_descriptors_t structure +/// +RCL_YAML_PARAM_PARSER_PUBLIC +void rcl_yaml_node_params_descriptors_fini( + rcl_node_params_descriptors_t * node_descriptors, + const rcutils_allocator_t allocator); + +#ifdef __cplusplus +} +#endif + +#endif // IMPL__NODE_PARAMS_DESCRIPTORS_H_ diff --git a/rcl_yaml_param_parser/src/impl/parse.h b/rcl_yaml_param_parser/src/impl/parse.h index 781e8f52d..b65673b64 100644 --- a/rcl_yaml_param_parser/src/impl/parse.h +++ b/rcl_yaml_param_parser/src/impl/parse.h @@ -46,6 +46,16 @@ rcutils_ret_t parse_value( data_types_t * seq_data_type, rcl_params_t * params_st); +RCL_YAML_PARAM_PARSER_PUBLIC +RCUTILS_WARN_UNUSED +rcutils_ret_t parse_descriptor( + namespace_tracker_t * ns_tracker, + const yaml_event_t event, + const bool is_seq, + const size_t node_idx, + const size_t parameter_idx, + rcl_params_t * params_st); + RCL_YAML_PARAM_PARSER_PUBLIC RCUTILS_WARN_UNUSED rcutils_ret_t parse_key( @@ -87,6 +97,14 @@ rcutils_ret_t find_parameter( rcl_params_t * param_st, size_t * parameter_idx); +RCL_YAML_PARAM_PARSER_PUBLIC +RCUTILS_WARN_UNUSED +rcutils_ret_t find_descriptor( + const size_t node_idx, + const char * parameter_name, + rcl_params_t * param_st, + size_t * parameter_idx); + #ifdef __cplusplus } #endif diff --git a/rcl_yaml_param_parser/src/impl/types.h b/rcl_yaml_param_parser/src/impl/types.h index 4776dc128..79d073c3b 100644 --- a/rcl_yaml_param_parser/src/impl/types.h +++ b/rcl_yaml_param_parser/src/impl/types.h @@ -28,6 +28,7 @@ extern "C" #endif #define PARAMS_KEY "ros__parameters" +#define PARAMS_DESCRIPTORS_KEY "ros__parameter_descriptors" #define NODE_NS_SEPERATOR "/" #define PARAMETER_NS_SEPERATOR "." @@ -36,6 +37,7 @@ typedef enum yaml_map_lvl_e MAP_UNINIT_LVL = 0U, MAP_NODE_NAME_LVL = 1U, MAP_PARAMS_LVL = 2U, + MAP_PARAMS_DESCRIPTORS_LVL = 3U, } yaml_map_lvl_t; /// Basic supported data types in the yaml file @@ -51,7 +53,8 @@ typedef enum data_types_e typedef enum namespace_type_e { NS_TYPE_NODE = 1U, - NS_TYPE_PARAM = 2U + NS_TYPE_PARAM = 2U, + NS_TYPE_DESCRIPTOR = 3U } namespace_type_t; /// Keep track of node and parameter name spaces @@ -61,6 +64,8 @@ typedef struct namespace_tracker_s uint32_t num_node_ns; char * parameter_ns; uint32_t num_parameter_ns; + char * descriptor_key_ns; + uint32_t num_descriptor_key_ns; } namespace_tracker_t; #ifdef __cplusplus diff --git a/rcl_yaml_param_parser/src/impl/yaml_descriptor.h b/rcl_yaml_param_parser/src/impl/yaml_descriptor.h new file mode 100644 index 000000000..e901dfacc --- /dev/null +++ b/rcl_yaml_param_parser/src/impl/yaml_descriptor.h @@ -0,0 +1,51 @@ +// Copyright 2018 Apex.AI, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef IMPL__YAML_DESCRIPTOR_H_ +#define IMPL__YAML_DESCRIPTOR_H_ + +#include "rcutils/allocator.h" + +#include "./types.h" +#include "rcl_yaml_param_parser/types.h" +#include "rcl_yaml_param_parser/visibility_control.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +/// +/// Finalize an rcl_param_descriptor_t +/// +RCL_YAML_PARAM_PARSER_PUBLIC +void rcl_yaml_descriptor_fini( + rcl_param_descriptor_t * param_desc, + const rcutils_allocator_t allocator); + +/// +/// Copy a rcl_param_descriptor_t from param_desc to out_param_desc +/// +RCL_YAML_PARAM_PARSER_PUBLIC +RCUTILS_WARN_UNUSED +bool rcl_yaml_descriptor_copy( + rcl_param_descriptor_t * out_param_desc, + const rcl_param_descriptor_t * param_desc, + rcutils_allocator_t allocator); + +#ifdef __cplusplus +} +#endif + +#endif // IMPL__YAML_DESCRIPTOR_H_ diff --git a/rcl_yaml_param_parser/src/namespace.c b/rcl_yaml_param_parser/src/namespace.c index 0ca3fed67..d11b883c1 100644 --- a/rcl_yaml_param_parser/src/namespace.c +++ b/rcl_yaml_param_parser/src/namespace.c @@ -184,6 +184,16 @@ rcutils_ret_t replace_ns( } ns_tracker->num_parameter_ns = new_ns_count; break; + case NS_TYPE_DESCRIPTOR: + if (NULL != ns_tracker->descriptor_key_ns) { + allocator.deallocate(ns_tracker->descriptor_key_ns, allocator.state); + } + ns_tracker->descriptor_key_ns = rcutils_strdup(new_ns, allocator); + if (NULL == ns_tracker->descriptor_key_ns) { + return RCUTILS_RET_BAD_ALLOC; + } + ns_tracker->num_descriptor_key_ns = new_ns_count; + break; default: res = RCUTILS_RET_ERROR; break; diff --git a/rcl_yaml_param_parser/src/node_params_descriptors.c b/rcl_yaml_param_parser/src/node_params_descriptors.c new file mode 100644 index 000000000..5eb003252 --- /dev/null +++ b/rcl_yaml_param_parser/src/node_params_descriptors.c @@ -0,0 +1,148 @@ +// Copyright 2018 Apex.AI, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "./impl/node_params_descriptors.h" +#include "./impl/types.h" +#include "./impl/yaml_descriptor.h" + +#define INIT_NUM_PARAMS_DESCRIPTORS_PER_NODE 128U + +rcutils_ret_t node_params_descriptors_init( + rcl_node_params_descriptors_t * node_descriptors, + const rcutils_allocator_t allocator) +{ + return node_params_descriptors_init_with_capacity( + node_descriptors, INIT_NUM_PARAMS_DESCRIPTORS_PER_NODE, allocator); +} + +rcutils_ret_t node_params_descriptors_init_with_capacity( + rcl_node_params_descriptors_t * node_descriptors, + size_t capacity, + const rcutils_allocator_t allocator) +{ + RCUTILS_CHECK_ARGUMENT_FOR_NULL(node_descriptors, RCUTILS_RET_INVALID_ARGUMENT); + RCUTILS_CHECK_ALLOCATOR_WITH_MSG( + &allocator, "invalid allocator", return RCUTILS_RET_INVALID_ARGUMENT); + if (capacity == 0) { + RCUTILS_SET_ERROR_MSG("capacity can't be zero"); + return RCUTILS_RET_INVALID_ARGUMENT; + } + + node_descriptors->parameter_names = allocator.zero_allocate( + capacity, sizeof(char *), allocator.state); + if (NULL == node_descriptors->parameter_names) { + RCUTILS_SET_ERROR_MSG("Failed to allocate memory for node parameter names"); + return RCUTILS_RET_BAD_ALLOC; + } + + node_descriptors->parameter_descriptors = allocator.zero_allocate( + capacity, sizeof(rcl_param_descriptor_t), allocator.state); + if (NULL == node_descriptors->parameter_descriptors) { + allocator.deallocate(node_descriptors->parameter_names, allocator.state); + node_descriptors->parameter_names = NULL; + RCUTILS_SET_ERROR_MSG("Failed to allocate memory for node parameter descriptors"); + return RCUTILS_RET_BAD_ALLOC; + } + + node_descriptors->num_descriptors = 0U; + node_descriptors->capacity_descriptors = capacity; + return RCUTILS_RET_OK; +} + +rcutils_ret_t node_params_descriptors_reallocate( + rcl_node_params_descriptors_t * node_descriptors, + size_t new_capacity, + const rcutils_allocator_t allocator) +{ + RCUTILS_CHECK_ARGUMENT_FOR_NULL(node_descriptors, RCUTILS_RET_INVALID_ARGUMENT); + RCUTILS_CHECK_ALLOCATOR_WITH_MSG( + &allocator, "invalid allocator", return RCUTILS_RET_INVALID_ARGUMENT); + // invalid if new_capacity is less than num_descriptors + if (new_capacity < node_descriptors->num_descriptors) { + RCUTILS_SET_ERROR_MSG_WITH_FORMAT_STRING( + "new capacity '%zu' must be greater than or equal to '%zu'", + new_capacity, + node_descriptors->num_descriptors); + return RCUTILS_RET_INVALID_ARGUMENT; + } + + void * parameter_names = allocator.reallocate( + node_descriptors->parameter_names, new_capacity * sizeof(char *), allocator.state); + if (NULL == parameter_names) { + RCUTILS_SET_ERROR_MSG("Failed to reallocate node parameter names"); + return RCUTILS_RET_BAD_ALLOC; + } + node_descriptors->parameter_names = parameter_names; + // zero initialization for the added memory + if (new_capacity > node_descriptors->capacity_descriptors) { + memset( + node_descriptors->parameter_names + node_descriptors->capacity_descriptors, 0, + (new_capacity - node_descriptors->capacity_descriptors) * sizeof(char *)); + } + + void * parameter_descriptors = allocator.reallocate( + node_descriptors->parameter_descriptors, + new_capacity * sizeof(rcl_param_descriptor_t), allocator.state); + if (NULL == parameter_descriptors) { + RCUTILS_SET_ERROR_MSG("Failed to reallocate node parameter values"); + return RCUTILS_RET_BAD_ALLOC; + } + node_descriptors->parameter_descriptors = parameter_descriptors; + // zero initialization for the added memory + if (new_capacity > node_descriptors->capacity_descriptors) { + memset( + &node_descriptors->parameter_descriptors[node_descriptors->capacity_descriptors], 0, + (new_capacity - node_descriptors->capacity_descriptors) * sizeof(rcl_param_descriptor_t)); + } + + node_descriptors->capacity_descriptors = new_capacity; + return RCUTILS_RET_OK; +} + +void rcl_yaml_node_params_descriptors_fini( + rcl_node_params_descriptors_t * node_descriptors, + const rcutils_allocator_t allocator) +{ + if (NULL == node_descriptors) { + return; + } + + if (NULL != node_descriptors->parameter_names) { + for (size_t parameter_idx = 0U; parameter_idx < node_descriptors->num_descriptors; + parameter_idx++) + { + char * param_name = node_descriptors->parameter_names[parameter_idx]; + if (NULL != param_name) { + allocator.deallocate(param_name, allocator.state); + } + } + allocator.deallocate(node_descriptors->parameter_names, allocator.state); + node_descriptors->parameter_names = NULL; + } + + if (NULL != node_descriptors->parameter_descriptors) { + for (size_t parameter_idx = 0U; parameter_idx < node_descriptors->num_descriptors; + parameter_idx++) + { + rcl_yaml_descriptor_fini( + &(node_descriptors->parameter_descriptors[parameter_idx]), allocator); + } + + allocator.deallocate(node_descriptors->parameter_descriptors, allocator.state); + node_descriptors->parameter_descriptors = NULL; + } + + node_descriptors->num_descriptors = 0; + node_descriptors->capacity_descriptors = 0; +} diff --git a/rcl_yaml_param_parser/src/parse.c b/rcl_yaml_param_parser/src/parse.c index acce68a92..ceadf8718 100644 --- a/rcl_yaml_param_parser/src/parse.c +++ b/rcl_yaml_param_parser/src/parse.c @@ -29,6 +29,7 @@ #include "./impl/parse.h" #include "./impl/namespace.h" #include "./impl/node_params.h" +#include "./impl/node_params_descriptors.h" #include "rcl_yaml_param_parser/parser.h" #include "rcl_yaml_param_parser/visibility_control.h" @@ -445,6 +446,184 @@ rcutils_ret_t parse_value( return ret; } +rcutils_ret_t parse_descriptor( + namespace_tracker_t * ns_tracker, + const yaml_event_t event, + const bool is_seq, + const size_t node_idx, + const size_t parameter_idx, + rcl_params_t * params_st) +{ + RCUTILS_CHECK_ARGUMENT_FOR_NULL(params_st, RCUTILS_RET_INVALID_ARGUMENT); + + rcutils_allocator_t allocator = params_st->allocator; + RCUTILS_CHECK_ALLOCATOR_WITH_MSG( + &allocator, "invalid allocator", return RCUTILS_RET_INVALID_ARGUMENT); + + if (0U == params_st->num_nodes) { + RCUTILS_SET_ERROR_MSG("No node to update"); + return RCUTILS_RET_INVALID_ARGUMENT; + } + + const size_t val_size = event.data.scalar.length; + const char * value = (char *)event.data.scalar.value; + yaml_scalar_style_t style = event.data.scalar.style; + const uint32_t line_num = ((uint32_t)(event.start_mark.line) + 1U); + + RCUTILS_CHECK_FOR_NULL_WITH_MSG( + value, "event argument has no value", return RCUTILS_RET_INVALID_ARGUMENT); + + if (is_seq) { + RCUTILS_SET_ERROR_MSG_WITH_FORMAT_STRING( + "Sequences not supported for parameter descriptors at line %d", line_num); + return RCUTILS_RET_ERROR; + } + + if (style != YAML_SINGLE_QUOTED_SCALAR_STYLE && + style != YAML_DOUBLE_QUOTED_SCALAR_STYLE && + 0U == val_size) + { + RCUTILS_SET_ERROR_MSG_WITH_FORMAT_STRING("No value at line %d", line_num); + return RCUTILS_RET_ERROR; + } + + if (NULL == params_st->descriptors[node_idx].parameter_descriptors) { + RCUTILS_SET_ERROR_MSG("Internal error: Invalid mem"); + return RCUTILS_RET_BAD_ALLOC; + } + + rcl_param_descriptor_t * param_descriptor = + &(params_st->descriptors[node_idx].parameter_descriptors[parameter_idx]); + + rcl_node_params_descriptors_t * node_descriptors_st = &(params_st->descriptors[node_idx]); + + // Increase descriptor count only once when a new parameter descriptor value is parsed + if (parameter_idx == node_descriptors_st->num_descriptors) { + node_descriptors_st->num_descriptors++; + } + + data_types_t val_type; + void * ret_val = get_value(value, style, &val_type, allocator); + if (NULL == ret_val) { + RCUTILS_SET_ERROR_MSG_WITH_FORMAT_STRING( + "Error parsing value %s at line %d", value, line_num); + return RCUTILS_RET_ERROR; + } + + if (NULL == ns_tracker->parameter_ns) { + RCUTILS_SET_ERROR_MSG_WITH_FORMAT_STRING( + "Parameter assignment at line %d unallowed in " PARAMS_DESCRIPTORS_KEY, line_num); + return RCUTILS_RET_ERROR; + } + + // If parsing a yaml value, then current parameter namespace must be parameter name + allocator.deallocate( + params_st->descriptors[node_idx].parameter_names[parameter_idx], + allocator.state); + params_st->descriptors[node_idx].parameter_names[parameter_idx] = + rcutils_strdup(ns_tracker->parameter_ns, allocator); + + if (NULL == ns_tracker->descriptor_key_ns) { + RCUTILS_SET_ERROR_MSG("Internal error: Invalid mem"); + return RCUTILS_RET_ERROR; + } + + char * dt = "dynamic_typing"; + if (0 == strncmp( + "additional_constraints", ns_tracker->descriptor_key_ns, + strlen("additional_constraints"))) + { + if (val_type == DATA_TYPE_STRING) { + param_descriptor->additional_constraints = (char *)ret_val; + } else { + RCUTILS_SET_ERROR_MSG_WITH_FORMAT_STRING( + "Value type 'string' expected at line %d for " + PARAMS_DESCRIPTORS_KEY " key: additional_constraints", + line_num); + return RCUTILS_RET_ERROR; + } + } else if (0 == strncmp("type", ns_tracker->descriptor_key_ns, strlen("type"))) { + if (val_type == DATA_TYPE_INT64) { + param_descriptor->type = (uint8_t *)ret_val; + } else { + RCUTILS_SET_ERROR_MSG_WITH_FORMAT_STRING( + "Value type 'integer' expected at line %d for " PARAMS_DESCRIPTORS_KEY " key: type", + line_num); + return RCUTILS_RET_ERROR; + } + } else if (0 == strncmp("min_value", ns_tracker->descriptor_key_ns, strlen("min_value"))) { + if (val_type == DATA_TYPE_INT64) { + param_descriptor->min_value_int = (int64_t *)ret_val; + } else if (val_type == DATA_TYPE_DOUBLE) { + param_descriptor->min_value_double = (double *)ret_val; + } else { + RCUTILS_SET_ERROR_MSG_WITH_FORMAT_STRING( + "Value type 'integer' or 'double' expected at line %d for " + PARAMS_DESCRIPTORS_KEY " key: min_value", + line_num); + return RCUTILS_RET_ERROR; + } + } else if (0 == strncmp("max_value", ns_tracker->descriptor_key_ns, strlen("max_value"))) { + if (val_type == DATA_TYPE_INT64) { + param_descriptor->max_value_int = (int64_t *)ret_val; + } else if (val_type == DATA_TYPE_DOUBLE) { + param_descriptor->max_value_double = (double *)ret_val; + } else { + RCUTILS_SET_ERROR_MSG_WITH_FORMAT_STRING( + "Value type 'integer' or 'double' expected at line %d for " + PARAMS_DESCRIPTORS_KEY " key: max_value", + line_num); + return RCUTILS_RET_ERROR; + } + } else if (0 == strncmp("read_only", ns_tracker->descriptor_key_ns, strlen("read_only"))) { + if (val_type == DATA_TYPE_BOOL) { + param_descriptor->read_only = (bool *)ret_val; + } else { + RCUTILS_SET_ERROR_MSG_WITH_FORMAT_STRING( + "Value type 'bool' expected at line %d for " PARAMS_DESCRIPTORS_KEY " key: read_only", + line_num); + return RCUTILS_RET_ERROR; + } + } else if (0 == strncmp("description", ns_tracker->descriptor_key_ns, strlen("description"))) { + if (val_type == DATA_TYPE_STRING) { + param_descriptor->description = (char *)ret_val; + } else { + RCUTILS_SET_ERROR_MSG_WITH_FORMAT_STRING( + "Value type 'string' expected at line %d for " PARAMS_DESCRIPTORS_KEY " key: description", + line_num); + return RCUTILS_RET_ERROR; + } + } else if (0 == strncmp("step", ns_tracker->descriptor_key_ns, strlen("step"))) { + if (val_type == DATA_TYPE_INT64) { + param_descriptor->step_int = (int64_t *)ret_val; + } else if (val_type == DATA_TYPE_DOUBLE) { + param_descriptor->step_double = (double *)ret_val; + } else { + RCUTILS_SET_ERROR_MSG_WITH_FORMAT_STRING( + "Value type 'integer' or 'double' expected at line %d for " + PARAMS_DESCRIPTORS_KEY " key: step", + line_num); + return RCUTILS_RET_ERROR; + } + } else if (0 == strncmp(dt, ns_tracker->descriptor_key_ns, strlen(dt))) { + if (val_type == DATA_TYPE_BOOL) { + param_descriptor->dynamic_typing = (bool *)ret_val; + } else { + RCUTILS_SET_ERROR_MSG_WITH_FORMAT_STRING( + "Value type 'bool' expected at line %d for " PARAMS_DESCRIPTORS_KEY " key: dynamic_typing", + line_num); + return RCUTILS_RET_ERROR; + } + } else { + RCUTILS_SET_ERROR_MSG_WITH_FORMAT_STRING( + "Descriptor key at line %d does not match any valid " PARAMS_DESCRIPTORS_KEY " key", + line_num); + return RCUTILS_RET_ERROR; + } + + return RCUTILS_RET_OK; +} + rcutils_ret_t _validate_namespace(const char * namespace_) { @@ -616,60 +795,151 @@ rcutils_ret_t parse_key( break; case MAP_NODE_NAME_LVL: { - /// Till we get PARAMS_KEY, keep adding to node namespace - if (0 != strncmp(PARAMS_KEY, value, strlen(PARAMS_KEY))) { + /// Till we get PARAMS_KEY or PARAMS_DESCRIPTORS_KEY, keep adding to node namespace + if (0 == strncmp(PARAMS_KEY, value, strlen(PARAMS_KEY))) { + if (*is_new_map) { + /// The previous key(last name in namespace) was the node name. Remove it + /// from the namespace + char * node_name_ns = rcutils_strdup(ns_tracker->node_ns, allocator); + if (NULL == node_name_ns) { + ret = RCUTILS_RET_BAD_ALLOC; + 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); + if (RCUTILS_RET_OK != ret) { + break; + } + + ret = rem_name_from_ns(ns_tracker, NS_TYPE_NODE, allocator); + if (RCUTILS_RET_OK != ret) { + RCUTILS_SET_ERROR_MSG_WITH_FORMAT_STRING( + "Internal error adding node namespace at line %d", line_num); + break; + } + } + + /// Bump the map level to PARAMS + (*map_level)++; + } else if (0 == strncmp(PARAMS_DESCRIPTORS_KEY, value, strlen(PARAMS_DESCRIPTORS_KEY))) { + if (*is_new_map) { + /// The previous key(last name in namespace) was the node name. Remove it + /// from the namespace + char * node_name_ns = rcutils_strdup(ns_tracker->node_ns, allocator); + if (NULL == node_name_ns) { + ret = RCUTILS_RET_BAD_ALLOC; + 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) { + break; + } + + ret = rem_name_from_ns(ns_tracker, NS_TYPE_NODE, allocator); + if (RCUTILS_RET_OK != ret) { + RCUTILS_SET_ERROR_MSG_WITH_FORMAT_STRING( + "Internal error adding node namespace at line %d", line_num); + break; + } + } + + /// Bump the map level to PARAMS_DESCRIPTOR + *map_level = 3U; + } else { ret = add_name_to_ns(ns_tracker, value, NS_TYPE_NODE, allocator); if (RCUTILS_RET_OK != ret) { RCUTILS_SET_ERROR_MSG_WITH_FORMAT_STRING( "Internal error adding node namespace at line %d", line_num); break; } - } else { - if (0U == ns_tracker->num_node_ns) { + } + } + break; + case MAP_PARAMS_LVL: + { + char * parameter_ns = NULL; + char * param_name = NULL; + + /// If it is a new map, the previous key is param namespace + if (*is_new_map) { + parameter_ns = params_st->params[*node_idx].parameter_names[*parameter_idx]; + if (NULL == parameter_ns) { RCUTILS_SET_ERROR_MSG_WITH_FORMAT_STRING( - "There are no node names before %s at line %d", PARAMS_KEY, line_num); + "Internal error creating param namespace at line %d", line_num); ret = RCUTILS_RET_ERROR; break; } - /// The previous key(last name in namespace) was the node name. Remove it - /// from the namespace - char * node_name_ns = rcutils_strdup(ns_tracker->node_ns, allocator); - if (NULL == node_name_ns) { - ret = RCUTILS_RET_BAD_ALLOC; + ret = replace_ns( + ns_tracker, parameter_ns, (ns_tracker->num_parameter_ns + 1U), + NS_TYPE_PARAM, allocator); + if (RCUTILS_RET_OK != ret) { + RCUTILS_SET_ERROR_MSG_WITH_FORMAT_STRING( + "Internal error replacing namespace at line %d", line_num); + ret = RCUTILS_RET_ERROR; break; } + *is_new_map = false; + } - ret = _validate_name(node_name_ns, allocator); - if (RCUTILS_RET_OK != ret) { - allocator.deallocate(node_name_ns, allocator.state); + /// Add a parameter name into the node parameters + parameter_ns = ns_tracker->parameter_ns; + if (NULL == parameter_ns) { + ret = find_parameter(*node_idx, value, params_st, parameter_idx); + if (ret != RCUTILS_RET_OK) { break; } - - ret = find_node(node_name_ns, params_st, node_idx); - allocator.deallocate(node_name_ns, allocator.state); - if (RCUTILS_RET_OK != ret) { + } else { + ret = find_parameter(*node_idx, parameter_ns, params_st, parameter_idx); + if (ret != RCUTILS_RET_OK) { break; } - ret = rem_name_from_ns(ns_tracker, NS_TYPE_NODE, allocator); - if (RCUTILS_RET_OK != ret) { - RCUTILS_SET_ERROR_MSG_WITH_FORMAT_STRING( - "Internal error adding node namespace at line %d", line_num); + const size_t params_ns_len = strlen(parameter_ns); + const size_t param_name_len = strlen(value); + const size_t tot_len = (params_ns_len + param_name_len + 2U); + + param_name = allocator.zero_allocate(1U, tot_len, allocator.state); + if (NULL == param_name) { + ret = RCUTILS_RET_BAD_ALLOC; break; } - /// Bump the map level to PARAMS - (*map_level)++; + + memcpy(param_name, parameter_ns, params_ns_len); + param_name[params_ns_len] = '.'; + memcpy((param_name + params_ns_len + 1U), value, param_name_len); + param_name[tot_len - 1U] = '\0'; + + if (NULL != params_st->params[*node_idx].parameter_names[*parameter_idx]) { + // This memory was allocated in find_parameter(), and its pointer is being overwritten + allocator.deallocate( + params_st->params[*node_idx].parameter_names[*parameter_idx], allocator.state); + } + params_st->params[*node_idx].parameter_names[*parameter_idx] = param_name; } } break; - case MAP_PARAMS_LVL: + case MAP_PARAMS_DESCRIPTORS_LVL: { char * parameter_ns = NULL; char * param_name = NULL; /// If it is a new map, the previous key is param namespace if (*is_new_map) { - parameter_ns = params_st->params[*node_idx].parameter_names[*parameter_idx]; + parameter_ns = params_st->descriptors[*node_idx].parameter_names[*parameter_idx]; if (NULL == parameter_ns) { RCUTILS_SET_ERROR_MSG_WITH_FORMAT_STRING( "Internal error creating param namespace at line %d", line_num); @@ -688,19 +958,31 @@ rcutils_ret_t parse_key( *is_new_map = false; } - /// Add a parameter name into the node parameters + /// Add a parameter name into the node parameter descriptors parameter_ns = ns_tracker->parameter_ns; if (NULL == parameter_ns) { - ret = find_parameter(*node_idx, value, params_st, parameter_idx); + ret = find_descriptor(*node_idx, value, params_st, parameter_idx); if (ret != RCUTILS_RET_OK) { break; } } else { - ret = find_parameter(*node_idx, parameter_ns, params_st, parameter_idx); + ret = find_descriptor(*node_idx, parameter_ns, params_st, parameter_idx); if (ret != RCUTILS_RET_OK) { break; } + ret = + replace_ns( + ns_tracker, rcutils_strdup(value, allocator), + (ns_tracker->num_descriptor_key_ns + 1U), + NS_TYPE_DESCRIPTOR, allocator); + if (RCUTILS_RET_OK != ret) { + RCUTILS_SET_ERROR_MSG_WITH_FORMAT_STRING( + "Internal error replacing namespace at line %d", line_num); + ret = RCUTILS_RET_ERROR; + break; + } + const size_t params_ns_len = strlen(parameter_ns); const size_t param_name_len = strlen(value); const size_t tot_len = (params_ns_len + param_name_len + 2U); @@ -716,12 +998,12 @@ rcutils_ret_t parse_key( memcpy((param_name + params_ns_len + 1U), value, param_name_len); param_name[tot_len - 1U] = '\0'; - if (NULL != params_st->params[*node_idx].parameter_names[*parameter_idx]) { + if (NULL != params_st->descriptors[*node_idx].parameter_names[*parameter_idx]) { // This memory was allocated in find_parameter(), and its pointer is being overwritten allocator.deallocate( - params_st->params[*node_idx].parameter_names[*parameter_idx], allocator.state); + params_st->descriptors[*node_idx].parameter_names[*parameter_idx], allocator.state); } - params_st->params[*node_idx].parameter_names[*parameter_idx] = param_name; + params_st->descriptors[*node_idx].parameter_names[*parameter_idx] = param_name; } } break; @@ -801,13 +1083,19 @@ rcutils_ret_t parse_file_events( yaml_event_delete(&event); return RCUTILS_RET_ERROR; } - if (0U == params_st->params[node_idx].num_params) { + if (0U == params_st->params[node_idx].num_params && map_level != 3U) { RCUTILS_SET_ERROR_MSG_WITH_FORMAT_STRING( "Cannot have a value before %s at line %d", PARAMS_KEY, line_num); yaml_event_delete(&event); return RCUTILS_RET_ERROR; } - ret = parse_value(event, is_seq, node_idx, parameter_idx, &seq_data_type, params_st); + if (map_level != 3U) { + ret = parse_value(event, is_seq, node_idx, parameter_idx, &seq_data_type, params_st); + } else { + ret = parse_descriptor( + ns_tracker, event, is_seq, node_idx, parameter_idx, + params_st); + } if (RCUTILS_RET_OK != ret) { break; } @@ -847,6 +1135,11 @@ rcutils_ret_t parse_file_events( { is_new_map = false; } + if ((MAP_PARAMS_DESCRIPTORS_LVL == map_level) && + ((map_depth - (ns_tracker->num_node_ns + 1U)) == 2U)) + { + is_new_map = false; + } break; case YAML_MAPPING_END_EVENT: if (MAP_PARAMS_LVL == map_level) { @@ -861,6 +1154,18 @@ rcutils_ret_t parse_file_events( } else { map_level--; } + } else if (MAP_PARAMS_DESCRIPTORS_LVL == map_level) { + if (ns_tracker->num_parameter_ns > 0U) { + /// Remove param namesapce + ret = rem_name_from_ns(ns_tracker, NS_TYPE_PARAM, allocator); + if (RCUTILS_RET_OK != ret) { + RCUTILS_SET_ERROR_MSG_WITH_FORMAT_STRING( + "Internal error removing parameter namespace at line %d", line_num); + break; + } + } else { + map_level = 1U; + } } else { if ((MAP_NODE_NAME_LVL == map_level) && (map_depth == (ns_tracker->num_node_ns + 1U))) @@ -1002,6 +1307,51 @@ rcutils_ret_t find_parameter( return RCUTILS_RET_OK; } +/// +/// Find parameter descriptor entry index in node parameters' structure +/// +rcutils_ret_t find_descriptor( + const size_t node_idx, + const char * parameter_name, + rcl_params_t * param_st, + size_t * parameter_idx) +{ + assert(NULL != parameter_name); + assert(NULL != param_st); + assert(NULL != parameter_idx); + + assert(node_idx < param_st->num_nodes); + + rcl_node_params_descriptors_t * node_descriptors_st = &(param_st->descriptors[node_idx]); + for (*parameter_idx = 0U; *parameter_idx < node_descriptors_st->num_descriptors; + (*parameter_idx)++) + { + if (0 == strcmp(node_descriptors_st->parameter_names[*parameter_idx], parameter_name)) { + // Descriptor found. + return RCUTILS_RET_OK; + } + } + // Descriptor not found, add it. + rcutils_allocator_t allocator = param_st->allocator; + // Reallocate if necessary + if (node_descriptors_st->num_descriptors >= node_descriptors_st->capacity_descriptors) { + if (RCUTILS_RET_OK != node_params_descriptors_reallocate( + node_descriptors_st, node_descriptors_st->capacity_descriptors * 2, allocator)) + { + return RCUTILS_RET_BAD_ALLOC; + } + } + if (NULL != node_descriptors_st->parameter_names[*parameter_idx]) { + param_st->allocator.deallocate( + node_descriptors_st->parameter_names[*parameter_idx], param_st->allocator.state); + } + node_descriptors_st->parameter_names[*parameter_idx] = rcutils_strdup(parameter_name, allocator); + if (NULL == node_descriptors_st->parameter_names[*parameter_idx]) { + return RCUTILS_RET_BAD_ALLOC; + } + return RCUTILS_RET_OK; +} + /// /// Find node entry index in parameters' structure /// @@ -1040,6 +1390,14 @@ rcutils_ret_t find_node( param_st->node_names[*node_idx] = NULL; return ret; } + + ret = node_params_descriptors_init(&(param_st->descriptors[*node_idx]), allocator); + if (RCUTILS_RET_OK != ret) { + allocator.deallocate(param_st->node_names[*node_idx], allocator.state); + param_st->node_names[*node_idx] = NULL; + return ret; + } + param_st->num_nodes++; return RCUTILS_RET_OK; } diff --git a/rcl_yaml_param_parser/src/parser.c b/rcl_yaml_param_parser/src/parser.c index 028ad870e..dafc49240 100644 --- a/rcl_yaml_param_parser/src/parser.c +++ b/rcl_yaml_param_parser/src/parser.c @@ -33,7 +33,9 @@ #include "./impl/types.h" #include "./impl/parse.h" #include "./impl/node_params.h" +#include "./impl/node_params_descriptors.h" #include "./impl/yaml_variant.h" +#include "./impl/yaml_descriptor.h" #define INIT_NUM_NODE_ENTRIES 128U @@ -79,6 +81,15 @@ rcl_params_t * rcl_yaml_node_struct_init_with_capacity( goto clean; } + params_st->descriptors = allocator.zero_allocate( + capacity, sizeof(rcl_node_params_descriptors_t), allocator.state); + if (NULL == params_st->descriptors) { + allocator.deallocate(params_st->node_names, allocator.state); + params_st->node_names = NULL; + RCUTILS_SET_ERROR_MSG("Failed to allocate memory for parameter descriptors"); + goto clean; + } + params_st->num_nodes = 0U; params_st->capacity_nodes = capacity; return params_st; @@ -126,6 +137,15 @@ rcutils_ret_t rcl_yaml_node_struct_reallocate( return RCUTILS_RET_BAD_ALLOC; } params_st->params = params; + + void * descriptors = allocator.reallocate( + params_st->descriptors, new_capacity * sizeof(rcl_node_params_descriptors_t), allocator.state); + if (NULL == descriptors) { + RCUTILS_SET_ERROR_MSG("Failed to reallocate memory for parameter descriptors"); + return RCUTILS_RET_BAD_ALLOC; + } + params_st->descriptors = descriptors; + // zero initialization for the added memory if (new_capacity > params_st->capacity_nodes) { memset( @@ -192,6 +212,38 @@ rcl_params_t * rcl_yaml_node_struct_copy( goto fail; } } + + rcl_node_params_descriptors_t * node_params_descriptors_st = + &(params_st->descriptors[node_idx]); + rcl_node_params_descriptors_t * out_node_params_descriptors_st = + &(out_params_st->descriptors[node_idx]); + ret = node_params_descriptors_init_with_capacity( + out_node_params_descriptors_st, + node_params_descriptors_st->capacity_descriptors, + allocator); + if (RCUTILS_RET_OK != ret) { + if (RCUTILS_RET_BAD_ALLOC == ret) { + RCUTILS_SAFE_FWRITE_TO_STDERR("Error allocating mem\n"); + } + goto fail; + } + for (size_t desc_idx = 0U; desc_idx < node_params_descriptors_st->num_descriptors; ++desc_idx) { + out_node_params_descriptors_st->parameter_names[desc_idx] = + rcutils_strdup(node_params_descriptors_st->parameter_names[desc_idx], allocator); + if (NULL == out_node_params_descriptors_st->parameter_names[desc_idx]) { + RCUTILS_SAFE_FWRITE_TO_STDERR("Error allocating mem\n"); + goto fail; + } + out_node_params_descriptors_st->num_descriptors++; + + const rcl_param_descriptor_t * param_descriptor = + &(node_params_descriptors_st->parameter_descriptors[desc_idx]); + rcl_param_descriptor_t * out_param_descriptor = + &(out_node_params_descriptors_st->parameter_descriptors[desc_idx]); + if (!rcl_yaml_descriptor_copy(out_param_descriptor, param_descriptor, allocator)) { + goto fail; + } + } } return out_params_st; @@ -234,6 +286,15 @@ void rcl_yaml_node_struct_fini( params_st->params = NULL; } // if (params) + if (NULL != params_st->descriptors) { + for (size_t node_idx = 0U; node_idx < params_st->num_nodes; node_idx++) { + rcl_yaml_node_params_descriptors_fini(&(params_st->descriptors[node_idx]), allocator); + } // for (node_idx) + + allocator.deallocate(params_st->descriptors, allocator.state); + params_st->descriptors = NULL; + } // if (descriptors) + params_st->num_nodes = 0U; params_st->capacity_nodes = 0U; allocator.deallocate(params_st, allocator.state); @@ -288,7 +349,9 @@ bool rcl_parse_yaml_file( if (NULL != ns_tracker.parameter_ns) { allocator.deallocate(ns_tracker.parameter_ns, allocator.state); } - + if (NULL != ns_tracker.descriptor_key_ns) { + allocator.deallocate(ns_tracker.descriptor_key_ns, allocator.state); + } return RCUTILS_RET_OK == ret; } @@ -366,6 +429,29 @@ rcl_variant_t * rcl_yaml_node_struct_get( return param_value; } +rcl_param_descriptor_t * rcl_yaml_node_struct_get_descriptor( + const char * node_name, + const char * param_name, + rcl_params_t * params_st) +{ + RCUTILS_CHECK_ARGUMENT_FOR_NULL(node_name, NULL); + RCUTILS_CHECK_ARGUMENT_FOR_NULL(param_name, NULL); + RCUTILS_CHECK_ARGUMENT_FOR_NULL(params_st, NULL); + + rcl_param_descriptor_t * param_descriptor = NULL; + + size_t node_idx = 0U; + rcutils_ret_t ret = find_node(node_name, params_st, &node_idx); + if (RCUTILS_RET_OK == ret) { + size_t parameter_idx = 0U; + ret = find_descriptor(node_idx, param_name, params_st, ¶meter_idx); + if (RCUTILS_RET_OK == ret) { + param_descriptor = &(params_st->descriptors[node_idx].parameter_descriptors[parameter_idx]); + } + } + return param_descriptor; +} + /// /// Dump the param structure /// @@ -446,4 +532,73 @@ void rcl_yaml_node_struct_print( } } } + printf("\n Node Name\t\t\t\tParameter Descriptors\n"); + for (size_t node_idx = 0U; node_idx < params_st->num_nodes; node_idx++) { + int32_t param_col = 50; + const char * const node_name = params_st->node_names[node_idx]; + if (NULL != node_name) { + printf("%s\n", node_name); + } + + if (NULL != params_st->descriptors) { + rcl_node_params_descriptors_t * node_descriptors_st = &(params_st->descriptors[node_idx]); + for (size_t parameter_idx = 0U; parameter_idx < node_descriptors_st->num_descriptors; + parameter_idx++) + { + if (NULL != node_descriptors_st->parameter_names) { + char * param_name = node_descriptors_st->parameter_names[parameter_idx]; + if (NULL != param_name) { + printf("%*s:", param_col, param_name); + } + rcl_param_descriptor_t * descriptor = + &(node_descriptors_st->parameter_descriptors[parameter_idx]); + if (NULL != descriptor->description) { + printf("\n%*sdescription: ", param_col + 2, ""); + printf("%s", descriptor->description); + } + if (NULL != descriptor->additional_constraints) { + printf("\n%*sadditional_constraints: ", param_col + 2, ""); + printf("%s", descriptor->additional_constraints); + } + if (NULL != descriptor->type) { + printf("\n%*stype: ", param_col + 2, ""); + printf("%d", *(descriptor->type)); + } + if (NULL != descriptor->min_value_int) { + printf("\n%*smin_value: ", param_col + 2, ""); + printf("%ld", *(descriptor->min_value_int)); + } + if (NULL != descriptor->min_value_double) { + printf("\n%*smin_value: ", param_col + 2, ""); + printf("%lf", *(descriptor->min_value_double)); + } + if (NULL != descriptor->max_value_int) { + printf("\n%*smax_value: ", param_col + 2, ""); + printf("%ld", *(descriptor->max_value_int)); + } + if (NULL != descriptor->max_value_double) { + printf("\n%*smax_value: ", param_col + 2, ""); + printf("%lf", *(descriptor->max_value_double)); + } + if (NULL != descriptor->step_int) { + printf("\n%*sstep: ", param_col + 2, ""); + printf("%ld", *(descriptor->step_int)); + } + if (NULL != descriptor->step_double) { + printf("\n%*sstep: ", param_col + 2, ""); + printf("%lf", *(descriptor->step_double)); + } + if (NULL != descriptor->read_only) { + printf("\n%*sread_only: ", param_col + 2, ""); + printf("%s", *(descriptor->read_only) ? "true" : "false"); + } + if (NULL != descriptor->dynamic_typing) { + printf("\n%*sdynamic_typing: ", param_col + 2, ""); + printf("%s", *(descriptor->dynamic_typing) ? "true" : "false"); + } + printf("\n"); + } + } + } + } } diff --git a/rcl_yaml_param_parser/src/yaml_descriptor.c b/rcl_yaml_param_parser/src/yaml_descriptor.c new file mode 100644 index 000000000..c0b95bc8e --- /dev/null +++ b/rcl_yaml_param_parser/src/yaml_descriptor.c @@ -0,0 +1,177 @@ +// Copyright 2018 Apex.AI, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "rcutils/allocator.h" +#include "rcutils/strdup.h" +#include "rcutils/types.h" + +#include "./impl/types.h" +#include "./impl/yaml_descriptor.h" +#include "rcl_yaml_param_parser/types.h" + +void rcl_yaml_descriptor_fini( + rcl_param_descriptor_t * param_descriptor, + const rcutils_allocator_t allocator) +{ + if (NULL == param_descriptor) { + return; + } + + if (NULL != param_descriptor->read_only) { + allocator.deallocate(param_descriptor->read_only, allocator.state); + param_descriptor->read_only = NULL; + } + if (NULL != param_descriptor->dynamic_typing) { + allocator.deallocate(param_descriptor->dynamic_typing, allocator.state); + param_descriptor->dynamic_typing = NULL; + } + if (NULL != param_descriptor->type) { + allocator.deallocate(param_descriptor->type, allocator.state); + param_descriptor->type = NULL; + } + if (NULL != param_descriptor->description) { + allocator.deallocate(param_descriptor->description, allocator.state); + param_descriptor->description = NULL; + } + if (NULL != param_descriptor->additional_constraints) { + allocator.deallocate(param_descriptor->additional_constraints, allocator.state); + param_descriptor->additional_constraints = NULL; + } + if (NULL != param_descriptor->min_value_double) { + allocator.deallocate(param_descriptor->min_value_double, allocator.state); + param_descriptor->min_value_double = NULL; + } + if (NULL != param_descriptor->max_value_double) { + allocator.deallocate(param_descriptor->max_value_double, allocator.state); + param_descriptor->max_value_double = NULL; + } + if (NULL != param_descriptor->step_double) { + allocator.deallocate(param_descriptor->step_double, allocator.state); + param_descriptor->step_double = NULL; + } + if (NULL != param_descriptor->min_value_int) { + allocator.deallocate(param_descriptor->min_value_int, allocator.state); + param_descriptor->min_value_int = NULL; + } + if (NULL != param_descriptor->max_value_int) { + allocator.deallocate(param_descriptor->max_value_int, allocator.state); + param_descriptor->max_value_int = NULL; + } + if (NULL != param_descriptor->step_int) { + allocator.deallocate(param_descriptor->step_int, allocator.state); + param_descriptor->step_int = NULL; + } +} + +bool rcl_yaml_descriptor_copy( + rcl_param_descriptor_t * out_param_descriptor, + const rcl_param_descriptor_t * param_descriptor, + rcutils_allocator_t allocator) +{ + if (NULL == param_descriptor || NULL == out_param_descriptor) { + return false; + } + + if (NULL != param_descriptor->description) { + out_param_descriptor->description = + rcutils_strdup(param_descriptor->description, allocator); + if (NULL == out_param_descriptor->description) { + RCUTILS_SAFE_FWRITE_TO_STDERR("Error allocating mem"); + return false; + } + } + if (NULL != param_descriptor->additional_constraints) { + out_param_descriptor->additional_constraints = + rcutils_strdup(param_descriptor->additional_constraints, allocator); + if (NULL == out_param_descriptor->additional_constraints) { + RCUTILS_SAFE_FWRITE_TO_STDERR("Error allocating mem"); + return false; + } + } + if (NULL != param_descriptor->type) { + out_param_descriptor->type = allocator.allocate(sizeof(uint8_t), allocator.state); + if (NULL == out_param_descriptor->type) { + RCUTILS_SAFE_FWRITE_TO_STDERR("Error allocating mem"); + return false; + } + *(out_param_descriptor->type) = *(param_descriptor->type); + } + if (NULL != param_descriptor->min_value_int) { + out_param_descriptor->min_value_int = allocator.allocate(sizeof(int64_t), allocator.state); + if (NULL == out_param_descriptor->min_value_int) { + RCUTILS_SAFE_FWRITE_TO_STDERR("Error allocating mem"); + return false; + } + *(out_param_descriptor->min_value_int) = *(param_descriptor->min_value_int); + } + if (NULL != param_descriptor->min_value_double) { + out_param_descriptor->min_value_double = + allocator.allocate(sizeof(double), allocator.state); + if (NULL == out_param_descriptor->min_value_double) { + RCUTILS_SAFE_FWRITE_TO_STDERR("Error allocating mem"); + return false; + } + *(out_param_descriptor->min_value_double) = *(param_descriptor->min_value_double); + } + if (NULL != param_descriptor->max_value_int) { + out_param_descriptor->max_value_int = allocator.allocate(sizeof(int64_t), allocator.state); + if (NULL == out_param_descriptor->max_value_int) { + RCUTILS_SAFE_FWRITE_TO_STDERR("Error allocating mem"); + return false; + } + *(out_param_descriptor->max_value_int) = *(param_descriptor->max_value_int); + } + if (NULL != param_descriptor->max_value_double) { + out_param_descriptor->max_value_double = + allocator.allocate(sizeof(double), allocator.state); + if (NULL == out_param_descriptor->max_value_double) { + RCUTILS_SAFE_FWRITE_TO_STDERR("Error allocating mem"); + return false; + } + *(out_param_descriptor->max_value_double) = *(param_descriptor->max_value_double); + } + if (NULL != param_descriptor->step_int) { + out_param_descriptor->step_int = allocator.allocate(sizeof(int64_t), allocator.state); + if (NULL == out_param_descriptor->step_int) { + RCUTILS_SAFE_FWRITE_TO_STDERR("Error allocating mem"); + return false; + } + *(out_param_descriptor->step_int) = *(param_descriptor->step_int); + } + if (NULL != param_descriptor->step_double) { + out_param_descriptor->step_double = allocator.allocate(sizeof(double), allocator.state); + if (NULL == out_param_descriptor->step_double) { + RCUTILS_SAFE_FWRITE_TO_STDERR("Error allocating mem"); + return false; + } + *(out_param_descriptor->step_double) = *(param_descriptor->step_double); + } + if (NULL != param_descriptor->read_only) { + out_param_descriptor->read_only = allocator.allocate(sizeof(bool), allocator.state); + if (NULL == out_param_descriptor->read_only) { + RCUTILS_SAFE_FWRITE_TO_STDERR("Error allocating mem"); + return false; + } + *(out_param_descriptor->read_only) = *(param_descriptor->read_only); + } + if (NULL != param_descriptor->dynamic_typing) { + out_param_descriptor->dynamic_typing = allocator.allocate(sizeof(bool), allocator.state); + if (NULL == out_param_descriptor->dynamic_typing) { + RCUTILS_SAFE_FWRITE_TO_STDERR("Error allocating mem"); + return false; + } + *(out_param_descriptor->dynamic_typing) = *(param_descriptor->dynamic_typing); + } + return true; +} diff --git a/rcl_yaml_param_parser/test/assign_param_in_descriptors.yaml b/rcl_yaml_param_parser/test/assign_param_in_descriptors.yaml new file mode 100644 index 000000000..73b7a461b --- /dev/null +++ b/rcl_yaml_param_parser/test/assign_param_in_descriptors.yaml @@ -0,0 +1,5 @@ +node1: + ros__parameter_descriptors: + bar: 42 + foo: + read_only: true diff --git a/rcl_yaml_param_parser/test/correct_param_descriptors.yaml b/rcl_yaml_param_parser/test/correct_param_descriptors.yaml new file mode 100644 index 000000000..819eab6da --- /dev/null +++ b/rcl_yaml_param_parser/test/correct_param_descriptors.yaml @@ -0,0 +1,33 @@ +# config/test_yaml +--- + +node_ns: + node1: + ros__parameter_descriptors: + param1: + type: 2 + min_value: 5 + max_value: 100 + step: 5 + read_only: false + description: "int parameter" + additional_constraints: "only multiples of 5" + param2: + min_value: 1.0 + max_value: 10.0 + description: "double parameter" + ros__parameters: + param1: 28 + node2: + ros__parameters: + foo: + bar: 10 + baz: "hello" + ros__parameter_descriptors: + foo: + bar: + description: "namespaced parameter" + read_only: false + baz: + description: "another namespaced parameter" + dynamic_typing: true diff --git a/rcl_yaml_param_parser/test/invalid_param_descriptor_key.yaml b/rcl_yaml_param_parser/test/invalid_param_descriptor_key.yaml new file mode 100644 index 000000000..a5f7d6b38 --- /dev/null +++ b/rcl_yaml_param_parser/test/invalid_param_descriptor_key.yaml @@ -0,0 +1,5 @@ +node1: + ros__parameter_descriptors: + foo: + read_only: true + invalid_description: "This is an invalid descriptor key" diff --git a/rcl_yaml_param_parser/test/invalid_param_descriptor_type.yaml b/rcl_yaml_param_parser/test/invalid_param_descriptor_type.yaml new file mode 100644 index 000000000..01c92c263 --- /dev/null +++ b/rcl_yaml_param_parser/test/invalid_param_descriptor_type.yaml @@ -0,0 +1,4 @@ +node1: + ros__parameter_descriptors: + foo: + min_value: "5" diff --git a/rcl_yaml_param_parser/test/no_value_descriptor.yaml b/rcl_yaml_param_parser/test/no_value_descriptor.yaml new file mode 100644 index 000000000..d78c3fb52 --- /dev/null +++ b/rcl_yaml_param_parser/test/no_value_descriptor.yaml @@ -0,0 +1,4 @@ +node1: + ros__parameter_descriptors: + foo: + description: diff --git a/rcl_yaml_param_parser/test/overlay_descriptors.yaml b/rcl_yaml_param_parser/test/overlay_descriptors.yaml new file mode 100644 index 000000000..953819d19 --- /dev/null +++ b/rcl_yaml_param_parser/test/overlay_descriptors.yaml @@ -0,0 +1,14 @@ +node_ns: + node1: + ros__parameter_descriptors: + param1: + max_value: 200 + param2: + min_value: -2.0 + node2: + ros__parameter_descriptors: + foo: + bar: + read_only: true + baz: + description: "other namespaced parameter" diff --git a/rcl_yaml_param_parser/test/test_node_params_descriptors.cpp b/rcl_yaml_param_parser/test/test_node_params_descriptors.cpp new file mode 100644 index 000000000..1d9a20d02 --- /dev/null +++ b/rcl_yaml_param_parser/test/test_node_params_descriptors.cpp @@ -0,0 +1,84 @@ +// Copyright 2020 Open Source Robotics Foundation, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include "../src/impl/node_params_descriptors.h" +#include "rcutils/allocator.h" + +TEST(TestNodeParamsDescriptors, init_fini) { + rcutils_allocator_t allocator = rcutils_get_default_allocator(); + rcl_node_params_descriptors_t node_descriptors = {NULL, NULL, 0u, 128u}; + EXPECT_EQ(RCUTILS_RET_OK, node_params_descriptors_init(&node_descriptors, allocator)); + EXPECT_NE(nullptr, node_descriptors.parameter_names); + EXPECT_NE(nullptr, node_descriptors.parameter_descriptors); + EXPECT_EQ(0u, node_descriptors.num_descriptors); + EXPECT_EQ(128u, node_descriptors.capacity_descriptors); + rcl_yaml_node_params_descriptors_fini(&node_descriptors, allocator); + EXPECT_EQ(nullptr, node_descriptors.parameter_names); + EXPECT_EQ(nullptr, node_descriptors.parameter_descriptors); + EXPECT_EQ(0u, node_descriptors.num_descriptors); + EXPECT_EQ(0u, node_descriptors.capacity_descriptors); + + // This function doesn't return anything, so just check it doesn't segfault on the second try + rcl_yaml_node_params_descriptors_fini(&node_descriptors, allocator); + rcl_yaml_node_params_descriptors_fini(nullptr, allocator); +} + +TEST(TestNodeParamsDescriptors, init_with_capacity_fini) { + rcutils_allocator_t allocator = rcutils_get_default_allocator(); + rcl_node_params_descriptors_t node_descriptors = {NULL, NULL, 0u, 1024u}; + EXPECT_EQ( + RCUTILS_RET_OK, node_params_descriptors_init_with_capacity( + &node_descriptors, 1024, allocator)); + EXPECT_NE(nullptr, node_descriptors.parameter_names); + EXPECT_NE(nullptr, node_descriptors.parameter_descriptors); + EXPECT_EQ(0u, node_descriptors.num_descriptors); + EXPECT_EQ(1024u, node_descriptors.capacity_descriptors); + rcl_yaml_node_params_descriptors_fini(&node_descriptors, allocator); + EXPECT_EQ(nullptr, node_descriptors.parameter_names); + EXPECT_EQ(nullptr, node_descriptors.parameter_descriptors); + EXPECT_EQ(0u, node_descriptors.num_descriptors); + EXPECT_EQ(0u, node_descriptors.capacity_descriptors); + + // This function doesn't return anything, so just check it doesn't segfault on the second try + rcl_yaml_node_params_descriptors_fini(&node_descriptors, allocator); + rcl_yaml_node_params_descriptors_fini(nullptr, allocator); +} + +TEST(TestNodeParamsDescriptors, reallocate_with_capacity_fini) { + rcutils_allocator_t allocator = rcutils_get_default_allocator(); + rcl_node_params_descriptors_t node_descriptors = {NULL, NULL, 0u, 1024u}; + EXPECT_EQ( + RCUTILS_RET_OK, node_params_descriptors_init_with_capacity( + &node_descriptors, 1024, allocator)); + EXPECT_NE(nullptr, node_descriptors.parameter_names); + EXPECT_NE(nullptr, node_descriptors.parameter_descriptors); + EXPECT_EQ(0u, node_descriptors.num_descriptors); + EXPECT_EQ(1024u, node_descriptors.capacity_descriptors); + EXPECT_EQ(RCUTILS_RET_OK, node_params_descriptors_reallocate(&node_descriptors, 2048, allocator)); + EXPECT_NE(nullptr, node_descriptors.parameter_names); + EXPECT_NE(nullptr, node_descriptors.parameter_descriptors); + EXPECT_EQ(0u, node_descriptors.num_descriptors); + EXPECT_EQ(2048u, node_descriptors.capacity_descriptors); + rcl_yaml_node_params_descriptors_fini(&node_descriptors, allocator); + EXPECT_EQ(nullptr, node_descriptors.parameter_names); + EXPECT_EQ(nullptr, node_descriptors.parameter_descriptors); + EXPECT_EQ(0u, node_descriptors.num_descriptors); + EXPECT_EQ(0u, node_descriptors.capacity_descriptors); + + // This function doesn't return anything, so just check it doesn't segfault on the second try + rcl_yaml_node_params_descriptors_fini(&node_descriptors, allocator); + rcl_yaml_node_params_descriptors_fini(nullptr, allocator); +} diff --git a/rcl_yaml_param_parser/test/test_parse.cpp b/rcl_yaml_param_parser/test/test_parse.cpp index e53849cc0..6be3d99e1 100644 --- a/rcl_yaml_param_parser/test/test_parse.cpp +++ b/rcl_yaml_param_parser/test/test_parse.cpp @@ -23,7 +23,10 @@ #include "rcl_yaml_param_parser/parser.h" #include "../src/impl/parse.h" #include "../src/impl/node_params.h" +#include "../src/impl/node_params_descriptors.h" +#include "../src/impl/types.h" #include "rcutils/filesystem.h" +#include "rcutils/strdup.h" #include "./mocking_utils/patch.hpp" @@ -97,7 +100,7 @@ TEST(TestParse, parse_value) { params_st->params[node_idx].parameter_values[parameter_idx].double_value, allocator.state); params_st->params[node_idx].parameter_values[parameter_idx].double_value = nullptr; - // double value + // string value yaml_char_t string_value[] = "hello, I am a string"; const size_t string_value_length = sizeof(string_value) / sizeof(string_value[0]); event.data.scalar.value = string_value; @@ -116,6 +119,536 @@ TEST(TestParse, parse_value) { params_st->params[node_idx].parameter_values[parameter_idx].string_value = nullptr; } +TEST(TestParse, parse_descriptor) { + rcutils_allocator_t allocator = rcutils_get_default_allocator(); + yaml_event_t event; + event.type = YAML_NO_EVENT; + event.start_mark = {0u, 0u, 0u}; + event.end_mark = {0u, 0u, 0u}; + event.data.scalar = {NULL, NULL, NULL, 1u, 0, 0, YAML_ANY_SCALAR_STYLE}; + + namespace_tracker_t ns_tracker; + ns_tracker.node_ns = nullptr; + ns_tracker.parameter_ns = nullptr; + ns_tracker.descriptor_key_ns = nullptr; + ns_tracker.num_node_ns = 0; + ns_tracker.num_parameter_ns = 0; + ns_tracker.num_descriptor_key_ns = 0; + + bool is_seq = false; + size_t node_idx = 0u; + size_t parameter_idx = 0u; + rcl_params_t * params_st = rcl_yaml_node_struct_init(allocator); + ASSERT_NE(nullptr, params_st); + OSRF_TESTING_TOOLS_CPP_SCOPE_EXIT( + { + rcl_yaml_node_struct_fini(params_st); + }); + + ASSERT_EQ(RCUTILS_RET_OK, node_params_descriptors_init(¶ms_st->descriptors[0], allocator)); + params_st->num_nodes = 1u; + + ns_tracker.parameter_ns = rcutils_strdup("param", allocator); + ASSERT_STREQ("param", ns_tracker.parameter_ns); + OSRF_TESTING_TOOLS_CPP_SCOPE_EXIT( + { + allocator.deallocate(ns_tracker.parameter_ns, allocator.state); + }); + + // Set up descriptor test case data + yaml_char_t bool_value[] = "true"; + yaml_char_t int_value[] = "28"; + yaml_char_t double_value[] = "1.23456"; + yaml_char_t string_value[] = "I am a string"; + + const size_t bool_value_length = sizeof(bool_value) / sizeof(bool_value[0]); + const size_t int_value_length = sizeof(int_value) / sizeof(int_value[0]); + const size_t double_value_length = sizeof(double_value) / sizeof(double_value[0]); + const size_t string_value_length = sizeof(string_value) / sizeof(string_value[0]); + + // read_only + ns_tracker.descriptor_key_ns = rcutils_strdup("read_only", allocator); + ASSERT_STREQ("read_only", ns_tracker.descriptor_key_ns); + event.data.scalar.value = bool_value; + event.data.scalar.length = bool_value_length; + EXPECT_EQ( + RCUTILS_RET_OK, + parse_descriptor(&ns_tracker, event, is_seq, node_idx, parameter_idx, params_st)) << + rcutils_get_error_string().str; + ASSERT_NE( + nullptr, + params_st->descriptors[node_idx].parameter_descriptors[parameter_idx].read_only); + EXPECT_TRUE(*params_st->descriptors[node_idx].parameter_descriptors[parameter_idx].read_only); + allocator.deallocate( + params_st->descriptors[node_idx].parameter_descriptors[parameter_idx].read_only, + allocator.state); + allocator.deallocate(ns_tracker.descriptor_key_ns, allocator.state); + params_st->descriptors[node_idx].parameter_descriptors[parameter_idx].read_only = nullptr; + + // dynamic_typing + ns_tracker.descriptor_key_ns = rcutils_strdup("dynamic_typing", allocator); + ASSERT_STREQ("dynamic_typing", ns_tracker.descriptor_key_ns); + event.data.scalar.value = bool_value; + event.data.scalar.length = bool_value_length; + EXPECT_EQ( + RCUTILS_RET_OK, + parse_descriptor(&ns_tracker, event, is_seq, node_idx, parameter_idx, params_st)) << + rcutils_get_error_string().str; + ASSERT_NE( + nullptr, + params_st->descriptors[node_idx].parameter_descriptors[parameter_idx].dynamic_typing); + EXPECT_TRUE( + *params_st->descriptors[node_idx].parameter_descriptors[parameter_idx].dynamic_typing); + allocator.deallocate( + params_st->descriptors[node_idx].parameter_descriptors[parameter_idx].dynamic_typing, + allocator.state); + allocator.deallocate(ns_tracker.descriptor_key_ns, allocator.state); + params_st->descriptors[node_idx].parameter_descriptors[parameter_idx].dynamic_typing = nullptr; + + // min_value (int) + ns_tracker.descriptor_key_ns = rcutils_strdup("min_value", allocator); + ASSERT_STREQ("min_value", ns_tracker.descriptor_key_ns); + event.data.scalar.value = int_value; + event.data.scalar.length = int_value_length; + EXPECT_EQ( + RCUTILS_RET_OK, + parse_descriptor(&ns_tracker, event, is_seq, node_idx, parameter_idx, params_st)) << + rcutils_get_error_string().str; + ASSERT_NE( + nullptr, + params_st->descriptors[node_idx].parameter_descriptors[parameter_idx].min_value_int); + EXPECT_EQ( + 28, + *params_st->descriptors[node_idx].parameter_descriptors[parameter_idx].min_value_int); + allocator.deallocate( + params_st->descriptors[node_idx].parameter_descriptors[parameter_idx].min_value_int, + allocator.state); + params_st->descriptors[node_idx].parameter_descriptors[parameter_idx].min_value_int = nullptr; + + // min_value (double) + event.data.scalar.value = double_value; + event.data.scalar.length = double_value_length; + EXPECT_EQ( + RCUTILS_RET_OK, + parse_descriptor(&ns_tracker, event, is_seq, node_idx, parameter_idx, params_st)) << + rcutils_get_error_string().str; + ASSERT_NE( + nullptr, + params_st->descriptors[node_idx].parameter_descriptors[parameter_idx].min_value_double); + EXPECT_EQ( + 1.23456, + *params_st->descriptors[node_idx].parameter_descriptors[parameter_idx].min_value_double); + allocator.deallocate( + params_st->descriptors[node_idx].parameter_descriptors[parameter_idx].min_value_double, + allocator.state); + allocator.deallocate(ns_tracker.descriptor_key_ns, allocator.state); + params_st->descriptors[node_idx].parameter_descriptors[parameter_idx].min_value_double = nullptr; + + // max_value (int) + ns_tracker.descriptor_key_ns = rcutils_strdup("max_value", allocator); + ASSERT_STREQ("max_value", ns_tracker.descriptor_key_ns); + event.data.scalar.value = int_value; + event.data.scalar.length = int_value_length; + EXPECT_EQ( + RCUTILS_RET_OK, + parse_descriptor(&ns_tracker, event, is_seq, node_idx, parameter_idx, params_st)) << + rcutils_get_error_string().str; + ASSERT_NE( + nullptr, + params_st->descriptors[node_idx].parameter_descriptors[parameter_idx].max_value_int); + EXPECT_EQ( + 28, + *params_st->descriptors[node_idx].parameter_descriptors[parameter_idx].max_value_int); + allocator.deallocate( + params_st->descriptors[node_idx].parameter_descriptors[parameter_idx].max_value_int, + allocator.state); + params_st->descriptors[node_idx].parameter_descriptors[parameter_idx].max_value_int = nullptr; + + // max_value (double) + event.data.scalar.value = double_value; + event.data.scalar.length = double_value_length; + EXPECT_EQ( + RCUTILS_RET_OK, + parse_descriptor(&ns_tracker, event, is_seq, node_idx, parameter_idx, params_st)) << + rcutils_get_error_string().str; + ASSERT_NE( + nullptr, + params_st->descriptors[node_idx].parameter_descriptors[parameter_idx].max_value_double); + EXPECT_EQ( + 1.23456, + *params_st->descriptors[node_idx].parameter_descriptors[parameter_idx].max_value_double); + allocator.deallocate( + params_st->descriptors[node_idx].parameter_descriptors[parameter_idx].max_value_double, + allocator.state); + allocator.deallocate(ns_tracker.descriptor_key_ns, allocator.state); + params_st->descriptors[node_idx].parameter_descriptors[parameter_idx].max_value_double = nullptr; + + // step (int) + ns_tracker.descriptor_key_ns = rcutils_strdup("step", allocator); + ASSERT_STREQ("step", ns_tracker.descriptor_key_ns); + event.data.scalar.value = int_value; + event.data.scalar.length = int_value_length; + EXPECT_EQ( + RCUTILS_RET_OK, + parse_descriptor(&ns_tracker, event, is_seq, node_idx, parameter_idx, params_st)) << + rcutils_get_error_string().str; + ASSERT_NE( + nullptr, + params_st->descriptors[node_idx].parameter_descriptors[parameter_idx].step_int); + EXPECT_EQ(28, *params_st->descriptors[node_idx].parameter_descriptors[parameter_idx].step_int); + allocator.deallocate( + params_st->descriptors[node_idx].parameter_descriptors[parameter_idx].step_int, + allocator.state); + params_st->descriptors[node_idx].parameter_descriptors[parameter_idx].step_int = nullptr; + + // step (double) + event.data.scalar.value = double_value; + event.data.scalar.length = double_value_length; + EXPECT_EQ( + RCUTILS_RET_OK, + parse_descriptor(&ns_tracker, event, is_seq, node_idx, parameter_idx, params_st)) << + rcutils_get_error_string().str; + ASSERT_NE( + nullptr, + params_st->descriptors[node_idx].parameter_descriptors[parameter_idx].step_double); + EXPECT_EQ( + 1.23456, + *params_st->descriptors[node_idx].parameter_descriptors[parameter_idx].step_double); + allocator.deallocate( + params_st->descriptors[node_idx].parameter_descriptors[parameter_idx].step_double, + allocator.state); + allocator.deallocate(ns_tracker.descriptor_key_ns, allocator.state); + params_st->descriptors[node_idx].parameter_descriptors[parameter_idx].step_double = nullptr; + + // description + ns_tracker.descriptor_key_ns = rcutils_strdup("description", allocator); + ASSERT_STREQ("description", ns_tracker.descriptor_key_ns); + event.data.scalar.value = string_value; + event.data.scalar.length = string_value_length; + EXPECT_EQ( + RCUTILS_RET_OK, + parse_descriptor(&ns_tracker, event, is_seq, node_idx, parameter_idx, params_st)) << + rcutils_get_error_string().str; + ASSERT_NE( + nullptr, + params_st->descriptors[node_idx].parameter_descriptors[parameter_idx].description); + EXPECT_STREQ( + "I am a string", + params_st->descriptors[node_idx].parameter_descriptors[parameter_idx].description); + allocator.deallocate( + params_st->descriptors[node_idx].parameter_descriptors[parameter_idx].description, + allocator.state); + allocator.deallocate(ns_tracker.descriptor_key_ns, allocator.state); + params_st->descriptors[node_idx].parameter_descriptors[parameter_idx].description = nullptr; + + // additional_constraints + ns_tracker.descriptor_key_ns = rcutils_strdup("additional_constraints", allocator); + ASSERT_STREQ("additional_constraints", ns_tracker.descriptor_key_ns); + event.data.scalar.value = string_value; + event.data.scalar.length = string_value_length; + EXPECT_EQ( + RCUTILS_RET_OK, + parse_descriptor(&ns_tracker, event, is_seq, node_idx, parameter_idx, params_st)) << + rcutils_get_error_string().str; + ASSERT_NE( + nullptr, + params_st->descriptors[node_idx].parameter_descriptors[parameter_idx].additional_constraints); + EXPECT_STREQ( + "I am a string", + params_st->descriptors[node_idx].parameter_descriptors[parameter_idx].additional_constraints); + allocator.deallocate( + params_st->descriptors[node_idx].parameter_descriptors[parameter_idx].additional_constraints, + allocator.state); + allocator.deallocate(ns_tracker.descriptor_key_ns, allocator.state); + params_st->descriptors[node_idx].parameter_descriptors[parameter_idx].additional_constraints = + nullptr; +} + +TEST(TestParse, parse_descriptor_bad_args) { + rcutils_allocator_t allocator = rcutils_get_default_allocator(); + yaml_event_t event; + event.type = YAML_NO_EVENT; + event.start_mark = {0u, 0u, 0u}; + event.end_mark = {0u, 0u, 0u}; + event.data.scalar = {NULL, NULL, NULL, 1u, 0, 0, YAML_ANY_SCALAR_STYLE}; + + namespace_tracker_t ns_tracker; + ns_tracker.node_ns = nullptr; + ns_tracker.parameter_ns = nullptr; + ns_tracker.descriptor_key_ns = nullptr; + ns_tracker.num_node_ns = 0; + ns_tracker.num_parameter_ns = 0; + ns_tracker.num_descriptor_key_ns = 0; + + bool is_seq = false; + size_t node_idx = 0u; + size_t parameter_idx = 0u; + rcl_params_t * params_st = rcl_yaml_node_struct_init(allocator); + ASSERT_NE(nullptr, params_st); + OSRF_TESTING_TOOLS_CPP_SCOPE_EXIT( + { + rcl_yaml_node_struct_fini(params_st); + }); + + EXPECT_EQ( + RCUTILS_RET_INVALID_ARGUMENT, + parse_descriptor(&ns_tracker, event, is_seq, node_idx, parameter_idx, nullptr)); + EXPECT_TRUE(rcutils_error_is_set()); + rcutils_reset_error(); + + // No node to update + const size_t num_nodes = params_st->num_nodes; + params_st->num_nodes = 0u; + EXPECT_EQ( + RCUTILS_RET_INVALID_ARGUMENT, + parse_descriptor(&ns_tracker, event, is_seq, node_idx, parameter_idx, params_st)); + EXPECT_TRUE(rcutils_error_is_set()); + rcutils_reset_error(); + params_st->num_nodes = num_nodes; + + ASSERT_EQ(RCUTILS_RET_OK, node_params_descriptors_init(¶ms_st->descriptors[0], allocator)); + params_st->num_nodes = 1u; + + // event.data.scalar.value is NULL, but event.data.scalar.length > 0 + event.data.scalar.value = NULL; + EXPECT_EQ( + RCUTILS_RET_INVALID_ARGUMENT, + parse_descriptor(&ns_tracker, event, is_seq, node_idx, parameter_idx, params_st)); + EXPECT_TRUE(rcutils_error_is_set()); + rcutils_reset_error(); + + // event.data.scalar.length is 0 and style is not a QUOTED_SCALAR_STYLE + event.data.scalar.length = 0u; + yaml_char_t event_value[] = "non_empty_string"; + const size_t event_value_length = sizeof(event_value) / sizeof(event_value[0]); + event.data.scalar.value = event_value; + EXPECT_EQ( + RCUTILS_RET_ERROR, + parse_descriptor(&ns_tracker, event, is_seq, node_idx, parameter_idx, params_st)) << + rcutils_get_error_string().str; + EXPECT_TRUE(rcutils_error_is_set()); + rcutils_reset_error(); + + // parameter_descriptors is NULL + event.data.scalar.length = event_value_length; + rcl_param_descriptor_t * tmp_descriptor_array = params_st->descriptors[0].parameter_descriptors; + params_st->descriptors[0].parameter_descriptors = NULL; + EXPECT_EQ( + RCUTILS_RET_BAD_ALLOC, + parse_descriptor(&ns_tracker, event, is_seq, node_idx, parameter_idx, params_st)) << + rcutils_get_error_string().str; + EXPECT_TRUE(rcutils_error_is_set()); + rcutils_reset_error(); + params_st->descriptors[0].parameter_descriptors = tmp_descriptor_array; + + // is_seq is true + EXPECT_EQ( + RCUTILS_RET_ERROR, + parse_descriptor(&ns_tracker, event, true, node_idx, parameter_idx, params_st)); + EXPECT_TRUE(rcutils_error_is_set()); + rcutils_reset_error(); + + // namespace_tracker->parameter_ns is NULL + EXPECT_EQ( + RCUTILS_RET_ERROR, + parse_descriptor(&ns_tracker, event, is_seq, node_idx, parameter_idx, params_st)); + EXPECT_TRUE(rcutils_error_is_set()); + rcutils_reset_error(); + + // namespace_tracker->descriptor_key_ns is NULL + ns_tracker.parameter_ns = rcutils_strdup("param", allocator); + ASSERT_STREQ("param", ns_tracker.parameter_ns); + EXPECT_EQ( + RCUTILS_RET_ERROR, + parse_descriptor(&ns_tracker, event, is_seq, node_idx, parameter_idx, params_st)); + EXPECT_TRUE(rcutils_error_is_set()); + rcutils_reset_error(); + + // Invalid namespace_tracker->descriptor_key_ns + ns_tracker.descriptor_key_ns = rcutils_strdup("invalid_key", allocator); + ASSERT_STREQ("invalid_key", ns_tracker.descriptor_key_ns); + EXPECT_EQ( + RCUTILS_RET_ERROR, + parse_descriptor(&ns_tracker, event, is_seq, node_idx, parameter_idx, params_st)); + EXPECT_TRUE(rcutils_error_is_set()); + allocator.deallocate(ns_tracker.descriptor_key_ns, allocator.state); + rcutils_reset_error(); + + // Valid namespace_tracker->descriptor_key_ns + ns_tracker.descriptor_key_ns = rcutils_strdup("description", allocator); + ASSERT_STREQ("description", ns_tracker.descriptor_key_ns); + EXPECT_EQ( + RCUTILS_RET_OK, + parse_descriptor(&ns_tracker, event, is_seq, node_idx, parameter_idx, params_st)); + EXPECT_FALSE(rcutils_error_is_set()); + rcutils_reset_error(); +} + +TEST(TestParse, parse_descriptor_bad_types) { + rcutils_allocator_t allocator = rcutils_get_default_allocator(); + yaml_event_t event; + event.type = YAML_NO_EVENT; + event.start_mark = {0u, 0u, 0u}; + event.end_mark = {0u, 0u, 0u}; + event.data.scalar = {NULL, NULL, NULL, 1u, 0, 0, YAML_ANY_SCALAR_STYLE}; + + namespace_tracker_t ns_tracker; + ns_tracker.node_ns = nullptr; + ns_tracker.parameter_ns = nullptr; + ns_tracker.descriptor_key_ns = nullptr; + ns_tracker.num_node_ns = 0; + ns_tracker.num_parameter_ns = 0; + ns_tracker.num_descriptor_key_ns = 0; + + bool is_seq = false; + size_t node_idx = 0u; + size_t parameter_idx = 0u; + rcl_params_t * params_st = rcl_yaml_node_struct_init(allocator); + ASSERT_NE(nullptr, params_st); + OSRF_TESTING_TOOLS_CPP_SCOPE_EXIT( + { + rcl_yaml_node_struct_fini(params_st); + }); + + ASSERT_EQ(RCUTILS_RET_OK, node_params_descriptors_init(¶ms_st->descriptors[0], allocator)); + params_st->num_nodes = 1u; + + ns_tracker.parameter_ns = rcutils_strdup("param", allocator); + ASSERT_STREQ("param", ns_tracker.parameter_ns); + OSRF_TESTING_TOOLS_CPP_SCOPE_EXIT( + { + allocator.deallocate(ns_tracker.parameter_ns, allocator.state); + }); + + // Set up descriptor test case data + yaml_char_t bool_value[] = "true"; + yaml_char_t int_value[] = "28"; + yaml_char_t double_value[] = "1.23456"; + yaml_char_t string_value[] = "I am a string"; + + const size_t bool_value_length = sizeof(bool_value) / sizeof(bool_value[0]); + const size_t int_value_length = sizeof(int_value) / sizeof(int_value[0]); + const size_t double_value_length = sizeof(double_value) / sizeof(double_value[0]); + const size_t string_value_length = sizeof(string_value) / sizeof(string_value[0]); + + // read_only + ns_tracker.descriptor_key_ns = rcutils_strdup("read_only", allocator); + ASSERT_STREQ("read_only", ns_tracker.descriptor_key_ns); + // read_only: catch invalid value type + event.data.scalar.value = double_value; + event.data.scalar.length = double_value_length; + EXPECT_NE( + RCUTILS_RET_OK, + parse_descriptor(&ns_tracker, event, is_seq, node_idx, parameter_idx, params_st)) << + rcutils_get_error_string().str; + allocator.deallocate( + params_st->descriptors[node_idx].parameter_descriptors[parameter_idx].read_only, + allocator.state); + allocator.deallocate(ns_tracker.descriptor_key_ns, allocator.state); + params_st->descriptors[node_idx].parameter_descriptors[parameter_idx].read_only = nullptr; + rcutils_reset_error(); + + // dynamic_typing + ns_tracker.descriptor_key_ns = rcutils_strdup("dynamic_typing", allocator); + ASSERT_STREQ("dynamic_typing", ns_tracker.descriptor_key_ns); + // dynamic_typing: catch invalid value type + event.data.scalar.value = double_value; + event.data.scalar.length = double_value_length; + EXPECT_NE( + RCUTILS_RET_OK, + parse_descriptor(&ns_tracker, event, is_seq, node_idx, parameter_idx, params_st)) << + rcutils_get_error_string().str; + allocator.deallocate( + params_st->descriptors[node_idx].parameter_descriptors[parameter_idx].dynamic_typing, + allocator.state); + allocator.deallocate(ns_tracker.descriptor_key_ns, allocator.state); + params_st->descriptors[node_idx].parameter_descriptors[parameter_idx].dynamic_typing = nullptr; + rcutils_reset_error(); + + // min_value + ns_tracker.descriptor_key_ns = rcutils_strdup("min_value", allocator); + ASSERT_STREQ("min_value", ns_tracker.descriptor_key_ns); + // min_value: catch invalid value type + event.data.scalar.value = string_value; + event.data.scalar.length = string_value_length; + EXPECT_NE( + RCUTILS_RET_OK, + parse_descriptor(&ns_tracker, event, is_seq, node_idx, parameter_idx, params_st)) << + rcutils_get_error_string().str; + allocator.deallocate( + params_st->descriptors[node_idx].parameter_descriptors[parameter_idx].min_value_int, + allocator.state); + params_st->descriptors[node_idx].parameter_descriptors[parameter_idx].min_value_int = nullptr; + rcutils_reset_error(); + + // max_value + ns_tracker.descriptor_key_ns = rcutils_strdup("max_value", allocator); + ASSERT_STREQ("max_value", ns_tracker.descriptor_key_ns); + // max_value: catch invalid value type + event.data.scalar.value = bool_value; + event.data.scalar.length = bool_value_length; + EXPECT_NE( + RCUTILS_RET_OK, + parse_descriptor(&ns_tracker, event, is_seq, node_idx, parameter_idx, params_st)) << + rcutils_get_error_string().str; + allocator.deallocate( + params_st->descriptors[node_idx].parameter_descriptors[parameter_idx].max_value_int, + allocator.state); + params_st->descriptors[node_idx].parameter_descriptors[parameter_idx].max_value_int = nullptr; + rcutils_reset_error(); + + // step (int) + ns_tracker.descriptor_key_ns = rcutils_strdup("step", allocator); + ASSERT_STREQ("step", ns_tracker.descriptor_key_ns); + // step: catch invalid value type + event.data.scalar.value = bool_value; + event.data.scalar.length = bool_value_length; + EXPECT_NE( + RCUTILS_RET_OK, + parse_descriptor(&ns_tracker, event, is_seq, node_idx, parameter_idx, params_st)) << + rcutils_get_error_string().str; + allocator.deallocate( + params_st->descriptors[node_idx].parameter_descriptors[parameter_idx].step_int, + allocator.state); + params_st->descriptors[node_idx].parameter_descriptors[parameter_idx].step_int = nullptr; + rcutils_reset_error(); + + // description + ns_tracker.descriptor_key_ns = rcutils_strdup("description", allocator); + ASSERT_STREQ("description", ns_tracker.descriptor_key_ns); + // description: catch invalid value type + event.data.scalar.value = bool_value; + event.data.scalar.length = bool_value_length; + EXPECT_NE( + RCUTILS_RET_OK, + parse_descriptor(&ns_tracker, event, is_seq, node_idx, parameter_idx, params_st)) << + rcutils_get_error_string().str; + allocator.deallocate( + params_st->descriptors[node_idx].parameter_descriptors[parameter_idx].description, + allocator.state); + allocator.deallocate(ns_tracker.descriptor_key_ns, allocator.state); + params_st->descriptors[node_idx].parameter_descriptors[parameter_idx].description = nullptr; + rcutils_reset_error(); + + // additional_constraints + ns_tracker.descriptor_key_ns = rcutils_strdup("additional_constraints", allocator); + ASSERT_STREQ("additional_constraints", ns_tracker.descriptor_key_ns); + // additional_constraints: catch invalid value type + event.data.scalar.value = int_value; + event.data.scalar.length = int_value_length; + EXPECT_NE( + RCUTILS_RET_OK, + parse_descriptor(&ns_tracker, event, is_seq, node_idx, parameter_idx, params_st)) << + rcutils_get_error_string().str; + allocator.deallocate( + params_st->descriptors[node_idx].parameter_descriptors[parameter_idx].additional_constraints, + allocator.state); + allocator.deallocate(ns_tracker.descriptor_key_ns, allocator.state); + params_st->descriptors[node_idx].parameter_descriptors[parameter_idx].additional_constraints = + nullptr; + rcutils_reset_error(); +} + TEST(TestParse, parse_value_sequence) { rcutils_allocator_t allocator = rcutils_get_default_allocator(); yaml_event_t event; @@ -376,6 +909,7 @@ TEST(TestParse, parse_key_bad_args) }); ASSERT_EQ(RCUTILS_RET_OK, node_params_init(¶ms_st->params[0], allocator)); + ASSERT_EQ(RCUTILS_RET_OK, node_params_descriptors_init(¶ms_st->descriptors[0], allocator)); params_st->num_nodes = 1u; // map_level is nullptr @@ -451,8 +985,19 @@ TEST(TestParse, parse_key_bad_args) rcutils_get_error_string().str; EXPECT_TRUE(rcutils_error_is_set()); rcutils_reset_error(); + + // previous parameter names required for parameter namespace in descriptors level + map_level = MAP_PARAMS_DESCRIPTORS_LVL; + EXPECT_EQ( + RCUTILS_RET_ERROR, + parse_key( + event, &map_level, &is_new_map, &node_idx, ¶meter_idx, &ns_tracker, params_st)) << + rcutils_get_error_string().str; + EXPECT_TRUE(rcutils_error_is_set()); + rcutils_reset_error(); } + TEST(TestParse, parse_file_events_mock_yaml_parser_parse) { char cur_dir[1024]; rcutils_reset_error(); diff --git a/rcl_yaml_param_parser/test/test_parse_yaml.cpp b/rcl_yaml_param_parser/test/test_parse_yaml.cpp index 0c50eda91..cc4160b29 100644 --- a/rcl_yaml_param_parser/test/test_parse_yaml.cpp +++ b/rcl_yaml_param_parser/test/test_parse_yaml.cpp @@ -661,6 +661,203 @@ TEST(test_file_parser, wildcards_partial) { } } +TEST(test_file_parser, correct_syntax_descriptors) { + rcutils_reset_error(); + EXPECT_TRUE(rcutils_get_cwd(cur_dir, 1024)) << rcutils_get_error_string().str; + 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, "correct_param_descriptors.yaml", allocator); + ASSERT_TRUE(NULL != path) << rcutils_get_error_string().str; + OSRF_TESTING_TOOLS_CPP_SCOPE_EXIT( + { + allocator.deallocate(path, allocator.state); + }); + ASSERT_TRUE(rcutils_exists(path)) << "No test YAML file found at " << 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); + ASSERT_TRUE(res) << rcutils_get_error_string().str; + + char * another_path = rcutils_join_path(test_path, "overlay_descriptors.yaml", allocator); + ASSERT_TRUE(NULL != another_path) << rcutils_get_error_string().str; + OSRF_TESTING_TOOLS_CPP_SCOPE_EXIT( + { + allocator.deallocate(another_path, allocator.state); + }); + ASSERT_TRUE(rcutils_exists(another_path)) << "No test YAML file found at " << another_path; + res = rcl_parse_yaml_file(another_path, params_hdl); + ASSERT_TRUE(res) << rcutils_get_error_string().str; + + rcl_params_t * copy_of_params_hdl = rcl_yaml_node_struct_copy(params_hdl); + ASSERT_TRUE(NULL != copy_of_params_hdl) << rcutils_get_error_string().str; + OSRF_TESTING_TOOLS_CPP_SCOPE_EXIT( + { + rcl_yaml_node_struct_fini(copy_of_params_hdl); + }); + + rcl_params_t * params_hdl_set[] = {params_hdl, copy_of_params_hdl}; + for (rcl_params_t * params : params_hdl_set) { + rcl_param_descriptor_t * param_descriptor = NULL; + param_descriptor = rcl_yaml_node_struct_get_descriptor("node_ns/node1", "param1", params); + ASSERT_TRUE(NULL != param_descriptor); + ASSERT_TRUE(NULL != param_descriptor->description); + EXPECT_STREQ("int parameter", param_descriptor->description); + ASSERT_TRUE(NULL != param_descriptor->additional_constraints); + EXPECT_STREQ("only multiples of 5", param_descriptor->additional_constraints); + ASSERT_TRUE(NULL != param_descriptor->type); + EXPECT_EQ(2U, *param_descriptor->type); + ASSERT_TRUE(NULL != param_descriptor->min_value_int); + EXPECT_EQ(5, *param_descriptor->min_value_int); + ASSERT_TRUE(NULL != param_descriptor->max_value_int); + EXPECT_EQ(200, *param_descriptor->max_value_int); + ASSERT_TRUE(NULL != param_descriptor->step_int); + EXPECT_EQ(5, *param_descriptor->step_int); + ASSERT_TRUE(NULL != param_descriptor->read_only); + EXPECT_FALSE(*param_descriptor->read_only); + + param_descriptor = rcl_yaml_node_struct_get_descriptor("node_ns/node1", "param2", params); + ASSERT_TRUE(NULL != param_descriptor); + ASSERT_TRUE(NULL != param_descriptor->description); + EXPECT_STREQ("double parameter", param_descriptor->description); + ASSERT_TRUE(NULL != param_descriptor->min_value_double); + EXPECT_DOUBLE_EQ(-2.0, *param_descriptor->min_value_double); + ASSERT_TRUE(NULL != param_descriptor->max_value_double); + EXPECT_DOUBLE_EQ(10.0, *param_descriptor->max_value_double); + + param_descriptor = rcl_yaml_node_struct_get_descriptor("node_ns/node2", "foo.bar", params); + ASSERT_TRUE(NULL != param_descriptor); + ASSERT_TRUE(NULL != param_descriptor->description); + EXPECT_STREQ("namespaced parameter", param_descriptor->description); + ASSERT_TRUE(NULL != param_descriptor->read_only); + EXPECT_TRUE(*param_descriptor->read_only); + + param_descriptor = rcl_yaml_node_struct_get_descriptor("node_ns/node2", "foo.baz", params); + ASSERT_TRUE(NULL != param_descriptor); + ASSERT_TRUE(NULL != param_descriptor->description); + EXPECT_STREQ("other namespaced parameter", param_descriptor->description); + + rcl_variant_t * param_value = rcl_yaml_node_struct_get("node_ns/node1", "param1", params); + ASSERT_TRUE(NULL != param_value) << rcutils_get_error_string().str; + ASSERT_TRUE(NULL != param_value->integer_value); + EXPECT_EQ(28, *param_value->integer_value); + + param_value = rcl_yaml_node_struct_get("node_ns/node2", "foo.bar", params); + ASSERT_TRUE(NULL != param_value) << rcutils_get_error_string().str; + ASSERT_TRUE(NULL != param_value->integer_value); + EXPECT_EQ(10, *param_value->integer_value); + + param_value = rcl_yaml_node_struct_get("node_ns/node2", "foo.baz", params); + ASSERT_TRUE(NULL != param_value) << rcutils_get_error_string().str; + ASSERT_TRUE(NULL != param_value->string_value); + EXPECT_STREQ("hello", param_value->string_value); + ASSERT_TRUE(NULL != param_descriptor->dynamic_typing); + EXPECT_TRUE(*param_descriptor->dynamic_typing); + + rcl_yaml_node_struct_print(params); + } +} + +TEST(test_parser, param_assign_in_descriptors) { + rcutils_reset_error(); + EXPECT_TRUE(rcutils_get_cwd(cur_dir, 1024)) << rcutils_get_error_string().str; + 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, "assign_param_in_descriptors.yaml", allocator); + ASSERT_TRUE(NULL != path) << rcutils_get_error_string().str; + OSRF_TESTING_TOOLS_CPP_SCOPE_EXIT( + { + allocator.deallocate(path, allocator.state); + }); + ASSERT_TRUE(rcutils_exists(path)) << "No test YAML file found at " << path; + rcl_params_t * params_hdl = rcl_yaml_node_struct_init(allocator); + ASSERT_TRUE(NULL != params_hdl) << rcutils_get_error_string().str; + bool res = rcl_parse_yaml_file(path, params_hdl); + EXPECT_FALSE(res); +} + +TEST(test_parser, invalid_param_descriptor_key) { + rcutils_reset_error(); + EXPECT_TRUE(rcutils_get_cwd(cur_dir, 1024)) << rcutils_get_error_string().str; + 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, "invalid_param_descriptor_key.yaml", allocator); + ASSERT_TRUE(NULL != path) << rcutils_get_error_string().str; + OSRF_TESTING_TOOLS_CPP_SCOPE_EXIT( + { + allocator.deallocate(path, allocator.state); + }); + ASSERT_TRUE(rcutils_exists(path)) << "No test YAML file found at " << path; + rcl_params_t * params_hdl = rcl_yaml_node_struct_init(allocator); + ASSERT_TRUE(NULL != params_hdl) << rcutils_get_error_string().str; + bool res = rcl_parse_yaml_file(path, params_hdl); + EXPECT_FALSE(res); +} + +TEST(test_parser, invalid_param_descriptor_type) { + rcutils_reset_error(); + EXPECT_TRUE(rcutils_get_cwd(cur_dir, 1024)) << rcutils_get_error_string().str; + 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, "invalid_param_descriptor_type.yaml", allocator); + ASSERT_TRUE(NULL != path) << rcutils_get_error_string().str; + OSRF_TESTING_TOOLS_CPP_SCOPE_EXIT( + { + allocator.deallocate(path, allocator.state); + }); + ASSERT_TRUE(rcutils_exists(path)) << "No test YAML file found at " << path; + rcl_params_t * params_hdl = rcl_yaml_node_struct_init(allocator); + ASSERT_TRUE(NULL != params_hdl) << rcutils_get_error_string().str; + bool res = rcl_parse_yaml_file(path, params_hdl); + EXPECT_FALSE(res); +} + +TEST(test_parser, no_value_descriptor) { + rcutils_reset_error(); + EXPECT_TRUE(rcutils_get_cwd(cur_dir, 1024)) << rcutils_get_error_string().str; + 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, "no_value_descriptor.yaml", allocator); + ASSERT_TRUE(NULL != path) << rcutils_get_error_string().str; + OSRF_TESTING_TOOLS_CPP_SCOPE_EXIT( + { + allocator.deallocate(path, allocator.state); + }); + ASSERT_TRUE(rcutils_exists(path)) << "No test YAML file found at " << path; + rcl_params_t * params_hdl = rcl_yaml_node_struct_init(allocator); + ASSERT_TRUE(NULL != params_hdl) << rcutils_get_error_string().str; + 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/test_parser.cpp b/rcl_yaml_param_parser/test/test_parser.cpp index d627cc8fe..be8d13e29 100644 --- a/rcl_yaml_param_parser/test/test_parser.cpp +++ b/rcl_yaml_param_parser/test/test_parser.cpp @@ -117,7 +117,7 @@ TEST(RclYamlParamParser, node_copy) { set_time_bomb_allocator_calloc_count(params_st->allocator, 1); EXPECT_EQ(nullptr, rcl_yaml_node_struct_copy(params_st)); - constexpr int expected_num_calloc_calls = 5; + constexpr int expected_num_calloc_calls = 8; for (int i = 0; i < expected_num_calloc_calls; ++i) { // Check various locations for allocation failures set_time_bomb_allocator_calloc_count(params_st->allocator, i); diff --git a/rcl_yaml_param_parser/test/test_yaml_descriptor.cpp b/rcl_yaml_param_parser/test/test_yaml_descriptor.cpp new file mode 100644 index 000000000..2b35f006d --- /dev/null +++ b/rcl_yaml_param_parser/test/test_yaml_descriptor.cpp @@ -0,0 +1,111 @@ +// Copyright 2020 Open Source Robotics Foundation, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include "osrf_testing_tools_cpp/scope_exit.hpp" +#include "../src/impl/yaml_descriptor.h" +#include "rcutils/allocator.h" +#include "rcutils/strdup.h" + +#define TEST_DESCRIPTOR_COPY(field, tmp_field) \ + do { \ + SCOPED_TRACE("TEST_DESCRIPTOR_COPY " #field); \ + rcl_param_descriptor_t src_descriptor{}; \ + rcl_param_descriptor_t dest_descriptor{}; \ + rcutils_allocator_t allocator = rcutils_get_default_allocator(); \ + src_descriptor.field = &tmp_field; \ + EXPECT_TRUE(rcl_yaml_descriptor_copy(&dest_descriptor, &src_descriptor, allocator)); \ + ASSERT_NE(nullptr, dest_descriptor.field); \ + EXPECT_EQ(*src_descriptor.field, *dest_descriptor.field); \ + rcl_yaml_descriptor_fini(&dest_descriptor, allocator); \ + src_descriptor.field = nullptr; \ + } while (0) + +TEST(TestYamlDescriptor, copy_fini) { + rcl_param_descriptor_t descriptor{}; + rcl_param_descriptor_t copy{}; + rcutils_allocator_t allocator = rcutils_get_default_allocator(); + + EXPECT_FALSE(rcl_yaml_descriptor_copy(nullptr, &descriptor, allocator)); + EXPECT_FALSE(rcl_yaml_descriptor_copy(©, nullptr, allocator)); + + ASSERT_TRUE(rcl_yaml_descriptor_copy(©, &descriptor, allocator)); + + rcl_yaml_descriptor_fini(©, allocator); + + // Check second fini works fine + rcl_yaml_descriptor_fini(©, allocator); + + // Check fini with a nullptr doesn't crash. + rcl_yaml_descriptor_fini(nullptr, allocator); +} + +TEST(TestYamlDescriptor, copy_fields) { + bool tmp_read_only = true; + TEST_DESCRIPTOR_COPY(read_only, tmp_read_only); + + uint8_t tmp_type = 2; + TEST_DESCRIPTOR_COPY(type, tmp_type); + + double tmp_min_value_double = -5.5; + TEST_DESCRIPTOR_COPY(min_value_double, tmp_min_value_double); + + double tmp_max_value_double = 16.4; + TEST_DESCRIPTOR_COPY(max_value_double, tmp_max_value_double); + + double tmp_step_double = 0.1; + TEST_DESCRIPTOR_COPY(step_double, tmp_step_double); + + int64_t tmp_min_value_int = 1; + TEST_DESCRIPTOR_COPY(min_value_int, tmp_min_value_int); + + int64_t tmp_max_value_int = 1001; + TEST_DESCRIPTOR_COPY(max_value_int, tmp_max_value_int); + + int64_t tmp_step_int = 5; + TEST_DESCRIPTOR_COPY(step_int, tmp_step_int); +} + +TEST(TestYamlDescriptor, copy_string_fields) { + // String version is slightly different and can't use the above macro + rcl_param_descriptor_t src_descriptor{}; + rcl_param_descriptor_t dest_descriptor{}; + rcutils_allocator_t allocator = rcutils_get_default_allocator(); + + char * tmp_description = rcutils_strdup("param description", allocator); + ASSERT_STREQ("param description", tmp_description); + + char * tmp_additional_constraints = rcutils_strdup("param additional constraints", allocator); + ASSERT_STREQ("param additional constraints", tmp_additional_constraints); + + OSRF_TESTING_TOOLS_CPP_SCOPE_EXIT( + { + allocator.deallocate(tmp_description, allocator.state); + allocator.deallocate(tmp_additional_constraints, allocator.state); + }); + + src_descriptor.description = tmp_description; + src_descriptor.additional_constraints = tmp_additional_constraints; + + EXPECT_TRUE(rcl_yaml_descriptor_copy(&dest_descriptor, &src_descriptor, allocator)); + ASSERT_NE(nullptr, dest_descriptor.description); + ASSERT_NE(nullptr, dest_descriptor.additional_constraints); + OSRF_TESTING_TOOLS_CPP_SCOPE_EXIT( + { + rcl_yaml_descriptor_fini(&dest_descriptor, allocator); + }); + EXPECT_STREQ(tmp_description, dest_descriptor.description); + EXPECT_STREQ(tmp_additional_constraints, dest_descriptor.additional_constraints); +}