sharpemu/tests/SharpEmu.Libs.Tests/Memory/GuestMemory/FailingHostViews.cs

139 lines
4.0 KiB
C#

// Copyright (C) 2026 SharpEmu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
using SharpEmu.HLE.Host;
namespace SharpEmu.Libs.Tests.Memory.GuestMemory;
// Delegates to the real host. One chosen call fails without reaching the host.
internal sealed class FailingHostViews : IHostViewMemory
{
public enum Op
{
CreateBacking,
ReserveHole,
SplitHole,
JoinHoles,
FreeHole,
MapView,
UnmapView,
CommitPrivate,
ReleasePrivate,
ChangeAccess,
FreeOwnedRange,
}
private readonly IHostViewMemory _inner;
private Op? _failOp;
private int _failAfter;
private HostViewFailure _failure;
public FailingHostViews(IHostViewMemory inner)
{
_inner = inner;
}
public List<Op> Log { get; } = new();
public Action? AfterMapView { get; set; }
public Action<ulong, ulong>? AfterUnmapView { get; set; }
public Action<ulong, ulong>? BeforeReserveHole { get; set; }
public Action<ulong, ulong>? BeforeChangeAccess { get; set; }
public ulong PageSize => _inner.PageSize;
public ulong Granularity => _inner.Granularity;
public void FailNext(Op op, int afterCalls = 0, HostViewFailure failure = HostViewFailure.FixedMapFailed)
{
_failOp = op;
_failAfter = afterCalls;
_failure = failure;
}
public bool TryCreateBacking(ulong size, out HostBackingObject? backing, out HostViewFailure failure)
{
if (ShouldFail(Op.CreateBacking))
{
backing = null;
failure = HostViewFailure.BackingUnavailable;
return false;
}
return _inner.TryCreateBacking(size, out backing, out failure);
}
public ulong ReserveHole(ulong address, ulong size)
{
BeforeReserveHole?.Invoke(address, size);
return ShouldFail(Op.ReserveHole) ? 0 : _inner.ReserveHole(address, size);
}
public bool SplitHole(ulong address, ulong size) =>
!ShouldFail(Op.SplitHole) && _inner.SplitHole(address, size);
public bool JoinHoles(ulong address, ulong size) =>
!ShouldFail(Op.JoinHoles) && _inner.JoinHoles(address, size);
public bool FreeHole(ulong address, ulong size) =>
!ShouldFail(Op.FreeHole) && _inner.FreeHole(address, size);
public bool TryMapView(HostBackingObject backing, ulong address, ulong offset, ulong size, HostPageProtection protection, out HostViewFailure failure)
{
if (ShouldFail(Op.MapView))
{
failure = _failure;
return false;
}
var mapped = _inner.TryMapView(backing, address, offset, size, protection, out failure);
AfterMapView?.Invoke();
return mapped;
}
public bool UnmapView(ulong address, ulong size)
{
if (ShouldFail(Op.UnmapView) || !_inner.UnmapView(address, size)) return false;
AfterUnmapView?.Invoke(address, size);
return true;
}
public bool CommitPrivate(ulong address, ulong size, HostPageProtection protection) =>
!ShouldFail(Op.CommitPrivate) && _inner.CommitPrivate(address, size, protection);
public bool ReleasePrivate(ulong address, ulong size) =>
!ShouldFail(Op.ReleasePrivate) && _inner.ReleasePrivate(address, size);
public bool ChangeAccess(ulong address, ulong size, HostPageProtection protection)
{
BeforeChangeAccess?.Invoke(address, size);
return !ShouldFail(Op.ChangeAccess) && _inner.ChangeAccess(address, size, protection);
}
public bool FreeOwnedRange(ulong address, ulong size) =>
!ShouldFail(Op.FreeOwnedRange) && _inner.FreeOwnedRange(address, size);
private bool ShouldFail(Op op)
{
lock (Log)
{
Log.Add(op);
if (_failOp != op)
{
return false;
}
if (_failAfter-- > 0)
{
return false;
}
_failOp = null;
return true;
}
}
}