diff --git a/.runsettings b/.runsettings
index 3d17b679..536c73fb 100644
--- a/.runsettings
+++ b/.runsettings
@@ -1,4 +1,4 @@
-
+
@@ -12,5 +12,6 @@
Verbose
False
--forcegc
+ True
-
\ No newline at end of file
+
diff --git a/Tests/NFUnitTestGC/TestGC.cs b/Tests/NFUnitTestGC/TestGC.cs
index 0bec97a8..bf52e121 100644
--- a/Tests/NFUnitTestGC/TestGC.cs
+++ b/Tests/NFUnitTestGC/TestGC.cs
@@ -1,7 +1,8 @@
-// Licensed to the .NET Foundation under one or more agreements.
+// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+using System;
using nanoFramework.TestFramework;
namespace NFUnitTestGC
@@ -15,8 +16,6 @@ public void TestGCStress()
int maxArraySize = 1024 * 32;
object[] arrays = new object[600];
- // Starting TestGCStress
-
for (int loop = 0; loop < 100; loop++)
{
OutputHelper.WriteLine($"Running iteration {loop}");
@@ -24,7 +23,7 @@ public void TestGCStress()
for (int i = 0; i < arrays.Length - 1;)
{
OutputHelper.WriteLine($"Alloc array of {maxArraySize} bytes @ pos {i}");
- arrays[i++] = new byte[maxArraySize]; ;
+ arrays[i++] = new byte[maxArraySize];
OutputHelper.WriteLine($"Alloc array of 64 bytes @ pos {i}");
arrays[i++] = new byte[64];
@@ -37,8 +36,35 @@ public void TestGCStress()
arrays[i] = null;
}
}
+ }
+
+ [TestMethod]
+ public void TestGetTotalMemory()
+ {
+ // create several objects
+ object[] objects = new object[100];
+
+ for (int i = 0; i < objects.Length; i++)
+ {
+ objects[i] = new object();
+ }
+
+ // get total memory
+ long totalMemory = GC.GetTotalMemory(false);
+ OutputHelper.WriteLine($"Total memory: {totalMemory} bytes");
+
+ // release objects
+ for (int i = 0; i < objects.Length; i++)
+ {
+ objects[i] = null;
+ }
+
+ // get total memory, forcing full collection
+ long totalMemoryAfterCollection = GC.GetTotalMemory(true);
+ OutputHelper.WriteLine($"Total memory: {totalMemoryAfterCollection} bytes");
- // Completed TestGCStress
+ // check if memory was released
+ Assert.IsTrue(totalMemory > totalMemoryAfterCollection, "Memory was not released");
}
}
}
diff --git a/Tests/NFUnitTestSystemLib/NFUnitTestSystemLib.nfproj b/Tests/NFUnitTestSystemLib/NFUnitTestSystemLib.nfproj
index 593d741a..95da98f9 100644
--- a/Tests/NFUnitTestSystemLib/NFUnitTestSystemLib.nfproj
+++ b/Tests/NFUnitTestSystemLib/NFUnitTestSystemLib.nfproj
@@ -21,9 +21,12 @@
true
UnitTest
v1.0
+ 13.0
+
+
@@ -61,4 +64,4 @@
-
\ No newline at end of file
+
diff --git a/Tests/NFUnitTestSystemLib/RuntimeHelpersTests.cs b/Tests/NFUnitTestSystemLib/RuntimeHelpersTests.cs
new file mode 100644
index 00000000..d02d1c00
--- /dev/null
+++ b/Tests/NFUnitTestSystemLib/RuntimeHelpersTests.cs
@@ -0,0 +1,68 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Runtime.CompilerServices;
+using nanoFramework.TestFramework;
+
+namespace NFUnitTestSystemLib
+{
+ [TestClass]
+ class RuntimeHelpersTests
+ {
+ [TestMethod]
+ public static void IsReferenceOrContainsReferences()
+ {
+ Assert.IsFalse(RuntimeHelpers.IsReferenceOrContainsReferences());
+ Assert.IsTrue(RuntimeHelpers.IsReferenceOrContainsReferences());
+ Assert.IsFalse(RuntimeHelpers.IsReferenceOrContainsReferences());
+ Assert.IsFalse(RuntimeHelpers.IsReferenceOrContainsReferences());
+ Assert.IsTrue(RuntimeHelpers.IsReferenceOrContainsReferences());
+ Assert.IsFalse(RuntimeHelpers.IsReferenceOrContainsReferences());
+ Assert.IsTrue(RuntimeHelpers.IsReferenceOrContainsReferences());
+ Assert.IsTrue(RuntimeHelpers.IsReferenceOrContainsReferences>());
+ Assert.IsTrue(RuntimeHelpers.IsReferenceOrContainsReferences>());
+ //Assert.IsTrue(RuntimeHelpers.IsReferenceOrContainsReferences());
+ //Assert.IsTrue(RuntimeHelpers.IsReferenceOrContainsReferences());
+ }
+
+ private struct StructWithoutReferences
+ {
+ public int a, b, c;
+ }
+
+ private struct StructWithReferences
+ {
+ public int a, b, c;
+ public object d;
+ }
+
+ private ref struct RefStructWithoutReferences
+ {
+ public int a;
+ public long b;
+ }
+
+ private ref struct RefStructWithReferences
+ {
+ public int a;
+ public object b;
+ }
+
+ // TODO: add after checking viability of ref fields in ref structs
+ //private ref struct RefStructWithRef
+ //{
+ // public ref int a;
+
+ // internal RefStructWithRef(ref int aVal)
+ // {
+ // a = ref aVal;
+ // }
+ //}
+
+ //private ref struct RefStructWithNestedRef
+ //{
+ // public Span a;
+ //}
+ }
+}
diff --git a/Tests/NFUnitTestSystemLib/UnitTestGCTest.cs b/Tests/NFUnitTestSystemLib/UnitTestGCTest.cs
index 05c73642..42a3e8f0 100644
--- a/Tests/NFUnitTestSystemLib/UnitTestGCTest.cs
+++ b/Tests/NFUnitTestSystemLib/UnitTestGCTest.cs
@@ -9,6 +9,11 @@ namespace NFUnitTestSystemLib
[TestClass]
public class UnitTestGCTest
{
+#pragma warning disable S1215 // this is intended to test the GC
+#pragma warning disable S1854 // this is intended to test the GC
+#pragma warning disable S2696 // this is intended to test the GC
+#pragma warning disable S3971 // this is intended to test the GC
+
internal class FinalizeObject
{
public static FinalizeObject m_currentInstance = null;
@@ -54,17 +59,20 @@ public void SystemGC1_Test()
/// 6. Verify that object has been collected
///
///
- // Tests ReRegisterForFinalize
- // Create a FinalizeObject.
+
+ OutputHelper.WriteLine("Tests ReRegisterForFinalize");
+ OutputHelper.WriteLine("Create a FinalizeObject.");
+
FinalizeObject mfo = new FinalizeObject();
m_hasFinalized1 = false;
m_hasFinalized2 = false;
// Release reference
+ OutputHelper.WriteLine("Release reference");
mfo = null;
- // Allow GC
- GC.WaitForPendingFinalizers();
+ OutputHelper.WriteLine("Allow GC");
+ GC.Collect();
int sleepTime = 1000;
int slept = 0;
@@ -85,10 +93,10 @@ public void SystemGC1_Test()
// FinalizeObject.m_currentInstance field. Setting this value
// to null and forcing another garbage collection will now
// cause the object to Finalize permanently.
- // Reregister and allow for GC
- FinalizeObject.m_currentInstance = null;
- GC.WaitForPendingFinalizers();
+ OutputHelper.WriteLine("Reregister and allow for GC");
+ FinalizeObject.m_currentInstance = null;
+ GC.Collect();
sleepTime = 1000;
slept = 0;
@@ -119,18 +127,19 @@ public void SystemGC2_Test()
/// 6. Verify that object has not been collected
///
///
- // Tests SuppressFinalize
- // Create a FinalizeObject.
+
+ OutputHelper.WriteLine("Tests SuppressFinalize");
+ OutputHelper.WriteLine("Create a FinalizeObject");
FinalizeObject mfo = new FinalizeObject();
m_hasFinalized1 = false;
m_hasFinalized2 = false;
- // Releasing
+ OutputHelper.WriteLine("Releasing");
GC.SuppressFinalize(mfo);
mfo = null;
- // Allow GC
- GC.WaitForPendingFinalizers();
+ OutputHelper.WriteLine("Allow GC");
+ GC.Collect();
int sleepTime = 1000;
int slept = 0;
@@ -138,7 +147,7 @@ public void SystemGC2_Test()
while (!m_hasFinalized1 && slept < sleepTime)
{
// force GC run caused by memory allocation
- var dummyArray = new byte[1024 * 1024 * 1];
+ _ = new byte[1024 * 1024 * 1];
System.Threading.Thread.Sleep(10);
slept += 10;
@@ -161,59 +170,35 @@ public void SystemGC3_Test()
///
///
- // Tests WaitForPendingFinalizers, dependant on test 1
- // will auto-fail if test 1 fails.
OutputHelper.Write("Tests WaitForPendingFinalizers, dependant on test 1");
- OutputHelper.WriteLine("will auto-fail if test 1 fails.");
+ OutputHelper.WriteLine("will fail if test 1 fails.");
- Assert.IsTrue(m_Test1Result);
+ Assert.IsTrue(m_Test1Result, "Can't run this test as SystemGC1_Test has failed.");
- // Create a FinalizeObject.
+ OutputHelper.WriteLine("Create a FinalizeObject");
FinalizeObject mfo = new FinalizeObject();
m_hasFinalized1 = false;
m_hasFinalized2 = false;
- // Releasing
+ OutputHelper.WriteLine("Releasing");
mfo = null;
- int sleepTime = 1000;
- int slept = 0;
-
- while (!m_hasFinalized1 && slept < sleepTime)
- {
- // force GC run caused by memory allocation
- var dummyArray = new byte[1024 * 1024 * 1];
-
- System.Threading.Thread.Sleep(10);
- slept += 10;
- }
-
- OutputHelper.WriteLine($"GC took {slept}");
-
- // Wait for GC
+ OutputHelper.WriteLine("Wait for GC");
+ GC.Collect();
GC.WaitForPendingFinalizers();
- // Releasing again
+ OutputHelper.WriteLine("Releasing again");
FinalizeObject.m_currentInstance = null;
- sleepTime = 1000;
- slept = 0;
-
- while (!m_hasFinalized2 && slept < sleepTime)
- {
- // force GC run caused by memory allocation
- var dummyArray = new byte[1024 * 1024 * 1];
-
- System.Threading.Thread.Sleep(10);
- slept += 10;
- }
-
- OutputHelper.WriteLine($"GC took {slept}");
-
- // Wait for GC
+ OutputHelper.WriteLine("Wait for GC");
+ GC.Collect();
GC.WaitForPendingFinalizers();
Assert.IsTrue(m_hasFinalized2);
}
}
+#pragma warning restore S1215 // "GC.Collect" should not be called
+#pragma warning restore S1854 // Unused assignments should be removed
+#pragma warning restore S2696 // Instance members should not write to "static" fields
+#pragma warning restore S3971 // "GC.SuppressFinalize" should not be called
}
diff --git a/Tests/NFUnitTestSystemLib/UnitTestInitLocalTests.cs b/Tests/NFUnitTestSystemLib/UnitTestInitLocalTests.cs
index fea53af7..a26039e7 100644
--- a/Tests/NFUnitTestSystemLib/UnitTestInitLocalTests.cs
+++ b/Tests/NFUnitTestSystemLib/UnitTestInitLocalTests.cs
@@ -49,6 +49,9 @@ public void SystemType1_GetType_Test()
// ConstructorInfo)
// NOTE: We add the reflection items to the ArrayList to assure that they can be properly
// assigned to a object container (this used to lead to a access violation)
+
+ OutputHelper.WriteLine("Testing Int32");
+
Type type = typeof(int);
list.Add(type);
string name = ((Type)list[i]).Name;
@@ -68,12 +71,16 @@ public void SystemType1_GetType_Test()
//fRes &= name.ToLower() == "mscorlib";
//i++;
+ OutputHelper.WriteLine("Testing NFUnitTestSystemLib.UnitTestInitLocalTests+TestObj");
+
type = Type.GetType("NFUnitTestSystemLib.UnitTestInitLocalTests+TestObj");
list.Add(type);
name = ((Type)list[i]).Name;
Assert.AreEqual(name, "TestObj");
i++;
+ OutputHelper.WriteLine("Testing IEmptyInterface");
+
Type iface = type.GetInterfaces()[0];
list.Add(iface);
name = ((Type)list[i]).Name;
@@ -81,6 +88,8 @@ public void SystemType1_GetType_Test()
Assert.AreEqual(iface.Name, "IEmptyInterface");
i++;
+ OutputHelper.WriteLine("Testing FieldInfo");
+
FieldInfo fi = type.GetField("Field1");
list.Add(fi);
name = ((FieldInfo)list[i]).Name;
@@ -88,6 +97,8 @@ public void SystemType1_GetType_Test()
Assert.AreEqual(fi.Name, "Field1");
i++;
+ OutputHelper.WriteLine("Testing MethodInfo");
+
MethodInfo mi = type.GetMethod("Method1");
list.Add(mi);
name = ((MethodInfo)list[i]).Name;
@@ -95,6 +106,8 @@ public void SystemType1_GetType_Test()
Assert.AreEqual(mi.Name, "Method1");
i++;
+ OutputHelper.WriteLine("Testing ConstructorInfo");
+
ConstructorInfo ci = type.GetConstructor(new Type[] { });
list.Add(ci);
name = ((ConstructorInfo)list[i]).Name;
@@ -104,7 +117,10 @@ public void SystemType1_GetType_Test()
//
// Now test arrays of reflection types
- //
+ //
+
+ OutputHelper.WriteLine("Testing Array of Type");
+
Type[] types = new Type[] { typeof(int), typeof(bool), Type.GetType("NFUnitTestSystemLib.UnitTestInitLocalTests+TestObj") };
list.Add(types[2]);
name = ((Type)list[i]).Name;
@@ -127,6 +143,8 @@ public void SystemType1_GetType_Test()
//fRes &= asms[0].GetName().Name == "Microsoft.SPOT.Platform.Tests.Systemlib2";
//i++;
+ OutputHelper.WriteLine("Testing Array of FieldInfo");
+
FieldInfo[] fis = new FieldInfo[] { types[2].GetField("Field1"), type.GetFields()[0] };
list.Add(fis[0]);
name = ((FieldInfo)list[i]).Name;
@@ -134,6 +152,8 @@ public void SystemType1_GetType_Test()
Assert.AreEqual(fis[0].Name, "Field1");
i++;
+ OutputHelper.WriteLine("Testing Array of MethodInfo");
+
MethodInfo[] mis = new MethodInfo[] { type.GetMethods()[2], types[2].GetMethod("Method2", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public) };
list.Add(mis[1]);
name = ((MethodInfo)list[i]).Name;
@@ -141,6 +161,8 @@ public void SystemType1_GetType_Test()
Assert.AreEqual(mis[1].Name, "Method2");
i++;
+ OutputHelper.WriteLine("Testing Array of ConstructorInfo");
+
ConstructorInfo[] cis = new ConstructorInfo[] { types[2].GetConstructor(new Type[] { }), typeof(TestObj).GetConstructor(new Type[] { typeof(int) }) };
list.Add(cis[0]);
name = ((ConstructorInfo)list[i]).Name;
@@ -148,6 +170,8 @@ public void SystemType1_GetType_Test()
Assert.AreEqual(cis[0].Name, ".ctor");
i++;
+ OutputHelper.WriteLine("Testing Array of System.Collections.ArrayList");
+
Array ar = Array.CreateInstance(typeof(Type), 3);
((IList)ar)[0] = typeof(Type);
((IList)ar)[1] = Type.GetType("System.Collections.ArrayList");
@@ -157,7 +181,6 @@ public void SystemType1_GetType_Test()
Assert.AreEqual(name, "ArrayList");
Assert.AreEqual(((Type)((IList)ar)[0]).Name, "Type");
Assert.AreEqual(((Type)ar.GetValue(1)).Name, "ArrayList");
- i++;
list.Clear();
}
@@ -192,14 +215,24 @@ public void SystemType1_ArrayListToArrayForStruct_Test()
// make sure boxing of struct value type (Guid) is handled properly
// this test was a result of a bug found by a customer.
Guid ret = new Guid();
+
ArrayList guidList = new ArrayList();
guidList.Add(Guid.NewGuid());
guidList.Add(Guid.NewGuid());
+
Guid[] guidArray = (Guid[])guidList.ToArray(typeof(Guid));
+ // Verify the array has the correct length
+ Assert.AreEqual(2, guidArray.Length);
+
+ // Verify each element is not empty and matches the source list
+ int i = 0;
foreach (Guid g in guidArray)
{
+ Assert.IsFalse(Guid.Empty.Equals(g), "Guid should not be empty");
+ Assert.AreEqual(guidList[i], g, "Guid should match the source ArrayList element");
ret = g;
+ i++;
}
}
diff --git a/Tests/NFUnitTestSystemLib/UnitTestNullable.cs b/Tests/NFUnitTestSystemLib/UnitTestNullable.cs
new file mode 100644
index 00000000..74a98741
--- /dev/null
+++ b/Tests/NFUnitTestSystemLib/UnitTestNullable.cs
@@ -0,0 +1,266 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using nanoFramework.TestFramework;
+
+namespace NFUnitTestSystemLib
+{
+ [TestClass]
+ public class UnitTestNullable
+ {
+ [TestMethod]
+ public void Ctor_Empty()
+ {
+ // Nullable and Nullable are mostly verbatim ports so we don't test much here.
+
+ int? n = default(int?);
+ Assert.IsFalse(n.HasValue);
+
+ // TODO replace with Assert.ThrowsException when available
+ Assert.ThrowsException(
+ typeof(InvalidOperationException),
+ () => _ = n.Value);
+ Assert.ThrowsException(
+ typeof(InvalidOperationException),
+ () => _ = (int)n);
+
+ Assert.IsNull(n);
+ Assert.AreNotEqual(7, n);
+ Assert.AreEqual(0, n.GetHashCode());
+ Assert.AreEqual("", n.ToString());
+ Assert.AreEqual(default(int), n.GetValueOrDefault());
+ Assert.AreEqual(999, n.GetValueOrDefault(999));
+
+ n = new int?(42);
+ Assert.IsTrue(n.HasValue);
+ Assert.AreEqual(42, n.Value);
+ Assert.AreEqual(42, (int)n);
+ Assert.IsNotNull(n);
+ Assert.AreNotEqual(7, n);
+ Assert.AreEqual(42, n);
+ Assert.AreEqual(42.GetHashCode(), n.GetHashCode());
+ Assert.AreEqual(42.ToString(), n.ToString());
+ Assert.AreEqual(42, n.GetValueOrDefault());
+ Assert.AreEqual(42, n.GetValueOrDefault(999));
+
+ n = 88;
+ Assert.IsTrue(n.HasValue);
+ Assert.AreEqual(88, n.Value);
+ }
+
+ [TestMethod]
+ public static void Boxing()
+ {
+ int? n = new int?(42);
+ Unbox(n);
+ }
+
+ private static void Unbox(object o)
+ {
+ Type t = o.GetType();
+
+ // TOOD: replace with Assert.IsNotType(t); when available
+ Assert.IsNotInstanceOfType(typeof(int?), t);
+
+ Assert.AreEqual(typeof(int), t);
+ }
+
+ [TestMethod]
+ public static void ExplicitCast_T()
+ {
+ int? nullable = 5;
+ int value = (int)nullable;
+ Assert.AreEqual(5, value);
+
+ nullable = null;
+ // TODO replace with Assert.Throws(() => (int)nullable); when available
+ Assert.ThrowsException(
+ typeof(InvalidOperationException),
+ () => _ = (int)nullable);
+ }
+
+ [TestMethod]
+ public static void GetUnderlyingType()
+ {
+ Assert.AreEqual(typeof(int), Nullable.GetUnderlyingType(typeof(int?)));
+ Assert.AreEqual(null, Nullable.GetUnderlyingType(typeof(int)));
+ Assert.AreEqual(null, Nullable.GetUnderlyingType(typeof(G)));
+ }
+
+ [TestMethod]
+ public static void GetUnderlyingType_NullType_ThrowsArgumentNullException()
+ {
+ // TODO replace with Assert.Throws("nullableType", () => Nullable.GetUnderlyingType(null)); when available
+ Assert.ThrowsException(
+ typeof(ArgumentNullException),
+ () => Nullable.GetUnderlyingType(null)
+ );
+ }
+
+ // TODO: Uncomment when available
+ //[TestMethod]
+ //public static void GetValueRefOrDefaultRef_WithValue()
+ //{
+ // static void Test(T before, T after)
+ // where T : struct
+ // {
+ // T? nullable = before;
+ // ref readonly T reference = ref Nullable.GetValueRefOrDefaultRef(in nullable);
+
+ // Assert.AreEqual(before, nullable!.Value);
+
+ // Unsafe.AsRef(in reference) = after;
+
+ // Assert.Equal(after, nullable.Value);
+ // }
+
+ // Test((byte)0, (byte)42);
+ // Test(0, 42);
+ // Test(1.3f, 3.14f);
+ // Test(0.555, 8.49);
+ // Test(Guid.NewGuid(), Guid.NewGuid());
+ //}
+
+ // TODO: Uncomment when available
+ //[TestMethod]
+ //public static void GetValueRefOrDefaultRef_WithDefault()
+ //{
+ // static void Test()
+ // where T : struct
+ // {
+ // T? nullable = null;
+ // ref readonly T reference = ref Nullable.GetValueRefOrDefaultRef(in nullable);
+
+ // Assert.Equal(nullable!.GetValueOrDefault(), reference);
+ // }
+
+ // Test();
+ // Test();
+ // Test();
+ // Test();
+ // Test();
+ //}
+
+ // TODO: Uncomment when available
+ //[TestMethod]
+ //public static void GetValueRefOrDefaultRef_UnsafeWriteToNullMaintainsExpectedBehavior()
+ //{
+ // static void Test(T after)
+ // where T : struct
+ // {
+ // T? nullable = null;
+ // ref readonly T reference = ref Nullable.GetValueRefOrDefaultRef(in nullable);
+
+ // Unsafe.AsRef(in reference) = after;
+
+ // Assert.Equal(after, nullable.GetValueOrDefault()); // GetValueOrDefault() unconditionally returns the field
+ // Assert.False(nullable.HasValue);
+ // Assert.Equal(0, nullable.GetHashCode()); // GetHashCode() returns 0 if HasValue is false, without reading the field
+ // Assert.Throws(() => nullable.Value); // Accessing the value should still throw despite the write
+ // Assert.Throws(() => (T)nullable);
+ // }
+
+ // Test((byte)42);
+ // Test(42);
+ // Test(3.14f);
+ // Test(8.49);
+ // Test(Guid.NewGuid());
+ //}
+
+ [TestMethod]
+ public static void Compare_Equals()
+ {
+ // Case 1: (null, null, 0)
+ int? n1 = null;
+ int? n2 = null;
+ int expected = 0;
+ Assert.AreEqual(expected == 0, Nullable.Equals(n1, n2));
+ Assert.AreEqual(expected == 0, n1.Equals(n2));
+ // TODO Assert.AreEqual(expected, Nullable.Compare(n1, n2));
+
+ // Case 2: (7, null, 1)
+ n1 = 7;
+ n2 = null;
+ expected = 1;
+ Assert.AreEqual(expected == 0, Nullable.Equals(n1, n2));
+ Assert.AreEqual(expected == 0, n1.Equals(n2));
+ // TODO Assert.AreEqual(expected, Nullable.Compare(n1, n2));
+
+ // Case 3: (null, 7, -1)
+ n1 = null;
+ n2 = 7;
+ expected = -1;
+ Assert.AreEqual(expected == 0, Nullable.Equals(n1, n2));
+ Assert.AreEqual(expected == 0, n1.Equals(n2));
+ // TODO Assert.AreEqual(expected, Nullable.Compare(n1, n2));
+
+ // Case 4: (7, 7, 0)
+ n1 = 7;
+ n2 = 7;
+ expected = 0;
+ Assert.AreEqual(expected == 0, Nullable.Equals(n1, n2));
+ Assert.AreEqual(expected == 0, n1.Equals(n2));
+ // TODO Assert.AreEqual(expected, Nullable.Compare(n1, n2));
+
+ // Case 5: (7, 5, 1)
+ n1 = 7;
+ n2 = 5;
+ expected = 1;
+ Assert.AreEqual(expected == 0, Nullable.Equals(n1, n2));
+ Assert.AreEqual(expected == 0, n1.Equals(n2));
+ // TODO Assert.AreEqual(expected, Nullable.Compare(n1, n2));
+
+ // Case 6: (5, 7, -1)
+ n1 = 5;
+ n2 = 7;
+ expected = -1;
+ Assert.AreEqual(expected == 0, Nullable.Equals(n1, n2));
+ Assert.AreEqual(expected == 0, n1.Equals(n2));
+ // TODO Assert.AreEqual(expected, Nullable.Compare(n1, n2));
+ }
+
+ //[TestMethod]
+ //public static void MutatingMethods_MutationsAffectOriginal()
+ //{
+ // MutatingStruct? ms = new MutatingStruct() { Value = 1 };
+
+ // for (int i = 1; i <= 2; i++)
+ // {
+ // Assert.AreEqual(i.ToString(), ms.Value.ToString());
+ // Assert.AreEqual(i, ms.Value.Value);
+
+ // Assert.AreEqual(i.ToString(), ms.ToString());
+ // Assert.AreEqual(i + 1, ms.Value.Value);
+ // }
+
+ // for (int i = 3; i <= 4; i++)
+ // {
+ // Assert.AreEqual(i, ms.Value.GetHashCode());
+ // Assert.AreEqual(i, ms.Value.Value);
+
+ // Assert.AreEqual(i, ms.GetHashCode());
+ // Assert.AreEqual(i + 1, ms.Value.Value);
+ // }
+
+ // for (int i = 5; i <= 6; i++)
+ // {
+ // ms.Value.Equals(new object());
+ // Assert.AreEqual(i, ms.Value.Value);
+
+ // ms.Equals(new object());
+ // Assert.AreEqual(i + 1, ms.Value.Value);
+ // }
+ //}
+
+ //private struct MutatingStruct
+ //{
+ // public int Value;
+ // public override string ToString() => Value++.ToString();
+ // public override bool Equals(object obj) => Value++.Equals(null);
+ // public override int GetHashCode() => Value++.GetHashCode();
+ //}
+
+ public class G { }
+ }
+}
diff --git a/Tests/NFUnitTestSystemLib/UnitTestReflectionTypeTest.cs b/Tests/NFUnitTestSystemLib/UnitTestReflectionTypeTest.cs
index 709f2122..805c2b93 100644
--- a/Tests/NFUnitTestSystemLib/UnitTestReflectionTypeTest.cs
+++ b/Tests/NFUnitTestSystemLib/UnitTestReflectionTypeTest.cs
@@ -5,6 +5,7 @@
using System.Collections;
using System.Reflection;
using nanoFramework.TestFramework;
+using static NFUnitTestSystemLib.UnitTestNullable;
namespace NFUnitTestSystemLib
{
@@ -52,85 +53,103 @@ public void SystemReflectionType_RuntimeType_Test1()
///
TestClass cls = new TestClass();
+ OutputHelper.WriteLine("Testing Type members for a class type");
+
// Test Type members for a class type
Type t = cls.GetType();
Assembly asm = t.Assembly;
list.Add(asm);
- Assert.AreEqual(((Assembly)list[i]).GetName().Name, "NFUnitTest");
- Assert.AreEqual(asm.GetName().Name, "NFUnitTest");
- Assert.AreEqual(t.Name, "TestClass");
- Assert.AreEqual(t.FullName, "NFUnitTestSystemLib.UnitTestReflectionTypeTest+TestClass");
+ Assert.AreEqual("NFUnitTest", ((Assembly)list[i]).GetName().Name);
+ Assert.AreEqual("NFUnitTest", asm.GetName().Name);
+ Assert.AreEqual("TestClass", t.Name);
+ Assert.AreEqual("NFUnitTestSystemLib.UnitTestReflectionTypeTest+TestClass", t.FullName);
Assert.IsInstanceOfType(t.BaseType, typeof(object));
Assert.IsNull(t.GetElementType());
+ OutputHelper.WriteLine("Testing methods of class type");
+
MethodInfo[] mis = t.GetMethods();
- Assert.AreEqual(mis[0].Name, "Method1");
+ Assert.AreEqual("Method1", mis[0].Name);
mis = t.GetMethods(BindingFlags.Instance | BindingFlags.NonPublic);
- Assert.AreEqual(mis[0].Name, "Method2");
+ Assert.AreEqual("Method2", mis[0].Name);
Assert.IsNotNull(t.GetMethod("Method1"));
Assert.IsNotNull(t.GetMethod("Method2", BindingFlags.Instance | BindingFlags.NonPublic));
+ OutputHelper.WriteLine("Testing fields of class type");
+
FieldInfo[] fis = t.GetFields();
- Assert.AreEqual(fis[0].Name, "m_Field1");
+ Assert.AreEqual("m_Field1", fis[0].Name);
fis = t.GetFields(BindingFlags.Instance | BindingFlags.NonPublic);
- Assert.AreEqual(fis[0].Name, "m_Field2");
+ Assert.AreEqual("m_Field2", fis[0].Name);
Assert.IsNotNull(t.GetField("m_Field1"));
Assert.IsNotNull(t.GetField("m_Field2", BindingFlags.NonPublic | BindingFlags.Instance));
Assert.IsNotNull(t.GetConstructor(new Type[] { }));
+ OutputHelper.WriteLine("Testing interfaces of class type");
+
Type[] ifaces = t.GetInterfaces();
- Assert.AreEqual(ifaces.Length, 2);
- Assert.AreEqual(ifaces[0].Name, "IInterface1");
- Assert.AreEqual(ifaces[1].Name, "IInterface2");
+ Assert.AreEqual(2, ifaces.Length);
+ Assert.AreEqual("IInterface1", ifaces[0].Name);
+ Assert.AreEqual("IInterface2", ifaces[1].Name);
Assert.IsTrue(t.IsSubclassOf(typeof(object)));
i++;
+ OutputHelper.WriteLine("Testing Type members for a struct valuetype");
+
// test Type members for a struct valuetype
TestStruct str = new TestStruct();
t = str.GetType();
asm = t.Assembly;
list.Add(asm);
- Assert.AreEqual(((Assembly)list[i]).GetName().Name, "NFUnitTest");
- Assert.AreEqual(asm.GetName().Name, "NFUnitTest");
- Assert.AreEqual(t.Name, "TestStruct");
- Assert.AreEqual(t.FullName, "NFUnitTestSystemLib.UnitTestReflectionTypeTest+TestStruct");
+ Assert.AreEqual("NFUnitTest", ((Assembly)list[i]).GetName().Name);
+ Assert.AreEqual("NFUnitTest", asm.GetName().Name);
+ Assert.AreEqual("TestStruct", t.Name);
+ Assert.AreEqual("NFUnitTestSystemLib.UnitTestReflectionTypeTest+TestStruct", t.FullName);
Assert.IsInstanceOfType(t.BaseType, typeof(ValueType));
- Assert.AreEqual(t.GetInterfaces().Length, 0);
+ Assert.AreEqual(0, t.GetInterfaces().Length);
Assert.IsNull(t.GetElementType());
i++;
+ OutputHelper.WriteLine("Testing Type members for an Assembly reflection type");
+
// test Type members for an Assembly reflection type
//Assembly asmObj = typeof(TestClass).Assembly;
//t = asmObj.GetType();
//asm = t.Assembly;
//list.Add(asm);
- //Assert.AreEqual(((Assembly)list[i]).GetName().Name, "mscorlib");
- //Assert.AreEqual(asm.GetName().Name, "mscorlib");
- //Assert.AreEqual(t.Name, "Assembly");
- //Assert.AreEqual(t.FullName, "System.Reflection.Assembly");
+ //Assert.AreEqual("mscorlib", ((Assembly)list[i]).GetName().Name);
+ //Assert.AreEqual("mscorlib", asm.GetName().Name);
+ //Assert.AreEqual("Assembly", t.Name);
+ //Assert.AreEqual("System.Reflection.Assembly", t.FullName);
//Assert.IsInstanceOfType(t.BaseType, typeof(Object));
- //Assert.AreEqual(t.GetInterfaces().Length, 0);
+ //Assert.AreEqual(0, t.GetInterfaces().Length);
//Assert.IsNull(t.GetElementType());
+ OutputHelper.WriteLine("Testing Type members for a MethodInfo reflection type");
+
mis = typeof(TestClass).GetMethods(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
t = mis.GetType();
- Assert.AreEqual(t.Name, "RuntimeMethodInfo[]");
- Assert.AreEqual(t.FullName, "System.Reflection.RuntimeMethodInfo[]");
+ Assert.AreEqual("RuntimeMethodInfo[]", t.Name);
+ Assert.AreEqual("System.Reflection.RuntimeMethodInfo[]", t.FullName);
Assert.IsInstanceOfType(t.BaseType, typeof(Array));
Assert.IsTrue(t.GetInterfaces().Length > 0);
- Assert.AreEqual(t.GetElementType().Name, "RuntimeMethodInfo");
+ Assert.AreEqual("RuntimeMethodInfo", t.GetElementType().Name);
+
+ OutputHelper.WriteLine("Testing Type members for a delegate");
// test Type members for a delegate
Delegate del = new MyDelegate(MyDelegateImpl);
t = del.GetType();
Assert.IsNotNull(t.DeclaringType);
- Assert.AreEqual(t.Name, "MyDelegate");
+ Assert.AreEqual("MyDelegate", t.Name);
Assert.IsInstanceOfType(t.BaseType, typeof(MulticastDelegate));
+ OutputHelper.WriteLine("Testing Type members for an enum");
+
// test Type members for an enum
TestEnum en = TestEnum.Item1;
t = en.GetType();
- Assert.IsInstanceOfType(t.DeclaringType, typeof(UnitTestReflectionTypeTest));
+ Assert.IsInstanceOfType(typeof(UnitTestReflectionTypeTest), t.DeclaringType);
Assert.IsTrue(t.IsEnum);
Assert.IsFalse(t.IsAbstract);
Assert.IsFalse(t.IsClass);
@@ -145,10 +164,11 @@ public void SystemReflectionType_SystemType_Test2()
Assert.IsTrue(typeof(Array).IsInstanceOfType(blah));
Assert.IsTrue(typeof(TestStruct[]).IsArray);
+ Assert.IsTrue(typeof(G[]).IsArray);
Assert.IsFalse(typeof(Array).IsValueType);
Assert.IsTrue(typeof(TestStruct).IsValueType);
Assert.IsTrue(typeof(Type).IsSubclassOf(typeof(MemberInfo)));
- Assert.AreEqual(typeof(Type).GetInterfaces()[0].Name, "IReflect");
+ Assert.AreEqual("IReflect", typeof(Type).GetInterfaces()[0].Name);
Assert.IsTrue(typeof(MyDelegate).IsInstanceOfType(new MyDelegate(MyDelegateImpl)));
// Get known type from assembly qualified type name Culture and PublicKeyToken are used by debugger to identify types
diff --git a/Tests/NFUnitTestTypes/NFUnitTestTypes.nfproj b/Tests/NFUnitTestTypes/NFUnitTestTypes.nfproj
index 7ede25bb..f6f14b4a 100644
--- a/Tests/NFUnitTestTypes/NFUnitTestTypes.nfproj
+++ b/Tests/NFUnitTestTypes/NFUnitTestTypes.nfproj
@@ -24,7 +24,9 @@
+
+
@@ -47,4 +49,4 @@
-
+
\ No newline at end of file
diff --git a/Tests/NFUnitTestTypes/UnitTestGuid.cs b/Tests/NFUnitTestTypes/UnitTestGuid.cs
new file mode 100644
index 00000000..886207cb
--- /dev/null
+++ b/Tests/NFUnitTestTypes/UnitTestGuid.cs
@@ -0,0 +1,140 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using nanoFramework.TestFramework;
+
+namespace NFUnitTestTypes
+{
+ [TestClass]
+ class UnitTestGuid
+ {
+ [TestMethod]
+ public void Guid_Compare_To_Empty()
+ {
+ var empty = Guid.Empty;
+ var notEmpty = Guid.NewGuid();
+ Assert.IsFalse(empty == notEmpty);
+ }
+
+ [TestMethod]
+ public void Guid_Empty_IsAllZeros()
+ {
+ var empty = Guid.Empty;
+ var bytes = empty.ToByteArray();
+ foreach (var b in bytes)
+ {
+ Assert.AreEqual((byte)0, b);
+ }
+ }
+
+ [TestMethod]
+ public void Guid_Constructor_ByteArray_RoundTrip()
+ {
+ var original = Guid.NewGuid();
+ var bytes = original.ToByteArray();
+ var roundTrip = new Guid(bytes);
+ Assert.AreEqual(original, roundTrip);
+ }
+
+ [TestMethod]
+ public void Guid_Equals_And_Operator()
+ {
+ var g1 = Guid.NewGuid();
+ var g2 = new Guid(g1.ToByteArray());
+ Assert.IsTrue(g1.Equals(g2));
+ Assert.IsTrue(g1 == g2);
+ Assert.IsFalse(g1 != g2);
+ }
+
+ [TestMethod]
+ public void Guid_NotEquals_And_Operator()
+ {
+ var g1 = Guid.NewGuid();
+ var g2 = Guid.NewGuid();
+ Assert.IsFalse(g1.Equals(g2));
+ Assert.IsFalse(g1 == g2);
+ Assert.IsTrue(g1 != g2);
+ }
+
+ [TestMethod]
+ public void Guid_ToString_And_Parse()
+ {
+ var g1 = Guid.NewGuid();
+ var str = g1.ToString();
+ var g2 = new Guid(str);
+ Assert.AreEqual(g1, g2);
+ }
+
+ [TestMethod]
+ public void Guid_GetHashCode_Consistent()
+ {
+ var g1 = Guid.NewGuid();
+ var g2 = new Guid(g1.ToByteArray());
+ Assert.AreEqual(g1.GetHashCode(), g2.GetHashCode());
+ }
+
+ [TestMethod]
+ public void Guid_CompareTo_Object_And_Self()
+ {
+ var g1 = Guid.NewGuid();
+ var g2 = new Guid(g1.ToByteArray());
+ Assert.AreEqual(0, g1.CompareTo(g2));
+ Assert.AreEqual(0, g1.CompareTo((object)g2));
+ Assert.AreEqual(1, g1.CompareTo(null));
+ }
+
+ [TestMethod]
+ public void Guid_CompareTo_InvalidType_Throws()
+ {
+ var g1 = Guid.NewGuid();
+ Assert.ThrowsException(typeof(ArgumentException), () =>
+ {
+ g1.CompareTo("not a guid");
+ });
+ }
+
+ [TestMethod]
+ public void Guid_TryParseGuidWithDashes_Valid()
+ {
+ var g1 = Guid.NewGuid();
+ var str = g1.ToString();
+ bool parsed = Guid.TryParse(str, out var g2);
+ Assert.IsTrue(parsed);
+ Assert.AreEqual(g1, g2);
+ }
+
+ [TestMethod]
+ public void Guid_TryParseGuidWithDashes_Invalid()
+ {
+ bool parsed = Guid.TryParse("invalid-guid", out var g2);
+ Assert.IsFalse(parsed);
+ Assert.AreEqual(Guid.Empty, g2);
+ }
+
+ [TestMethod]
+ public void Guid_Constructor_String_WithBraces()
+ {
+ var g1 = Guid.NewGuid();
+ var str = "{" + g1.ToString() + "}";
+ var g2 = new Guid(str);
+ Assert.AreEqual(g1, g2);
+ }
+
+ [TestMethod]
+ public void Guid_Constructor_String_Invalid_Throws()
+ {
+ Assert.ThrowsException(typeof(ArgumentException), () =>
+ {
+ var g = new Guid("invalid-guid");
+ });
+ }
+
+ [TestMethod]
+ public void Guid_GetHashCode_Empty()
+ {
+ var empty = Guid.Empty;
+ Assert.AreEqual(0, empty.GetHashCode());
+ }
+ }
+}
diff --git a/Tests/NFUnitTestTypes/UnitTestsReadOnlySpanByte.cs b/Tests/NFUnitTestTypes/UnitTestsReadOnlySpanByte.cs
new file mode 100644
index 00000000..01266551
--- /dev/null
+++ b/Tests/NFUnitTestTypes/UnitTestsReadOnlySpanByte.cs
@@ -0,0 +1,978 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using nanoFramework.TestFramework;
+
+namespace NFUnitTestTypes
+{
+ [TestClass]
+ public class UnitTestsReadOnlySpanByte
+ {
+ [TestMethod]
+ public void EmptySpanTests()
+ {
+ // Empty span using default constructor
+ ReadOnlySpan span = new ReadOnlySpan();
+
+ Assert.AreEqual(0, span.Length, "Empty ReadOnlySpan should have length of 0");
+ Assert.IsTrue(span.IsEmpty, "Empty ReadOnlySpan should be IsEmpty");
+
+ // Empty span from null array
+ span = new ReadOnlySpan(null);
+
+ Assert.AreEqual(0, span.Length, "ReadOnlySpan from null should have length of 0");
+ Assert.IsTrue(span.IsEmpty, "ReadOnlySpan from null should be IsEmpty");
+
+ // Empty span using Empty property
+ span = ReadOnlySpan.Empty;
+
+ Assert.AreEqual(0, span.Length, "Empty ReadOnlySpan should have length of 0");
+ Assert.IsTrue(span.IsEmpty, "Empty ReadOnlySpan should be IsEmpty");
+ }
+
+ [TestMethod]
+ public void RaisingExceptionsOfAllKindsTests()
+ {
+ // Should raise an exception on creation
+ Assert.ThrowsException(typeof(ArgumentOutOfRangeException), () => { ReadOnlySpan span = new ReadOnlySpan(null, 1, 2); }, "ArgumentOutOfRangeException when array is null, start is 1 and length is 2");
+ Assert.ThrowsException(typeof(ArgumentOutOfRangeException), () => { ReadOnlySpan span = new ReadOnlySpan(new byte[1], 1, 2); }, "ArgumentOutOfRangeException when array is new byte[1], start is 1 and length is 2");
+ Assert.ThrowsException(typeof(ArgumentOutOfRangeException), () => { ReadOnlySpan span = new ReadOnlySpan(new byte[1], 0, 2); }, "ArgumentOutOfRangeException when array is new byte[1], start is 0 and length is 2");
+ Assert.ThrowsException(typeof(ArgumentOutOfRangeException), () => { ReadOnlySpan span = new ReadOnlySpan(new byte[1], 2, 0); }, "ArgumentOutOfRangeException when array is new byte[1], start is 2 and length is 0");
+
+ // Exception on index access
+ byte[] array = new byte[16] { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F };
+ Assert.ThrowsException(typeof(IndexOutOfRangeException), () =>
+ {
+ ReadOnlySpan span = new ReadOnlySpan(array);
+ _ = span[span.Length];
+ }, "IndexOutOfRangeException when accessing index equal to Length");
+
+ Assert.ThrowsException(typeof(IndexOutOfRangeException), () =>
+ {
+ ReadOnlySpan span = new ReadOnlySpan(array);
+ _ = span[-1];
+ }, "IndexOutOfRangeException when accessing negative index");
+
+ Assert.ThrowsException(typeof(IndexOutOfRangeException), () =>
+ {
+ ReadOnlySpan span = new ReadOnlySpan(array);
+ _ = span[span.Length + 1];
+ }, "IndexOutOfRangeException when accessing index beyond Length");
+
+ // CopyTo with too small destination
+ Assert.ThrowsException(typeof(ArgumentException), () =>
+ {
+ ReadOnlySpan span = new ReadOnlySpan(array);
+ Span destination = new Span(new byte[span.Length - 1]);
+
+ span.CopyTo(destination);
+ });
+
+ // Slicing arguments
+ Assert.ThrowsException(typeof(ArgumentOutOfRangeException), () =>
+ {
+ ReadOnlySpan span = new ReadOnlySpan(array);
+ _ = span.Slice(span.Length + 1);
+ });
+
+ Assert.ThrowsException(typeof(ArgumentOutOfRangeException), () =>
+ {
+ ReadOnlySpan span = new ReadOnlySpan(array);
+ _ = span.Slice(1, span.Length);
+ });
+
+ Assert.ThrowsException(typeof(ArgumentOutOfRangeException), () =>
+ {
+ ReadOnlySpan span = new ReadOnlySpan(array);
+ _ = span.Slice(-1, span.Length);
+ });
+
+ Assert.ThrowsException(typeof(ArgumentOutOfRangeException), () =>
+ {
+ ReadOnlySpan span = new ReadOnlySpan(array);
+ _ = span.Slice(1, -1);
+ });
+ }
+
+ [TestMethod]
+ public void IndexOutOfRangeExceptionTests()
+ {
+ // Comprehensive tests for IndexOutOfRangeException in the indexer
+ byte[] array = new byte[10] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
+ ReadOnlySpan span = new ReadOnlySpan(array);
+
+ // Test reading at exact boundary
+ Assert.ThrowsException(typeof(IndexOutOfRangeException), () =>
+ {
+ _ = GetReadOnlySpanElement(array, 10);
+ }, "Should throw IndexOutOfRangeException at index equal to Length");
+
+ // Test reading beyond boundary
+ Assert.ThrowsException(typeof(IndexOutOfRangeException), () =>
+ {
+ _ = GetReadOnlySpanElement(array, 100);
+ }, "Should throw IndexOutOfRangeException at large index");
+
+ // Test negative index
+ Assert.ThrowsException(typeof(IndexOutOfRangeException), () =>
+ {
+ _ = GetReadOnlySpanElement(array, -1);
+ }, "Should throw IndexOutOfRangeException at negative index");
+
+ Assert.ThrowsException(typeof(IndexOutOfRangeException), () =>
+ {
+ _ = GetReadOnlySpanElement(array, -100);
+ }, "Should throw IndexOutOfRangeException at large negative index");
+
+ // Valid access should not throw
+ byte value = span[0];
+ Assert.AreEqual((byte)0, value, "Valid index 0 should return correct value");
+
+ value = span[9];
+ Assert.AreEqual((byte)9, value, "Valid index 9 should return correct value");
+
+ value = span[5];
+ Assert.AreEqual((byte)5, value, "Valid index 5 should return correct value");
+ }
+
+ // Helper methods to avoid capturing ref struct in lambdas
+ private static byte GetReadOnlySpanElement(byte[] array, int index)
+ {
+ ReadOnlySpan span = new ReadOnlySpan(array);
+ return span[index];
+ }
+
+ private static byte GetPartialReadOnlySpanElement(byte[] array, int start, int length, int index)
+ {
+ ReadOnlySpan span = new ReadOnlySpan(array, start, length);
+ return span[index];
+ }
+
+ private static byte GetEmptyReadOnlySpanElement(int index)
+ {
+ ReadOnlySpan span = ReadOnlySpan.Empty;
+ return span[index];
+ }
+
+ private static byte GetNullReadOnlySpanElement(int index)
+ {
+ ReadOnlySpan span = new ReadOnlySpan(null);
+ return span[index];
+ }
+
+ private static byte GetSlicedReadOnlySpanElement(byte[] array, int sliceStart, int sliceLength, int index)
+ {
+ ReadOnlySpan span = new ReadOnlySpan(array);
+ ReadOnlySpan sliced = span.Slice(sliceStart, sliceLength);
+ return sliced[index];
+ }
+
+ private static byte GetStackAllocReadOnlySpanElement(int spanLength, int index)
+ {
+ ReadOnlySpan span = spanLength == 8
+ ? stackalloc byte[8] { 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80 }
+ : stackalloc byte[0];
+ return span[index];
+ }
+
+ [TestMethod]
+ public void PartialSpanIndexOutOfRangeExceptionTests()
+ {
+ // Test IndexOutOfRangeException with partial ReadOnlySpans
+ byte[] array = new byte[20];
+ ReadOnlySpan span = new ReadOnlySpan(array, 5, 10);
+
+ // Should have length 10
+ Assert.AreEqual(10, span.Length, "Partial ReadOnlySpan should have length 10");
+
+ // Valid access
+ byte value = span[0];
+ value = span[9];
+
+ // Invalid access - at boundary
+ Assert.ThrowsException(typeof(IndexOutOfRangeException), () =>
+ {
+ _ = GetPartialReadOnlySpanElement(array, 5, 10, 10);
+ }, "Should throw IndexOutOfRangeException at index equal to partial span Length");
+
+ // Invalid access - negative index
+ Assert.ThrowsException(typeof(IndexOutOfRangeException), () =>
+ {
+ _ = GetPartialReadOnlySpanElement(array, 5, 10, -1);
+ }, "Should throw IndexOutOfRangeException at negative index in partial span");
+
+ // Invalid access - beyond boundary
+ Assert.ThrowsException(typeof(IndexOutOfRangeException), () =>
+ {
+ _ = GetPartialReadOnlySpanElement(array, 5, 10, 15);
+ }, "Should throw IndexOutOfRangeException at index beyond partial span Length");
+ }
+
+ [TestMethod]
+ public void EmptySpanIndexOutOfRangeExceptionTests()
+ {
+ // Test IndexOutOfRangeException with empty ReadOnlySpans
+ ReadOnlySpan emptySpan = ReadOnlySpan.Empty;
+
+ Assert.AreEqual(0, emptySpan.Length, "Empty ReadOnlySpan should have length 0");
+
+ // Any index access should throw
+ Assert.ThrowsException(typeof(IndexOutOfRangeException), () =>
+ {
+ _ = GetEmptyReadOnlySpanElement(0);
+ }, "Should throw IndexOutOfRangeException on empty span at index 0");
+
+ Assert.ThrowsException(typeof(IndexOutOfRangeException), () =>
+ {
+ _ = GetEmptyReadOnlySpanElement(-1);
+ }, "Should throw IndexOutOfRangeException on empty span at negative index");
+
+ Assert.ThrowsException(typeof(IndexOutOfRangeException), () =>
+ {
+ _ = GetEmptyReadOnlySpanElement(1);
+ }, "Should throw IndexOutOfRangeException on empty span at index 1");
+ }
+
+ [TestMethod]
+ public void NullArrayIndexOutOfRangeExceptionTests()
+ {
+ // Test IndexOutOfRangeException with null-based ReadOnlySpan
+ ReadOnlySpan nullSpan = new ReadOnlySpan(null);
+
+ Assert.AreEqual(0, nullSpan.Length, "ReadOnlySpan from null should have length 0");
+ Assert.IsTrue(nullSpan.IsEmpty, "ReadOnlySpan from null should be empty");
+
+ // Any index access should throw
+ Assert.ThrowsException(typeof(IndexOutOfRangeException), () =>
+ {
+ _ = GetNullReadOnlySpanElement(0);
+ }, "Should throw IndexOutOfRangeException when accessing null-based span at index 0");
+
+ Assert.ThrowsException(typeof(IndexOutOfRangeException), () =>
+ {
+ _ = GetNullReadOnlySpanElement(-1);
+ }, "Should throw IndexOutOfRangeException when accessing null-based span at negative index");
+ }
+
+ [TestMethod]
+ public void SlicedSpanIndexOutOfRangeExceptionTests()
+ {
+ // Test IndexOutOfRangeException with sliced ReadOnlySpans
+ byte[] array = new byte[16] { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F };
+ ReadOnlySpan span = new ReadOnlySpan(array);
+ ReadOnlySpan sliced = span.Slice(4, 8);
+
+ // Should have length 8
+ Assert.AreEqual(8, sliced.Length, "Sliced ReadOnlySpan should have length 8");
+
+ // Valid access
+ byte value = sliced[0];
+ Assert.AreEqual((byte)0x04, value, "First element of slice should be 0x04");
+
+ value = sliced[7];
+ Assert.AreEqual((byte)0x0B, value, "Last element of slice should be 0x0B");
+
+ // Invalid access at boundary
+ Assert.ThrowsException(typeof(IndexOutOfRangeException), () =>
+ {
+ _ = GetSlicedReadOnlySpanElement(array, 4, 8, 8);
+ }, "Should throw IndexOutOfRangeException at sliced span boundary");
+
+ // Invalid access beyond boundary
+ Assert.ThrowsException(typeof(IndexOutOfRangeException), () =>
+ {
+ _ = GetSlicedReadOnlySpanElement(array, 4, 8, 10);
+ }, "Should throw IndexOutOfRangeException beyond sliced span boundary");
+
+ // Invalid negative access
+ Assert.ThrowsException(typeof(IndexOutOfRangeException), () =>
+ {
+ _ = GetSlicedReadOnlySpanElement(array, 4, 8, -1);
+ }, "Should throw IndexOutOfRangeException at negative index in sliced span");
+ }
+
+ [TestMethod]
+ public void StackAllocIndexOutOfRangeExceptionTests()
+ {
+ // Test IndexOutOfRangeException with stack-allocated ReadOnlySpans
+ ReadOnlySpan span = stackalloc byte[8] { 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80 };
+
+ Assert.AreEqual(8, span.Length, "Stack-allocated ReadOnlySpan should have length 8");
+
+ // Valid access
+ byte value = span[0];
+ Assert.AreEqual((byte)0x10, value, "First element should be 0x10");
+
+ value = span[7];
+ Assert.AreEqual((byte)0x80, value, "Last element should be 0x80");
+
+ // Invalid access at boundary
+ Assert.ThrowsException(typeof(IndexOutOfRangeException), () =>
+ {
+ _ = GetStackAllocReadOnlySpanElement(8, 8);
+ }, "Should throw IndexOutOfRangeException at stack-allocated span boundary");
+
+ // Invalid negative access
+ Assert.ThrowsException(typeof(IndexOutOfRangeException), () =>
+ {
+ _ = GetStackAllocReadOnlySpanElement(8, -1);
+ }, "Should throw IndexOutOfRangeException at negative index in stack-allocated span");
+ }
+
+ [TestMethod]
+ public void ToArrayTest()
+ {
+ byte[] array = new byte[16] { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F };
+
+ ReadOnlySpan span = new ReadOnlySpan(array);
+
+ byte[] toArray = span.ToArray();
+
+ CollectionAssert.AreEqual(array, toArray, "Original array and ReadOnlySpan.ToArray should be the same");
+ }
+
+ [TestMethod]
+ public void ConstructorsOfAllKindsTests()
+ {
+ // Empty span using default constructor
+ ReadOnlySpan span = new ReadOnlySpan();
+
+ Assert.AreEqual(span.Length, 0, "Empty ReadOnlySpan should have length of 0");
+ Assert.IsTrue(span.IsEmpty, "Empty ReadOnlySpan should be IsEmpty");
+
+ // Empty span from null with zero start and length
+ span = new ReadOnlySpan(null, 0, 0);
+
+ Assert.AreEqual(span.Length, 0, "Empty ReadOnlySpan should have length of 0");
+ Assert.IsTrue(span.IsEmpty, "Empty ReadOnlySpan should be IsEmpty");
+
+ // Empty span using Empty property
+ span = ReadOnlySpan.Empty;
+
+ Assert.AreEqual(span.Length, 0, "Empty ReadOnlySpan should have length of 0");
+ Assert.IsTrue(span.IsEmpty, "Empty ReadOnlySpan should be IsEmpty");
+
+ // ReadOnlySpan from normal array
+ byte[] array = new byte[16] { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F };
+ span = new ReadOnlySpan(array);
+
+ Assert.AreEqual(span.Length, array.Length, $"ReadOnlySpan should have length of the array it takes: {array.Length}");
+ Assert.IsFalse(span.IsEmpty, "ReadOnlySpan should NOT be IsEmpty");
+
+ // ReadOnlySpan from normal array with different start and length
+ span = new ReadOnlySpan(array, 2, 8);
+
+ Assert.AreEqual(span.Length, 8, $"ReadOnlySpan should have length of 8");
+ Assert.IsFalse(span.IsEmpty, "ReadOnlySpan should NOT be IsEmpty");
+ }
+
+ [TestMethod]
+ public void SliceTests()
+ {
+ // ReadOnlySpan from normal array
+ byte[] array = new byte[16] { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F };
+ ReadOnlySpan span = new ReadOnlySpan(array);
+
+ // Slice 2 elements and check
+ ReadOnlySpan sliced = span.Slice(0, 2);
+
+ Assert.AreEqual(sliced.Length, 2, "Sliced span length must be 2");
+ Assert.AreEqual(sliced[0], (byte)0x00, "Sliced first element must be value 0");
+ Assert.AreEqual(sliced[1], (byte)0x01, "Sliced second element must be value 1");
+
+ // Slice 4 elements starting at index 2 and check
+ sliced = span.Slice(2, 4);
+
+ Assert.AreEqual(sliced.Length, 4, "Sliced span length must be 4");
+ Assert.AreEqual(sliced[0], (byte)0x02, "Sliced first element must be value 2");
+ Assert.AreEqual(sliced[1], (byte)0x03, "Sliced second element must be value 3");
+ Assert.AreEqual(sliced[2], (byte)0x04, "Sliced third element must be value 4");
+ Assert.AreEqual(sliced[3], (byte)0x05, "Sliced fourth element must be value 5");
+
+ // Slice starting at element 4 and check
+ sliced = span.Slice(4);
+
+ Assert.AreEqual(sliced.Length, 12, "Sliced span length must be 12");
+
+ for (int i = 0; i < sliced.Length; i++)
+ {
+ Assert.AreEqual(sliced[i], span[i + 4], "ReadOnlySpan value should be the same as from the original span");
+ }
+
+ // Slice of slice
+ ReadOnlySpan secondSliced = sliced.Slice(2, 4);
+
+ Assert.AreEqual(secondSliced.Length, 4, "Sliced span length must be 4");
+
+ for (int i = 0; i < secondSliced.Length; i++)
+ {
+ Assert.AreEqual(secondSliced[i], sliced[i + 2], "ReadOnlySpan value should be the same as from the original span");
+ }
+
+ // Should be an empty one
+ ReadOnlySpan empty = span.Slice(span.Length);
+
+ Assert.AreEqual(empty.Length, 0, "Slicing all the span should result in an empty span");
+ Assert.IsTrue(empty.IsEmpty, "Empty span should be empty");
+ }
+
+ [TestMethod]
+ public void GetElementsTests()
+ {
+ // ReadOnlySpan from normal array
+ byte[] array = new byte[16] { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F };
+ ReadOnlySpan span = new ReadOnlySpan(array);
+ for (int i = 0; i < span.Length; i++)
+ {
+ Assert.AreEqual(span[i], array[i], "ReadOnlySpan value should be the same as from the original array");
+ }
+
+ // Partial span
+ span = new ReadOnlySpan(array, 2, 8);
+ for (int i = 0; i < span.Length; i++)
+ {
+ Assert.AreEqual(span[i], array[i + 2], "ReadOnlySpan value should be the same as from the original array");
+ }
+ }
+
+ [TestMethod]
+ public void CopyToTests()
+ {
+ byte[] array = new byte[16] { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F };
+ ReadOnlySpan span = new ReadOnlySpan(array);
+
+ // Copy to with the exact same size
+ Span destination = new Span(new byte[span.Length]);
+ span.CopyTo(destination);
+
+ CollectionAssert.AreEqual(array, destination.ToArray(), "Original array and ReadOnlySpan.CopyTo should be the same");
+
+ // Copy to a larger span
+ destination = new Span(new byte[span.Length + 4]);
+ span.CopyTo(destination);
+
+ Assert.AreEqual(destination.Length, span.Length + 4, "Destination should maintain its original size");
+
+ // Verify that the copied elements match
+ for (int i = 0; i < span.Length; i++)
+ {
+ Assert.AreEqual(span[i], destination[i], $"Element at index {i} should match after CopyTo");
+ }
+
+ // Verify that extra elements in destination remain zero
+ for (int i = span.Length; i < destination.Length; i++)
+ {
+ Assert.AreEqual((byte)0, destination[i], $"Element at index {i} beyond copied range should remain zero");
+ }
+ }
+
+ [TestMethod]
+ public void CopyToEmptySpanTests()
+ {
+ // Empty ReadOnlySpan CopyTo empty Span
+ ReadOnlySpan emptySource = ReadOnlySpan.Empty;
+ Span emptyDestination = Span.Empty;
+
+ emptySource.CopyTo(emptyDestination);
+
+ Assert.AreEqual(0, emptyDestination.Length, "Empty destination should remain empty");
+
+ // Empty ReadOnlySpan CopyTo larger Span
+ Span largerDestination = new Span(new byte[5]);
+ emptySource.CopyTo(largerDestination);
+
+ Assert.AreEqual(5, largerDestination.Length, "Destination should maintain its size");
+
+ // Verify all elements remain zero
+ for (int i = 0; i < largerDestination.Length; i++)
+ {
+ Assert.AreEqual((byte)0, largerDestination[i], $"Element at index {i} should be zero");
+ }
+ }
+
+ [TestMethod]
+ public void CopyToPartialSpanTests()
+ {
+ byte[] array = new byte[16] { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F };
+
+ // Create a partial ReadOnlySpan
+ ReadOnlySpan span = new ReadOnlySpan(array, 4, 8);
+
+ // Copy to a span of exact size
+ Span destination = new Span(new byte[8]);
+ span.CopyTo(destination);
+
+ // Verify copied elements
+ for (int i = 0; i < span.Length; i++)
+ {
+ Assert.AreEqual(span[i], destination[i], $"Element at index {i} should match");
+ Assert.AreEqual(array[i + 4], destination[i], $"Element should match original array at offset");
+ }
+ }
+
+ [TestMethod]
+ public void CopyToSlicedSpanTests()
+ {
+ byte[] array = new byte[16] { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F };
+ ReadOnlySpan span = new ReadOnlySpan(array);
+
+ // Slice the ReadOnlySpan and then copy
+ ReadOnlySpan sliced = span.Slice(2, 6);
+ Span destination = new Span(new byte[6]);
+
+ sliced.CopyTo(destination);
+
+ // Verify copied elements match the sliced portion
+ for (int i = 0; i < sliced.Length; i++)
+ {
+ Assert.AreEqual(sliced[i], destination[i], $"Element at index {i} should match sliced span");
+ Assert.AreEqual(array[i + 2], destination[i], $"Element should match original array at offset 2");
+ }
+ }
+
+ [TestMethod]
+ public void CopyToWithDestinationSliceTests()
+ {
+ byte[] array = new byte[8] { 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80 };
+ ReadOnlySpan source = new ReadOnlySpan(array);
+
+ // Create a larger destination and copy to a slice of it
+ byte[] destArray = new byte[12];
+ Span destination = new Span(destArray);
+ Span destSlice = destination.Slice(2, 8);
+
+ source.CopyTo(destSlice);
+
+ // Verify first 2 elements remain zero
+ Assert.AreEqual((byte)0, destArray[0], "Element before copied range should be zero");
+ Assert.AreEqual((byte)0, destArray[1], "Element before copied range should be zero");
+
+ // Verify copied elements
+ for (int i = 0; i < source.Length; i++)
+ {
+ Assert.AreEqual(source[i], destArray[i + 2], $"Copied element at index {i + 2} should match source");
+ }
+
+ // Verify last elements remain zero
+ Assert.AreEqual((byte)0, destArray[10], "Element after copied range should be zero");
+ Assert.AreEqual((byte)0, destArray[11], "Element after copied range should be zero");
+ }
+
+ [TestMethod]
+ public void CopyToWithNullableArrayTests()
+ {
+ // Test that CopyTo works correctly when the internal _array field is nullable
+ // Create ReadOnlySpan from null (tests nullable _array field)
+ ReadOnlySpan nullSpan = new ReadOnlySpan(null);
+ Span destination = new Span(new byte[5]);
+
+ // CopyTo from empty span should not throw
+ nullSpan.CopyTo(destination);
+
+ // Verify destination remains unchanged (all zeros)
+ for (int i = 0; i < destination.Length; i++)
+ {
+ Assert.AreEqual((byte)0, destination[i], $"Element at index {i} should be zero");
+ }
+ }
+
+ [TestMethod]
+ public void StackAllocReadOnlySpanTests()
+ {
+ // Create a ReadOnlySpan from stack-allocated memory
+ ReadOnlySpan span = stackalloc byte[16];
+
+ Assert.AreEqual(16, span.Length, "Stack-allocated ReadOnlySpan should have length of 16");
+ Assert.IsFalse(span.IsEmpty, "Stack-allocated ReadOnlySpan should NOT be IsEmpty");
+
+ // Verify all elements are initialized to zero
+ for (int i = 0; i < span.Length; i++)
+ {
+ Assert.AreEqual((byte)0, span[i], "Stack-allocated ReadOnlySpan elements should be initialized to 0");
+ }
+ }
+
+ [TestMethod]
+ public void StackAllocReadOnlySpanWithInitializerTests()
+ {
+ // Create a ReadOnlySpan from stack-allocated memory with initializer
+ ReadOnlySpan span = stackalloc byte[8] { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 };
+
+ Assert.AreEqual(8, span.Length, "Stack-allocated ReadOnlySpan with initializer should have length of 8");
+ Assert.IsFalse(span.IsEmpty, "Stack-allocated ReadOnlySpan should NOT be IsEmpty");
+
+ // Verify the initialized values
+ for (int i = 0; i < span.Length; i++)
+ {
+ Assert.AreEqual((byte)(i + 1), span[i], $"Stack-allocated ReadOnlySpan element at index {i} should be {i + 1}");
+ }
+ }
+
+ [TestMethod]
+ public void StackAllocReadOnlySpanSliceTests()
+ {
+ // Create a ReadOnlySpan from stack-allocated memory
+ ReadOnlySpan span = stackalloc byte[16] { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F };
+
+ // Slice the stack-allocated span
+ ReadOnlySpan sliced = span.Slice(4, 8);
+
+ Assert.AreEqual(8, sliced.Length, "Sliced stack-allocated ReadOnlySpan should have length of 8");
+
+ for (int i = 0; i < sliced.Length; i++)
+ {
+ Assert.AreEqual((byte)(i + 4), sliced[i], $"Sliced element at index {i} should be {i + 4}");
+ }
+ }
+
+ [TestMethod]
+ public void StackAllocReadOnlySpanToArrayTests()
+ {
+ // Create a ReadOnlySpan from stack-allocated memory
+ ReadOnlySpan span = stackalloc byte[6] { 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF };
+
+ // Convert to array
+ byte[] array = span.ToArray();
+
+ Assert.AreEqual(6, array.Length, "ToArray should create an array with the same length");
+
+ byte[] expected = new byte[] { 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF };
+ CollectionAssert.AreEqual(expected, array, "Stack-allocated ReadOnlySpan ToArray should match expected values");
+ }
+
+ [TestMethod]
+ public void StackAllocReadOnlySpanCopyToTests()
+ {
+ // Create a ReadOnlySpan from stack-allocated memory
+ ReadOnlySpan source = stackalloc byte[8] { 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88 };
+
+ // Create a destination Span
+ Span destination = new Span(new byte[8]);
+
+ // Copy from stack-allocated ReadOnlySpan to heap-allocated Span
+ source.CopyTo(destination);
+
+ // Verify the copy
+ for (int i = 0; i < source.Length; i++)
+ {
+ Assert.AreEqual(source[i], destination[i], $"Element at index {i} should match after CopyTo from stack-allocated span");
+ }
+ }
+
+ [TestMethod]
+ public void StackAllocEmptyReadOnlySpanTests()
+ {
+ // Create an empty stack-allocated ReadOnlySpan
+ ReadOnlySpan span = stackalloc byte[0];
+
+ Assert.AreEqual(0, span.Length, "Empty stack-allocated ReadOnlySpan should have length of 0");
+ Assert.IsTrue(span.IsEmpty, "Empty stack-allocated ReadOnlySpan should be IsEmpty");
+ }
+
+ [TestMethod]
+ public void ImplicitConversionFromArrayTests()
+ {
+ // Test implicit conversion from array to ReadOnlySpan
+ byte[] array = new byte[8] { 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80 };
+
+ ReadOnlySpan span = array; // Implicit conversion
+
+ Assert.AreEqual(array.Length, span.Length, "Implicitly converted ReadOnlySpan should have same length as array");
+
+ for (int i = 0; i < array.Length; i++)
+ {
+ Assert.AreEqual(array[i], span[i], $"Element at index {i} should match");
+ }
+ }
+
+ [TestMethod]
+ public void EqualityOperatorTests()
+ {
+ byte[] array1 = new byte[4] { 0x01, 0x02, 0x03, 0x04 };
+ byte[] array2 = new byte[4] { 0x01, 0x02, 0x03, 0x04 };
+ byte[] array3 = new byte[4] { 0x01, 0x02, 0x03, 0x05 };
+
+ ReadOnlySpan span1 = new ReadOnlySpan(array1);
+ ReadOnlySpan span2 = new ReadOnlySpan(array2);
+ ReadOnlySpan span3 = new ReadOnlySpan(array3);
+
+ // Test equality with same content (content-based equality)
+ Assert.IsTrue(span1 == span2, "ReadOnlySpans with same content should be equal");
+ Assert.IsFalse(span1 != span2, "ReadOnlySpans with same content should not be not-equal");
+
+ // Test inequality with different content
+ Assert.IsTrue(span1 != span3, "ReadOnlySpans with different content should not be equal");
+ Assert.IsFalse(span1 == span3, "ReadOnlySpans with different content should not be equal");
+
+ // Test empty spans
+ ReadOnlySpan empty1 = new ReadOnlySpan();
+ ReadOnlySpan empty2 = new ReadOnlySpan();
+
+ Assert.IsTrue(empty1 == empty2, "Empty ReadOnlySpans should be equal");
+ Assert.IsFalse(empty1 != empty2, "Empty ReadOnlySpans should not be not-equal");
+ }
+
+ [TestMethod]
+ public void EqualityWithPartialSpansTests()
+ {
+ byte[] array1 = new byte[10] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
+ byte[] array2 = new byte[10] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
+
+ // Create two spans from different arrays with same content
+ ReadOnlySpan span1 = new ReadOnlySpan(array1, 2, 5);
+ ReadOnlySpan span2 = new ReadOnlySpan(array2, 2, 5);
+
+ // They should be equal (content-based equality)
+ Assert.IsTrue(span1 == span2, "Partial spans with same content should be equal");
+ Assert.IsFalse(span1 != span2, "Partial spans with same content should not be not-equal");
+
+ // Create span from different array with different content
+ byte[] array3 = new byte[10] { 0, 1, 2, 3, 99, 5, 6, 7, 8, 9 };
+ ReadOnlySpan span3 = new ReadOnlySpan(array3, 2, 5);
+ Assert.IsFalse(span1 == span3, "Partial spans with different content should not be equal");
+ Assert.IsTrue(span1 != span3, "Partial spans with different content should be not-equal");
+
+ // Create span with different length
+ ReadOnlySpan span4 = new ReadOnlySpan(array1, 2, 4);
+ Assert.IsFalse(span1 == span4, "Partial spans with different length should not be equal");
+ Assert.IsTrue(span1 != span4, "Partial spans with different length should be not-equal");
+ }
+
+ [TestMethod]
+ public void EqualityWithSlicedSpansTests()
+ {
+ byte[] array1 = new byte[10] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
+ byte[] array2 = new byte[10] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
+
+ ReadOnlySpan originalSpan1 = new ReadOnlySpan(array1);
+ ReadOnlySpan originalSpan2 = new ReadOnlySpan(array2);
+
+ // Create two slices from different arrays with same content
+ ReadOnlySpan slice1 = originalSpan1.Slice(2, 5);
+ ReadOnlySpan slice2 = originalSpan2.Slice(2, 5);
+
+ // They should be equal (content-based equality)
+ Assert.IsTrue(slice1 == slice2, "Slices with same content should be equal");
+ Assert.IsFalse(slice1 != slice2, "Slices with same content should not be not-equal");
+
+ // Create a slice with different content
+ byte[] array3 = new byte[10] { 0, 1, 2, 99, 4, 5, 6, 7, 8, 9 };
+ ReadOnlySpan originalSpan3 = new ReadOnlySpan(array3);
+ ReadOnlySpan slice3 = originalSpan3.Slice(2, 5);
+ Assert.IsFalse(slice1 == slice3, "Slices with different content should not be equal");
+
+ // Create a slice with different length
+ ReadOnlySpan slice4 = originalSpan1.Slice(2, 4);
+ Assert.IsFalse(slice1 == slice4, "Slices with different length should not be equal");
+ }
+
+ [TestMethod]
+ public void EqualityWithNullArrayTests()
+ {
+ // Test equality with null-backed spans
+ ReadOnlySpan nullSpan1 = new ReadOnlySpan(null);
+ ReadOnlySpan nullSpan2 = new ReadOnlySpan(null);
+ ReadOnlySpan nullSpan3 = new ReadOnlySpan(null, 0, 0);
+
+ // All null-backed spans should be equal (both are empty)
+ Assert.IsTrue(nullSpan1 == nullSpan2, "Null-backed spans should be equal");
+ Assert.IsTrue(nullSpan1 == nullSpan3, "Null-backed spans with explicit 0,0 should be equal");
+ Assert.IsFalse(nullSpan1 != nullSpan2, "Null-backed spans should not be not-equal");
+
+ // Compare with Empty
+ ReadOnlySpan emptySpan = ReadOnlySpan.Empty;
+ Assert.IsTrue(nullSpan1 == emptySpan, "Null-backed span should equal Empty span");
+ }
+
+ [TestMethod]
+ public void EqualityContentBasedTests()
+ {
+ // Create two arrays with identical content but different references
+ byte[] array1 = new byte[5] { 10, 20, 30, 40, 50 };
+ byte[] array2 = new byte[5] { 10, 20, 30, 40, 50 };
+
+ ReadOnlySpan span1 = new ReadOnlySpan(array1);
+ ReadOnlySpan span2 = new ReadOnlySpan(array2);
+
+ // ReadOnlySpans with same content should be equal (content-based equality)
+ Assert.IsTrue(span1 == span2, "ReadOnlySpans with same content should be equal");
+ Assert.IsFalse(span1 != span2, "ReadOnlySpans with same content should not be not-equal");
+
+ // Now create another span from array1
+ ReadOnlySpan span1Copy = new ReadOnlySpan(array1);
+ Assert.IsTrue(span1 == span1Copy, "ReadOnlySpans from same array should be equal");
+
+ // Modify array1 content
+ array1[2] = 99;
+
+ // Spans should no longer be equal because content changed
+ Assert.IsFalse(span1 == span2, "ReadOnlySpans should not be equal after content changes");
+ Assert.IsTrue(span1 != span2, "ReadOnlySpans should be not-equal after content changes");
+
+ // But span1 and span1Copy still reference the same array so should see same content
+ Assert.IsTrue(span1 == span1Copy, "ReadOnlySpans from same array should remain equal");
+ }
+
+ [TestMethod]
+ public void EqualityWithStackAllocatedSpansTests()
+ {
+ // Stack-allocated spans with same content should be equal
+ ReadOnlySpan stackSpan1 = stackalloc byte[5] { 1, 2, 3, 4, 5 };
+ ReadOnlySpan stackSpan2 = stackalloc byte[5] { 1, 2, 3, 4, 5 };
+
+ // Stack-allocated spans with identical content should be equal (content-based)
+ Assert.IsTrue(stackSpan1 == stackSpan2, "Stack-allocated spans with same content should be equal");
+ Assert.IsFalse(stackSpan1 != stackSpan2, "Stack-allocated spans with same content should not be not-equal");
+
+ // Stack-allocated spans with different content should not be equal
+ ReadOnlySpan stackSpan3 = stackalloc byte[5] { 1, 2, 99, 4, 5 };
+ Assert.IsFalse(stackSpan1 == stackSpan3, "Stack-allocated spans with different content should not be equal");
+ Assert.IsTrue(stackSpan1 != stackSpan3, "Stack-allocated spans with different content should be not-equal");
+
+ // Create a "copy" reference to the same stack span
+ ReadOnlySpan stackSpan1Copy = stackSpan1;
+ Assert.IsTrue(stackSpan1 == stackSpan1Copy, "Same stack-allocated span should equal itself");
+ }
+
+ [TestMethod]
+ public void EqualityTransitivityTests()
+ {
+ byte[] array1 = new byte[5] { 1, 2, 3, 4, 5 };
+ byte[] array2 = new byte[5] { 1, 2, 3, 4, 5 };
+ byte[] array3 = new byte[5] { 1, 2, 3, 4, 5 };
+
+ ReadOnlySpan span1 = new ReadOnlySpan(array1);
+ ReadOnlySpan span2 = new ReadOnlySpan(array2);
+ ReadOnlySpan span3 = new ReadOnlySpan(array3);
+
+ // Test transitivity: if span1 == span2 and span2 == span3, then span1 == span3
+ Assert.IsTrue(span1 == span2, "span1 should equal span2");
+ Assert.IsTrue(span2 == span3, "span2 should equal span3");
+ Assert.IsTrue(span1 == span3, "span1 should equal span3 (transitivity)");
+ }
+
+ [TestMethod]
+ public void EqualityReflexivityTests()
+ {
+ byte[] array = new byte[5] { 1, 2, 3, 4, 5 };
+ ReadOnlySpan span = new ReadOnlySpan(array);
+
+ // Test reflexivity: span should equal itself
+ Assert.IsTrue(span == span, "ReadOnlySpan should equal itself (reflexivity)");
+ Assert.IsFalse(span != span, "ReadOnlySpan should not be not-equal to itself");
+
+ // Test with empty span
+ ReadOnlySpan emptySpan = ReadOnlySpan.Empty;
+ Assert.IsTrue(emptySpan == emptySpan, "Empty ReadOnlySpan should equal itself");
+ }
+
+ [TestMethod]
+ public void EqualitySymmetryTests()
+ {
+ byte[] array1 = new byte[5] { 1, 2, 3, 4, 5 };
+ byte[] array2 = new byte[5] { 1, 2, 3, 4, 5 };
+ byte[] array3 = new byte[5] { 1, 2, 99, 4, 5 };
+
+ ReadOnlySpan span1 = new ReadOnlySpan(array1);
+ ReadOnlySpan span2 = new ReadOnlySpan(array2);
+ ReadOnlySpan span3 = new ReadOnlySpan(array3);
+
+ // Test symmetry: if span1 == span2, then span2 == span1
+ Assert.IsTrue(span1 == span2, "span1 should equal span2");
+ Assert.IsTrue(span2 == span1, "span2 should equal span1 (symmetry)");
+
+ // Test symmetry with inequality
+ Assert.IsFalse(span1 == span3, "span1 should not equal span3");
+ Assert.IsFalse(span3 == span1, "span3 should not equal span1 (symmetry)");
+ }
+
+ [TestMethod]
+ public void EqualityWithDifferentOffsetsTests()
+ {
+ byte[] array = new byte[10] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
+
+ // Create spans with overlapping but different regions
+ ReadOnlySpan span1 = new ReadOnlySpan(array, 2, 5); // [2,3,4,5,6]
+ ReadOnlySpan span2 = new ReadOnlySpan(array, 3, 5); // [3,4,5,6,7]
+
+ // They have different content so should not be equal
+ Assert.IsFalse(span1 == span2, "ReadOnlySpans with different content should not be equal");
+ Assert.IsTrue(span1 != span2, "ReadOnlySpans with different content should be not-equal");
+
+ // Create span with same content from different array
+ byte[] array2 = new byte[10] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
+ ReadOnlySpan span3 = new ReadOnlySpan(array2, 2, 5);
+ Assert.IsTrue(span1 == span3, "ReadOnlySpans with same content should be equal");
+ }
+
+ [TestMethod]
+ public void EqualityZeroLengthSpansTests()
+ {
+ byte[] array1 = new byte[10];
+ byte[] array2 = new byte[10];
+
+ // Create zero-length spans from different arrays
+ ReadOnlySpan zeroSpan1 = new ReadOnlySpan(array1, 5, 0);
+ ReadOnlySpan zeroSpan2 = new ReadOnlySpan(array2, 3, 0);
+
+ // Zero-length spans should be equal (both empty, content-based)
+ Assert.IsTrue(zeroSpan1 == zeroSpan2, "Zero-length spans should be equal");
+ Assert.IsFalse(zeroSpan1 != zeroSpan2, "Zero-length spans should not be not-equal");
+
+ // Zero-length span from same array with different offset
+ ReadOnlySpan zeroSpan3 = new ReadOnlySpan(array1, 7, 0);
+ Assert.IsTrue(zeroSpan1 == zeroSpan3, "Zero-length spans should be equal regardless of offset");
+
+ // Compare with Empty
+ ReadOnlySpan emptySpan = ReadOnlySpan.Empty;
+ Assert.IsTrue(zeroSpan1 == emptySpan, "Zero-length span should equal Empty span");
+ }
+
+ [TestMethod]
+ public void EqualityDifferentLengthsTests()
+ {
+ byte[] array1 = new byte[6] { 1, 2, 3, 4, 5, 6 };
+ byte[] array2 = new byte[4] { 1, 2, 3, 4 };
+
+ ReadOnlySpan span1 = new ReadOnlySpan(array1);
+ ReadOnlySpan span2 = new ReadOnlySpan(array2);
+
+ // Spans with different lengths should not be equal
+ Assert.IsFalse(span1 == span2, "ReadOnlySpans with different lengths should not be equal");
+ Assert.IsTrue(span1 != span2, "ReadOnlySpans with different lengths should be not-equal");
+
+ // Test with partial spans that have same initial content but different lengths
+ byte[] array3 = new byte[10] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
+ ReadOnlySpan span3 = new ReadOnlySpan(array3, 0, 4);
+ ReadOnlySpan span4 = new ReadOnlySpan(array3, 0, 6);
+
+ Assert.IsFalse(span3 == span4, "Partial spans with different lengths should not be equal");
+ }
+
+ [TestMethod]
+ public void EqualityPartialContentMatchTests()
+ {
+ // Test equality when only part of the content matches
+ byte[] array1 = new byte[5] { 1, 2, 3, 4, 5 };
+ byte[] array2 = new byte[5] { 1, 2, 99, 4, 5 };
+
+ ReadOnlySpan span1 = new ReadOnlySpan(array1);
+ ReadOnlySpan span2 = new ReadOnlySpan(array2);
+
+ // Should not be equal (one element differs)
+ Assert.IsFalse(span1 == span2, "ReadOnlySpans with partial content match should not be equal");
+ Assert.IsTrue(span1 != span2, "ReadOnlySpans with partial content match should be not-equal");
+
+ // Test with matching prefix
+ ReadOnlySpan span1Prefix = span1.Slice(0, 2);
+ ReadOnlySpan span2Prefix = span2.Slice(0, 2);
+ Assert.IsTrue(span1Prefix == span2Prefix, "ReadOnlySpans with matching prefix should be equal");
+
+ // Test with matching suffix
+ ReadOnlySpan span1Suffix = span1.Slice(3, 2);
+ ReadOnlySpan span2Suffix = span2.Slice(3, 2);
+ Assert.IsTrue(span1Suffix == span2Suffix, "ReadOnlySpans with matching suffix should be equal");
+ }
+ }
+}
diff --git a/Tests/NFUnitTestTypes/UnitTestsSpanByte.cs b/Tests/NFUnitTestTypes/UnitTestsSpanByte.cs
index e4ffde13..90fa50da 100644
--- a/Tests/NFUnitTestTypes/UnitTestsSpanByte.cs
+++ b/Tests/NFUnitTestTypes/UnitTestsSpanByte.cs
@@ -13,13 +13,15 @@ public class UnitTestsSpanByte
public void EmptySpanTests()
{
// Empty span
- SpanByte span = SpanByte.Empty;
+ Span span = Span.Empty;
// Create a destination span larger
- SpanByte destination = new byte[1];
+ Span destination = new Span(new byte[1]);
+
span.CopyTo(destination);
// Now also empty
- destination = SpanByte.Empty;
+ destination = Span.Empty;
+
span.CopyTo(destination);
}
@@ -27,56 +29,270 @@ public void EmptySpanTests()
public void RaisingExceptionsOfAllKindsTests()
{
// Should raise an exception on creation
- Assert.ThrowsException(typeof(ArgumentOutOfRangeException), () => { SpanByte span = new SpanByte(null, 1, 2); }, "ArgumentOutOfRangeException when array is null, start is 1 and length is 2");
- Assert.ThrowsException(typeof(ArgumentOutOfRangeException), () => { SpanByte span = new SpanByte(new byte[1], 1, 2); }, "ArgumentOutOfRangeException when array is new byte[1], start is 1 and length is 2");
- Assert.ThrowsException(typeof(ArgumentOutOfRangeException), () => { SpanByte span = new SpanByte(new byte[1], 0, 2); }, "ArgumentOutOfRangeException when array is new byte[1], start is 0 and length is 2");
- Assert.ThrowsException(typeof(ArgumentOutOfRangeException), () => { SpanByte span = new SpanByte(new byte[1], 2, 0); }, "ArgumentOutOfRangeException when array is new byte[1], start is 2 and length is 0");
+ Assert.ThrowsException(typeof(ArgumentOutOfRangeException), () => { Span span = new Span(null, 1, 2); }, "ArgumentOutOfRangeException when array is null, start is 1 and length is 2");
+ Assert.ThrowsException(typeof(ArgumentOutOfRangeException), () => { Span span = new Span(new byte[1], 1, 2); }, "ArgumentOutOfRangeException when array is new byte[1], start is 1 and length is 2");
+ Assert.ThrowsException(typeof(ArgumentOutOfRangeException), () => { Span span = new Span(new byte[1], 0, 2); }, "ArgumentOutOfRangeException when array is new byte[1], start is 0 and length is 2");
+ Assert.ThrowsException(typeof(ArgumentOutOfRangeException), () => { Span span = new Span(new byte[1], 2, 0); }, "ArgumentOutOfRangeException when array is new byte[1], start is 2 and length is 0");
// Exception on index access
byte[] array = new byte[16] { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F };
- Assert.ThrowsException(typeof(ArgumentOutOfRangeException), () =>
+ Assert.ThrowsException(typeof(IndexOutOfRangeException), () =>
{
- SpanByte span = new SpanByte(array);
- var data = span[span.Length];
- });
+ Span span = new Span(array);
+ _ = span[span.Length];
+ }, "IndexOutOfRangeException when accessing index equal to Length");
+
Assert.ThrowsException(typeof(IndexOutOfRangeException), () =>
{
- SpanByte span = new SpanByte(array);
- var data = span[-1];
- });
+ Span span = new Span(array);
+ _ = span[-1];
+ }, "IndexOutOfRangeException when accessing negative index");
+
+ Assert.ThrowsException(typeof(IndexOutOfRangeException), () =>
+ {
+ Span span = new Span(array);
+ _ = span[span.Length + 1];
+ }, "IndexOutOfRangeException when accessing index beyond Length");
// Copy to with too small destination
Assert.ThrowsException(typeof(ArgumentException), () =>
{
- SpanByte span = new SpanByte(array);
- SpanByte destination = new byte[span.Length - 1];
+ Span