diff --git a/X10D.Tests/X10D.Tests.csproj b/X10D.Tests/X10D.Tests.csproj
new file mode 100644
index 0000000..41dc0ed
--- /dev/null
+++ b/X10D.Tests/X10D.Tests.csproj
@@ -0,0 +1,27 @@
+
+
+
+ netcoreapp3.1
+ false
+
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/X10D.Tests/src/BooleanTests.cs b/X10D.Tests/src/BooleanTests.cs
new file mode 100644
index 0000000..d9c8c26
--- /dev/null
+++ b/X10D.Tests/src/BooleanTests.cs
@@ -0,0 +1,26 @@
+namespace X10D.Tests
+{
+ using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+ ///
+ /// Tests for .
+ ///
+ [TestClass]
+ public class BooleanTests
+ {
+ ///
+ /// Tests for .
+ ///
+ [TestMethod]
+ public void ToInt32()
+ {
+ const bool a = true;
+ const bool b = false;
+
+ Assert.IsTrue(a);
+ Assert.IsFalse(b);
+ Assert.AreEqual(1, a.ToInt32());
+ Assert.AreEqual(0, b.ToInt32());
+ }
+ }
+}
diff --git a/X10D.Tests/src/ByteTests.cs b/X10D.Tests/src/ByteTests.cs
new file mode 100644
index 0000000..6ebed35
--- /dev/null
+++ b/X10D.Tests/src/ByteTests.cs
@@ -0,0 +1,103 @@
+namespace X10D.Tests
+{
+ using System.Collections.Generic;
+ using System.Text;
+ using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+ ///
+ /// Tests for .
+ ///
+ [TestClass]
+ public class ByteTests
+ {
+ ///
+ /// Tests for .
+ ///
+ [TestMethod]
+ public void AsString()
+ {
+ byte[] a = { 0x00, 0x73, 0xc6, 0xff };
+ Assert.AreEqual("00-73-C6-FF", a.AsString());
+ }
+
+ ///
+ /// Tests for .
+ ///
+ [TestMethod]
+ public void GetInt16()
+ {
+ byte[] a = { 0xF3, 0x3F };
+ Assert.AreEqual(16371, a.GetInt16());
+ }
+
+ ///
+ /// Tests for .
+ ///
+ [TestMethod]
+ public void GetInt32()
+ {
+ byte[] a = { 0xB0, 0x0B, 0x13, 0x5F };
+ Assert.AreEqual(1595083696, a.GetInt32());
+ }
+
+ ///
+ /// Tests for .
+ ///
+ [TestMethod]
+ public void GetInt64()
+ {
+ byte[] a = { 0xB0, 0x0B, 0x13, 0x50, 0x05, 0x31, 0xB0, 0x0B };
+ Assert.AreEqual(842227029206305712L, a.GetInt64());
+ }
+
+ ///
+ /// Tests for .
+ ///
+ [TestMethod]
+ public void GetString()
+ {
+ byte[] a = { 0x48, 0xc3, 0xa9, 0x6c, 0x6c, 0x6f, 0x20, 0x57, 0x6f, 0x72, 0x6c, 0x64 };
+ Assert.AreEqual("Héllo World", a.GetString());
+ }
+
+ ///
+ /// Tests for .
+ ///
+ [TestMethod]
+ public void GetStringAscii()
+ {
+ byte[] a = { 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x57, 0x6f, 0x72, 0x6c, 0x64 };
+ Assert.AreEqual("Hello World", a.GetString(Encoding.ASCII));
+ }
+
+ ///
+ /// Tests for .
+ ///
+ [TestMethod]
+ public void GetUInt16()
+ {
+ byte[] a = { 0xF3, 0x3F };
+ Assert.AreEqual(16371, a.GetUInt16());
+ }
+
+ ///
+ /// Tests for .
+ ///
+ [TestMethod]
+ public void GetUInt32()
+ {
+ byte[] a = { 0xB0, 0x0B, 0x13, 0x5F };
+ Assert.AreEqual(1595083696U, a.GetUInt32());
+ }
+
+ ///
+ /// Tests for .
+ ///
+ [TestMethod]
+ public void GetUInt64()
+ {
+ byte[] a = { 0xB0, 0x0B, 0x13, 0x50, 0x05, 0x31, 0xB0, 0x0B };
+ Assert.AreEqual(842227029206305712UL, a.GetUInt64());
+ }
+ }
+}
diff --git a/X10D.Tests/src/CharTests.cs b/X10D.Tests/src/CharTests.cs
new file mode 100644
index 0000000..c0f328d
--- /dev/null
+++ b/X10D.Tests/src/CharTests.cs
@@ -0,0 +1,35 @@
+namespace X10D.Tests
+{
+ using System;
+ using System.Linq;
+ using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+ ///
+ /// Tests for .
+ ///
+ [TestClass]
+ public class CharTests
+ {
+ ///
+ /// Tests for .
+ ///
+ [TestMethod]
+ public void Random()
+ {
+ var set = "abcdefghijklmnopqrstuvwxyz".ToCharArray();
+ var random = set.Random(20);
+
+ Assert.IsTrue(random.All(c => Array.IndexOf(set, c) >= 0));
+ Assert.IsFalse(random.Any(c => Array.IndexOf(set, c) < -1));
+ }
+
+ ///
+ /// Tests for .
+ ///
+ [TestMethod]
+ public void Repeat()
+ {
+ Assert.AreEqual("aaaaaaaaaa", 'a'.Repeat(10));
+ }
+ }
+}
diff --git a/X10D.Tests/src/ComparableTests.cs b/X10D.Tests/src/ComparableTests.cs
new file mode 100644
index 0000000..7d27db9
--- /dev/null
+++ b/X10D.Tests/src/ComparableTests.cs
@@ -0,0 +1,22 @@
+namespace X10D.Tests
+{
+ using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+ ///
+ /// Tests for .
+ ///
+ [TestClass]
+ public class ComparableTests
+ {
+ ///
+ /// Tests for .
+ ///
+ [TestMethod]
+ public void Between()
+ {
+ Assert.IsTrue(5.Between(2, 7));
+ Assert.IsTrue(10.Between(9, 11));
+ Assert.IsFalse(100.Between(80, 99));
+ }
+ }
+}
diff --git a/X10D.Tests/src/ConvertibleTests.cs b/X10D.Tests/src/ConvertibleTests.cs
new file mode 100644
index 0000000..c366aea
--- /dev/null
+++ b/X10D.Tests/src/ConvertibleTests.cs
@@ -0,0 +1,58 @@
+namespace X10D.Tests
+{
+ using System;
+ using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+ ///
+ /// Tests for .
+ ///
+ [TestClass]
+ public class ConvertibleTests
+ {
+ ///
+ /// Tests for .
+ ///
+ [TestMethod]
+ public void To()
+ {
+ Assert.AreEqual(2, "2".To());
+ Assert.AreEqual("12.5", 12.50.To());
+ Assert.IsTrue("True".To());
+ }
+
+ ///
+ /// Tests for .
+ ///
+ [TestMethod]
+ public void ToOrDefault()
+ {
+ Assert.AreEqual(2, "2".ToOrDefault());
+ Assert.AreEqual("12.5", 12.50.ToOrDefault());
+ Assert.IsTrue("True".ToOrDefault());
+ Assert.ThrowsException(() => "Foo".ToOrDefault());
+ Assert.IsTrue("1.5".ToOrDefault(out float f));
+ Assert.AreEqual(1.5f, f);
+ }
+
+ ///
+ /// Tests for .
+ ///
+ [TestMethod]
+ public void ToOrOther()
+ {
+ Assert.AreEqual(2.0, "Foo".ToOrOther(2.0));
+ Assert.IsFalse("Foo".ToOrOther(out var d, 2.0));
+ Assert.AreEqual(2.0, d);
+ }
+
+ ///
+ /// Tests for .
+ ///
+ [TestMethod]
+ public void ToOrNull()
+ {
+ Assert.IsFalse("foo".ToOrNull(out ConvertibleTests t));
+ Assert.IsNull(t);
+ }
+ }
+}
diff --git a/X10D.Tests/src/DateTimeTests.cs b/X10D.Tests/src/DateTimeTests.cs
new file mode 100644
index 0000000..c7b6502
--- /dev/null
+++ b/X10D.Tests/src/DateTimeTests.cs
@@ -0,0 +1,107 @@
+namespace X10D.Tests
+{
+ using System;
+ using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+ ///
+ /// Tests for .
+ ///
+ [TestClass]
+ public class DateTimeTests
+ {
+ ///
+ /// Tests for .
+ ///
+ [TestMethod]
+ public void Age()
+ {
+ // no choice but to create dynamic based on today's date.
+ // age varies with time
+ DateTime now = DateTime.Now;
+ var dt = new DateTime(now.Year - 18, 1, 1);
+
+ Assert.AreEqual(18, dt.Age());
+ }
+
+ ///
+ /// Tests for .
+ ///
+ [TestMethod]
+ public void First()
+ {
+ var dt = new DateTime(2018, 6, 20);
+
+ Assert.AreEqual(4, dt.First(DayOfWeek.Monday).Day);
+ }
+
+ ///
+ /// Tests for .
+ ///
+ [TestMethod]
+ public void FirstDayOfMonth()
+ {
+ var dt = new DateTime(2018, 6, 20);
+ DateTime first = dt.FirstDayOfMonth();
+
+ Assert.AreEqual(dt.Year, first.Year);
+ Assert.AreEqual(dt.Month, first.Month);
+ Assert.AreEqual(1, first.Day);
+ }
+
+ ///
+ /// Tests for .
+ ///
+ [TestMethod]
+ public void Last()
+ {
+ {
+ var dt = new DateTime(2019, 12, 1);
+ DateTime last = dt.Last(DayOfWeek.Wednesday);
+
+ Assert.AreEqual(dt.Year, last.Year);
+ Assert.AreEqual(dt.Month, last.Month);
+ Assert.AreEqual(25, last.Day);
+ }
+
+ {
+ var dt = new DateTime(2020, 4, 14);
+ DateTime last = dt.Last(DayOfWeek.Friday);
+
+ Assert.AreEqual(dt.Year, last.Year);
+ Assert.AreEqual(dt.Month, last.Month);
+ Assert.AreEqual(24, last.Day);
+
+ last = dt.Last(DayOfWeek.Thursday);
+ Assert.AreEqual(dt.Year, last.Year);
+ Assert.AreEqual(dt.Month, last.Month);
+ Assert.AreEqual(30, last.Day);
+ }
+ }
+
+ ///
+ /// Tests for .
+ ///
+ [TestMethod]
+ public void LastDayOfMonth()
+ {
+ var dt = new DateTime(2016, 2, 4);
+ DateTime last = dt.LastDayOfMonth();
+
+ Assert.AreEqual(dt.Year, last.Year);
+ Assert.AreEqual(dt.Month, last.Month);
+ Assert.AreEqual(29, last.Day); // 2016 is a leap year
+ }
+
+ ///
+ /// Tests for .
+ ///
+ [TestMethod]
+ public void ToUnixTimestamp()
+ {
+ var dt = new DateTime(2015, 10, 21, 1, 0, 0, DateTimeKind.Utc);
+ var unix = dt.ToUnixTimeStamp();
+
+ Assert.AreEqual(1445389200L, unix);
+ }
+ }
+}
diff --git a/X10D.Tests/src/DictionaryTests.cs b/X10D.Tests/src/DictionaryTests.cs
new file mode 100644
index 0000000..e85b895
--- /dev/null
+++ b/X10D.Tests/src/DictionaryTests.cs
@@ -0,0 +1,42 @@
+namespace X10D.Tests
+{
+ using System.Collections.Generic;
+ using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+ ///
+ /// Tests for .
+ ///
+ [TestClass]
+ public class DictionaryTests
+ {
+ ///
+ /// Tests for .
+ ///
+ [TestMethod]
+ public void ToConnectionString()
+ {
+ var dictionary = new Dictionary
+ {
+ { "username", "Foo" }, { "password", "Foo Bar" }, { "port", 3306 },
+ };
+
+ var connectionString = dictionary.ToConnectionString();
+ Assert.AreEqual("username=Foo;password=\"Foo Bar\";port=3306", connectionString);
+ }
+
+ ///
+ /// Tests for .
+ ///
+ [TestMethod]
+ public void ToGetParameters()
+ {
+ var dictionary = new Dictionary
+ {
+ { "username", "Foo" }, { "password", "Foo Bar" }, { "port", 3306 },
+ };
+
+ var getParameterString = dictionary.ToGetParameters();
+ Assert.AreEqual("username=Foo&password=Foo+Bar&port=3306", getParameterString);
+ }
+ }
+}
diff --git a/X10D.Tests/src/DoubleTests.cs b/X10D.Tests/src/DoubleTests.cs
new file mode 100644
index 0000000..9f7a25a
--- /dev/null
+++ b/X10D.Tests/src/DoubleTests.cs
@@ -0,0 +1,84 @@
+namespace X10D.Tests
+{
+ using System;
+ using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+ ///
+ /// Tests for .
+ ///
+ [TestClass]
+ public class DoubleTests
+ {
+ ///
+ /// Tests for .
+ ///
+ [TestMethod]
+ public void Clamp()
+ {
+ Assert.AreEqual(2.0, 3.0.Clamp(1.0, 2.0));
+ Assert.AreEqual(1.0, (-3.0).Clamp(1.0, 2.0));
+ }
+
+ ///
+ /// Tests for .
+ ///
+ [TestMethod]
+ public void DegreesToRadians()
+ {
+ Assert.AreEqual(Math.PI, 180.0.DegreesToRadians());
+ Assert.AreEqual(Math.PI * 1.5, 270.0.DegreesToRadians());
+ }
+
+ ///
+ /// Tests for .
+ ///
+ [TestMethod]
+ public void GetBytes()
+ {
+ CollectionAssert.AreEqual(
+ new byte[] { 0x18, 0x2D, 0x44, 0x54, 0xFB, 0x21, 0x09, 0x40 },
+ Math.PI.GetBytes());
+ }
+
+ ///
+ /// Tests for .
+ ///
+ [TestMethod]
+ public void IsEven()
+ {
+ Assert.IsTrue(2.0.IsEven());
+ Assert.IsFalse(1.0.IsEven());
+ }
+
+ ///
+ /// Tests for .
+ ///
+ [TestMethod]
+ public void IsOdd()
+ {
+ Assert.IsFalse(2.0.IsOdd());
+ Assert.IsTrue(1.0.IsOdd());
+ }
+
+ ///
+ /// Tests for .
+ ///
+ [TestMethod]
+ public void RadiansToDegrees()
+ {
+ Assert.AreEqual(180.0, Math.PI.RadiansToDegrees());
+ Assert.AreEqual(360.0, (2.0 * Math.PI).RadiansToDegrees());
+ }
+
+ ///
+ /// Tests for .
+ ///
+ [TestMethod]
+ public void Round()
+ {
+ Assert.AreEqual(5.0, 3.5.Round(5));
+ Assert.AreEqual(5.0, 7.0.Round(5));
+ Assert.AreEqual(10.0, 7.5.Round(5));
+ }
+ }
+}
diff --git a/X10D.Tests/src/EnumerableTests.cs b/X10D.Tests/src/EnumerableTests.cs
new file mode 100644
index 0000000..c87ef8a
--- /dev/null
+++ b/X10D.Tests/src/EnumerableTests.cs
@@ -0,0 +1,55 @@
+namespace X10D.Tests
+{
+ using System.Collections.Generic;
+ using System.Linq;
+ using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+ ///
+ /// Tests for .
+ ///
+ [TestClass]
+ public class EnumerableTests
+ {
+ ///
+ /// Tests for using an array of .
+ ///
+ [TestMethod]
+ public void SplitByte()
+ {
+ byte[] foo = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 };
+ IEnumerable> chunks = foo.Split(2).ToArray();
+
+ Assert.AreEqual(4, chunks.Count());
+ CollectionAssert.AreEqual(new byte[] { 0x01, 0x02 }, chunks.ElementAt(0).ToList());
+ CollectionAssert.AreEqual(new byte[] { 0x03, 0x04 }, chunks.ElementAt(1).ToList());
+ CollectionAssert.AreEqual(new byte[] { 0x05, 0x06 }, chunks.ElementAt(2).ToList());
+ CollectionAssert.AreEqual(new byte[] { 0x07, 0x08 }, chunks.ElementAt(3).ToList());
+
+ // test exceeding chunk size
+ chunks = foo.Split(foo.Length + 10).ToArray();
+ Assert.AreEqual(1, chunks.Count());
+ CollectionAssert.AreEqual(foo, chunks.SelectMany(c => c).ToList());
+ }
+
+ ///
+ /// Tests for using an array of .
+ ///
+ [TestMethod]
+ public void SplitInt32()
+ {
+ int[] foo = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 };
+ IEnumerable> chunks = foo.Split(2).ToArray();
+
+ Assert.AreEqual(4, chunks.Count());
+ CollectionAssert.AreEqual(new[] { 0x01, 0x02 }, chunks.ElementAt(0).ToList());
+ CollectionAssert.AreEqual(new[] { 0x03, 0x04 }, chunks.ElementAt(1).ToList());
+ CollectionAssert.AreEqual(new[] { 0x05, 0x06 }, chunks.ElementAt(2).ToList());
+ CollectionAssert.AreEqual(new[] { 0x07, 0x08 }, chunks.ElementAt(3).ToList());
+
+ // test exceeding chunk size
+ chunks = foo.Split(foo.Length + 10).ToArray();
+ Assert.AreEqual(1, chunks.Count());
+ CollectionAssert.AreEqual(foo, chunks.SelectMany(c => c).ToList());
+ }
+ }
+}
diff --git a/X10D.Tests/src/TimeSpanParserTests.cs b/X10D.Tests/src/TimeSpanParserTests.cs
new file mode 100644
index 0000000..452a3a1
--- /dev/null
+++ b/X10D.Tests/src/TimeSpanParserTests.cs
@@ -0,0 +1,20 @@
+namespace X10D.Tests
+{
+ using System;
+ using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+ [TestClass]
+ public class TimeSpanParserTests
+ {
+ [TestMethod]
+ public void TestParser()
+ {
+ Assert.AreEqual(TimeSpan.FromHours(3), "3h".ToTimeSpan());
+ Assert.AreEqual(TimeSpan.FromMinutes(2.5), "2.5m".ToTimeSpan());
+ Assert.AreEqual(TimeSpan.FromHours(1), "60m".ToTimeSpan());
+ Assert.AreEqual(TimeSpan.FromDays(1), "1d".ToTimeSpan());
+ Assert.AreEqual(TimeSpan.FromDays(8), "1w 1d".ToTimeSpan());
+ Assert.AreEqual(TimeSpan.FromDays(8), "1w1d".ToTimeSpan());
+ }
+ }
+}