Dynamically allocate contact and efc_ arrays on a new memory arena.
- Add private function `mj_arenaAlloc`. This is used internally to allocate memory from the arena. - Add private function `mj_nefc` to count constraints. This function returns a tight upper bound on `d->nefc`. The number of counted constraints can be slightly bigger than exact `d->nefc` in the case of constraints with empty Jacobian, as when placing a frictional tendon between two world sites. - Add new `memory` attribute to the `size` XML element for specification of arena memory size. This attribute is mutually exclusive with `nstack` and `njmax` specifications, which are now deprecated (but left around for the time being for legacy compatibility). - Move `d->stack` to the end of the new arena space. The stack now grows in reverse from the end. PiperOrigin-RevId: 479341539 Change-Id: Ie019c202e0908577ffc6f833a37920858116f667
This commit is contained in:
committed by
Copybara-Service
parent
4d85a464cc
commit
58fd72f53d
@@ -37,9 +37,108 @@ using ::testing::IsNan;
|
||||
using ::testing::IsNull;
|
||||
using ::testing::NotNull;
|
||||
|
||||
using UserDataTest = MujocoTest;
|
||||
using XMLReaderTest = MujocoTest;
|
||||
|
||||
TEST_F(UserDataTest, InvalidNUserBody) {
|
||||
TEST_F(XMLReaderTest, MemorySize) {
|
||||
std::array<char, 1024> error;
|
||||
{
|
||||
static constexpr char xml[] = R"(
|
||||
<mujoco>
|
||||
<size memory="128"/>
|
||||
</mujoco>
|
||||
)";
|
||||
mjModel* model = LoadModelFromString(xml, error.data(), error.size());
|
||||
ASSERT_THAT(model, NotNull());
|
||||
EXPECT_EQ(model->nstack, 128 / sizeof(mjtNum));
|
||||
mj_deleteModel(model);
|
||||
}
|
||||
{
|
||||
static constexpr char xml[] = R"(
|
||||
<mujoco>
|
||||
<size memory="1K "/>
|
||||
</mujoco>
|
||||
)";
|
||||
mjModel* model = LoadModelFromString(xml, error.data(), error.size());
|
||||
ASSERT_THAT(model, NotNull());
|
||||
EXPECT_EQ(model->nstack, 1024 / sizeof(mjtNum));
|
||||
mj_deleteModel(model);
|
||||
}
|
||||
{
|
||||
static constexpr char xml[] = R"(
|
||||
<mujoco>
|
||||
<size memory=" 10K"/>
|
||||
</mujoco>
|
||||
)";
|
||||
mjModel* model = LoadModelFromString(xml, error.data(), error.size());
|
||||
ASSERT_THAT(model, NotNull());
|
||||
EXPECT_EQ(model->nstack, 10240 / sizeof(mjtNum));
|
||||
mj_deleteModel(model);
|
||||
}
|
||||
{
|
||||
static constexpr char xml[] = R"(
|
||||
<mujoco>
|
||||
<size memory=" 4M "/>
|
||||
</mujoco>
|
||||
)";
|
||||
mjModel* model = LoadModelFromString(xml, error.data(), error.size());
|
||||
ASSERT_THAT(model, NotNull());
|
||||
EXPECT_EQ(model->nstack, 4*1024*1024 / sizeof(mjtNum));
|
||||
mj_deleteModel(model);
|
||||
}
|
||||
{
|
||||
static constexpr char xml[] = R"(
|
||||
<mujoco>
|
||||
<size memory="1G"/>
|
||||
</mujoco>
|
||||
)";
|
||||
mjModel* model = LoadModelFromString(xml, error.data(), error.size());
|
||||
ASSERT_THAT(model, NotNull());
|
||||
EXPECT_EQ(model->nstack, 1024*1024*1024 / sizeof(mjtNum));
|
||||
mj_deleteModel(model);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(XMLReaderTest, InvalidMemorySize) {
|
||||
std::array<char, 1024> error;
|
||||
{
|
||||
static constexpr char xml[] = R"(
|
||||
<mujoco>
|
||||
<size memory="-3"/>
|
||||
</mujoco>
|
||||
)";
|
||||
mjModel* model = LoadModelFromString(xml, error.data(), error.size());
|
||||
ASSERT_THAT(model, IsNull());
|
||||
}
|
||||
{
|
||||
static constexpr char xml[] = R"(
|
||||
<mujoco>
|
||||
<size memory="1 M"/>
|
||||
</mujoco>
|
||||
)";
|
||||
mjModel* model = LoadModelFromString(xml, error.data(), error.size());
|
||||
ASSERT_THAT(model, IsNull());
|
||||
}
|
||||
{
|
||||
static constexpr char xml[] = R"(
|
||||
<mujoco>
|
||||
<size memory="2X"/>
|
||||
</mujoco>
|
||||
)";
|
||||
mjModel* model = LoadModelFromString(xml, error.data(), error.size());
|
||||
ASSERT_THAT(model, IsNull());
|
||||
}
|
||||
{
|
||||
static constexpr char xml[] = R"(
|
||||
<mujoco>
|
||||
<size memory="K"/>
|
||||
</mujoco>
|
||||
)";
|
||||
mjModel* model = LoadModelFromString(xml, error.data(), error.size());
|
||||
ASSERT_THAT(model, IsNull());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(XMLReaderTest, InvalidNUserBody) {
|
||||
static constexpr char xml[] = R"(
|
||||
<mujoco>
|
||||
<size nuser_body="-2"/>
|
||||
@@ -51,7 +150,7 @@ TEST_F(UserDataTest, InvalidNUserBody) {
|
||||
EXPECT_THAT(error.data(), HasSubstr("nuser_body"));
|
||||
}
|
||||
|
||||
TEST_F(UserDataTest, InvalidNUserJoint) {
|
||||
TEST_F(XMLReaderTest, InvalidNUserJoint) {
|
||||
static constexpr char xml[] = R"(
|
||||
<mujoco>
|
||||
<size nuser_jnt="-2"/>
|
||||
@@ -63,7 +162,7 @@ TEST_F(UserDataTest, InvalidNUserJoint) {
|
||||
EXPECT_THAT(error.data(), HasSubstr("nuser_jnt"));
|
||||
}
|
||||
|
||||
TEST_F(UserDataTest, InvalidNUserGeom) {
|
||||
TEST_F(XMLReaderTest, InvalidNUserGeom) {
|
||||
static constexpr char xml[] = R"(
|
||||
<mujoco>
|
||||
<size nuser_geom="-2"/>
|
||||
@@ -75,7 +174,7 @@ TEST_F(UserDataTest, InvalidNUserGeom) {
|
||||
EXPECT_THAT(error.data(), HasSubstr("nuser_geom"));
|
||||
}
|
||||
|
||||
TEST_F(UserDataTest, InvalidNUserSite) {
|
||||
TEST_F(XMLReaderTest, InvalidNUserSite) {
|
||||
static constexpr char xml[] = R"(
|
||||
<mujoco>
|
||||
<size nuser_site="-2"/>
|
||||
@@ -87,7 +186,7 @@ TEST_F(UserDataTest, InvalidNUserSite) {
|
||||
EXPECT_THAT(error.data(), HasSubstr("nuser_site"));
|
||||
}
|
||||
|
||||
TEST_F(UserDataTest, InvalidNUserCamera) {
|
||||
TEST_F(XMLReaderTest, InvalidNUserCamera) {
|
||||
static constexpr char xml[] = R"(
|
||||
<mujoco>
|
||||
<size nuser_cam="-2"/>
|
||||
@@ -99,7 +198,7 @@ TEST_F(UserDataTest, InvalidNUserCamera) {
|
||||
EXPECT_THAT(error.data(), HasSubstr("nuser_cam"));
|
||||
}
|
||||
|
||||
TEST_F(UserDataTest, InvalidNUserTendon) {
|
||||
TEST_F(XMLReaderTest, InvalidNUserTendon) {
|
||||
static constexpr char xml[] = R"(
|
||||
<mujoco>
|
||||
<size nuser_tendon="-2"/>
|
||||
@@ -111,7 +210,7 @@ TEST_F(UserDataTest, InvalidNUserTendon) {
|
||||
EXPECT_THAT(error.data(), HasSubstr("nuser_tendon"));
|
||||
}
|
||||
|
||||
TEST_F(UserDataTest, InvalidNUserActuator) {
|
||||
TEST_F(XMLReaderTest, InvalidNUserActuator) {
|
||||
static constexpr char xml[] = R"(
|
||||
<mujoco>
|
||||
<size nuser_actuator="-2"/>
|
||||
@@ -123,7 +222,7 @@ TEST_F(UserDataTest, InvalidNUserActuator) {
|
||||
EXPECT_THAT(error.data(), HasSubstr("nuser_actuator"));
|
||||
}
|
||||
|
||||
TEST_F(UserDataTest, InvalidNUserSensor) {
|
||||
TEST_F(XMLReaderTest, InvalidNUserSensor) {
|
||||
static constexpr char xml[] = R"(
|
||||
<mujoco>
|
||||
<size nuser_sensor="-2"/>
|
||||
@@ -135,7 +234,7 @@ TEST_F(UserDataTest, InvalidNUserSensor) {
|
||||
EXPECT_THAT(error.data(), HasSubstr("nuser_sensor"));
|
||||
}
|
||||
|
||||
TEST_F(UserDataTest, CanParseInf) {
|
||||
TEST_F(XMLReaderTest, CanParseInf) {
|
||||
static constexpr char xml[] = R"(
|
||||
<mujoco>
|
||||
<worldbody>
|
||||
@@ -158,7 +257,7 @@ TEST_F(UserDataTest, CanParseInf) {
|
||||
mj_deleteModel(model);
|
||||
}
|
||||
|
||||
TEST_F(UserDataTest, CanParseNanAndRaisesWarning) {
|
||||
TEST_F(XMLReaderTest, CanParseNanAndRaisesWarning) {
|
||||
static constexpr char xml[] = R"(
|
||||
<mujoco>
|
||||
<worldbody>
|
||||
@@ -187,7 +286,7 @@ TEST_F(UserDataTest, CanParseNanAndRaisesWarning) {
|
||||
mj_deleteModel(model);
|
||||
}
|
||||
|
||||
TEST_F(UserDataTest, InvalidArrayElement) {
|
||||
TEST_F(XMLReaderTest, InvalidArrayElement) {
|
||||
static constexpr char xml[] = R"(
|
||||
<mujoco>
|
||||
<worldbody>
|
||||
@@ -203,7 +302,7 @@ TEST_F(UserDataTest, InvalidArrayElement) {
|
||||
EXPECT_THAT(error.data(), HasSubstr("problem reading attribute 'axisangle'"));
|
||||
}
|
||||
|
||||
TEST_F(UserDataTest, InvalidArrayLength) {
|
||||
TEST_F(XMLReaderTest, InvalidArrayLength) {
|
||||
static constexpr char xml[] = R"(
|
||||
<mujoco>
|
||||
<worldbody>
|
||||
@@ -219,7 +318,7 @@ TEST_F(UserDataTest, InvalidArrayLength) {
|
||||
EXPECT_THAT(error.data(), HasSubstr("has too much data"));
|
||||
}
|
||||
|
||||
TEST_F(UserDataTest, InvalidNumber) {
|
||||
TEST_F(XMLReaderTest, InvalidNumber) {
|
||||
static constexpr char xml[] = R"(
|
||||
<mujoco>
|
||||
<worldbody>
|
||||
@@ -235,7 +334,7 @@ TEST_F(UserDataTest, InvalidNumber) {
|
||||
EXPECT_THAT(error.data(), HasSubstr("problem reading attribute"));
|
||||
}
|
||||
|
||||
TEST_F(UserDataTest, AllowsSpaces) {
|
||||
TEST_F(XMLReaderTest, AllowsSpaces) {
|
||||
static constexpr char xml[] = R"(
|
||||
<mujoco>
|
||||
<worldbody>
|
||||
@@ -251,7 +350,7 @@ TEST_F(UserDataTest, AllowsSpaces) {
|
||||
mj_deleteModel(model);
|
||||
}
|
||||
|
||||
TEST_F(UserDataTest, InvalidDoubleOrientation) {
|
||||
TEST_F(XMLReaderTest, InvalidDoubleOrientation) {
|
||||
std::string prefix = "<mujoco><worldbody><";
|
||||
std::string suffix = "/></worldbody></mujoco>";
|
||||
std::vector<std::string> orientations = {
|
||||
@@ -270,15 +369,18 @@ TEST_F(UserDataTest, InvalidDoubleOrientation) {
|
||||
if (orient1 == orient2) continue;
|
||||
std::string xml = prefix + field + orient1 + orient2 + suffix;
|
||||
std::array<char, 1024> error;
|
||||
mjModel* model = LoadModelFromString(xml.c_str(), error.data(), error.size());
|
||||
mjModel* model =
|
||||
LoadModelFromString(xml.c_str(), error.data(), error.size());
|
||||
ASSERT_THAT(model, IsNull());
|
||||
EXPECT_THAT(error.data(), HasSubstr("multiple orientation specifiers for the same field"));
|
||||
EXPECT_THAT(
|
||||
error.data(),
|
||||
HasSubstr("multiple orientation specifiers for the same field"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(UserDataTest, InvalidInertialOrientation) {
|
||||
TEST_F(XMLReaderTest, InvalidInertialOrientation) {
|
||||
static constexpr char xml[] = R"(
|
||||
<mujoco>
|
||||
<worldbody>
|
||||
@@ -291,10 +393,11 @@ TEST_F(UserDataTest, InvalidInertialOrientation) {
|
||||
std::array<char, 1024> error;
|
||||
mjModel* model = LoadModelFromString(xml, error.data(), error.size());
|
||||
ASSERT_THAT(model, IsNull());
|
||||
EXPECT_THAT(error.data(), HasSubstr("multiple orientation specifiers for the same field"));
|
||||
EXPECT_THAT(error.data(),
|
||||
HasSubstr("multiple orientation specifiers for the same field"));
|
||||
}
|
||||
|
||||
TEST_F(UserDataTest, ReadShellParameter) {
|
||||
TEST_F(XMLReaderTest, ReadShellParameter) {
|
||||
static constexpr char xml[] = R"(
|
||||
<mujoco>
|
||||
<asset>
|
||||
@@ -313,7 +416,7 @@ TEST_F(UserDataTest, ReadShellParameter) {
|
||||
mj_deleteModel(model);
|
||||
}
|
||||
|
||||
TEST_F(UserDataTest, ReadsDamper) {
|
||||
TEST_F(XMLReaderTest, ReadsDamper) {
|
||||
static constexpr char xml[] = R"(
|
||||
<mujoco>
|
||||
<compiler autolimits="true"/>
|
||||
@@ -339,7 +442,7 @@ TEST_F(UserDataTest, ReadsDamper) {
|
||||
mj_deleteModel(model);
|
||||
}
|
||||
|
||||
TEST_F(UserDataTest, RequiresPoisitiveDamping) {
|
||||
TEST_F(XMLReaderTest, RequiresPoisitiveDamping) {
|
||||
static constexpr char xml[] = R"(
|
||||
<mujoco>
|
||||
<worldbody>
|
||||
@@ -356,10 +459,11 @@ TEST_F(UserDataTest, RequiresPoisitiveDamping) {
|
||||
std::array<char, 1024> error;
|
||||
mjModel* model = LoadModelFromString(xml, error.data(), error.size());
|
||||
ASSERT_THAT(model, IsNull());
|
||||
EXPECT_THAT(error.data(), HasSubstr("damping coefficient cannot be negative"));
|
||||
EXPECT_THAT(error.data(),
|
||||
HasSubstr("damping coefficient cannot be negative"));
|
||||
}
|
||||
|
||||
TEST_F(UserDataTest, RequiresControlRange) {
|
||||
TEST_F(XMLReaderTest, RequiresControlRange) {
|
||||
static constexpr char xml[] = R"(
|
||||
<mujoco>
|
||||
<worldbody>
|
||||
@@ -379,7 +483,7 @@ TEST_F(UserDataTest, RequiresControlRange) {
|
||||
EXPECT_THAT(error.data(), HasSubstr("invalid control range"));
|
||||
}
|
||||
|
||||
TEST_F(UserDataTest, PositiveControlRange) {
|
||||
TEST_F(XMLReaderTest, PositiveControlRange) {
|
||||
static constexpr char xml[] = R"(
|
||||
<mujoco>
|
||||
<worldbody>
|
||||
@@ -399,7 +503,7 @@ TEST_F(UserDataTest, PositiveControlRange) {
|
||||
EXPECT_THAT(error.data(), HasSubstr("control range cannot be negative"));
|
||||
}
|
||||
|
||||
TEST_F(UserDataTest, ReadsSkinGroups) {
|
||||
TEST_F(XMLReaderTest, ReadsSkinGroups) {
|
||||
static constexpr char xml[] = R"(
|
||||
<mujoco>
|
||||
<worldbody>
|
||||
@@ -430,7 +534,7 @@ TEST_F(UserDataTest, ReadsSkinGroups) {
|
||||
mj_deleteModel(model);
|
||||
}
|
||||
|
||||
TEST_F(UserDataTest, InvalidSkinGroup) {
|
||||
TEST_F(XMLReaderTest, InvalidSkinGroup) {
|
||||
static constexpr char xml[] = R"(
|
||||
<mujoco>
|
||||
<worldbody>
|
||||
@@ -446,7 +550,9 @@ TEST_F(UserDataTest, InvalidSkinGroup) {
|
||||
std::array<char, 1024> error;
|
||||
mjModel* model = LoadModelFromString(xml, error.data(), error.size());
|
||||
ASSERT_THAT(model, IsNull());
|
||||
EXPECT_THAT(error.data(), HasSubstr("skin group must be between 0 and 5\nElement 'skin', line 7"));
|
||||
EXPECT_THAT(
|
||||
error.data(),
|
||||
HasSubstr("skin group must be between 0 and 5\nElement 'skin', line 7"));
|
||||
mj_deleteModel(model);
|
||||
}
|
||||
|
||||
@@ -529,7 +635,8 @@ TEST_F(ActuatorTest, IncompleteActlimited) {
|
||||
std::array<char, 1024> error;
|
||||
mjModel* model = LoadModelFromString(xml, error.data(), error.size());
|
||||
ASSERT_THAT(model, IsNull());
|
||||
EXPECT_THAT(error.data(), HasSubstr("attribute 'actrange' does not have enough data"));
|
||||
EXPECT_THAT(error.data(),
|
||||
HasSubstr("attribute 'actrange' does not have enough data"));
|
||||
}
|
||||
|
||||
TEST_F(ActuatorTest, ReadsByte) {
|
||||
@@ -762,7 +869,7 @@ TEST_F(ActuatorParseTest, DampersDontRequireRange) {
|
||||
mj_deleteModel(model);
|
||||
}
|
||||
|
||||
TEST_F(UserDataTest, ZnearZeroNotAllowed) {
|
||||
TEST_F(XMLReaderTest, ZnearZeroNotAllowed) {
|
||||
static constexpr char xml[] = R"(
|
||||
<mujoco>
|
||||
<visual>
|
||||
@@ -776,7 +883,7 @@ TEST_F(UserDataTest, ZnearZeroNotAllowed) {
|
||||
EXPECT_THAT(error.data(), HasSubstr("znear must be strictly positive"));
|
||||
}
|
||||
|
||||
TEST_F(UserDataTest, ZnearNegativeNotAllowed) {
|
||||
TEST_F(XMLReaderTest, ZnearNegativeNotAllowed) {
|
||||
static constexpr char xml[] = R"(
|
||||
<mujoco>
|
||||
<visual>
|
||||
@@ -790,7 +897,7 @@ TEST_F(UserDataTest, ZnearNegativeNotAllowed) {
|
||||
EXPECT_THAT(error.data(), HasSubstr("znear must be strictly positive"));
|
||||
}
|
||||
|
||||
TEST_F(UserDataTest, ExtentZeroNotAllowed) {
|
||||
TEST_F(XMLReaderTest, ExtentZeroNotAllowed) {
|
||||
static constexpr char xml[] = R"(
|
||||
<mujoco>
|
||||
<statistic extent="0"/>
|
||||
@@ -802,7 +909,7 @@ TEST_F(UserDataTest, ExtentZeroNotAllowed) {
|
||||
EXPECT_THAT(error.data(), HasSubstr("extent must be strictly positive"));
|
||||
}
|
||||
|
||||
TEST_F(UserDataTest, ExtentNegativeNotAllowed) {
|
||||
TEST_F(XMLReaderTest, ExtentNegativeNotAllowed) {
|
||||
static constexpr char xml[] = R"(
|
||||
<mujoco>
|
||||
<statistic extent="-1"/>
|
||||
|
||||
@@ -52,6 +52,69 @@ using ::testing::NotNull;
|
||||
|
||||
using XMLWriterTest = MujocoTest;
|
||||
|
||||
TEST_F(XMLWriterTest, SavesMemory) {
|
||||
{
|
||||
static constexpr char xml[] = R"(
|
||||
<mujoco>
|
||||
<size memory=" 1023 "/>
|
||||
</mujoco>
|
||||
)";
|
||||
mjModel* model = LoadModelFromString(xml);
|
||||
ASSERT_THAT(model, NotNull());
|
||||
std::string saved_xml = SaveAndReadXml(model);
|
||||
EXPECT_THAT(saved_xml, HasSubstr("memory=\"1023\""));
|
||||
mj_deleteModel(model);
|
||||
}
|
||||
{
|
||||
static constexpr char xml[] = R"(
|
||||
<mujoco>
|
||||
<size memory="1024"/>
|
||||
</mujoco>
|
||||
)";
|
||||
mjModel* model = LoadModelFromString(xml);
|
||||
ASSERT_THAT(model, NotNull());
|
||||
std::string saved_xml = SaveAndReadXml(model);
|
||||
EXPECT_THAT(saved_xml, HasSubstr("memory=\"1K\""));
|
||||
mj_deleteModel(model);
|
||||
}
|
||||
{
|
||||
static constexpr char xml[] = R"(
|
||||
<mujoco>
|
||||
<size memory="4096"/>
|
||||
</mujoco>
|
||||
)";
|
||||
mjModel* model = LoadModelFromString(xml);
|
||||
ASSERT_THAT(model, NotNull());
|
||||
std::string saved_xml = SaveAndReadXml(model);
|
||||
EXPECT_THAT(saved_xml, HasSubstr("memory=\"4K\""));
|
||||
mj_deleteModel(model);
|
||||
}
|
||||
{
|
||||
static constexpr char xml[] = R"(
|
||||
<mujoco>
|
||||
<size memory="1048576"/>
|
||||
</mujoco>
|
||||
)";
|
||||
mjModel* model = LoadModelFromString(xml);
|
||||
ASSERT_THAT(model, NotNull());
|
||||
std::string saved_xml = SaveAndReadXml(model);
|
||||
EXPECT_THAT(saved_xml, HasSubstr("memory=\"1M\""));
|
||||
mj_deleteModel(model);
|
||||
}
|
||||
{
|
||||
static constexpr char xml[] = R"(
|
||||
<mujoco>
|
||||
<size memory="1047552"/>
|
||||
</mujoco>
|
||||
)";
|
||||
mjModel* model = LoadModelFromString(xml);
|
||||
ASSERT_THAT(model, NotNull());
|
||||
std::string saved_xml = SaveAndReadXml(model);
|
||||
EXPECT_THAT(saved_xml, HasSubstr("memory=\"1023K\""));
|
||||
mj_deleteModel(model);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(XMLWriterTest, SavesDisableSensor) {
|
||||
static constexpr char xml[] = R"(
|
||||
<mujoco>
|
||||
@@ -118,6 +181,7 @@ TEST_F(XMLWriterTest, NotAddsInertial) {
|
||||
</mujoco>
|
||||
)";
|
||||
mjModel* model = LoadModelFromString(xml);
|
||||
ASSERT_THAT(model, NotNull());
|
||||
std::string saved_xml = SaveAndReadXml(model);
|
||||
EXPECT_THAT(saved_xml, Not(HasSubstr("inertial")));
|
||||
mj_deleteModel(model);
|
||||
@@ -136,6 +200,7 @@ TEST_F(XMLWriterTest, DropsInertialIfFromGeom) {
|
||||
</mujoco>
|
||||
)";
|
||||
mjModel* model = LoadModelFromString(xml);
|
||||
ASSERT_THAT(model, NotNull());
|
||||
std::string saved_xml = SaveAndReadXml(model);
|
||||
EXPECT_THAT(saved_xml, Not(HasSubstr("inertial")));
|
||||
mj_deleteModel(model);
|
||||
@@ -154,6 +219,7 @@ TEST_F(XMLWriterTest, DoesNotKeepInferredJointLimited) {
|
||||
</mujoco>
|
||||
)";
|
||||
mjModel* model = LoadModelFromString(xml);
|
||||
ASSERT_THAT(model, NotNull());
|
||||
std::string saved_xml = SaveAndReadXml(model);
|
||||
EXPECT_THAT(saved_xml, HasSubstr("range=\"-1 1\""));
|
||||
EXPECT_THAT(saved_xml, Not(HasSubstr("limited=\"true\"")));
|
||||
@@ -173,6 +239,7 @@ TEST_F(XMLWriterTest, DoesNotKeepExplicitJointLimitedIfAutoLimits) {
|
||||
</mujoco>
|
||||
)";
|
||||
mjModel* model = LoadModelFromString(xml);
|
||||
ASSERT_THAT(model, NotNull());
|
||||
std::string saved_xml = SaveAndReadXml(model);
|
||||
EXPECT_THAT(saved_xml, HasSubstr("autolimits=\"true\""));
|
||||
EXPECT_THAT(saved_xml, HasSubstr("range=\"-1 1\""));
|
||||
@@ -193,6 +260,7 @@ TEST_F(XMLWriterTest, KeepsJointLimitedFalseIfAutoLimits) {
|
||||
</mujoco>
|
||||
)";
|
||||
mjModel* model = LoadModelFromString(xml);
|
||||
ASSERT_THAT(model, NotNull());
|
||||
std::string saved_xml = SaveAndReadXml(model);
|
||||
EXPECT_THAT(saved_xml, HasSubstr("limited=\"false\" range=\"-1 1\""));
|
||||
mj_deleteModel(model);
|
||||
@@ -219,6 +287,7 @@ TEST_F(XMLWriterTest, DoesNotKeepInferredTendonLimited) {
|
||||
</mujoco>
|
||||
)";
|
||||
mjModel* model = LoadModelFromString(xml);
|
||||
ASSERT_THAT(model, NotNull());
|
||||
std::string saved_xml = SaveAndReadXml(model);
|
||||
EXPECT_THAT(saved_xml, HasSubstr("autolimits=\"true\""));
|
||||
EXPECT_THAT(saved_xml, HasSubstr("range=\"-1 1\""));
|
||||
@@ -247,6 +316,7 @@ TEST_F(XMLWriterTest, DoesNotKeepExplicitTendonLimitedIfAutoLimits) {
|
||||
</mujoco>
|
||||
)";
|
||||
mjModel* model = LoadModelFromString(xml);
|
||||
ASSERT_THAT(model, NotNull());
|
||||
std::string saved_xml = SaveAndReadXml(model);
|
||||
EXPECT_THAT(saved_xml, HasSubstr("autolimits=\"true\""));
|
||||
EXPECT_THAT(saved_xml, HasSubstr("range=\"-1 1\""));
|
||||
@@ -275,6 +345,7 @@ TEST_F(XMLWriterTest, KeepsTendonLimitedFalseIfAutoLimits) {
|
||||
</mujoco>
|
||||
)";
|
||||
mjModel* model = LoadModelFromString(xml);
|
||||
ASSERT_THAT(model, NotNull());
|
||||
std::string saved_xml = SaveAndReadXml(model);
|
||||
EXPECT_THAT(saved_xml, HasSubstr("limited=\"false\" range=\"-1 1\""));
|
||||
mj_deleteModel(model);
|
||||
@@ -296,6 +367,7 @@ TEST_F(XMLWriterTest, DoesNotKeepInferredActlimited) {
|
||||
</mujoco>
|
||||
)";
|
||||
mjModel* model = LoadModelFromString(xml);
|
||||
ASSERT_THAT(model, NotNull());
|
||||
std::string saved_xml = SaveAndReadXml(model);
|
||||
EXPECT_THAT(saved_xml, HasSubstr("autolimits=\"true\""));
|
||||
EXPECT_THAT(saved_xml, HasSubstr("actrange=\"-1 1\""));
|
||||
@@ -319,6 +391,7 @@ TEST_F(XMLWriterTest, DoesNotKeepExplicitActlimitedIfAutoLimits) {
|
||||
</mujoco>
|
||||
)";
|
||||
mjModel* model = LoadModelFromString(xml);
|
||||
ASSERT_THAT(model, NotNull());
|
||||
std::string saved_xml = SaveAndReadXml(model);
|
||||
EXPECT_THAT(saved_xml, HasSubstr("autolimits=\"true\""));
|
||||
EXPECT_THAT(saved_xml, HasSubstr("actrange=\"-1 1\""));
|
||||
@@ -342,6 +415,7 @@ TEST_F(XMLWriterTest, KeepsActlimitedFalse) {
|
||||
</mujoco>
|
||||
)";
|
||||
mjModel* model = LoadModelFromString(xml);
|
||||
ASSERT_THAT(model, NotNull());
|
||||
std::string saved_xml = SaveAndReadXml(model);
|
||||
EXPECT_THAT(saved_xml, HasSubstr("actlimited=\"false\" actrange=\"-1 1\""));
|
||||
mj_deleteModel(model);
|
||||
@@ -363,6 +437,7 @@ TEST_F(XMLWriterTest, DoesNotKeepInferredCtrllimited) {
|
||||
</mujoco>
|
||||
)";
|
||||
mjModel* model = LoadModelFromString(xml);
|
||||
ASSERT_THAT(model, NotNull());
|
||||
std::string saved_xml = SaveAndReadXml(model);
|
||||
EXPECT_THAT(saved_xml, HasSubstr("ctrlrange=\"-1 1\""));
|
||||
EXPECT_THAT(saved_xml, Not(HasSubstr("ctrllimited=\"true\"")));
|
||||
@@ -385,6 +460,7 @@ TEST_F(XMLWriterTest, DoesNotKeepExplicitCtrllimitedIfAutoLimits) {
|
||||
</mujoco>
|
||||
)";
|
||||
mjModel* model = LoadModelFromString(xml);
|
||||
ASSERT_THAT(model, NotNull());
|
||||
std::string saved_xml = SaveAndReadXml(model);
|
||||
EXPECT_THAT(saved_xml, HasSubstr("ctrlrange=\"-1 1\""));
|
||||
EXPECT_THAT(saved_xml, Not(HasSubstr("ctrllimited=\"true\"")));
|
||||
@@ -406,6 +482,7 @@ TEST_F(XMLWriterTest, KeepsCtrllimitedFalse) {
|
||||
</mujoco>
|
||||
)";
|
||||
mjModel* model = LoadModelFromString(xml);
|
||||
ASSERT_THAT(model, NotNull());
|
||||
std::string saved_xml = SaveAndReadXml(model);
|
||||
EXPECT_THAT(saved_xml, HasSubstr("ctrllimited=\"false\" ctrlrange=\"-1 1\""));
|
||||
mj_deleteModel(model);
|
||||
@@ -427,6 +504,7 @@ TEST_F(XMLWriterTest, DoesNotKeepInferredForcelimited) {
|
||||
</mujoco>
|
||||
)";
|
||||
mjModel* model = LoadModelFromString(xml);
|
||||
ASSERT_THAT(model, NotNull());
|
||||
std::string saved_xml = SaveAndReadXml(model);
|
||||
EXPECT_THAT(saved_xml, HasSubstr("autolimits=\"true\""));
|
||||
EXPECT_THAT(saved_xml, HasSubstr("forcerange=\"-1 1\""));
|
||||
@@ -449,6 +527,7 @@ TEST_F(XMLWriterTest, DoesNotKeepExplicitForcelimited) {
|
||||
</mujoco>
|
||||
)";
|
||||
mjModel* model = LoadModelFromString(xml);
|
||||
ASSERT_THAT(model, NotNull());
|
||||
std::string saved_xml = SaveAndReadXml(model);
|
||||
EXPECT_THAT(saved_xml, HasSubstr("autolimits=\"true\""));
|
||||
EXPECT_THAT(saved_xml, HasSubstr("forcerange=\"-1 1\""));
|
||||
@@ -471,8 +550,10 @@ TEST_F(XMLWriterTest, KeepsForcelimitedFalse) {
|
||||
</mujoco>
|
||||
)";
|
||||
mjModel* model = LoadModelFromString(xml);
|
||||
ASSERT_THAT(model, NotNull());
|
||||
std::string saved_xml = SaveAndReadXml(model);
|
||||
EXPECT_THAT(saved_xml, HasSubstr("forcelimited=\"false\" forcerange=\"-1 1\""));
|
||||
EXPECT_THAT(saved_xml,
|
||||
HasSubstr("forcelimited=\"false\" forcerange=\"-1 1\""));
|
||||
mj_deleteModel(model);
|
||||
}
|
||||
|
||||
@@ -487,6 +568,7 @@ TEST_F(XMLWriterTest, UndefinedMassDensity) {
|
||||
</mujoco>
|
||||
)";
|
||||
mjModel* model = LoadModelFromString(xml);
|
||||
ASSERT_THAT(model, NotNull());
|
||||
std::string saved_xml = SaveAndReadXml(model);
|
||||
EXPECT_THAT(saved_xml, Not(HasSubstr("density")));
|
||||
EXPECT_THAT(saved_xml, Not(HasSubstr("mass")));
|
||||
@@ -507,6 +589,7 @@ TEST_F(XMLWriterTest, WritesDefaults) {
|
||||
</mujoco>
|
||||
)";
|
||||
mjModel* model = LoadModelFromString(xml);
|
||||
ASSERT_THAT(model, NotNull());
|
||||
std::string saved_xml = SaveAndReadXml(model);
|
||||
EXPECT_THAT(saved_xml, Not(HasSubstr("mass")));
|
||||
EXPECT_THAT(saved_xml, HasSubstr("<geom density=\"100\"/>"));
|
||||
@@ -524,6 +607,7 @@ TEST_F(XMLWriterTest, WritesDensity) {
|
||||
</mujoco>
|
||||
)";
|
||||
mjModel* model = LoadModelFromString(xml);
|
||||
ASSERT_THAT(model, NotNull());
|
||||
std::string saved_xml = SaveAndReadXml(model);
|
||||
EXPECT_THAT(saved_xml, HasSubstr("density=\"100\""));
|
||||
EXPECT_THAT(saved_xml, Not(HasSubstr("mass")));
|
||||
@@ -541,6 +625,7 @@ TEST_F(XMLWriterTest, WritesMass) {
|
||||
</mujoco>
|
||||
)";
|
||||
mjModel* model = LoadModelFromString(xml);
|
||||
ASSERT_THAT(model, NotNull());
|
||||
std::string saved_xml = SaveAndReadXml(model);
|
||||
EXPECT_THAT(saved_xml, Not(HasSubstr("density")));
|
||||
EXPECT_THAT(saved_xml, HasSubstr("mass=\"0.1\""));
|
||||
@@ -558,6 +643,7 @@ TEST_F(XMLWriterTest, ZeroMass) {
|
||||
</mujoco>
|
||||
)";
|
||||
mjModel* model = LoadModelFromString(xml);
|
||||
ASSERT_THAT(model, NotNull());
|
||||
std::string saved_xml = SaveAndReadXml(model);
|
||||
EXPECT_THAT(saved_xml, Not(HasSubstr("density")));
|
||||
EXPECT_THAT(saved_xml, HasSubstr("mass=\"0\""));
|
||||
@@ -575,6 +661,7 @@ TEST_F(XMLWriterTest, OverwritesDensity) {
|
||||
</mujoco>
|
||||
)";
|
||||
mjModel* model = LoadModelFromString(xml);
|
||||
ASSERT_THAT(model, NotNull());
|
||||
std::string saved_xml = SaveAndReadXml(model);
|
||||
EXPECT_THAT(saved_xml, Not(HasSubstr("density")));
|
||||
EXPECT_THAT(saved_xml, HasSubstr("mass=\"100\""));
|
||||
@@ -589,6 +676,7 @@ TEST_F(XMLWriterTest, UsesTwoSpaces) {
|
||||
</mujoco>
|
||||
)";
|
||||
mjModel* model = LoadModelFromString(xml);
|
||||
ASSERT_THAT(model, NotNull());
|
||||
std::string saved_xml = SaveAndReadXml(model);
|
||||
EXPECT_THAT(saved_xml, HasSubstr(" "));
|
||||
EXPECT_THAT(saved_xml, Not(HasSubstr(" ")));
|
||||
@@ -609,9 +697,13 @@ TEST_F(XMLWriterTest, WritesSkin) {
|
||||
</mujoco>
|
||||
)";
|
||||
mjModel* model = LoadModelFromString(xml);
|
||||
mjModel* mtemp = LoadModelFromString(SaveAndReadXml(model));
|
||||
ASSERT_THAT(model, NotNull());
|
||||
EXPECT_THAT(model->nskin, 1);
|
||||
|
||||
mjModel* mtemp = LoadModelFromString(SaveAndReadXml(model));
|
||||
ASSERT_THAT(mtemp, NotNull());
|
||||
EXPECT_THAT(mtemp->nskin, 1);
|
||||
|
||||
mj_deleteModel(model);
|
||||
mj_deleteModel(mtemp);
|
||||
}
|
||||
@@ -626,12 +718,17 @@ TEST_F(XMLWriterTest, SetPrecision) {
|
||||
</mujoco>
|
||||
)";
|
||||
mjModel* model = LoadModelFromString(xml);
|
||||
ASSERT_THAT(model, NotNull());
|
||||
|
||||
// save to XML and re-load, expect to lose precision
|
||||
mjModel* model_lo = LoadModelFromString(SaveAndReadXml(model));
|
||||
ASSERT_THAT(model_lo, NotNull());
|
||||
|
||||
EXPECT_EQ(model->geom_size[1], model_lo->geom_size[1]);
|
||||
EXPECT_NE(model->geom_size[2], model_lo->geom_size[2]);
|
||||
{
|
||||
// save to XML and re-load with FullFloatPrecision, expect to maintain precision
|
||||
// save to XML and re-load with FullFloatPrecision
|
||||
// expect to maintain precision
|
||||
FullFloatPrecision increase_precision;
|
||||
mjModel* model_hi = LoadModelFromString(SaveAndReadXml(model));
|
||||
EXPECT_EQ(model->geom_size[2], model_hi->geom_size[2]);
|
||||
@@ -664,6 +761,7 @@ TEST_F(XMLWriterLocaleTest, IgnoresLocale) {
|
||||
</mujoco>
|
||||
)";
|
||||
mjModel* model = LoadModelFromString(xml);
|
||||
ASSERT_THAT(model, NotNull());
|
||||
std::string saved_xml = SaveAndReadXml(model);
|
||||
EXPECT_THAT(saved_xml, HasSubstr("0.1 1.23 2.345"));
|
||||
mj_deleteModel(model);
|
||||
@@ -675,7 +773,7 @@ TEST_F(XMLWriterLocaleTest, IgnoresLocale) {
|
||||
}
|
||||
|
||||
|
||||
// ------------------------ test loading and saving multiple files ---------------------------------
|
||||
// ------------------- test loading and saving multiple files ------------------
|
||||
namespace mju = ::mujoco::util;
|
||||
static constexpr int kFieldSize = 500;
|
||||
|
||||
@@ -695,10 +793,12 @@ template<typename T = mjtNum> T Compare(T val1, T val2) {
|
||||
return error < 2*10*std::numeric_limits<T>::epsilon() ? 0 : error;
|
||||
}
|
||||
|
||||
mjtNum CompareModel(const mjModel* m1, const mjModel* m2, char (&field)[kFieldSize]) {
|
||||
mjtNum CompareModel(const mjModel* m1, const mjModel* m2,
|
||||
char (&field)[kFieldSize]) {
|
||||
mjtNum dif, maxdif = 0.0;
|
||||
|
||||
// define symbols corresponding to number of columns (needed in MJMODEL_POINTERS)
|
||||
// define symbols corresponding to number of columns
|
||||
// (needed in MJMODEL_POINTERS)
|
||||
MJMODEL_POINTERS_PREAMBLE(m1);
|
||||
|
||||
// compare ints
|
||||
@@ -739,7 +839,8 @@ mjtNum CompareModel(const mjModel* m1, const mjModel* m2, char (&field)[kFieldSi
|
||||
TEST_F(XMLWriterTest, WriteReadCompare) {
|
||||
FullFloatPrecision increase_precision;
|
||||
// Loop over all xml files in data
|
||||
std::vector<std::string> paths = {GetTestDataFilePath("."), GetModelPath(".")};
|
||||
std::vector<std::string> paths = {GetTestDataFilePath("."),
|
||||
GetModelPath(".")};
|
||||
std::string ext(".xml");
|
||||
for (auto const& path : paths) {
|
||||
for (auto &p : std::filesystem::recursive_directory_iterator(path)) {
|
||||
@@ -753,15 +854,18 @@ TEST_F(XMLWriterTest, WriteReadCompare) {
|
||||
|
||||
// load model
|
||||
std::array<char, 1000> error;
|
||||
mjModel* m = mj_loadXML(xml.c_str(), nullptr, error.data(), error.size());
|
||||
ASSERT_THAT(m, NotNull()) << "Failed to load " << xml.c_str() << ": " << error.data();
|
||||
mjModel* m = mj_loadXML(
|
||||
xml.c_str(), nullptr, error.data(), error.size());
|
||||
ASSERT_THAT(m, NotNull())
|
||||
<< "Failed to load " << xml.c_str() << ": " << error.data();
|
||||
|
||||
// make data
|
||||
mjData* d = mj_makeData(m);
|
||||
ASSERT_THAT(d, testing::NotNull()) << "Failed to create data" << std::endl;
|
||||
ASSERT_THAT(d, testing::NotNull()) << "Failed to create data\n";
|
||||
|
||||
// save and load back
|
||||
mjModel* mtemp = LoadModelFromString(SaveAndReadXml(m), error.data(), error.size());
|
||||
mjModel* mtemp =
|
||||
LoadModelFromString(SaveAndReadXml(m), error.data(), error.size());
|
||||
|
||||
if (!mtemp) {
|
||||
// if failing because assets are missing, accept the test
|
||||
@@ -770,9 +874,9 @@ TEST_F(XMLWriterTest, WriteReadCompare) {
|
||||
// compare and delete
|
||||
char field[kFieldSize] = "";
|
||||
mjtNum result = CompareModel(m, mtemp, field);
|
||||
EXPECT_LE(result, 0) << "Loaded and saved models are different!" << std::endl
|
||||
<< "Affected file " << p.path().string() << std::endl
|
||||
<< "Different field: " << field << std::endl;
|
||||
EXPECT_LE(result, 0) << "Loaded and saved models are different!\n"
|
||||
<< "Affected file " << p.path().string() << '\n'
|
||||
<< "Different field: " << field << '\n';
|
||||
mj_deleteModel(mtemp);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user