@@ -212,6 +212,51 @@ TEST_F(OnnxComponentsTest, MemoryOutput_WithRealRuntime) {
212212 EXPECT_TRUE (memory_was_overwritten) << " Memory should be overwritten with new output data" ;
213213}
214214
215+ TEST_F (OnnxComponentsTest, CommandJointPositionInput_InitAndRead) {
216+ metadata::JointPositionCommandMetadata meta;
217+ meta.joint_names = {" j1" , " j2" };
218+ CommandJointPositionInput input (" cmd.joint_pos.arm" , " arm" , meta);
219+
220+ EXPECT_CALL (command_mock_, initJointPosition (" arm" , " j1" )).WillOnce (Return (true ));
221+ EXPECT_CALL (command_mock_, initJointPosition (" arm" , " j2" )).WillOnce (Return (true ));
222+ EXPECT_TRUE (input.init (state_mock_, command_mock_));
223+
224+ EXPECT_CALL (command_mock_, jointPosition (" arm" , " j1" )).WillOnce (Return (std::make_optional (0 .1f )));
225+ EXPECT_CALL (command_mock_, jointPosition (" arm" , " j2" )).WillOnce (Return (std::make_optional (0 .2f )));
226+ EXPECT_TRUE (input.read (runtime, state_mock_, command_mock_));
227+
228+ auto buffer = runtime.inputBuffer <float >(" cmd.joint_pos.arm" );
229+ ASSERT_TRUE (buffer.has_value ());
230+ ASSERT_EQ (buffer->size (), 2u );
231+ EXPECT_FLOAT_EQ ((*buffer)[0 ], 0 .1f );
232+ EXPECT_FLOAT_EQ ((*buffer)[1 ], 0 .2f );
233+ }
234+
235+ TEST_F (OnnxComponentsTest, CommandJointPositionInput_InitFailsWhenJointNamesEmpty) {
236+ metadata::JointPositionCommandMetadata meta; // joint_names left empty
237+ CommandJointPositionInput input (" cmd.joint_pos.arm" , " arm" , meta);
238+ EXPECT_FALSE (input.init (state_mock_, command_mock_));
239+ }
240+
241+ TEST_F (OnnxComponentsTest, CommandJointPositionInput_InitFailsWhenOneJointFails) {
242+ metadata::JointPositionCommandMetadata meta;
243+ meta.joint_names = {" j1" , " j2" };
244+ CommandJointPositionInput input (" cmd.joint_pos.arm" , " arm" , meta);
245+
246+ EXPECT_CALL (command_mock_, initJointPosition (" arm" , " j1" )).WillOnce (Return (false ));
247+ EXPECT_FALSE (input.init (state_mock_, command_mock_));
248+ }
249+
250+ TEST_F (OnnxComponentsTest, CommandJointPositionInput_ReadFailsWhenJointUnavailable) {
251+ metadata::JointPositionCommandMetadata meta;
252+ meta.joint_names = {" j1" , " j2" };
253+ CommandJointPositionInput input (" cmd.joint_pos.arm" , " arm" , meta);
254+
255+ EXPECT_CALL (command_mock_, jointPosition (" arm" , " j1" )).WillOnce (Return (std::make_optional (0 .1f )));
256+ EXPECT_CALL (command_mock_, jointPosition (" arm" , " j2" )).WillOnce (Return (std::nullopt ));
257+ EXPECT_FALSE (input.read (runtime, state_mock_, command_mock_));
258+ }
259+
215260// --------------- Matcher tests for default metadata --------------------------------
216261
217262TEST (CommandFloatMatcherTest, CreatesInputWithoutMetadata) {
@@ -257,4 +302,61 @@ TEST(CommandSE2VelocityMatcherTest, CreatesInputWithMetadata) {
257302 ASSERT_EQ (inputs.size (), 1u );
258303}
259304
305+ TEST (CommandJointPositionMatcherTest, DoesNotMatchOtherPatterns) {
306+ CommandJointPositionMatcher matcher;
307+ EXPECT_FALSE (matcher.matches ({.name = " cmd.float.gain" }));
308+ EXPECT_FALSE (matcher.matches ({.name = " cmd.joint_vel.arm" }));
309+ EXPECT_FALSE (matcher.matches ({.name = " cmd.joint_pos" }));
310+ }
311+
312+ TEST (CommandJointPositionMatcherTest, SkipsInputWithoutMetadata) {
313+ CommandJointPositionMatcher matcher;
314+ ASSERT_TRUE (matcher.matches ({.name = " cmd.joint_pos.arm" }));
315+
316+ auto inputs = matcher.createInputs ();
317+ ASSERT_EQ (inputs.size (), 0u );
318+ }
319+
320+ TEST (CommandJointPositionMatcherTest, SkipsInputWithEmptyJointNames) {
321+ CommandJointPositionMatcher matcher;
322+ ASSERT_TRUE (matcher.matches ({.name = " cmd.joint_pos.arm" , .metadata = R"( {"joint_names": []})" }));
323+
324+ auto inputs = matcher.createInputs ();
325+ ASSERT_EQ (inputs.size (), 0u );
326+ }
327+
328+ TEST (CommandJointPositionMatcherTest, CreatesInputWithMetadata) {
329+ CommandJointPositionMatcher matcher;
330+ Match m{
331+ .name = " cmd.joint_pos.arm" ,
332+ .metadata = R"( {"joint_names": ["j1", "j2"]})" ,
333+ };
334+ ASSERT_TRUE (matcher.matches (m));
335+
336+ auto inputs = matcher.createInputs ();
337+ ASSERT_EQ (inputs.size (), 1u );
338+ }
339+
340+ TEST (CommandJointPositionMatcherTest, DoesNotMatchSameNameTwice) {
341+ CommandJointPositionMatcher matcher;
342+ ASSERT_TRUE (matcher.matches ({.name = " cmd.joint_pos.arm" }));
343+ // Second match with the same name overwrites, still produces one entry (but 0 inputs without
344+ // metadata)
345+ ASSERT_TRUE (matcher.matches ({.name = " cmd.joint_pos.arm" }));
346+
347+ auto inputs = matcher.createInputs ();
348+ ASSERT_EQ (inputs.size (), 0u );
349+ }
350+
351+ TEST (CommandJointPositionMatcherTest, MatchesMultipleCommands) {
352+ CommandJointPositionMatcher matcher;
353+ ASSERT_TRUE (matcher.matches (
354+ {.name = " cmd.joint_pos.arm" , .metadata = R"( {"joint_names": ["j1", "j2"]})" }));
355+ ASSERT_TRUE (matcher.matches (
356+ {.name = " cmd.joint_pos.leg" , .metadata = R"( {"joint_names": ["j3", "j4"]})" }));
357+
358+ auto inputs = matcher.createInputs ();
359+ ASSERT_EQ (inputs.size (), 2u );
360+ }
361+
260362} // namespace exploy::control
0 commit comments