Refactor user codebase to use LoadResource helper method.

* Fix small bug when registering resource providers.
* Add test cases for OS filesystem fallback with VFS.
* Rewrite LoadModelFromString using a resource provider (now can pass custom VFSs).

PiperOrigin-RevId: 526959705
Change-Id: Iee1773e5584535d868aab6b206d1bf362e600c8a
This commit is contained in:
Kyle Bayes
2023-04-25 07:23:53 -07:00
committed by Copybara-Service
parent c2115a1f51
commit 23961e24b7
10 changed files with 446 additions and 274 deletions
+87 -7
View File
@@ -16,8 +16,8 @@
#include <array>
#include <cstddef>
#include <ostream>
#include <string>
#include <vector>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
@@ -25,6 +25,7 @@
#include <mujoco/mjmodel.h>
#include <mujoco/mjtnum.h>
#include <mujoco/mujoco.h>
#include "src/cc/array_safety.h"
#include "test/fixture.h"
namespace mujoco {
@@ -58,6 +59,7 @@ static const char* const kMalformedFaceOBJPath =
"user/testdata/malformed_face.xml";
using ::testing::HasSubstr;
using ::testing::IsNull;
// ------------- test invalid filenames ----------------------------------------
@@ -91,6 +93,83 @@ TEST_F(MjCMeshTest, UnknownMeshFormat) {
}
}
// -------------------- test OS filesystem fallback ----------------------------
TEST_F(MjCMeshTest, LoadMSHWithVFS) {
static constexpr char xml[] = R"(
<mujoco>
<asset>
<mesh name="mesh1" file="unknown_file.msh"/>
</asset>
<worldbody>
<geom type="mesh" mesh="mesh1"/>
</worldbody>
</mujoco>
)";
char error[1024];
size_t error_sz = 1024;
// load VFS on the heap
auto vfs = std::make_unique<mjVFS>();
mj_defaultVFS(vfs.get());
// should fallback to OS filesystem
mjModel* model = LoadModelFromString(xml, error, error_sz, vfs.get());
EXPECT_THAT(model, IsNull());
EXPECT_THAT(error, HasSubstr("resource not found via provider or OS filesystem"));
}
TEST_F(MjCMeshTest, LoadOBJWithVFS) {
static constexpr char xml[] = R"(
<mujoco>
<asset>
<mesh name="mesh1" file="unknown_file.obj"/>
</asset>
<worldbody>
<geom type="mesh" mesh="mesh1"/>
</worldbody>
</mujoco>
)";
char error[1024];
size_t error_sz = 1024;
// load VFS on the heap
auto vfs = std::make_unique<mjVFS>();
mj_defaultVFS(vfs.get());
// should fallback to OS filesystem
mjModel* model = LoadModelFromString(xml, error, error_sz, vfs.get());
EXPECT_THAT(model, IsNull());
EXPECT_THAT(error, HasSubstr("resource not found via provider or OS filesystem"));
}
TEST_F(MjCMeshTest, LoadSTLWithVFS) {
static constexpr char xml[] = R"(
<mujoco>
<asset>
<mesh name="mesh1" file="unknown_file.stl"/>
</asset>
<worldbody>
<geom type="mesh" mesh="mesh1"/>
</worldbody>
</mujoco>
)";
char error[1024];
size_t error_sz = 1024;
// load VFS on the heap
auto vfs = std::make_unique<mjVFS>();
mj_defaultVFS(vfs.get());
// should fallback to OS filesystem
mjModel* model = LoadModelFromString(xml, error, error_sz, vfs.get());
EXPECT_THAT(model, IsNull());
EXPECT_THAT(error, HasSubstr("resource not found via provider or OS filesystem"));
}
// ------------- test vertex de-duplication (STL) ------------------------------
TEST_F(MjCMeshTest, DeDuplicateSTLVertices) {
@@ -102,7 +181,8 @@ TEST_F(MjCMeshTest, DeDuplicateSTLVertices) {
mj_deleteModel(model);
}
// ------------- test Mesh loading (MSH) ------------------------------
// -------------------- test Mesh loading (MSH) --------------------------------
TEST_F(MjCMeshTest, LoadMSH) {
const std::string xml_path = GetTestDataFilePath(kMeshPath);
char error[1024];
@@ -184,7 +264,7 @@ TEST_F(MjCMeshTest, TinyMeshLoads) {
</worldbody>
</mujoco>
)";
mjModel* model = LoadModelFromString(xml, 0, 0);
mjModel* model = LoadModelFromString(xml);
ASSERT_THAT(model, testing::NotNull());
mj_deleteModel(model);
}
@@ -204,7 +284,7 @@ TEST_F(MjCMeshTest, FaceNormalAutogenerated) {
</worldbody>
</mujoco>
)";
mjModel* model = LoadModelFromString(xml, 0, 0);
mjModel* model = LoadModelFromString(xml);
ASSERT_THAT(model, testing::NotNull());
mj_deleteModel(model);
}
@@ -226,7 +306,7 @@ TEST_F(MjCMeshTest, SmallInertiaLoads) {
</worldbody>
</mujoco>
)";
mjModel* model = LoadModelFromString(xml, 0, 0);
mjModel* model = LoadModelFromString(xml);
ASSERT_THAT(model, testing::NotNull());
mj_deleteModel(model);
}
@@ -533,7 +613,7 @@ TEST_F(MjCMeshTest, ExactConcaveInertia) {
EXPECT_LE(fabs(model->body_mass[2] - m_concave_cube), max_abs_err);
EXPECT_LE(fabs(model->body_mass[3] - m_concave_cube), max_abs_err);
EXPECT_LE(fabs(model->body_mass[4] - m_concave_cube), max_abs_err);
for (int i=3; i<15; i+=3) {
for (int i = 3; i < 15; i += 3) {
EXPECT_LE(fabs(model->body_inertia[i] - I1), max_abs_err);
EXPECT_LE(fabs(model->body_inertia[i+1] - I2), max_abs_err);
EXPECT_LE(fabs(model->body_inertia[i+2] - I2), max_abs_err);
@@ -550,7 +630,7 @@ TEST_F(MjCMeshTest, ExactConvexInertia) {
mjtNum I_solid_cube = 1./6. * m_solid_cube;
EXPECT_LE(fabs(model->body_mass[1] - m_solid_cube), max_abs_err);
EXPECT_LE(fabs(model->body_mass[2] - m_solid_cube), max_abs_err);
for (int i=3; i<9; i++) {
for (int i = 3; i < 9; i++) {
EXPECT_LE(fabs(model->body_inertia[i] - I_solid_cube), max_abs_err);
}
mj_deleteModel(model);
+114
View File
@@ -14,15 +14,18 @@
// Tests for user/user_objects.cc.
#include <algorithm>
#include <array>
#include <cstddef>
#include <string>
#include <vector>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <mujoco/mjmodel.h>
#include <mujoco/mjtnum.h>
#include <mujoco/mujoco.h>
#include "src/cc/array_safety.h"
#include "test/fixture.h"
namespace mujoco {
@@ -37,6 +40,116 @@ using ::testing::HasSubstr;
using ::testing::IsNull;
using ::testing::NotNull;
// -------------------- test OS filesystem fallback ----------------------------
using VfsTest = MujocoTest;
TEST_F(VfsTest, HFieldPngWithVFS) {
static constexpr char xml[] = R"(
<mujoco>
<asset>
<hfield name="hfield" file="unknown_file.png" size="0.5 0.5 1 0.1"/>
</asset>
<worldbody>
<geom type="hfield" hfield="hfield" pos="-.4 .6 .05"/>
</worldbody>
</mujoco>
)";
char error[1024];
size_t error_sz = 1024;
// load VFS on the heap
auto vfs = std::make_unique<mjVFS>();
mj_defaultVFS(vfs.get());
// should fallback to OS filesystem
mjModel* model = LoadModelFromString(xml, error, error_sz, vfs.get());
EXPECT_THAT(model, IsNull());
EXPECT_THAT(error, HasSubstr("resource not found via provider or OS filesystem"));
}
TEST_F(VfsTest, HFieldCustomWithVFS) {
static constexpr char xml[] = R"(
<mujoco>
<asset>
<hfield name="hfield" file="unknown_file" size="0.5 0.5 1 0.1"/>
</asset>
<worldbody>
<geom type="hfield" hfield="hfield" pos="-.4 .6 .05"/>
</worldbody>
</mujoco>
)";
char error[1024];
size_t error_sz = 1024;
// load VFS on the heap
auto vfs = std::make_unique<mjVFS>();
mj_defaultVFS(vfs.get());
// should fallback to OS filesystem
mjModel* model = LoadModelFromString(xml, error, error_sz, vfs.get());
EXPECT_THAT(model, IsNull());
EXPECT_THAT(error, HasSubstr("resource not found via provider or OS filesystem"));
}
TEST_F(VfsTest, TexturePngWithVFS) {
static constexpr char xml[] = R"(
<mujoco>
<asset>
<texture name="texture" file="unknown_file.png" type="2d"/>
<material name="material" texture="texture"/>
</asset>
<worldbody>
<geom type="plane" material="material" size="4 4 4"/>
</worldbody>
</mujoco>
)";
char error[1024];
size_t error_sz = 1024;
// load VFS on the heap
auto vfs = std::make_unique<mjVFS>();
mj_defaultVFS(vfs.get());
// should fallback to OS filesystem
mjModel* model = LoadModelFromString(xml, error, error_sz, vfs.get());
EXPECT_THAT(model, IsNull());
EXPECT_THAT(error, HasSubstr("resource not found via provider or OS filesystem"));
}
TEST_F(VfsTest, TextureCustomWithVFS) {
static constexpr char xml[] = R"(
<mujoco>
<asset>
<texture name="texture" file="unknown_file" type="2d"/>
<material name="material" texture="texture"/>
</asset>
<worldbody>
<geom type="plane" material="material" size="4 4 4"/>
</worldbody>
</mujoco>
)";
char error[1024];
size_t error_sz = 1024;
// load VFS on the heap
auto vfs = std::make_unique<mjVFS>();
mj_defaultVFS(vfs.get());
// should fallback to OS filesystem
mjModel* model = LoadModelFromString(xml, error, error_sz, vfs.get());
EXPECT_THAT(model, IsNull());
EXPECT_THAT(error, HasSubstr("resource not found via provider or OS filesystem"));
}
// ------------------------ test keyframes -------------------------------------
using KeyframeTest = MujocoTest;
@@ -289,6 +402,7 @@ TEST_F(MjCGeomTest, CapsuleInertiaX) {
}
// ------------- test inertiagrouprange ----------------------------------------
TEST_F(MjCGeomTest, IgnoreGeomOutsideInertiagrouprange) {
static constexpr char xml[] = R"(
<mujoco>