CultMath GLSL tests: overloads compared by signature; the two-file header contract pinned

EveryPublicFunctionIsLowered compares (name, parameter types) after the lowering's own
step 3, so a dropped overload such as cultmath_pcg3d(vec2) fails. TwoFileHeaderContract
pins distinct guards, the MIT and MPL-2.0 provenance labels, and the MIT file's pointer
to CultMath.Phacelle.glsl. GlslLowering exposes step 3 as LowerTypeNames; output unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Meta Vens 2026-10-03 14:15:32 +02:00
commit e877b708be
2 changed files with 40 additions and 7 deletions

View file

@ -73,10 +73,7 @@ internal static class GlslLowering
source = Regex.Replace(source, @"(?ms)^\w+ \w+\([^)]*\b(Texture2D|SamplerState)\b[^)]*\)\s*\{.*?^\}[ \t]*$", string.Empty);
// 3. Type names: floatN, intN, uintN and boolN become vecN, ivecN, uvecN and bvecN.
source = Regex.Replace(source, @"\bfloat([234])\b", "vec$1");
source = Regex.Replace(source, @"\bint([234])\b", "ivec$1");
source = Regex.Replace(source, @"\buint([234])\b", "uvec$1");
source = Regex.Replace(source, @"\bbool([234])\b", "bvec$1");
source = LowerTypeNames(source);
// 4. File-scope `static const` becomes `const`.
source = Regex.Replace(source, @"(?m)^static const ", "const ");
@ -103,6 +100,15 @@ internal static class GlslLowering
return provenance + "\n#ifndef " + guard + "\n#define " + guard + "\n" + source.Trim('\n') + "\n\n#endif\n";
}
/// <summary>Step 3 alone; GlslMirrorTests maps the HLSL signatures with it to compare overloads.</summary>
internal static string LowerTypeNames(string source)
{
source = Regex.Replace(source, @"\bfloat([234])\b", "vec$1");
source = Regex.Replace(source, @"\bint([234])\b", "ivec$1");
source = Regex.Replace(source, @"\buint([234])\b", "uvec$1");
return Regex.Replace(source, @"\bbool([234])\b", "bvec$1");
}
private static readonly Regex Cast = new(@"\((float|int|uint)\)");
private static string LowerCasts(string source)

View file

@ -61,14 +61,21 @@ public sealed class GlslMirrorTests
private static readonly Regex Definition = new(@"(?m)^\w+\s+(cultmath_\w+)\s*\(([^)]*)\)");
// A definition's signature, its name and parameter types, so an overload counts on its own:
// "cultmath_pcg3d(vec2)". HLSL type names are mapped by the lowering's own step 3.
private static string Signature(Match definition) =>
definition.Groups[1].Value + "(" + string.Join(", ", definition.Groups[2].Value
.Split(',', StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries)
.Select(parameter => GlslLowering.LowerTypeNames(string.Join(' ', Regex.Split(parameter, @"\s+")[..^1])))) + ")";
[Fact]
public void EveryPublicFunctionIsLowered()
{
var lowered = Lowered;
var hlsl = Definition.Matches(Hlsl).Where(m => !Regex.IsMatch(m.Groups[2].Value, @"\b(Texture2D|SamplerState)\b"))
.Select(m => m.Groups[1].Value).ToHashSet();
var glsl = lowered.SelectMany(output => Definition.Matches(output.Text)).Select(m => m.Groups[1].Value).ToHashSet();
Assert.Contains("cultmath_snoise", glsl);
.Select(Signature).ToHashSet();
var glsl = lowered.SelectMany(output => Definition.Matches(output.Text)).Select(Signature).ToHashSet();
Assert.Contains("cultmath_pcg3d(vec2)", glsl);
Assert.True(hlsl.SetEquals(glsl), "HLSL only: " + string.Join(", ", hlsl.Except(glsl)) + "; GLSL only: " + string.Join(", ", glsl.Except(hlsl)));
// The MPL-2.0 code is in its own file and nowhere in the MIT library.
@ -81,6 +88,26 @@ public sealed class GlslMirrorTests
}
}
// The two-file contract a consumer relies on when it concatenates CultMath.Phacelle.glsl after
// CultMath.glsl: distinct guards (a shared one would hide the second file's body), each provenance
// line naming its file's licence, and the MIT file pointing at the one that holds cultmath_phacelle.
[Fact]
public void TwoFileHeaderContract()
{
var lowered = Lowered;
var guards = lowered.Select(output => Regex.Match(output.Text, @"^[^\n]*\n#ifndef (\w+)\n#define \1\n").Groups[1].Value).ToArray();
Assert.All(guards, guard => Assert.NotEqual(string.Empty, guard));
Assert.Equal(guards.Length, guards.Distinct().Count());
var library = lowered.Single(output => output.File == "CultMath.glsl").Text.Split('\n')[0];
var phacelle = lowered.Single(output => output.File == "CultMath.Phacelle.glsl").Text.Split('\n')[0];
Assert.Contains(" MIT.", library);
Assert.DoesNotContain("MPL", library);
Assert.Contains("cultmath_phacelle is in CultMath.Phacelle.glsl", library);
Assert.Contains(" MPL-2.0;", phacelle);
Assert.DoesNotContain("MIT", phacelle);
}
[Fact]
public void GoldenFixtureMatchesCSharp()
{