diff --git a/rosidl_generator_c/test/test_interfaces.c b/rosidl_generator_c/test/test_interfaces.c index 621e3d5a9..2df89e96d 100644 --- a/rosidl_generator_c/test/test_interfaces.c +++ b/rosidl_generator_c/test/test_interfaces.c @@ -258,6 +258,8 @@ void test_strings(void) EXPECT_EQ(0, strcmp(strings->empty_string.data, TEST_STRING)); EXPECT_EQ(0, strcmp(strings->def_string.data, "Hello world!")); // since upper-bound checking is not implemented yet, we restrict the string copying + // TODO(mikaelarguedas) Test string length properly instead of cheating copy + // res = rosidl_generator_c__String__assign(&strings->ub_string, TEST_STRING); res = rosidl_generator_c__String__assignn(&strings->ub_string, TEST_STRING, 22); EXPECT_EQ(true, res); EXPECT_EQ(0, strcmp(strings->ub_string.data, "Deep into that darknes")); diff --git a/rosidl_generator_cpp/CMakeLists.txt b/rosidl_generator_cpp/CMakeLists.txt index b590a2a7a..b14403c03 100644 --- a/rosidl_generator_cpp/CMakeLists.txt +++ b/rosidl_generator_cpp/CMakeLists.txt @@ -33,6 +33,10 @@ if(BUILD_TESTING) "msg/StaticArrayStatic.msg" "msg/StaticArrayUnbounded.msg" + "msg/String.msg" + "msg/StringBounded.msg" + "msg/StringArrayStatic.msg" + "msg/UnboundedArrayBounded.msg" "msg/UnboundedArrayStatic.msg" "msg/UnboundedArrayUnbounded.msg" diff --git a/rosidl_generator_cpp/msg/PrimitiveStaticArrays.msg b/rosidl_generator_cpp/msg/PrimitiveStaticArrays.msg index 230cf3d4d..d511e7b5c 100644 --- a/rosidl_generator_cpp/msg/PrimitiveStaticArrays.msg +++ b/rosidl_generator_cpp/msg/PrimitiveStaticArrays.msg @@ -1,13 +1,13 @@ -bool[3] bool_value -byte[3] byte_value -char[3] char_value -float32[3] float32_value -float64[3] float64_value -int8[3] int8_value -uint8[3] uint8_value -int16[3] int16_value -uint16[3] uint16_value -int32[3] int32_value -uint32[3] uint32_value -int64[3] int64_value -uint64[3] uint64_value +bool[10] bool_value +byte[10] byte_value +char[10] char_value +float32[10] float32_value +float64[10] float64_value +int8[10] int8_value +uint8[10] uint8_value +int16[10] int16_value +uint16[10] uint16_value +int32[10] int32_value +uint32[10] uint32_value +int64[10] int64_value +uint64[10] uint64_value diff --git a/rosidl_generator_cpp/msg/PrimitivesBounded.msg b/rosidl_generator_cpp/msg/PrimitivesBounded.msg index f04716562..e5ca62cd0 100644 --- a/rosidl_generator_cpp/msg/PrimitivesBounded.msg +++ b/rosidl_generator_cpp/msg/PrimitivesBounded.msg @@ -11,4 +11,4 @@ int32[<=10] int32_value uint32[<=10] uint32_value int64[<=10] int64_value uint64[<=10] uint64_value -string<=10 string_value +string<=10[<=10] string_values diff --git a/rosidl_generator_cpp/msg/PrimitivesUnbounded.msg b/rosidl_generator_cpp/msg/PrimitivesUnbounded.msg index 6f89135d8..25f1aba07 100644 --- a/rosidl_generator_cpp/msg/PrimitivesUnbounded.msg +++ b/rosidl_generator_cpp/msg/PrimitivesUnbounded.msg @@ -11,4 +11,4 @@ int32[] int32_value uint32[] uint32_value int64[] int64_value uint64[] uint64_value -string string_value +string[] string_value diff --git a/rosidl_generator_cpp/msg/String.msg b/rosidl_generator_cpp/msg/String.msg new file mode 100644 index 000000000..055e799f1 --- /dev/null +++ b/rosidl_generator_cpp/msg/String.msg @@ -0,0 +1 @@ +string string_value diff --git a/rosidl_generator_cpp/msg/StringArrayStatic.msg b/rosidl_generator_cpp/msg/StringArrayStatic.msg new file mode 100644 index 000000000..dac94a0f7 --- /dev/null +++ b/rosidl_generator_cpp/msg/StringArrayStatic.msg @@ -0,0 +1 @@ +string[10] string_value diff --git a/rosidl_generator_cpp/msg/StringBounded.msg b/rosidl_generator_cpp/msg/StringBounded.msg new file mode 100644 index 000000000..81e038a3f --- /dev/null +++ b/rosidl_generator_cpp/msg/StringBounded.msg @@ -0,0 +1 @@ +string<=10 string_value diff --git a/rosidl_generator_cpp/test/test_array_generator.hpp b/rosidl_generator_cpp/test/test_array_generator.hpp index 1e12865a4..11c769880 100644 --- a/rosidl_generator_cpp/test/test_array_generator.hpp +++ b/rosidl_generator_cpp/test/test_array_generator.hpp @@ -17,6 +17,7 @@ #include #include +#include #include /** @@ -49,8 +50,8 @@ void test_vector_fill(C * container, size_t size, bool val1 = true, * Mininum and maximum values for the type and random values in the middle. * @param C Container (vector, array, etc) to be filled * @param size How many elements to fill in. Must size<=container_size - * @param min Minimum value in the rage to fill. - * @param max Maximum value in the rage to fill. + * @param min Minimum value in the range to fill. + * @param max Maximum value in the range to fill. */ template< typename C, @@ -84,8 +85,8 @@ void test_vector_fill(C * container, size_t size, * Mininum and maximum values for the type and random values in the middle. * @param C Container (vector, array, etc) to be filled * @param size How many elements to fill in. Must size<=container_size - * @param min Minimum value in the rage to fill. - * @param max Maximum value in the rage to fill. + * @param min Minimum value in the range to fill. + * @param max Maximum value in the range to fill. */ template< typename C, @@ -115,8 +116,8 @@ void test_vector_fill(C * container, size_t size, * Mininum and maximum values for the type and random numbers in the middle. * @param C Container (vector, array, etc) to be filled * @param size How many elements to fill in. Must size<=container_size - * @param min Minimum value in the rage to fill. - * @param max Maximum value in the rage to fill. + * @param min Minimum value in the range to fill. + * @param max Maximum value in the range to fill. */ template< typename C, @@ -139,4 +140,42 @@ void test_vector_fill(C * container, size_t size, } } +/** + * Helper function to generate a test pattern for string types. + * Mininum and maximum values for the type and random numbers in the middle. + * @param C Container (vector, array, etc) to be filled + * @param size How many elements to fill in. Must size<=container_size + * @param min Minimum value in the range to fill. + * @param max Maximum value in the range to fill. + * @param minlength Minimum length of the generated strings. + * @param maxlength Maximum length of the generated strings. + */ +template< + typename C, + typename std::enable_if< + std::is_same::value + >::type * = nullptr +> +void test_vector_fill(C * container, size_t size, + int min, int max, + int minlength, const int maxlength) +{ + std::default_random_engine rand_generator; + std::uniform_int_distribution randnum(min, max); + std::uniform_int_distribution randlen(minlength, maxlength); + + if (size > 0) { + char * tmpstr = reinterpret_cast(malloc(maxlength)); + std::snprintf(tmpstr, minlength, "%*d", minlength, min); + (*container)[0] = std::string(tmpstr); + for (size_t i = 1; i < size - 1; i++) { + int length = randlen(rand_generator); + std::snprintf(tmpstr, length, "%*d", length, randnum(rand_generator)); + (*container)[i] = std::string(tmpstr); + } + std::snprintf(tmpstr, maxlength, "%*d", maxlength, max); + (*container)[size - 1] = std::string(tmpstr); + } +} + #endif // TEST_ARRAY_GENERATOR_HPP_ diff --git a/rosidl_generator_cpp/test/test_interfaces.cpp b/rosidl_generator_cpp/test/test_interfaces.cpp index 57f0bcbb9..f70c731a6 100644 --- a/rosidl_generator_cpp/test/test_interfaces.cpp +++ b/rosidl_generator_cpp/test/test_interfaces.cpp @@ -40,11 +40,16 @@ #include "rosidl_generator_cpp/msg/static_array_static.hpp" #include "rosidl_generator_cpp/msg/static_array_unbounded.hpp" +#include "rosidl_generator_cpp/msg/string.hpp" +#include "rosidl_generator_cpp/msg/string_bounded.hpp" +#include "rosidl_generator_cpp/msg/string_array_static.hpp" + #include "rosidl_generator_cpp/msg/unbounded_array_bounded.hpp" #include "rosidl_generator_cpp/msg/unbounded_array_static.hpp" #include "rosidl_generator_cpp/msg/unbounded_array_unbounded.hpp" #define PRIMITIVES_ARRAY_SIZE 10 +#define BOUNDED_STRING_LENGTH 10 #define SUBMESSAGE_ARRAY_SIZE 3 TEST(Test_rosidl_generator_traits, has_fixed_size) { @@ -90,6 +95,19 @@ TEST(Test_rosidl_generator_traits, has_fixed_size) { rosidl_generator_cpp::msg::StaticArrayUnbounded>::value, "StaticArrayUnbounded::has_fixed_size is true"); + static_assert( + !rosidl_generator_traits::has_fixed_size::value, + "String::has_fixed_size is true"); + + static_assert( + !rosidl_generator_traits::has_fixed_size::value, + "StringBounded::has_fixed_size is true"); + + static_assert( + !rosidl_generator_traits::has_fixed_size< + rosidl_generator_cpp::msg::StringArrayStatic>::value, + "StringArrayStatic::has_fixed_size is true"); + static_assert( !rosidl_generator_traits::has_fixed_size::value, "BoundedArrayStatic::has_fixed_size is true"); @@ -166,6 +184,16 @@ void test_message_primitives_static(rosidl_generator_cpp::msg::PrimitivesStatic std::copy_n(pattern_ ## FieldName.begin(), Message.FieldName.size(), Message.FieldName.begin()); \ ASSERT_EQ(pattern_ ## FieldName, Message.FieldName); \ +#define TEST_BOUNDED_ARRAY_STRING( \ + Message, FieldName, PrimitiveType, ArraySize, MinVal, MaxVal, MinLength, MaxLength) \ + rosidl_generator_cpp::BoundedVector pattern_ ## FieldName; \ + Message.FieldName.resize(ArraySize); \ + pattern_ ## FieldName.resize(ArraySize); \ + test_vector_fill( \ + &pattern_ ## FieldName, ArraySize, MinVal, MaxVal, MinLength, MaxLength); \ + std::copy_n(pattern_ ## FieldName.begin(), Message.FieldName.size(), Message.FieldName.begin()); \ + ASSERT_EQ(pattern_ ## FieldName, Message.FieldName); \ + void test_message_primitives_bounded(rosidl_generator_cpp::msg::PrimitivesBounded message) { TEST_BOUNDED_ARRAY_PRIMITIVE(message, bool_value, bool, PRIMITIVES_ARRAY_SIZE, \ @@ -194,8 +222,8 @@ void test_message_primitives_bounded(rosidl_generator_cpp::msg::PrimitivesBounde INT64_MIN, INT64_MAX) TEST_BOUNDED_ARRAY_PRIMITIVE(message, uint64_value, uint64_t, PRIMITIVES_ARRAY_SIZE, \ 0, UINT64_MAX) - // Arrays of strings not supported yet - TEST_STRING_FIELD_ASSIGNMENT(message, string_value, "", "Deep into that darkness peering") + TEST_BOUNDED_ARRAY_STRING(message, string_values, std::string, PRIMITIVES_ARRAY_SIZE, \ + 0, UINT32_MAX, 0, BOUNDED_STRING_LENGTH) } #define TEST_UNBOUNDED_ARRAY_PRIMITIVE( \ @@ -208,6 +236,16 @@ void test_message_primitives_bounded(rosidl_generator_cpp::msg::PrimitivesBounde std::copy_n(pattern_ ## FieldName.begin(), ArraySize, Message.FieldName.begin()); \ ASSERT_EQ(pattern_ ## FieldName, Message.FieldName); \ +#define TEST_UNBOUNDED_ARRAY_STRING( \ + Message, FieldName, PrimitiveType, ArraySize, MinVal, MaxVal, MinLength, MaxLength) \ + std::vector pattern_ ## FieldName; \ + Message.FieldName.resize(ArraySize); \ + pattern_ ## FieldName.resize(ArraySize); \ + test_vector_fill( \ + &pattern_ ## FieldName, ArraySize, MinVal, MaxVal, MinLength, MaxLength); \ + std::copy_n(pattern_ ## FieldName.begin(), Message.FieldName.size(), Message.FieldName.begin()); \ + ASSERT_EQ(pattern_ ## FieldName, Message.FieldName); \ + void test_message_primitives_unbounded(rosidl_generator_cpp::msg::PrimitivesUnbounded message) { TEST_UNBOUNDED_ARRAY_PRIMITIVE(message, bool_value, bool, PRIMITIVES_ARRAY_SIZE, \ @@ -236,8 +274,46 @@ void test_message_primitives_unbounded(rosidl_generator_cpp::msg::PrimitivesUnbo INT64_MIN, INT64_MAX) TEST_UNBOUNDED_ARRAY_PRIMITIVE(message, uint64_value, uint64_t, PRIMITIVES_ARRAY_SIZE, \ 0, UINT64_MAX) - // Arrays of strings not supported yet - TEST_STRING_FIELD_ASSIGNMENT(message, string_value, "", "Deep into that darkness peering") + TEST_UNBOUNDED_ARRAY_STRING(message, string_value, std::string, PRIMITIVES_ARRAY_SIZE, \ + 0, UINT32_MAX, 0, UINT16_MAX) +} + +#define TEST_STATIC_ARRAY_PRIMITIVE( \ + Message, FieldName, PrimitiveType, ArraySize, MinVal, MaxVal) \ + std::array pattern_ ## FieldName; \ + test_vector_fill( \ + &pattern_ ## FieldName, ArraySize, MinVal, MaxVal); \ + std::copy_n(pattern_ ## FieldName.begin(), ArraySize, Message.FieldName.begin()); \ + ASSERT_EQ(pattern_ ## FieldName, Message.FieldName); \ + +void test_message_primitives_static_arrays(rosidl_generator_cpp::msg::PrimitiveStaticArrays message) +{ + TEST_STATIC_ARRAY_PRIMITIVE(message, bool_value, bool, PRIMITIVES_ARRAY_SIZE, \ + false, true) + TEST_STATIC_ARRAY_PRIMITIVE(message, char_value, char, PRIMITIVES_ARRAY_SIZE, \ + CHAR_MIN, CHAR_MAX) + TEST_STATIC_ARRAY_PRIMITIVE(message, byte_value, uint8_t, PRIMITIVES_ARRAY_SIZE, \ + 0, UINT8_MAX) + TEST_STATIC_ARRAY_PRIMITIVE(message, float32_value, float, PRIMITIVES_ARRAY_SIZE, \ + FLT_MIN, FLT_MAX) + TEST_STATIC_ARRAY_PRIMITIVE(message, float64_value, double, PRIMITIVES_ARRAY_SIZE, \ + DBL_MIN, DBL_MAX) + TEST_STATIC_ARRAY_PRIMITIVE(message, int8_value, int8_t, PRIMITIVES_ARRAY_SIZE, \ + INT8_MIN, INT8_MAX) + TEST_STATIC_ARRAY_PRIMITIVE(message, uint8_value, uint8_t, PRIMITIVES_ARRAY_SIZE, \ + 0, UINT8_MAX) + TEST_STATIC_ARRAY_PRIMITIVE(message, int16_value, int16_t, PRIMITIVES_ARRAY_SIZE, \ + INT16_MIN, INT16_MAX) + TEST_STATIC_ARRAY_PRIMITIVE(message, uint16_value, uint16_t, PRIMITIVES_ARRAY_SIZE, \ + 0, UINT16_MAX) + TEST_STATIC_ARRAY_PRIMITIVE(message, int32_value, int32_t, PRIMITIVES_ARRAY_SIZE, \ + INT32_MIN, INT32_MAX) + TEST_STATIC_ARRAY_PRIMITIVE(message, uint32_value, uint32_t, PRIMITIVES_ARRAY_SIZE, \ + 0, UINT32_MAX) + TEST_STATIC_ARRAY_PRIMITIVE(message, int64_value, int64_t, PRIMITIVES_ARRAY_SIZE, \ + INT64_MIN, INT64_MAX) + TEST_STATIC_ARRAY_PRIMITIVE(message, uint64_value, uint64_t, PRIMITIVES_ARRAY_SIZE, \ + 0, UINT64_MAX) } // Primitives static @@ -246,6 +322,12 @@ TEST(Test_messages, primitives_static) { test_message_primitives_static(message); } +// Primitives static arrays +TEST(Test_messages, primitives_static_arrays) { + rosidl_generator_cpp::msg::PrimitiveStaticArrays message; + test_message_primitives_static_arrays(message); +} + // Primitives bounded arrays TEST(Test_messages, primitives_bounded) { rosidl_generator_cpp::msg::PrimitivesBounded message; @@ -382,3 +464,32 @@ TEST(Test_messages, primitives_default) { TEST_PRIMITIVE_FIELD_ASSIGNMENT(message, uint64_value, 50000000ull, UINT64_MAX); TEST_STRING_FIELD_ASSIGNMENT(message, string_value, "bar", "Hello World!") } + +// TODO(mikaelarguedas) reenable this test when bounded strings enforce length +TEST(Test_messages, DISABLED_Test_bounded_strings) { + rosidl_generator_cpp::msg::StringBounded message; + TEST_STRING_FIELD_ASSIGNMENT(message, string_value, "", "Deep into") + std::string tooLongString = std::string("Too long string"); + message.string_value = tooLongString; + tooLongString.resize(BOUNDED_STRING_LENGTH); + ASSERT_STREQ(tooLongString.c_str(), message.string_value.c_str()); +} + +TEST(Test_messages, Test_string) { + rosidl_generator_cpp::msg::String message; + TEST_STRING_FIELD_ASSIGNMENT(message, string_value, "", "Deep into") +} + +#define TEST_STATIC_ARRAY_STRING( \ + Message, FieldName, PrimitiveType, ArraySize, MinVal, MaxVal, MinLength, MaxLength) \ + std::array pattern_ ## FieldName; \ + test_vector_fill( \ + &pattern_ ## FieldName, ArraySize, MinVal, MaxVal, MinLength, MaxLength); \ + std::copy_n(pattern_ ## FieldName.begin(), Message.FieldName.size(), Message.FieldName.begin()); \ + ASSERT_EQ(pattern_ ## FieldName, Message.FieldName); \ + +TEST(Test_messages, Test_string_array_static) { + rosidl_generator_cpp::msg::StringArrayStatic message; + TEST_STATIC_ARRAY_STRING(message, string_value, std::string, PRIMITIVES_ARRAY_SIZE, \ + 0, UINT32_MAX, 0, UINT16_MAX) +} diff --git a/rosidl_generator_py/msg/Primitives.msg b/rosidl_generator_py/msg/Primitives.msg index ec13f6adb..8062e68cb 100644 --- a/rosidl_generator_py/msg/Primitives.msg +++ b/rosidl_generator_py/msg/Primitives.msg @@ -13,6 +13,4 @@ int64 int64_value uint64 uint64_value string string_value string string_value_with_default 'default' -#string<=5[3] fixed_length_string_value -#string<=5[<=10] upper_bound_string_value string unbound_string_value diff --git a/rosidl_generator_py/msg/Strings.msg b/rosidl_generator_py/msg/Strings.msg index f81e029bc..fd1de1d0f 100644 --- a/rosidl_generator_py/msg/Strings.msg +++ b/rosidl_generator_py/msg/Strings.msg @@ -2,3 +2,6 @@ string empty_string string def_string "Hello world!" string<=22 ub_string string<=22 ub_def_string "Upper bounded string." +string<=5[3] ub_string_static_array_value +string<=5[<=10] ub_string_ub_array_value +string<=5[] ub_string_dynamic_array_value diff --git a/rosidl_generator_py/resource/_msg.py.em b/rosidl_generator_py/resource/_msg.py.em index 3243a2443..e76c775b3 100644 --- a/rosidl_generator_py/resource/_msg.py.em +++ b/rosidl_generator_py/resource/_msg.py.em @@ -181,6 +181,9 @@ class @(spec.base_type.type)(metaclass=Metaclass): isinstance(value, UserList)) and not isinstance(value, str) and not isinstance(value, UserString) and +@[ if field.type.type == 'string' and field.type.string_upper_bound]@ + all([len(val) <= @field.type.string_upper_bound for val in value]) and +@[ end if]@ @[ if field.type.array_size]@ @[ if field.type.is_upper_bound]@ len(value) <= @(field.type.array_size) and diff --git a/rosidl_generator_py/test/test_interfaces.py b/rosidl_generator_py/test/test_interfaces.py index 6e464c3a1..fabfc9f1e 100644 --- a/rosidl_generator_py/test/test_interfaces.py +++ b/rosidl_generator_py/test/test_interfaces.py @@ -28,6 +28,54 @@ def test_strings(): assert 'Hello world!' == a.def_string +def test_arrays_of_bounded_strings(): + a = Strings() + array_valid_string_length = ['a' * 2, 'b' * 3, 'c' * 4] + array_too_long_strings = ['a' * 2, 'b' * 3, 'c' * 6] + assert ['', '', ''] == a.ub_string_static_array_value + a.ub_string_static_array_value = array_valid_string_length + assert array_valid_string_length == a.ub_string_static_array_value + + assert_raises( + AssertionError, setattr, a, + 'ub_string_static_array_value', array_too_long_strings) + + assert_raises( + AssertionError, setattr, a, + 'ub_string_static_array_value', ['a' * 2, 'b' * 3]) + + assert [] == a.ub_string_ub_array_value + a.ub_string_ub_array_value = array_valid_string_length + assert array_valid_string_length == a.ub_string_ub_array_value + + assert_raises( + AssertionError, setattr, a, + 'ub_string_ub_array_value', array_too_long_strings) + + array10strings = [] + [str(i) for i in range(10)] + a.ub_string_ub_array_value = array10strings + assert array10strings == a.ub_string_ub_array_value + + assert_raises( + AssertionError, setattr, a, + 'ub_string_ub_array_value', array10strings + ['gfg']) + + assert [] == a.ub_string_dynamic_array_value + a.ub_string_dynamic_array_value = array_valid_string_length + assert array_valid_string_length == a.ub_string_dynamic_array_value + + assert_raises( + AssertionError, setattr, a, + 'ub_string_dynamic_array_value', array_too_long_strings) + + array10strings = [] + [str(i) for i in range(10)] + a.ub_string_dynamic_array_value = array10strings + assert array10strings == a.ub_string_dynamic_array_value + array10strings += ['gfg'] + a.ub_string_dynamic_array_value = array10strings + assert array10strings == a.ub_string_dynamic_array_value + + def test_invalid_attribute(): a = Strings()