From 720362817c464ce3a477f058e8012a67f5d47467 Mon Sep 17 00:00:00 2001 From: Fedor Chelnokov Date: Fri, 24 Apr 2026 17:01:34 +0300 Subject: [PATCH 1/2] test: replace sphere with pseudo-random bytes in CompressSphereToZip makeSphere's output is slightly arch-dependent -- ARM NEON vs x86 SSE/AVX produce a handful of different floating-point vertex coordinates, which shifts the resulting .mrmesh file's bytes just enough that the test's `sphere.zip size` came out ~74 B apart between arm64 and x86_64 CI legs (58844 vs 58918 on run 24887440903). That noise obscures what the test is actually meant to exercise: the zip write path (libzip + deflate). Swap the sphere+MeshSave::toMrmesh pair for LCG-generated deterministic bytes, sized (119808 B) to match what makeSphere(1000 verts)+toMrmesh used to emit so historical size measurements stay comparable. Now every platform starts from bit-identical input to `compressZip`, so any sphere.zip-size drift that remains comes from the deflate engine itself. DRY: the LCG helper that CompressManySmallFilesToZip already carried as a local lambda is lifted to an anonymous-namespace inline function and reused. Drop includes MRMakeSphereMesh.h / MRMesh.h / MRMeshSave.h -- no longer referenced from this file. Co-Authored-By: Claude Opus 4.7 (1M context) --- source/MRTest/MRZipCompressTests.cpp | 85 ++++++++++++++++++---------- 1 file changed, 55 insertions(+), 30 deletions(-) diff --git a/source/MRTest/MRZipCompressTests.cpp b/source/MRTest/MRZipCompressTests.cpp index 9cf9ff58b068..1bb00f39b237 100644 --- a/source/MRTest/MRZipCompressTests.cpp +++ b/source/MRTest/MRZipCompressTests.cpp @@ -1,8 +1,5 @@ #include #include -#include -#include -#include #include #include #include @@ -19,30 +16,68 @@ namespace MR { -// Writes a sphere to a .mrmesh file in a temporary folder, then -// compresses that folder to a .zip and verifies the archive was created and -// is non-empty. Serves as a realistic end-to-end exercise of MeshLib's zip -// write path (libzip + deflate) on mesh-sized data. +namespace +{ + +// Simple LCG used to produce deterministic pseudo-random bytes. +// Keeps the test reproducible across runs and platforms while avoiding +// trivially-compressible input (an all-zeros buffer would make deflate +// look unrealistically good). +inline uint64_t nextLcg( uint64_t & state ) +{ + state = state * 6364136223846793005ULL + 1442695040888963407ULL; + return state; +} + +} // namespace + +// Writes a fixed-size payload of deterministic pseudo-random bytes to a +// .mrmesh file in a temporary folder, then compresses that folder to a .zip +// and verifies the archive was created and is non-empty. Serves as an +// end-to-end exercise of MeshLib's zip write path (libzip + deflate) on +// mesh-sized data. +// +// The content used to be a real sphere produced by makeSphere(1000 verts) +// serialized via MeshSave::toMrmesh. That made the test input slightly +// arch-dependent: ARM NEON vs x86 SSE/AVX produced a handful of different +// floating-point vertex coordinates, which in turn made sphere.zip's +// compressed size differ by ~74 bytes between arm64 and x86_64 CI legs. +// Replacing the sphere with LCG-generated bytes keeps the test input +// bit-identical on every platform, so the only remaining cause of +// sphere.zip-size drift is the deflate engine itself — which is what +// this test is supposed to exercise. +// +// The byte count (119808) matches the previous makeSphere(1000)+toMrmesh +// output, so the archive's working set stays comparable to historical +// measurements. TEST( MRMesh, CompressSphereToZip ) { UniqueTemporaryFolder srcFolder; ASSERT_TRUE( bool( srcFolder ) ); - constexpr int targetVerts = 1000; // increase it to make the file being compressed larger, 100'000 vertices -> 12M bytes - SphereParams params; - params.radius = 1.0f; - params.numMeshVertices = targetVerts; - const Mesh sphere = makeSphere( params ); - EXPECT_EQ( (int)sphere.topology.numValidVerts(), targetVerts ); + // Matches the `.mrmesh` size that makeSphere( numMeshVertices=1000 ) + + // MeshSave::toMrmesh produced before this test was converted to + // synthetic bytes. Increase to make the file being compressed larger. + constexpr std::size_t meshBytes = 119808; - // Save mesh as a .mrmesh file in the temp folder. + // Write `sphere.mrmesh` filled with `meshBytes` pseudo-random bytes. + // The filename is kept for continuity of log output and the CI history + // of "sphere.zip size" lines; the content is no longer a mesh. const std::filesystem::path meshPath = srcFolder / "sphere.mrmesh"; - const auto saveRes = MeshSave::toMrmesh( sphere, meshPath ); - ASSERT_TRUE( saveRes.has_value() ) << saveRes.error(); + { + uint64_t state = 0x00DDECAFCAFEF00DULL; + std::vector buf( meshBytes ); + for ( std::size_t j = 0; j < meshBytes; ++j ) + buf[j] = (char)( nextLcg( state ) >> 56 ); + std::ofstream out( meshPath, std::ios::binary ); + ASSERT_TRUE( out.is_open() ); + out.write( buf.data(), (std::streamsize)buf.size() ); + ASSERT_TRUE( out.good() ); + } std::error_code ec; ASSERT_TRUE( std::filesystem::exists( meshPath, ec ) ); const auto meshSize = std::filesystem::file_size( meshPath, ec ); - EXPECT_GT( meshSize, 0u ); + EXPECT_EQ( meshSize, meshBytes ); spdlog::info( "sphere.mrmesh size: {} bytes", meshSize ); // Compress the temp folder into a .zip located in a second temp folder @@ -63,9 +98,9 @@ TEST( MRMesh, CompressSphereToZip ) spdlog::info( "sphere.zip compression time: {} sec", sec ); // Sanity: the zip should not be absurdly larger than the source - // (that would indicate something is wrong with the envelope); and - // since .mrmesh is a raw binary dump of topology plus coordinate - // floats, deflate typically produces a modestly smaller archive. + // (that would indicate something is wrong with the envelope). + // Pseudo-random bytes are near-incompressible, so in practice zipSize + // will be close to meshSize plus a small per-entry zip overhead. EXPECT_LT( zipSize, meshSize * 2u ); } @@ -86,16 +121,6 @@ TEST( MRMesh, CompressManySmallFilesToZip ) constexpr int numJsonFiles = numBinaryFiles; constexpr size_t bytesPerFile = 6000; - // Simple LCG used to produce deterministic pseudo-random bytes. - // Keeps the test reproducible across runs and platforms while avoiding - // trivially-compressible input (an all-zeros buffer would make deflate - // look unrealistically good). - auto nextLcg = []( uint64_t & state ) -> uint64_t - { - state = state * 6364136223846793005ULL + 1442695040888963407ULL; - return state; - }; - auto makeName = []( const char * prefix, int i, const char * ext ) { char buf[64]; From 20d9438b693a2b2a3b055d31b63b704ed1ab059d Mon Sep 17 00:00:00 2001 From: Fedor Chelnokov Date: Sat, 25 Apr 2026 00:14:02 +0300 Subject: [PATCH 2/2] test: rename CompressSphereToZip -> CompressOneBigFileToZip The test no longer operates on a sphere mesh; rename it and its artefacts to reflect what it actually does: - TEST name: CompressSphereToZip -> CompressOneBigFileToZip - source file: sphere.mrmesh -> big.bin - output archive: sphere.zip -> big.zip - vars: meshPath/meshBytes/meshSize -> filePath/fileBytes/fileSize - log labels: "sphere.mrmesh size" -> "big.bin size" "sphere.zip size" -> "big.zip size" "sphere.zip compression time" -> "big.zip compression time" Also trim comments and drop references to the prior sphere+toMrmesh implementation. Update cross-reference in CompressManySmallFilesToZip to point at the new name. Co-Authored-By: Claude Opus 4.7 (1M context) --- source/MRTest/MRZipCompressTests.cpp | 72 ++++++++++------------------ 1 file changed, 25 insertions(+), 47 deletions(-) diff --git a/source/MRTest/MRZipCompressTests.cpp b/source/MRTest/MRZipCompressTests.cpp index 1bb00f39b237..e00c690ac78a 100644 --- a/source/MRTest/MRZipCompressTests.cpp +++ b/source/MRTest/MRZipCompressTests.cpp @@ -31,60 +31,40 @@ inline uint64_t nextLcg( uint64_t & state ) } // namespace -// Writes a fixed-size payload of deterministic pseudo-random bytes to a -// .mrmesh file in a temporary folder, then compresses that folder to a .zip -// and verifies the archive was created and is non-empty. Serves as an -// end-to-end exercise of MeshLib's zip write path (libzip + deflate) on -// mesh-sized data. -// -// The content used to be a real sphere produced by makeSphere(1000 verts) -// serialized via MeshSave::toMrmesh. That made the test input slightly -// arch-dependent: ARM NEON vs x86 SSE/AVX produced a handful of different -// floating-point vertex coordinates, which in turn made sphere.zip's -// compressed size differ by ~74 bytes between arm64 and x86_64 CI legs. -// Replacing the sphere with LCG-generated bytes keeps the test input -// bit-identical on every platform, so the only remaining cause of -// sphere.zip-size drift is the deflate engine itself — which is what -// this test is supposed to exercise. -// -// The byte count (119808) matches the previous makeSphere(1000)+toMrmesh -// output, so the archive's working set stays comparable to historical -// measurements. -TEST( MRMesh, CompressSphereToZip ) +// Writes one large file of deterministic pseudo-random bytes to a temp +// folder and compresses that folder to a .zip. End-to-end check of the +// zip write path (libzip + deflate) on a single big near-incompressible +// entry. Pairs with CompressManySmallFilesToZip to contrast one-big-entry +// vs many-small-entry archiver behaviour. +TEST( MRMesh, CompressOneBigFileToZip ) { UniqueTemporaryFolder srcFolder; ASSERT_TRUE( bool( srcFolder ) ); - // Matches the `.mrmesh` size that makeSphere( numMeshVertices=1000 ) + - // MeshSave::toMrmesh produced before this test was converted to - // synthetic bytes. Increase to make the file being compressed larger. - constexpr std::size_t meshBytes = 119808; + // ~120 KB; increase to compress a larger payload. + constexpr std::size_t fileBytes = 119808; - // Write `sphere.mrmesh` filled with `meshBytes` pseudo-random bytes. - // The filename is kept for continuity of log output and the CI history - // of "sphere.zip size" lines; the content is no longer a mesh. - const std::filesystem::path meshPath = srcFolder / "sphere.mrmesh"; + const std::filesystem::path filePath = srcFolder / "big.bin"; { uint64_t state = 0x00DDECAFCAFEF00DULL; - std::vector buf( meshBytes ); - for ( std::size_t j = 0; j < meshBytes; ++j ) + std::vector buf( fileBytes ); + for ( std::size_t j = 0; j < fileBytes; ++j ) buf[j] = (char)( nextLcg( state ) >> 56 ); - std::ofstream out( meshPath, std::ios::binary ); + std::ofstream out( filePath, std::ios::binary ); ASSERT_TRUE( out.is_open() ); out.write( buf.data(), (std::streamsize)buf.size() ); ASSERT_TRUE( out.good() ); } std::error_code ec; - ASSERT_TRUE( std::filesystem::exists( meshPath, ec ) ); - const auto meshSize = std::filesystem::file_size( meshPath, ec ); - EXPECT_EQ( meshSize, meshBytes ); - spdlog::info( "sphere.mrmesh size: {} bytes", meshSize ); + ASSERT_TRUE( std::filesystem::exists( filePath, ec ) ); + const auto fileSize = std::filesystem::file_size( filePath, ec ); + EXPECT_EQ( fileSize, fileBytes ); + spdlog::info( "big.bin size: {} bytes", fileSize ); - // Compress the temp folder into a .zip located in a second temp folder - // (so the zip isn't inside the folder being compressed). + // Zip lands in a separate temp folder so it isn't inside the source tree. UniqueTemporaryFolder dstFolder; ASSERT_TRUE( bool( dstFolder ) ); - const std::filesystem::path zipPath = dstFolder / "sphere.zip"; + const std::filesystem::path zipPath = dstFolder / "big.zip"; Timer t( "t" ); const auto compressRes = compressZip( zipPath, srcFolder ); @@ -94,18 +74,16 @@ TEST( MRMesh, CompressSphereToZip ) ASSERT_TRUE( std::filesystem::exists( zipPath, ec ) ); const auto zipSize = std::filesystem::file_size( zipPath, ec ); EXPECT_GT( zipSize, 0u ); - spdlog::info( "sphere.zip size: {} bytes", zipSize ); - spdlog::info( "sphere.zip compression time: {} sec", sec ); + spdlog::info( "big.zip size: {} bytes", zipSize ); + spdlog::info( "big.zip compression time: {} sec", sec ); - // Sanity: the zip should not be absurdly larger than the source - // (that would indicate something is wrong with the envelope). - // Pseudo-random bytes are near-incompressible, so in practice zipSize - // will be close to meshSize plus a small per-entry zip overhead. - EXPECT_LT( zipSize, meshSize * 2u ); + // Sanity envelope: random input is near-incompressible, so zipSize + // should be close to fileSize plus a small zip overhead. + EXPECT_LT( zipSize, fileSize * 2u ); } // Writes many binary files and same number JSON files to a temporary folder, then -// compresses the folder to a .zip. Pairs with CompressSphereToZip to compare +// compresses the folder to a .zip. Pairs with CompressOneBigFileToZip to compare // compression of one large binary vs many small mixed-type entries. // // libzip compresses each entry independently, so per-entry overhead (local @@ -245,7 +223,7 @@ TEST( MRMesh, CompressManySmallFilesToZip ) spdlog::info( "level {}: many.zip size: {} bytes, compression time: {} sec", level, zipSize, sec ); - // Sanity envelope: same bound as the sphere test. + // Sanity envelope: same bound as CompressOneBigFileToZip. EXPECT_LT( zipSize, totalInput * 2u ) << "level " << level; // Round-trip: decompress into a fresh folder and compare every file's