Add policies for global attribute conflict resolution upon attach
For example when loading parent_merge.xml: ``` WARNING: Attach conflict when attaching 'child' to 'parent_merge', policy is 'merge' timestep: parent has 0.005, child has 0.002, taking the minimum iterations: parent has 50, child has 100, taking the maximum flag 'Damper': added from child ``` When loading parent_error.xml: ``` XML Error: Attach conflict when attaching 'child' to 'parent_error', policy is 'error' timestep: parent has 0.005, child has 0.002 iterations: parent has 50, child has 100 Element 'attach', line 10 ``` PiperOrigin-RevId: 933620810 Change-Id: Ib477863b5ef763474d27fb4be5a4148be1d5d500
This commit is contained in:
committed by
Copybara-Service
parent
30357c38a3
commit
410c73168c
@@ -99,6 +99,8 @@ std::vector<std::string> GetWriteReadTestModels() {
|
||||
// exclude files that fail since we do not save pinned flex nodes
|
||||
absl::StrContains(xml, "gripper_trilinear") ||
|
||||
absl::StrContains(xml, "strain") ||
|
||||
// exclude conflict test assets (designed to fail compile)
|
||||
absl::StrContains(xml, "xml/testdata/parent_") ||
|
||||
// exclude mjz testdata with VFS files
|
||||
absl::StrContains(xml, "mixed_test")) {
|
||||
continue;
|
||||
|
||||
Vendored
+10
@@ -0,0 +1,10 @@
|
||||
<mujoco model="child_mergable">
|
||||
<option timestep="0.002" iterations="100" gravity="0 0 -10">
|
||||
<flag damper="disable"/>
|
||||
</option>
|
||||
<worldbody>
|
||||
<body name="child_body">
|
||||
<geom name="child_geom" size="1"/>
|
||||
</body>
|
||||
</worldbody>
|
||||
</mujoco>
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
<mujoco model="child_unmergable">
|
||||
<option timestep="0.005" iterations="50" gravity="0 0 0">
|
||||
<flag damper="enable" constraint="disable"/>
|
||||
</option>
|
||||
<worldbody>
|
||||
<body name="child_body">
|
||||
<geom name="child_geom" size="1"/>
|
||||
</body>
|
||||
</worldbody>
|
||||
</mujoco>
|
||||
Vendored
+13
@@ -0,0 +1,13 @@
|
||||
<mujoco model="parent_error">
|
||||
<compiler conflict="error"/>
|
||||
<option timestep="0.005" iterations="50" gravity="0 0 -10"/>
|
||||
<asset>
|
||||
<model name="child" file="child_mergable.xml"/>
|
||||
</asset>
|
||||
<worldbody>
|
||||
<body name="parent_body">
|
||||
<geom name="parent_geom" size="2"/>
|
||||
<attach model="child" body="child_body" prefix="child/"/>
|
||||
</body>
|
||||
</worldbody>
|
||||
</mujoco>
|
||||
Vendored
+13
@@ -0,0 +1,13 @@
|
||||
<mujoco model="parent_merge">
|
||||
<compiler conflict="merge"/>
|
||||
<option timestep="0.005" iterations="50" gravity="0 0 -10"/>
|
||||
<asset>
|
||||
<model name="child" file="child_mergable.xml"/>
|
||||
</asset>
|
||||
<worldbody>
|
||||
<body name="parent_body">
|
||||
<geom name="parent_geom" size="2"/>
|
||||
<attach model="child" body="child_body" prefix="child/"/>
|
||||
</body>
|
||||
</worldbody>
|
||||
</mujoco>
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
<mujoco model="parent_merge_unmergable">
|
||||
<compiler conflict="merge"/>
|
||||
<option timestep="0.005" iterations="50" gravity="0 0 -10"/>
|
||||
<asset>
|
||||
<model name="child" file="child_unmergable.xml"/>
|
||||
</asset>
|
||||
<worldbody>
|
||||
<body name="parent_body">
|
||||
<geom name="parent_geom" size="2"/>
|
||||
<attach model="child" body="child_body" prefix="child/"/>
|
||||
</body>
|
||||
</worldbody>
|
||||
</mujoco>
|
||||
Vendored
+15
@@ -0,0 +1,15 @@
|
||||
<mujoco model="parent_warn">
|
||||
<compiler conflict="warning"/>
|
||||
<option timestep="0.005" iterations="50" gravity="0 0 -10">
|
||||
<flag damper="disable"/>
|
||||
</option>
|
||||
<asset>
|
||||
<model name="child" file="child_unmergable.xml"/>
|
||||
</asset>
|
||||
<worldbody>
|
||||
<body name="parent_body">
|
||||
<geom name="parent_geom" size="2"/>
|
||||
<attach model="child" body="child_body" prefix="child/"/>
|
||||
</body>
|
||||
</worldbody>
|
||||
</mujoco>
|
||||
@@ -3897,5 +3897,80 @@ TEST_F(ActuatorParseTest, DampingArmatureDefaultsPropagate) {
|
||||
mj_deleteModel(model);
|
||||
}
|
||||
|
||||
TEST_F(XMLReaderTest, AttachConflictXMLWarning) {
|
||||
mock_warning_handler.ExpectWarnings();
|
||||
std::array<char, 1024> error;
|
||||
std::string path = GetTestDataFilePath("xml/testdata/parent_warn.xml");
|
||||
mjSpec* spec = mj_parseXML(path.c_str(), nullptr, error.data(), error.size());
|
||||
ASSERT_THAT(spec, NotNull()) << error.data();
|
||||
|
||||
// one grouped warning per attach, containing gravity and damper flag
|
||||
// (constraint is only authored by child, not a conflict)
|
||||
EXPECT_EQ(mjs_numWarnings(spec), 1);
|
||||
std::string w = mjs_getWarning(spec, 0);
|
||||
EXPECT_THAT(w, HasSubstr("gravity: parent has 0 0 -10, child has 0 0 0,"
|
||||
" keeping parent value"));
|
||||
EXPECT_THAT(w, HasSubstr("flag 'Damper'"));
|
||||
|
||||
mjModel* m = mj_compile(spec, nullptr);
|
||||
ASSERT_THAT(m, NotNull());
|
||||
// Gravity should be parent's value (0 0 -10)
|
||||
EXPECT_MJTNUM_EQ(m->opt.gravity[0], 0);
|
||||
EXPECT_MJTNUM_EQ(m->opt.gravity[1], 0);
|
||||
EXPECT_MJTNUM_EQ(m->opt.gravity[2], -10);
|
||||
|
||||
mj_deleteModel(m);
|
||||
mj_deleteSpec(spec);
|
||||
}
|
||||
|
||||
TEST_F(XMLReaderTest, AttachConflictXMLMerge) {
|
||||
mock_warning_handler.ExpectWarnings();
|
||||
std::array<char, 1024> error;
|
||||
std::string path = GetTestDataFilePath("xml/testdata/parent_merge.xml");
|
||||
mjSpec* spec = mj_parseXML(path.c_str(), nullptr, error.data(), error.size());
|
||||
ASSERT_THAT(spec, NotNull()) << error.data();
|
||||
|
||||
// one grouped warning per attach, containing timestep, iterations, and flag
|
||||
EXPECT_GE(mjs_numWarnings(spec), 1);
|
||||
std::string w = mjs_getWarning(spec, 0);
|
||||
EXPECT_THAT(w, HasSubstr("timestep: parent has 0.005, child has 0.002,"
|
||||
" taking the minimum"));
|
||||
EXPECT_THAT(w, HasSubstr("iterations: parent has 50, child has 100,"
|
||||
" taking the maximum"));
|
||||
|
||||
mjModel* m = mj_compile(spec, nullptr);
|
||||
ASSERT_THAT(m, NotNull());
|
||||
// Timestep should be min (0.002)
|
||||
EXPECT_MJTNUM_EQ(m->opt.timestep, 0.002);
|
||||
// Iterations should be max (100)
|
||||
EXPECT_EQ(m->opt.iterations, 100);
|
||||
|
||||
mj_deleteModel(m);
|
||||
mj_deleteSpec(spec);
|
||||
}
|
||||
|
||||
TEST_F(XMLReaderTest, AttachConflictXMLError) {
|
||||
std::array<char, 1024> error;
|
||||
std::string path = GetTestDataFilePath("xml/testdata/parent_error.xml");
|
||||
mjSpec* spec = mj_parseXML(path.c_str(), nullptr, error.data(), error.size());
|
||||
|
||||
// Should fail to parse because of conflict in timestep in error mode
|
||||
EXPECT_THAT(spec, IsNull());
|
||||
EXPECT_THAT(error.data(),
|
||||
HasSubstr("timestep: parent has 0.005, child has 0.002"));
|
||||
}
|
||||
|
||||
TEST_F(XMLReaderTest, AttachConflictXMLMergeUnmergableError) {
|
||||
std::array<char, 1024> error;
|
||||
std::string path =
|
||||
GetTestDataFilePath("xml/testdata/parent_merge_unmergable.xml");
|
||||
mjSpec* spec = mj_parseXML(path.c_str(), nullptr, error.data(), error.size());
|
||||
|
||||
// Should fail to parse because gravity is unmergeable in merge mode
|
||||
EXPECT_THAT(spec, IsNull());
|
||||
EXPECT_THAT(error.data(),
|
||||
HasSubstr("gravity: parent has 0 0 -10, child has 0 0 0"));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace mujoco
|
||||
|
||||
@@ -45,29 +45,30 @@ std::vector<std::string> GetWriteReadTestModels() {
|
||||
if (p.path().extension() == ext) {
|
||||
std::string xml = p.path().string();
|
||||
if ( // if file is meant to fail, skip it
|
||||
absl::StrContains(xml, "malformed_") ||
|
||||
absl::StrContains(xml, "_fail") ||
|
||||
// exclude files that are too slow to load
|
||||
absl::StrContains(xml, "cow") ||
|
||||
absl::StrContains(xml, "gmsh_") ||
|
||||
absl::StrContains(xml, "shark_") ||
|
||||
absl::StrContains(xml, "perf") ||
|
||||
// exclude files that fail the comparison test
|
||||
absl::StrContains(xml, "rfcamera") ||
|
||||
absl::StrContains(xml, "tactile") ||
|
||||
absl::StrContains(xml, "makemesh") ||
|
||||
absl::StrContains(xml, "many_dependencies") ||
|
||||
absl::StrContains(xml, "usd") ||
|
||||
absl::StrContains(xml, "torus_maxhull") ||
|
||||
absl::StrContains(xml, "fitmesh_") ||
|
||||
absl::StrContains(xml, "lengthrange") ||
|
||||
absl::StrContains(xml, "hfield_xml") ||
|
||||
absl::StrContains(xml, "fromto_convex") ||
|
||||
absl::StrContains(xml, "cube_skin") ||
|
||||
absl::StrContains(xml, "cube_3x3x3") ||
|
||||
// exclude files that fail since we do not save pinned flex nodes
|
||||
absl::StrContains(xml, "gripper_trilinear") ||
|
||||
absl::StrContains(xml, "strain")) {
|
||||
absl::StrContains(xml, "malformed_") ||
|
||||
absl::StrContains(xml, "_fail") ||
|
||||
// exclude files that are too slow to load
|
||||
absl::StrContains(xml, "cow") || absl::StrContains(xml, "gmsh_") ||
|
||||
absl::StrContains(xml, "shark_") ||
|
||||
absl::StrContains(xml, "perf") ||
|
||||
// exclude files that fail the comparison test
|
||||
absl::StrContains(xml, "rfcamera") ||
|
||||
absl::StrContains(xml, "tactile") ||
|
||||
absl::StrContains(xml, "makemesh") ||
|
||||
absl::StrContains(xml, "many_dependencies") ||
|
||||
absl::StrContains(xml, "usd") ||
|
||||
absl::StrContains(xml, "torus_maxhull") ||
|
||||
absl::StrContains(xml, "fitmesh_") ||
|
||||
absl::StrContains(xml, "lengthrange") ||
|
||||
absl::StrContains(xml, "hfield_xml") ||
|
||||
absl::StrContains(xml, "fromto_convex") ||
|
||||
absl::StrContains(xml, "cube_skin") ||
|
||||
absl::StrContains(xml, "cube_3x3x3") ||
|
||||
// exclude files that fail since we do not save pinned flex nodes
|
||||
absl::StrContains(xml, "gripper_trilinear") ||
|
||||
absl::StrContains(xml, "strain") ||
|
||||
// exclude conflict tests (known option conflict warnings/errors)
|
||||
absl::StrContains(xml, "xml/testdata/parent_")) {
|
||||
continue;
|
||||
}
|
||||
models.push_back(xml);
|
||||
@@ -84,12 +85,6 @@ class WriteReadCompareTest : public XMLWriterTest,
|
||||
TEST_P(WriteReadCompareTest, WriteReadCompare) {
|
||||
std::string xml = GetParam();
|
||||
|
||||
// If this is the flex_line_obj model, expect the 'is not rigid' warning
|
||||
if (absl::StrContains(xml, "flex_line_obj")) {
|
||||
EXPECT_CALL(mock_warning_handler, Warn(testing::HasSubstr("is not rigid")))
|
||||
.WillRepeatedly(testing::Return());
|
||||
}
|
||||
|
||||
// full precision float printing
|
||||
FullFloatPrecision increase_precision;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user