Skip to content

Commit

Permalink
Cleanup a bit
Browse files Browse the repository at this point in the history
Add a test for a very complex struct
Add test for array vectors
  • Loading branch information
matt-attack committed Feb 26, 2025
1 parent de2dd7d commit 7ea7230
Show file tree
Hide file tree
Showing 8 changed files with 140 additions and 25 deletions.
26 changes: 13 additions & 13 deletions include/pubsub/Serialization.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@ extern "C"
{
#endif

struct ps_allocator_t
{
void*(*alloc)(unsigned int size, void* context);
void(*free)(void*, void* context);
void* context;
};
struct ps_allocator_t
{
void*(*alloc)(unsigned int size, void* context);
void(*free)(void*, void* context);
void* context;
};

extern struct ps_allocator_t ps_default_allocator;
extern struct ps_allocator_t ps_default_allocator;

enum ps_field_types
{
Expand Down Expand Up @@ -103,15 +103,15 @@ extern "C"

// Serializes a given message definition to a buffer.
// Returns: Number of bytes written
int ps_serialize_message_definition(void* start, const struct ps_message_definition_t* definition);
int ps_serialize_message_definition(void* dst, const struct ps_message_definition_t* definition);

// Deserializes a message definition from the specified buffer.
void ps_deserialize_message_definition(const void* start, struct ps_message_definition_t* definition);
void ps_deserialize_message_definition(const void* src, struct ps_message_definition_t* definition);

// print out the deserialized contents of the message to console, for rostopic echo like implementations
// in yaml format
// if field is non-null only print out the content of that field
void ps_deserialize_print(const void* data, const struct ps_message_definition_t* definition, unsigned int max_array_size, const char* field);
// if field_name is non-null only print out the content of that field
void ps_deserialize_print(const void* data, const struct ps_message_definition_t* definition, unsigned int max_array_size, const char* field_name);

struct ps_deserialize_iterator
{
Expand All @@ -126,11 +126,11 @@ extern "C"

// Create an iteratator to iterate through the fields of a serialized message
// Returns: The iterator
struct ps_deserialize_iterator ps_deserialize_start(const char* msg, const struct ps_message_definition_t* definition);
struct ps_deserialize_iterator ps_deserialize_start(const void* msg, const struct ps_message_definition_t* definition);

// Iterate through a serialized message one field at a time
// Returns: Start pointer in the message for the current field or zero when at the end
const char* ps_deserialize_iterate(struct ps_deserialize_iterator* iter, const struct ps_msg_field_t** f, uint32_t* l);
const void* ps_deserialize_iterate(struct ps_deserialize_iterator* iter, const struct ps_msg_field_t** f, uint32_t* l);

// Frees a dynamically allocated message definition
void ps_free_message_definition(struct ps_message_definition_t* definition);
Expand Down
15 changes: 10 additions & 5 deletions include/pubsub_cpp/array_string.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,12 +42,17 @@ class FixedString
strncpy(data_, string, string_length - 1);
}

bool operator==(const char* other)
bool operator==(const char* other) const
{
return strcmp(other, data_) == 0;
}

bool operator==(const std::string& other)
bool operator==(const std::string& other) const
{
return strcmp(other.c_str(), data_) == 0;
}

bool operator==(const FixedString& other) const
{
return strcmp(other.c_str(), data_) == 0;
}
Expand Down Expand Up @@ -135,7 +140,7 @@ class CString
}
}

bool operator==(const char* other)
bool operator==(const char* other) const
{
if (data_ == 0)
{
Expand All @@ -144,7 +149,7 @@ class CString
return strcmp(other, data_) == 0;
}

bool operator==(const std::string& other)
bool operator==(const std::string& other) const
{
if (data_ == 0)
{
Expand All @@ -153,7 +158,7 @@ class CString
return strcmp(other.c_str(), data_) == 0;
}

bool operator==(const CString& other)
bool operator==(const CString& other) const
{
if (data_ == 0)
{
Expand Down
2 changes: 1 addition & 1 deletion src/Bindings.c
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@ EXPORT int ps_create_publisher(int node, const char* topic, const char* definiti

EXPORT void ps_publish(int pub, const void* msg, int len)
{
// publish the message simply since it is already encoded
// publish the message simply since it is already encoded
struct ps_msg_t omsg;
ps_msg_alloc(len, 0, &omsg);
memcpy(ps_get_msg_start(omsg.data), msg, len);
Expand Down
6 changes: 2 additions & 4 deletions src/Serialization.c
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ static int GetFieldSize(const struct ps_msg_field_t* field)
return field_size;
}

struct ps_deserialize_iterator ps_deserialize_start(const char* msg, const struct ps_message_definition_t* definition)
struct ps_deserialize_iterator ps_deserialize_start(const void* msg, const struct ps_message_definition_t* definition)
{
struct ps_deserialize_iterator iter;
iter.next_field_index = 0;
Expand All @@ -217,7 +217,7 @@ struct ps_deserialize_iterator ps_deserialize_start(const char* msg, const struc
}

// takes in a deserialize iterator and returns pointer to the data and the current field
const char* ps_deserialize_iterate(struct ps_deserialize_iterator* iter, const struct ps_msg_field_t** f, uint32_t* l)
const void* ps_deserialize_iterate(struct ps_deserialize_iterator* iter, const struct ps_msg_field_t** f, uint32_t* l)
{
if (iter->next_field_index == iter->num_fields)
{
Expand Down Expand Up @@ -415,10 +415,8 @@ void ps_deserialize_print(const void * data, const struct ps_message_definition_
{
struct ps_deserialize_iterator iter = ps_deserialize_start(data, definition);
const struct ps_msg_field_t* field; uint32_t length; const char* ptr;
int it = -1;
while (ptr = ps_deserialize_iterate(&iter, &field, &length))
{
it++;
if (field_name && strcmp(field_name, field->name) != 0)
{
continue;
Expand Down
2 changes: 1 addition & 1 deletion tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ foreach(test_file ${tests})
add_executable("${test_file}" "${test_source}")
target_include_directories("${test_file}" PUBLIC ../include)
add_dependencies("${test_file}" pubsub pubsub_msgs)
target_link_libraries("${test_file}" pubsub ${CMAKE_THREAD_LIBS_INIT} pubsub_msgs)
target_link_libraries("${test_file}" pubsub ${CMAKE_THREAD_LIBS_INIT} pubsub_msgs pubsub_test_msgs)

# Auto populate the tests from test source file
# Note : CMake must be reconfigured if tests are added/renamed/removed from test source file
Expand Down
2 changes: 1 addition & 1 deletion tests/test_pubsub_c.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ TEST(test_publish_subscribe_large, []() {
pubsub__PointCloud_free(data, &ps_default_allocator);
free(message);
};
ps_node_create_subscriber_adv(&node, "/data", 0, &string_sub, &options);
ps_node_create_subscriber_adv(&node, "/data", &pubsub__PointCloud_def, &string_sub, &options);

// now spin and wait for us to get the published message
while (ps_okay() && !got_message)
Expand Down
103 changes: 103 additions & 0 deletions tests/test_serialization.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include <pubsub/Costmap.msg.h>
#include <pubsub/Path2D.msg.h>
#include <pubsub/String.msg.h>
#include <pubsub/Test.msg.h>

#include "mini_mock.hpp"

Expand Down Expand Up @@ -41,6 +42,9 @@ TEST(test_string_cpp, []() {
EXPECT(msg.value == value);
EXPECT(msg.value == value.c_str());
EXPECT(strcmp(msg.value.c_str(), value.c_str()) == 0);

// make sure the C++ string object is the same size as a pointer
EXPECT(sizeof(msg.value) == sizeof(char*));
}
});

Expand All @@ -65,6 +69,25 @@ TEST(test_fixed_string_cpp, []() {
}
});

TEST(test_array_vector_cpp, []() {
// test C++ array vectors
{
ArrayVector<uint32_t> vector;
EXPECT(sizeof(vector) == 4 + sizeof(char*));
EXPECT(vector.size() == 0);
vector.resize(4);
EXPECT(vector.size() == 4)

for (auto& item: vector)
{
item = 55;
}

EXPECT(vector[0] == 55);
EXPECT(vector[3] == 55);
}
});

TEST(test_costmap_c_cpp, []() {
// verify that the C type matches the C++ one memory wise
pubsub::msg::Costmap msg;
Expand Down Expand Up @@ -201,4 +224,84 @@ TEST(test_path2d_foreach, []() {
delete out;
});

void compare_struct(const pubsub::msg::Test::Struct& a, const pubsub::msg::Test::Struct& b)
{
EXPECT(a.test_array_string == b.test_array_string);
EXPECT(a.i1 == b.i1);
EXPECT(a.i2 == b.i2);
EXPECT(a.f3 == b.f3);
EXPECT(a.d4 == b.d4);
for (int i = 0; i < 3; i++)
EXPECT(a.a5[i] == b.a5[i]);
}

void fill_struct(pubsub::msg::Test::Struct& a)
{
a.test_array_string = "ok";
a.i1 = rand();
a.i2 = rand();
a.f3 = rand()/100.0;
a.d4 = rand()/100.0;
for (int i = 0; i < 3; i++)
a.a5[i] = rand()/100.0;
a.test_enum = pubsub::msg::Test::TYPE_INT;
}

TEST(test_complex_message, []() {
// try serializing then deserializing a message, making sure everything matches expectation
EXPECT(sizeof(pubsub::msg::Test) == sizeof(pubsub__Test));
EXPECT(sizeof(pubsub::msg::Test::Struct) == 56);
pubsub::msg::Test msg;
msg.test_int = rand();
msg.test_string = "THIS IS A STRING";
fill_struct(msg.test_struct);
fill_struct(msg.test_structs[0]);
fill_struct(msg.test_structs[1]);
msg.test_struct_array.resize(4);
for (int i = 0; i < 4; i++)
fill_struct(msg.test_struct_array[i]);
msg.test_bitmask[0] = pubsub::msg::Test::TYPE2_FLAG1;
msg.test_bitmask[1] = pubsub::msg::Test::TYPE2_FLAG2;
msg.test_bitmask[2] = pubsub::msg::Test::TYPE2_FLAG1 | pubsub::msg::Test::TYPE2_FLAG2;

ps_msg_t in = msg.Encode();
EXPECT(in.len == 420);// 56*7 + 4 + 4 + 3 + 17 = 392 + 11 + 17

// Make sure deserialize iterators work correctly
struct ps_deserialize_iterator iter = ps_deserialize_start(ps_get_msg_start((const char*)in.data), pubsub::msg::Test::GetDefinition());
const struct ps_msg_field_t* field; uint32_t length; const void* ptr;
std::vector<std::string> fields;
while (ptr = ps_deserialize_iterate(&iter, &field, &length))
{
fields.push_back(field->name);

// check some values
if (strcmp(field->name, "test_bitmask") == 0)
{
EXPECT(*(uint8_t*)ptr == msg.test_bitmask[0]);
}
if (strcmp(field->name, "test_int") == 0)
{
EXPECT(*(uint32_t*)ptr == msg.test_int);
}
}
EXPECT(fields.size() == 6);
EXPECT(fields[0] == "test_int");
EXPECT(fields[5] == "test_bitmask");

auto* out = pubsub::msg::Test::Decode(ps_get_msg_start(in.data));
free(in.data);

EXPECT(msg.test_int == out->test_int);
EXPECT(msg.test_string == out->test_string);
compare_struct(msg.test_struct, out->test_struct);
compare_struct(msg.test_structs[0], out->test_structs[0]);
compare_struct(msg.test_structs[1], out->test_structs[1]);
EXPECT(msg.test_struct_array.size() == out->test_struct_array.size())
for (int i = 0; i < 4; i++)
compare_struct(msg.test_struct_array[i], out->test_struct_array[i]);
for (int i = 0; i < 3; i++)
EXPECT(msg.test_bitmask[i] == out->test_bitmask[i]);
});

CREATE_MAIN_ENTRY_POINT();
9 changes: 9 additions & 0 deletions tools/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ macro(generate_messages)
endmacro()

generate_messages(FILES
Frames.msg
Joy.msg
String.msg
Float.msg
Expand All @@ -72,6 +73,14 @@ generate_messages(FILES
Int.msg
)

generate_messages(FILES
Test.msg
TARGET
pubsub_test_msgs
DIRECTORY
tests
)


# Build the test program
add_executable(pubsubtest "PubSubTest.cpp")
Expand Down

0 comments on commit 7ea7230

Please sign in to comment.