// This file is part of the 64k demo project. // It tests the TextureManager for procedural texture generation and management. // Tests all public methods with both success and failure cases. #if !defined(STRIP_ALL) // Test code only - zero size impact on final binary #include "gpu/texture_manager.h" #include "procedural/generator.h" #include "webgpu_test_fixture.h" #include #include #include // Test 1: Basic initialization and shutdown static void test_init_shutdown() { fprintf(stdout, "Testing init() and shutdown()...\n"); WebGPUTestFixture fixture; if (!fixture.init()) { fprintf(stdout, " ⚠ WebGPU unavailable - skipping test\n"); return; } TextureManager tm; // Test init tm.init(fixture.device(), fixture.queue()); // Test shutdown (should not crash with empty texture map) tm.shutdown(); fprintf(stdout, " ✓ Init and shutdown OK\n"); } // Test 2: Create texture from raw data static void test_create_texture_from_data() { fprintf(stdout, "Testing create_texture() with raw data...\n"); WebGPUTestFixture fixture; if (!fixture.init()) { fprintf(stdout, " ⚠ WebGPU unavailable - skipping test\n"); return; } TextureManager tm; tm.init(fixture.device(), fixture.queue()); // Create 4x4 red texture (RGBA8) const int width = 4; const int height = 4; uint8_t pixels[4 * 4 * 4]; // 4x4 RGBA for (int i = 0; i < width * height; ++i) { pixels[i * 4 + 0] = 255; // R pixels[i * 4 + 1] = 0; // G pixels[i * 4 + 2] = 0; // B pixels[i * 4 + 3] = 255; // A } tm.create_texture("red_texture", width, height, pixels); // Verify texture view is valid WGPUTextureView view = tm.get_texture_view("red_texture"); assert(view != nullptr && "Texture view should be valid"); tm.shutdown(); fprintf(stdout, " ✓ Create texture from raw data OK\n"); } // Test 3: Create procedural texture static void test_create_procedural_texture() { fprintf(stdout, "Testing create_procedural_texture()...\n"); WebGPUTestFixture fixture; if (!fixture.init()) { fprintf(stdout, " ⚠ WebGPU unavailable - skipping test\n"); return; } TextureManager tm; tm.init(fixture.device(), fixture.queue()); // Create noise texture using procedural generator ProceduralTextureDef noise_def; noise_def.width = 64; noise_def.height = 64; noise_def.gen_func = procedural::gen_noise; noise_def.params = {1234.0f, 1.0f}; // seed, frequency tm.create_procedural_texture("noise", noise_def); // Verify texture was created WGPUTextureView view = tm.get_texture_view("noise"); assert(view != nullptr && "Procedural texture view should be valid"); tm.shutdown(); fprintf(stdout, " ✓ Create procedural texture OK\n"); } // Test 4: Get texture view for non-existent texture static void test_get_nonexistent_texture() { fprintf(stdout, "Testing get_texture_view() for non-existent texture...\n"); WebGPUTestFixture fixture; if (!fixture.init()) { fprintf(stdout, " ⚠ WebGPU unavailable - skipping test\n"); return; } TextureManager tm; tm.init(fixture.device(), fixture.queue()); // Try to get non-existent texture WGPUTextureView view = tm.get_texture_view("does_not_exist"); assert(view == nullptr && "Non-existent texture should return nullptr"); tm.shutdown(); fprintf(stdout, " ✓ Non-existent texture returns nullptr OK\n"); } // Test 5: Create multiple textures and retrieve them static void test_multiple_textures() { fprintf(stdout, "Testing multiple texture creation and retrieval...\n"); WebGPUTestFixture fixture; if (!fixture.init()) { fprintf(stdout, " ⚠ WebGPU unavailable - skipping test\n"); return; } TextureManager tm; tm.init(fixture.device(), fixture.queue()); // Create multiple textures const int size = 32; uint8_t green_pixels[32 * 32 * 4]; uint8_t blue_pixels[32 * 32 * 4]; // Fill green texture for (int i = 0; i < size * size; ++i) { green_pixels[i * 4 + 0] = 0; // R green_pixels[i * 4 + 1] = 255; // G green_pixels[i * 4 + 2] = 0; // B green_pixels[i * 4 + 3] = 255; // A } // Fill blue texture for (int i = 0; i < size * size; ++i) { blue_pixels[i * 4 + 0] = 0; // R blue_pixels[i * 4 + 1] = 0; // G blue_pixels[i * 4 + 2] = 255; // B blue_pixels[i * 4 + 3] = 255; // A } tm.create_texture("green", size, size, green_pixels); tm.create_texture("blue", size, size, blue_pixels); // Verify both textures exist WGPUTextureView green_view = tm.get_texture_view("green"); WGPUTextureView blue_view = tm.get_texture_view("blue"); assert(green_view != nullptr && "Green texture should exist"); assert(blue_view != nullptr && "Blue texture should exist"); assert(green_view != blue_view && "Textures should be different"); tm.shutdown(); fprintf(stdout, " ✓ Multiple textures OK\n"); } // Test 6: Procedural generation failure handling static void test_procedural_generation_failure() { fprintf(stdout, "Testing procedural generation failure handling...\n"); WebGPUTestFixture fixture; if (!fixture.init()) { fprintf(stdout, " ⚠ WebGPU unavailable - skipping test\n"); return; } TextureManager tm; tm.init(fixture.device(), fixture.queue()); // Create a generator function that always fails auto failing_gen = [](uint8_t* buffer, int w, int h, const float* params, int num_params) -> bool { (void)buffer; (void)w; (void)h; (void)params; (void)num_params; return false; // Simulate failure }; ProceduralTextureDef failing_def; failing_def.width = 64; failing_def.height = 64; failing_def.gen_func = failing_gen; failing_def.params = {}; // This should print error message but not crash tm.create_procedural_texture("failing_texture", failing_def); // Texture should NOT be created WGPUTextureView view = tm.get_texture_view("failing_texture"); assert(view == nullptr && "Failed procedural generation should not create texture"); tm.shutdown(); fprintf(stdout, " ✓ Procedural generation failure handled OK\n"); } // Test 7: Shutdown releases all textures static void test_shutdown_cleanup() { fprintf(stdout, "Testing shutdown() releases all textures...\n"); WebGPUTestFixture fixture; if (!fixture.init()) { fprintf(stdout, " ⚠ WebGPU unavailable - skipping test\n"); return; } TextureManager tm; tm.init(fixture.device(), fixture.queue()); // Create multiple textures uint8_t pixels[16 * 16 * 4]; memset(pixels, 128, sizeof(pixels)); tm.create_texture("texture1", 16, 16, pixels); tm.create_texture("texture2", 16, 16, pixels); tm.create_texture("texture3", 16, 16, pixels); // Verify textures exist assert(tm.get_texture_view("texture1") != nullptr); assert(tm.get_texture_view("texture2") != nullptr); assert(tm.get_texture_view("texture3") != nullptr); // Shutdown should release all textures tm.shutdown(); // After shutdown, textures should be cleared (but we can't query them // as the TextureManager's internal map is cleared) fprintf(stdout, " ✓ Shutdown cleanup OK\n"); } int main() { fprintf(stdout, "=== TextureManager Tests ===\n"); test_init_shutdown(); test_create_texture_from_data(); test_create_procedural_texture(); test_get_nonexistent_texture(); test_multiple_textures(); test_procedural_generation_failure(); test_shutdown_cleanup(); fprintf(stdout, "=== All TextureManager Tests Passed ===\n"); return 0; } #endif /* !defined(STRIP_ALL) */