1
0

Erweiterung der UnitTests und kleinere Fixes

This commit is contained in:
2021-11-17 23:30:54 +01:00
parent 3a0732dd22
commit 80fc6ff2b3
16 changed files with 873 additions and 37 deletions

1
.gitignore vendored
View File

@@ -13,6 +13,7 @@
nuget.config
build
coverage.json
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs

View File

@@ -3,12 +3,18 @@
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<IsPackable>false</IsPackable>
<CollectCoverage>true</CollectCoverage>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="coverlet.msbuild" Version="3.1.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
<PackageReference Include="MSTest.TestAdapter" Version="2.2.7" />
<PackageReference Include="MSTest.TestFramework" Version="2.2.7" />
<PackageReference Include="ReflectionMagic" Version="4.1.0" />
</ItemGroup>
<ItemGroup>

View File

@@ -0,0 +1,75 @@
using System;
using AMWD.Common.Tests.Utils;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Security.Cryptography;
namespace AMWD.Common.Tests.Extensions
{
[TestClass]
public class CryptographyHelperExtensionsTests
{
[TestMethod]
public void ShouldReturnMd5Hash()
{
// arrange
string str = "Hello World!";
byte[] bytes = new byte[] { 0xaf, 0xfe };
// act
string strHash = str.Md5();
string byteHash = bytes.Md5();
// assert
Assert.AreEqual("ed076287532e86365e841e92bfc50d8c", strHash);
Assert.AreEqual("63c983de427ce9e2430ba8554d2a822f", byteHash);
}
[TestMethod]
public void ShouldReturnSha1Hash()
{
// arrange
string str = "Hello World!";
byte[] bytes = new byte[] { 0xaf, 0xfe };
// act
string strHash = str.Sha1();
string byteHash = bytes.Sha1();
// assert
Assert.AreEqual("2ef7bde608ce5404e97d5f042f95f89f1c232871", strHash);
Assert.AreEqual("ec2c39d500316044fa49f6c8f471ddec8b4fb9d1", byteHash);
}
[TestMethod]
public void ShouldReturnSha256Hash()
{
// arrange
string str = "Hello World!";
byte[] bytes = new byte[] { 0xaf, 0xfe };
// act
string strHash = str.Sha256();
string byteHash = bytes.Sha256();
// assert
Assert.AreEqual("7f83b1657ff1fc53b92dc18148a1d65dfc2d4b1fa3d677284addd200126d9069", strHash);
Assert.AreEqual("4e0da689dc7a51957be426d6cfb1bd860169cb25dd1ac946a2f141228217804a", byteHash);
}
[TestMethod]
public void ShouldReturnSha512Hash()
{
// arrange
string str = "Hello World!";
byte[] bytes = new byte[] { 0xaf, 0xfe };
// act
string strHash = str.Sha512();
string byteHash = bytes.Sha512();
// assert
Assert.AreEqual("861844d6704e8573fec34d967e20bcfef3d424cf48be04e6dc08f2bd58c729743371015ead891cc3cf1c9d34b49264b510751b1ff9e537937bc46b5d6ff4ecc8", strHash);
Assert.AreEqual("591098c5d470a09f0ff48a4fdb7769ab89f803eae9e23b6f9f69dd228cca46c074bbc11a5fceaa8a5f48d14d2bf19a83a629266c2c5b7d9ef34623b64cb2f8e7", byteHash);
}
}
}

View File

@@ -1,4 +1,5 @@
using System;
using AMWD.Common.Tests.Utils;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace AMWD.Common.Tests.Extensions
@@ -10,6 +11,9 @@ namespace AMWD.Common.Tests.Extensions
public void ShouldReturnUtc()
{
// arrange
var timeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById("Europe/Berlin");
using var mock = TimeZoneInfoLocalMock.Create(timeZoneInfo);
var utc = new DateTime(2021, 11, 15, 11, 22, 33, DateTimeKind.Utc);
var local = new DateTime(2021, 11, 15, 11, 22, 33, DateTimeKind.Local);
var unspecified = new DateTime(2021, 11, 15, 11, 22, 33, DateTimeKind.Unspecified);
@@ -33,6 +37,9 @@ namespace AMWD.Common.Tests.Extensions
public void ShouldReturnLocal()
{
// arrange
var timeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById("Europe/Berlin");
using var mock = TimeZoneInfoLocalMock.Create(timeZoneInfo);
var utc = new DateTime(2021, 11, 15, 11, 22, 33, DateTimeKind.Utc);
var local = new DateTime(2021, 11, 15, 11, 22, 33, DateTimeKind.Local);
var unspecified = new DateTime(2021, 11, 15, 11, 22, 33, DateTimeKind.Unspecified);
@@ -122,6 +129,9 @@ namespace AMWD.Common.Tests.Extensions
public void ShouldReturnCorrectLocalAlignmentDaylightSavingEnd()
{
// arrange
var timeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById("Europe/Berlin");
using var mock = TimeZoneInfoLocalMock.Create(timeZoneInfo);
var utcNow = new DateTime(2021, 10, 30, 12, 15, 30, 45, DateTimeKind.Local);
var intervalThreeSeconds = TimeSpan.FromSeconds(3);
@@ -155,6 +165,9 @@ namespace AMWD.Common.Tests.Extensions
public void ShouldReturnCorrectLocalAlignmentDaylightSavingStart()
{
// arrange
var timeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById("Europe/Berlin");
using var mock = TimeZoneInfoLocalMock.Create(timeZoneInfo);
var utcNow = new DateTime(2022, 3, 26, 12, 15, 30, 45, DateTimeKind.Local);
var intervalThreeSeconds = TimeSpan.FromSeconds(3);

View File

@@ -0,0 +1,307 @@
using System;
using AMWD.Common.Tests.Utils;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace AMWD.Common.Tests.Extensions
{
[TestClass]
public class JsonExtensionsTests
{
[TestMethod]
public void ShouldReturnJson()
{
// arrange
var testObject = new JsonTestClass();
string expected = @"{""stringValue"":""Hello World!"",""isBoolTrue"":true,""floatValue"":12.34,""doubleValue"":21.42,""decimalValue"":42.21,""localTimestamp"":""2021-11-16T20:15:34+01:00"",""utcTimestamp"":""2021-11-16T20:15:34Z"",""object"":{""integerValue"":42,""stringValue"":""Foo-Bar""}}";
// act
string json = testObject.SerializeJson();
// assert
Assert.IsFalse(string.IsNullOrWhiteSpace(json));
Assert.AreEqual(expected, json);
}
[TestMethod]
public void ShouldReturnJsonIndented()
{
// arrange
var testObject = new JsonTestClass();
string expected = @"{
""stringValue"": ""Hello World!"",
""isBoolTrue"": true,
""floatValue"": 12.34,
""doubleValue"": 21.42,
""decimalValue"": 42.21,
""localTimestamp"": ""2021-11-16T20:15:34+01:00"",
""utcTimestamp"": ""2021-11-16T20:15:34Z"",
""object"": {
""integerValue"": 42,
""stringValue"": ""Foo-Bar""
}
}";
// act
string json = testObject.SerializeJson(indented: true);
// assert
Assert.IsFalse(string.IsNullOrWhiteSpace(json));
Assert.AreEqual(expected.Replace("\r", ""), json.Replace("\r", ""));
}
[TestMethod]
public void ShouldReturnJsonWithSingleQuotes()
{
// arrange
var testObject = new JsonTestClass
{
StringValue = "Sam's Pub"
};
string expected = "{'stringValue':'Sam\\'s Pub','isBoolTrue':true,'floatValue':12.34,'doubleValue':21.42,'decimalValue':42.21,'localTimestamp':'2021-11-16T20:15:34+01:00','utcTimestamp':'2021-11-16T20:15:34Z','object':{'integerValue':42,'stringValue':'Foo-Bar'}}";
// act
string json = testObject.SerializeJson(useSingleQuotes: true);
// assert
Assert.IsFalse(string.IsNullOrWhiteSpace(json));
Assert.AreEqual(expected, json);
}
[TestMethod]
public void ShouldReturnJsonWithoutCamelCase()
{
// arrange
var testObject = new JsonTestClass();
string expected = @"{""StringValue"":""Hello World!"",""IsBoolTrue"":true,""FloatValue"":12.34,""DoubleValue"":21.42,""DecimalValue"":42.21,""LocalTimestamp"":""2021-11-16T20:15:34+01:00"",""UtcTimestamp"":""2021-11-16T20:15:34Z"",""Object"":{""IntegerValue"":42,""StringValue"":""Foo-Bar""}}";
// act
string json = testObject.SerializeJson(useCamelCase: false);
// assert
Assert.IsFalse(string.IsNullOrWhiteSpace(json));
Assert.AreEqual(expected, json);
}
[TestMethod]
public void ShouldReturnJsonWithType()
{
// arrange
var testObject = new JsonTestClass();
string expected = @"{""$type"":""AMWD.Common.Tests.Utils.JsonTestClass, AMWD.Common.Tests"",""stringValue"":""Hello World!"",""isBoolTrue"":true,""floatValue"":12.34,""doubleValue"":21.42,""decimalValue"":42.21,""localTimestamp"":""2021-11-16T20:15:34+01:00"",""utcTimestamp"":""2021-11-16T20:15:34Z"",""object"":{""$type"":""AMWD.Common.Tests.Utils.JsonTestSubClass, AMWD.Common.Tests"",""integerValue"":42,""stringValue"":""Foo-Bar""}}";
// act
string json = testObject.SerializeJson(includeType: true);
// assert
Assert.IsFalse(string.IsNullOrWhiteSpace(json));
Assert.AreEqual(expected, json);
}
[TestMethod]
public void ShouldDeserializeWithoutFallback()
{
// arrange
string workingJson = @"{""stringValue"":""Some fancy string"",""isBoolTrue"":false,""floatValue"":12.34,""doubleValue"":21.42,""decimalValue"":123.45,""localTimestamp"":""2021-11-15T20:15:34+01:00"",""utcTimestamp"":""2021-10-16T20:15:34Z"",""object"":{""integerValue"":21,""stringValue"":""FooBar""}}";
string brokenJson = @"{""strValue"":""Some fancy string"",""isBoolTrue"":false,""floatValue"":12.34,""doubleValue"":21.42,""decimalValue"":123.45,""localTimestamp"":""2021-11-15T20:15:34+01:00"",""utcTimestamp"":""2021-10-16T20:15:34Z"",""object"":{""integerValue"":21.12,""stringValue"":""FooBar""}}";
string emptyString = "";
// act
var workingObj = workingJson.DeserializeJson<JsonTestClass>();
var emptyObj = emptyString.DeserializeJson<JsonTestClass>();
try
{
var brokenObj = brokenJson.DeserializeJson<JsonTestClass>();
Assert.Fail();
}
catch (JsonReaderException)
{ }
// assert
Assert.IsNotNull(workingObj);
Assert.IsNull(emptyObj);
Assert.AreEqual("Some fancy string", workingObj.StringValue);
Assert.IsFalse(workingObj.IsBoolTrue);
Assert.AreEqual(123.45m, workingObj.DecimalValue);
Assert.AreEqual(DateTimeKind.Local, workingObj.LocalTimestamp.Kind);
Assert.AreEqual("15.11.2021 20:15:34", workingObj.LocalTimestamp.ToString("dd.MM.yyyy HH:mm:ss"));
Assert.AreEqual(DateTimeKind.Utc, workingObj.UtcTimestamp.Kind);
Assert.AreEqual("16.10.2021 20:15:34", workingObj.UtcTimestamp.ToString("dd.MM.yyyy HH:mm:ss"));
}
[TestMethod]
public void ShouldDeserializeWithFallback()
{
// arrange
string workingJson = @"{""stringValue"":""Some fancy string"",""isBoolTrue"":false,""floatValue"":12.34,""doubleValue"":21.42,""decimalValue"":123.45,""localTimestamp"":""2021-11-15T20:15:34+01:00"",""utcTimestamp"":""2021-10-16T20:15:34Z"",""object"":{""integerValue"":21,""stringValue"":""FooBar""}}";
string brokenJson = @"{""strValue"":""Some fancy string"",""isBoolTrue"":false,""floatValue"":12.34,""doubleValue"":21.42,""decimalValue"":123.45,""localTimestamp"":""2021-11-15T20:15:34+01:00"",""utcTimestamp"":""2021-10-16T20:15:34Z"",""object"":{""integerValue"":21.12,""stringValue"":""FooBar""}}";
string emptyString = "";
var fallback = new JsonTestClass
{
DoubleValue = 0.815
};
// act
var workingObj = workingJson.DeserializeJson(fallback);
var brokenObj = brokenJson.DeserializeJson(fallback);
var emptyObj = emptyString.DeserializeJson(fallback);
// assert
Assert.IsNotNull(workingObj);
Assert.IsNotNull(brokenObj);
Assert.IsNull(emptyObj);
Assert.AreEqual("Some fancy string", workingObj.StringValue);
Assert.IsFalse(workingObj.IsBoolTrue);
Assert.AreEqual(123.45m, workingObj.DecimalValue);
Assert.AreEqual(21.42, workingObj.DoubleValue);
Assert.AreEqual(DateTimeKind.Local, workingObj.LocalTimestamp.Kind);
Assert.AreEqual("15.11.2021 20:15:34", workingObj.LocalTimestamp.ToString("dd.MM.yyyy HH:mm:ss"));
Assert.AreEqual(DateTimeKind.Utc, workingObj.UtcTimestamp.Kind);
Assert.AreEqual("16.10.2021 20:15:34", workingObj.UtcTimestamp.ToString("dd.MM.yyyy HH:mm:ss"));
Assert.AreEqual(fallback.StringValue, brokenObj.StringValue);
Assert.AreEqual(fallback.IsBoolTrue, brokenObj.IsBoolTrue);
Assert.AreEqual(fallback.DecimalValue, brokenObj.DecimalValue);
Assert.AreEqual(fallback.DoubleValue, brokenObj.DoubleValue);
Assert.AreEqual(fallback.LocalTimestamp.Kind, brokenObj.LocalTimestamp.Kind);
Assert.AreEqual(fallback.LocalTimestamp, brokenObj.LocalTimestamp);
Assert.AreEqual(fallback.UtcTimestamp.Kind, brokenObj.UtcTimestamp.Kind);
Assert.AreEqual(fallback.UtcTimestamp, brokenObj.UtcTimestamp);
}
[TestMethod]
public void ShouldDeserializeUsingPopulate()
{
// arrange
string json = @"{""stringValue"":""Some fancy string"",""isBoolTrue"":false,""floatValue"":12.34,""doubleValue"":21.42,""decimalValue"":123.45,""localTimestamp"":""2021-11-15T20:15:34+01:00"",""utcTimestamp"":""2021-10-16T20:15:34Z"",""object"":{""integerValue"":21,""stringValue"":""FooBar""}}";
var obj = new JsonTestClass();
// act
obj.DeserializeJson(json);
// assert
Assert.AreEqual("Some fancy string", obj.StringValue);
Assert.IsFalse(obj.IsBoolTrue);
Assert.AreEqual(123.45m, obj.DecimalValue);
Assert.AreEqual(21.42, obj.DoubleValue);
Assert.AreEqual(DateTimeKind.Local, obj.LocalTimestamp.Kind);
Assert.AreEqual("15.11.2021 20:15:34", obj.LocalTimestamp.ToString("dd.MM.yyyy HH:mm:ss"));
Assert.AreEqual(DateTimeKind.Utc, obj.UtcTimestamp.Kind);
Assert.AreEqual("16.10.2021 20:15:34", obj.UtcTimestamp.ToString("dd.MM.yyyy HH:mm:ss"));
}
[TestMethod]
public void ShouldConvertToJObject()
{
// arrange
var obj = new JsonTestClass
{
StringValue = "Hello JSON",
DecimalValue = 0.815m
};
// act
var jObj = obj.ConvertToJObject();
// assert
Assert.IsNotNull(jObj);
Assert.AreEqual(typeof(JObject), jObj.GetType());
Assert.AreEqual(obj.StringValue, jObj.Value<string>("stringValue"));
Assert.AreEqual(obj.DecimalValue, jObj.Value<decimal>("decimalValue"));
Assert.AreEqual(obj.LocalTimestamp.Kind, jObj.Value<DateTime>("localTimestamp").Kind);
Assert.AreEqual(obj.LocalTimestamp, jObj.Value<DateTime>("localTimestamp"));
Assert.AreEqual(obj.UtcTimestamp.Kind, jObj.Value<DateTime>("utcTimestamp").Kind);
Assert.AreEqual(obj.UtcTimestamp, jObj.Value<DateTime>("utcTimestamp"));
}
[TestMethod]
public void ShouldConvertToJArray()
{
// arrange
string[] stringArray = new[] { "one", "two", "three" };
var objectArray = new[]
{
new JsonTestClass { StringValue = "One" },
new JsonTestClass { StringValue = "Two" },
new JsonTestClass { StringValue = "Three" }
};
// act
var stringJArray = stringArray.ConvertToJArray();
var objectJArray = objectArray.ConvertToJArray();
// assert
Assert.IsNotNull(stringJArray);
Assert.AreEqual(typeof(JArray), stringJArray.GetType());
Assert.AreEqual(stringArray[0], stringJArray[0]);
Assert.AreEqual(stringArray[1], stringJArray[1]);
Assert.AreEqual(stringArray[2], stringJArray[2]);
Assert.IsNotNull(objectJArray);
Assert.AreEqual(typeof(JArray), objectJArray.GetType());
Assert.AreEqual(objectArray[0].StringValue, objectJArray[0].Value<string>("stringValue"));
Assert.AreEqual(objectArray[1].StringValue, objectJArray[1].Value<string>("stringValue"));
Assert.AreEqual(objectArray[2].StringValue, objectJArray[2].Value<string>("stringValue"));
}
[TestMethod]
public void ShouldRunJsonTreeWithoutDefault()
{
// arrange
var obj = new JsonTestClass { StringValue = "Running Json", Object = new JsonTestSubClass { IntegerValue = 4711 } };
var jObj = obj.ConvertToJObject();
// act
string topLevelString = jObj.GetValue<string>("stringValue");
decimal topLevelDecimal = jObj.GetValue<decimal>("decimalValue");
int subLevelInteger = jObj.GetValue<int>("object:integerValue");
string subLevelString = jObj.GetValue<string>("object:stringValue");
string notExistingOnTopLevel = jObj.GetValue<string>("fancyValue");
string notExistingOnSubLevel = jObj.GetValue<string>("object:fancyValue");
int? notExistingLevel = jObj.GetValue<int?>("fancy:int");
// assert
Assert.AreEqual(obj.StringValue, topLevelString);
Assert.AreEqual(obj.DecimalValue, topLevelDecimal);
Assert.AreEqual(obj.Object.IntegerValue, subLevelInteger);
Assert.AreEqual(obj.Object.StringValue, subLevelString);
Assert.IsNull(notExistingOnTopLevel);
Assert.IsNull(notExistingOnSubLevel);
Assert.IsNull(notExistingLevel);
}
[TestMethod]
public void ShouldRunJsonTreeWithDefault()
{
// arrange
var obj = new JsonTestClass { StringValue = "Running Json", Object = new JsonTestSubClass { IntegerValue = 4711 } };
var jObj = obj.ConvertToJObject();
// act
string topLevelString = jObj.GetValue("stringValue", "Test String");
decimal topLevelDecimal = jObj.GetValue("decimalValue", 13.24m);
int subLevelInteger = jObj.GetValue("object:integerValue", 55);
string subLevelString = jObj.GetValue("object:stringValue", "Yeah!");
string notExistingOnTopLevel = jObj.GetValue("fancyValue", "Party!");
string notExistingOnSubLevel = jObj.GetValue("object:fancyValue", "Well Done");
int? notExistingLevel = jObj.GetValue<int?>("fancy:int", 13);
// assert
Assert.AreEqual(obj.StringValue, topLevelString);
Assert.AreEqual(obj.DecimalValue, topLevelDecimal);
Assert.AreEqual(obj.Object.IntegerValue, subLevelInteger);
Assert.AreEqual(obj.Object.StringValue, subLevelString);
Assert.AreEqual("Party!", notExistingOnTopLevel);
Assert.AreEqual("Well Done", notExistingOnSubLevel);
Assert.AreEqual(13, notExistingLevel);
}
}
}

View File

@@ -0,0 +1,98 @@
using System.Threading;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace AMWD.Common.Tests.Extensions
{
[TestClass]
public class ReaderWriterLockSlimExtensionsTests
{
[TestMethod]
public void ShouldEnterReadLock()
{
// arrange
var rwLock = new ReaderWriterLockSlim();
// act
using var disposable = rwLock.GetReadLock();
// assert
Assert.IsNotNull(disposable);
Assert.IsTrue(rwLock.IsReadLockHeld);
Assert.IsFalse(rwLock.IsUpgradeableReadLockHeld);
Assert.IsFalse(rwLock.IsWriteLockHeld);
}
[TestMethod]
public void ShouldEnterUpgradeableReadLock()
{
// arrange
var rwLock = new ReaderWriterLockSlim();
// act
using var disposable = rwLock.GetUpgradeableReadLock();
// assert
Assert.IsNotNull(disposable);
Assert.IsFalse(rwLock.IsReadLockHeld);
Assert.IsTrue(rwLock.IsUpgradeableReadLockHeld);
Assert.IsFalse(rwLock.IsWriteLockHeld);
}
[TestMethod]
public void ShouldEnterWriteLock()
{
// arrange
var rwLock = new ReaderWriterLockSlim();
// act
using var disposable = rwLock.GetWriteLock();
// assert
Assert.IsNotNull(disposable);
Assert.IsFalse(rwLock.IsReadLockHeld);
Assert.IsFalse(rwLock.IsUpgradeableReadLockHeld);
Assert.IsTrue(rwLock.IsWriteLockHeld);
}
[TestMethod]
public void ShouldNotAllowWriteLockAfterReadLock()
{
// arrange
var rwLock = new ReaderWriterLockSlim();
// act
using var disposableRead = rwLock.GetReadLock();
try
{
using var disposaleWrite = rwLock.GetWriteLock();
Assert.Fail();
}
catch (LockRecursionException)
{ }
// assert
Assert.IsNotNull(disposableRead);
Assert.IsTrue(rwLock.IsReadLockHeld);
Assert.IsFalse(rwLock.IsUpgradeableReadLockHeld);
Assert.IsFalse(rwLock.IsWriteLockHeld);
}
[TestMethod]
public void ShouldAllowWriteLockAfterUpgradeableReadLock()
{
// arrange
var rwLock = new ReaderWriterLockSlim();
// act
using var disposableRead = rwLock.GetUpgradeableReadLock();
using var disposaleWrite = rwLock.GetWriteLock();
// assert
Assert.IsNotNull(disposableRead);
Assert.IsNotNull(disposaleWrite);
Assert.IsFalse(rwLock.IsReadLockHeld);
Assert.IsTrue(rwLock.IsUpgradeableReadLockHeld);
Assert.IsTrue(rwLock.IsWriteLockHeld);
}
}
}

View File

@@ -0,0 +1,249 @@
using System;
using System.Linq;
using System.Text;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace AMWD.Common.Tests.Extensions
{
[TestClass]
public class StringExtensionsTests
{
[TestMethod]
public void ShouldReturnEmptyList()
{
// arrange
string hex = "";
// act
var bytes = hex.HexToBytes();
// assert
Assert.IsNotNull(bytes);
Assert.IsFalse(bytes.Any());
}
[TestMethod]
public void ShouldReturnEmptyListWhenInvalid()
{
// arrange
string hex1 = "aff";
string hex2 = "de:ad:be:e";
string hex3 = "hell";
// act
var bytes1 = hex1.HexToBytes();
var bytes2 = hex2.HexToBytes(":");
var bytes3 = hex3.HexToBytes();
// assert
Assert.IsNotNull(bytes1);
Assert.IsFalse(bytes1.Any());
Assert.IsNotNull(bytes2);
Assert.IsFalse(bytes2.Any());
Assert.IsNotNull(bytes3);
Assert.IsFalse(bytes3.Any());
}
[TestMethod]
public void ShouldConvertHexToBytes()
{
// arrange
string hex = "deadbeef";
// act
var bytes = hex.HexToBytes();
// assert
Assert.IsNotNull(bytes);
Assert.AreEqual(4, bytes.Count());
Assert.AreEqual(0xde, bytes.ElementAt(0));
Assert.AreEqual(0xad, bytes.ElementAt(1));
Assert.AreEqual(0xbe, bytes.ElementAt(2));
Assert.AreEqual(0xef, bytes.ElementAt(3));
}
[TestMethod]
public void ShouldConvertHexToBytesWithDelimiter()
{
// arrange
string hex = "af:fe";
// act
var bytes = hex.HexToBytes(":");
// assert
Assert.IsNotNull(bytes);
Assert.AreEqual(2, bytes.Count());
Assert.AreEqual(0xaf, bytes.ElementAt(0));
Assert.AreEqual(0xfe, bytes.ElementAt(1));
}
[TestMethod]
public void ShouldReturnNullWhenInvalid()
{
// arrange
byte[] bytes1 = null;
byte[] bytes2 = Array.Empty<byte>();
// act
string hex1 = bytes1.BytesToHex();
string hex2 = bytes2.BytesToHex();
// assert
Assert.IsNull(hex1);
Assert.IsNull(hex2);
}
[TestMethod]
public void ShouldReturnHexString()
{
// arrange
byte[] bytes = new byte[] { 0xaf, 0xfe };
// act
string hex = bytes.BytesToHex();
// assert
Assert.IsNotNull(hex);
Assert.AreEqual("affe", hex);
}
[TestMethod]
public void ShouldReturnHexStringWithDelimiter()
{
// arrange
byte[] bytes = new byte[] { 0xde, 0xad, 0xbe, 0xef };
// act
string hex = bytes.BytesToHex("_");
// assert
Assert.IsNotNull(hex);
Assert.AreEqual("de_ad_be_ef", hex);
}
[TestMethod]
public void ShouldEncodeStringToHex()
{
// arrange
string plain = "Hello";
// act
string hex = plain.HexEncode(Encoding.UTF8);
// assert
Assert.AreEqual("48656c6c6f", hex);
}
[TestMethod]
public void ShouldDecodeStringFromHex()
{
// arrange
string hex = "48656c6c6f";
// act
string plain = hex.HexDecode(Encoding.UTF8);
// assert
Assert.AreEqual("Hello", plain);
}
[TestMethod]
public void ShouldEncodeStringToBase64()
{
// arrange
string plain = "Hello";
// act
string base64 = plain.Base64Encode(Encoding.UTF8);
// assert
Assert.AreEqual("SGVsbG8=", base64);
}
[TestMethod]
public void ShouldDecodeStringFromBase64()
{
// arrange
string base64 = "SGVsbG8=";
// act
string plain = base64.Base64Decode(Encoding.UTF8);
// assert
Assert.AreEqual("Hello", plain);
}
[TestMethod]
public void ShouldReplaceStart()
{
// arrange
string str = "Hello World!";
// act
string test1 = str.ReplaceStart("Hello", "Bye");
string test2 = str.ReplaceStart("World!", "Mars?");
// assert
Assert.AreNotEqual(str, test1);
Assert.AreEqual("Bye World!", test1);
Assert.AreEqual(str, test2);
Assert.AreNotEqual("Hello Mars?", test2);
}
[TestMethod]
public void ShouldReplaceEnd()
{
// arrange
string str = "Hello World!";
// act
string test1 = str.ReplaceEnd("Hello", "Bye");
string test2 = str.ReplaceEnd("World!", "Mars?");
// assert
Assert.AreEqual(str, test1);
Assert.AreNotEqual("Bye World!", test1);
Assert.AreNotEqual(str, test2);
Assert.AreEqual("Hello Mars?", test2);
}
[TestMethod]
public void ShouldParseDecimal()
{
// arrange
decimal number = 1234.56m;
string stringNumberEn = "1234.56";
string stringNumberDe = "1234,56";
// act
decimal numberEn = stringNumberEn.ParseDecimal();
decimal numberDe = stringNumberDe.ParseDecimal();
// assert
Assert.AreEqual(number, numberEn);
Assert.AreEqual(number, numberDe);
}
[TestMethod]
public void ShouldParseDecimalWithThousandsSeparator()
{
// arrange
decimal number = 1234.56m;
string stringNumberEn = "1,234.56";
string stringNumberDe = "1.234,56";
// act
decimal numberEn = stringNumberEn.ParseDecimal();
decimal numberDe = stringNumberDe.ParseDecimal();
// assert
Assert.AreEqual(number, numberEn);
Assert.AreEqual(number, numberDe);
}
}
}

View File

@@ -2,6 +2,7 @@
namespace AMWD.Common.Tests.Utils
{
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
[AttributeUsage(AttributeTargets.All, AllowMultiple = true)]
internal class CustomMultipleAttribute : Attribute
{

View File

@@ -0,0 +1,32 @@
using System;
namespace AMWD.Common.Tests.Utils
{
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
internal class JsonTestClass
{
public string StringValue { get; set; } = "Hello World!";
public bool IsBoolTrue { get; set; } = true;
public float FloatValue { get; set; } = 12.34f;
public double DoubleValue { get; set; } = 21.42;
public decimal DecimalValue { get; set; } = 42.21m;
public DateTime LocalTimestamp { get; set; } = new(2021, 11, 16, 20, 15, 34, DateTimeKind.Local);
public DateTime UtcTimestamp { get; set; } = new(2021, 11, 16, 20, 15, 34, DateTimeKind.Utc);
public JsonTestSubClass Object { get; set; } = new();
}
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
internal class JsonTestSubClass
{
public int IntegerValue { get; set; } = 42;
public string StringValue { get; set; } = "Foo-Bar";
}
}

View File

@@ -0,0 +1,26 @@
using System;
using ReflectionMagic;
namespace AMWD.Common.Tests.Utils
{
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
public class TimeZoneInfoLocalMock : IDisposable
{
private readonly TimeZoneInfo localTimeZoneInfo;
private TimeZoneInfoLocalMock(TimeZoneInfo timeZoneInfo)
{
localTimeZoneInfo = TimeZoneInfo.Local;
SetLocalTimeZone(timeZoneInfo);
}
public static IDisposable Create(TimeZoneInfo mockTimeZoneInfo)
=> new TimeZoneInfoLocalMock(mockTimeZoneInfo);
public void Dispose()
=> SetLocalTimeZone(localTimeZoneInfo);
private static void SetLocalTimeZone(TimeZoneInfo timeZoneInfo)
=> typeof(TimeZoneInfo).AsDynamicType().s_cachedData._localTimeZone = timeZoneInfo;
}
}

View File

@@ -50,14 +50,14 @@
/// </summary>
/// <param name="str">The string to hash, using UTF-8 encoding.</param>
/// <returns>The SHA-256 hash value, in hexadecimal notation.</returns>
public static string Sha256(string str) => CryptographyHelper.Sha256(str);
public static string Sha256(this string str) => CryptographyHelper.Sha256(str);
/// <summary>
/// Computes a hash from a byte array value using the SHA-256 algorithm.
/// </summary>
/// <param name="bytes">The byte array.</param>
/// <returns>The SHA-256 hash value, in hexadecimal notation.</returns>
public static string Sha256(byte[] bytes) => CryptographyHelper.Sha256(bytes);
public static string Sha256(this byte[] bytes) => CryptographyHelper.Sha256(bytes);
#endregion SHA-256

View File

@@ -5,6 +5,7 @@ namespace System
/// <summary>
/// Provides extension methods for exceptions.
/// </summary>
[Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
public static class ExceptionExtensions
{
/// <summary>

View File

@@ -1,5 +1,4 @@
using System;
using System.Collections;
using System.Collections;
using System.Globalization;
using System.IO;
using System.Text;
@@ -36,6 +35,34 @@ namespace Newtonsoft.Json
JsonConvert.PopulateObject(json, target, jsonSerializerSettings);
}
/// <summary>
/// Deserializes a JSON string into a new instance.
/// </summary>
/// <typeparam name="T">The type of the instance to deserialize.</typeparam>
/// <param name="json">The JSON string to read the values from.</param>
/// <returns>A new instance of <typeparamref name="T"/> with the deserialized values.</returns>
public static T DeserializeJson<T>(this string json)
=> JsonConvert.DeserializeObject<T>(json, jsonSerializerSettings);
/// <summary>
/// Deserializes a JSON string into a new instance or using the fallback value.
/// </summary>
/// <typeparam name="T">The type of the instance to deserialize.</typeparam>
/// <param name="json">The JSON string to read the values from.</param>
/// <param name="fallbackValue">A fallback value when deserialization fails.</param>
/// <returns>A new instance of <typeparamref name="T"/> with the deserialized values or the fallback value.</returns>
public static T DeserializeJson<T>(this string json, T fallbackValue)
{
try
{
return JsonConvert.DeserializeObject<T>(json, jsonSerializerSettings);
}
catch
{
return fallbackValue;
}
}
/// <summary>
/// Serializes an instance to a JSON string.
/// </summary>
@@ -63,29 +90,13 @@ namespace Newtonsoft.Json
a.ErrorContext.Handled = true;
};
if (includeType)
serializer.TypeNameHandling = TypeNameHandling.Auto;
serializer.TypeNameHandling = includeType ? TypeNameHandling.All : TypeNameHandling.None;
serializer.Serialize(jw, source, typeof(T));
}
return sb.ToString().Trim();
}
/// <summary>
/// Deserializes a JSON string into a new instance.
/// </summary>
/// <typeparam name="T">The type of the instance to deserialize.</typeparam>
/// <param name="json">The JSON string to read the values from.</param>
/// <param name="fallback">A fallback value.</param>
/// <returns>A new instance of <typeparamref name="T"/> with the deserialized values.</returns>
public static T DeserializeJson<T>(this string json, T fallback = default)
{
if (!string.IsNullOrWhiteSpace(json))
return JsonConvert.DeserializeObject<T>(json, jsonSerializerSettings);
return fallback;
}
/// <summary>
/// Converts an object into a JObject using the custom serializer settings.
/// </summary>
@@ -141,10 +152,7 @@ namespace Newtonsoft.Json
if (lvlObj == null)
return defaultValue;
if (typeof(T) == typeof(string))
return (T)Convert.ChangeType(lvlObj, typeof(T));
return DeepConvert.ChangeType<T>(lvlObj);
return DeepConvert.ChangeType<T>(lvlObj is JValue ? ((JValue)lvlObj).Value : lvlObj.Value<object>());
}
/// <summary>

View File

@@ -1,4 +1,6 @@
namespace System.Threading
//[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("AMWD.Common.Tests")]
namespace System.Threading
{
/// <summary>
/// Provides extension methods for the <see cref="ReaderWriterLockSlim"/>.
@@ -18,7 +20,7 @@
if (!rwLock.TryEnterReadLock(timeoutMilliseconds))
throw new TimeoutException("The read lock could not be acquired.");
return new RWLockDisposable(rwLock, 1);
return new DisposableReadWriteLock(rwLock, LockMode.Read);
}
/// <summary>
@@ -37,7 +39,7 @@
if (!rwLock.TryEnterUpgradeableReadLock(timeoutMilliseconds))
throw new TimeoutException("The upgradeable read lock could not be acquired.");
return new RWLockDisposable(rwLock, 2);
return new DisposableReadWriteLock(rwLock, LockMode.Upgradable);
}
/// <summary>
@@ -53,15 +55,15 @@
if (!rwLock.TryEnterWriteLock(timeoutMilliseconds))
throw new TimeoutException("The write lock could not be acquired.");
return new RWLockDisposable(rwLock, 3);
return new DisposableReadWriteLock(rwLock, LockMode.Write);
}
private struct RWLockDisposable : IDisposable
private struct DisposableReadWriteLock : IDisposable
{
private readonly ReaderWriterLockSlim rwLock;
private int lockMode;
private LockMode lockMode;
public RWLockDisposable(ReaderWriterLockSlim rwLock, int lockMode)
public DisposableReadWriteLock(ReaderWriterLockSlim rwLock, LockMode lockMode)
{
this.rwLock = rwLock;
this.lockMode = lockMode;
@@ -69,16 +71,28 @@
public void Dispose()
{
if (lockMode == 1)
if (lockMode == LockMode.Read)
rwLock.ExitReadLock();
if (lockMode == 2 && rwLock.IsWriteLockHeld) // Upgraded with EnterWriteLock alone
if (lockMode == LockMode.Upgradable && rwLock.IsWriteLockHeld) // Upgraded with EnterWriteLock alone
rwLock.ExitWriteLock();
if (lockMode == 2)
if (lockMode == LockMode.Upgradable)
rwLock.ExitUpgradeableReadLock();
if (lockMode == 3)
if (lockMode == LockMode.Write)
rwLock.ExitWriteLock();
lockMode = 0;
lockMode = LockMode.None;
}
}
private enum LockMode
{
None = 0,
Read = 1,
Upgradable = 2,
Write = 3,
}
}
}

View File

@@ -2,6 +2,7 @@
using System.Globalization;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
namespace System
{
@@ -25,6 +26,9 @@ namespace System
if (str.Length % 2 == 1)
yield break;
if (Regex.IsMatch(str, "[^0-9a-fA-F]"))
yield break;
for (int i = 0; i < str.Length; i += 2)
yield return Convert.ToByte(str.Substring(i, 2), 16);
}

View File

@@ -49,6 +49,7 @@ namespace AMWD.Common.Utilities
/// <param name="interfaceName">The interface name to resolve.</param>
/// <param name="addressFamily">An address family to use (available: <see cref="AddressFamily.InterNetwork"/> and <see cref="AddressFamily.InterNetworkV6"/>).</param>
/// <returns>The resolved <see cref="IPAddress"/>es or an empty list.</returns>
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] // not possible to define interfaces on unit tests
public static List<IPAddress> ResolveInterface(string interfaceName, AddressFamily addressFamily = default)
{
if (string.IsNullOrWhiteSpace(interfaceName))