diff options
| author | skal <pascal.massimino@gmail.com> | 2026-02-09 20:27:04 +0100 |
|---|---|---|
| committer | skal <pascal.massimino@gmail.com> | 2026-02-09 20:27:04 +0100 |
| commit | eff8d43479e7704df65fae2a80eefa787213f502 (patch) | |
| tree | 76f2fb8fe8d3db2c15179449df2cf12f7f54e0bf /src/tests/gpu/test_effect_base.cc | |
| parent | 12378b1b7e9091ba59895b4360b2fa959180a56a (diff) | |
refactor: Reorganize tests into subsystem subdirectories
Restructured test suite for better organization and targeted testing:
**Structure:**
- src/tests/audio/ - 15 audio system tests
- src/tests/gpu/ - 12 GPU/shader tests
- src/tests/3d/ - 6 3D rendering tests
- src/tests/assets/ - 2 asset system tests
- src/tests/util/ - 3 utility tests
- src/tests/common/ - 3 shared test helpers
- src/tests/scripts/ - 2 bash test scripts (moved conceptually, not physically)
**CMake changes:**
- Updated add_demo_test macro to accept LABEL parameter
- Applied CTest labels to all 36 tests for subsystem filtering
- Updated all test file paths in CMakeLists.txt
- Fixed common helper paths (webgpu_test_fixture, etc.)
- Added custom targets for subsystem testing:
- run_audio_tests, run_gpu_tests, run_3d_tests
- run_assets_tests, run_util_tests, run_all_tests
**Include path updates:**
- Fixed relative includes in GPU tests to reference ../common/
**Documentation:**
- Updated doc/HOWTO.md with subsystem test commands
- Updated doc/CONTRIBUTING.md with new test organization
- Updated scripts/check_all.sh to reflect new structure
**Verification:**
- All 36 tests passing (100%)
- ctest -L <subsystem> filters work correctly
- make run_<subsystem>_tests targets functional
- scripts/check_all.sh passes
Backward compatible: make test and ctest continue to work unchanged.
handoff(Gemini): Test reorganization complete. 36/36 tests passing.
Diffstat (limited to 'src/tests/gpu/test_effect_base.cc')
| -rw-r--r-- | src/tests/gpu/test_effect_base.cc | 265 |
1 files changed, 265 insertions, 0 deletions
diff --git a/src/tests/gpu/test_effect_base.cc b/src/tests/gpu/test_effect_base.cc new file mode 100644 index 0000000..08cf0a1 --- /dev/null +++ b/src/tests/gpu/test_effect_base.cc @@ -0,0 +1,265 @@ +// This file is part of the 64k demo project. +// It tests the Effect/Sequence/MainSequence lifecycle using headless rendering. +// Verifies effect initialization, activation, and basic rendering. + +#include "../common/effect_test_helpers.h" +#include "gpu/demo_effects.h" +#include "gpu/effect.h" +#include "../common/offscreen_render_target.h" +#include "../common/webgpu_test_fixture.h" +#include <cassert> +#include <cstdio> +#include <memory> + +// Test 1: WebGPU fixture initialization +static void test_webgpu_fixture() { + fprintf(stdout, "Testing WebGPU fixture...\n"); + + WebGPUTestFixture fixture; + const bool init_success = fixture.init(); + + if (!init_success) { + fprintf(stdout, " ⚠ WebGPU unavailable - skipping test\n"); + return; + } + + assert(fixture.is_initialized() && "Fixture should be initialized"); + assert(fixture.device() != nullptr && "Device should be valid"); + assert(fixture.queue() != nullptr && "Queue should be valid"); + + fprintf(stdout, " ✓ WebGPU fixture initialized successfully\n"); + + fixture.shutdown(); + assert(!fixture.is_initialized() && "Fixture should be shutdown"); + + fprintf(stdout, " ✓ WebGPU fixture shutdown successfully\n"); +} + +// Test 2: Offscreen render target creation +static void test_offscreen_render_target() { + fprintf(stdout, "Testing offscreen render target...\n"); + + WebGPUTestFixture fixture; + if (!fixture.init()) { + fprintf(stdout, " ⚠ WebGPU unavailable - skipping test\n"); + return; + } + + OffscreenRenderTarget target(fixture.instance(), fixture.device(), 256, 256); + + assert(target.texture() != nullptr && "Texture should be valid"); + assert(target.view() != nullptr && "Texture view should be valid"); + assert(target.width() == 256 && "Width should be 256"); + assert(target.height() == 256 && "Height should be 256"); + + fprintf(stdout, " ✓ Offscreen render target created (256x256)\n"); + + // Test pixel readback (should initially be all zeros or uninitialized) + const std::vector<uint8_t> pixels = target.read_pixels(); + + // Note: Buffer mapping may fail on some systems (WebGPU driver issue) + // Don't fail the test if readback returns empty buffer + if (pixels.empty()) { + fprintf(stdout, + " ⚠ Pixel readback skipped (buffer mapping unavailable)\n"); + } else { + assert(pixels.size() == 256 * 256 * 4 && "Pixel buffer size should match"); + fprintf(stdout, " ✓ Pixel readback succeeded (%zu bytes)\n", + pixels.size()); + } +} + +// Test 3: Effect construction +static void test_effect_construction() { + fprintf(stdout, "Testing effect construction...\n"); + + WebGPUTestFixture fixture; + if (!fixture.init()) { + fprintf(stdout, " ⚠ WebGPU unavailable - skipping test\n"); + return; + } + + // Create FlashEffect (simple post-process effect) + auto effect = std::make_shared<FlashEffect>(fixture.ctx()); + + assert(!effect->is_initialized && "Effect should not be initialized yet"); + + fprintf(stdout, " ✓ FlashEffect constructed (not initialized)\n"); +} + +// Test 4: Effect initialization via Sequence +static void test_effect_initialization() { + fprintf(stdout, "Testing effect initialization...\n"); + + WebGPUTestFixture fixture; + if (!fixture.init()) { + fprintf(stdout, " ⚠ WebGPU unavailable - skipping test\n"); + return; + } + + // Create MainSequence (use init_test for test environment) + MainSequence main_seq; + main_seq.init_test(fixture.ctx()); + + // Create FlashEffect + auto effect = std::make_shared<FlashEffect>(fixture.ctx()); + + assert(!effect->is_initialized && "Effect should not be initialized yet"); + + // Add effect to sequence + auto seq = std::make_shared<Sequence>(); + seq->add_effect(effect, 0.0f, 10.0f, 0); + + // Initialize sequence (this sets effect->is_initialized) + seq->init(&main_seq); + + assert(effect->is_initialized && + "Effect should be initialized after Sequence::init()"); + + fprintf(stdout, " ✓ FlashEffect initialized via Sequence::init()\n"); +} + +// Test 5: Sequence add_effect +static void test_sequence_add_effect() { + fprintf(stdout, "Testing Sequence::add_effect...\n"); + + WebGPUTestFixture fixture; + if (!fixture.init()) { + fprintf(stdout, " ⚠ WebGPU unavailable - skipping test\n"); + return; + } + + MainSequence main_seq; + main_seq.init_test(fixture.ctx()); + + // Create sequence + auto seq = std::make_shared<Sequence>(); + + // Create effect + auto effect = std::make_shared<FlashEffect>(fixture.ctx()); + + assert(!effect->is_initialized && + "Effect should not be initialized before Sequence::init()"); + + // Add effect to sequence (time range: 0.0 - 10.0, priority 0) + seq->add_effect(effect, 0.0f, 10.0f, 0); + + // Initialize sequence (this should initialize the effect) + seq->init(&main_seq); + + assert(effect->is_initialized && + "Effect should be initialized after Sequence::init()"); + + fprintf(stdout, + " ✓ Effect added to sequence and initialized (time=0.0-10.0, " + "priority=0)\n"); +} + +// Test 6: Sequence activation logic +static void test_sequence_activation() { + fprintf(stdout, "Testing sequence activation logic...\n"); + + WebGPUTestFixture fixture; + if (!fixture.init()) { + fprintf(stdout, " ⚠ WebGPU unavailable - skipping test\n"); + return; + } + + MainSequence main_seq; + main_seq.init_test(fixture.ctx()); + + auto seq = std::make_shared<Sequence>(); + auto effect = std::make_shared<FlashEffect>(fixture.ctx()); + + // Effect active from 5.0 to 10.0 seconds + seq->add_effect(effect, 5.0f, 10.0f, 0); + seq->init(&main_seq); + + // Before start time: should not be active + seq->update_active_list(-1.0f); + std::vector<SequenceItem*> scene_before, post_before; + seq->collect_active_effects(scene_before, post_before); + assert(scene_before.empty() && post_before.empty() && + "Effect should not be active before start time"); + + fprintf(stdout, " ✓ Effect not active before start time (t=-1.0)\n"); + + // At start time: should be active + seq->update_active_list(5.0f); + std::vector<SequenceItem*> scene_at_start, post_at_start; + seq->collect_active_effects(scene_at_start, post_at_start); + const size_t active_at_start = scene_at_start.size() + post_at_start.size(); + assert(active_at_start == 1 && "Effect should be active at start time"); + + fprintf(stdout, " ✓ Effect active at start time (t=5.0)\n"); + + // During active period: should remain active + seq->update_active_list(7.5f); + std::vector<SequenceItem*> scene_during, post_during; + seq->collect_active_effects(scene_during, post_during); + const size_t active_during = scene_during.size() + post_during.size(); + assert(active_during == 1 && "Effect should be active during period"); + + fprintf(stdout, " ✓ Effect active during period (t=7.5)\n"); + + // After end time: should not be active + seq->update_active_list(11.0f); + std::vector<SequenceItem*> scene_after, post_after; + seq->collect_active_effects(scene_after, post_after); + assert(scene_after.empty() && post_after.empty() && + "Effect should not be active after end time"); + + fprintf(stdout, " ✓ Effect not active after end time (t=11.0)\n"); +} + +// Test 7: Pixel validation helpers +static void test_pixel_helpers() { + fprintf(stdout, "Testing pixel validation helpers...\n"); + + // Test has_rendered_content (should detect non-black pixels) + std::vector<uint8_t> black_frame(256 * 256 * 4, 0); + assert(!has_rendered_content(black_frame, 256, 256) && + "Black frame should have no content"); + + std::vector<uint8_t> colored_frame(256 * 256 * 4, 0); + colored_frame[0] = 255; // Set one red pixel + assert(has_rendered_content(colored_frame, 256, 256) && + "Colored frame should have content"); + + fprintf(stdout, " ✓ has_rendered_content() works correctly\n"); + + // Test all_pixels_match_color + std::vector<uint8_t> red_frame(256 * 256 * 4, 0); + for (size_t i = 0; i < 256 * 256; ++i) { + red_frame[i * 4 + 2] = 255; // BGRA: Red in position 2 + } + assert(all_pixels_match_color(red_frame, 256, 256, 255, 0, 0, 5) && + "Red frame should match red color"); + + fprintf(stdout, " ✓ all_pixels_match_color() works correctly\n"); + + // Test hash_pixels + const uint64_t hash1 = hash_pixels(black_frame); + const uint64_t hash2 = hash_pixels(colored_frame); + assert(hash1 != hash2 && "Different frames should have different hashes"); + + fprintf(stdout, " ✓ hash_pixels() produces unique hashes\n"); +} + +int main() { + fprintf(stdout, "=== Effect Base Tests ===\n"); + + extern void InitShaderComposer(); + InitShaderComposer(); + + test_webgpu_fixture(); + test_offscreen_render_target(); + test_effect_construction(); + test_effect_initialization(); + test_sequence_add_effect(); + test_sequence_activation(); + test_pixel_helpers(); + + fprintf(stdout, "=== All Effect Base Tests Passed ===\n"); + return 0; +} |
