@@ -182,15 +182,30 @@ const unsigned char RUNTIME_EXCEPTION_MESSAGE[] = {
182182 0x6e , 0x6f , 0x74 , 0x5f , 0x66 , 0x6f , 0x75 , 0x6e , 0x64 , 0x3a , 0x74 , 0x78 , 0x74 , 0x6d , 0x73 , 0x67 , 0x3a
183183};
184184
185- TEST (primary_parser, parse_calibration_data)
185+ class PrimaryParserTest : public ::testing::Test
186+ {
187+ protected:
188+ virtual void SetUp () override
189+ {
190+ // In these tests we use strict mode, which means that the parser will not ignore any extra
191+ // bytes in the payload. This allows us to verify that the parser correctly identifies the size
192+ // of each message and does not consume more bytes than it should.
193+ // If new software versions add new fields to the messages, these tests will fail, which is
194+ // desirable as it will prompt us to update the parser with the new fields.
195+ parser_.setStrictMode (true );
196+ }
197+
198+ primary_interface::PrimaryParser parser_;
199+ };
200+
201+ TEST_F (PrimaryParserTest, parse_calibration_data)
186202{
187203 unsigned char raw_data[sizeof (ROBOT_STATE )];
188204 memcpy (raw_data, ROBOT_STATE , sizeof (ROBOT_STATE ));
189205 comm::BinParser bp (raw_data, sizeof (raw_data));
190206
191207 std::vector<std::unique_ptr<primary_interface::PrimaryPackage>> products;
192- primary_interface::PrimaryParser parser;
193- parser.parse (bp, products);
208+ parser_.parse (bp, products);
194209
195210 EXPECT_EQ (products.size (), 13 );
196211
@@ -205,26 +220,24 @@ TEST(primary_parser, parse_calibration_data)
205220 }
206221}
207222
208- TEST (primary_parser , parse_robot_state_with_single_parser)
223+ TEST_F (PrimaryParserTest , parse_robot_state_with_single_parser)
209224{
210225 unsigned char raw_data[sizeof (ROBOT_STATE )];
211226 memcpy (raw_data, ROBOT_STATE , sizeof (ROBOT_STATE ));
212227 comm::BinParser bp (raw_data, sizeof (raw_data));
213228
214229 std::unique_ptr<primary_interface::PrimaryPackage> product;
215- primary_interface::PrimaryParser parser;
216- ASSERT_FALSE (parser.parse (bp, product));
230+ ASSERT_FALSE (parser_.parse (bp, product));
217231};
218232
219- TEST (primary_parser , parse_version_message)
233+ TEST_F (PrimaryParserTest , parse_version_message)
220234{
221235 unsigned char raw_data[sizeof (VERSION_MESSAGE )];
222236 memcpy (raw_data, VERSION_MESSAGE , sizeof (VERSION_MESSAGE ));
223237 comm::BinParser bp (raw_data, sizeof (raw_data));
224238
225239 std::unique_ptr<primary_interface::PrimaryPackage> product;
226- primary_interface::PrimaryParser parser;
227- ASSERT_TRUE (parser.parse (bp, product));
240+ ASSERT_TRUE (parser_.parse (bp, product));
228241
229242 EXPECT_NE (product, nullptr );
230243 if (primary_interface::VersionMessage* data = dynamic_cast <primary_interface::VersionMessage*>(product.get ()))
@@ -241,15 +254,14 @@ TEST(primary_parser, parse_version_message)
241254 }
242255}
243256
244- TEST (primary_parser , parse_key_message)
257+ TEST_F (PrimaryParserTest , parse_key_message)
245258{
246259 unsigned char raw_data[sizeof (KEY_MESSAGE )];
247260 memcpy (raw_data, KEY_MESSAGE , sizeof (KEY_MESSAGE ));
248261 comm::BinParser bp (raw_data, sizeof (raw_data));
249262
250263 std::vector<std::unique_ptr<primary_interface::PrimaryPackage>> products;
251- primary_interface::PrimaryParser parser;
252- ASSERT_TRUE (parser.parse (bp, products));
264+ ASSERT_TRUE (parser_.parse (bp, products));
253265
254266 ASSERT_EQ (products.size (), 1 );
255267 if (auto data = dynamic_cast <primary_interface::KeyMessage*>(products[0 ].get ()))
@@ -274,15 +286,14 @@ TEST(primary_parser, parse_key_message)
274286 }
275287}
276288
277- TEST (primary_parser , parse_runtime_exception_message)
289+ TEST_F (PrimaryParserTest , parse_runtime_exception_message)
278290{
279291 unsigned char raw_data[sizeof (RUNTIME_EXCEPTION_MESSAGE )];
280292 memcpy (raw_data, RUNTIME_EXCEPTION_MESSAGE , sizeof (RUNTIME_EXCEPTION_MESSAGE ));
281293 comm::BinParser bp (raw_data, sizeof (raw_data));
282294
283295 std::vector<std::unique_ptr<primary_interface::PrimaryPackage>> products;
284- primary_interface::PrimaryParser parser;
285- ASSERT_TRUE (parser.parse (bp, products));
296+ ASSERT_TRUE (parser_.parse (bp, products));
286297
287298 EXPECT_EQ (products.size (), 1 );
288299
@@ -305,6 +316,78 @@ TEST(primary_parser, parse_runtime_exception_message)
305316 }
306317}
307318
319+ TEST_F (PrimaryParserTest, parse_robot_state_with_oversized_submessage)
320+ {
321+ // KinematicsInfo parses exactly 220 bytes of payload:
322+ // 6×uint32 (checksum) + 4×vector6d (DH params) + uint32 (calibration_status)
323+ const size_t ki_payload_size = 6 * sizeof (uint32_t ) + 4 * 6 * sizeof (double ) + sizeof (uint32_t );
324+ const size_t extra_bytes = 10 ;
325+ const size_t sub1_size = sizeof (uint32_t ) + sizeof (uint8_t ) + ki_payload_size + extra_bytes;
326+ const size_t sub2_size = sizeof (uint32_t ) + sizeof (uint8_t ) + 1 ;
327+ const size_t total_size = sizeof (int32_t ) + sizeof (uint8_t ) + sub1_size + sub2_size;
328+
329+ std::vector<uint8_t > test_data (total_size, 0 );
330+ size_t offset = 0 ;
331+
332+ // Total packet size (big-endian int32)
333+ test_data[offset++] = (total_size >> 24 ) & 0xFF ;
334+ test_data[offset++] = (total_size >> 16 ) & 0xFF ;
335+ test_data[offset++] = (total_size >> 8 ) & 0xFF ;
336+ test_data[offset++] = total_size & 0xFF ;
337+
338+ // Packet type: ROBOT_STATE (0x10)
339+ test_data[offset++] = 0x10 ;
340+
341+ // Submessage 1: KinematicsInfo with extra trailing bytes
342+ test_data[offset++] = (sub1_size >> 24 ) & 0xFF ;
343+ test_data[offset++] = (sub1_size >> 16 ) & 0xFF ;
344+ test_data[offset++] = (sub1_size >> 8 ) & 0xFF ;
345+ test_data[offset++] = sub1_size & 0xFF ;
346+ test_data[offset++] = 0x05 ; // KINEMATICS_INFO
347+ offset += ki_payload_size; // payload is all zeros
348+ memset (test_data.data () + offset, 0xAA , extra_bytes);
349+ offset += extra_bytes;
350+
351+ // Submessage 2: ADDITIONAL_INFO (parsed by base RobotState → rawData)
352+ test_data[offset++] = (sub2_size >> 24 ) & 0xFF ;
353+ test_data[offset++] = (sub2_size >> 16 ) & 0xFF ;
354+ test_data[offset++] = (sub2_size >> 8 ) & 0xFF ;
355+ test_data[offset++] = sub2_size & 0xFF ;
356+ test_data[offset++] = 0x08 ; // ADDITIONAL_INFO
357+ test_data[offset++] = 0x42 ; // 1 byte of payload
358+
359+ ASSERT_EQ (offset, total_size);
360+
361+ {
362+ // With strict mode enabled, an execption should be thrown due to the oversized KinematicsInfo
363+ // submessage
364+ std::vector<std::unique_ptr<primary_interface::PrimaryPackage>> products;
365+ unsigned char raw_data[total_size];
366+ memcpy (raw_data, test_data.data (), test_data.size ());
367+ comm::BinParser bp (raw_data, total_size);
368+ EXPECT_THROW (parser_.parse (bp, products), UrException);
369+ }
370+
371+ {
372+ // Using a non-strict parser that allows oversized submessages should succeed and parse all
373+ // submessages
374+ std::vector<std::unique_ptr<primary_interface::PrimaryPackage>> products;
375+ unsigned char raw_data[total_size];
376+ memcpy (raw_data, test_data.data (), test_data.size ());
377+ comm::BinParser bp (raw_data, total_size);
378+ primary_interface::PrimaryParser non_strict_parser;
379+ EXPECT_TRUE (non_strict_parser.parse (bp, products));
380+
381+ // Both submessages should be parsed despite the oversized first one
382+ ASSERT_EQ (products.size (), 2 );
383+
384+ auto * ki = dynamic_cast <primary_interface::KinematicsInfo*>(products[0 ].get ());
385+ ASSERT_NE (ki, nullptr );
386+ EXPECT_EQ (ki->calibration_status_ , 0u );
387+ EXPECT_EQ (ki->dh_theta_ , vector6d_t ({ 0 , 0 , 0 , 0 , 0 , 0 }));
388+ }
389+ }
390+
308391int main (int argc, char * argv[])
309392{
310393 ::testing::InitGoogleTest (&argc, argv);
0 commit comments