sharpemu/tests/SharpEmu.ShaderCompiler.Tests/Resources/ResourceMaterializationProf...

116 lines
4.2 KiB
C#

// Copyright (C) 2026 SharpEmu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
using System.Globalization;
using System.Text.RegularExpressions;
using SharpEmu.ShaderCompiler.Resources;
using Xunit;
namespace SharpEmu.ShaderCompiler.Tests.Resources;
[CollectionDefinition("ResourceMaterializationProfile", DisableParallelization = true)]
public sealed class ResourceMaterializationProfileCollection;
[Collection("ResourceMaterializationProfile")]
public sealed class ResourceMaterializationProfileTests
{
[Theory]
[InlineData(null, null, false)]
[InlineData("1", null, false)]
[InlineData(null, "1", false)]
[InlineData("1", "0", false)]
[InlineData("0", "1", false)]
[InlineData("1", "1", true)]
public void DetailedMeasurementsRequireBothSwitches(string? performance, string? frameTrace, bool expected)
=> Assert.Equal(expected, ResourceMaterializationProfile.IsEnabled(performance, frameTrace));
[Fact]
public void ScopeRecordsAllocationAndExceptionalExitWhenEnabled()
{
var before = ReadReport();
Assert.Throws<InvalidOperationException>((Action)(() =>
{
using var measurement = ResourceMaterializationProfile.Measure(ResourceMaterializationProfile.Phase.Total);
GC.KeepAlive(new byte[4096]);
throw new InvalidOperationException();
}));
var after = ReadReport();
if (!ResourceMaterializationProfile.Enabled)
{
Assert.Empty(after);
return;
}
Assert.Equal(ReadTotalCalls(before) + 1, ReadTotalCalls(after));
Assert.True(ReadCounter(after, "allocated_bytes") - ReadCounter(before, "allocated_bytes") >= 4096);
default(ResourceMaterializationProfile.Scope).Dispose();
Assert.Equal(after, ReadReport());
}
[Fact]
public void ActivityCountsSourcesWithoutChangingThem()
{
bool[] activeSources = [true, false, false, true];
var before = ReadReport();
ResourceMaterializationProfile.RecordActivity(activeSources);
var after = ReadReport();
Assert.Equal(new[] { true, false, false, true }, activeSources);
if (!ResourceMaterializationProfile.Enabled)
{
Assert.Empty(after);
return;
}
Assert.Equal(ReadCounter(before, "branch_evaluations") + 1, ReadCounter(after, "branch_evaluations"));
Assert.Equal(ReadCounter(before, "descriptor_sources") + 4, ReadCounter(after, "descriptor_sources"));
Assert.Equal(ReadCounter(before, "inactive_sources") + 2, ReadCounter(after, "inactive_sources"));
}
[Fact]
public void ConcurrentScopesDoNotLoseCalls()
{
var before = ReadReport();
Parallel.For(0, 1000, _ =>
{
using var measurement = ResourceMaterializationProfile.Measure(ResourceMaterializationProfile.Phase.Total);
});
var after = ReadReport();
if (!ResourceMaterializationProfile.Enabled)
{
Assert.Empty(after);
return;
}
Assert.Equal(ReadTotalCalls(before) + 1000, ReadTotalCalls(after));
}
[Fact]
public void ReportUsesInvariantDecimalSeparators()
{
var previousCulture = CultureInfo.CurrentCulture;
try
{
CultureInfo.CurrentCulture = CultureInfo.GetCultureInfo("de-DE");
var report = ReadReport();
if (ResourceMaterializationProfile.Enabled)
Assert.Matches(@"Total=\d+\.\d{3}ms/n\d+", report);
else
Assert.Empty(report);
}
finally
{
CultureInfo.CurrentCulture = previousCulture;
}
}
private static string ReadReport()
{
using var output = new StringWriter();
ResourceMaterializationProfile.WriteReport(output);
return output.ToString();
}
private static long ReadTotalCalls(string report) =>
long.Parse(Regex.Match(report, @"Total=[\d.]+ms/n(\d+)").Groups[1].Value, CultureInfo.InvariantCulture);
private static long ReadCounter(string report, string name) =>
long.Parse(Regex.Match(report, $@"\b{name}=(\d+)").Groups[1].Value, CultureInfo.InvariantCulture);
}