diff --git a/src/engine/engine_resource.c b/src/engine/engine_resource.c index 79a6a9b4..107bf3b4 100644 --- a/src/engine/engine_resource.c +++ b/src/engine/engine_resource.c @@ -34,6 +34,7 @@ #include #include "engine/engine_plugin.h" #include "engine/engine_util_errmem.h" +#include "engine/engine_util_misc.h" // internal helper for mju_fileToMemory (closes fp automatically) static void* _fileToMemory(FILE* fp, const char* filename, size_t* filesize); @@ -113,10 +114,8 @@ mjResource* mju_openResource(const char* name, char* error, size_t error_sz) { memcpy(&spec->mtime, &file_stat.st_mtime, sizeof(time_t)); } else { memset(&spec->mtime, 0, sizeof(time_t)); - resource->timestamp[0] = '\0'; } - strftime(resource->timestamp, 512, "%Y-%m-%d-%H:%M:%S", - localtime(&(spec->mtime))); + mju_encodeBase64(resource->timestamp, (uint8_t*) &spec->mtime, sizeof(time_t)); return resource; } @@ -199,19 +198,6 @@ void mju_getResourceDir(mjResource* resource, const char** dir, int* ndir) { -// modified callback for OS filesystem -static int mju_isModifiedFile(const char* name, const file_spec* spec) { - if (spec != NULL) { - struct stat file_stat; - if (stat(name, &file_stat) == 0) { - return difftime(spec->mtime, file_stat.st_mtime) < 0; - } - } - return 1; // modified (default) -} - - - // return 0 if the resource's timestamp matches the provided timestamp // return > 0 if the the resource is younger than the given timestamp // return < 0 if the resource is older than the given timestamp @@ -224,7 +210,18 @@ int mju_isModifiedResource(const mjResource* resource, const char* timestamp) { return 1; // default (modified) } - return mju_isModifiedFile(resource->name, (file_spec*) resource->data); + // fallback to OS filesystem + if (mju_isValidBase64(timestamp) != sizeof(time_t)) { + return 1; // error (assume modified) + } + + time_t time1, time2; + mju_decodeBase64((uint8_t*) &time1, timestamp); + time2 = ((file_spec*) resource->data)->mtime; + double diff = difftime(time2, time1); + if (diff < 0) return -1; + if (diff > 0) return 1; + return 0; } diff --git a/test/engine/engine_resource_test.cc b/test/engine/engine_resource_test.cc index 07390b63..aba5f585 100644 --- a/test/engine/engine_resource_test.cc +++ b/test/engine/engine_resource_test.cc @@ -15,7 +15,10 @@ // Tests for engine/engine_resource.c #include +#include #include +#include +#include #include #include @@ -24,6 +27,7 @@ #include #include "src/engine/engine_plugin.h" #include "src/engine/engine_resource.h" +#include "src/engine/engine_util_misc.h" #include "test/fixture.h" namespace mujoco { @@ -327,5 +331,34 @@ TEST_F(ResourceTest, NameWithInvalidPrefix) { ASSERT_THAT(resource, IsNull()); } +TEST_F(ResourceTest, OSFilesystemTimestamps) { + time_t t; + + // some random file + const char* const file = "engine/testdata/collision_box/boxbox_deep.xml"; + const std::string xml_path = GetTestDataFilePath(file); + + mjResource* resource = mju_openResource(xml_path.c_str(), nullptr, 0); + mju_decodeBase64((uint8_t*) &t, resource->timestamp); + + // equal timestamps + EXPECT_EQ(mju_isModifiedResource(resource, resource->timestamp), 0); + + std::array test_timestamp; + + // older resource timestamp + t++; + mju_encodeBase64(test_timestamp.data(), (uint8_t*) &t, sizeof(time_t)); + EXPECT_EQ(mju_isModifiedResource(resource, test_timestamp.data()), -1); + + + // newer resource timestamp + t -= 2; + mju_encodeBase64(test_timestamp.data(), (uint8_t*) &t, sizeof(time_t)); + EXPECT_EQ(mju_isModifiedResource(resource, test_timestamp.data()), 1); + + mju_closeResource(resource); +} + } // namespace } // namespace mujoco