CultMath: the fixture cases separate each lattice-loop mutant from the shader text
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
773a3efbc9
commit
70914d9a95
2 changed files with 62 additions and 1 deletions
|
|
@ -0,0 +1,59 @@
|
|||
using System.Reflection;
|
||||
using System.Text.RegularExpressions;
|
||||
using Xunit;
|
||||
|
||||
namespace CultMath.Tests;
|
||||
|
||||
public sealed partial class GlslMirrorTests
|
||||
{
|
||||
private const string Weighted = "cultmath_weighted_lattice_cells(";
|
||||
private const string SmoothStep = "cultmath_lattice_smooth_step(";
|
||||
|
||||
// Edits of the shader text that each break one rule of the lattice loop. M7 (the exit one ring late) is absent: by the
|
||||
// window-independence proof a later exit visits more cells and changes nothing, so no case can kill it.
|
||||
private static readonly (string Name, string Function, string Old, string New, int Kills)[] LatticeMutants =
|
||||
{
|
||||
("M1 exit one ring early", Weighted, "0.5 * (float)(k - 1) >= max(f2", "0.5 * (float)k >= max(f2", 3),
|
||||
("M2 exit ignores the smoothing band", Weighted, ">= max(f2, f1 + 2.0 * smoothing))", ">= f2)", 1),
|
||||
("M3 no pre-normalisation of the normal", Weighted, "normal = stretchNormal / scale;", "normal = stretchNormal;", 1),
|
||||
("M4 no unit length for the normal", Weighted, "normal = normal / length(normal);", "normal = normal * 1.0;", 1),
|
||||
("M5 smooth step without its clamp", SmoothStep, "return min(next, f1 + smoothing);", "return next;", 1),
|
||||
("M6 box pass skips its first row", Weighted, "for (int dz = -reach; dz <= reach; dz++)", "for (int dz = -reach + 1; dz <= reach; dz++)", 1),
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Each rule of the lattice loop is pinned by the committed fixture's own cases: the shader text with one rule broken (a
|
||||
/// mutant mirror, the text a device runs) returns another result than the fixture on at least the stated number of
|
||||
/// cases of the family that exercises it, the exit one ring early on at least three. The device judge holds the same
|
||||
/// cases to their recorded bound, so a mutant these cases separate fails it too.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void TheFixtureCasesSeparateEveryLatticeMutantFromTheShader()
|
||||
{
|
||||
var families = FixtureFamilies();
|
||||
foreach (var (name, function, old, replacement, kills) in LatticeMutants)
|
||||
{
|
||||
var (assembly, errors) = HlslSourceCompatibilityTests.CompileShaderMirror(mutate: text =>
|
||||
{
|
||||
Assert.True(text.Split(old).Length == 2, $"{name}: the shader text no longer holds exactly one \"{old}\"");
|
||||
return text.Replace(old, replacement);
|
||||
});
|
||||
Assert.True(assembly is not null, name + ": " + string.Join(Environment.NewLine, errors));
|
||||
var shaderType = assembly!.GetType("CultMathHlsl.HlslShader")!;
|
||||
var shader = Activator.CreateInstance(shaderType);
|
||||
var mirror = shaderType.GetMethod(function[..^1], BindingFlags.Instance | BindingFlags.NonPublic)!;
|
||||
|
||||
var f = Array.FindIndex(Families, family => family.Glsl.Contains(function));
|
||||
var method = Resolve(Families[f].Glsl);
|
||||
var killed = 0;
|
||||
foreach (var entry in families[f]!["cases"]!.AsArray())
|
||||
{
|
||||
var sides = Sides(entry);
|
||||
if (Hex(new[] { mirror.Invoke(shader, Arguments(method, sides[0]))! }) != sides[1])
|
||||
killed++;
|
||||
}
|
||||
|
||||
Assert.True(killed >= kills, $"{name}: the {Families[f].Glsl} fixture cases separate it on {killed} of {Points}, fewer than {kills}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -782,9 +782,11 @@ public sealed class HlslSourceCompatibilityTests
|
|||
return "using CultMath;\nusing static CultMath.math;\nnamespace CultMathHlsl;\npublic class HlslShader\n{\n" + members + source + "\n}\n";
|
||||
}
|
||||
|
||||
internal static (Assembly? Assembly, IReadOnlyList<string> Errors) CompileShaderMirror(string members = "", IReadOnlyDictionary<string, int>? defines = null)
|
||||
internal static (Assembly? Assembly, IReadOnlyList<string> Errors) CompileShaderMirror(string members = "", IReadOnlyDictionary<string, int>? defines = null, Func<string, string>? mutate = null)
|
||||
{
|
||||
var hlsl = ReadShaderSource(FindCultMathRoot());
|
||||
if (mutate is not null)
|
||||
hlsl = mutate(hlsl);
|
||||
var tree = CSharpSyntaxTree.ParseText(TransformHlslToCSharp(hlsl, members, defines));
|
||||
var references = ((string)AppContext.GetData("TRUSTED_PLATFORM_ASSEMBLIES")!)
|
||||
.Split(Path.PathSeparator)
|
||||
|
|
|
|||
Loading…
Reference in a new issue