Files
Fritiof Hedman c74dbd2602 1.1 MacAddress value type
Permissive parser (colon, hyphen, unseparated; case-insensitive; surrounding
whitespace) with canonical lowercase no-separator ToString. Implements
IParsable/ISpanParsable for ASP.NET route binding and ships a System.Text.Json
converter that normalizes on read.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-07 13:55:48 +02:00

113 lines
3.4 KiB
C#

using System.Text.Json;
using FrameProcessor.Domain;
namespace FrameProcessor.Tests;
public class MacAddressTests
{
private static readonly byte[] ExpectedBytes = [0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF];
private const string Canonical = "aabbccddeeff";
[Theory]
[InlineData("AA:BB:CC:DD:EE:FF")]
[InlineData("aa:bb:cc:dd:ee:ff")]
[InlineData("AA-BB-CC-DD-EE-FF")]
[InlineData("aa-bb-cc-dd-ee-ff")]
[InlineData("AABBCCDDEEFF")]
[InlineData("aabbccddeeff")]
[InlineData(" AA:BB:CC:DD:EE:FF ")]
[InlineData("\tAA-bb-CC-dd-EE-ff\n")]
public void TryParse_AcceptedForms_ProduceCanonicalString(string input)
{
Assert.True(MacAddress.TryParse(input, out var mac));
Assert.Equal(Canonical, mac.ToString());
}
[Fact]
public void Equality_IsByteWise_AcrossInputForms()
{
Assert.True(MacAddress.TryParse("AA:BB:CC:DD:EE:FF", out var a));
Assert.True(MacAddress.TryParse("aa-bb-cc-dd-ee-ff", out var b));
Assert.True(MacAddress.TryParse("AABBCCDDEEFF", out var c));
Assert.Equal(a, b);
Assert.Equal(b, c);
Assert.Equal(a.GetHashCode(), b.GetHashCode());
}
[Fact]
public void Equality_DiffersOnAnySingleByte()
{
Assert.True(MacAddress.TryParse("aabbccddeeff", out var a));
Assert.True(MacAddress.TryParse("aabbccddeefe", out var b));
Assert.NotEqual(a, b);
}
[Theory]
[InlineData("")]
[InlineData(" ")]
[InlineData("not-a-mac")]
[InlineData("AA:BB:CC:DD:EE")] // too short
[InlineData("AA:BB:CC:DD:EE:FF:00")] // too long
[InlineData("ZZ:BB:CC:DD:EE:FF")] // non-hex digit
[InlineData("AA:BB:CC:DD:EE:F")] // odd hex count
[InlineData("AA BB CC DD EE FF")] // unsupported space separator
public void TryParse_RejectsBadInput(string input)
{
Assert.False(MacAddress.TryParse(input, out _));
}
[Fact]
public void TryParse_Null_ReturnsFalse()
{
Assert.False(MacAddress.TryParse((string?)null, out _));
}
[Fact]
public void Parse_Throws_OnBadInput()
{
Assert.Throws<FormatException>(() => MacAddress.Parse("nope"));
}
[Fact]
public void Constructor_FromBytes_RoundTripsThroughToString()
{
var mac = new MacAddress(ExpectedBytes);
Assert.Equal(Canonical, mac.ToString());
}
[Fact]
public void Constructor_FromWrongLength_Throws()
{
Assert.Throws<ArgumentException>(() => new MacAddress(new byte[] { 0x00 }));
Assert.Throws<ArgumentException>(() => new MacAddress(new byte[7]));
}
[Fact]
public void JsonRoundTrip_NormalizesToCanonicalForm()
{
var json = "\"AA:BB:CC:DD:EE:FF\"";
var mac = JsonSerializer.Deserialize<MacAddress>(json);
var written = JsonSerializer.Serialize(mac);
Assert.Equal($"\"{Canonical}\"", written);
}
[Fact]
public void JsonDeserialize_BadInput_Throws()
{
Assert.Throws<JsonException>(() => JsonSerializer.Deserialize<MacAddress>("\"not-a-mac\""));
}
[Fact]
public void IParsable_Hookup_Works()
{
var mac = MacAddress.Parse("AA:BB:CC:DD:EE:FF", provider: null);
Assert.Equal(Canonical, mac.ToString());
Assert.True(MacAddress.TryParse("AA:BB:CC:DD:EE:FF", provider: null, out var mac2));
Assert.Equal(mac, mac2);
}
}