import { test } from 'brittle'
import { parseFile } from '../lib/parsers/glsl.js'
test('glsl: compute shader with buffers and push constants', (t) => {
const src = `#version 450
layout(local_size_x = 256) in;
layout(set = 0, binding = 0, std430) readonly buffer DensityBuf { float density[]; };
layout(set = 0, binding = 1, std430) writeonly buffer EdgeBuf { uint edges[]; };
layout(push_constant) uniform PushConstants { uint grid_size; float iso_level; } pc;
void main() {
uint idx = 0u;
}
`
const result = parseFile(src, 'compute.glsl')
t.is(result.error, undefined, 'no parse error')
t.is(result.buffers.length, 2, '2 buffers')
t.is(result.buffers[0].name, 'DensityBuf', 'first buffer name')
t.ok(result.buffers[0].readonly, 'first buffer is readonly')
t.ok(result.buffers[1].writeonly, 'second buffer is writeonly')
t.is(result.buffers[0].binding, '0', 'binding captured')
t.is(result.buffers[0].set, '0', 'set captured')
t.is(result.pushConstants.length, 1, '1 push constant block')
t.is(result.pushConstants[0].name, 'PushConstants', 'push constant name')
t.ok(result.functions.find(f => f.name === 'main'), 'main function found')
t.ok(result.exports.find(e => e.name === 'main'), 'main is exported (shader entry)')
})
test('glsl: regular uniforms', (t) => {
const src = `#version 330
uniform float u_time;
uniform sampler2D u_texture;
uniform vec3 u_color;
void main() { }
`
const result = parseFile(src, 'frag.glsl')
t.is(result.uniforms.length, 3, '3 uniforms')
const names = result.uniforms.map(u => u.name)
t.ok(names.includes('u_time'), 'u_time uniform')
t.ok(names.includes('u_texture'), 'u_texture uniform')
t.ok(names.includes('u_color'), 'u_color uniform')
})
test('glsl: #define becomes constant', (t) => {
const src = `#define THREADS 256
#define MAX_ITERATIONS 100
void main() { }
`
const result = parseFile(src, 'compute.glsl')
const names = result.constants.map(c => c.name)
t.ok(names.includes('THREADS'), 'THREADS define captured')
t.ok(names.includes('MAX_ITERATIONS'), 'MAX_ITERATIONS define captured')
})
test('glsl: struct becomes a class with kind=struct', (t) => {
const src = `struct Vertex {
vec3 position;
vec3 normal;
vec2 uv;
};
void main() { }
`
const result = parseFile(src, 'vert.glsl')
t.is(result.classes.length, 1, '1 struct')
t.is(result.classes[0].name, 'Vertex', 'struct name')
t.is(result.classes[0].kind, 'struct', 'kind is struct')
})
test('glsl: Godot .gdshader hint syntax is stripped', (t) => {
const src = `shader_type spatial;
render_mode unshaded, cull_disabled;
uniform float density_scale : hint_range(0.0, 20.0) = 8.0;
uniform sampler3D cloud_density : filter_linear_mipmap, repeat_enable;
void fragment() {
ALBEDO = vec3(1.0);
}
`
const result = parseFile(src, 'cloud.gdshader')
t.is(result.error, undefined, 'no parse error after hint stripping')
t.is(result.uniforms.length, 2, '2 uniforms despite hints')
t.ok(result.functions.find(f => f.name === 'fragment'), 'fragment function')
})
test('glsl: parse error returns empty structure (no crash)', (t) => {
const src = `this is not valid glsl !!!`
const result = parseFile(src, 'broken.glsl')
t.ok(result.error, 'error reported')
t.is(result.functions.length, 0, 'functions is still an empty array')
t.is(result.classes.length, 0, 'classes is still an empty array')
})