ModernUO/Projects/Server.Tests/Tests/Serialization/FileBufferWriterTests.cs
Kamron Batman 3b11a376f0
perf(saves): delete SerializedTypes.db write path and runtime type tracking
The idx v4 type table supersedes the global type queue: workers no longer
enqueue every Write(Type) into a shared ConcurrentQueue during the freeze,
WriteFiles no longer drains and dedupes millions of entries, and the db file
is no longer produced. Loading old saves still reads SerializedTypes.db via
the legacy v2/v3 path.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 22:38:49 -07:00

136 lines
3.7 KiB
C#

using System;
using System.IO;
using System.Text;
using Xunit;
namespace Server.Tests;
public class FileBufferWriterTests
{
private static string TempFile() =>
Path.Combine(Path.GetTempPath(), $"muo-fbw-{Guid.NewGuid():N}.bin");
[Fact]
public void DrainsAcrossStagingBoundariesAndPatchesBackwards()
{
var path = TempFile();
try
{
// Tiny staging block so every few records cross a drain; mirrors the idx
// pattern: version, count placeholder, records, backwards count patch.
using (var writer = new FileBufferWriter(path, expectedSize: 64))
{
writer.Write(3); // version
var countPosition = writer.Position;
writer.Write(0);
const int records = 1000;
for (var i = 0; i < records; i++)
{
writer.Write((ulong)i * 0x9E3779B97F4A7C15);
writer.Write(i);
}
var end = writer.Position;
writer.Seek(countPosition, SeekOrigin.Begin);
writer.Write(records);
writer.Seek(0, SeekOrigin.End);
Assert.Equal(end, writer.Position);
}
var bytes = File.ReadAllBytes(path);
Assert.Equal(4 + 4 + 1000 * 12, bytes.Length);
IGenericReader reader = new BufferReader(bytes);
Assert.Equal(3, reader.ReadInt());
Assert.Equal(1000, reader.ReadInt());
for (var i = 0; i < 1000; i++)
{
Assert.Equal((ulong)i * 0x9E3779B97F4A7C15, reader.ReadULong());
Assert.Equal(i, reader.ReadInt());
}
}
finally
{
File.Delete(path);
}
}
[Fact]
public void OversizedSingleItemGrowsTheStagingBlock()
{
var path = TempFile();
try
{
// A string whose worst-case reservation exceeds the staging block must grow
// the block instead of deadlocking the drain loop.
var value = new string('二', 500); // 1500 bytes utf8, staging 64
using (var writer = new FileBufferWriter(path, expectedSize: 64))
{
writer.Write(value);
writer.Write(0xC0FFEE);
}
IGenericReader reader = new BufferReader(File.ReadAllBytes(path));
Assert.Equal(value, reader.ReadString());
Assert.Equal(0xC0FFEE, reader.ReadInt());
}
finally
{
File.Delete(path);
}
}
[Fact]
public void SpanWritesCrossDrains()
{
var path = TempFile();
try
{
var payload = new byte[777];
new System.Random(0x5EED).NextBytes(payload);
using (var writer = new FileBufferWriter(path, expectedSize: 64))
{
writer.Write((ReadOnlySpan<byte>)payload);
}
Assert.Equal(payload, File.ReadAllBytes(path));
}
finally
{
File.Delete(path);
}
}
[Fact]
public void TypeWritesUseTagAndHashFormat()
{
var path = TempFile();
try
{
using (var writer = new FileBufferWriter(path))
{
writer.Write(typeof(string));
writer.Write((Type)null);
}
var bytes = File.ReadAllBytes(path);
Assert.Equal(1 + 8 + 1, bytes.Length); // flag + hash + null flag
Assert.Equal(2, bytes[0]);
Assert.Equal(0, bytes[^1]);
}
finally
{
File.Delete(path);
}
}
}