mirror of
https://github.com/kwsch/NHSE.git
synced 2026-03-21 17:24:30 -05:00
Updates the Field Item Editor to render layers based on the entire map, and the per-patch positioning of each layer. Import/export will gracefully handle upgrade/downgrade, and viewport import/export will gracefully update tiles rather than a per-acre basis. Performance has also been slightly improved; no allocation is done anymore when updating the image.
132 lines
4.2 KiB
C#
132 lines
4.2 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using FluentAssertions;
|
|
using NHSE.Core;
|
|
using Xunit;
|
|
|
|
namespace NHSE.Tests;
|
|
|
|
public static class ArrayUtilTests
|
|
{
|
|
[Fact]
|
|
public static void ReplaceOccurrences_WhenPatternNotFound_ReturnsZero()
|
|
{
|
|
byte[] array = [0x01, 0x02, 0x03, 0x04, 0x05];
|
|
byte[] pattern = [0xAA, 0xBB];
|
|
byte[] swap = [0xCC, 0xDD];
|
|
|
|
var count = array.ReplaceOccurrences(pattern, swap);
|
|
|
|
count.Should().Be(0);
|
|
array.Should().BeEquivalentTo([0x01, 0x02, 0x03, 0x04, 0x05]);
|
|
}
|
|
|
|
[Fact]
|
|
public static void ReplaceOccurrences_WhenSingleOccurrence_ReplacesAndReturnsOne()
|
|
{
|
|
byte[] array = [0x01, 0xAA, 0xBB, 0x04, 0x05];
|
|
byte[] pattern = [0xAA, 0xBB];
|
|
byte[] swap = [0xCC, 0xDD];
|
|
|
|
var count = array.ReplaceOccurrences(pattern, swap);
|
|
|
|
count.Should().Be(1);
|
|
array.Should().BeEquivalentTo([0x01, 0xCC, 0xDD, 0x04, 0x05]);
|
|
}
|
|
|
|
[Fact]
|
|
public static void ReplaceOccurrences_WhenMultipleOccurrences_ReplacesAllAndReturnsCount()
|
|
{
|
|
byte[] array = [0xAA, 0xBB, 0x03, 0xAA, 0xBB, 0x06, 0xAA, 0xBB];
|
|
byte[] pattern = [0xAA, 0xBB];
|
|
byte[] swap = [0xCC, 0xDD];
|
|
|
|
var count = array.ReplaceOccurrences(pattern, swap);
|
|
|
|
count.Should().Be(3);
|
|
array.Should().BeEquivalentTo([0xCC, 0xDD, 0x03, 0xCC, 0xDD, 0x06, 0xCC, 0xDD]);
|
|
}
|
|
|
|
[Fact]
|
|
public static void ReplaceOccurrences_WhenConsecutiveOccurrences_ReplacesAll()
|
|
{
|
|
byte[] array = [0xAA, 0xBB, 0xAA, 0xBB, 0xAA, 0xBB];
|
|
byte[] pattern = [0xAA, 0xBB];
|
|
byte[] swap = [0xCC, 0xDD];
|
|
|
|
var count = array.ReplaceOccurrences(pattern, swap);
|
|
|
|
count.Should().Be(3);
|
|
array.Should().BeEquivalentTo([0xCC, 0xDD, 0xCC, 0xDD, 0xCC, 0xDD]);
|
|
}
|
|
|
|
[Fact]
|
|
public static void ReplaceOccurrences_WhenSwapContainsPattern_DoesNotCauseInfiniteLoop()
|
|
{
|
|
// Swap contains the original pattern - must skip past swapped data
|
|
byte[] array = [0x01, 0xAA, 0xBB, 0x04];
|
|
byte[] pattern = [0xAA, 0xBB];
|
|
byte[] swap = [0xAA, 0xBB]; // Same as pattern
|
|
|
|
var count = array.ReplaceOccurrences(pattern, swap);
|
|
|
|
count.Should().Be(0);
|
|
array.Should().BeEquivalentTo([0x01, 0xAA, 0xBB, 0x04]);
|
|
}
|
|
|
|
[Fact]
|
|
public static void ReplaceOccurrences_WhenLargeFileWithRandomPlacements_ReplacesAllOccurrences()
|
|
{
|
|
const int fileSize = 1024 * 1024; // 1 MB
|
|
const int sequenceLength = 0x13; // 19 bytes
|
|
const int seed = 42;
|
|
|
|
var random = new Random(seed);
|
|
Span<byte> file = new byte[fileSize];
|
|
random.NextBytes(file);
|
|
|
|
// Create two distinct random sequences
|
|
Span<byte> patternA = stackalloc byte[sequenceLength];
|
|
Span<byte> patternB = stackalloc byte[sequenceLength];
|
|
random.NextBytes(patternA);
|
|
random.NextBytes(patternB);
|
|
|
|
// Ensure patterns are different
|
|
patternB[0] = (byte)(patternA[0] ^ 0xFF);
|
|
|
|
// Insert pattern A at random non-overlapping offsets
|
|
var insertedOffsets = new List<int>();
|
|
int currentPosition = 0;
|
|
|
|
while (currentPosition + sequenceLength <= fileSize)
|
|
{
|
|
// Random gap between 0 and 1000 bytes, then add sequence length to avoid overlap
|
|
int gap = random.Next(0, 1000);
|
|
int nextOffset = currentPosition + gap;
|
|
|
|
if (nextOffset + sequenceLength > fileSize)
|
|
break;
|
|
|
|
// Insert pattern A at this offset
|
|
patternA.CopyTo(file[nextOffset..]);
|
|
insertedOffsets.Add(nextOffset);
|
|
|
|
// Move past the inserted sequence
|
|
currentPosition = nextOffset + sequenceLength;
|
|
}
|
|
|
|
// Run the replacement
|
|
var count = file.ReplaceOccurrences(patternA, patternB);
|
|
|
|
// Verify count matches
|
|
count.Should().Be(insertedOffsets.Count);
|
|
|
|
// Verify each offset now contains pattern B
|
|
foreach (var offset in insertedOffsets)
|
|
{
|
|
var actual = file.Slice(offset, sequenceLength);
|
|
actual.SequenceEqual(patternB).Should().BeTrue(because: $"offset {offset} should have been replaced");
|
|
}
|
|
}
|
|
}
|