Skip to content

Commit cd6ad52

Browse files
committed
Add test of primitive types
1 parent cfdb90b commit cd6ad52

3 files changed

Lines changed: 421 additions & 0 deletions

File tree

tests/CMakeLists.txt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,3 +271,13 @@ add_executable(helpers_tests test_helpers.cpp)
271271
target_link_libraries(helpers_tests PRIVATE ur_client_library::urcl GTest::gtest_main)
272272
gtest_add_tests(TARGET helpers_tests
273273
)
274+
275+
add_executable(types_tests test_types.cpp)
276+
target_link_libraries(types_tests PRIVATE ur_client_library::urcl GTest::gtest_main)
277+
gtest_add_tests(TARGET types_tests
278+
)
279+
280+
add_executable(motion_primitives_tests test_motion_primitives.cpp)
281+
target_link_libraries(motion_primitives_tests PRIVATE ur_client_library::urcl GTest::gtest_main)
282+
gtest_add_tests(TARGET motion_primitives_tests
283+
)

tests/test_motion_primitives.cpp

Lines changed: 246 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,246 @@
1+
// -- BEGIN LICENSE BLOCK ----------------------------------------------
2+
// Copyright 2026 Universal Robots A/S
3+
//
4+
// Redistribution and use in source and binary forms, with or without
5+
// modification, are permitted provided that the following conditions are met:
6+
//
7+
// * Redistributions of source code must retain the above copyright
8+
// notice, this list of conditions and the following disclaimer.
9+
//
10+
// * Redistributions in binary form must reproduce the above copyright
11+
// notice, this list of conditions and the following disclaimer in the
12+
// documentation and/or other materials provided with the distribution.
13+
//
14+
// * Neither the name of the {copyright_holder} nor the names of its
15+
// contributors may be used to endorse or promote products derived from
16+
// this software without specific prior written permission.
17+
//
18+
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19+
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20+
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21+
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
22+
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23+
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24+
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25+
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26+
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27+
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28+
// POSSIBILITY OF SUCH DAMAGE.
29+
// -- END LICENSE BLOCK ------------------------------------------------
30+
31+
#include <gtest/gtest.h>
32+
33+
#include <cstdint>
34+
#include <variant>
35+
36+
#include <ur_client_library/control/motion_primitives.h>
37+
38+
using urcl::MotionTarget;
39+
using urcl::Pose;
40+
using urcl::Q;
41+
using urcl::vector6d_t;
42+
using urcl::control::MotionPrimitive;
43+
using urcl::control::MotionType;
44+
using urcl::control::motionTypeToString;
45+
using urcl::control::MoveCPrimitive;
46+
using urcl::control::MoveJPrimitive;
47+
using urcl::control::MoveLPrimitive;
48+
using urcl::control::MovePPrimitive;
49+
using urcl::control::OptimoveJPrimitive;
50+
using urcl::control::OptimoveLPrimitive;
51+
using urcl::control::SplinePrimitive;
52+
using urcl::control::TrajectorySplineType;
53+
54+
namespace
55+
{
56+
vector6d_t zeroJoints()
57+
{
58+
return { { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 } };
59+
}
60+
61+
Pose samplePose(const double x = 1.0)
62+
{
63+
return Pose(x, 2.0, 3.0, 0.1, 0.2, 0.3);
64+
}
65+
} // namespace
66+
67+
TEST(MotionTypeToStringTest, returns_expected_string_for_each_known_type)
68+
{
69+
EXPECT_EQ(motionTypeToString(MotionType::MOVEJ), "MOVEJ");
70+
EXPECT_EQ(motionTypeToString(MotionType::MOVEL), "MOVEL");
71+
EXPECT_EQ(motionTypeToString(MotionType::MOVEP), "MOVEP");
72+
EXPECT_EQ(motionTypeToString(MotionType::MOVEC), "MOVEC");
73+
EXPECT_EQ(motionTypeToString(MotionType::OPTIMOVEJ), "OPTIMOVEJ");
74+
EXPECT_EQ(motionTypeToString(MotionType::OPTIMOVEL), "OPTIMOVEL");
75+
EXPECT_EQ(motionTypeToString(MotionType::MOVEJ_POSE), "MOVEJ_POSE");
76+
EXPECT_EQ(motionTypeToString(MotionType::MOVEL_JOINT), "MOVEL_JOINT");
77+
EXPECT_EQ(motionTypeToString(MotionType::MOVEP_JOINT), "MOVEP_JOINT");
78+
EXPECT_EQ(motionTypeToString(MotionType::MOVEC_JOINT), "MOVEC_JOINT");
79+
EXPECT_EQ(motionTypeToString(MotionType::MOVEC_JOINT_POSE), "MOVEC_JOINT_POSE");
80+
EXPECT_EQ(motionTypeToString(MotionType::MOVEC_POSE_JOINT), "MOVEC_POSE_JOINT");
81+
EXPECT_EQ(motionTypeToString(MotionType::OPTIMOVEJ_POSE), "OPTIMOVEJ_POSE");
82+
EXPECT_EQ(motionTypeToString(MotionType::OPTIMOVEL_JOINT), "OPTIMOVEL_JOINT");
83+
EXPECT_EQ(motionTypeToString(MotionType::SPLINE), "SPLINE");
84+
}
85+
86+
TEST(MotionTypeToStringTest, returns_unknown_for_explicit_unknown_value)
87+
{
88+
EXPECT_EQ(motionTypeToString(MotionType::UNKNOWN), "UNKNOWN");
89+
}
90+
91+
TEST(MotionTypeToStringTest, returns_unknown_for_out_of_range_value)
92+
{
93+
const auto out_of_range = static_cast<MotionType>(static_cast<uint8_t>(200));
94+
EXPECT_EQ(motionTypeToString(out_of_range), "UNKNOWN");
95+
}
96+
97+
TEST(MotionPrimitiveTest, validate_accepts_defaults_and_non_negative_parameters)
98+
{
99+
MotionPrimitive mp;
100+
EXPECT_TRUE(mp.validate());
101+
EXPECT_DOUBLE_EQ(mp.blend_radius, 0.0);
102+
EXPECT_DOUBLE_EQ(mp.acceleration, 1.4);
103+
EXPECT_DOUBLE_EQ(mp.velocity, 1.04);
104+
}
105+
106+
TEST(MotionPrimitiveTest, validate_rejects_negative_blend_radius_acceleration_or_velocity)
107+
{
108+
MotionPrimitive bad_blend(-1.0);
109+
EXPECT_FALSE(bad_blend.validate());
110+
111+
MotionPrimitive bad_acc(0, std::chrono::milliseconds(0), -0.1);
112+
EXPECT_FALSE(bad_acc.validate());
113+
114+
MotionPrimitive bad_vel(0, std::chrono::milliseconds(0), 1.4, -1.0);
115+
EXPECT_FALSE(bad_vel.validate());
116+
}
117+
118+
TEST(MoveJPrimitiveTest, joint_vector_constructor_sets_movej_and_target)
119+
{
120+
const vector6d_t q{ { 0.1, 0.2, 0.3, 0.4, 0.5, 0.6 } };
121+
MoveJPrimitive mj(q);
122+
EXPECT_EQ(mj.type, MotionType::MOVEJ);
123+
ASSERT_TRUE(mj.getTarget().has_value());
124+
ASSERT_TRUE(std::holds_alternative<Q>(*mj.getTarget()));
125+
EXPECT_EQ(std::get<Q>(*mj.getTarget()), Q(q));
126+
}
127+
128+
TEST(MoveJPrimitiveTest, motion_target_q_vs_pose_sets_motion_type)
129+
{
130+
MoveJPrimitive from_q(MotionTarget(Q(0, 0, 0, 0, 0, 0)));
131+
EXPECT_EQ(from_q.type, MotionType::MOVEJ);
132+
133+
const MotionTarget pose_target{ samplePose() };
134+
MoveJPrimitive movej_from_pose(pose_target);
135+
EXPECT_EQ(movej_from_pose.type, MotionType::MOVEJ_POSE);
136+
ASSERT_TRUE(movej_from_pose.getTarget().has_value());
137+
ASSERT_TRUE(std::holds_alternative<Pose>(*movej_from_pose.getTarget()));
138+
}
139+
140+
TEST(MoveLPrimitiveTest, pose_and_motion_target_variants_set_motion_type)
141+
{
142+
MoveLPrimitive ml(samplePose());
143+
EXPECT_EQ(ml.type, MotionType::MOVEL);
144+
ASSERT_TRUE(ml.getTarget().has_value());
145+
ASSERT_TRUE(std::holds_alternative<Pose>(*ml.getTarget()));
146+
147+
MoveLPrimitive mlj(MotionTarget(Q(1, 2, 3, 4, 5, 6)));
148+
EXPECT_EQ(mlj.type, MotionType::MOVEL_JOINT);
149+
ASSERT_TRUE(std::holds_alternative<Q>(*mlj.getTarget()));
150+
}
151+
152+
TEST(MovePPrimitiveTest, pose_and_motion_target_variants_set_motion_type)
153+
{
154+
MovePPrimitive mp(samplePose());
155+
EXPECT_EQ(mp.type, MotionType::MOVEP);
156+
157+
MovePPrimitive mpj(MotionTarget(Q(0.1, 0.2, 0.3, 0.4, 0.5, 0.6)));
158+
EXPECT_EQ(mpj.type, MotionType::MOVEP_JOINT);
159+
}
160+
161+
TEST(MoveCPrimitiveTest, recomputes_motion_type_for_via_target_combinations)
162+
{
163+
const Pose via_p = samplePose(0.0);
164+
const Pose target_p = samplePose(10.0);
165+
const Q via_q(0, 0, 0, 0, 0, 0);
166+
const Q target_q(1, 1, 1, 1, 1, 1);
167+
168+
MoveCPrimitive pp(via_p, target_p);
169+
EXPECT_EQ(pp.type, MotionType::MOVEC);
170+
171+
MoveCPrimitive qq(via_q, target_q);
172+
EXPECT_EQ(qq.type, MotionType::MOVEC_JOINT);
173+
174+
MoveCPrimitive qp(via_q, target_p);
175+
EXPECT_EQ(qp.type, MotionType::MOVEC_POSE_JOINT);
176+
177+
MoveCPrimitive pq(via_p, target_q);
178+
EXPECT_EQ(pq.type, MotionType::MOVEC_JOINT_POSE);
179+
}
180+
181+
TEST(MoveCPrimitiveTest, set_via_and_set_target_update_type_and_get_via)
182+
{
183+
MoveCPrimitive mc(samplePose(0.0), samplePose(1.0));
184+
EXPECT_EQ(mc.type, MotionType::MOVEC);
185+
186+
mc.setVia(MotionTarget(Q(0, 0, 0, 0, 0, 0)));
187+
mc.setTarget(MotionTarget(Q(1, 1, 1, 1, 1, 1)));
188+
EXPECT_EQ(mc.type, MotionType::MOVEC_JOINT);
189+
190+
ASSERT_TRUE(std::holds_alternative<Q>(mc.getVia()));
191+
EXPECT_EQ(std::get<Q>(mc.getVia()), Q(0, 0, 0, 0, 0, 0));
192+
193+
EXPECT_EQ(mc.mode, 0);
194+
}
195+
196+
TEST(SplinePrimitiveTest, type_spline_and_getSplineType_cubic_vs_quintic)
197+
{
198+
const vector6d_t pos = zeroJoints();
199+
const vector6d_t vel = zeroJoints();
200+
201+
SplinePrimitive cubic(pos, vel, std::nullopt);
202+
EXPECT_EQ(cubic.type, MotionType::SPLINE);
203+
EXPECT_EQ(cubic.getSplineType(), TrajectorySplineType::SPLINE_CUBIC);
204+
EXPECT_TRUE(cubic.validate());
205+
206+
const vector6d_t acc = zeroJoints();
207+
SplinePrimitive quintic(pos, vel, acc);
208+
EXPECT_EQ(quintic.getSplineType(), TrajectorySplineType::SPLINE_QUINTIC);
209+
EXPECT_TRUE(quintic.validate());
210+
}
211+
212+
TEST(OptimoveJPrimitiveTest, validate_fraction_range_and_motion_types)
213+
{
214+
OptimoveJPrimitive ok(zeroJoints(), 0.0, 0.3, 0.3);
215+
EXPECT_TRUE(ok.validate());
216+
EXPECT_EQ(ok.type, MotionType::OPTIMOVEJ);
217+
218+
const MotionTarget pose_target{ samplePose() };
219+
OptimoveJPrimitive optj_from_pose(pose_target);
220+
EXPECT_EQ(optj_from_pose.type, MotionType::OPTIMOVEJ_POSE);
221+
222+
OptimoveJPrimitive bad_acc(zeroJoints(), 0.0, 0.0, 0.5);
223+
EXPECT_FALSE(bad_acc.validate());
224+
225+
OptimoveJPrimitive bad_acc_high(zeroJoints(), 0.0, 1.01, 0.5);
226+
EXPECT_FALSE(bad_acc_high.validate());
227+
228+
OptimoveJPrimitive bad_vel(zeroJoints(), 0.0, 0.5, 0.0);
229+
EXPECT_FALSE(bad_vel.validate());
230+
231+
OptimoveJPrimitive bad_vel_high(zeroJoints(), 0.0, 0.5, 1.01);
232+
EXPECT_FALSE(bad_vel_high.validate());
233+
}
234+
235+
TEST(OptimoveLPrimitiveTest, validate_fraction_range_and_motion_types)
236+
{
237+
OptimoveLPrimitive ok(samplePose(), 0.0, 0.3, 0.3);
238+
EXPECT_TRUE(ok.validate());
239+
EXPECT_EQ(ok.type, MotionType::OPTIMOVEL);
240+
241+
OptimoveLPrimitive from_q(MotionTarget(Q(0, 0, 0, 0, 0, 0)));
242+
EXPECT_EQ(from_q.type, MotionType::OPTIMOVEL_JOINT);
243+
244+
OptimoveLPrimitive bad(samplePose(), 0.0, 0.0, 0.5);
245+
EXPECT_FALSE(bad.validate());
246+
}

0 commit comments

Comments
 (0)