diff --git a/test/engine/engine_vis_visualize_test.cc b/test/engine/engine_vis_visualize_test.cc index 4a9a6497..7e51c806 100644 --- a/test/engine/engine_vis_visualize_test.cc +++ b/test/engine/engine_vis_visualize_test.cc @@ -12,7 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include #include #include @@ -25,56 +24,86 @@ namespace mujoco { namespace { using ::testing::NotNull; -using MjvSceneTest = MujocoTest; - -constexpr int kMaxGeom = 10000; static const char* const kModelPath = "testdata/model.xml"; -TEST_F(MjvSceneTest, UpdateScene) { - for (const char* path : {kModelPath}) { - const std::string xml_path = GetTestDataFilePath(path); - mjModel* model = mj_loadXML(xml_path.c_str(), nullptr, 0, 0); - ASSERT_THAT(model, NotNull()) << "Failed to load model from " << path; - mjData* data = mj_makeData(model); +class MjvSceneTest : public MujocoTest { + protected: + static constexpr int kMaxGeom = 10000; - while (data->time < .2) { - mj_step(model, data); - } + void InitSceneObjects(mjModel* model, int maxgeom = kMaxGeom) { + mjv_defaultScene(&scn_); + mjv_makeScene(model, &scn_, maxgeom); + mjv_defaultOption(&opt_); + mjv_defaultPerturb(&pert_); + mjv_defaultFreeCamera(model, &cam_); - mjvScene scn; - mjv_defaultScene(&scn); - mjv_makeScene(model, &scn, kMaxGeom); - - mjvOption opt; - mjv_defaultOption(&opt); - - mjvPerturb pert; - mjv_defaultPerturb(&pert); - - mjvCamera cam; - mjv_defaultFreeCamera(model, &cam); - - // Enable all flags to exercise all code paths + // enable flags to exercise additional code paths for (int i = 0; i < mjNVISFLAG; ++i) { - opt.flags[i] = 1; + opt_.flags[i] = 1; } - - mjv_updateScene(model, data, &opt, &pert, &cam, mjCAT_ALL, &scn); - EXPECT_GT(scn.ngeom, 0); - if (model->nskin) EXPECT_GT(scn.nskin, 0); - EXPECT_GT(scn.nlight, 0); - - mjv_updateScene(model, data, &opt, &pert, &cam, mjCAT_ALL, &scn); - - // call mj_copyData to expose any memory leaks mjv_updateScene. - mjData* data_copy = mj_copyData(nullptr, model, data); - - mjv_freeScene(&scn); - mj_deleteData(data_copy); - mj_deleteData(data); - mj_deleteModel(model); } + + void FreeSceneObjects() { + mjv_freeScene(&scn_); + } + + mjvScene scn_; + mjvOption opt_; + mjvPerturb pert_; + mjvCamera cam_; +}; + +TEST_F(MjvSceneTest, UpdateScene) { + const std::string xml_path = GetTestDataFilePath(kModelPath); + mjModel* model = mj_loadXML(xml_path.c_str(), nullptr, 0, 0); + ASSERT_THAT(model, NotNull()) << "Failed to load model from " << kModelPath; + + InitSceneObjects(model); + + mjData* data = mj_makeData(model); + while (data->time < .2) { + mj_step(model, data); + } + + mjv_updateScene(model, data, &opt_, &pert_, &cam_, mjCAT_ALL, &scn_); + EXPECT_GT(scn_.ngeom, 0); + EXPECT_GT(scn_.nlight, 0); + if (model->nskin) EXPECT_GT(scn_.nskin, 0); + if (model->nflex) EXPECT_GT(scn_.nflex, 0); + + mjv_updateScene(model, data, &opt_, &pert_, &cam_, mjCAT_ALL, &scn_); + + // call mj_copyData to expose any memory leaks in mjv_updateScene + mjData* data_copy = mj_copyData(nullptr, model, data); + + mj_deleteData(data_copy); + mj_deleteData(data); + + FreeSceneObjects(); + mj_deleteModel(model); +} + +TEST_F(MjvSceneTest, UpdateSceneGeomsExhausted) { + const std::string xml_path = GetTestDataFilePath(kModelPath); + mjModel* model = mj_loadXML(xml_path.c_str(), nullptr, 0, 0); + ASSERT_THAT(model, NotNull()) << "Failed to load model from " << kModelPath; + + const int maxgeoms = 1; + InitSceneObjects(model, maxgeoms); + + mjData* data = mj_makeData(model); + mj_forward(model, data); + + // clear handlers to avoid test failure; we are explicitly expecting a warning + mju_clearHandlers(); + mjv_updateScene(model, data, &opt_, &pert_, &cam_, mjCAT_ALL, &scn_); + EXPECT_EQ(scn_.ngeom, maxgeoms); + EXPECT_EQ(data->warning[mjWARN_VGEOMFULL].number, 1); + + mj_deleteData(data); + FreeSceneObjects(); + mj_deleteModel(model); } } // namespace