--- description: "Write, build, and run C tests for the VoxelPic library. USE FOR: adding new C test cases, understanding the test data format, debugging test failures, or running the existing C test suite." --- # Testing the C Library ## Test Suite Overview There are three C test executables under `test/`, each registered with CTest: | Executable | Tests | Data Files | |--------------|------------------------------|-------------------------| | `voxel_test` | OcTree build round-trip | `voxel_1.dat`–`voxel_5.dat` | | `color_test` | Hue-codec encode/decode | *(none — self-contained)* | | `image_test` | Level encode/decode to image | `image_7.dat`–`image_9.dat` | ## Building and Running ```sh cmake -B build --preset debug # or release / debug-clang / release-clang cmake --build build ctest --test-dir build -V # -V for verbose output ``` All four CMake presets enable `VOXELPIC_BUILD_TESTS`. To run a single test: ```sh ctest --test-dir build -R color_test -V ``` To build with AddressSanitizer (clang): ```sh cmake -B build --preset release-clang -DVOXELPIC_SANITIZE=address cmake --build build ctest --test-dir build -V ``` ## Test Data Generation Test data files are **generated automatically** by Python scripts as a post-build step (defined in `test/CMakeLists.txt`): - `test/voxel_test_gen.py` → produces `voxel_1.dat`–`voxel_5.dat` - `test/image_test_gen.py` → produces `image_7.dat`–`image_9.dat` Both scripts use a fixed random seed for reproducibility, and skip generation if the output file already exists. Delete the `.dat` files to force regeneration. ### Binary Data Format All test data files follow the same layout: ``` [int32 big-endian] octree level [int32 big-endian] number of points/voxels (N) repeated N times: [float32 native] x [float32 native] y [float32 native] z [uint8] r [uint8] g [uint8] b ``` `voxel_test` data files additionally contain an expected-output section after the input section with the same `[count][positions+colors]` layout. Read big-endian header integers with the `read_be()` helper from `test/test.h`. ## Conventions for Writing a New C Test ### File Structure 1. Create `test/_test.c`. 2. `#include ` and `#include "test.h"`. 3. Link against `voxelpic::voxelpic` in `test/CMakeLists.txt`. 4. Register with CTest via `add_test(...)`. ### Pass / Fail Signalling - Return **0** from `main()` on success, **1** on assertion failure. - CTest treats any non-zero exit code as failure. - Use `printf()` to log diagnostics on failure (no external assertion library). ### Comparison Pattern Compare results element-by-element and print details on mismatch. Example: ```c for (size_t i = 0; i < cloud->size; ++i) { if (voxelpicVec3Compare(&actual->positions[i], &expected->positions[i])) { printf("Position mismatch at %zu\n", i); return 1; } if (actual->colors[i].value != expected->colors[i].value) { printf("Color mismatch at %zu\n", i); return 1; } } ``` ### Resource Cleanup Use `goto end` with a cleanup label to ensure all `*New()` allocations are matched by `*Free()` calls, even on error paths: ```c int main(int argc, char *argv[]) { int result = 1; voxelpicOcTree *octree = voxelpicOcTreeNew(4, 9); voxelpicPointCloud *cloud = voxelpicPointCloudNew(0); // ... test logic ... result = 0; end: voxelpicOcTreeFree(octree); voxelpicPointCloudFree(cloud); return result; } ``` ### Test Data Files If the test needs generated data, add a Python generator script and a `add_custom_command(TARGET ... POST_BUILD ...)` in `test/CMakeLists.txt` following the existing pattern. Use `struct.pack(">i", ...)` for big-endian header integers and native byte order for floats and uint8 colour values. ### CMakeLists.txt Registration ```cmake add_executable(_test _test.c) target_link_libraries(_test PRIVATE voxelpic::voxelpic) add_test(NAME _test COMMAND _test [args...] WORKING_DIRECTORY $_test>) ``` ## Existing Test Details ### voxel_test Tests the full OcTree round-trip: loads a point cloud, builds an OcTree, extracts the level back to a cloud, and compares with expected output. Also verifies save/load I/O for both `voxelpicPointCloud` and `voxelpicLevel`. ### color_test Tests `voxelpicValueToColor()` / `voxelpicColorToValue()` round-trip across the full `[0, VPIC_MAX_ENCODE_VALUE]` range, plus many smaller sub-ranges. Tests both explicit-range and implicit-range (auto-detected min/max) modes. ### image_test Tests level-to-image encoding and image-to-level decoding. Builds an OcTree, encodes a level into an RGBA image via `voxelpicLevelEncode()`, decodes it back with `voxelpicLevelDecode()`, and compares the reconstructed cloud against the original.