diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 5b897bc2..469fa722 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -76,6 +76,7 @@ scripts/lint-cpp.sh --fix # rewrite the formatting and apply the fixes clang- Both tools come from the NDK the project pins, so a machine that builds the library can lint it; `.clang-format` and `.clang-tidy` at the root hold the rules. The script configures `splat-core`, `splatkit-engine` and `splatkit-android` for the Android target and lints tests and tools too. +It also links the Android native tests, so a wrong source path or an undefined reference fails CI; [running them on a device](packages/splatkit-android/README.md#native-tests-on-a-device) stays manual. `git config core.hooksPath .githooks` installs a pre-push hook that runs `scripts/lint-cpp.sh --format-only`, the seconds-long formatting half of that check. VS Code's C/C++ extension reads the compile databases it leaves in `build/lint`, as [.vscode/settings.json](.vscode/settings.json) sets up. clang-tidy holds every function to its default cognitive complexity of 25, counting the source as written, so test assertion macros add nothing. diff --git a/packages/splatkit-android/README.md b/packages/splatkit-android/README.md index 0ffb28d7..e57c0f6c 100644 --- a/packages/splatkit-android/README.md +++ b/packages/splatkit-android/README.md @@ -113,6 +113,25 @@ cd apps/android-dev ./gradlew :splatkit:assembleRelease :splatkit:testDebugUnitTest ``` +## Native tests on a device + +The `splatkit_vulkan_*_test` executables need a real Vulkan device and run without a surface. +`scripts/lint-cpp.sh` links all of them in CI; running them is a manual device step. +Build them outside the checkout with the NDK toolchain flags from that script plus `-DSPLATKIT_ANDROID_BUILD_TESTS=ON`. + +```sh +cmake -S packages/splatkit-android/src/main/cpp -B "$BUILD" \ + -DCMAKE_TOOLCHAIN_FILE="$ANDROID_NDK_HOME/build/cmake/android.toolchain.cmake" \ + -DANDROID_ABI=arm64-v8a -DANDROID_PLATFORM=android-29 -DANDROID_STL=c++_static \ + -DSPLATKIT_ANDROID_BUILD_TESTS=ON +cmake --build "$BUILD" --target splatkit_android_tests +for t in "$BUILD"/tests/splatkit_vulkan_*_test; do + adb push "$t" /data/local/tmp/ && adb shell "/data/local/tmp/$(basename "$t")" +done +``` + +Each test exits 0 on success; `splatkit_vulkan_buffer_test` and `splatkit_vulkan_upload_pressure_test` accept `--small` for a device with little memory, which skips their large cases. + Tests and emulator evidence do not replace physical Adreno/Mali validation or reference-image acceptance. Source builds enable 16 KB ELF alignment; CI checks every packaged shared library and APK ZIP alignment with `scripts/check_android_alignment.py`. Artifact alignment checks do not replace execution on a 16 KB device. diff --git a/packages/splatkit-android/tests/CMakeLists.txt b/packages/splatkit-android/tests/CMakeLists.txt index f25579fb..19798c0e 100644 --- a/packages/splatkit-android/tests/CMakeLists.txt +++ b/packages/splatkit-android/tests/CMakeLists.txt @@ -15,27 +15,38 @@ target_compile_options(splatkit_vulkan_test_support PRIVATE target_link_libraries(splatkit_vulkan_test_support PUBLIC splatkit_engine vk-bootstrap::vk-bootstrap GPUOpen::VulkanMemoryAllocator vulkan android log) -add_executable(splatkit_gpu_buffer_test rendering/vulkan/device/VulkanBufferTest.cpp) -target_link_libraries(splatkit_gpu_buffer_test PRIVATE splatkit_vulkan_test_support) -add_executable(splatkit_gpu_upload_pressure_test rendering/vulkan/device/VulkanUploadPressureTest.cpp) -target_link_libraries(splatkit_gpu_upload_pressure_test PRIVATE splatkit_vulkan_test_support) -add_executable(splatkit_world_test rendering/vulkan/world/VulkanWorldTest.cpp +add_executable(splatkit_vulkan_buffer_test rendering/vulkan/device/VulkanBufferTest.cpp) +target_link_libraries(splatkit_vulkan_buffer_test PRIVATE splatkit_vulkan_test_support) +add_executable(splatkit_vulkan_upload_pressure_test rendering/vulkan/device/VulkanUploadPressureTest.cpp) +target_link_libraries(splatkit_vulkan_upload_pressure_test PRIVATE splatkit_vulkan_test_support) +add_executable(splatkit_vulkan_world_test rendering/vulkan/world/VulkanWorldTest.cpp ${vulkan_source_dir}/world/VulkanWorld.cpp) -target_link_libraries(splatkit_world_test PRIVATE splatkit_vulkan_test_support) -add_executable(splatkit_lod_selection_test rendering/vulkan/selection/VulkanLodSelectionTest.cpp +target_link_libraries(splatkit_vulkan_world_test PRIVATE splatkit_vulkan_test_support) +add_executable(splatkit_vulkan_lod_selection_test rendering/vulkan/selection/VulkanLodSelectionTest.cpp ${vulkan_source_dir}/selection/VulkanLodSelection.cpp) -target_link_libraries(splatkit_lod_selection_test PRIVATE +target_link_libraries(splatkit_vulkan_lod_selection_test PRIVATE splatkit_vulkan_test_support splatkit_shaders) -add_executable(splatkit_visibility_test rendering/vulkan/cull/VulkanVisibilityPassTest.cpp +add_executable(splatkit_vulkan_visibility_pass_test rendering/vulkan/cull/VulkanVisibilityPassTest.cpp ${vulkan_source_dir}/cull/VulkanVisibilityPass.cpp) -target_link_libraries(splatkit_visibility_test PRIVATE +target_link_libraries(splatkit_vulkan_visibility_pass_test PRIVATE splatkit_vulkan_test_support splatkit_shaders) -add_executable(splatkit_vulkan_frame_test rendering/vulkan/frame/VulkanFrameComputeTest.cpp +add_executable(splatkit_vulkan_frame_compute_test rendering/vulkan/frame/VulkanFrameComputeTest.cpp ${vulkan_source_dir}/frame/VulkanFrameCompute.cpp ${vulkan_source_dir}/selection/VulkanLodSelection.cpp ${vulkan_source_dir}/cull/VulkanVisibilityPass.cpp ${vulkan_source_dir}/sort/VulkanRadixSort.cpp) -target_link_libraries(splatkit_vulkan_frame_test PRIVATE +target_link_libraries(splatkit_vulkan_frame_compute_test PRIVATE splatkit_vulkan_test_support splatkit_shaders) -add_executable(splatkit_radix_sort_test rendering/vulkan/sort/VulkanRadixSortTest.cpp +add_executable(splatkit_vulkan_radix_sort_test rendering/vulkan/sort/VulkanRadixSortTest.cpp ${vulkan_source_dir}/sort/VulkanRadixSort.cpp) -target_link_libraries(splatkit_radix_sort_test PRIVATE +target_link_libraries(splatkit_vulkan_radix_sort_test PRIVATE splatkit_vulkan_test_support splatkit_shaders) + +# Building this target links every device test; scripts/lint-cpp.sh does so in CI. +add_custom_target(splatkit_android_tests DEPENDS + splatkit_vulkan_buffer_test + splatkit_vulkan_upload_pressure_test + splatkit_vulkan_world_test + splatkit_vulkan_lod_selection_test + splatkit_vulkan_visibility_pass_test + splatkit_vulkan_frame_compute_test + splatkit_vulkan_radix_sort_test +) diff --git a/packages/splatkit-android/tests/rendering/vulkan/device/VulkanBufferTest.cpp b/packages/splatkit-android/tests/rendering/vulkan/device/VulkanBufferTest.cpp index 657ffff7..a16f8265 100644 --- a/packages/splatkit-android/tests/rendering/vulkan/device/VulkanBufferTest.cpp +++ b/packages/splatkit-android/tests/rendering/vulkan/device/VulkanBufferTest.cpp @@ -8,7 +8,7 @@ int main(int argc, char** argv) { using test::require; try { const bool small = argc == 2 && std::strcmp(argv[1], "--small") == 0; - require(argc == 1 || small, "usage: splatkit_gpu_buffer_test [--small]"); + require(argc == 1 || small, "usage: splatkit_vulkan_buffer_test [--small]"); test::VulkanTestContext gpu; std::printf("GPU: %s\n", gpu.context->deviceDescription().c_str()); std::vector expected(small ? 4096 : 16000000); diff --git a/packages/splatkit-android/tests/rendering/vulkan/device/VulkanUploadPressureTest.cpp b/packages/splatkit-android/tests/rendering/vulkan/device/VulkanUploadPressureTest.cpp index 0cb18a2a..c4e3a2e8 100644 --- a/packages/splatkit-android/tests/rendering/vulkan/device/VulkanUploadPressureTest.cpp +++ b/packages/splatkit-android/tests/rendering/vulkan/device/VulkanUploadPressureTest.cpp @@ -11,7 +11,7 @@ int main(int argc, char** argv) { std::setvbuf(stdout, nullptr, _IONBF, 0); try { const bool small = argc == 2 && std::strcmp(argv[1], "--small") == 0; - require(argc == 1 || small, "usage: splatkit_gpu_upload_pressure_test [--small]"); + require(argc == 1 || small, "usage: splatkit_vulkan_upload_pressure_test [--small]"); splatkit::test::VulkanTestContext gpu; const uint32_t capacity = small ? 262145 : 3000000; std::printf("upload-pressure GPU=%s capacity=%u validation=%s\n", diff --git a/scripts/lint-cpp.sh b/scripts/lint-cpp.sh index 4ae4ea57..540bb404 100755 --- a/scripts/lint-cpp.sh +++ b/scripts/lint-cpp.sh @@ -1,6 +1,7 @@ #!/usr/bin/env bash # Formats and lints every C++ source of the repository with the clang tools the NDK -# ships, so the machine that builds the library is the machine that lints it. +# ships, so the machine that builds the library is the machine that lints it. It also +# links the Android device tests, which nothing else builds in CI. # # scripts/lint-cpp.sh check: exit 1 on a formatting or clang-tidy finding # scripts/lint-cpp.sh --fix rewrite the formatting and apply clang-tidy's fixes @@ -90,6 +91,13 @@ cmake -S "$android" -B "$build/android" "${toolchain[@]}" \ -DSPLATKIT_ANDROID_BUILD_TESTS=ON > /dev/null # The splat pipeline includes the generated shader headers. cmake --build "$build/android" --target splatkit_shaders_generate > /dev/null +# Linking the device tests catches a wrong source path or an undefined reference that +# clang-tidy, which only parses, would let through to a phone. +echo "link splatkit-android tests" +if ! tests_log="$(cmake --build "$build/android" --target splatkit_android_tests 2>&1)"; then + printf '%s\n' "$tests_log" >&2 + exit 1 +fi failed=0 tidy() { #