Skip to content
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 55 additions & 30 deletions source/MRTest/MRZipCompressTests.cpp
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
#include <MRMesh/MRDirectory.h>
#include <MRMesh/MRGTest.h>
#include <MRMesh/MRMakeSphereMesh.h>
#include <MRMesh/MRMesh.h>
#include <MRMesh/MRMeshSave.h>
#include <MRMesh/MRUniqueTemporaryFolder.h>
#include <MRMesh/MRZip.h>
#include <MRMesh/MRTimer.h>
Expand All @@ -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<char> 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
Expand All @@ -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 );
}

Expand All @@ -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];
Expand Down
Loading