diff --git a/tests/SharpEmu.Libs.Tests/Fiber/FiberExportsTests.cs b/tests/SharpEmu.Libs.Tests/Fiber/FiberExportsTests.cs
new file mode 100644
index 00000000..9b73ef2d
--- /dev/null
+++ b/tests/SharpEmu.Libs.Tests/Fiber/FiberExportsTests.cs
@@ -0,0 +1,332 @@
+// Copyright (C) 2026 SharpEmu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+using System.Buffers.Binary;
+
+using SharpEmu.HLE;
+using SharpEmu.Libs.Fiber;
+
+using Xunit;
+
+namespace SharpEmu.Libs.Tests.Fiber;
+
+///
+/// Contract tests for the libSceFiber HLE exports. These pin the current
+/// validation and layout behaviour of ; they do not
+/// exercise a live guest thread scheduler.
+///
+public sealed class FiberExportsTests
+{
+ private const ulong Base = 0x3_0000_0000UL;
+ private const int RegionSize = 0x2000;
+
+ private const int ErrorNull = unchecked((int)0x80590001);
+ private const int ErrorAlignment = unchecked((int)0x80590002);
+ private const int ErrorRange = unchecked((int)0x80590003);
+ private const int ErrorInvalid = unchecked((int)0x80590004);
+ private const int ErrorPermission = unchecked((int)0x80590005);
+
+ private const uint SignatureStart = 0xDEF1649Cu;
+ private const uint SignatureEnd = 0xB37592A0u;
+ private const ulong StackSignature = 0x7149F2CA7149F2CAUL;
+ private const uint StateIdle = 2;
+
+ private const ulong FiberAddress = Base;
+ private const ulong NameAddress = Base + 0x200;
+ private const ulong ContextAddress = Base + 0x400;
+ private const ulong EntryAddress = 0x4_0000_1000UL;
+ private const ulong InfoAddress = Base + 0x800;
+
+ public FiberExportsTests()
+ {
+ FiberExports.ResetRuntimeState();
+ }
+
+ [Fact]
+ public void OptParamInitialize_NullParam_ReturnsNullError()
+ {
+ var memory = new FakeCpuMemory(Base, RegionSize);
+ var context = new CpuContext(memory, Generation.Gen5);
+
+ context[CpuRegister.Rdi] = 0;
+
+ var result = FiberExports.FiberOptParamInitialize(context);
+
+ Assert.Equal(ErrorNull, result);
+ }
+
+ [Fact]
+ public void GetSelf_NullOutAddress_ReturnsNullError()
+ {
+ var memory = new FakeCpuMemory(Base, RegionSize);
+ var context = new CpuContext(memory, Generation.Gen5);
+
+ context[CpuRegister.Rdi] = 0;
+
+ var result = FiberExports.FiberGetSelf(context);
+
+ Assert.Equal(ErrorNull, result);
+ }
+
+ [Fact]
+ public void GetSelf_OutsideFiberContext_ReturnsPermissionError()
+ {
+ var memory = new FakeCpuMemory(Base, RegionSize);
+ var context = new CpuContext(memory, Generation.Gen5);
+
+ context[CpuRegister.Rdi] = Base + 0x100;
+
+ var result = FiberExports.FiberGetSelf(context);
+
+ Assert.Equal(ErrorPermission, result);
+ }
+
+ [Fact]
+ public void GetInfo_NullInfo_ReturnsNullError()
+ {
+ var memory = new FakeCpuMemory(Base, RegionSize);
+ var context = new CpuContext(memory, Generation.Gen5);
+
+ context[CpuRegister.Rdi] = FiberAddress;
+ context[CpuRegister.Rsi] = 0;
+
+ var result = FiberExports.FiberGetInfo(context);
+
+ Assert.Equal(ErrorNull, result);
+ }
+
+ [Fact]
+ public void Initialize_NullFiber_ReturnsNullError()
+ {
+ var memory = new FakeCpuMemory(Base, RegionSize);
+ var context = new CpuContext(memory, Generation.Gen5);
+ WriteCString(memory, NameAddress, "F");
+
+ context[CpuRegister.Rdi] = 0;
+ context[CpuRegister.Rsi] = NameAddress;
+ context[CpuRegister.Rdx] = EntryAddress;
+
+ var result = FiberExports.FiberInitialize(context);
+
+ Assert.Equal(ErrorNull, result);
+ }
+
+ [Fact]
+ public void Initialize_NullName_ReturnsNullError()
+ {
+ var memory = new FakeCpuMemory(Base, RegionSize);
+ var context = new CpuContext(memory, Generation.Gen5);
+
+ context[CpuRegister.Rdi] = FiberAddress;
+ context[CpuRegister.Rsi] = 0;
+ context[CpuRegister.Rdx] = EntryAddress;
+
+ var result = FiberExports.FiberInitialize(context);
+
+ Assert.Equal(ErrorNull, result);
+ }
+
+ [Fact]
+ public void Initialize_NullEntry_ReturnsNullError()
+ {
+ var memory = new FakeCpuMemory(Base, RegionSize);
+ var context = new CpuContext(memory, Generation.Gen5);
+ WriteCString(memory, NameAddress, "F");
+
+ context[CpuRegister.Rdi] = FiberAddress;
+ context[CpuRegister.Rsi] = NameAddress;
+ context[CpuRegister.Rdx] = 0;
+
+ var result = FiberExports.FiberInitialize(context);
+
+ Assert.Equal(ErrorNull, result);
+ }
+
+ [Fact]
+ public void Initialize_MisalignedFiber_ReturnsAlignmentError()
+ {
+ var memory = new FakeCpuMemory(Base, RegionSize);
+ var context = new CpuContext(memory, Generation.Gen5);
+ WriteCString(memory, NameAddress, "F");
+
+ context[CpuRegister.Rdi] = FiberAddress + 4;
+ context[CpuRegister.Rsi] = NameAddress;
+ context[CpuRegister.Rdx] = EntryAddress;
+
+ var result = FiberExports.FiberInitialize(context);
+
+ Assert.Equal(ErrorAlignment, result);
+ }
+
+ [Fact]
+ public void Initialize_TooSmallContextSize_ReturnsRangeError()
+ {
+ var memory = new FakeCpuMemory(Base, RegionSize);
+ var context = new CpuContext(memory, Generation.Gen5);
+ WriteCString(memory, NameAddress, "F");
+
+ context[CpuRegister.Rdi] = FiberAddress;
+ context[CpuRegister.Rsi] = NameAddress;
+ context[CpuRegister.Rdx] = EntryAddress;
+ context[CpuRegister.R8] = ContextAddress;
+ context[CpuRegister.R9] = 256;
+
+ var result = FiberExports.FiberInitialize(context);
+
+ Assert.Equal(ErrorRange, result);
+ }
+
+ [Fact]
+ public void Initialize_ContextAddressWithoutSize_ReturnsInvalidError()
+ {
+ var memory = new FakeCpuMemory(Base, RegionSize);
+ var context = new CpuContext(memory, Generation.Gen5);
+ WriteCString(memory, NameAddress, "F");
+
+ context[CpuRegister.Rdi] = FiberAddress;
+ context[CpuRegister.Rsi] = NameAddress;
+ context[CpuRegister.Rdx] = EntryAddress;
+ context[CpuRegister.R8] = ContextAddress;
+ context[CpuRegister.R9] = 0;
+
+ var result = FiberExports.FiberInitialize(context);
+
+ Assert.Equal(ErrorInvalid, result);
+ }
+
+ [Fact]
+ public void Initialize_Valid_WritesExpectedLayout()
+ {
+ var memory = new FakeCpuMemory(Base, RegionSize);
+ var context = new CpuContext(memory, Generation.Gen5);
+ const string name = "TestFiber";
+ WriteCString(memory, NameAddress, name);
+ const ulong argOnInitialize = 0xDEADUL;
+ const ulong contextSize = 512UL;
+
+ context[CpuRegister.Rdi] = FiberAddress;
+ context[CpuRegister.Rsi] = NameAddress;
+ context[CpuRegister.Rdx] = EntryAddress;
+ context[CpuRegister.Rcx] = argOnInitialize;
+ context[CpuRegister.R8] = ContextAddress;
+ context[CpuRegister.R9] = contextSize;
+ // RSP defaults to 0 (unmapped); ReadStackArg64 falls back to 0 ->
+ // optParam = 0, buildVersion = 0. ApplyInitializationFlags(0, 0, false) == 0.
+
+ var result = FiberExports.FiberInitialize(context);
+
+ Assert.Equal(0, result);
+ Assert.Equal(0UL, context[CpuRegister.Rax]);
+
+ Assert.Equal(SignatureStart, ReadUInt32(memory, FiberAddress + 0));
+ Assert.Equal(StateIdle, ReadUInt32(memory, FiberAddress + 4));
+ Assert.Equal(EntryAddress, ReadUInt64(memory, FiberAddress + 8));
+ Assert.Equal(argOnInitialize, ReadUInt64(memory, FiberAddress + 16));
+ Assert.Equal(ContextAddress, ReadUInt64(memory, FiberAddress + 24));
+ Assert.Equal(contextSize, ReadUInt64(memory, FiberAddress + 32));
+ AssertInlineName(memory, FiberAddress + 40, name);
+ Assert.Equal(0UL, ReadUInt64(memory, FiberAddress + 72));
+ Assert.Equal(0u, ReadUInt32(memory, FiberAddress + 80));
+ Assert.Equal(ContextAddress, ReadUInt64(memory, FiberAddress + 88));
+ Assert.Equal(ContextAddress + contextSize, ReadUInt64(memory, FiberAddress + 96));
+ Assert.Equal(SignatureEnd, ReadUInt32(memory, FiberAddress + 104));
+ Assert.Equal(StackSignature, ReadUInt64(memory, ContextAddress));
+ }
+
+ [Fact]
+ public void GetInfo_AfterInitialize_RoundTripsFields()
+ {
+ var memory = new FakeCpuMemory(Base, RegionSize);
+ var context = new CpuContext(memory, Generation.Gen5);
+ const string name = "RoundTrip";
+ WriteCString(memory, NameAddress, name);
+ const ulong argOnInitialize = 0xCAFEUL;
+ const ulong contextSize = 512UL;
+
+ context[CpuRegister.Rdi] = FiberAddress;
+ context[CpuRegister.Rsi] = NameAddress;
+ context[CpuRegister.Rdx] = EntryAddress;
+ context[CpuRegister.Rcx] = argOnInitialize;
+ context[CpuRegister.R8] = ContextAddress;
+ context[CpuRegister.R9] = contextSize;
+
+ Assert.Equal(0, FiberExports.FiberInitialize(context));
+
+ WriteUInt64(memory, InfoAddress, 128);
+ context[CpuRegister.Rdi] = FiberAddress;
+ context[CpuRegister.Rsi] = InfoAddress;
+
+ var result = FiberExports.FiberGetInfo(context);
+
+ Assert.Equal(0, result);
+ Assert.Equal(EntryAddress, ReadUInt64(memory, InfoAddress + 8));
+ Assert.Equal(argOnInitialize, ReadUInt64(memory, InfoAddress + 16));
+ Assert.Equal(ContextAddress, ReadUInt64(memory, InfoAddress + 24));
+ Assert.Equal(contextSize, ReadUInt64(memory, InfoAddress + 32));
+ AssertInlineName(memory, InfoAddress + 40, name);
+ Assert.Equal(ulong.MaxValue, ReadUInt64(memory, InfoAddress + 72));
+ }
+
+ [Fact]
+ public void GetInfo_WrongSize_ReturnsInvalidError()
+ {
+ var memory = new FakeCpuMemory(Base, RegionSize);
+ var context = new CpuContext(memory, Generation.Gen5);
+ WriteCString(memory, NameAddress, "F");
+
+ context[CpuRegister.Rdi] = FiberAddress;
+ context[CpuRegister.Rsi] = NameAddress;
+ context[CpuRegister.Rdx] = EntryAddress;
+ context[CpuRegister.R8] = ContextAddress;
+ context[CpuRegister.R9] = 512;
+
+ Assert.Equal(0, FiberExports.FiberInitialize(context));
+
+ WriteUInt64(memory, InfoAddress, 64);
+ context[CpuRegister.Rdi] = FiberAddress;
+ context[CpuRegister.Rsi] = InfoAddress;
+
+ var result = FiberExports.FiberGetInfo(context);
+
+ Assert.Equal(ErrorInvalid, result);
+ }
+
+ private static void WriteCString(FakeCpuMemory memory, ulong address, string text)
+ {
+ memory.WriteCString(address, text);
+ }
+
+ private static void WriteUInt64(FakeCpuMemory memory, ulong address, ulong value)
+ {
+ Span buffer = stackalloc byte[sizeof(ulong)];
+ BinaryPrimitives.WriteUInt64LittleEndian(buffer, value);
+ Assert.True(memory.TryWrite(address, buffer));
+ }
+
+ private static uint ReadUInt32(FakeCpuMemory memory, ulong address)
+ {
+ Span buffer = stackalloc byte[sizeof(uint)];
+ Assert.True(memory.TryRead(address, buffer));
+ return BinaryPrimitives.ReadUInt32LittleEndian(buffer);
+ }
+
+ private static ulong ReadUInt64(FakeCpuMemory memory, ulong address)
+ {
+ Span buffer = stackalloc byte[sizeof(ulong)];
+ Assert.True(memory.TryRead(address, buffer));
+ return BinaryPrimitives.ReadUInt64LittleEndian(buffer);
+ }
+
+ private static void AssertInlineName(FakeCpuMemory memory, ulong address, string expected)
+ {
+ Span buffer = stackalloc byte[32];
+ Assert.True(memory.TryRead(address, buffer));
+ var length = buffer.IndexOf((byte)0);
+ if (length < 0)
+ {
+ length = buffer.Length;
+ }
+
+ Assert.Equal(expected, System.Text.Encoding.UTF8.GetString(buffer[..length]));
+ }
+}