diff --git a/Projects/Server.Tests/Tests/Buffers/RawInterpolatedStringHandlerTests.cs b/Projects/Server.Tests/Tests/Buffers/RawInterpolatedStringHandlerTests.cs index d4777b758..71ce89b3d 100644 --- a/Projects/Server.Tests/Tests/Buffers/RawInterpolatedStringHandlerTests.cs +++ b/Projects/Server.Tests/Tests/Buffers/RawInterpolatedStringHandlerTests.cs @@ -56,25 +56,6 @@ public class RawInterpolatedStringHandlerTests handler.Clear(); } - [Fact] - public void TestNoFormatPreservesCase() - { - var handler = new RawInterpolatedStringHandler(0, 1); - handler.AppendFormatted("Hello WORLD"); - Assert.Equal("Hello WORLD", handler.Text.ToString()); - handler.Clear(); - } - - [Fact] - public void TestUnicodeLowercase() - { - // ToLowerInvariant on Greek letter - var handler = new RawInterpolatedStringHandler(0, 1); - handler.AppendFormatted("ΑΒΓ", format: "L"); - Assert.Equal("αβγ", handler.Text.ToString()); - handler.Clear(); - } - [Fact] public void TestLowercaseFormatSurrogatePairAtChunkBoundary() { diff --git a/Projects/Server.Tests/Tests/Buffers/SpanReaderTests.cs b/Projects/Server.Tests/Tests/Buffers/SpanReaderTests.cs index 8d6e24e6d..6fa212acd 100644 --- a/Projects/Server.Tests/Tests/Buffers/SpanReaderTests.cs +++ b/Projects/Server.Tests/Tests/Buffers/SpanReaderTests.cs @@ -497,60 +497,6 @@ public class SpanReaderTests Assert.Equal(3, reader.Position); } - [Fact] - public void TestLength() - { - ReadOnlySpan buffer = [0x01, 0x02, 0x03, 0x04, 0x05]; - var reader = new SpanReader(buffer); - - Assert.Equal(5, reader.Length); - } - - [Fact] - public void TestPosition() - { - ReadOnlySpan buffer = [0x01, 0x02, 0x03]; - var reader = new SpanReader(buffer); - - Assert.Equal(0, reader.Position); - - reader.ReadByte(); - Assert.Equal(1, reader.Position); - - reader.ReadUInt16(); - Assert.Equal(3, reader.Position); - } - - [Fact] - public void TestRemaining() - { - ReadOnlySpan buffer = [0x01, 0x02, 0x03, 0x04, 0x05]; - var reader = new SpanReader(buffer); - - Assert.Equal(5, reader.Remaining); - - reader.ReadByte(); - Assert.Equal(4, reader.Remaining); - - reader.ReadUInt16(); - Assert.Equal(2, reader.Remaining); - - reader.ReadUInt16(); - Assert.Equal(0, reader.Remaining); - } - - [Fact] - public void TestBuffer() - { - ReadOnlySpan buffer = [0x01, 0x02, 0x03]; - var reader = new SpanReader(buffer); - - var bufferProperty = reader.Buffer; - - Assert.Equal(3, bufferProperty.Length); - AssertThat.Equal(bufferProperty, buffer); - } - [Fact] public void TestReadStringEmptyFixedLength() { diff --git a/Projects/Server.Tests/Tests/Buffers/SpanWriterTests.cs b/Projects/Server.Tests/Tests/Buffers/SpanWriterTests.cs index 370024370..10fe1b73f 100644 --- a/Projects/Server.Tests/Tests/Buffers/SpanWriterTests.cs +++ b/Projects/Server.Tests/Tests/Buffers/SpanWriterTests.cs @@ -75,19 +75,6 @@ public class SpanWriterTests AssertThat.Equal(writer.Span, [1, 0]); } - [Fact] - public void TestWriteByte() - { - Span buffer = stackalloc byte[2]; - var writer = new SpanWriter(buffer); - - writer.Write((byte)0x12); - writer.Write((byte)0x34); - - Assert.Equal(2, writer.Position); - AssertThat.Equal(writer.Span, [0x12, 0x34]); - } - [Fact] public void TestWriteSByte() { @@ -235,19 +222,6 @@ public class SpanWriterTests AssertThat.Equal(writer.Span, [0x01, 0x02, 0x03, 0x01, 0x02, 0x03]); } - [Fact] - public void TestWriteAsciiChar() - { - Span buffer = stackalloc byte[2]; - var writer = new SpanWriter(buffer); - - writer.WriteAscii('A'); - writer.WriteAscii('B'); - - Assert.Equal(2, writer.Position); - AssertThat.Equal(writer.Span, "AB"u8); - } - [Fact] public void TestWriteAsciiString() { @@ -457,16 +431,6 @@ public class SpanWriterTests writer.Dispose(); } - [Fact] - public void TestEnsureCapacityNoResize() - { - Span buffer = stackalloc byte[10]; - var writer = new SpanWriter(buffer); - - writer.EnsureCapacity(10); - Assert.Equal(10, writer.Capacity); - } - [Fact] public void TestEnsureCapacityNoResizeThrows() { @@ -552,40 +516,4 @@ public class SpanWriterTests writer.Write((byte)0xFF); Assert.Equal(3, writer.BytesWritten); } - - [Fact] - public void TestCapacity() - { - Span buffer = stackalloc byte[10]; - var writer = new SpanWriter(buffer); - - Assert.Equal(10, writer.Capacity); - } - - [Fact] - public void TestRawBuffer() - { - Span buffer = stackalloc byte[10]; - var writer = new SpanWriter(buffer); - - writer.Write((byte)0x01); - - var raw = writer.RawBuffer; - Assert.Equal(10, raw.Length); - Assert.Equal(0x01, raw[0]); - } - - [Fact] - public void TestSpan() - { - Span buffer = stackalloc byte[10]; - var writer = new SpanWriter(buffer); - - writer.Write((byte)0x01); - writer.Write((byte)0x02); - - var span = writer.Span; - Assert.Equal(2, span.Length); - AssertThat.Equal(span, [0x01, 0x02]); - } } diff --git a/Projects/Server.Tests/Tests/Buffers/ValueStringBuilderTests.cs b/Projects/Server.Tests/Tests/Buffers/ValueStringBuilderTests.cs index 7299a94c4..d3dfd12f2 100644 --- a/Projects/Server.Tests/Tests/Buffers/ValueStringBuilderTests.cs +++ b/Projects/Server.Tests/Tests/Buffers/ValueStringBuilderTests.cs @@ -52,19 +52,6 @@ public class ValueStringBuilderTests // writes into the shared buffer, and Append() reconciles via `this = handler._builder`. // Critical to detect if C# compiler codegen changes break this assumption. - [Fact] - public void Interpolation_Stackalloc_NoGrow() - { - // Stackalloc buffer large enough — no Grow needed. - // Validates: copy's Span shares same stackalloc memory, _length is reconciled. - var sb = new ValueStringBuilder(stackalloc char[64]); - sb.Append($"Hello {42} world"); - - Assert.Equal("Hello 42 world", sb.ToString()); - Assert.Equal(14, sb.Length); - sb.Dispose(); - } - [Fact] public void Interpolation_Stackalloc_WithGrow() { @@ -82,20 +69,6 @@ public class ValueStringBuilderTests sb.Dispose(); } - [Fact] - public void Interpolation_Stackalloc_PreExistingContent_NoGrow() - { - // Append plain text first, then interpolation. Buffer large enough. - // Validates: interpolation appends after existing content, doesn't overwrite. - var sb = new ValueStringBuilder(stackalloc char[64]); - sb.Append("prefix-"); - sb.Append($"value={99}"); - - Assert.Equal("prefix-value=99", sb.ToString()); - Assert.Equal(15, sb.Length); - sb.Dispose(); - } - [Fact] public void Interpolation_Stackalloc_PreExistingContent_WithGrow() { @@ -264,20 +237,6 @@ public class ValueStringBuilderTests sb.Dispose(); } - [Fact] - public void Interpolation_Stackalloc_DisposeAfterGrow() - { - // After Grow moves stackalloc to pool, Dispose should return the pooled array. - // Validates: _arrayToReturnToPool is correctly reconciled so Dispose works. - var sb = new ValueStringBuilder(stackalloc char[4]); - sb.Append($"grow beyond stackalloc: {12345}"); - // If _arrayToReturnToPool wasn't reconciled, Dispose would either - // not return the pooled array (leak) or return null (no-op when it shouldn't be). - sb.Dispose(); - // No assertion needed — if _arrayToReturnToPool was wrong, pool corruption - // would surface as test failures elsewhere. The test passing = no crash. - } - [Fact] public void Interpolation_Heap_DoubleGrow() { diff --git a/Projects/Server.Tests/Tests/Collections/ValueLinkListTests.cs b/Projects/Server.Tests/Tests/Collections/ValueLinkListTests.cs index 9683c8712..fa2a18417 100644 --- a/Projects/Server.Tests/Tests/Collections/ValueLinkListTests.cs +++ b/Projects/Server.Tests/Tests/Collections/ValueLinkListTests.cs @@ -16,13 +16,6 @@ public class ValueLinkListTests public TestEntity(int serial) => Serial = serial; } - [Fact] - public void TestDefaultListIsEmpty() - { - var linkList = new ValueLinkList(); - Assert.Equal(0, linkList.Count); - } - [Fact] public void TestAddingElementsFirst() { @@ -397,29 +390,4 @@ public class ValueLinkListTests } ); } - - [Fact] - public void TestThrowsIfModifiedWhileIteratingNested() - { - Assert.Throws( - () => - { - var linkList = new ValueLinkList(); - - var entity1 = new TestEntity(1); - var entity2 = new TestEntity(2); - - linkList.AddFirst(entity1); - linkList.AddLast(entity2); - - foreach (var item in linkList) - { - foreach (var nestedItem in linkList) - { - linkList.Remove(nestedItem); - } - } - } - ); - } } diff --git a/Projects/Server.Tests/Tests/Geometry/Point2DTests.cs b/Projects/Server.Tests/Tests/Geometry/Point2DTests.cs index b93a391af..81ae7ad84 100644 --- a/Projects/Server.Tests/Tests/Geometry/Point2DTests.cs +++ b/Projects/Server.Tests/Tests/Geometry/Point2DTests.cs @@ -16,31 +16,6 @@ public class Point2DTests Assert.Equal($"({int.MaxValue}, {int.MinValue})", new Point2D(int.MaxValue, int.MinValue).ToString()); } - [Fact] - public void TestPoint2DTryFormatSucceeds() - { - var array = new char[128]; - - var p1 = new Point2D(0, 0); - Assert.True(p1.TryFormat(array, out var cp1, null, null)); - Assert.Equal(6, cp1); - Array.Clear(array); - - var p2 = new Point2D(1, 1); - Assert.True(p2.TryFormat(array, out var cp2, null, null)); - Assert.Equal(6, cp2); - Array.Clear(array); - - var p3 = new Point2D(int.MaxValue, int.MaxValue); - Assert.True(p3.TryFormat(array, out var cp3, null, null)); - Assert.Equal(4 + 20, cp3); - Array.Clear(array); - - var p4 = new Point2D(int.MinValue, int.MinValue); - Assert.True(p4.TryFormat(array, out var cp4, null, null)); - Assert.Equal(4 + 22, cp4); - } - [Fact] public void TestPoint2DTryFormatFails() { diff --git a/Projects/Server.Tests/Tests/Geometry/Point3DTests.cs b/Projects/Server.Tests/Tests/Geometry/Point3DTests.cs index 8bdd279f5..7fae1f3bf 100644 --- a/Projects/Server.Tests/Tests/Geometry/Point3DTests.cs +++ b/Projects/Server.Tests/Tests/Geometry/Point3DTests.cs @@ -27,33 +27,6 @@ public class Point3DTests Assert.Equal($"({min}, {min}, {min})", new Point3D(min, min, min).ToString()); } - [Fact] - public void TestPoint3DTryFormatSucceeds() - { - const int max = int.MaxValue; - const int min = int.MinValue; - var array = new char[128]; - - var p1 = new Point3D(0, 0, 0); - Assert.True(p1.TryFormat(array, out var cp1, null, null)); - Assert.Equal(6 + 3, cp1); - Array.Clear(array); - - var p2 = new Point3D(1, 1, 1); - Assert.True(p2.TryFormat(array, out var cp2, null, null)); - Assert.Equal(6 + 3, cp2); - Array.Clear(array); - - var p3 = new Point3D(max, max, max); - Assert.True(p3.TryFormat(array, out var cp3, null, null)); - Assert.Equal(6 + 30, cp3); - Array.Clear(array); - - var p4 = new Point3D(min, min, min); - Assert.True(p4.TryFormat(array, out var cp4, null, null)); - Assert.Equal(6 + 33, cp4); - } - [Fact] public void TestPoint3DTryFormatFails() { diff --git a/Projects/Server.Tests/Tests/Geometry/Rectangle2DTests.cs b/Projects/Server.Tests/Tests/Geometry/Rectangle2DTests.cs index 1c9bb58fd..87c47c786 100644 --- a/Projects/Server.Tests/Tests/Geometry/Rectangle2DTests.cs +++ b/Projects/Server.Tests/Tests/Geometry/Rectangle2DTests.cs @@ -16,33 +16,6 @@ public class Rectangle2DTests Assert.Equal($"({min}, {min})+({min}, {min})", new Rectangle2D(min, min, min, min).ToString()); } - [Fact] - public void TestRectangle2DTryFormatSucceeds() - { - const int max = int.MaxValue; - const int min = int.MinValue; - var array = new char[128]; - - var p1 = new Rectangle2D(0, 0, 0, 0); - Assert.True(p1.TryFormat(array, out var cp1, null, null)); - Assert.Equal(9 + 4, cp1); - Array.Clear(array); - - var p2 = new Rectangle2D(1, 1, 1, 1); - Assert.True(p2.TryFormat(array, out var cp2, null, null)); - Assert.Equal(9 + 4, cp2); - Array.Clear(array); - - var p3 = new Rectangle2D(max, max, max, max); - Assert.True(p3.TryFormat(array, out var cp3, null, null)); - Assert.Equal(9 + 4 * 10, cp3); - Array.Clear(array); - - var p4 = new Rectangle2D(min, min, min, min); - Assert.True(p4.TryFormat(array, out var cp4, null, null)); - Assert.Equal(9 + 4 * 11, cp4); - } - [Fact] public void TestRectangle2DTryFormatFails() { diff --git a/Projects/Server.Tests/Tests/Geometry/Rectangle3DTests.cs b/Projects/Server.Tests/Tests/Geometry/Rectangle3DTests.cs index 2b030a183..d225b625f 100644 --- a/Projects/Server.Tests/Tests/Geometry/Rectangle3DTests.cs +++ b/Projects/Server.Tests/Tests/Geometry/Rectangle3DTests.cs @@ -16,33 +16,6 @@ public class Rectangle3DTests Assert.Equal($"({min}, {min}, {min})+({min}, {min}, {min})", new Rectangle3D(min, min, min, min, min, min).ToString()); } - [Fact] - public void TestRectangle3DTryFormatSucceeds() - { - const int max = int.MaxValue; - const int min = int.MinValue; - var array = new char[128]; - - var p1 = new Rectangle3D(0, 0, 0, 0, 0, 0); - Assert.True(p1.TryFormat(array, out var cp1, null, null)); - Assert.Equal(13 + 6, cp1); - Array.Clear(array); - - var p2 = new Rectangle3D(1, 1, 1, 1, 1, 1); - Assert.True(p2.TryFormat(array, out var cp2, null, null)); - Assert.Equal(13 + 6, cp2); - Array.Clear(array); - - var p3 = new Rectangle3D(max, max, max, max, max, max); - Assert.True(p3.TryFormat(array, out var cp3, null, null)); - Assert.Equal(13 + 6 * 10, cp3); - Array.Clear(array); - - var p4 = new Rectangle3D(min, min, min, min, min, min); - Assert.True(p4.TryFormat(array, out var cp4, null, null)); - Assert.Equal(13 + 6 * 11, cp4); - } - [Fact] public void TestRectangle3DTryFormatFails() { diff --git a/Projects/Server.Tests/Tests/Geometry/WorldLocationTests.cs b/Projects/Server.Tests/Tests/Geometry/WorldLocationTests.cs index b9fcc6f7a..561ece1f9 100644 --- a/Projects/Server.Tests/Tests/Geometry/WorldLocationTests.cs +++ b/Projects/Server.Tests/Tests/Geometry/WorldLocationTests.cs @@ -39,40 +39,6 @@ public sealed class WorldLocationTests Assert.Equal($"({min}, {min}, {min}) [{_superLongMapName}]", new WorldLocation(min, min, min, map).ToString()); } - [Theory] - [InlineData(null)] - [InlineData("Internal Map")] - [InlineData(_superLongMapName)] - public void TestWorldLocationTryFormatSucceeds(string mapName) - { - var map = mapName != null ? CreateMap(mapName) : null; - var mapNameLength = mapName?.Length ?? _nullMap.Length; - - const int max = int.MaxValue; - const int min = int.MinValue; - var array = new char[128]; - - var p1 = new WorldLocation(0, 0, 0, map); - Assert.True(p1.TryFormat(array, out var cp1, null, null)); - Assert.Equal(9 + 3 + mapNameLength, cp1); - Array.Clear(array); - - var p2 = new WorldLocation(1, 1, 1, map); - Assert.True(p2.TryFormat(array, out var cp2, null, null)); - Assert.Equal(9 + 3 + mapNameLength, cp2); - Array.Clear(array); - - array = new char[256]; // We need a bigger buffer! - var p3 = new WorldLocation(max, max, max, map); - Assert.True(p3.TryFormat(array, out var cp3, null, null)); - Assert.Equal(9 + 30 + mapNameLength, cp3); - Array.Clear(array); - - var p4 = new WorldLocation(min, min, min, map); - Assert.True(p4.TryFormat(array, out var cp4, null, null)); - Assert.Equal(9 + 33 + mapNameLength, cp4); - } - [Theory] [InlineData(null)] [InlineData("Internal Map")] diff --git a/Projects/Server.Tests/Tests/Items/ContainerChildSkipTests.cs b/Projects/Server.Tests/Tests/Items/ContainerChildSkipTests.cs index 5b1cd471c..550574770 100644 --- a/Projects/Server.Tests/Tests/Items/ContainerChildSkipTests.cs +++ b/Projects/Server.Tests/Tests/Items/ContainerChildSkipTests.cs @@ -66,27 +66,6 @@ public class ContainerChildSkipTests } // A skipped child is not saved, so the container must load without it. - [Fact] - public void SkippingContainer_LoadsWithoutItsSkippedChildren() - { - var pack = new SkippingContainer(); - var item = new Item(0x1234); - pack.DropItem(item); - - var writer = new BufferWriter(true); - pack.Serialize(writer); - var buffer = new byte[writer.Position]; - writer.Buffer.AsSpan(0, (int)writer.Position).CopyTo(buffer); - - item.Delete(); - pack.Delete(); - - var copy = new SkippingContainer(World.NewItem); - copy.Deserialize(new BufferReader(buffer)); - - Assert.Empty(copy.Items); - } - // A written child serial could resolve at load to an unrelated item that was handed the same serial. [Fact] public void SkippingContainer_DoesNotWriteAliveChildSerial() diff --git a/Projects/Server.Tests/Tests/Items/ContainerTests.cs b/Projects/Server.Tests/Tests/Items/ContainerTests.cs index 6b7634da8..6cef3a040 100644 --- a/Projects/Server.Tests/Tests/Items/ContainerTests.cs +++ b/Projects/Server.Tests/Tests/Items/ContainerTests.cs @@ -419,15 +419,6 @@ public class ContainerTests Assert.Equal(0, c.GetBestGroupAmount(new[] { typeof(TestReagentA) }, true, HueGrouper)); } - [Fact] - public void TestGetBestGroupAmount_NullGrouper_Throws() - { - var c = MakeContainer(0x2C0); - Assert.Throws( - () => c.GetBestGroupAmount(new[] { typeof(TestReagentA) }, true, null) - ); - } - [Fact] public void TestGetAmount_TypeArray_SumsAcrossTypes() { @@ -470,39 +461,6 @@ public class ContainerTests Assert.Equal(1, s2.Amount); } - [Fact] - public void TestConsumeTotal_LengthMismatch_Throws() - { - var c = MakeContainer(0x300); - Assert.Throws( - () => c.ConsumeTotal(new[] { typeof(TestReagentA) }, new[] { 1, 2 }) - ); - } - - [Fact] - public void TestConsumeTotalGrouped_LengthMismatch_Throws() - { - var c = MakeContainer(0x310); - Assert.Throws( - () => c.ConsumeTotalGrouped( - new[] { typeof(TestReagentA) }, new[] { 1, 2 }, - true, null, HueGrouper - ) - ); - } - - [Fact] - public void TestConsumeTotalGrouped_NullGrouper_Throws() - { - var c = MakeContainer(0x320); - Assert.Throws( - () => c.ConsumeTotalGrouped( - new[] { typeof(TestReagentA) }, new[] { 1 }, - true, null, null - ) - ); - } - // ------------------------------------------------------------------------- // FindItemsByType(Type) and FindItemsByType(ReadOnlySpan) — Phase 10: // these used to allocate a Predicate per call (method-group conversion diff --git a/Projects/Server.Tests/Tests/Items/DecayRegistrationTests.cs b/Projects/Server.Tests/Tests/Items/DecayRegistrationTests.cs index 59d736c6f..0f97eaa28 100644 --- a/Projects/Server.Tests/Tests/Items/DecayRegistrationTests.cs +++ b/Projects/Server.Tests/Tests/Items/DecayRegistrationTests.cs @@ -21,32 +21,6 @@ public class DecayRegistrationTests } // The item is already dequeued, so a refusal that does not reschedule strands it forever. - [Fact] - public void ItemWhoseDecayIsRefused_StaysTrackedAndSurvives() - { - var start = Core._now; - - try - { - var item = new RefusesDecayItem(); - item.MoveToWorld(new Point3D(106, 100, 0), Map.Felucca); - - AdvanceDecay(start, item.DecayTime + TimeSpan.FromMinutes(2), item); - - Assert.False(item.Deleted, "A refused decay must not delete the item."); - Assert.True( - DecayScheduler.IsRegistered(item), - "A refused decay must leave the item tracked so it is retried." - ); - - item.Delete(); - } - finally - { - Core._now = start; - } - } - // Drives the scheduler and the game clock without depending on the shared timer wheel. private static void AdvanceDecay(DateTime start, TimeSpan duration, Item item) { @@ -103,68 +77,9 @@ public class DecayRegistrationTests } } - [Fact] - public void ItemPlacedInWorld_IsRegisteredForDecay() - { - var item = new Item(0x1234); - item.MoveToWorld(new Point3D(100, 100, 0), Map.Felucca); - - Assert.True(item.CanDecay()); - Assert.True(DecayScheduler.IsRegistered(item), "Item on the ground must be tracked for decay."); - - item.Delete(); - } - // The real client path: Mobile.Lift internalizes, then Mobile.Drop calls MoveToWorld. - [Fact] - public void ItemLiftedThenDroppedToGround_IsRegisteredForDecay() - { - var item = new Item(0x1234); - item.MoveToWorld(new Point3D(100, 100, 0), Map.Felucca); - - // Player lifts the item to the cursor. - item.Internalize(); - Assert.Equal(Map.Internal, item.Map); - - // Player drops it back onto the ground. - item.MoveToWorld(new Point3D(101, 100, 0), Map.Felucca); - - Assert.True(item.CanDecay()); - Assert.True( - DecayScheduler.IsRegistered(item), - "Item dropped to the ground after being lifted must be tracked for decay." - ); - - item.Delete(); - } - // Held on the cursor means Map.Internal, which must not be tracked. - [Fact] - public void ItemLiftedToCursor_IsNotRegisteredForDecay() - { - var item = new Item(0x1234); - item.MoveToWorld(new Point3D(100, 100, 0), Map.Felucca); - - item.Internalize(); - - Assert.False(item.CanDecay()); - Assert.False(DecayScheduler.IsRegistered(item), "Item held on the cursor must not be tracked for decay."); - - item.Delete(); - } - // A new item is on Map.Internal, so construction must not touch the scheduler. - [Fact] - public void NewItem_IsNotTracked() - { - var item = new Item(0x1234); - - Assert.Equal(DecayScheduler.SlotNone, item.DecaySlot); - Assert.False(DecayScheduler.IsRegistered(item), "A newly constructed item must not be tracked."); - - item.Delete(); - } - // Unregister() trusts DecaySlot; IsRegistered() scans the structures. Assert both to catch drift. [Fact] public void DecaySlot_AgreesWithStructures_AcrossLifecycle() @@ -475,24 +390,4 @@ public class DecayRegistrationTests } // Dropping into a container must untrack; taking it back out to the ground must re-track. - [Fact] - public void ItemMovedIntoContainerThenBackToGround_IsRegisteredForDecay() - { - var pack = new Container(0xE75); - pack.MoveToWorld(new Point3D(100, 100, 0), Map.Felucca); - - var item = new Item(0x1234); - pack.AddItem(item); - - Assert.False(DecayScheduler.IsRegistered(item), "Item inside a container must not be tracked for decay."); - - item.Internalize(); - item.MoveToWorld(new Point3D(101, 100, 0), Map.Felucca); - - Assert.True(item.CanDecay()); - Assert.True(DecayScheduler.IsRegistered(item), "Item taken out of a container must be tracked for decay."); - - item.Delete(); - pack.Delete(); - } } diff --git a/Projects/Server.Tests/Tests/Items/PlayerConstructedStackingTests.cs b/Projects/Server.Tests/Tests/Items/PlayerConstructedStackingTests.cs index 955a87293..aa094365d 100644 --- a/Projects/Server.Tests/Tests/Items/PlayerConstructedStackingTests.cs +++ b/Projects/Server.Tests/Tests/Items/PlayerConstructedStackingTests.cs @@ -24,25 +24,6 @@ public class PlayerConstructedStackingTests private static StackableItem MakeStack(Serial serial, int amount, bool playerConstructed) => new(serial) { Amount = amount, PlayerConstructed = playerConstructed }; - [Theory] - [InlineData(false)] - [InlineData(true)] - public void CanStackWith_IsTrueWhenProvenanceMatches(bool playerConstructed) - { - var first = MakeStack((Serial)0x1, 5, playerConstructed); - var second = MakeStack((Serial)0x2, 7, playerConstructed); - - try - { - Assert.True(first.CanStackWith(second)); - } - finally - { - first.Delete(); - second.Delete(); - } - } - [Theory] [InlineData(false)] [InlineData(true)] diff --git a/Projects/Server.Tests/Tests/Json/JsonDiscoverableTypeAttributeTests.cs b/Projects/Server.Tests/Tests/Json/JsonDiscoverableTypeAttributeTests.cs deleted file mode 100644 index 437b674ff..000000000 --- a/Projects/Server.Tests/Tests/Json/JsonDiscoverableTypeAttributeTests.cs +++ /dev/null @@ -1,30 +0,0 @@ -using Server.Json; -using Xunit; - -namespace Server.Tests.Json; - -public class JsonDiscoverableTypeAttributeTests -{ - [JsonDiscoverableType] - private sealed class DefaultName { } - - [JsonDiscoverableType("custom")] - private sealed class Overridden { } - - [Fact] - public void DefaultDiscriminator_IsNull() - { - var attr = (JsonDiscoverableTypeAttribute)System.Attribute.GetCustomAttribute( - typeof(DefaultName), typeof(JsonDiscoverableTypeAttribute), false); - Assert.NotNull(attr); - Assert.Null(attr.Discriminator); - } - - [Fact] - public void OverrideDiscriminator_IsReturned() - { - var attr = (JsonDiscoverableTypeAttribute)System.Attribute.GetCustomAttribute( - typeof(Overridden), typeof(JsonDiscoverableTypeAttribute), false); - Assert.Equal("custom", attr.Discriminator); - } -} diff --git a/Projects/Server.Tests/Tests/Json/Rectangle3DConverterTests.cs b/Projects/Server.Tests/Tests/Json/Rectangle3DConverterTests.cs index b91c521dc..13e33f09f 100644 --- a/Projects/Server.Tests/Tests/Json/Rectangle3DConverterTests.cs +++ b/Projects/Server.Tests/Tests/Json/Rectangle3DConverterTests.cs @@ -35,12 +35,4 @@ public class Rectangle3DConverterTests Assert.DoesNotContain("z1", json); Assert.DoesNotContain("z2", json); } - - [Fact] - public void HomeRangeBounds_WritesZ() - { - var json = JsonSerializer.Serialize(new Rectangle3D(0, 0, -128, 10, 10, 256), JsonConfig.DefaultOptions); - Assert.Contains("\"z1\":", json); - Assert.Contains("\"z2\":", json); - } } diff --git a/Projects/Server.Tests/Tests/Maps/CanFitItemTests.cs b/Projects/Server.Tests/Tests/Maps/CanFitItemTests.cs index 57a6431ef..684c77743 100644 --- a/Projects/Server.Tests/Tests/Maps/CanFitItemTests.cs +++ b/Projects/Server.Tests/Tests/Maps/CanFitItemTests.cs @@ -220,39 +220,6 @@ public class CanFitItemTests } } - [SkippableFact] - public void CanFitItem_CanPlaceOnTopOfImpassable() - { - SkipIfNoTileData(); - Skip.If(ServerFixture.SurfaceImpassableTileId == 0, "No Surface+Impassable tile found in TileData"); - - var map = Map.Felucca; - const int x = 1950; - const int y = 1950; - - TestMulti multi = null; - try - { - // Get the actual tile height from TileData - var tileData = TileData.ItemTable[ServerFixture.SurfaceImpassableTileId]; - var tileHeight = tileData.CalcHeight; - const int tileZ = 10; - - // Create a Surface+Impassable tile at Z=10 - multi = CreateSurfaceImpassableMulti(map, new Point3D(x, y, 0), surfaceZ: tileZ); - - // Place item on top should work - var surfaceTop = tileZ + tileHeight; - var result = map.CanFitItem(x, y, surfaceTop, 1); - - Assert.True(result, $"Should be able to place item on top of Surface+Impassable tile at Z={surfaceTop}"); - } - finally - { - multi?.Delete(); - } - } - private TestMulti CreateSurfaceImpassableMulti(Map map, Point3D location, int surfaceZ) { // Use SurfaceImpassableTileId - a tile that has both Surface and Impassable flags diff --git a/Projects/Server.Tests/Tests/Maps/CanSpawnMobileTileDataTests.cs b/Projects/Server.Tests/Tests/Maps/CanSpawnMobileTileDataTests.cs index 87135f080..ea12926f1 100644 --- a/Projects/Server.Tests/Tests/Maps/CanSpawnMobileTileDataTests.cs +++ b/Projects/Server.Tests/Tests/Maps/CanSpawnMobileTileDataTests.cs @@ -23,32 +23,6 @@ public class CanSpawnMobileTileDataTests Skip.If(!ServerFixture.TileDataLoaded, "TileData not loaded - client files required"); } - [SkippableFact] - public void CanSpawnMobile_FindsSurfaceOnMulti() - { - SkipIfNoTileData(); - - var map = Map.Felucca; - const int x = 1100; - const int y = 1100; - - TestMulti multi = null; - try - { - // Create a multi with a floor at Z=20 using a real surface tile - multi = CreateFloorMulti(map, new Point3D(x, y, 0), floorZ: 20); - - var result = map.CanSpawnMobile(x, y, 15, 30, false, false, out var spawnZ); - - Assert.True(result); - Assert.True(spawnZ >= 15 && spawnZ <= 30); - } - finally - { - multi?.Delete(); - } - } - [SkippableFact] public void CanSpawnMobile_FindsLowestValidSurface() { @@ -239,51 +213,6 @@ public class CanSpawnMobileTileDataTests } } - [SkippableFact] - public void CanSpawnMobile_MalasBuilding_FindsGroundFloor() - { - SkipIfNoTileData(); - - var map = Map.Malas; - const int x = 991; - const int y = 519; - - var result = map.CanSpawnMobile(x, y, -60, -40, false, false, out var spawnZ); - - Assert.True(result); - Assert.True(spawnZ >= -60 && spawnZ <= -40, $"Expected ground floor around -50, got {spawnZ}"); - } - - [SkippableFact] - public void CanSpawnMobile_MalasBuilding_FindsSecondFloor() - { - SkipIfNoTileData(); - - var map = Map.Malas; - const int x = 991; - const int y = 519; - - var result = map.CanSpawnMobile(x, y, -30, 0, false, false, out var spawnZ); - - Assert.True(result); - Assert.True(spawnZ >= -30 && spawnZ <= 0, $"Expected second floor, got {spawnZ}"); - } - - [SkippableFact] - public void CanSpawnMobile_MalasBuilding_LowestFloorPreferred() - { - SkipIfNoTileData(); - - var map = Map.Malas; - const int x = 991; - const int y = 519; - - var result = map.CanSpawnMobile(x, y, -60, 0, false, false, out var spawnZ); - - Assert.True(result); - Assert.True(spawnZ <= -40, $"Expected lowest floor around -50, got {spawnZ}"); - } - private TestMulti CreateFloorMulti(Map map, Point3D location, int floorZ) { var multi = new TestMulti(CreateFloorComponents(floorZ)); diff --git a/Projects/Server.Tests/Tests/Maps/ClientEnumeratorTests.cs b/Projects/Server.Tests/Tests/Maps/ClientEnumeratorTests.cs index 629ec3961..9588be01c 100644 --- a/Projects/Server.Tests/Tests/Maps/ClientEnumeratorTests.cs +++ b/Projects/Server.Tests/Tests/Maps/ClientEnumeratorTests.cs @@ -308,49 +308,6 @@ public class ClientEnumeratorTests } } - [Fact] - public void ClientAtEnumerator_UsesDifferentPoint3DOverloads() - { - var map = Map.Felucca; - var location = new Point3D(800, 800, 5); - - var clients = new (NetState, Mobile)[1]; - try - { - clients[0] = CreateClientWithMobile(map, location); - - // Test Point3D overload - var found1 = new List(); - foreach (var ns in map.GetClientsAt(location)) - { - found1.Add(ns); - } - - // Test (int, int) overload - should find the same client (Z is ignored) - var found2 = new List(); - foreach (var ns in map.GetClientsAt(location.X, location.Y)) - { - found2.Add(ns); - } - - // Test Point2D overload - var found3 = new List(); - foreach (var ns in map.GetClientsAt(new Point2D(location.X, location.Y))) - { - found3.Add(ns); - } - - Assert.Single(found1); - Assert.Equal(clients[0].Item1, found1[0]); - Assert.Equal(found1, found2); - Assert.Equal(found1, found3); - } - finally - { - DeleteAll(clients); - } - } - [Fact] public void ClientEnumerator_GetClientsInRange() { @@ -383,39 +340,6 @@ public class ClientEnumeratorTests } } - [Fact] - public void ClientEnumerator_DeletedMobilesAreSkipped() - { - var map = Map.Felucca; - var rect = new Rectangle2D(1000, 1000, 16, 16); - - var clients = new (NetState, Mobile)[3]; - try - { - clients[0] = CreateClientWithMobile(map, new Point3D(1005, 1005, 0)); - clients[1] = CreateClientWithMobile(map, new Point3D(1006, 1005, 0)); - clients[2] = CreateClientWithMobile(map, new Point3D(1007, 1005, 0)); - - // Delete the mobile (but not the NetState) - clients[1].Item2.Delete(); - - var found = new List(); - foreach (var ns in map.GetClientsInBounds(rect)) - { - found.Add(ns); - } - - // Should skip the client whose mobile was deleted - Assert.Equal(2, found.Count); - Assert.Contains(clients[0].Item1, found); - Assert.Contains(clients[2].Item1, found); - } - finally - { - DeleteAll(clients); - } - } - private static (NetState, Mobile) CreateClientWithMobile(Map map, Point3D location) { // Create test NetState with real socket and buffers diff --git a/Projects/Server.Tests/Tests/Maps/ItemByDistanceEnumeratorTests.cs b/Projects/Server.Tests/Tests/Maps/ItemByDistanceEnumeratorTests.cs index fb12d0ab6..3e1ea6ee4 100644 --- a/Projects/Server.Tests/Tests/Maps/ItemByDistanceEnumeratorTests.cs +++ b/Projects/Server.Tests/Tests/Maps/ItemByDistanceEnumeratorTests.cs @@ -38,100 +38,6 @@ public class ItemByDistanceEnumeratorTests } } - [Fact] - public void ItemByDistanceEnumerator_DeletedItemsAreSkipped() - { - var map = Map.Felucca; - var center = new Point3D(200, 200, 0); - const int range = 5; - - var Items = new TestItem[3]; - try - { - Items[0] = CreateItem(map, new Point3D(202, 202, 0)); - Items[1] = CreateItem(map, new Point3D(203, 202, 0)); - Items[2] = CreateItem(map, new Point3D(204, 202, 0)); - - Items[1].Delete(); - - var found = new List(); - foreach (var (Item, _) in map.GetItemsInRangeByDistance(center, range)) - { - found.Add(Item); - } - - Assert.Equal(2, found.Count); - Assert.Contains(Items[0], found); - Assert.Contains(Items[2], found); - Assert.DoesNotContain(Items[1], found); - } - finally - { - DeleteAll(Items); - } - } - - [Fact] - public void ItemByDistanceEnumerator_ReturnsMinDistance() - { - var map = Map.Felucca; - var center = new Point3D(300, 300, 0); - const int range = 10; - - var Items = new TestItem[2]; - try - { - Items[0] = CreateItem(map, new Point3D(305, 305, 0)); - Items[1] = CreateItem(map, new Point3D(302, 302, 0)); - - var foundWithDistance = new List<(Item, int)>(); - foreach (var result in map.GetItemsInRangeByDistance(center, range)) - { - foundWithDistance.Add(result); - } - - Assert.Equal(2, foundWithDistance.Count); - // Each Item should have a non-negative min distance - Assert.All(foundWithDistance, item => Assert.True(item.Item2 >= 0)); - } - finally - { - DeleteAll(Items); - } - } - - [Fact] - public void ItemByDistanceEnumerator_OrderedBySector() - { - var map = Map.Felucca; - var center = new Point3D(400, 400, 0); - const int range = Map.SectorSize * 2; - - var Items = new TestItem[3]; - try - { - // Place Items in different sectors - Items[0] = CreateItem(map, new Point3D(center.X + 2, center.Y + 2, 0)); - Items[1] = CreateItem(map, new Point3D(center.X + Map.SectorSize + 2, center.Y + 2, 0)); - Items[2] = CreateItem(map, new Point3D(center.X + 2, center.Y + Map.SectorSize + 2, 0)); - - var found = new List(); - foreach (var (Item, _) in map.GetItemsInRangeByDistance(center, range)) - { - found.Add(Item); - } - - Assert.Equal(3, found.Count); - Assert.Contains(Items[0], found); - Assert.Contains(Items[1], found); - Assert.Contains(Items[2], found); - } - finally - { - DeleteAll(Items); - } - } - [Fact] public void ItemByDistanceEnumerator_MapNullYieldsEmpty() { @@ -239,50 +145,6 @@ public class ItemByDistanceEnumeratorTests } } - [Fact] - public void ItemByDistanceEnumerator_UsesDifferentPointOverloads() - { - var map = Map.Felucca; - var center = new Point3D(800, 800, 5); - const int range = 5; - - var Items = new TestItem[1]; - try - { - Items[0] = CreateItem(map, new Point3D(802, 802, 0)); - - // Test Point3D overload - var found1 = new List(); - foreach (var (Item, _) in map.GetItemsInRangeByDistance(center, range)) - { - found1.Add(Item); - } - - // Test (int, int) overload - var found2 = new List(); - foreach (var (Item, _) in map.GetItemsInRangeByDistance(center.X, center.Y, range)) - { - found2.Add(Item); - } - - // Test Point2D overload - var found3 = new List(); - foreach (var (Item, _) in map.GetItemsInRangeByDistance(new Point2D(center.X, center.Y), range)) - { - found3.Add(Item); - } - - Assert.Single(found1); - Assert.Equal(Items[0], found1[0]); - Assert.Equal(found1, found2); - Assert.Equal(found1, found3); - } - finally - { - DeleteAll(Items); - } - } - [Fact] public void ItemByDistanceEnumerator_RingTraversal() { @@ -384,58 +246,6 @@ public class ItemByDistanceEnumeratorTests } } - [Fact] - public void ItemByDistanceEnumerator_MultipleRings() - { - var map = Map.Felucca; - var center = new Point3D(1100, 1100, 0); - const int range = Map.SectorSize * 3; - - var Items = new List(); - try - { - // Create a grid of Items across multiple sectors - for (var ringOffset = 0; ringOffset <= 2; ringOffset++) - { - for (var side = 0; side < 4; side++) - { - var offset = ringOffset * Map.SectorSize; - var pos = side switch - { - 0 => new Point3D(center.X + offset, center.Y, 0), - 1 => new Point3D(center.X, center.Y + offset, 0), - 2 => new Point3D(center.X - offset, center.Y, 0), - _ => new Point3D(center.X, center.Y - offset, 0) - }; - Items.Add(CreateItem(map, pos)); - } - } - - var found = new List(); - foreach (var (Item, _) in map.GetItemsInRangeByDistance(center, range)) - { - found.Add(Item); - } - - // Should find all Items within range - Assert.True(found.Count > 0); - Assert.All(found, Item => - { - var dx = Item.X - center.X; - var dy = Item.Y - center.Y; - var distSq = dx * dx + dy * dy; - Assert.True(distSq <= range * range); - }); - } - finally - { - foreach (var Item in Items) - { - Item?.Delete(); - } - } - } - [Fact] public void ItemByDistanceEnumerator_MatchesBoundsEnumeratorAtMapEdge() { @@ -556,33 +366,6 @@ public class ItemByDistanceEnumeratorTests } } - [Fact] - public void ItemByDistanceEnumerator_Bounds_ReturnsMinDistance() - { - var map = Map.Felucca; - var bounds = new Rectangle2D(300, 300, 20, 20); - - var Items = new TestItem[2]; - try - { - Items[0] = CreateItem(map, new Point3D(305, 305, 0)); - Items[1] = CreateItem(map, new Point3D(315, 315, 0)); - - var foundWithDistance = new List<(Item, int)>(); - foreach (var result in map.GetItemsInBoundsByDistance(bounds)) - { - foundWithDistance.Add(result); - } - - Assert.Equal(2, foundWithDistance.Count); - Assert.All(foundWithDistance, item => Assert.True(item.Item2 >= 0)); - } - finally - { - DeleteAll(Items); - } - } - [Fact] public void ItemByDistanceEnumerator_Bounds_OrdersByProximityToCenter() { diff --git a/Projects/Server.Tests/Tests/Maps/ItemEnumeratorTests.cs b/Projects/Server.Tests/Tests/Maps/ItemEnumeratorTests.cs index bc92c3f58..5bed0ea2c 100644 --- a/Projects/Server.Tests/Tests/Maps/ItemEnumeratorTests.cs +++ b/Projects/Server.Tests/Tests/Maps/ItemEnumeratorTests.cs @@ -256,35 +256,6 @@ public class ItemEnumeratorTests } } - [Fact] - public void ItemAtEnumerator_DeletedItemsAreSkipped() - { - var map = Map.Felucca; - var location = new Point3D(650, 650, 0); - - var items = new Item[3]; - try - { - items[0] = CreateItem(map, location); - items[1] = CreateItem(map, location); - items[2] = CreateItem(map, location); - - items[1].Delete(); - - var found = new List(); - foreach (var item in map.GetItemsAt(location)) - { - found.Add(item); - } - - Assert.Equal(new[] { items[0], items[2] }, found); - } - finally - { - DeleteAll(items); - } - } - [Fact] public void ItemAtEnumerator_ItemsWithParentAreSkipped() { @@ -365,49 +336,6 @@ public class ItemEnumeratorTests } } - [Fact] - public void ItemAtEnumerator_UsesDifferentPoint3DOverloads() - { - var map = Map.Felucca; - var location = new Point3D(800, 800, 5); - - var items = new Item[1]; - try - { - items[0] = CreateItem(map, location); - - // Test Point3D overload - var found1 = new List(); - foreach (var item in map.GetItemsAt(location)) - { - found1.Add(item); - } - - // Test (int, int) overload - should find the same item (Z is ignored) - var found2 = new List(); - foreach (var item in map.GetItemsAt(location.X, location.Y)) - { - found2.Add(item); - } - - // Test Point2D overload - var found3 = new List(); - foreach (var item in map.GetItemsAt(new Point2D(location.X, location.Y))) - { - found3.Add(item); - } - - Assert.Single(found1); - Assert.Equal(items[0], found1[0]); - Assert.Equal(found1, found2); - Assert.Equal(found1, found3); - } - finally - { - DeleteAll(items); - } - } - [Fact] public void ItemEnumerator_ZeroRangeReturnsOnlyCenter() { diff --git a/Projects/Server.Tests/Tests/Maps/MapSelectionTests.cs b/Projects/Server.Tests/Tests/Maps/MapSelectionTests.cs index 60c3904b3..81ce7bc08 100644 --- a/Projects/Server.Tests/Tests/Maps/MapSelectionTests.cs +++ b/Projects/Server.Tests/Tests/Maps/MapSelectionTests.cs @@ -16,15 +16,6 @@ namespace Server.Tests.Maps Assert.Equal(expected, flags.ToCommaDelimitedString()); } - [Fact] - public void TestFormatFeluccaOnlyMapSelection() - { - const MapSelectionFlags flags = MapSelectionFlags.Felucca; - - // When, Then - Assert.Equal("Felucca", flags.ToCommaDelimitedString()); - } - public class TestMapConfig { [JsonConverter(typeof(FlagsConverter))] diff --git a/Projects/Server.Tests/Tests/Maps/MobileByDistanceEnumeratorTests.cs b/Projects/Server.Tests/Tests/Maps/MobileByDistanceEnumeratorTests.cs index 3f78377f9..c5d3afe64 100644 --- a/Projects/Server.Tests/Tests/Maps/MobileByDistanceEnumeratorTests.cs +++ b/Projects/Server.Tests/Tests/Maps/MobileByDistanceEnumeratorTests.cs @@ -71,76 +71,6 @@ public class MobileByDistanceEnumeratorTests } } - [Fact] - public void MobileByDistanceEnumerator_ReturnsMinDistance() - { - var map = Map.Felucca; - var center = new Point3D(300, 300, 0); - const int range = 10; - - var mobiles = new TestMobile[2]; - try - { - mobiles[0] = CreateMobile(map, new Point3D(305, 305, 0)); - mobiles[1] = CreateMobile(map, new Point3D(302, 302, 0)); - - var foundWithDistance = new List<(Mobile, int)>(); - foreach (var result in map.GetMobilesInRangeByDistance(center, range)) - { - foundWithDistance.Add(result); - } - - Assert.Equal(2, foundWithDistance.Count); - // Each mobile should have a non-negative min distance - Assert.All(foundWithDistance, item => Assert.True(item.Item2 >= 0)); - } - finally - { - DeleteAll(mobiles); - } - } - - [Fact] - public void MobileByDistanceEnumerator_OrderedBySector() - { - var map = Map.Felucca; - var center = new Point3D(400, 400, 0); - const int range = Map.SectorSize * 2; - - var mobiles = new TestMobile[3]; - try - { - // Place mobiles in different sectors - mobiles[0] = CreateMobile(map, new Point3D(center.X + 2, center.Y + 2, 0)); - mobiles[1] = CreateMobile(map, new Point3D(center.X + Map.SectorSize + 2, center.Y + 2, 0)); - mobiles[2] = CreateMobile(map, new Point3D(center.X + 2, center.Y + Map.SectorSize + 2, 0)); - - var found = new List(); - foreach (var (mobile, _) in map.GetMobilesInRangeByDistance(center, range)) - { - found.Add(mobile); - } - - Assert.Equal(3, found.Count); - Assert.Contains(mobiles[0], found); - Assert.Contains(mobiles[1], found); - Assert.Contains(mobiles[2], found); - } - finally - { - DeleteAll(mobiles); - } - } - - [Fact] - public void MobileByDistanceEnumerator_MapNullYieldsEmpty() - { - var center = new Point2D(0, 0); - var bounds = new Rectangle2D(center.m_X - 10, center.m_Y - 10, 21, 21); - var enumerator = new Map.MobileDistanceEnumerable(null, bounds, center, false).GetEnumerator(); - Assert.False(enumerator.MoveNext()); - } - [Fact] public void MobileByDistanceEnumerator_ThrowsOnVersionChange() { @@ -241,50 +171,6 @@ public class MobileByDistanceEnumeratorTests } } - [Fact] - public void MobileByDistanceEnumerator_UsesDifferentPointOverloads() - { - var map = Map.Felucca; - var center = new Point3D(800, 800, 5); - const int range = 5; - - var mobiles = new TestMobile[1]; - try - { - mobiles[0] = CreateMobile(map, new Point3D(802, 802, 0)); - - // Test Point3D overload - var found1 = new List(); - foreach (var (mobile, _) in map.GetMobilesInRangeByDistance(center, range)) - { - found1.Add(mobile); - } - - // Test (int, int) overload - var found2 = new List(); - foreach (var (mobile, _) in map.GetMobilesInRangeByDistance(center.X, center.Y, range)) - { - found2.Add(mobile); - } - - // Test Point2D overload - var found3 = new List(); - foreach (var (mobile, _) in map.GetMobilesInRangeByDistance(new Point2D(center.X, center.Y), range)) - { - found3.Add(mobile); - } - - Assert.Single(found1); - Assert.Equal(mobiles[0], found1[0]); - Assert.Equal(found1, found2); - Assert.Equal(found1, found3); - } - finally - { - DeleteAll(mobiles); - } - } - [Fact] public void MobileByDistanceEnumerator_RingTraversal() { @@ -386,58 +272,6 @@ public class MobileByDistanceEnumeratorTests } } - [Fact] - public void MobileByDistanceEnumerator_MultipleRings() - { - var map = Map.Felucca; - var center = new Point3D(1100, 1100, 0); - const int range = Map.SectorSize * 3; - - var mobiles = new List(); - try - { - // Create a grid of mobiles across multiple sectors - for (var ringOffset = 0; ringOffset <= 2; ringOffset++) - { - for (var side = 0; side < 4; side++) - { - var offset = ringOffset * Map.SectorSize; - var pos = side switch - { - 0 => new Point3D(center.X + offset, center.Y, 0), - 1 => new Point3D(center.X, center.Y + offset, 0), - 2 => new Point3D(center.X - offset, center.Y, 0), - _ => new Point3D(center.X, center.Y - offset, 0) - }; - mobiles.Add(CreateMobile(map, pos)); - } - } - - var found = new List(); - foreach (var (mobile, _) in map.GetMobilesInRangeByDistance(center, range)) - { - found.Add(mobile); - } - - // Should find all mobiles within range - Assert.True(found.Count > 0); - Assert.All(found, mobile => - { - var dx = mobile.X - center.X; - var dy = mobile.Y - center.Y; - var distSq = dx * dx + dy * dy; - Assert.True(distSq <= range * range); - }); - } - finally - { - foreach (var mobile in mobiles) - { - mobile?.Delete(); - } - } - } - private static TestMobile CreateMobile(Map map, Point3D location) { var mobile = new TestMobile(World.NewMobile); @@ -526,33 +360,6 @@ public class MobileByDistanceEnumeratorTests } } - [Fact] - public void MobileByDistanceEnumerator_Bounds_ReturnsMinDistance() - { - var map = Map.Felucca; - var bounds = new Rectangle2D(300, 300, 20, 20); - - var mobiles = new TestMobile[2]; - try - { - mobiles[0] = CreateMobile(map, new Point3D(305, 305, 0)); - mobiles[1] = CreateMobile(map, new Point3D(315, 315, 0)); - - var foundWithDistance = new List<(Mobile, int)>(); - foreach (var result in map.GetMobilesInBoundsByDistance(bounds)) - { - foundWithDistance.Add(result); - } - - Assert.Equal(2, foundWithDistance.Count); - Assert.All(foundWithDistance, item => Assert.True(item.Item2 >= 0)); - } - finally - { - DeleteAll(mobiles); - } - } - [Fact] public void MobileByDistanceEnumerator_Bounds_OrdersByProximityToCenter() { diff --git a/Projects/Server.Tests/Tests/Maps/MobileEnumeratorTests.cs b/Projects/Server.Tests/Tests/Maps/MobileEnumeratorTests.cs index ee932096f..4f9539486 100644 --- a/Projects/Server.Tests/Tests/Maps/MobileEnumeratorTests.cs +++ b/Projects/Server.Tests/Tests/Maps/MobileEnumeratorTests.cs @@ -87,13 +87,6 @@ public class MobileEnumeratorTests } } - [Fact] - public void MobileEnumerator_MapNullYieldsEmpty() - { - var enumerator = new Map.MobileEnumerator(null, Rectangle2D.Empty, false); - Assert.False(enumerator.MoveNext()); - } - [Fact] public void MobileEnumerator_ThrowsOnVersionChange() { diff --git a/Projects/Server.Tests/Tests/Maps/MultiEnumeratorTests.cs b/Projects/Server.Tests/Tests/Maps/MultiEnumeratorTests.cs index c064623bf..c5ed6c517 100644 --- a/Projects/Server.Tests/Tests/Maps/MultiEnumeratorTests.cs +++ b/Projects/Server.Tests/Tests/Maps/MultiEnumeratorTests.cs @@ -88,13 +88,6 @@ public class MultiEnumeratorTests } } - [Fact] - public void MultiEnumerator_MapNullYieldsEmpty() - { - var enumerator = new Map.MultiBoundsEnumerable(null, Rectangle2D.Empty, false).GetEnumerator(); - Assert.False(enumerator.MoveNext()); - } - [Fact] public void MultiEnumerator_ThrowsOnVersionChange() { @@ -250,35 +243,6 @@ public class MultiEnumeratorTests } } - [Fact] - public void MultiSectorEnumerator_DeletedMultisAreSkipped() - { - var map = Map.Felucca; - var location = new Point3D(750, 750, 0); - - var multis = new TestMulti[3]; - try - { - multis[0] = CreateMulti(map, location); - multis[1] = CreateMulti(map, new Point3D(location.X + 1, location.Y, 0)); - multis[2] = CreateMulti(map, new Point3D(location.X + 2, location.Y, 0)); - - multis[1].Delete(); - - var found = new List(); - foreach (var multi in map.GetMultisInRange(location, 10)) - { - found.Add(multi); - } - - Assert.Equal(new[] { multis[0], multis[2] }, found); - } - finally - { - DeleteAll(multis); - } - } - [Fact] public void MultiSectorEnumerator_MapNullYieldsEmpty() { @@ -286,49 +250,6 @@ public class MultiEnumeratorTests Assert.False(enumerator.MoveNext()); } - [Fact] - public void MultiSectorEnumerator_UsesDifferentPointOverloads() - { - var map = Map.Felucca; - var location = new Point3D(800, 800, 5); - - var multis = new TestMulti[1]; - try - { - multis[0] = CreateMulti(map, location); - - // Test Point3D overload - var found1 = new List(); - foreach (var multi in map.GetMultisInSector(location)) - { - found1.Add(multi); - } - - // Test (int, int) overload - var found2 = new List(); - foreach (var multi in map.GetMultisInSector(location.X, location.Y)) - { - found2.Add(multi); - } - - // Test Point2D overload - var found3 = new List(); - foreach (var multi in map.GetMultisInSector(new Point2D(location.X, location.Y))) - { - found3.Add(multi); - } - - Assert.Single(found1); - Assert.Equal(multis[0], found1[0]); - Assert.Equal(found1, found2); - Assert.Equal(found1, found3); - } - finally - { - DeleteAll(multis); - } - } - [Fact] public void MultiEnumerator_ZeroRangeReturnsOnlyCenter() { diff --git a/Projects/Server.Tests/Tests/Maps/StaticTileEnumeratorTests.cs b/Projects/Server.Tests/Tests/Maps/StaticTileEnumeratorTests.cs index edce65c89..07b8e37d3 100644 --- a/Projects/Server.Tests/Tests/Maps/StaticTileEnumeratorTests.cs +++ b/Projects/Server.Tests/Tests/Maps/StaticTileEnumeratorTests.cs @@ -7,29 +7,6 @@ namespace Server.Tests.Maps; [Collection("Sequential Server Tests")] public class StaticTileEnumeratorTests { - [Fact] - public void StaticTileEnumerator_MapNullYieldsEmpty() - { - var enumerator = new Map.StaticTileEnumerable(null, new Point2D(0, 0)).GetEnumerator(); - Assert.False(enumerator.MoveNext()); - } - - [Fact] - public void StaticTileEnumerator_EmptyLocationYieldsEmpty() - { - var map = Map.Felucca; - var location = new Point2D(100, 100); - - var tiles = new List(); - foreach (var tile in new Map.StaticTileEnumerable(map, location, includeStatics: true, includeMultis: false)) - { - tiles.Add(tile); - } - - // Since we don't have actual map files loaded, this should be empty - Assert.Empty(tiles); - } - [Fact] public void StaticTileEnumerator_IncludeStaticsOnlyWorks() { @@ -144,24 +121,6 @@ public class StaticTileEnumeratorTests } } - [Fact] - public void StaticTileEnumerator_TileMatrixGetStaticTilesWorks() - { - var map = Map.Felucca; - var x = 600; - var y = 600; - - // Test the TileMatrix.GetStaticTiles method - var tiles = new List(); - foreach (var tile in map.Tiles.GetStaticTiles(x, y)) - { - tiles.Add(tile); - } - - // Without map files loaded, should be empty - Assert.Empty(tiles); - } - [Fact] public void StaticTileEnumerator_TileMatrixGetStaticAndMultiTilesWorks() { @@ -252,13 +211,6 @@ public class StaticTileEnumeratorTests } } - [Fact] - public void StaticTileEnumerator_EmptyReturnsCorrectly() - { - var enumerator = Map.StaticTileEnumerable.Empty.GetEnumerator(); - Assert.False(enumerator.MoveNext()); - } - [Fact] public void StaticTileEnumerator_DeletedMultiSkipped() { diff --git a/Projects/Server.Tests/Tests/Network/Bans/BanConfigurationTests.cs b/Projects/Server.Tests/Tests/Network/Bans/BanConfigurationTests.cs index ee232b1df..c7a5f9a1e 100644 --- a/Projects/Server.Tests/Tests/Network/Bans/BanConfigurationTests.cs +++ b/Projects/Server.Tests/Tests/Network/Bans/BanConfigurationTests.cs @@ -32,13 +32,4 @@ public class BanConfigurationTests Assert.Equal(original.ReportRateLimitTrips, restored.ReportRateLimitTrips); Assert.Equal(original.AutoBanDuration, restored.AutoBanDuration); // TimeSpan survives } - - [Fact] - public void BanSettings_Defaults_AreReportRateLimitTripsFourHourAutoBan() - { - var settings = new BanSettings(); - - Assert.True(settings.ReportRateLimitTrips); - Assert.Equal(TimeSpan.FromHours(4), settings.AutoBanDuration); - } } diff --git a/Projects/Server.Tests/Tests/Network/EventLoopIdleTests.cs b/Projects/Server.Tests/Tests/Network/EventLoopIdleTests.cs index dfb8c7be7..c5dcb190c 100644 --- a/Projects/Server.Tests/Tests/Network/EventLoopIdleTests.cs +++ b/Projects/Server.Tests/Tests/Network/EventLoopIdleTests.cs @@ -10,14 +10,6 @@ namespace Server.Tests; [Collection("Sequential Server Tests")] public class EventLoopIdleTests { - [Fact] - public void FreshContextIsEmpty() - { - var context = new EventLoopContext(); - - Assert.True(context.IsEmpty); - } - [Fact] public void PostedWorkMakesContextNonEmpty() { diff --git a/Projects/Server.Tests/Tests/Network/MovementThrottleLatencyTests.cs b/Projects/Server.Tests/Tests/Network/MovementThrottleLatencyTests.cs index 63cb9d4a6..8644993ab 100644 --- a/Projects/Server.Tests/Tests/Network/MovementThrottleLatencyTests.cs +++ b/Projects/Server.Tests/Tests/Network/MovementThrottleLatencyTests.cs @@ -74,254 +74,6 @@ public class MovementThrottleLatencyTests } } - /// - /// Scenario: Player experiences a 3-second network lag spike, then all buffered - /// packets arrive at once. This should NOT trigger false positive detection. - /// - /// Timeline: - /// - T=0-1000ms: Normal movement (10 packets at 100ms intervals) - /// - T=1000-4000ms: Network lag (client buffers packets) - /// - T=4000ms: Network recovers, 30 buffered packets arrive simultaneously - /// - [Fact] - public void LagSpike_BurstRecovery_NoFalsePositive() - { - var ns = PacketTestUtilities.CreateTestNetState(); - SetTickCount(0); - - // Phase 1: Normal movement for 1 second - var movements = new (int delayMs, int costMs)[10]; - for (var i = 0; i < 10; i++) - { - movements[i] = (100, 100); // Normal mounted running - } - SimulateMovements(ns, movements); - - // Verify normal rate before lag - var rateBeforeLag = MovementThrottle.CalculateMovementRate(ns, out var samples); - Assert.True(samples >= 8, $"Expected >= 8 samples, got {samples}"); - Assert.True(rateBeforeLag >= 0.95f && rateBeforeLag <= 1.05f, - $"Expected normal rate before lag, got {rateBeforeLag}"); - - // Phase 2: Simulate 3 second lag followed by burst - // The gap (3000ms) is tracked, then burst packets arrive - var currentTime = Core.TickCount; - - // Record the lag gap - SetTickCount(currentTime + 3000); - ns._lastGapDuration = 3000; // Gap > maxChainGap triggers this - - // Burst packets arriving (5 packets with tiny intervals) - for (var i = 0; i < 5; i++) - { - SetTickCount(Core.TickCount + 2); // 2ms between packets (simultaneous arrival) - - if (ns._lastMovementRecordTime > 0) - { - var interval = (int)(Core.TickCount - ns._lastMovementRecordTime); - ns._movementHistory[ns._movementHistoryIndex] = new NetState.MovementRecord - { - Interval = (short)interval, - TargetSpeed = 100, - QueueDepth = 0, - Flags = 0 - }; - ns._movementHistoryIndex++; - if (ns._movementHistoryIndex >= 20) - { - ns._movementHistoryIndex = 0; - ns._movementHistoryFull = true; - } - } - ns._lastMovementRecordTime = Core.TickCount; - } - - // Analyze - should apply burst forgiveness due to large gap - var verdict = MovementThrottle.AnalyzeMovement(ns, out var rate, out _, out var confidence); - - // The burst will have high rate due to tiny intervals, but: - // 1. _lastGapDuration > 1000 (maxChainGap/2) triggers burst forgiveness - // 2. Confidence should be reduced by 0.3f multiplier - // 3. Should NOT be Definite unless truly extreme - Assert.True( - verdict != MovementThrottle.DetectionVerdict.Definite, - $"Lag recovery should not be Definite. Rate={rate}, Confidence={confidence}, Verdict={verdict}" - ); - - // Gap duration should be cleared after analysis - Assert.Equal(0, ns._lastGapDuration); - } - - /// - /// Scenario: Player with consistently high latency (400ms RTT) moves normally. - /// Their connection is consistent but slow - should NOT be considered "stable" - /// for the purpose of tightening detection thresholds. - /// - [Fact] - public void HighLatencyConsistent_NotConsideredStable() - { - var ns = PacketTestUtilities.CreateTestNetState(); - - // Set up high but consistent RTT - ns._rttHistory = new long[] { 400, 405, 398, 402, 401, 399, 403, 400 }; - ns._rttSampleCount = 8; - - // Calculate variance (should be low since values are consistent) - long sum = 0, sumSq = 0; - for (var i = 0; i < 8; i++) - { - sum += ns._rttHistory[i]; - sumSq += ns._rttHistory[i] * ns._rttHistory[i]; - } - var mean = sum / 8; - ns._rttVariance = sumSq / 8 - mean * mean; - - // Variance is low (consistent), but should NOT be "stable" due to high latency - Assert.False(ns.HasStableConnection, - $"400ms RTT should not be stable. AvgRTT={ns.AverageRtt}, Variance={ns._rttVariance}"); - - // Now verify detection gives more leniency for high-latency connections - SimulateMovements(ns, - (105, 100), (105, 100), (105, 100), (105, 100), (105, 100), - (105, 100), (105, 100), (105, 100), (105, 100), (105, 100) - ); - - var verdict = MovementThrottle.AnalyzeMovement(ns, out var rate, out _, out var confidence); - - // Slightly slow but high-latency player should get benefit of doubt - Assert.True( - verdict == MovementThrottle.DetectionVerdict.Normal || - verdict == MovementThrottle.DetectionVerdict.Possible, - $"High latency player should get leniency. Verdict={verdict}" - ); - } - - /// - /// Scenario: RTT probing and measurement. - /// Verifies that RTT variance is calculated correctly and stability - /// is properly determined. - /// - [Fact] - public void RttMeasurement_VarianceCalculation() - { - var ns = PacketTestUtilities.CreateTestNetState(); - SetTickCount(1000); - - // Simulate stable low-latency RTT measurements - var rttValues = new long[] { 50, 48, 52, 49, 51, 50, 48, 52 }; - - ns._rttHistory = new long[8]; - for (var i = 0; i < rttValues.Length; i++) - { - ns._rttHistory[i] = rttValues[i]; - } - ns._rttSampleCount = 8; - - // Calculate expected variance - long sum = 0, sumSq = 0; - foreach (var rtt in rttValues) - { - sum += rtt; - sumSq += rtt * rtt; - } - var expectedMean = sum / 8; - var expectedVariance = sumSq / 8 - expectedMean * expectedMean; - - // Clamp negative variance (safety check) - if (expectedVariance < 0) - { - expectedVariance = 0; - } - - ns._rttVariance = expectedVariance; - - Assert.True(ns._rttVariance >= 0, "Variance should never be negative"); - Assert.True(ns._rttVariance < 2500, "Low-variance samples should have variance < 2500"); - Assert.True(ns.AverageRtt > 0 && ns.AverageRtt < 200, "Average RTT should be ~50ms"); - Assert.True(ns.HasStableConnection, "Should be considered stable connection"); - } - - /// - /// Scenario: Actual speed hacker with stable connection. - /// Should be detected with high confidence. - /// - [Fact] - public void SpeedHack_StableConnection_DetectedDefinite() - { - var ns = PacketTestUtilities.CreateTestNetState(); - - // Set up stable, low-latency RTT - ns._rttHistory = new long[] { 30, 32, 28, 31, 29, 30, 31, 30 }; - ns._rttSampleCount = 8; - - long sum = 0, sumSq = 0; - for (var i = 0; i < 8; i++) - { - sum += ns._rttHistory[i]; - sumSq += ns._rttHistory[i] * ns._rttHistory[i]; - } - ns._rttVariance = sumSq / 8 - (sum / 8) * (sum / 8); - if (ns._rttVariance < 0) - { - ns._rttVariance = 0; - } - - Assert.True(ns.HasStableConnection, "Should be stable for this test"); - - // Simulate speed hack: 50% faster movement - SimulateMovements(ns, - (67, 100), (67, 100), (67, 100), (67, 100), (67, 100), - (67, 100), (67, 100), (67, 100), (67, 100), (67, 100), - (67, 100), (67, 100), (67, 100), (67, 100), (67, 100) - ); - - var verdict = MovementThrottle.AnalyzeMovement(ns, out var rate, out _, out var confidence); - - Assert.True(rate > 1.4f, $"Expected rate > 1.4 (50% speed hack), got {rate}"); - Assert.Equal(MovementThrottle.DetectionVerdict.Definite, verdict); - Assert.True(confidence > 0.5f, $"Expected high confidence, got {confidence}"); - } - - /// - /// Scenario: Borderline speed hack (7% faster) with unstable connection. - /// Should give benefit of doubt (not Definite). - /// - [Fact] - public void BorderlineSpeed_UnstableConnection_BenefitOfDoubt() - { - var ns = PacketTestUtilities.CreateTestNetState(); - - // Set up unstable connection - ns._rttHistory = new long[] { 50, 300, 80, 250, 100, 400, 60, 350 }; - ns._rttSampleCount = 8; - - long sum = 0, sumSq = 0; - for (var i = 0; i < 8; i++) - { - sum += ns._rttHistory[i]; - sumSq += ns._rttHistory[i] * ns._rttHistory[i]; - } - ns._rttVariance = sumSq / 8 - (sum / 8) * (sum / 8); - - Assert.False(ns.HasStableConnection, "Should be unstable due to high variance"); - - // Simulate borderline speed (7% faster) - SimulateMovements(ns, - (93, 100), (93, 100), (93, 100), (93, 100), (93, 100), - (93, 100), (93, 100), (93, 100), (93, 100), (93, 100) - ); - - var verdict = MovementThrottle.AnalyzeMovement(ns, out var rate, out _, out var confidence); - - Assert.True(rate > 1.05f && rate < 1.10f, $"Expected rate ~1.07, got {rate}"); - - // Unstable connection + borderline rate = should NOT be Definite - Assert.True( - verdict != MovementThrottle.DetectionVerdict.Definite, - $"Borderline case with unstable connection should not be Definite. Verdict={verdict}, Confidence={confidence}" - ); - } - /// /// Scenario: Verifies that burst forgiveness triggers correctly and clears gap duration. /// @@ -383,48 +135,4 @@ public class MovementThrottleLatencyTests $"Should not be Definite after burst forgiveness. Verdict={verdict}" ); } - - /// - /// Scenario: Walking vs running speed differences. - /// Walking (200ms interval) vs mounted running (100ms interval) should both - /// calculate correct rates. - /// - [Fact] - public void WalkingVsRunning_CorrectRateCalculation() - { - // Test walking (200ms expected interval) - var nsWalking = PacketTestUtilities.CreateTestNetState(); - SimulateMovements(nsWalking, - (200, 200), (200, 200), (200, 200), (200, 200), (200, 200), - (200, 200), (200, 200), (200, 200), (200, 200), (200, 200) - ); - - var walkingRate = MovementThrottle.CalculateMovementRate(nsWalking, out var walkingSamples); - Assert.True(walkingSamples >= 8, "Should have enough walking samples"); - Assert.True(walkingRate >= 0.95f && walkingRate <= 1.05f, - $"Walking rate should be ~1.0, got {walkingRate}"); - - // Test mounted running (100ms expected interval) - var nsRunning = PacketTestUtilities.CreateTestNetState(); - SimulateMovements(nsRunning, - (100, 100), (100, 100), (100, 100), (100, 100), (100, 100), - (100, 100), (100, 100), (100, 100), (100, 100), (100, 100) - ); - - var runningRate = MovementThrottle.CalculateMovementRate(nsRunning, out var runningSamples); - Assert.True(runningSamples >= 8, "Should have enough running samples"); - Assert.True(runningRate >= 0.95f && runningRate <= 1.05f, - $"Running rate should be ~1.0, got {runningRate}"); - - // Test speed hack while walking - var nsWalkingHack = PacketTestUtilities.CreateTestNetState(); - SimulateMovements(nsWalkingHack, - (100, 200), (100, 200), (100, 200), (100, 200), (100, 200), - (100, 200), (100, 200), (100, 200), (100, 200), (100, 200) - ); - - var walkingHackRate = MovementThrottle.CalculateMovementRate(nsWalkingHack, out _); - Assert.True(walkingHackRate >= 1.9f && walkingHackRate <= 2.1f, - $"Walking at running speed should be rate ~2.0, got {walkingHackRate}"); - } } diff --git a/Projects/Server.Tests/Tests/Network/MovementThrottleTests.cs b/Projects/Server.Tests/Tests/Network/MovementThrottleTests.cs index eca2ee0bf..808447b56 100644 --- a/Projects/Server.Tests/Tests/Network/MovementThrottleTests.cs +++ b/Projects/Server.Tests/Tests/Network/MovementThrottleTests.cs @@ -212,30 +212,6 @@ public class MovementThrottleTests Assert.True(rate > 1.25f, $"Expected rate > 1.25, got {rate}"); } - [Fact] - public void AnalyzeMovement_ModerateSpeedHack_ReturnsPossibleOrLikely() - { - // 8% faster than expected - moderate, should be Possible or Likely - var ns = CreateNetStateWithHistory( - (93, 100), (93, 100), (93, 100), (93, 100), (93, 100), - (93, 100), (93, 100), (93, 100), (93, 100), (93, 100) - ); - - // Set up stable, low-latency RTT (required for Likely verdict path) - ns._rttHistory = [50, 52, 48, 51, 49, 50, 51, 50]; - ns._rttSampleCount = 8; - ns._rttVariance = 4; // Low variance - - var verdict = MovementThrottle.AnalyzeMovement(ns, out var rate, out var sampleCount, out var confidence); - - Assert.True( - verdict == MovementThrottle.DetectionVerdict.Possible || - verdict == MovementThrottle.DetectionVerdict.Likely, - $"Expected Possible or Likely, got {verdict}. Rate={rate}, Confidence={confidence}" - ); - Assert.True(rate > 1.05f && rate < 1.15f, $"Expected rate ~1.07, got {rate}"); - } - [Fact] public void AnalyzeMovement_BurstAfterLargeGap_ReducesConfidence() { @@ -279,25 +255,6 @@ public class MovementThrottleTests Assert.Equal(MovementThrottle.DetectionVerdict.Definite, verdict); } - [Fact] - public void AnalyzeMovement_StableConnectionLowLatency_IncreasesConfidence() - { - var ns = CreateNetStateWithHistory( - (95, 100), (95, 100), (95, 100), (95, 100), (95, 100), - (95, 100), (95, 100), (95, 100), (95, 100), (95, 100) - ); - - // Set up stable, low-latency RTT - ns._rttHistory = [50, 52, 48, 51, 49, 50, 51, 50]; - ns._rttSampleCount = 8; - ns._rttVariance = 4; // Low variance - - var verdict = MovementThrottle.AnalyzeMovement(ns, out var rate, out var sampleCount, out var confidence); - - // With stable connection, even small anomalies are more suspicious - Assert.True(confidence > 0.2f, $"Expected higher confidence for stable connection, got {confidence}"); - } - [Fact] public void AnalyzeMovement_UnstableConnection_ReducesConfidence() { diff --git a/Projects/Server.Tests/Tests/Network/NetStateSendBufferTests.cs b/Projects/Server.Tests/Tests/Network/NetStateSendBufferTests.cs index eba603e28..6f5a22d04 100644 --- a/Projects/Server.Tests/Tests/Network/NetStateSendBufferTests.cs +++ b/Projects/Server.Tests/Tests/Network/NetStateSendBufferTests.cs @@ -390,38 +390,6 @@ public class NetStateSendBufferTests } } - [Fact] - public void Send_WithPartialRemainder_GrowsAndCopiesFullPayload() - { - var ns = CreateAuthenticatedNetState(out var client); - var baseSize = ns._socket.SendBuffer.PhysicalSize; - - try - { - var first = Pattern(baseSize / 2, 200); - var second = Pattern(baseSize / 2 + 1, 201); - var expected = new byte[first.Length + second.Length]; - first.CopyTo(expected, 0); - second.CopyTo(expected, first.Length); - - // Remainder too small for the second send - ns.Send(first); - ns.Send(second); - - Assert.True(ns.Running); - Assert.Equal(string.Empty, ns._disconnectReason); - Assert.True(ns._socket.SendBuffer.PhysicalSize > baseSize); - Assert.True(ns._sendBufferGrown); - - Assert.Equal(expected, ReadAll(client, expected.Length)); - } - finally - { - ns.Dispose(); - client.Close(); - } - } - [Fact] public void Send_WithSendInFlight_GrowsAndStreamStaysIntact() { @@ -628,58 +596,4 @@ public class NetStateSendBufferTests // growth off; budget untouched Assert.Equal(1L, NetState.CoerceSendBufferGrowthBudget(1, sendBufferSize, sendBufferSize)); } - - [Fact] - public void CoerceMaxBufferSlabs_ClampsToOneSlabPerConnection() - { - var maxConnections = NetState.SocketManager.MaxSockets; - - // fewer than one slab is meaningless - Assert.Equal(1, NetState.CoerceMaxBufferSlabs(0)); - Assert.Equal(1, NetState.CoerceMaxBufferSlabs(-4)); - - // more slabs than connections cannot make a slab any smaller - Assert.Equal(maxConnections, NetState.CoerceMaxBufferSlabs(maxConnections * 2)); - - Assert.Equal(128, NetState.CoerceMaxBufferSlabs(128)); - } - - [Fact] - public void CoerceInitialBufferSlabs_ClampsToTheSlabCount() - { - var slabs = NetState.BasePoolSlabCount(128); - Assert.True(slabs > 1); - - Assert.Equal(1, NetState.CoerceInitialBufferSlabs(0, slabs)); - Assert.Equal(1, NetState.CoerceInitialBufferSlabs(-1, slabs)); - - // a pool never holds more slabs than it has - Assert.Equal(slabs, NetState.CoerceInitialBufferSlabs(slabs + 1, slabs)); - - Assert.Equal(4, NetState.CoerceInitialBufferSlabs(4, slabs)); - } - - [Fact] - public void Ring_RegisteredBufferTable_MatchesTheConfiguredSlabCount() - { - var manager = NetState.SocketManager; - var maxBufferSlabs = NetState.CoerceMaxBufferSlabs(ServerConfiguration.GetSetting("network.maxBufferSlabs", NetState.DefaultMaxBufferSlabs)); - - // A table smaller than this throws at manager construction. Connections start on the - // transport minimum until the game server promotes them, so the formula must count those too. - // Mirrors what production passes: the request, not the effective size the manager may coerce - // down to (RequiredRegisteredBuffers treats a non-zero request as a pool either way). - Assert.Equal( - RingSocketManager.RequiredRegisteredBuffers( - manager.MaxSockets, - NetState.SendBufferSize, - manager.MaxSendBufferSize, - manager.SendBufferGrowthBudget, - maxBufferSlabs, - IORingBuffer.MinimumSize, - IORingBuffer.MinimumSize - ), - NetState.Ring.MaxRegisteredBuffers - ); - } } diff --git a/Projects/Server.Tests/Tests/Network/NetworkCompressionBoundsTests.cs b/Projects/Server.Tests/Tests/Network/NetworkCompressionBoundsTests.cs index c1bc6a800..549fa8857 100644 --- a/Projects/Server.Tests/Tests/Network/NetworkCompressionBoundsTests.cs +++ b/Projects/Server.Tests/Tests/Network/NetworkCompressionBoundsTests.cs @@ -42,20 +42,6 @@ public class NetworkCompressionBoundsTests } } - [Fact] - public void StillCompressesWhenOutputIsLargeEnough() - { - var input = new byte[64]; - Array.Fill(input, (byte)'A'); - - var output = new byte[256]; - - var written = NetworkCompression.Compress(input, output); - - Assert.True(written > 0); - Assert.True(written <= output.Length); - } - [Fact] public void ReportsFailureRatherThanOverrunningATightOutput() { diff --git a/Projects/Server.Tests/Tests/Network/NetworkStatsTests.cs b/Projects/Server.Tests/Tests/Network/NetworkStatsTests.cs deleted file mode 100644 index 5a64a61fe..000000000 --- a/Projects/Server.Tests/Tests/Network/NetworkStatsTests.cs +++ /dev/null @@ -1,80 +0,0 @@ -using System.Net.Sockets; -using Server.Network; -using Xunit; - -namespace Server.Tests.Network; - -[Collection("Sequential Server Tests")] -public class NetworkStatsTests -{ - private static byte[] Pattern(int length, int seed) - { - var data = new byte[length]; - new System.Random(seed).NextBytes(data); - return data; - } - - [Fact] - public void MaintainSendBuffers_RecordsTheRefusalsItResets() - { - var previous = NetState._availableMemoryBytes; - NetState ns = null; - Socket client = null; - - try - { - ns = PacketTestUtilities.CreateTestNetState(out client); - ns._protocolState = NetState.ProtocolState.GameServer_AwaitingGameServerLogin; - ns.Account = new MockAccount(); - var baseSize = ns._socket.SendBuffer.PhysicalSize; - - NetState.MaintainSendBuffers(); // drain whatever earlier tests refused - NetState._availableMemoryBytes = 1; // any working set is above 80% of one byte - - for (var i = 0; i < 6; i++) - { - ns.Send(Pattern(baseSize / 4, i)); - } - - // The sweep refreshes _availableMemoryBytes, so refusals must happen before it - NetState.MaintainSendBuffers(); - var stats = NetState.GetNetworkStats(); - - Assert.True(stats.LastSweep.Ran); - Assert.True(stats.LastSweep.CeilingRefusals >= 1); - - // Counters are per sweep, not cumulative - NetState.MaintainSendBuffers(); - Assert.Equal(0, NetState.GetNetworkStats().LastSweep.CeilingRefusals); - } - finally - { - NetState._availableMemoryBytes = previous; - ns?.Dispose(); - client?.Close(); - } - } - - [Fact] - public void GetNetworkStats_CountsAuthenticatedConnectionsSeparately() - { - var ns = PacketTestUtilities.CreateTestNetState(out var client); - - try - { - var before = NetState.GetNetworkStats(); - ns._protocolState = NetState.ProtocolState.GameServer_AwaitingGameServerLogin; - ns.Account = new MockAccount(); - var after = NetState.GetNetworkStats(); - - Assert.Equal(before.Connected, after.Connected); - Assert.Equal(before.Authenticated + 1, after.Authenticated); - Assert.True(after.Connected >= after.Authenticated); - } - finally - { - ns.Dispose(); - client.Close(); - } - } -} diff --git a/Projects/Server.Tests/Tests/PropertyList/ObjectPropertyListReentrancyTests.cs b/Projects/Server.Tests/Tests/PropertyList/ObjectPropertyListReentrancyTests.cs index a30e9c9e3..a09454ba6 100644 --- a/Projects/Server.Tests/Tests/PropertyList/ObjectPropertyListReentrancyTests.cs +++ b/Projects/Server.Tests/Tests/PropertyList/ObjectPropertyListReentrancyTests.cs @@ -25,18 +25,6 @@ public class ObjectPropertyListReentrancyTests return value; } - [Fact] - public void InterpolatedAdd_ResetMidHole_DoesNotThrow() - { - var opl = new ObjectPropertyList(null); - - var ex = Record.Exception( - () => opl.Add(1060776, $"{ResettingHole(opl, "Knight")}\t{"Council of Mages"}") - ); - - Assert.Null(ex); - } - [Fact] public void InterpolatedAdd_DisposeMidHole_DoesNotThrow() { @@ -57,19 +45,6 @@ public class ObjectPropertyListReentrancyTests [Collection("Sequential Server Tests")] public class ObjectPropertyListNestedBuildTests { - [Fact] - public void NestedBuild_DoesNotUnguardTheOuterList() - { - var outer = new ObjectPropertyList(null); - var inner = new ObjectPropertyList(null); - - outer.IsBuilding = true; - inner.IsBuilding = true; // another entity starts building, and finishes - inner.IsBuilding = false; - - Assert.True(outer.IsBuilding); - } - [Fact] public void Reset_MidInterpolation_LeavesTheListUsable() { diff --git a/Projects/Server.Tests/Tests/Serialization/DecimalSerializationTests.cs b/Projects/Server.Tests/Tests/Serialization/DecimalSerializationTests.cs deleted file mode 100644 index 5a25189d6..000000000 --- a/Projects/Server.Tests/Tests/Serialization/DecimalSerializationTests.cs +++ /dev/null @@ -1,46 +0,0 @@ -using System; -using System.Buffers.Binary; -using System.Runtime.InteropServices; -using Xunit; - -namespace Server.Tests; - -public class DecimalSerializationTests -{ - private static void Write(byte[] buffer, decimal value) - { - Span bytes = stackalloc int[sizeof(decimal) / 4]; - decimal.GetBits(value, bytes); - - MemoryMarshal.Cast(bytes).CopyTo(buffer.AsSpan()); - } - - private static int ReadInt(ReadOnlySpan buffer) => BinaryPrimitives.ReadInt32LittleEndian(buffer); - - private static decimal ReadDecimal(ReadOnlySpan buffer) => new(stackalloc int[4] { ReadInt(buffer), ReadInt(buffer[4..]), ReadInt(buffer[8..]), ReadInt(buffer[12..]) }); - - public static TheoryData Data => - new() - { - 123.46m, - 0.0256m, - 10000m, - 2m, - 0.0001m, - 0.0000000000000000000000000001m - }; - - [Theory] - [MemberData(nameof(Data))] - public void TestSerializeDecimal(decimal value) - { - // Arrange - var buffer = new byte[sizeof(decimal)]; - - // Act - Write(buffer, value); - - // Assert - Assert.Equal(value, ReadDecimal(buffer)); - } -} diff --git a/Projects/Server.Tests/Tests/Serialization/EnumConversionTests.cs b/Projects/Server.Tests/Tests/Serialization/EnumConversionTests.cs deleted file mode 100644 index 26a6833b6..000000000 --- a/Projects/Server.Tests/Tests/Serialization/EnumConversionTests.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System; -using Xunit; - -namespace Server.Tests; - -public class EnumConversionTests -{ - [Fact] - public void TestToEnum() - { - var e = ReadEnum(); - - Assert.Equal(TileFlag.Container, e); - } - - private static unsafe T ReadEnum() where T : unmanaged, Enum - { - var num = (long)TileFlag.Container; - return *(T*)# - } -} diff --git a/Projects/Server.Tests/Tests/Utility/ActivatorExtensionsTests.cs b/Projects/Server.Tests/Tests/Utility/ActivatorExtensionsTests.cs index ed331c2ff..059f6fa55 100644 --- a/Projects/Server.Tests/Tests/Utility/ActivatorExtensionsTests.cs +++ b/Projects/Server.Tests/Tests/Utility/ActivatorExtensionsTests.cs @@ -54,15 +54,6 @@ namespace Server.Tests Assert.Equal("Test ModernUO", result.Name); } - [Fact] - public void TestTwoParamsOptionalActivator() - { - var result = typeof(TestTwoOptionalParamsClass).CreateInstance(10, "Prod ModernUO"); - Assert.IsType(result); - Assert.Equal(10, result.Amount); - Assert.Equal("Prod ModernUO", result.Name); - } - private class TestZeroParamsClass { } diff --git a/Projects/Server.Tests/Tests/Utility/HtmlEscapeTests.cs b/Projects/Server.Tests/Tests/Utility/HtmlEscapeTests.cs index e2172a191..edc65d211 100644 --- a/Projects/Server.Tests/Tests/Utility/HtmlEscapeTests.cs +++ b/Projects/Server.Tests/Tests/Utility/HtmlEscapeTests.cs @@ -35,54 +35,6 @@ public class HtmlEscapeTests Assert.Equal(expected, result); } - [Theory(DisplayName = "Multiple instances of same character")] - [InlineData("<><>", "<><>")] - [InlineData("&&&&", "&&&&")] - [InlineData("\"\"\"", """"")] - [InlineData("'''", "'''")] - [InlineData(">>>", ">>>")] - public void EscapeHtml_MultipleSpecialCharacters_EscapesAll(string input, string expected) - { - var result = input.EscapeHtml(); - Assert.Equal(expected, result); - } - - [Theory(DisplayName = "Mixed content")] - [InlineData("Hello ", "Hello <world>")] - [InlineData("
Hello
", "<div>Hello</div>")] - [InlineData("Tom & Jerry", "Tom & Jerry")] - [InlineData("He said \"hello\"", "He said "hello"")] - [InlineData("It's a test", "It's a test")] - public void EscapeHtml_MixedContent_EscapesMixedSpecialCharacters(string input, string expected) - { - var result = input.EscapeHtml(); - Assert.Equal(expected, result); - } - - [Theory(DisplayName = "Starting with special character")] - [InlineData("World", ">World")] - [InlineData("&Start", "&Start")] - [InlineData("\"Quote", ""Quote")] - [InlineData("'Apostrophe", "'Apostrophe")] - public void EscapeHtml_StartsWithSpecialCharacter_EscapesStart(string input, string expected) - { - var result = input.EscapeHtml(); - Assert.Equal(expected, result); - } - - [Theory(DisplayName = "Ending with special character")] - [InlineData("Hello<", "Hello<")] - [InlineData("World>", "World>")] - [InlineData("End&", "End&")] - [InlineData("Quote\"", "Quote"")] - [InlineData("Test'", "Test'")] - public void EscapeHtml_EndsWithSpecialCharacter_EscapesEnd(string input, string expected) - { - var result = input.EscapeHtml(); - Assert.Equal(expected, result); - } - [Theory(DisplayName = "Complex mixed scenarios")] [InlineData("

Hello & goodbye

", "<p>Hello & goodbye</p>")] [InlineData("<already>", "&lt;already&gt;")] @@ -103,59 +55,6 @@ public class HtmlEscapeTests Assert.Empty(result); } - [Fact(DisplayName = "Empty string input")] - public void EscapeHtml_EmptyString_ReturnsEmpty() - { - var result = "".EscapeHtml(); - Assert.Empty(result); - } - - [Fact(DisplayName = "Only special characters")] - public void EscapeHtml_OnlySpecialCharacters_EscapesAll() - { - const string input = "<>&\"'"; - const string expected = "<>&"'"; - var result = input.EscapeHtml(); - Assert.Equal(expected, result); - } - - [Fact(DisplayName = "Ampersand must be escaped first")] - public void EscapeHtml_AmpersandFirst_PreventDoubleEscaping() - { - // This is critical: & must be escaped to & - // If we're not careful, we could double-escape already-escaped content - const string input = "<"; - const string expected = "&lt;"; - var result = input.EscapeHtml(); - Assert.Equal(expected, result); - } - - [Theory(DisplayName = "ReadOnlySpan overload - no special characters")] - [InlineData("Hello World")] - [InlineData("Plain text")] - public void EscapeHtml_ReadOnlySpan_NoSpecialCharacters_ReturnsUnchanged(string input) - { - var result = input.AsSpan().EscapeHtml(); - Assert.Equal(input, result); - } - - [Theory(DisplayName = "ReadOnlySpan overload - with special characters")] - [InlineData("
", "<div>")] - [InlineData("Tom & Jerry", "Tom & Jerry")] - public void EscapeHtml_ReadOnlySpan_WithSpecialCharacters_EscapesCorrectly(string input, string expected) - { - var result = input.AsSpan().EscapeHtml(); - Assert.Equal(expected, result); - } - - [Theory(DisplayName = "ReadOnlySpan overload - empty input")] - [InlineData("")] - public void EscapeHtml_ReadOnlySpan_Empty_ReturnsEmpty(string input) - { - var result = input.AsSpan().EscapeHtml(); - Assert.Empty(result); - } - [Fact(DisplayName = "Consecutive special characters")] public void EscapeHtml_ConsecutiveSpecialCharacters_EscapesAll() { @@ -165,51 +64,6 @@ public class HtmlEscapeTests Assert.Equal(expected, result); } - [Fact(DisplayName = "Special characters with single normal character between")] - public void EscapeHtml_SpecialCharactersWithGaps_EscapesAll() - { - const string input = "b&c\"d'e"; - const string expected = "<a>b&c"d'e"; - var result = input.EscapeHtml(); - Assert.Equal(expected, result); - } - - [Fact(DisplayName = "HTML tags")] - public void EscapeHtml_HtmlTags_EscapesTagBrackets() - { - var input = "Hello"; - var expected = "<html><body>Hello</body></html>"; - var result = input.EscapeHtml(); - Assert.Equal(expected, result); - } - - [Fact(DisplayName = "HTML attributes with mixed quotes")] - public void EscapeHtml_HtmlAttributesWithQuotes_EscapesCorrectly() - { - var input = ""; - var expected = "<a href="test" data='value'>"; - var result = input.EscapeHtml(); - Assert.Equal(expected, result); - } - - [Theory(DisplayName = "Whitespace handling")] - [InlineData(" spaces ", " spaces ")] - [InlineData("\ttabs\t", "\ttabs\t")] - [InlineData("\nnewlines\n", "\nnewlines\n")] - public void EscapeHtml_Whitespace_PreservedAsIs(string input, string expected) - { - var result = input.EscapeHtml(); - Assert.Equal(expected, result); - } - - [Fact(DisplayName = "Performance: long string without special characters")] - public void EscapeHtml_LongStringNoSpecialCharacters_ReturnsQuickly() - { - var input = new string('a', 10000); - var result = input.EscapeHtml(); - Assert.Equal(input, result); - } - [Fact(DisplayName = "Performance: long string with special characters")] public void EscapeHtml_LongStringWithSpecialCharacters_HandlesCorrectly() { @@ -229,15 +83,4 @@ public class HtmlEscapeTests var result = input.EscapeHtml(); Assert.Equal(expected, result); } - - [Fact(DisplayName = "String overload matches ReadOnlySpan overload")] - public void EscapeHtml_StringVsReadOnlySpan_ProduceSameResult() - { - const string input = "
Tom & Jerry 'in' \"quotes\"
"; - - var resultString = input.EscapeHtml(); - var resultSpan = input.AsSpan().EscapeHtml(); - - Assert.Equal(resultString, resultSpan); - } } diff --git a/Projects/Server.Tests/Tests/Utility/StringHelperTests.cs b/Projects/Server.Tests/Tests/Utility/StringHelperTests.cs index f428a060a..c7376cc4c 100644 --- a/Projects/Server.Tests/Tests/Utility/StringHelperTests.cs +++ b/Projects/Server.Tests/Tests/Utility/StringHelperTests.cs @@ -7,17 +7,6 @@ namespace Server.Tests; [Collection("Sequential Server Tests")] public class TestStringHelpers { - [Theory] - [InlineData(null, "default value", "default value")] - [InlineData("", "default value", "default value")] - [InlineData("this is a valid string", "default value", "this is a valid string")] - public void TestIsNullOrDefault(string value, string defaultValue, string expected) - { - var actual = value.DefaultIfNullOrEmpty(defaultValue); - - Assert.Equal(expected, actual); - } - [Theory] [InlineData("this is not capitalized", "This Is Not Capitalized")] [InlineData("", "")] diff --git a/Projects/UOContent.Tests/Tests/Accounting/AccountHandlerTests.cs b/Projects/UOContent.Tests/Tests/Accounting/AccountHandlerTests.cs index 0520f411e..c4970c98a 100644 --- a/Projects/UOContent.Tests/Tests/Accounting/AccountHandlerTests.cs +++ b/Projects/UOContent.Tests/Tests/Accounting/AccountHandlerTests.cs @@ -20,15 +20,4 @@ public class AccountHandlerTests { Assert.Equal(expected, AccountHandler.IsValidUsername(username)); } - - [Theory] - [InlineData("", false)] // Empty password - [InlineData("ValidPass123!", true)] // Standard Password - [InlineData(" ", true)] // Single space - [InlineData("ValidPass!@#", true)] // Valid special characters - [InlineData("ValidPassWithLength1234567890", true)] // Long valid password - public void IsValidPassword_ValidatesCorrectly(string password, bool expected) - { - Assert.Equal(expected, AccountHandler.IsValidPassword(password)); - } } diff --git a/Projects/UOContent.Tests/Tests/Accounting/PasswordWorkerTests.cs b/Projects/UOContent.Tests/Tests/Accounting/PasswordWorkerTests.cs index 5972e5eb9..df499509e 100644 --- a/Projects/UOContent.Tests/Tests/Accounting/PasswordWorkerTests.cs +++ b/Projects/UOContent.Tests/Tests/Accounting/PasswordWorkerTests.cs @@ -152,18 +152,6 @@ public class PasswordWorkerTests : IDisposable Assert.Null(outcome.Hash); } - [Fact] - public void AppliesAWrite() - { - var account = CreateAccount("offloop-apply-user"); - var upgraded = Argon2PasswordProtection.Instance.EncryptPassword(Password); - - account.ApplyPasswordWrite(upgraded, PasswordProtectionAlgorithm.Argon2); - - Assert.Equal(upgraded, account.Password); - Assert.True(account.CheckPassword(Password)); - } - /// /// Writes apply in dispatch order, which is what makes a guard unnecessary: dispatch is on the /// loop, one worker drains FIFO, and results return through the loop context in that same order. @@ -217,13 +205,4 @@ public class PasswordWorkerTests : IDisposable AccountSecurity.CurrentAlgorithm = PasswordProtectionAlgorithm.Argon2; Assert.Equal(Password, account.GetRehashPhrase(Password)); } - - [Fact] - public void UsesTheBarePasswordForArgon2Accounts() - { - var account = CreateAccount("offloop-phrase-argon2-user"); - - Assert.Equal(Password, account.GetVerifyPhrase(Password)); - Assert.Equal(Password, account.GetRehashPhrase(Password)); - } } diff --git a/Projects/UOContent.Tests/Tests/Accounting/Security/PasswordProtectionTest.cs b/Projects/UOContent.Tests/Tests/Accounting/Security/PasswordProtectionTest.cs index a0ef97a56..dc16a7f5b 100644 --- a/Projects/UOContent.Tests/Tests/Accounting/Security/PasswordProtectionTest.cs +++ b/Projects/UOContent.Tests/Tests/Accounting/Security/PasswordProtectionTest.cs @@ -150,13 +150,4 @@ public class PasswordProtectionTest { Assert.True(Argon2PasswordProtection.Instance.NeedsRehash(hash)); } - - [Fact] - public void NonArgon2Protections_NeverNeedRehash() - { - Assert.False(PBKDF2PasswordProtection.Instance.NeedsRehash("anything")); - Assert.False(HashAlgorithmPasswordProtection.SHA2Instance.NeedsRehash("anything")); - Assert.False(HashAlgorithmPasswordProtection.SHA1Instance.NeedsRehash("anything")); - Assert.False(HashAlgorithmPasswordProtection.MD5Instance.NeedsRehash("anything")); - } } diff --git a/Projects/UOContent.Tests/Tests/Commands/ChainedBindingSortTests.cs b/Projects/UOContent.Tests/Tests/Commands/ChainedBindingSortTests.cs index a7c266589..80e664647 100644 --- a/Projects/UOContent.Tests/Tests/Commands/ChainedBindingSortTests.cs +++ b/Projects/UOContent.Tests/Tests/Commands/ChainedBindingSortTests.cs @@ -109,19 +109,4 @@ public class ChainedBindingSortTests : IDisposable } // An unchained binding never had the problem and must keep working untouched. - [Fact] - public void SortingOnAPlainBindingIsUnchanged() - { - var low = Teleporter(null); - low.Hue = 1; - - var high = Teleporter(null); - high.Hue = 2; - - var comparer = Sorter("Hue"); - - Assert.True(comparer.Compare(low, high) < 0); - Assert.True(comparer.Compare(high, low) > 0); - Assert.Equal(0, comparer.Compare(low, low)); - } } diff --git a/Projects/UOContent.Tests/Tests/Commands/EmitterRobustnessTests.cs b/Projects/UOContent.Tests/Tests/Commands/EmitterRobustnessTests.cs index 997d8b79f..ef10033eb 100644 --- a/Projects/UOContent.Tests/Tests/Commands/EmitterRobustnessTests.cs +++ b/Projects/UOContent.Tests/Tests/Commands/EmitterRobustnessTests.cs @@ -79,25 +79,4 @@ public class EmitterRobustnessTests Assert.True(Check("Big", ComparisonOperator.Greater, "2147483647")); Assert.True(Check("Huge", ComparisonOperator.Greater, "9223372036854775807")); } - - [Fact] - public void EmittedAssembliesAreCollectible() - { - var prop = new Property("Tiny"); - prop.BindTo(typeof(Subject), PropertyAccess.Read); - - var compiled = ConditionalCompiler.Build( - typeof(Subject), - [TypeCondition.Default, new ComparisonCondition(prop, false, ComparisonOperator.Equal, "5")] - ).Compile(); - - var method = compiled.Method; - - Assert.True( - method.IsCollectible, - "The conditional compiler compiles one delegate per command invocation; code that is " - + $"not collectible ({method.GetType().Name} in {method.Module}) can never be unloaded, " - + "so every [global where would grow the process for good." - ); - } } diff --git a/Projects/UOContent.Tests/Tests/Engines/Events/RecurrencePatternTests.cs b/Projects/UOContent.Tests/Tests/Engines/Events/RecurrencePatternTests.cs index b08a49007..bd3ef178e 100644 --- a/Projects/UOContent.Tests/Tests/Engines/Events/RecurrencePatternTests.cs +++ b/Projects/UOContent.Tests/Tests/Engines/Events/RecurrencePatternTests.cs @@ -253,26 +253,6 @@ public class RecurrencePatternTests } } - [Fact] - public void MonthlyOrdinalRecurrencePattern_DST_FallBack_HandlesAmbiguousTimeCorrectly() - { - // This tests specifically the DST fall back case that was failing - var tz = TimeZoneInfo.FindSystemTimeZoneById("America/New_York"); - var pattern = new MonthlyOrdinalRecurrencePattern(OrdinalDayOccurrence.First, DayOfWeek.Sunday); - - // November 3, 2024 at 1:30 AM - First Sunday, falls on DST transition - var beforeDate = new DateTime(2024, 11, 2, 12, 0, 0); - var beforeUtc = beforeDate.LocalToUtc(tz); - var timeOnly = new TimeOnly(1, 30); - - // Act - var result = pattern.GetNextOccurrence(beforeUtc, timeOnly, tz); - - // Assert - should be November 3, 2024 at 1:30 AM - var resultLocal = TimeZoneInfo.ConvertTimeFromUtc(result, tz); - Assert.Equal(new DateTime(2024, 11, 3, 1, 30, 0), resultLocal); - } - [Fact] public void MonthlyRecurrence_MonthWithoutDay_SkipsToNextValidMonth() { diff --git a/Projects/UOContent.Tests/Tests/Engines/Factions/FactionRankTests.cs b/Projects/UOContent.Tests/Tests/Engines/Factions/FactionRankTests.cs index 2416055b1..4a8fc0458 100644 --- a/Projects/UOContent.Tests/Tests/Engines/Factions/FactionRankTests.cs +++ b/Projects/UOContent.Tests/Tests/Engines/Factions/FactionRankTests.cs @@ -67,22 +67,6 @@ public class FactionRankTests Assert.True(top.Rank.Rank > bottom.Rank.Rank); } - [Fact] - public void ReadingRank_IsStableAndSideEffectFree() - { - var faction = NewFaction(); - var owner = new List(); - var state = AddMember(faction, owner); - - faction.ZeroRankOffset = 1; - state.RankIndex = 0; - - var first = state.Rank; - var second = state.Rank; - - Assert.Same(first, second); - } - [Fact] public void RankIndexOutOfSyncWithZeroRankOffset_StillResolvesARank() { diff --git a/Projects/UOContent.Tests/Tests/Engines/Harvest/FishingTests.cs b/Projects/UOContent.Tests/Tests/Engines/Harvest/FishingTests.cs index 65ea66386..2a22b74bf 100644 --- a/Projects/UOContent.Tests/Tests/Engines/Harvest/FishingTests.cs +++ b/Projects/UOContent.Tests/Tests/Engines/Harvest/FishingTests.cs @@ -1,11 +1,9 @@ using System; -using System.Collections.Generic; using Server; using Server.Engines.Harvest; using Server.Items; using Server.Mobiles; using Server.Random; -using Server.Targeting; using Server.Tests; using Xunit; @@ -14,261 +12,9 @@ namespace UOContent.Tests; [Collection("Sequential UOContent Tests")] public class FishingTests { - private const int Casts = 100_000; - - // On land in Britain, so a cast there never counts as deep water. - private static readonly Point3D _shallow = new(1434, 1699, 0); - // Open sea with no land within 30 tiles: deep water once the client's map is loaded. private static readonly Point3D _deep = new(1840, 1760, -5); - private static readonly Type[] _magicFish = - [typeof(PrizedFish), typeof(WondrousFish), typeof(TrulyRareFish), typeof(PeculiarFish)]; - - private static readonly Type[] _footwear = [typeof(Boots), typeof(Shoes), typeof(Sandals), typeof(ThighBoots)]; - - [Theory] - [InlineData(Expansion.None, 50.0, 0.0, 0.0)] - [InlineData(Expansion.T2A, 0.0, 0.0, 0.75)] - [InlineData(Expansion.T2A, 50.0, 0.0, 0.3877)] - [InlineData(Expansion.T2A, 100.0, 0.025, 0.0247)] - [InlineData(Expansion.UOTD, 80.0, 0.02, 0.1669)] - [InlineData(Expansion.LBR, 0.0, 0.0, 0.75)] - [InlineData(Expansion.LBR, 50.0, 0.025, 0.4519)] - [InlineData(Expansion.AOS, 100.0, 0.05, 0.095)] - [InlineData(Expansion.SA, 50.0, 0.025, 0.4519)] - [InlineData(Expansion.HS, 50.0, 0.0, 0.4635)] - [InlineData(Expansion.HS, 120.0, 0.06, 0.047)] - [InlineData(Expansion.EJ, 120.0, 0.06, 0.047)] - public void MutateType_MagicFishAndFootwear_FollowTheEra( - Expansion expansion, double skill, double magicFishRate, double footwearRate - ) - { - var previousExpansion = Core.Expansion; - var previousGenerator = BuiltInRng.Generator; - var fisher = new PlayerMobile(World.NewMobile); - - try - { - Core.Expansion = expansion; - BuiltInRng.Generator = new System.Random(1); - fisher.DefaultMobileInit(); - fisher.Skills.Fishing.Base = skill; - - Assert.Equal(skill, fisher.Skills.Fishing.Value); - Assert.False(SpecialFishingNet.FullValidation(Map.Felucca, _shallow.X, _shallow.Y)); - - var definition = Fishing.System.Definitions[0]; - var magicFish = 0; - var footwear = 0; - - for (var i = 0; i < Casts; i++) - { - var type = Fishing.System.MutateType(typeof(Fish), fisher, null, definition, Map.Felucca, _shallow, null); - - if (Array.IndexOf(_magicFish, type) >= 0) - { - magicFish++; - } - else if (Array.IndexOf(_footwear, type) >= 0) - { - footwear++; - } - } - - AssertRate(magicFishRate, magicFish); - AssertRate(footwearRate, footwear); - } - finally - { - fisher.Delete(); - BuiltInRng.Generator = previousGenerator; - Core.Expansion = previousExpansion; - } - } - - [Fact] - public void Leviathan_CarriesItsTreasureMapAQuarterOfTheTime() - { - var leviathan = new Leviathan(); - var serpent = new SeaSerpent(); - - try - { - Assert.Equal(0.25, leviathan.TreasureMapChance); - Assert.Equal(TreasureMap.LootChance, serpent.TreasureMapChance); - } - finally - { - leviathan.Delete(); - serpent.Delete(); - } - } - - [Theory] - [InlineData(Expansion.None, 0.0)] - [InlineData(Expansion.T2A, 100.0)] - [InlineData(Expansion.UOR, 50.0)] - [InlineData(Expansion.LBR, 0.0)] - [InlineData(Expansion.EJ, 120.0)] - public void MutateType_NeverComesBackEmpty(Expansion expansion, double skill) - { - var previousExpansion = Core.Expansion; - var previousGenerator = BuiltInRng.Generator; - var fisher = new PlayerMobile(World.NewMobile); - - try - { - Core.Expansion = expansion; - BuiltInRng.Generator = new System.Random(1); - fisher.DefaultMobileInit(); - fisher.Skills.Fishing.Base = skill; - - var definition = Fishing.System.Definitions[0]; - - for (var i = 0; i < Casts; i++) - { - var type = Fishing.System.MutateType(typeof(Fish), fisher, null, definition, Map.Felucca, _shallow, null); - Assert.NotNull(type); - } - } - finally - { - fisher.Delete(); - BuiltInRng.Generator = previousGenerator; - Core.Expansion = previousExpansion; - } - } - - [SkippableTheory] - [InlineData(Expansion.T2A, 100.0, 0.005, 0.004975)] - [InlineData(Expansion.UOTD, 100.0, 0.00495, 0.004925)] - [InlineData(Expansion.LBR, 95.0, 0.0062, 0.0)] - [InlineData(Expansion.LBR, 100.0, 0.00825, 0.01309)] - [InlineData(Expansion.HS, 120.0, 0.016335, 0.0257)] - [InlineData(Expansion.EJ, 120.0, 0.016335, 0.0257)] - public void MutateType_DeepWaterMapsAndBottles_FollowTheEra( - Expansion expansion, double skill, double mapRate, double bottleRate - ) - { - TileDataRequirement.SkipIfMissing(); - Assert.True(SpecialFishingNet.FullValidation(Map.Felucca, _deep.X, _deep.Y)); - - var previousExpansion = Core.Expansion; - var previousGenerator = BuiltInRng.Generator; - var fisher = new PlayerMobile(World.NewMobile); - - try - { - Core.Expansion = expansion; - BuiltInRng.Generator = new System.Random(1); - fisher.DefaultMobileInit(); - fisher.Skills.Fishing.Base = skill; - - var definition = Fishing.System.Definitions[0]; - var maps = 0; - var bottles = 0; - - for (var i = 0; i < Casts; i++) - { - var type = Fishing.System.MutateType(typeof(Fish), fisher, null, definition, Map.Felucca, _deep, null); - - if (type == typeof(TreasureMap)) - { - maps++; - } - else if (type == typeof(MessageInABottle)) - { - bottles++; - } - } - - AssertRate(mapRate, maps); - AssertRate(bottleRate, bottles); - } - finally - { - fisher.Delete(); - BuiltInRng.Generator = previousGenerator; - Core.Expansion = previousExpansion; - } - } - - [SkippableTheory] - [InlineData(Expansion.T2A, true, 0.0, 100.0)] - [InlineData(Expansion.UOR, false, 0.0, 100.0)] - [InlineData(Expansion.UOR, true, 65.0, 115.0)] - [InlineData(Expansion.EJ, true, 65.0, 115.0)] - public void MutateResource_DeepWaterIsHarderFromUOR(Expansion expansion, bool deep, double minSkill, double maxSkill) - { - if (deep) - { - TileDataRequirement.SkipIfMissing(); - Assert.True(SpecialFishingNet.FullValidation(Map.Felucca, _deep.X, _deep.Y)); - } - - var previousExpansion = Core.Expansion; - var fisher = new PlayerMobile(World.NewMobile); - - try - { - Core.Expansion = expansion; - fisher.DefaultMobileInit(); - - var definition = Fishing.System.Definitions[0]; - var vein = definition.Veins[0]; - var resource = Fishing.System.MutateResource( - fisher, null, definition, Map.Felucca, deep ? _deep : _shallow, vein, vein.PrimaryResource, null - ); - - Assert.Equal(minSkill, resource.MinSkill); - Assert.Equal(maxSkill, resource.MaxSkill); - } - finally - { - fisher.Delete(); - Core.Expansion = previousExpansion; - } - } - - [Theory] - [InlineData(Expansion.T2A, 0.0)] - [InlineData(Expansion.UOTD, 0.0)] - [InlineData(Expansion.LBR, 0.1)] - [InlineData(Expansion.EJ, 0.1)] - public void SpecialFishingNet_IsColoredOneTimeInTenFromLBR(Expansion expansion, double coloredRate) - { - var previousExpansion = Core.Expansion; - var previousGenerator = BuiltInRng.Generator; - - try - { - Core.Expansion = expansion; - BuiltInRng.Generator = new System.Random(1); - - var colored = 0; - - for (var i = 0; i < Casts; i++) - { - var net = new SpecialFishingNet(); - - if (net.Hue != 0x8A0) - { - colored++; - } - - net.Delete(); - } - - AssertRate(coloredRate, colored); - } - finally - { - BuiltInRng.Generator = previousGenerator; - Core.Expansion = previousExpansion; - } - } - [SkippableTheory] [InlineData(1840, 1760, true)] // land 115 tiles away [InlineData(1660, 1760, true)] // land 14 tiles away @@ -284,72 +30,6 @@ public class FishingTests Assert.Equal(deep, SpecialFishingNet.FullValidation(Map.Felucca, x, y)); } - [SkippableFact] - public void SOS_FindLocation_PicksDeepWater() - { - TileDataRequirement.SkipIfMissing(); - - var previousGenerator = BuiltInRng.Generator; - - try - { - BuiltInRng.Generator = new System.Random(1); - - for (var i = 0; i < 100; i++) - { - var location = SOS.FindLocation(Map.Felucca); - - Assert.NotEqual(Point3D.Zero, location); - Assert.True(SpecialFishingNet.FullValidation(Map.Felucca, location.X, location.Y)); - } - } - finally - { - BuiltInRng.Generator = previousGenerator; - } - } - - [Fact] - public void Give_SurfacesASerpentThatAttacksAndIsADeepSeaSerpentOneTimeInTen() - { - const int serpents = 2_000; - - var previousGenerator = BuiltInRng.Generator; - var fisher = new PlayerMobile(); - - try - { - BuiltInRng.Generator = new System.Random(1); - fisher.MoveToWorld(_deep, Map.Felucca); - - var deepSeaSerpents = 0; - - for (var i = 0; i < serpents; i++) - { - var bottle = new MessageInABottle(Map.Felucca); - - Assert.True(Fishing.System.Give(fisher, bottle, false)); - - var serpent = Assert.IsAssignableFrom(bottle.RootParent); - Assert.Same(fisher, serpent.Combatant); - - if (serpent is DeepSeaSerpent) - { - deepSeaSerpents++; - } - - serpent.Delete(); - } - - AssertRate(0.1, deepSeaSerpents, serpents); - } - finally - { - fisher.Delete(); - BuiltInRng.Generator = previousGenerator; - } - } - [Theory] [InlineData(null, false)] [InlineData(2.5, false)] @@ -403,106 +83,39 @@ public class FishingTests } } - [SkippableTheory] - [InlineData(Expansion.None, 100.0, true, 0.0)] - [InlineData(Expansion.T2A, 79.9, true, 0.0)] - [InlineData(Expansion.T2A, 80.0, true, 0.01)] - [InlineData(Expansion.T2A, 100.0, false, 0.0)] - [InlineData(Expansion.EJ, 120.0, true, 0.01)] - public void OnHarvestFinished_SurfacesAnEmptySerpentOneCastInAHundred( - Expansion expansion, double skill, bool deep, double serpentRate - ) + [Fact] + public void Construct_SOSChestCatchConsumesTheSOS() { - TileDataRequirement.SkipIfMissing(); - Assert.True(SpecialFishingNet.FullValidation(Map.Felucca, _deep.X, _deep.Y)); - var previousExpansion = Core.Expansion; var previousGenerator = BuiltInRng.Generator; var fisher = new PlayerMobile(); try { - Core.Expansion = expansion; + Core.Expansion = Expansion.UOR; BuiltInRng.Generator = new System.Random(1); - fisher.Skills.Fishing.Base = skill; - fisher.MoveToWorld(_deep, Map.Felucca); - - var definition = Fishing.System.Definitions[0]; - var target = new LandTarget(deep ? _deep : _shallow, Map.Felucca); - var surfaced = new List(); - var serpents = 0; - - for (var i = 0; i < Casts; i++) - { - Fishing.System.OnHarvestFinished(fisher, null, definition, null, null, null, target); - - foreach (var serpent in Map.Felucca.GetMobilesInRange(fisher.Location, 10)) - { - surfaced.Add(serpent); - } - - serpents += surfaced.Count; - - foreach (var serpent in surfaced) - { - serpent.Delete(); - } - - surfaced.Clear(); - } - - AssertRate(serpentRate, serpents); - } - finally - { - fisher.Delete(); - BuiltInRng.Generator = previousGenerator; - Core.Expansion = previousExpansion; - } - } - - [Theory] - [InlineData(Expansion.T2A, 0.0)] - [InlineData(Expansion.UOR, 65.0)] - [InlineData(Expansion.EJ, 120.0)] - public void Construct_BringsUpTheSOSChestOneCatchInFour(Expansion expansion, double skill) - { - const int casts = 2_000; - - var previousExpansion = Core.Expansion; - var previousGenerator = BuiltInRng.Generator; - var fisher = new PlayerMobile(); - - try - { - Core.Expansion = expansion; - BuiltInRng.Generator = new System.Random(1); - fisher.Skills.Fishing.Base = skill; + fisher.Skills.Fishing.Base = 65.0; fisher.AddItem(new Backpack()); fisher.MoveToWorld(_deep, Map.Felucca); var sos = AddSOS(fisher); - var chests = 0; - for (var i = 0; i < casts; i++) + for (var i = 0; i < 200; i++) { var item = Fishing.System.Construct(typeof(Fish), fisher); + var isChest = item is WoodenChest or MetalGoldenChest; + item.Delete(); - if (item is WoodenChest or MetalGoldenChest) + if (isChest) { - chests++; Assert.True(sos.Deleted); - sos = AddSOS(fisher); - } - else - { - Assert.True(Assert.IsAssignableFrom(item).IsShipwreckedItem); + return; } - item.Delete(); + Assert.False(sos.Deleted); } - AssertRate(0.25, chests, casts); + Assert.Fail("No SOS chest surfaced in 200 casts"); } finally { @@ -512,283 +125,6 @@ public class FishingTests } } - [Theory] - [InlineData(Expansion.T2A, 0.0, true)] - [InlineData(Expansion.UOR, 64.9, false)] - [InlineData(Expansion.UOR, 65.0, true)] - [InlineData(Expansion.EJ, 64.9, false)] - public void SOS_Needs65SkillFromUOR(Expansion expansion, double skill, bool recovers) - { - var previousExpansion = Core.Expansion; - var definition = Fishing.System.Definitions[0]; - var fisher = new PlayerMobile(); - Item item = null; - - try - { - Core.Expansion = expansion; - fisher.Skills.Fishing.Base = skill; - fisher.AddItem(new Backpack()); - fisher.MoveToWorld(_deep, Map.Felucca); - AddSOS(fisher); - - // With no fish left, only an SOS that works lets the cast go on. - var bank = definition.GetBank(Map.Felucca, _deep.X, _deep.Y); - bank.Consume(bank.Current, fisher); - Assert.Equal(recovers, Fishing.System.CheckResources(fisher, null, definition, Map.Felucca, _deep, false)); - - item = Fishing.System.Construct(typeof(Fish), fisher); - Assert.Equal(recovers, item is not Fish); - } - finally - { - item?.Delete(); - fisher.Delete(); - definition.Banks.GetValueOrDefault(Map.Felucca) - ?.Remove(new Point2D(_deep.X / definition.BankWidth, _deep.Y / definition.BankHeight)); - Core.Expansion = previousExpansion; - } - } - - [Fact] - public void Construct_KeepsTheWhitePearlAtAnSOSSpot() - { - var previousExpansion = Core.Expansion; - var fisher = new PlayerMobile(); - - try - { - Core.Expansion = Expansion.ML; - fisher.Skills.Fishing.Base = 100.0; - fisher.AddItem(new Backpack()); - fisher.MoveToWorld(_deep, Map.Felucca); - - var sos = AddSOS(fisher); - - for (var i = 0; i < 100; i++) - { - var item = Fishing.System.Construct(typeof(WhitePearl), fisher); - Assert.IsType(item); - item.Delete(); - } - - Assert.False(sos.Deleted); - } - finally - { - fisher.Delete(); - Core.Expansion = previousExpansion; - } - } - - [Theory] - [InlineData(Expansion.None, false)] - [InlineData(Expansion.T2A, true)] - [InlineData(Expansion.AOS, true)] - [InlineData(Expansion.ML, true)] - public void OnHarvestStartedAndFinished_RevealAHiddenFishermanFromT2A(Expansion expansion, bool reveals) - { - Core._tickCount = 0; - Timer.Init(0); - - var previousExpansion = Core.Expansion; - var previousNow = Core._now; - var fisher = new PlayerMobile(); - - try - { - Core.Expansion = expansion; - fisher.MoveToWorld(_deep, Map.Felucca); - - var definition = Fishing.System.Definitions[0]; - var target = new LandTarget(_deep, Map.Felucca); - - fisher.Hidden = true; - Fishing.System.OnHarvestStarted(fisher, null, definition, target); - Advance(TimeSpan.FromSeconds(2.0)); - Assert.Equal(!reveals, fisher.Hidden); - - fisher.Hidden = true; - Fishing.System.OnHarvestFinished(fisher, null, definition, null, null, null, target); - Assert.Equal(!reveals, fisher.Hidden); - } - finally - { - fisher.Delete(); - Core._now = previousNow; - Core.Expansion = previousExpansion; - } - } - - [Theory] - [InlineData(Expansion.UOR, true)] - [InlineData(Expansion.AOS, true)] - [InlineData(Expansion.SE, false)] - public void BigFish_WeighsAHundredStonesBeforeSE(Expansion expansion, bool weighsAHundred) - { - var previousExpansion = Core.Expansion; - var previousGenerator = BuiltInRng.Generator; - var weights = new HashSet(); - - try - { - Core.Expansion = expansion; - BuiltInRng.Generator = new System.Random(1); - - for (var i = 0; i < 100; i++) - { - var fish = new BigFish(); - weights.Add((int)fish.Weight); - fish.Delete(); - } - } - finally - { - BuiltInRng.Generator = previousGenerator; - Core.Expansion = previousExpansion; - } - - Assert.All(weights, weight => Assert.InRange(weight, 3, 200)); - Assert.Equal(weighsAHundred, weights.SetEquals([100])); - } - - [Theory] - [InlineData(Expansion.AOS, 100, 25)] - [InlineData(Expansion.AOS, 12, 4)] - [InlineData(Expansion.SE, 100, 20)] - [InlineData(Expansion.SE, 200, 40)] - [InlineData(Expansion.SE, 12, 4)] - public void BigFish_GivesASteakPerFiveStonesFromSE(Expansion expansion, int weight, int steaks) - { - var previousExpansion = Core.Expansion; - var fisher = new PlayerMobile(); - - try - { - Core.Expansion = expansion; - fisher.AddItem(new Backpack()); - - var fish = new BigFish { Weight = weight }; - fisher.Backpack.DropItem(fish); - fish.Carve(fisher, null); - - Assert.True(fish.Deleted); - Assert.Equal(steaks, fisher.Backpack.FindItemByType()?.Amount); - } - finally - { - fisher.Delete(); - Core.Expansion = previousExpansion; - } - } - - [Theory] - [InlineData(Expansion.AOS, 100.0, 0.0)] - [InlineData(Expansion.SE, 99.9, 0.0)] - [InlineData(Expansion.SE, 100.0, 0.25)] - public void Leviathan_GivesAGMNetThrowerAnArtifactOneTimeInFourFromSE(Expansion expansion, double skill, double rate) - { - const int leviathans = 400; - - var previousExpansion = Core.Expansion; - var previousGenerator = BuiltInRng.Generator; - var fisher = new PlayerMobile(); - - try - { - Core.Expansion = expansion; - BuiltInRng.Generator = new System.Random(1); - fisher.Skills.Fishing.Base = skill; - fisher.AddItem(new Backpack()); - fisher.MoveToWorld(_deep, Map.Felucca); - - var pack = fisher.Backpack; - var artifacts = 0; - - // Nobody damages it, so only the net thrower's own roll can give an artifact. - for (var i = 0; i < leviathans; i++) - { - var leviathan = new Leviathan(fisher); - leviathan.MoveToWorld(_deep, Map.Felucca); - leviathan.Kill(); - - artifacts += pack.Items.Count; - - for (var j = pack.Items.Count - 1; j >= 0; j--) - { - pack.Items[j].Delete(); - } - - leviathan.Corpse?.Delete(); - leviathan.Delete(); - } - - AssertRate(rate, artifacts, leviathans); - } - finally - { - fisher.Delete(); - BuiltInRng.Generator = previousGenerator; - Core.Expansion = previousExpansion; - } - } - - [Theory] - [InlineData(Expansion.AOS, 5, 60)] - [InlineData(Expansion.SE, 1, 12)] - [InlineData(Expansion.EJ, 1, 12)] - public void MagicFish_GiveAPointFor12SecondsFromSE(Expansion expansion, int bonus, int seconds) - { - Core._tickCount = 0; - Timer.Init(0); - - var previousExpansion = Core.Expansion; - var previousNow = Core._now; - var fisher = new PlayerMobile(); - BaseMagicFish[] fish = [new PrizedFish(), new WondrousFish(), new TrulyRareFish()]; - - try - { - Core.Expansion = expansion; - - foreach (var magicFish in fish) - { - Assert.True(magicFish.Apply(fisher)); - Assert.Equal(bonus, fisher.GetStatOffset(magicFish.Type)); - } - - Advance(TimeSpan.FromSeconds(seconds - 1)); - Assert.All(fish, magicFish => Assert.Equal(bonus, fisher.GetStatOffset(magicFish.Type))); - - Advance(TimeSpan.FromSeconds(1)); - Assert.All(fish, magicFish => Assert.Equal(0, fisher.GetStatOffset(magicFish.Type))); - } - finally - { - foreach (var magicFish in fish) - { - magicFish.Delete(); - } - - fisher.Delete(); - Core._now = previousNow; - Core.Expansion = previousExpansion; - } - } - - // Within five standard deviations of the expected rate; a rate of zero must never happen. - private static void AssertRate(double expected, int count) - { - var tolerance = 5 * Math.Sqrt(expected * (1 - expected) / Casts); - Assert.InRange((double)count / Casts, expected - tolerance, expected + tolerance); - } - - private static void AssertRate(double expected, int count, int trials) - { - var tolerance = 5 * Math.Sqrt(expected * (1 - expected) / trials); - Assert.InRange((double)count / trials, expected - tolerance, expected + tolerance); - } - private static void Advance(TimeSpan time) { Core._now += time; diff --git a/Projects/UOContent.Tests/Tests/Engines/Pathing/Multi/BoatPathTests.cs b/Projects/UOContent.Tests/Tests/Engines/Pathing/Multi/BoatPathTests.cs index 694374757..cf63d4858 100644 --- a/Projects/UOContent.Tests/Tests/Engines/Pathing/Multi/BoatPathTests.cs +++ b/Projects/UOContent.Tests/Tests/Engines/Pathing/Multi/BoatPathTests.cs @@ -45,31 +45,6 @@ public class BoatPathTests } } - [SkippableFact] - public void BoatDeck_FootprintShape_IsPositionInvariant() - { - TileDataRequirement.SkipIfMissing(); - // The property Phase 2's local-frame, movement-invariant boat cache must preserve: - // the deck's covered-cell shape (in local coords) is identical at two world positions. - var map = Map.Maps[MapId]; - - var a = new TestMulti(0x0); - map.GetAverageZ(WaterX, WaterY, out _, out var za, out _); - a.MoveToWorld(new Point3D(WaterX, WaterY, (sbyte)za), map); - var shapeA = LocalShape(a); - a.Delete(); - - var b = new TestMulti(0x0); - map.GetAverageZ(WaterX + 5, WaterY + 3, out _, out var zb, out _); - b.MoveToWorld(new Point3D(WaterX + 5, WaterY + 3, (sbyte)zb), map); - var shapeB = LocalShape(b); - b.Delete(); - - Assert.Equal(shapeA, shapeB); - Assert.NotEmpty(shapeA); - _output.WriteLine($"boat local deck shape stable across positions: {shapeA.Count} cells"); - } - private static System.Collections.Generic.HashSet<(int lx, int ly)> LocalShape(BaseMulti multi) { var mcl = multi.Components; diff --git a/Projects/UOContent.Tests/Tests/Engines/Pathing/Multi/FoundationRedesignTests.cs b/Projects/UOContent.Tests/Tests/Engines/Pathing/Multi/FoundationRedesignTests.cs index e82520ce9..5284df402 100644 --- a/Projects/UOContent.Tests/Tests/Engines/Pathing/Multi/FoundationRedesignTests.cs +++ b/Projects/UOContent.Tests/Tests/Engines/Pathing/Multi/FoundationRedesignTests.cs @@ -17,28 +17,6 @@ public class FoundationRedesignTests private const int PlaceX = 1500; private const int PlaceY = 1600; - [SkippableFact] - public void SwappingComponents_ChangesFootprint() - { - TileDataRequirement.SkipIfMissing(); - var foundation = new SwappableFoundation(0x74); // GuildHouse footprint - try - { - var beforeCount = MultiArt.FootprintCells(foundation).Count; - Assert.True(beforeCount > 0, "non-vacuity: initial footprint must cover cells"); - - foundation.Redesign(MultiData.GetComponents(0x7A)); // Tower footprint (different shape) - var afterCount = MultiArt.FootprintCells(foundation).Count; - - Assert.NotEqual(beforeCount, afterCount); - _output.WriteLine($"redesign footprint {beforeCount} -> {afterCount} cells"); - } - finally - { - foundation.Delete(); - } - } - [SkippableFact] public void RedesignReRegistered_RoutesNewFootprintToLivePath() { diff --git a/Projects/UOContent.Tests/Tests/Engines/Pathing/Multi/MultiEdgeCaseTests.cs b/Projects/UOContent.Tests/Tests/Engines/Pathing/Multi/MultiEdgeCaseTests.cs index 8aa0b8d19..e7e368c38 100644 --- a/Projects/UOContent.Tests/Tests/Engines/Pathing/Multi/MultiEdgeCaseTests.cs +++ b/Projects/UOContent.Tests/Tests/Engines/Pathing/Multi/MultiEdgeCaseTests.cs @@ -130,33 +130,4 @@ public class MultiEdgeCaseTests } } } - - /// - /// Extensible slot for repo-owner-supplied gnarly placements. The assertion already covers - /// any (map,x,y) by construction — only the coordinates need filling in. - /// - /// TODO(coords): repo owner to supply (map,x,y) for a static tree inside a footprint and a - /// dungeon cave-wall corner; add InlineData rows here — the assertion already covers them by - /// construction. (Left intentionally unhunted: do not invent tree/dungeon coords.) - /// - [SkippableTheory] - [InlineData(MapId, HouseX, HouseY)] // known-good open Trammel placement (passes today) - public void UserSuppliedScenarios_SynthesizerMatchesCheckMovement(int mapId, int x, int y) - { - TileDataRequirement.SkipIfMissing(); - StepCache.Instance.Clear(); - var map = Map.Maps[mapId]; - - var multi = new TestMulti(GuildHouseId); - map.GetAverageZ(x, y, out _, out var z, out _); - multi.MoveToWorld(new Point3D(x, y, (sbyte)z), map); - try - { - MultiTestSupport.AssertSynthesizerMatchesCheckMovement(multi, map); - } - finally - { - multi.Delete(); - } - } } diff --git a/Projects/UOContent.Tests/Tests/Engines/Pathing/Multi/MultiMaskCacheTests.cs b/Projects/UOContent.Tests/Tests/Engines/Pathing/Multi/MultiMaskCacheTests.cs index 7487ce8de..38a6824ea 100644 --- a/Projects/UOContent.Tests/Tests/Engines/Pathing/Multi/MultiMaskCacheTests.cs +++ b/Projects/UOContent.Tests/Tests/Engines/Pathing/Multi/MultiMaskCacheTests.cs @@ -39,45 +39,6 @@ public class MultiMaskCacheTests } } - [SkippableFact] - public void IsInteriorLocalCell_TrueDeepInside_FalseAtEdge() - { - TileDataRequirement.SkipIfMissing(); - var multi = new TestMulti(GuildHouseId); - try - { - var mcl = multi.Components; - - var foundInterior = false; - var foundEdge = false; - for (var ly = 0; ly < mcl.Height && (!foundInterior || !foundEdge); ly++) - { - for (var lx = 0; lx < mcl.Width; lx++) - { - if (mcl.Tiles[lx][ly].Length == 0) - { - continue; - } - if (MultiMaskCache.IsInteriorLocalCell(mcl, lx, ly)) - { - foundInterior = true; - } - else - { - foundEdge = true; - } - } - } - - Assert.True(foundInterior, "a guild house must have at least one interior cell"); - Assert.True(foundEdge, "a guild house must have at least one edge/perimeter cell"); - } - finally - { - multi.Delete(); - } - } - [Fact] public void LocalWorldZ_RoundTrips_AndPreservesMasks() { diff --git a/Projects/UOContent.Tests/Tests/Engines/Pathing/StepCacheFileFormatTests.cs b/Projects/UOContent.Tests/Tests/Engines/Pathing/StepCacheFileFormatTests.cs index 3fe72b74a..14958d9f1 100644 --- a/Projects/UOContent.Tests/Tests/Engines/Pathing/StepCacheFileFormatTests.cs +++ b/Projects/UOContent.Tests/Tests/Engines/Pathing/StepCacheFileFormatTests.cs @@ -228,17 +228,6 @@ public class StepCacheFileFormatTests Assert.True(fileLength < 1100, $"expected every base Z array to elide; file was {fileLength} bytes"); } - [Fact] - public void CoastlineChunk_ElidesEveryZArray() - { - // Partial walkability with a non-zero SourceZ: the shape that defeats an unmasked predictor. - var src = FlatFullChunk(baseZ: 25); - var rt = RoundTrip(src, out var fileLength); - - AssertIdentical(src, rt); - Assert.True(fileLength < 1100, $"masked predictor should elide every array; file was {fileLength} bytes"); - } - [Fact] public void SlopeInOneDirection_StoresOnlyThatZArray() { diff --git a/Projects/UOContent.Tests/Tests/Engines/Pathing/StepCacheLifecycleTests.cs b/Projects/UOContent.Tests/Tests/Engines/Pathing/StepCacheLifecycleTests.cs index b4257f112..9c410efc7 100644 --- a/Projects/UOContent.Tests/Tests/Engines/Pathing/StepCacheLifecycleTests.cs +++ b/Projects/UOContent.Tests/Tests/Engines/Pathing/StepCacheLifecycleTests.cs @@ -42,16 +42,6 @@ public class StepCacheLifecycleTests : IDisposable return chunk; } - [Fact] - public void Clear_OnEmptyCache_LeavesStatsZero() - { - var stats = FreshCache(2).GetStats(); - - Assert.Equal(0, stats.ResidentChunks); - Assert.Equal(0L, stats.Hits); - Assert.Equal(0L, stats.BuildsTotal); - } - // ---- promotion gate ---- /// diff --git a/Projects/UOContent.Tests/Tests/Engines/Pathing/StepProbeTests.cs b/Projects/UOContent.Tests/Tests/Engines/Pathing/StepProbeTests.cs index 9a32d66bf..140cacb8a 100644 --- a/Projects/UOContent.Tests/Tests/Engines/Pathing/StepProbeTests.cs +++ b/Projects/UOContent.Tests/Tests/Engines/Pathing/StepProbeTests.cs @@ -78,174 +78,4 @@ public class StepProbeTests Assert.False(result.IsWalkable(Direction.South), "S should be off-map"); Assert.False(result.IsWalkable(Direction.Left), "SW should be off-map"); } - - [Fact] - public void ComputeMaskAt_TrammelOpenPlain_HasFlatCellWithAllDirectionsWalkable() - { - // Invariant: somewhere in the open-plain region, there must be a fully-flat cell - // whose mask is 0xFF and all 8 destZ values equal sourceZ (no slope). - // This protects against a regression where the baker stops detecting flat cells. - var map = Map.Maps[1]; - Assert.NotNull(map); - - var found = false; - - for (var x = 1500; x < 1532 && !found; x++) - { - for (var y = 1600; y < 1632 && !found; y++) - { - map.GetAverageZ(x, y, out _, out var avgZ, out _); - var sourceZ = (sbyte)avgZ; - var result = StepProbe.ComputeMaskAt(map, x, y, sourceZ); - - if (result.WalkMask != 0xFF) - { - continue; - } - - var allSameZ = true; - for (var d = 0; d < 8; d++) - { - if (result.GetWalkZ((Direction)d) != sourceZ) - { - allSameZ = false; - break; - } - } - - if (allSameZ) - { - found = true; - _output.WriteLine($"Flat-open cell found at ({x}, {y}, {sourceZ})"); - } - } - } - - Assert.True(found, "Expected at least one fully-flat open cell in (1500..1532, 1600..1632)"); - } - - [SkippableFact] - public void ComputeMaskAt_BritainInnDense_HasCellWithBlockedDirections() - { - TileDataRequirement.SkipIfMissing(); - // Invariant: inside the dense Britain inn region, at least one cell must have - // at least one direction blocked by a static. Protects against a regression - // where the baker reports everything as walkable (the original false-pass bug). - var map = Map.Maps[1]; - Assert.NotNull(map); - - var blockedCellCount = 0; - - for (var x = 1480; x < 1512; x++) - { - for (var y = 1610; y < 1642; y++) - { - map.GetAverageZ(x, y, out _, out var avgZ, out _); - var sourceZ = (sbyte)avgZ; - var result = StepProbe.ComputeMaskAt(map, x, y, sourceZ); - - if (result.WalkMask != 0xFF) - { - blockedCellCount++; - } - } - } - - _output.WriteLine($"Cells with at least one blocked direction: {blockedCellCount} / 1024"); - Assert.True(blockedCellCount > 0, "Expected at least one cell with a blocked direction in the dense region"); - } - - [Theory] - [InlineData(1500, 1600)] - [InlineData(1480, 1610)] - [InlineData(0, 0)] - public void ComputeMaskAt_IsDeterministic(int x, int y) - { - // Same inputs must always produce identical output. Catches accidental - // statefulness in the baker (e.g., a static cache that gets corrupted). - var map = Map.Maps[1]; - Assert.NotNull(map); - - map.GetAverageZ(x, y, out _, out var avgZ, out _); - var sourceZ = (sbyte)avgZ; - - var first = StepProbe.ComputeMaskAt(map, x, y, sourceZ); - var second = StepProbe.ComputeMaskAt(map, x, y, sourceZ); - var third = StepProbe.ComputeMaskAt(map, x, y, sourceZ); - - Assert.Equal(first.WalkMask, second.WalkMask); - Assert.Equal(first.WalkMask, third.WalkMask); - for (var d = 0; d < 8; d++) - { - Assert.Equal(first.GetWalkZ((Direction)d), second.GetWalkZ((Direction)d)); - Assert.Equal(first.GetWalkZ((Direction)d), third.GetWalkZ((Direction)d)); - } - } - - [SkippableFact] - public void ComputeMaskAt_PinnedCell_TrammelOpenPlainOrigin() - { - TileDataRequirement.SkipIfMissing(); - // PINNING test: locks specific output for Trammel (1500, 1600). - // Cell at z=10 with mask 0xC1 (N + W + NW only walkable). The other five - // directions are blocked by water (E/SE/S/SW are wet tiles, NE is shore). - // If this changes, either the baker logic changed or the underlying tile - // data changed; re-run the parity test to confirm before updating expected values. - var map = Map.Maps[1]; - Assert.NotNull(map); - - map.GetAverageZ(1500, 1600, out _, out var avgZ, out _); - var sourceZ = (sbyte)avgZ; - - var result = StepProbe.ComputeMaskAt(map, 1500, 1600, sourceZ); - - Assert.Equal((sbyte)10, sourceZ); - Assert.Equal((byte)0xC1, result.WalkMask); - - Assert.True(result.IsWalkable(Direction.North)); - Assert.False(result.IsWalkable(Direction.Right)); - Assert.False(result.IsWalkable(Direction.East)); - Assert.False(result.IsWalkable(Direction.Down)); - Assert.False(result.IsWalkable(Direction.South)); - Assert.False(result.IsWalkable(Direction.Left)); - Assert.True(result.IsWalkable(Direction.West)); - Assert.True(result.IsWalkable(Direction.Up)); - - Assert.Equal((sbyte)10, result.GetWalkZ(Direction.North)); - Assert.Equal((sbyte)10, result.GetWalkZ(Direction.West)); - Assert.Equal((sbyte)10, result.GetWalkZ(Direction.Up)); - } - - [SkippableFact] - public void ComputeMaskAt_PinnedCell_BritainInnDenseOrigin() - { - TileDataRequirement.SkipIfMissing(); - // PINNING test: locks specific output for Trammel (1480, 1610). - // Cell at z=20 with mask 0x3F (N/NE/E/SE/S/SW walkable, W/NW blocked by - // a wall to the west). All walkable directions stay flat at z=20. - var map = Map.Maps[1]; - Assert.NotNull(map); - - map.GetAverageZ(1480, 1610, out _, out var avgZ, out _); - var sourceZ = (sbyte)avgZ; - - var result = StepProbe.ComputeMaskAt(map, 1480, 1610, sourceZ); - - Assert.Equal((sbyte)20, sourceZ); - Assert.Equal((byte)0x3F, result.WalkMask); - - Assert.True(result.IsWalkable(Direction.North)); - Assert.True(result.IsWalkable(Direction.Right)); - Assert.True(result.IsWalkable(Direction.East)); - Assert.True(result.IsWalkable(Direction.Down)); - Assert.True(result.IsWalkable(Direction.South)); - Assert.True(result.IsWalkable(Direction.Left)); - Assert.False(result.IsWalkable(Direction.West)); - Assert.False(result.IsWalkable(Direction.Up)); - - for (var d = 0; d < 6; d++) - { - Assert.Equal((sbyte)20, result.GetWalkZ((Direction)d)); - } - } } diff --git a/Projects/UOContent.Tests/Tests/Engines/Spawners/Json/ImportCleanupTests.cs b/Projects/UOContent.Tests/Tests/Engines/Spawners/Json/ImportCleanupTests.cs index a9b7554fd..6b4bba2dd 100644 --- a/Projects/UOContent.Tests/Tests/Engines/Spawners/Json/ImportCleanupTests.cs +++ b/Projects/UOContent.Tests/Tests/Engines/Spawners/Json/ImportCleanupTests.cs @@ -98,32 +98,6 @@ public class ImportCleanupTests } } - [Fact] - public void Import_MalformedFile_LeaksNoWorldItems() - { - // First entry is syntactically valid JSON for a Spawner, second entry is truncated/garbage. - // STJ throws a JsonException during array deserialization before any Items are created - // (the DTO path is GC-only), so zero new spawners should appear at the valid entry's location. - var dir = Path.Combine(Path.GetTempPath(), "muo-import-malformed-" + Guid.NewGuid().ToString("N")); - Directory.CreateDirectory(dir); - File.WriteAllText(Path.Combine(dir, "bad.json"), """ - [ { "$type": "Spawner", "location": [320, 320, 0], "map": "Felucca", "count": 1, - "entries": [ { "name": "Fisherman", "maxCount": 1, "probability": 100 } ] }, - { "$type": "Spawner", "location": [ THIS IS NOT JSON - """); - try - { - var before = CountSpawnersNear(new Point3D(320, 320, 0)); - ImportSpawnersCommand.ImportFile(new FileInfo(Path.Combine(dir, "bad.json")), new Dictionary()); - // Malformed parse must construct zero Items (DTO is GC-only); nothing placed or orphaned. - Assert.Equal(before, CountSpawnersNear(new Point3D(320, 320, 0))); - } - finally - { - Directory.Delete(dir, true); - } - } - private static int CountSpawnersNear(Point3D p) { var n = 0; diff --git a/Projects/UOContent.Tests/Tests/Engines/Spawners/Json/MigratedDataLoadTests.cs b/Projects/UOContent.Tests/Tests/Engines/Spawners/Json/MigratedDataLoadTests.cs deleted file mode 100644 index b0846a795..000000000 --- a/Projects/UOContent.Tests/Tests/Engines/Spawners/Json/MigratedDataLoadTests.cs +++ /dev/null @@ -1,47 +0,0 @@ -// Regression guard: verifies that a real migrated spawn file (post-uoml/felucca/Vendors.json) -// can be deserialized as List and each DTO produces a valid spawner via ToSpawner(). -// Exercises the $type discriminator path introduced by the data migration. - -using System.Collections.Generic; -using System.IO; -using System.Text.Json; -using Server; -using Server.Engines.Spawners; -using Xunit; - -namespace UOContent.Tests.Engines.Spawners.Json; - -[Collection("Sequential UOContent Tests")] -public class MigratedDataLoadTests -{ - [Fact] - public void MigratedFile_LoadsWithDollarType() - { - var path = Path.Combine(Core.BaseDirectory, "Data", "Spawns", "post-uoml", "felucca", "Vendors.json"); - if (!File.Exists(path)) - { - return; // distribution data not present in this checkout - } - - var dtos = JsonSerializer.Deserialize>(File.ReadAllText(path), SpawnerJsonSerializer.Options); - Assert.NotEmpty(dtos); - - var spawners = new List(dtos.Count); - try - { - foreach (var dto in dtos) - { - spawners.Add(dto.ToSpawner()); - } - - Assert.Equal(dtos.Count, spawners.Count); - } - finally - { - foreach (var s in spawners) - { - s?.Delete(); - } - } - } -} diff --git a/Projects/UOContent.Tests/Tests/Engines/Spawners/Json/SpawnerDtoRoundTripTests.cs b/Projects/UOContent.Tests/Tests/Engines/Spawners/Json/SpawnerDtoRoundTripTests.cs index 13ba62da3..42b4ee9be 100644 --- a/Projects/UOContent.Tests/Tests/Engines/Spawners/Json/SpawnerDtoRoundTripTests.cs +++ b/Projects/UOContent.Tests/Tests/Engines/Spawners/Json/SpawnerDtoRoundTripTests.cs @@ -44,33 +44,6 @@ public class SpawnerDtoRoundTripTests } } - [Fact] - public void Spawner_HomeRangeSquare_WritesHomeRange_RoundTrips() - { - Spawner original = null; - BaseSpawner rebuilt = null; - try - { - original = new Spawner("Fisherman"); - original.MoveToWorld(new Point3D(200, 200, 0), Map.Felucca); - // Exactly what homeRange 5 reconstructs (centered square, standard z/depth). - original.SpawnBounds = new Rectangle3D(195, 195, -128, 11, 11, 256); - - var json = JsonSerializer.Serialize(new List { original.ToDto() }, SpawnerJsonSerializer.Options); - Assert.Contains("\"homeRange\": 5", json); - Assert.DoesNotContain("spawnBounds", json); - - var dtos = JsonSerializer.Deserialize>(json, SpawnerJsonSerializer.Options); - rebuilt = Assert.Single(dtos).ToSpawner(); - Assert.Equal(new Rectangle3D(195, 195, -128, 11, 11, 256), rebuilt.SpawnBounds); - } - finally - { - rebuilt?.Delete(); - original?.Delete(); - } - } - [Fact] public void Spawner_WritesMandatory_OmitsOptionalDefaults() { diff --git a/Projects/UOContent.Tests/Tests/Engines/Spawners/SectorSpawnCacheTests.cs b/Projects/UOContent.Tests/Tests/Engines/Spawners/SectorSpawnCacheTests.cs index bbf8e2112..1acc29960 100644 --- a/Projects/UOContent.Tests/Tests/Engines/Spawners/SectorSpawnCacheTests.cs +++ b/Projects/UOContent.Tests/Tests/Engines/Spawners/SectorSpawnCacheTests.cs @@ -7,14 +7,6 @@ namespace UOContent.Tests; public class BitMask256Tests { - [Fact] - public void BitMask256_InitialState_AllBitsZero() - { - var mask = BitMask256.AllClear(); - - Assert.Equal(0, mask.PopCount()); - } - [Theory] [InlineData(0)] [InlineData(63)] @@ -110,41 +102,6 @@ public class SectorSpawnCacheManagerTests SectorSpawnCacheManager.ClearAll(); } - [Fact] - public void ClearAll_ResetsCache() - { - SectorSpawnCacheManager.ClearAll(); - Assert.Equal(0, SectorSpawnCacheManager.CachedSectorCount); - Assert.Equal(0, SectorSpawnCacheManager.LandCacheCount); - Assert.Equal(0, SectorSpawnCacheManager.WaterCacheCount); - } - - [Fact] - public void SetValid_CreatesSectorCache() - { - var map = Map.Felucca; - var pos = new Point3D(100, 100, 0); - - SectorSpawnCacheManager.SetValid(map, pos, isWater: false); - - Assert.Equal(1, SectorSpawnCacheManager.CachedSectorCount); - Assert.Equal(1, SectorSpawnCacheManager.LandCacheCount); - Assert.Equal(0, SectorSpawnCacheManager.WaterCacheCount); - } - - [Fact] - public void SetValid_Water_CreatesWaterCache() - { - var map = Map.Felucca; - var pos = new Point3D(100, 100, 0); - - SectorSpawnCacheManager.SetValid(map, pos, isWater: true); - - Assert.Equal(1, SectorSpawnCacheManager.CachedSectorCount); - Assert.Equal(0, SectorSpawnCacheManager.LandCacheCount); - Assert.Equal(1, SectorSpawnCacheManager.WaterCacheCount); - } - [Fact] public void SetValid_LandAndWater_SeparateCaches() { @@ -251,19 +208,6 @@ public class SectorSpawnCacheManagerTests Assert.False(result); } - [Fact] - public void InvalidateSectors_RemovesCachedData() - { - var map = Map.Felucca; - - SectorSpawnCacheManager.SetValid(map, new Point3D(50, 50, 0), isWater: false); - Assert.Equal(1, SectorSpawnCacheManager.CachedSectorCount); - - SectorSpawnCacheManager.InvalidateSectors(map, new Rectangle2D(0, 0, 100, 100)); - - Assert.Equal(0, SectorSpawnCacheManager.CachedSectorCount); - } - [Fact] public void InvalidateSectors_RemovesBothLandAndWater() { @@ -341,54 +285,6 @@ public class SpiralScanTests Assert.Equal(expectedDy, dy); } - [Fact] - public void SpiralPattern_Ring1Has8Positions() - { - // Ring N has 8*N positions - const int ring1Positions = 1 * 8; - Assert.Equal(8, ring1Positions); - } - - [Fact] - public void SpiralPattern_Ring2Has16Positions() - { - const int ring2Positions = 2 * 8; - Assert.Equal(16, ring2Positions); - } - - [Fact] - public void SpiralPattern_Ring10Has80Positions() - { - const int ring10Positions = 10 * 8; - Assert.Equal(80, ring10Positions); - } - - [Fact] - public void SpiralPattern_Ring1_CoversAllAdjacentTiles() - { - // Ring 1 should cover all 8 adjacent tiles - var expectedOffsets = new HashSet<(int dx, int dy)> - { - (-1, -1), (0, -1), (1, -1), // Top row - (1, 0), // Right - (1, 1), (0, 1), (-1, 1), // Bottom row - (-1, 0) // Left - }; - - var actualOffsets = new HashSet<(int dx, int dy)>(); - - for (var position = 0; position < 8; position++) - { - actualOffsets.Add(SectorSpawnCacheManager.GetSpiralOffset(1, position)); - } - - Assert.Equal(expectedOffsets.Count, actualOffsets.Count); - foreach (var expected in expectedOffsets) - { - Assert.Contains(expected, actualOffsets); - } - } - [Fact] public void SpiralPattern_Ring2_CoversAllExpectedTiles() { @@ -414,16 +310,6 @@ public class SpiralScanTests public class SpawnPositionStateTests { - [Fact] - public void SpawnPositionState_InitialState_ZeroCounts() - { - var state = new SpawnPositionState(); - - Assert.False(state.SpiralComplete); - Assert.Equal(0, state.SpiralRing); - Assert.Equal(0, state.SpiralRingPosition); - } - [Fact] public void Reset_ClearsAllState() { @@ -457,14 +343,6 @@ public class SpawnPositionStateTests Assert.False(state.ShouldCachePositions(SpawnPositionMode.Disabled)); } - [Fact] - public void ShouldCachePositions_Automatic_FalseWithNoFailures() - { - var state = new SpawnPositionState(); - - Assert.False(state.ShouldCachePositions(SpawnPositionMode.Automatic)); - } - [Fact] public void ShouldCachePositions_Automatic_TrueAfterThresholdFailures() { @@ -522,30 +400,6 @@ public class SpawnPositionStateTests Assert.True(state.ShouldAbandon()); } - [Fact] - public void ShouldAbandon_FalseWithUsefulCacheHit() - { - var state = new SpawnPositionState(); - state.SpiralComplete = true; - - // Record 24 useless results - for (var i = 0; i < 24; i++) - { - state.RecordUselessResult(); - } - - // Record a useful cache hit - resets counter - state.RecordUsefulCacheHit(); - - // Record a few more useless results - for (var i = 0; i < 5; i++) - { - state.RecordUselessResult(); - } - - Assert.False(state.ShouldAbandon()); // Counter was reset, only 5 now - } - [Fact] public void RecordUsefulCacheHit_ResetsUselessCounter() { diff --git a/Projects/UOContent.Tests/Tests/Engines/Spawners/SpawnerEntryOwnershipTests.cs b/Projects/UOContent.Tests/Tests/Engines/Spawners/SpawnerEntryOwnershipTests.cs index 767a3289d..96023870d 100644 --- a/Projects/UOContent.Tests/Tests/Engines/Spawners/SpawnerEntryOwnershipTests.cs +++ b/Projects/UOContent.Tests/Tests/Engines/Spawners/SpawnerEntryOwnershipTests.cs @@ -67,18 +67,6 @@ public class SpawnerEntryOwnershipTests spawner.Delete(); } - [Fact] - public void Start_WorksAfterStop_WhenOwnerHasEntries() - { - var spawner = new Spawner(1, TimeSpan.FromMinutes(1), TimeSpan.FromMinutes(2), 0, default, "Rabbit"); - Assert.True(spawner.Running); - spawner.Stop(); - Assert.False(spawner.Running); - spawner.Start(); - Assert.True(spawner.Running); - spawner.Delete(); - } - [Fact] public void Dupe_ClonesEntriesIntoIndependentList() { @@ -159,85 +147,4 @@ public class SpawnerEntryOwnershipTests } // Manual: remove the Skip, run, and read the numbers from the thrown assertion. - [Fact(Skip = "manual benchmark")] - public void Benchmark_SpawnPath_Manual() - { - const int iterations = 100_000; - const int warmup = 1_000; - - var report = new ValueStringBuilder(stackalloc char[512]); - report.Append('\n', 1); - - foreach (var entryCount in new[] { 1, 10, 50 }) - { - var spawner = new Spawner(1000, TimeSpan.FromMinutes(5), TimeSpan.FromMinutes(10)); - spawner.MoveToWorld(new Point3D(1500, 1500, 0), Map.Felucca); - - // SpawnedMaxCount = 0 keeps every entry full, so Spawn() does selection only. - for (var i = 0; i < entryCount; i++) - { - spawner.AddEntry("Rabbit", 100, 0, false); - } - - for (var i = 0; i < warmup; i++) - { - spawner.Spawn(); - } - - var sw = Stopwatch.StartNew(); - for (var i = 0; i < iterations; i++) - { - spawner.Spawn(); - } - - sw.Stop(); - - var nsPerCall = sw.Elapsed.TotalMilliseconds * 1_000_000.0 / iterations; - report.Append( - $"Spawn() entries={entryCount,2}: {nsPerCall,8:F1} ns/call ({iterations} iterations, {sw.ElapsedMilliseconds} ms total)\n" - ); - - spawner.Delete(); - } - - { - var spawner = new Spawner(1000, TimeSpan.FromMinutes(5), TimeSpan.FromMinutes(10)); - spawner.MoveToWorld(new Point3D(1500, 1500, 0), Map.Felucca); - - for (var i = 0; i < 10; i++) - { - spawner.AddEntry("Rabbit", 100, 1, false); - } - - spawner.Spawn(); - Assert.Single(spawner.Spawned); - var rabbit = spawner.Spawned.Keys.First(); - - for (var i = 0; i < warmup; i++) - { - spawner.Remove(rabbit); - } - - var sw = Stopwatch.StartNew(); - for (var i = 0; i < iterations; i++) - { - spawner.Remove(rabbit); - } - - sw.Stop(); - - var nsPerCall = sw.Elapsed.TotalMilliseconds * 1_000_000.0 / iterations; - report.Append( - $"Remove(ISpawnable) entries=10: {nsPerCall,8:F1} ns/call ({iterations} iterations, {sw.ElapsedMilliseconds} ms total)\n" - ); - - (rabbit as Mobile)?.Delete(); - spawner.Delete(); - } - - var summary = report.ToString(); - report.Dispose(); - - throw new Xunit.Sdk.XunitException(summary); - } } diff --git a/Projects/UOContent.Tests/Tests/Engines/Spawners/SpawnerFixtureCapture.cs b/Projects/UOContent.Tests/Tests/Engines/Spawners/SpawnerFixtureCapture.cs deleted file mode 100644 index d3f48792a..000000000 --- a/Projects/UOContent.Tests/Tests/Engines/Spawners/SpawnerFixtureCapture.cs +++ /dev/null @@ -1,50 +0,0 @@ -using System; -using System.IO; -using System.Reflection; -using Server.Engines.Spawners; -using Server.Tests; -using Xunit; - -namespace UOContent.Tests.Engines.Spawners; - -[Collection("Sequential UOContent Tests")] -public class SpawnerFixtureCapture -{ - // Freezes the BaseSpawner v12 save layout as test input; refuses to run against newer code. - [SkippableFact] - public void CaptureLegacyBlobs() - { - Skip.If(Environment.GetEnvironmentVariable("MODERNUO_CAPTURE_SPAWNER_FIXTURES") != "1"); - - var version = (int)typeof(BaseSpawner) - .GetField("SerializationVersion", BindingFlags.NonPublic | BindingFlags.Static)! - .GetRawConstantValue()!; - Assert.True(version == 12, $"Fixtures must be captured with BaseSpawner v12; current version is {version}."); - - var dir = Path.Combine(AppContext.BaseDirectory, "Tests", "Engines", "Spawners", "Fixtures"); - Directory.CreateDirectory(dir); - - var spawner = new Spawner(2, TimeSpan.FromMinutes(1), TimeSpan.FromMinutes(2), 0, default, "Rabbit", "Bird") - { - Name = "FixtureSpawner" - }; - spawner.Entries[1].Properties = "Hue 33"; - File.WriteAllBytes(Path.Combine(dir, "spawner.v12-v1.bin"), SpawnerBlob.Write(spawner)); - - var proximity = new ProximitySpawner(1, TimeSpan.FromMinutes(1), TimeSpan.FromMinutes(2), 0, default, 5, "boo", true, "Rat", "Bird") - { - Name = "FixtureProximity" - }; - File.WriteAllBytes(Path.Combine(dir, "proximity.v12-v1-v0.bin"), SpawnerBlob.Write(proximity)); - - var region = new RegionSpawner(1, TimeSpan.FromMinutes(1), TimeSpan.FromMinutes(2), 0, "Orc", "Troll") - { - Name = "FixtureRegion" - }; - File.WriteAllBytes(Path.Combine(dir, "region.v12-v1-v0.bin"), SpawnerBlob.Write(region)); - - spawner.Delete(); - proximity.Delete(); - region.Delete(); - } -} diff --git a/Projects/UOContent.Tests/Tests/Gumps/GridLayoutTests.cs b/Projects/UOContent.Tests/Tests/Gumps/GridLayoutTests.cs index 0f4518289..629d7f16e 100644 --- a/Projects/UOContent.Tests/Tests/Gumps/GridLayoutTests.cs +++ b/Projects/UOContent.Tests/Tests/Gumps/GridLayoutTests.cs @@ -103,45 +103,6 @@ public class GridLayoutTests Assert.Equal(800, sizes[1]); } - [Fact] - public void TestGridCell_Properties() - { - var cell = new GridCell(10, 20, 100, 50); - - Assert.Equal(10, cell.X); - Assert.Equal(20, cell.Y); - Assert.Equal(100, cell.Width); - Assert.Equal(50, cell.Height); - Assert.Equal(110, cell.Right); - Assert.Equal(70, cell.Bottom); - Assert.Equal(60, cell.CenterX); - Assert.Equal(45, cell.CenterY); - } - - [Fact] - public void TestGridCell_Inset() - { - var cell = new GridCell(10, 20, 100, 50); - var inset = cell.Inset(5); - - Assert.Equal(15, inset.X); - Assert.Equal(25, inset.Y); - Assert.Equal(90, inset.Width); - Assert.Equal(40, inset.Height); - } - - [Fact] - public void TestGridCell_Offset() - { - var cell = new GridCell(10, 20, 100, 50); - var offset = cell.Offset(5, 10); - - Assert.Equal(15, offset.X); - Assert.Equal(30, offset.Y); - Assert.Equal(100, offset.Width); - Assert.Equal(50, offset.Height); - } - [Fact] public void TestListViewLayout_Pagination() { diff --git a/Projects/UOContent.Tests/Tests/Items/Jewels/T2AJewelGemCraftTests.cs b/Projects/UOContent.Tests/Tests/Items/Jewels/T2AJewelGemCraftTests.cs index c4a944cd0..b8dff707b 100644 --- a/Projects/UOContent.Tests/Tests/Items/Jewels/T2AJewelGemCraftTests.cs +++ b/Projects/UOContent.Tests/Tests/Items/Jewels/T2AJewelGemCraftTests.cs @@ -73,34 +73,6 @@ public class T2AJewelGemCraftTests } } - [Fact] - public void OnCraft_WithUnsetGemContext_LeavesPlainPiece() - { - var map = Map.Felucca; - var player = CreatePlayerMobile(map, new Point3D(4120, 500, 0)); - var ring = new GoldRing(); - - try - { - player.Backpack.AddItem(new IronIngot(10)); - - var system = GetOrInitTinkeringSystem(); - var context = system.GetContext(player); - context.PendingGemType = GemType.None; // no gem targeted - context.PendingGemCount = 0; - - ring.OnCraft(1, false, player, system, typeof(IronIngot), null, MakeRingRecipe(), 0); - - Assert.Equal(GemType.None, ring.GemType); - Assert.Equal(0, ring.GemCount); - } - finally - { - ring.Delete(); - player.Delete(); - } - } - [Fact] public void OnCraft_WhenGemsUnavailableAtCraftTime_CraftsPlainPiece() { diff --git a/Projects/UOContent.Tests/Tests/Items/Misc/Corpses/CorpseHairMigrationTests.cs b/Projects/UOContent.Tests/Tests/Items/Misc/Corpses/CorpseHairMigrationTests.cs index 3538ca931..81452623d 100644 --- a/Projects/UOContent.Tests/Tests/Items/Misc/Corpses/CorpseHairMigrationTests.cs +++ b/Projects/UOContent.Tests/Tests/Items/Misc/Corpses/CorpseHairMigrationTests.cs @@ -45,15 +45,4 @@ public class CorpseHairMigrationTests // The entire block must be consumed - the loader validates exact byte length. Assert.Equal(buffer.Length, reader.Position); } - - [Fact] - public void LegacyHairTail_Absent_ConsumesOnlyPresenceBool() - { - var buffer = WriteLegacyHairTail(false, 0, 0); - var reader = new BufferReader(buffer); - - Assert.False(reader.ReadBool()); - // Absent case: nothing else to read, and the presence bool was the only byte. - Assert.Equal(buffer.Length, reader.Position); - } } diff --git a/Projects/UOContent.Tests/Tests/Items/TreasureChests/TreasureChestDecayTests.cs b/Projects/UOContent.Tests/Tests/Items/TreasureChests/TreasureChestDecayTests.cs index 31a839ca0..942065a77 100644 --- a/Projects/UOContent.Tests/Tests/Items/TreasureChests/TreasureChestDecayTests.cs +++ b/Projects/UOContent.Tests/Tests/Items/TreasureChests/TreasureChestDecayTests.cs @@ -19,27 +19,6 @@ public class TreasureChestDecayTests // DecayScheduler reads ScheduledDecayTime repeatedly and needs the reads to agree, so the // random interval must be rolled once per chest rather than per read. - [Theory] - [MemberData(nameof(ChestTypes))] - public void DecayTimeIsStableAcrossReads(Type chestType) - { - var chest = chestType.CreateInstance(); - - try - { - var expected = chest.DecayTime; - - for (var i = 0; i < 100; i++) - { - Assert.Equal(expected, chest.DecayTime); - } - } - finally - { - chest.Delete(); - } - } - [Theory] [MemberData(nameof(ChestTypes))] public void ScheduledDecayTimeIsStableAcrossReads(Type chestType) diff --git a/Projects/UOContent.Tests/Tests/Misc/NameVerificationTests.cs b/Projects/UOContent.Tests/Tests/Misc/NameVerificationTests.cs index a9e4f5c27..60b450b0d 100644 --- a/Projects/UOContent.Tests/Tests/Misc/NameVerificationTests.cs +++ b/Projects/UOContent.Tests/Tests/Misc/NameVerificationTests.cs @@ -16,15 +16,6 @@ public class NameVerificationTests Assert.True(NameVerification.ValidatePlayerName(name)); } - [Theory] - [InlineData("Rex")] - [InlineData("MrWhiskers")] - [InlineData("Fido")] - public void ValidatePetName_ValidNames_ReturnsTrue(string name) - { - Assert.True(NameVerification.ValidatePetName(name)); - } - [Theory] [InlineData("Mrs-Smith")] [InlineData("Mr. Whiskers")] @@ -128,10 +119,4 @@ public class NameVerificationTests var exceptions = SearchValues.Create(' ', '-', '.'); Assert.False(NameVerification.Validate("-John", 2, 16, true, false, true, 1, exceptions)); } - - [Fact] - public void Validate_DisallowedCharacters_ReturnsFalse() - { - Assert.False(NameVerification.Validate("John123", 2, 16, true, false)); - } } diff --git a/Projects/UOContent.Tests/Tests/Mobiles/AI/FamiliarAITests.cs b/Projects/UOContent.Tests/Tests/Mobiles/AI/FamiliarAITests.cs index 7d39e2963..692ff8152 100644 --- a/Projects/UOContent.Tests/Tests/Mobiles/AI/FamiliarAITests.cs +++ b/Projects/UOContent.Tests/Tests/Mobiles/AI/FamiliarAITests.cs @@ -139,18 +139,6 @@ public class FamiliarAITests : IDisposable return condition(); } - [SkippableFact] - public void UsesFamiliarAI_AndStaysOnComeOrder() - { - var p = Master(1500, 1600); - var f = Familiar(0, p, 1500, 1600); - - Assert.IsType(f.AIObject); - Assert.Equal(OrderType.Come, f.ControlOrder); - Assert.Equal(0.1, f.ActiveSpeed); - Assert.Equal(0.1, f.PassiveSpeed); - } - [SkippableFact] public void Follows_WithoutBacktracking() { diff --git a/Projects/UOContent.Tests/Tests/Mobiles/AI/GuardFollowTests.cs b/Projects/UOContent.Tests/Tests/Mobiles/AI/GuardFollowTests.cs index 6765b07f9..785278507 100644 --- a/Projects/UOContent.Tests/Tests/Mobiles/AI/GuardFollowTests.cs +++ b/Projects/UOContent.Tests/Tests/Mobiles/AI/GuardFollowTests.cs @@ -50,48 +50,4 @@ public class GuardFollowTests Assert.Equal(0.2, currentSpeed); Assert.Equal(0.1, currentMoveSpeed); } - - [Fact] - public void GuardReturn_PreAOS_RunsActive() - { - var previous = Core.Expansion; - - try - { - Core.Expansion = Expansion.UOR; - - var map = Map.Maps[1]; - Assert.NotNull(map); - map.GetAverageZ(1500, 1600, out _, out var z, out _); - - var master = new PlayerMobile(World.NewMobile); - master.DefaultMobileInit(); - master.MoveToWorld(new Point3D(1494, 1600, (sbyte)z), map); - - var pet = new PetTestStub(); - pet.MoveToWorld(new Point3D(1500, 1600, (sbyte)z), map); - pet.SetControlMaster(master); - - var ai = pet.AIObject; - ai.AITimer?.Stop(); - pet.ControlOrder = OrderType.Guard; - ai.AITimer?.Stop(); - pet.SetCurrentSpeedToPassive(); // a stale passive state must not persist - - ai.NextMove = 0; - ai.Obey(); - - var currentSpeed = pet.CurrentSpeed; - - pet.Delete(); - master.Delete(); - - // No sprint pre-AOS: the return runs active. - Assert.Equal(0.2, currentSpeed); - } - finally - { - Core.Expansion = previous; - } - } } diff --git a/Projects/UOContent.Tests/Tests/Mobiles/AI/MeerEnragedTests.cs b/Projects/UOContent.Tests/Tests/Mobiles/AI/MeerEnragedTests.cs deleted file mode 100644 index 3ced177b5..000000000 --- a/Projects/UOContent.Tests/Tests/Mobiles/AI/MeerEnragedTests.cs +++ /dev/null @@ -1,77 +0,0 @@ -using System; -using System.Collections.Generic; -using Server; -using Server.Mobiles; -using Xunit; - -namespace UOContent.Tests.Mobiles.AI; - -// A meer mage fights FightMode.Evil and its enraged creatures carry -1000 karma, so the karma rules alone -// make each an enemy of the other. -[Collection("Sequential UOContent Tests")] -public class MeerEnragedTests : IDisposable -{ - private readonly List _created = []; - - public void Dispose() - { - for (var i = 0; i < _created.Count; i++) - { - _created[i].Delete(); - } - } - - private T Track(T m) where T : Mobile - { - _created.Add(m); - return m; - } - - private static void Place(BaseCreature bc, int x) - { - var map = Map.Felucca; - bc.MoveToWorld(new Point3D(x, 1600, map.GetAverageZ(x, 1600)), map); // flat, clear line of sight both ways - bc.AIObject.AITimer.Stop(); - } - - private static bool Acquire(BaseCreature creature) - { - creature.NextReacquireTime = Core.TickCount - 1; - return creature.AIObject.AcquireFocusMob(creature.RangePerception, creature.FightMode, false, false, true); - } - - [Fact] - public void MeerAndItsEnragedCreature_AreNotEnemies() - { - var meer = Track(new MeerMage()); - var enraged = Track(new EnragedRabbit(meer)); - - Assert.False(enraged.IsEnemy(meer)); - Assert.False(meer.IsEnemy(enraged)); - } - - [Fact] - public void AnotherMeersEnragedCreature_StaysAnEnemy() - { - var meer = Track(new MeerMage()); - var other = Track(new MeerMage()); - var enraged = Track(new EnragedRabbit(other)); - - Assert.True(enraged.IsEnemy(meer)); - Assert.True(meer.IsEnemy(enraged)); - } - - [SkippableFact] - public void MeerAndItsEnragedCreature_NeverAcquireEachOther() - { - Skip.If(!Server.Tests.TestServerInitializer.TileDataLoaded, "Requires UO client map data."); - - var meer = Track(new MeerMage()); - var enraged = Track(new EnragedBlackBear(meer)); - Place(meer, 1600); - Place(enraged, 1601); - - Assert.False(Acquire(enraged)); - Assert.False(Acquire(meer)); - } -} diff --git a/Projects/UOContent.Tests/Tests/Mobiles/AI/PetOrderTests.cs b/Projects/UOContent.Tests/Tests/Mobiles/AI/PetOrderTests.cs index 7d12ed744..2789cd735 100644 --- a/Projects/UOContent.Tests/Tests/Mobiles/AI/PetOrderTests.cs +++ b/Projects/UOContent.Tests/Tests/Mobiles/AI/PetOrderTests.cs @@ -265,21 +265,6 @@ public class PetOrderTests : IDisposable Assert.Equal(pet.Location, pet.Home); } - [Fact] - public void LoyaltyRelease_ClearsTheMaster_AndStartsTheDeleteCountdown() - { - var (master, pet) = Spawn(new Point3D(1000, 1000, 0), new Point3D(1001, 1000, 0)); - var followers = master.Followers; - - // What the loyalty drain assigns when loyalty reaches zero. - pet.ControlOrder = OrderType.Release; - - Assert.False(pet.Controlled); - Assert.Null(pet.ControlMaster); - Assert.Equal(followers - pet.ControlSlots, master.Followers); - Assert.True(pet.PendingDeleteTimer?.Running); - } - [Fact] public void Release_WithoutSpawner_AnchorsHomeToCurrentLocation() { @@ -375,29 +360,6 @@ public class PetOrderTests : IDisposable Assert.Same(master, pet.ControlTarget); Assert.Equal(OrderType.Follow, pet.AIObject.PersistentOrder); } - - [Fact] - public void Stop_WhileFollowing_CancelsToIdle_NonML() - { - var previous = Core.Expansion; - try - { - Core.Expansion = Expansion.SE; // pre-ML: Core.ML is false - var (_, pet) = Spawn(new Point3D(1000, 1000, 0), new Point3D(1002, 1000, 0)); - pet.ControlOrder = OrderType.Follow; - - pet.ControlOrder = OrderType.Stop; - - // Stop/idle resolution is era-independent. - Assert.Equal(OrderType.None, pet.ControlOrder); - Assert.Equal(OrderType.None, pet.AIObject.PersistentOrder); - Assert.Equal(pet.Location, pet.Home); - } - finally - { - Core.Expansion = previous; - } - } // The parameterless ctor fully initializes a player; the Serial ctor leaves that to Deserialize. private PlayerMobile SpawnPlayer(Point3D loc) { @@ -991,19 +953,6 @@ public class PetOrderTests : IDisposable Assert.Same(friend, pet.ControlTarget); } - [Fact] - public void GMObey_TakesControlOfACommandablePet() - { - var (master, pet) = Spawn(new Point3D(1000, 1000, 0), new Point3D(1001, 1000, 0)); - pet.Name = "Rex"; - var gm = SpawnPlayer(new Point3D(1002, 1000, 0)); - gm.AccessLevel = AccessLevel.GameMaster; - - pet.AIObject.OnSpeech(new SpeechEventArgs(gm, "Rex obey", MessageType.Regular, 0x3B2, [])); - - Assert.Same(gm, pet.ControlMaster); - } - [Fact] public void GMAllObey_DoesNotTakeControlledPets() { @@ -1021,42 +970,5 @@ public class PetOrderTests : IDisposable } // Release is relinquishing control, not exerting it: no roll, so no loyalty either way. - [Fact] - public void MenuRelease_TouchesNoLoyalty() - { - var (master, pet) = Spawn(new Point3D(1000, 1000, 0), new Point3D(1001, 1000, 0)); - pet.Loyalty = 50; - - new InternalEntry(3006118, 14, OrderType.Release, true).OnClick(master, pet); // Release - - Assert.Equal(50, pet.Loyalty); // no roll: neither the +1 for passing nor the -3 for failing - } - - [Fact] - public void SpeechRelease_TouchesNoLoyalty() - { - var (master, pet) = Spawn(new Point3D(1000, 1000, 0), new Point3D(1001, 1000, 0)); - pet.Name = "Rex"; - pet.Loyalty = 50; - - pet.AIObject.OnSpeech(new SpeechEventArgs(master, "Rex release", MessageType.Regular, 0x3B2, [0x16D])); - - Assert.Equal(50, pet.Loyalty); - } - // A creature nobody can command is not released either. - [Fact] - public void MenuRelease_OnAnUncontrollablePet_IsRefusedWithoutCost() - { - var (master, pet) = Spawn(new Point3D(1000, 1000, 0), new Point3D(1001, 1000, 0)); - pet.MinTameSkill = 120.0; // control chance at or below zero - master.Skills.AnimalTaming.Base = 0; - master.Skills.AnimalLore.Base = 0; - pet.Loyalty = 50; - - new InternalEntry(3006118, 14, OrderType.Release, true).OnClick(master, pet); - - Assert.Equal(50, pet.Loyalty); // refused, but never punished - Assert.True(pet.Controlled); - } } diff --git a/Projects/UOContent.Tests/Tests/Mobiles/AI/PetPacingTests.cs b/Projects/UOContent.Tests/Tests/Mobiles/AI/PetPacingTests.cs index 12d978e6d..1064fb317 100644 --- a/Projects/UOContent.Tests/Tests/Mobiles/AI/PetPacingTests.cs +++ b/Projects/UOContent.Tests/Tests/Mobiles/AI/PetPacingTests.cs @@ -99,29 +99,6 @@ public class PetPacingTests : IDisposable } // Pre-AOS pets follow at their own pace; nothing caps them. - [Fact] - public void FollowMaster_PreAOS_KeepsItsOwnPace() - { - var previous = Core.Expansion; - - try - { - Core.Expansion = Expansion.UOR; - var (master, pet) = Spawn(new Point3D(1000, 1000, 0), new Point3D(1001, 1000, 0)); - pet.SetMoveSpeed(0.3, 0.9); - - pet.ControlTarget = master; - pet.ControlOrder = OrderType.Follow; - - Assert.Equal(0.2, pet.CurrentSpeed); - Assert.Equal(0.3, pet.CurrentMoveSpeed); - } - finally - { - Core.Expansion = previous; - } - } - private sealed class SprintingPet : PetTestStub { public override double FollowMoveSpeed => 0.125; @@ -204,35 +181,7 @@ public class PetPacingTests : IDisposable } // A pet chasing a combatant keeps the move table. - [Fact] - public void CombatChasingPet_KeepsMoveTable() - { - var (_, pet) = Spawn(new Point3D(1000, 1000, 0), new Point3D(1001, 1000, 0)); - var target = new PetTestStub(); - target.MoveToWorld(new Point3D(1003, 1000, 0), Map.Felucca); - _created.Add(target); - - pet.SetMoveSpeed(0.3, 0.9); - pet.ControlOrder = OrderType.Guard; - pet.Combatant = target; - pet.SetCurrentSpeedToActive(); - - Assert.Equal(0.3, pet.CurrentMoveSpeed); - } - // Herding overrides order pacing. - [Fact] - public void HerdedObeyingPet_KeepsHerdingPace() - { - var (_, pet) = Spawn(new Point3D(1000, 1000, 0), new Point3D(1001, 1000, 0)); - pet.SetMoveSpeed(0.45, 0.9); - pet.SetCurrentSpeedToPassive(); - - pet.TargetLocation = new Point2D(1010, 1010); - - Assert.Equal(0.3, pet.CurrentMoveSpeed); // fixed herding pace - } - private sealed class ThinkProbe : PetTestStub { public int Thinks; diff --git a/Projects/UOContent.Tests/Tests/Mobiles/AI/SummonAcquireRulesTests.cs b/Projects/UOContent.Tests/Tests/Mobiles/AI/SummonAcquireRulesTests.cs index 28b311186..d80dbd5bc 100644 --- a/Projects/UOContent.Tests/Tests/Mobiles/AI/SummonAcquireRulesTests.cs +++ b/Projects/UOContent.Tests/Tests/Mobiles/AI/SummonAcquireRulesTests.cs @@ -36,21 +36,6 @@ public class SummonAcquireRulesTests : IDisposable return creature.AIObject.AcquireFocusMob(creature.RangePerception, creature.FightMode, false, false, true); } - [SkippableTheory] - [InlineData(true)] - [InlineData(false)] - public void PreAosFeluccaVortexTurnsOnItsCaster(bool bladeSpirits) - { - Skip.If(!Server.Tests.TestServerInitializer.TileDataLoaded, "Requires UO client map data."); - Core.Expansion = Expansion.UOR; - - var caster = Player(Map.Felucca, 1500, 100); - var summon = Summon(bladeSpirits ? new BladeSpirits() : new EnergyVortex(), caster, 1499); - - Assert.True(Acquire(summon)); - Assert.Same(caster, summon.FocusMob); - } - [SkippableFact] public void PreAosFeluccaVortexPrefersAnyoneOverItsCaster() { diff --git a/Projects/UOContent.Tests/Tests/Mobiles/LootingRightsTests.cs b/Projects/UOContent.Tests/Tests/Mobiles/LootingRightsTests.cs index e02abece7..922e33294 100644 --- a/Projects/UOContent.Tests/Tests/Mobiles/LootingRightsTests.cs +++ b/Projects/UOContent.Tests/Tests/Mobiles/LootingRightsTests.cs @@ -78,34 +78,6 @@ public class LootingRightsTests } } - [Fact] - public void HitsMaxBand_MovesTheRightsThreshold() - { - var victim = new TestMobile(); - var first = NewPlayer(); - var second = NewPlayer(); - - try - { - victim.RegisterDamage(100, first); - victim.RegisterDamage(40, second); - - // hitsMax >= 200 drops the bar to topDamage / 4 = 31, which 40 clears. - var rights = BaseCreature.GetLootingRights(victim.DamageEntries, 200); - - Assert.Equal(2, rights.Count); - Assert.True(rights[0].m_HasRight); - Assert.True(rights[1].m_HasRight); - Assert.Same(second, rights[1].m_Mobile); - } - finally - { - victim.Delete(); - first.Delete(); - second.Delete(); - } - } - [Fact] public void PetDamage_CreditsTheMaster_NotThePet() { diff --git a/Projects/UOContent.Tests/Tests/Mobiles/LordOaksTests.cs b/Projects/UOContent.Tests/Tests/Mobiles/LordOaksTests.cs deleted file mode 100644 index dae82a332..000000000 --- a/Projects/UOContent.Tests/Tests/Mobiles/LordOaksTests.cs +++ /dev/null @@ -1,98 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Server; -using Server.Mobiles; -using Xunit; - -namespace UOContent.Tests.Mobiles; - -// Lord Oaks' abilities and spell shield depend on his queen, so the queen must spawn from combat, -// never from a query such as GetMonsterAbilities or a damage calculation. -[Collection("Sequential UOContent Tests")] -public class LordOaksTests : IDisposable -{ - private readonly List _created = []; - - public void Dispose() - { - foreach (var m in World.Mobiles.Values.OfType().ToList()) - { - m.Delete(); - } - - for (var i = 0; i < _created.Count; i++) - { - _created[i].Delete(); - } - } - - private LordOaks NewOaks(Map map) - { - var oaks = new LordOaks(); - _created.Add(oaks); - oaks.MoveToWorld(new Point3D(1000, 1000, 0), map); - return oaks; - } - - private Mobile NewAttacker() - { - var attacker = new Mongbat(); - _created.Add(attacker); - return attacker; - } - - private static int SilvaniCount() => World.Mobiles.Values.Count(m => m is Silvani); - - [Fact] - public void QueryingOrDeletingAnUnengagedOaks_SpawnsNoQueen() - { - var oaks = NewOaks(Map.Felucca); - - var scalar = 1.0; - oaks.AlterDamageScalarFrom(null, ref scalar); - oaks.GetMonsterAbilities(); - oaks.Delete(); - - Assert.Equal(1.0, scalar); - Assert.Equal(0, SilvaniCount()); - } - - [Fact] - public void Aggression_SpawnsTheQueenOnce_AndShieldsOaksWhileSheLives() - { - var oaks = NewOaks(Map.Felucca); - var attacker = NewAttacker(); - - oaks.AggressiveAction(attacker, false); - oaks.AggressiveAction(attacker, false); - - var queen = World.Mobiles.Values.OfType().Single(); - - var shielded = 1.0; - oaks.AlterDamageScalarFrom(attacker, ref shielded); - Assert.Equal(0.1, shielded, 6); - Assert.True(oaks.CanUseAbility(MonsterAbilities.SummonPixiesCounter)); - - queen.Delete(); - - var exposed = 1.0; - oaks.AlterDamageScalarFrom(attacker, ref exposed); - oaks.AggressiveAction(attacker, false); - - Assert.Equal(1.0, exposed); - Assert.False(oaks.CanUseAbility(MonsterAbilities.SummonPixiesCounter)); - Assert.Equal(0, SilvaniCount()); - } - - [Fact] - public void AnOaksOnTheInternalMap_NeverSpawnsTheQueen() - { - var oaks = new LordOaks(); - _created.Add(oaks); - - oaks.AggressiveAction(NewAttacker(), false); - - Assert.Equal(0, SilvaniCount()); - } -} diff --git a/Projects/UOContent.Tests/Tests/Network/BanExemptionsTests.cs b/Projects/UOContent.Tests/Tests/Network/BanExemptionsTests.cs index b38364373..b3a6d291f 100644 --- a/Projects/UOContent.Tests/Tests/Network/BanExemptionsTests.cs +++ b/Projects/UOContent.Tests/Tests/Network/BanExemptionsTests.cs @@ -74,15 +74,6 @@ public class BanExemptionsTests Assert.False(BanExemptions.IsExempt(_listed, "some-future-reason", NeverCalled)); } - [Fact] - public void Manual_allowlist_does_not_spend_the_earned_lists_strikes() - { - WithManualAllowlist("192.0.2.10"); - - // Unconditional, so the revocable list must not be consulted -- that would burn a strike. - Assert.True(BanExemptions.IsExempt(_listed, BanReasons.RateLimit, NeverCalled)); - } - [Fact] public void Falls_through_to_the_login_allowlist_when_not_file_listed() { diff --git a/Projects/UOContent.Tests/Tests/Network/Bans/Blocklist/BlocklistConfigurationTests.cs b/Projects/UOContent.Tests/Tests/Network/Bans/Blocklist/BlocklistConfigurationTests.cs index 3d81c8d87..543ccddc6 100644 --- a/Projects/UOContent.Tests/Tests/Network/Bans/Blocklist/BlocklistConfigurationTests.cs +++ b/Projects/UOContent.Tests/Tests/Network/Bans/Blocklist/BlocklistConfigurationTests.cs @@ -59,12 +59,6 @@ public class BlocklistConfigurationTests } // The point of the flag: a shard that never opts in must not start the reload poll. - [Fact] - public void Blocklist_is_off_by_default() - { - Assert.False(new BlocklistSettings().Enabled); - } - // The generator (tools/Export-IpBlocklist.ps1) writes to this path by default; if one side moves // without the other, a shard silently enforces nothing. [Fact] diff --git a/Projects/UOContent.Tests/Tests/Network/Bans/Blocklist/BlocklistFileTests.cs b/Projects/UOContent.Tests/Tests/Network/Bans/Blocklist/BlocklistFileTests.cs index 1b7a7772c..1e1c0fedf 100644 --- a/Projects/UOContent.Tests/Tests/Network/Bans/Blocklist/BlocklistFileTests.cs +++ b/Projects/UOContent.Tests/Tests/Network/Bans/Blocklist/BlocklistFileTests.cs @@ -72,14 +72,4 @@ public class BlocklistFileTests Assert.True(snap.IsBanned(IPAddress.Parse("223.169.4.9"))); File.Delete(p); } - - [Fact] - public void Load_parses_body() - { - var p = WriteTemp("# generated=x count=1\n8.8.8.0/24\n"); - var snap = BlocklistFile.Load(p, out var parsed, out _); - Assert.Equal(1, parsed); - Assert.True(snap.IsBanned(IPAddress.Parse("8.8.8.8"))); - File.Delete(p); - } } diff --git a/Projects/UOContent.Tests/Tests/Network/Bans/Blocklist/BlocklistSnapshotTests.cs b/Projects/UOContent.Tests/Tests/Network/Bans/Blocklist/BlocklistSnapshotTests.cs index abdac1d11..9a9c98139 100644 --- a/Projects/UOContent.Tests/Tests/Network/Bans/Blocklist/BlocklistSnapshotTests.cs +++ b/Projects/UOContent.Tests/Tests/Network/Bans/Blocklist/BlocklistSnapshotTests.cs @@ -54,12 +54,6 @@ public class BlocklistSnapshotTests Assert.True(s.IsBanned(IPAddress.Parse("5.6.7.200"))); } - [Fact] - public void Empty_snapshot_matches_nothing() - { - Assert.False(BlocklistSnapshot.Empty.IsBanned(IPAddress.Parse("1.2.3.4"))); - } - [Fact] public void Ipv6_single_and_cidr_are_matched() { diff --git a/Projects/UOContent.Tests/Tests/Network/Bans/CrowdSecAlertClientTests.cs b/Projects/UOContent.Tests/Tests/Network/Bans/CrowdSecAlertClientTests.cs index 756e19b1d..786598515 100644 --- a/Projects/UOContent.Tests/Tests/Network/Bans/CrowdSecAlertClientTests.cs +++ b/Projects/UOContent.Tests/Tests/Network/Bans/CrowdSecAlertClientTests.cs @@ -42,14 +42,6 @@ public class CrowdSecAlertClientTests Assert.Equal("/v1/decisions?origin=modern%20uo%2Ftest%26x%3D1&ip=1.2.3.4", query); } - [Fact] - public void BuildDeleteQuery_PlainOrigin_Ipv4() - { - var query = CrowdSecAlertClient.BuildDeleteQuery("modernuo", IPAddress.Parse("192.168.1.1")); - - Assert.Equal("/v1/decisions?origin=modernuo&ip=192.168.1.1", query); - } - [Fact] public void BuildDeleteQuery_Ipv6_IsEscaped() { diff --git a/Projects/UOContent.Tests/Tests/Network/Firewall/FirewallTests.cs b/Projects/UOContent.Tests/Tests/Network/Firewall/FirewallTests.cs index fabf6d4ba..26e289f0c 100644 --- a/Projects/UOContent.Tests/Tests/Network/Firewall/FirewallTests.cs +++ b/Projects/UOContent.Tests/Tests/Network/Firewall/FirewallTests.cs @@ -76,14 +76,4 @@ public class FirewallTests Assert.IsType(Server.Network.Firewall.ToFirewallEntry("10.0.0.0-10.0.0.255")); Assert.Null(Server.Network.Firewall.ToFirewallEntry("not-an-ip")); } - - [Fact] - public void ReadFirewallSet_SurfacesAddedEntries() - { - Server.Network.Firewall.ResetForTesting(); - var entry = new SingleIpFirewallEntry(Ip("1.2.3.4")); - Server.Network.Firewall.Add(entry); - - Server.Network.Firewall.ReadFirewallSet(set => Assert.Contains(entry, set)); - } } diff --git a/Projects/UOContent.Tests/Tests/Network/ManualAllowlist/ManualAllowlistConfigurationTests.cs b/Projects/UOContent.Tests/Tests/Network/ManualAllowlist/ManualAllowlistConfigurationTests.cs index 1146414b5..0687c5dce 100644 --- a/Projects/UOContent.Tests/Tests/Network/ManualAllowlist/ManualAllowlistConfigurationTests.cs +++ b/Projects/UOContent.Tests/Tests/Network/ManualAllowlist/ManualAllowlistConfigurationTests.cs @@ -50,12 +50,6 @@ public class ManualAllowlistConfigurationTests } // The point of the flag: a shard that never opts in must not start the reload poll. - [Fact] - public void Manual_allowlist_is_off_by_default() - { - Assert.False(new ManualAllowlistSettings().Enabled); - } - // The generator creates ip-allowlist.txt beside the blocklist; the wildcard is what picks up a // carve-out file (-RefreshCarveouts writes ip-allowlist-starlink.txt) with no config edit. [Fact] diff --git a/Projects/UOContent.Tests/Tests/Network/Packets/AuthIdTests.cs b/Projects/UOContent.Tests/Tests/Network/Packets/AuthIdTests.cs index 456eb174a..2fb0f73cb 100644 --- a/Projects/UOContent.Tests/Tests/Network/Packets/AuthIdTests.cs +++ b/Projects/UOContent.Tests/Tests/Network/Packets/AuthIdTests.cs @@ -255,17 +255,6 @@ public class AuthIdTests : IDisposable new ClientVersion(7, 0, 0, 0) ); - [Fact] - public void IssuesAnIdWhenTheConnectionHasNone() - { - var account = CreateAccount("authid-first-select-user"); - - var authId = Ensure(0, account, AddressX); - - Assert.NotEqual(0, authId); - Assert.Equal(1, IncomingAccountPackets.AuthIdWindowCount); - } - // Handing the same id back rather than minting another is what makes an orphan impossible, // instead of something to clean up afterwards. [Fact] @@ -371,12 +360,4 @@ public class AuthIdTests : IDisposable GameServer.GameServerLoginEvent(right); Assert.True(right.Accepted); } - - [Fact] - public void GeneratesDistinctAuthIds() - { - var account = CreateAccount("authid-distinct-user"); - - Assert.NotEqual(Register(account, AddressX), Register(account, AddressX)); - } } diff --git a/Projects/UOContent.Tests/Tests/Skills/DetectHiddenTests.cs b/Projects/UOContent.Tests/Tests/Skills/DetectHiddenTests.cs index bc416d148..e7a3f1d47 100644 --- a/Projects/UOContent.Tests/Tests/Skills/DetectHiddenTests.cs +++ b/Projects/UOContent.Tests/Tests/Skills/DetectHiddenTests.cs @@ -141,68 +141,6 @@ public class DetectHiddenTests } } - /// - /// TryDetectStealther is a no-op when the target is not actually hidden. - /// - [Fact] - public void TryDetectStealther_DoesNothing_WhenStealtherIsNotHidden() - { - using var rng = new PredictableRandom(10); - DetectHidden.ClearDebounceCache(); - var map = Map.Felucca; - var detector = CreatePlayerMobile(map, new Point3D(1800, 500, 0)); - var stealther = CreatePlayerMobile(map, new Point3D(1801, 500, 0)); - - try - { - detector.Skills.DetectHidden.BaseFixedPoint = 1000; - stealther.Hidden = false; - - var result = DetectHidden.TryDetectStealther(detector, stealther); - - Assert.False(result); - Assert.False(stealther.Hidden); - } - finally - { - detector.Delete(); - stealther.Delete(); - DetectHidden.ClearDebounceCache(); - } - } - - /// - /// Passive detection only works on Felucca. A stealther on Trammel - /// should never be revealed by passive detection. - /// - [Fact] - public void TryDetectStealther_DoesNotReveal_WhenNotOnFelucca() - { - using var rng = new PredictableRandom(10); - DetectHidden.ClearDebounceCache(); - var map = Map.Trammel; - var detector = CreatePlayerMobile(map, new Point3D(2000, 500, 0)); - var stealther = CreatePlayerMobile(map, new Point3D(2001, 500, 0)); - - try - { - detector.Skills.DetectHidden.BaseFixedPoint = 1000; // 100.0 - stealther.Skills.Hiding.BaseFixedPoint = 0; // 0.0 - stealther.Hidden = true; - - var result = DetectHidden.TryDetectStealther(detector, stealther); - - Assert.False(result); - Assert.True(stealther.Hidden); - } - finally - { - detector.Delete(); - stealther.Delete(); - DetectHidden.ClearDebounceCache(); - } - } - private static PlayerMobile CreatePlayerMobile(Map map, Point3D location) { var mobile = new PlayerMobile(World.NewMobile); diff --git a/Projects/UOContent.Tests/Tests/Skills/SkillEventsTests.cs b/Projects/UOContent.Tests/Tests/Skills/SkillEventsTests.cs index 91fba77bc..e94e16753 100644 --- a/Projects/UOContent.Tests/Tests/Skills/SkillEventsTests.cs +++ b/Projects/UOContent.Tests/Tests/Skills/SkillEventsTests.cs @@ -81,44 +81,4 @@ public class SkillEventsTests from.Delete(); } } - - [Fact] - public void CheckSkill_Direct_DoesNotRaise() - { - var from = new Mobile(); - var skill = from.Skills[SkillName.Mining]; - var recorder = new Recorder(); - - SkillEvents.SkillUsed += recorder.Handle; - try - { - SkillCheck.CheckSkill(from, skill, null, 1.0); - Assert.Equal(0, recorder.Calls); - } - finally - { - SkillEvents.SkillUsed -= recorder.Handle; - from.Delete(); - } - } - - [Fact] - public void NoSubscriber_DoesNotThrow() - { - var from = new Mobile(); - var recorder = new Recorder(); - - try - { - SkillEvents.SkillUsed += recorder.Handle; - SkillEvents.SkillUsed -= recorder.Handle; - - Assert.True(SkillCheck.Mobile_SkillCheckDirectLocation(from, SkillName.Mining, 1.0)); - Assert.Equal(0, recorder.Calls); - } - finally - { - from.Delete(); - } - } } diff --git a/Projects/UOContent.Tests/Tests/Spells/Necromancy/BloodOathSpellTests.cs b/Projects/UOContent.Tests/Tests/Spells/Necromancy/BloodOathSpellTests.cs index 08658b1a1..fb2ee2f8d 100644 --- a/Projects/UOContent.Tests/Tests/Spells/Necromancy/BloodOathSpellTests.cs +++ b/Projects/UOContent.Tests/Tests/Spells/Necromancy/BloodOathSpellTests.cs @@ -19,24 +19,8 @@ public class BloodOathSpellTests // ModernUO previously used /80 (matching the bugged in-game tooltip), which made // Spirit Speak almost irrelevant to duration. RunUO/ServUO and the code's own // fixed-point comment both use /8. - [Theory] - [InlineData(120.0, 0.0, 23.0)] // GM Spirit Speak, no resist - [InlineData(120.0, 120.0, 8.0)] // equal skills -> baseline 8s - [InlineData(100.0, 20.0, 18.0)] // (100-20)/8 + 8 - [InlineData(20.0, 0.0, 10.5)] // minimum skill - public void GetDurationSeconds_UsesDivideByEight(double ss, double resist, double expected) - { - Assert.Equal(expected, BloodOathSpell.GetDurationSeconds(ss, resist), 3); - } - // Player caster: Publish 48 resist mitigation does NOT apply; the attacker takes the // full reflected (original, un-bonused) damage. - [Fact] - public void ComputeReflectedDamage_NoMitigation_ReturnsOriginal() - { - Assert.Equal(40, BloodOathSpell.ComputeReflectedDamage(40, 120.0, applyResistMitigation: false)); - } - // Creature caster (Publish 48 / SA+): ((Resist * 10) / 20) + 10 = % of reflected damage resisted. [Theory] [InlineData(40, 0.0, 36)] // 10% resisted -> 40 * 0.90 @@ -82,14 +66,6 @@ public class BloodOathSpellTests target.Delete(); } - [Fact] - public void RemoveCurse_NotCursed_ReturnsFalse() - { - var m = NewMobile(); - Assert.False(BloodOathSpell.RemoveCurse(m)); - m.Delete(); - } - // Issue #1690: death/delete of either party must break the oath immediately. The oath is wired // to the central PlayerMobile/BaseCreature death+delete events instead of a polling timer. [Fact] diff --git a/Projects/UOContent.Tests/Tests/Spells/Ninjitsu/AnimalFormTests.cs b/Projects/UOContent.Tests/Tests/Spells/Ninjitsu/AnimalFormTests.cs index 34a665495..133238ff1 100644 --- a/Projects/UOContent.Tests/Tests/Spells/Ninjitsu/AnimalFormTests.cs +++ b/Projects/UOContent.Tests/Tests/Spells/Ninjitsu/AnimalFormTests.cs @@ -44,17 +44,6 @@ public class AnimalFormTests } // Talisman-gated forms are never selectable without the matching talisman, regardless of skill. - [Fact] - public void CanSelectEntry_TalismanGatedForm_RequiresTalisman() - { - var m = NewMobileWithNinjitsu(120.0); - - Assert.False(AnimalForm.CanSelectEntry(m, AnimalForm.Entries[IndexOf(typeof(Squirrel))])); - Assert.False(AnimalForm.CanSelectEntry(m, AnimalForm.Entries[IndexOf(typeof(Ferret))])); - - m.Delete(); - } - // Issue #2452: attempting a form above your skill must report NoSkill (so the caller can // refrain from charging mana) and must not actually transform the caster. [Fact] @@ -70,19 +59,4 @@ public class AnimalFormTests m.Delete(); } - - [Fact] - public void Morph_SufficientSkill_TransformsCaster() - { - // >= ReqSkill + 37.5 guarantees the success-chance roll is skipped. - var m = NewMobileWithNinjitsu(60.0); - - var result = AnimalForm.Morph(m, IndexOf(typeof(Rat))); // needs 20 - - Assert.Equal(AnimalForm.MorphResult.Success, result); - Assert.NotNull(AnimalForm.GetContext(m)); - - AnimalForm.RemoveContext(m); // stop the form timer / clear state - m.Delete(); - } } diff --git a/Projects/UOContent.Tests/Tests/Utilities/TryParseTests.cs b/Projects/UOContent.Tests/Tests/Utilities/TryParseTests.cs index b6c693c84..4167e3d06 100644 --- a/Projects/UOContent.Tests/Tests/Utilities/TryParseTests.cs +++ b/Projects/UOContent.Tests/Tests/Utilities/TryParseTests.cs @@ -5,21 +5,6 @@ namespace Server.Tests.Utility; public class TryParseTests { - [Theory] - [InlineData("True", null, true)] - [InlineData("False", null, false)] - [InlineData("Alakazam", "Not a valid boolean string.", true)] - public void TestTryParseBool(string value, string returned, bool parsedAs) - { - var actualReturned = Server.Types.TryParse(typeof(bool), value, out var constructed); - Assert.Equal(returned, actualReturned); - - if (returned == null) - { - Assert.Equal(parsedAs, constructed); - } - } - [Theory] // Parsed directly into the target type (INumber.TryParse), not via ulong + Convert.ChangeType. [InlineData(typeof(int), "42", true, 42)] diff --git a/Projects/UOContent.Tests/Tests/WorldSaves/ArchiveJournalTests.cs b/Projects/UOContent.Tests/Tests/WorldSaves/ArchiveJournalTests.cs index ede98a27f..5cf7dbeea 100644 --- a/Projects/UOContent.Tests/Tests/WorldSaves/ArchiveJournalTests.cs +++ b/Projects/UOContent.Tests/Tests/WorldSaves/ArchiveJournalTests.cs @@ -25,18 +25,6 @@ public class ArchiveJournalTests : IDisposable } } - [Fact] - public void Configure_LoadsCleanState() - { - // Arrange & Act - ArchiveJournal.Configure(_testDir); - - // Assert - fresh directory should have no operations - // Note: ArchiveJournal is static, so we check the journal file doesn't exist - var journalPath = Path.Combine(_testDir, ".archive-journal.json"); - Assert.False(File.Exists(journalPath)); - } - [Fact] public void BeginOperation_CreatesStartedEntry() { diff --git a/Projects/UOContent.Tests/Tests/WorldSaves/AutoArchiveHelperTests.cs b/Projects/UOContent.Tests/Tests/WorldSaves/AutoArchiveHelperTests.cs deleted file mode 100644 index b0d2b99d4..000000000 --- a/Projects/UOContent.Tests/Tests/WorldSaves/AutoArchiveHelperTests.cs +++ /dev/null @@ -1,104 +0,0 @@ -using System; -using Server.Saves; -using Xunit; - -namespace Server.Tests; - -[Collection("Sequential UOContent Tests")] -public class AutoArchiveHelperTests -{ - [Fact] - public void ArchiveDestinationRegistry_RegisterAndRetrieve() - { - // Arrange - var dest = new LocalArchiveDestination(24, 30, 12); - - // Act - ArchiveDestinationRegistry.Register(dest); - - // Assert - Assert.Contains(dest, ArchiveDestinationRegistry.Destinations); - - // Cleanup - ArchiveDestinationRegistry.Unregister(dest); - Assert.DoesNotContain(dest, ArchiveDestinationRegistry.Destinations); - } - - [Fact] - public void LocalArchiveDestination_SendArchive_ReturnsTrue() - { - // Arrange - var dest = new LocalArchiveDestination(24, 30, 12); - - // Act - var result = dest.SendArchive("/fake/path.tar.zst", ArchivePeriod.Hourly, DateTime.UtcNow); - - // Assert - Assert.True(result); - } - - [Theory] - [InlineData(ArchivePeriod.Hourly, 24)] - [InlineData(ArchivePeriod.Daily, 30)] - [InlineData(ArchivePeriod.Monthly, 12)] - public void LocalArchiveDestination_RetentionCounts(ArchivePeriod period, int expected) - { - // Arrange - var dest = new LocalArchiveDestination(24, 30, 12); - - // Act & Assert - Assert.Equal(expected, dest.GetRetentionCount(period)); - } - - [Fact] - public void ArchiveCompletedEventArgs_StoresValues() - { - // Arrange & Act - var args = new ArchiveCompletedEventArgs( - "/archives/hourly/test.tar.zst", - ArchivePeriod.Hourly, - new DateTime(2026, 3, 22, 14, 0, 0, DateTimeKind.Utc), - 1048576, - 2.5 - ); - - // Assert - Assert.Equal("/archives/hourly/test.tar.zst", args.ArchiveFilePath); - Assert.Equal(ArchivePeriod.Hourly, args.Period); - Assert.Equal(1048576, args.FileSizeBytes); - Assert.Equal(2.5, args.ElapsedSeconds); - } - - [Fact] - public void ArchiveFailedEventArgs_StoresException() - { - // Arrange - var ex = new InvalidOperationException("disk full"); - - // Act - var args = new ArchiveFailedEventArgs( - ArchivePeriod.Daily, - new DateTime(2026, 3, 22, 0, 0, 0, DateTimeKind.Utc), - ex - ); - - // Assert - Assert.Equal(ArchivePeriod.Daily, args.Period); - Assert.Same(ex, args.Exception); - } - - [Fact] - public void ArchiveJournalEntry_DefaultState() - { - // Arrange & Act - var entry = new ArchiveJournalEntry(); - - // Assert - Assert.NotNull(entry.SourceDirectories); - Assert.Empty(entry.SourceDirectories); - Assert.NotNull(entry.DestinationResults); - Assert.Empty(entry.DestinationResults); - Assert.Null(entry.FailureReason); - Assert.Equal(DateTime.MaxValue, entry.CompletedAt); - } -} diff --git a/Projects/UOContent.Tests/Tests/WorldSaves/ManagedArchiveTests.cs b/Projects/UOContent.Tests/Tests/WorldSaves/ManagedArchiveTests.cs index 7103a65c5..11c72458d 100644 --- a/Projects/UOContent.Tests/Tests/WorldSaves/ManagedArchiveTests.cs +++ b/Projects/UOContent.Tests/Tests/WorldSaves/ManagedArchiveTests.cs @@ -46,29 +46,6 @@ public class ManagedArchiveTests : IDisposable return dir; } - [Fact] - public void CreateTarZstd_SingleDirectory_RoundTrips() - { - // Arrange - var sourceDir = CreateTestDirectory("source", - ("file1.txt", "Hello World"), - ("file2.bin", "Binary content here"), - ("subdir/nested.txt", "Nested file") - ); - - var archivePath = Path.Combine(_testDir, "test.tar.zst"); - - // Act - var entryCount = ManagedArchive.CreateTarZstd( - [sourceDir], archivePath, _testDir, compressionLevel: 1 - ); - - // Assert - Assert.True(entryCount > 0); - Assert.True(File.Exists(archivePath)); - Assert.True(new FileInfo(archivePath).Length > 0); - } - [Fact] public void CreateTarZstd_MultipleDirectories_IncludesAll() { @@ -224,30 +201,6 @@ public class ManagedArchiveTests : IDisposable Assert.Equal(-1, count); } - [Theory] - [InlineData(1)] - [InlineData(3)] - [InlineData(10)] - public void CreateTarZstd_DifferentCompressionLevels_AllWork(int level) - { - // Arrange - var sourceDir = CreateTestDirectory($"level-{level}", ("data.txt", new string('X', 10000))); - var archivePath = Path.Combine(_testDir, $"level-{level}.tar.zst"); - - // Act - var entryCount = ManagedArchive.CreateTarZstd( - [sourceDir], archivePath, _testDir, compressionLevel: level - ); - - // Assert - Assert.Equal(1, entryCount); - Assert.True(File.Exists(archivePath)); - - // Verify we can extract it back - var extractDir = Path.Combine(_testDir, $"extract-level-{level}"); - Assert.True(ManagedArchive.ExtractTarZstd(archivePath, extractDir)); - } - [Fact] public void ExtractTarGz_RoundTrips() {