<dependency>
<groupId>com.hellblazer.luciferase</groupId>
<artifactId>gpu-test-framework</artifactId>
<version>0.0.1-SNAPSHOT</version>
<scope>test</scope>
</dependency>import com.hellblazer.luciferase.gpu.test.CICompatibleGPUTest;
import org.junit.jupiter.api.Test;
public class MyGPUTest extends CICompatibleGPUTest {
@Test
void testGPUComputation() {
withGPUContext(context -> {
// Your GPU code here
float[] data = new float[1000];
// Process on GPU...
assertTrue(context.isValid());
});
}
}The framework automatically selects the best available GPU backend:
import com.hellblazer.luciferase.gpu.test.support.TestSupportMatrix;
public class AutoSelectExample {
@Test
void testWithAutoBackend() {
var supportMatrix = new TestSupportMatrix();
// Check what's available
if (supportMatrix.getBackendSupport(TestSupportMatrix.Backend.OPENCL)
== TestSupportMatrix.SupportLevel.FULL) {
// Use OpenCL
runOpenCLTest();
} else if (supportMatrix.getBackendSupport(TestSupportMatrix.Backend.METAL)
== TestSupportMatrix.SupportLevel.FULL) {
// Use Metal (macOS)
runMetalTest();
} else {
// Fallback to mock
runMockTest();
}
}
}Tests automatically detect CI environments and use mock implementations:
public class CISafeTest extends CICompatibleGPUTest {
@BeforeEach
void checkEnvironment() {
if (isCI()) {
// Automatically uses mock GPU
log.info("CI detected - using mock GPU backend");
}
}
@Test
void testThatWorksInCI() {
// This test will use real GPU locally, mock in CI
withGPUContext(context -> {
var result = context.compute(data);
assertNotNull(result);
});
}
}import org.lwjgl.opencl.*;
public class OpenCLExample {
@Test
void testOpenCLRayTraversal() {
// Create test data
Ray[] rays = generateRays(1000);
OctreeNode[] octree = generateOctree(8);
// Create OpenCL buffers
long rayBuffer = BufferUtils.createRayBuffer(context, rays, CL10.CL_MEM_READ_ONLY);
long octreeBuffer = BufferUtils.createNodeBuffer(context, octree, CL10.CL_MEM_READ_ONLY);
// Execute kernel
CL10.clEnqueueNDRangeKernel(queue, kernel, 1, null, stackPointers(rays.length), null, null, null);
// Read results
IntersectionResult[] results = BufferUtils.readResults(queue, resultBuffer, rays.length);
// Validate
assertTrue(results.length == rays.length);
}
}import org.openjdk.jmh.annotations.*;
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MICROSECONDS)
public class GPUBenchmark {
@Benchmark
public void benchmarkCPU() {
processCPU(testData);
}
@Benchmark
public void benchmarkGPU() {
processGPU(testData);
}
}Run benchmarks:
mvn test -Dtest=*Benchmarkpublic class MemoryTransferTest {
@Test
void analyzeTransferOverhead() {
int[] sizes = {1_000, 10_000, 100_000, 1_000_000};
for (int size : sizes) {
long cpuTime = timeCPUProcessing(size);
long gpuTime = timeGPUProcessing(size);
long transferTime = timeMemoryTransfer(size);
float overhead = (float)transferTime / gpuTime;
if (overhead < 0.1) {
log.info("Size {}: GPU beneficial ({}x speedup)",
size, cpuTime/gpuTime);
} else {
log.info("Size {}: CPU better (transfer overhead {}%)",
size, overhead * 100);
}
}
}
}Ensure GPU and CPU implementations produce identical results:
@Test
void testCrossValidation() {
var input = generateTestData();
var cpuResult = processCPU(input);
var gpuResult = processGPU(input);
assertArrayEquals(cpuResult, gpuResult, 1e-6f);
}@Test
void testWithFallback() {
try {
// Try GPU first
var result = processGPU(data);
assertNotNull(result);
} catch (GPUNotAvailableException e) {
// Fallback to CPU
var result = processCPU(data);
assertNotNull(result);
}
}@Test
@EnabledOnOs(OS.MAC)
void testMetalBackend() {
assumeTrue(metalAvailable());
// Metal-specific test
}
@Test
@EnabledOnOs({OS.LINUX, OS.WINDOWS})
void testVulkanBackend() {
assumeTrue(vulkanAvailable());
// Vulkan-specific test
}mvn testmvn test -Pgpu-testsmvn test -Pgpu-benchmarkmvn test -DargLine="--enable-native-access=ALL-UNNAMED"CI=true mvn test # Automatically uses mock backends<configuration>
<logger name="com.hellblazer.luciferase.gpu" level="DEBUG"/>
</configuration>var matrix = new TestSupportMatrix();
matrix.printSupportMatrix();mvn test -Dtest=HeadlessPlatformValidationTestSolution: Add -XstartOnFirstThread for GLFW tests:
mvn test -DargLine="-XstartOnFirstThread"Solution: Install OpenCL drivers or use mock backend:
@Test
void testWithMockFallback() {
assumeTrue(openCLAvailable() || mockAvailable());
// Test continues with available backend
}Solution: Reduce data size or batch processing:
var batchSize = 10000;
for (int i = 0; i < totalSize; i += batchSize) {
processGPUBatch(data, i, Math.min(i + batchSize, totalSize));
}- Always validate GPU availability before running GPU-specific code
- Provide CPU fallbacks for critical functionality
- Cross-validate results between CPU and GPU implementations
- Profile memory transfers to ensure GPU benefit
- Use appropriate data sizes - GPU benefits from larger workloads
- Clean up resources - Always release GPU buffers and contexts
- Test in CI - Ensure tests work with mock backends
See the examples/ directory for complete examples:
ray-tracing/- ESVO ray traversal implementationmatrix-multiply/- Basic GPU computation exampleimage-processing/- GPU image filtersmachine-learning/- Neural network GPU acceleration