Solution restructured to use multiple test projects
This commit is contained in:
15
test/AMWD.Common.Tests/AMWD.Common.Tests.csproj
Normal file
15
test/AMWD.Common.Tests/AMWD.Common.Tests.csproj
Normal file
@@ -0,0 +1,15 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="DNS" Version="7.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\AMWD.Common\AMWD.Common.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
305
test/AMWD.Common.Tests/Cli/CommandLineParserTest.cs
Normal file
305
test/AMWD.Common.Tests/Cli/CommandLineParserTest.cs
Normal file
@@ -0,0 +1,305 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using AMWD.Common.Cli;
|
||||
|
||||
namespace AMWD.Common.Tests.Cli
|
||||
{
|
||||
[TestClass]
|
||||
public class CommandLineParserTest
|
||||
{
|
||||
[TestMethod]
|
||||
public void ShouldParseStringToArgs()
|
||||
{
|
||||
// arrange
|
||||
|
||||
// act
|
||||
string[] result = CommandLineParser.ParseArgsString("Option1 \"Option 2\" \"Some \"\" Option\" Foo=Bar \\ /help \\\\backslash \\\"escapedquote \\test");
|
||||
|
||||
// assert
|
||||
Assert.IsNotNull(result);
|
||||
Assert.AreEqual(9, result.Length);
|
||||
|
||||
Assert.AreEqual("Option1", result[0]);
|
||||
Assert.AreEqual("Option 2", result[1]);
|
||||
Assert.AreEqual("Some \" Option", result[2]);
|
||||
Assert.AreEqual("Foo=Bar", result[3]);
|
||||
Assert.AreEqual("\\", result[4]);
|
||||
Assert.AreEqual("/help", result[5]);
|
||||
Assert.AreEqual("\\backslash", result[6]);
|
||||
Assert.AreEqual("\"escapedquote", result[7]);
|
||||
Assert.AreEqual("\\test", result[8]);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldReadArgs()
|
||||
{
|
||||
// arrange
|
||||
var parser = new CommandLineParser();
|
||||
|
||||
// act
|
||||
parser.ReadArgs("Option1 \"Option 2\"");
|
||||
string[] args = parser.GetType().GetField("_args", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(parser) as string[];
|
||||
|
||||
// assert
|
||||
Assert.IsNotNull(args);
|
||||
Assert.AreEqual(2, args.Length);
|
||||
|
||||
Assert.AreEqual("Option1", args[0]);
|
||||
Assert.AreEqual("Option 2", args[1]);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldRegisterOptions()
|
||||
{
|
||||
// arrange
|
||||
var parser = new CommandLineParser();
|
||||
|
||||
// act
|
||||
parser.RegisterOption("opt1");
|
||||
parser.RegisterOption("opt2", 1);
|
||||
parser.RegisterOption("opt3", 2).Required().Single();
|
||||
parser.RegisterOption("opt4").Alias("option4");
|
||||
|
||||
var options = parser.GetType().GetField("_options", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(parser) as List<Option>;
|
||||
|
||||
// assert
|
||||
Assert.IsNotNull(options);
|
||||
Assert.AreEqual(4, options.Count);
|
||||
|
||||
Assert.AreEqual(1, options.ElementAt(0).Names.Count);
|
||||
Assert.AreEqual("opt1", options.ElementAt(0).Names.First());
|
||||
Assert.AreEqual(0, options.ElementAt(0).ParameterCount);
|
||||
Assert.IsFalse(options.ElementAt(0).IsSingle);
|
||||
Assert.IsFalse(options.ElementAt(0).IsRequired);
|
||||
|
||||
Assert.AreEqual(1, options.ElementAt(1).Names.Count);
|
||||
Assert.AreEqual("opt2", options.ElementAt(1).Names.First());
|
||||
Assert.AreEqual(1, options.ElementAt(1).ParameterCount);
|
||||
Assert.IsFalse(options.ElementAt(1).IsSingle);
|
||||
Assert.IsFalse(options.ElementAt(1).IsRequired);
|
||||
|
||||
Assert.AreEqual(1, options.ElementAt(2).Names.Count);
|
||||
Assert.AreEqual("opt3", options.ElementAt(2).Names.First());
|
||||
Assert.AreEqual(2, options.ElementAt(2).ParameterCount);
|
||||
Assert.IsTrue(options.ElementAt(2).IsSingle);
|
||||
Assert.IsTrue(options.ElementAt(2).IsRequired);
|
||||
|
||||
Assert.AreEqual(2, options.ElementAt(3).Names.Count);
|
||||
Assert.AreEqual("opt4", options.ElementAt(3).Names.First());
|
||||
Assert.AreEqual("option4", options.ElementAt(3).Names.Last());
|
||||
Assert.AreEqual(0, options.ElementAt(3).ParameterCount);
|
||||
Assert.IsFalse(options.ElementAt(3).IsSingle);
|
||||
Assert.IsFalse(options.ElementAt(3).IsRequired);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldParse()
|
||||
{
|
||||
// arrange
|
||||
string argString = "/opt1 /opt2:two -opt3=three1 three2 --opt4:four /test:done -- foo bar";
|
||||
|
||||
var parser = new CommandLineParser();
|
||||
parser.RegisterOption("opt1");
|
||||
parser.RegisterOption("opt2", 1);
|
||||
parser.RegisterOption("opt3", 2);
|
||||
parser.RegisterOption("opt4", 1);
|
||||
var opt = parser.RegisterOption("notUsed");
|
||||
parser.RegisterOption("testing", 1);
|
||||
|
||||
parser.ReadArgs(argString);
|
||||
|
||||
// act
|
||||
parser.Parse();
|
||||
|
||||
// assert
|
||||
Assert.IsFalse(opt.IsSet);
|
||||
Assert.AreEqual(7, parser.Arguments.Length);
|
||||
Assert.AreEqual(2, parser.FreeArguments.Length);
|
||||
|
||||
Assert.AreEqual("foo", parser.FreeArguments.First());
|
||||
Assert.AreEqual("bar", parser.FreeArguments.Last());
|
||||
|
||||
Assert.IsTrue(parser.Arguments.ElementAt(0).Option.IsSet);
|
||||
Assert.IsNull(parser.Arguments.ElementAt(0).Option.Value);
|
||||
Assert.AreEqual(0, parser.Arguments.ElementAt(0).Values.Length);
|
||||
|
||||
Assert.IsTrue(parser.Arguments.ElementAt(1).Option.IsSet);
|
||||
Assert.AreEqual("two", parser.Arguments.ElementAt(1).Option.Value);
|
||||
Assert.AreEqual(1, parser.Arguments.ElementAt(1).Values.Length);
|
||||
Assert.AreEqual("two", parser.Arguments.ElementAt(1).Values.First());
|
||||
|
||||
Assert.IsTrue(parser.Arguments.ElementAt(2).Option.IsSet);
|
||||
Assert.AreEqual("three1", parser.Arguments.ElementAt(2).Option.Value);
|
||||
Assert.AreEqual(2, parser.Arguments.ElementAt(2).Values.Length);
|
||||
Assert.AreEqual("three1", parser.Arguments.ElementAt(2).Values.First());
|
||||
Assert.AreEqual("three2", parser.Arguments.ElementAt(2).Values.Last());
|
||||
|
||||
Assert.IsTrue(parser.Arguments.ElementAt(3).Option.IsSet);
|
||||
Assert.AreEqual("four", parser.Arguments.ElementAt(3).Option.Value);
|
||||
Assert.AreEqual(1, parser.Arguments.ElementAt(3).Values.Length);
|
||||
Assert.AreEqual("four", parser.Arguments.ElementAt(3).Values.First());
|
||||
|
||||
Assert.IsTrue(parser.Arguments.ElementAt(4).Option.IsSet);
|
||||
Assert.AreEqual("testing", parser.Arguments.ElementAt(4).Option.Names.First());
|
||||
Assert.AreEqual("done", parser.Arguments.ElementAt(4).Option.Value);
|
||||
Assert.AreEqual(1, parser.Arguments.ElementAt(4).Values.Length);
|
||||
Assert.AreEqual("done", parser.Arguments.ElementAt(4).Values.First());
|
||||
|
||||
Assert.IsNull(parser.Arguments.ElementAt(5).Option);
|
||||
Assert.AreEqual("foo", parser.Arguments.ElementAt(5).Value);
|
||||
Assert.AreEqual(1, parser.Arguments.ElementAt(5).Values.Length);
|
||||
|
||||
Assert.IsNull(parser.Arguments.ElementAt(6).Option);
|
||||
Assert.AreEqual("bar", parser.Arguments.ElementAt(6).Value);
|
||||
Assert.AreEqual(1, parser.Arguments.ElementAt(6).Values.Length);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldExecuteOptionActionOnParse()
|
||||
{
|
||||
// arrange
|
||||
Argument actionArgument = null;
|
||||
|
||||
string[] args = ["/run", "--opt"];
|
||||
var parser = new CommandLineParser();
|
||||
parser.RegisterOption("opt").Required();
|
||||
parser.RegisterOption("run").Do(arg => actionArgument = arg);
|
||||
|
||||
// act
|
||||
parser.Parse(args);
|
||||
|
||||
// assert
|
||||
Assert.IsNotNull(actionArgument);
|
||||
Assert.IsNotNull(actionArgument.Option);
|
||||
Assert.AreEqual("run", actionArgument.Option.Names.First());
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldReturnSetOptions()
|
||||
{
|
||||
// arrange
|
||||
string argString = "/Opt1 --opt3";
|
||||
|
||||
var parser = new CommandLineParser();
|
||||
parser.ReadArgs(argString);
|
||||
|
||||
parser.RegisterOption("opt1");
|
||||
parser.RegisterOption("opt2");
|
||||
parser.RegisterOption("opt3");
|
||||
|
||||
// act
|
||||
var opts = parser.SetOptions;
|
||||
|
||||
// assert
|
||||
Assert.AreEqual(2, opts.Length);
|
||||
|
||||
Assert.AreEqual("opt1", opts.First().Names.First());
|
||||
Assert.AreEqual("opt3", opts.Last().Names.First());
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(ArgumentNullException))]
|
||||
public void ShouldThrowExceptionOnNullArgs()
|
||||
{
|
||||
string[] args = null;
|
||||
var parser = new CommandLineParser();
|
||||
|
||||
// act
|
||||
parser.Parse(args);
|
||||
|
||||
// assert - ArgumentNullException
|
||||
Assert.Fail();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(Exception))]
|
||||
public void ShouldThrowExceptionOnMultipleAutocomplete()
|
||||
{
|
||||
// arrange
|
||||
string[] args = ["/Opt:on"];
|
||||
var parser = new CommandLineParser
|
||||
{
|
||||
IsCaseSensitive = true
|
||||
};
|
||||
parser.RegisterOption("Option1", 1);
|
||||
parser.RegisterOption("Option2", 1);
|
||||
|
||||
// act
|
||||
parser.Parse(args);
|
||||
|
||||
// assert - Exception
|
||||
Assert.Fail();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(Exception))]
|
||||
public void ShouldThrowExceptionOnMissingOption()
|
||||
{
|
||||
// arrange
|
||||
string[] args = ["/Option:on"];
|
||||
var parser = new CommandLineParser
|
||||
{
|
||||
AutoCompleteOptions = false
|
||||
};
|
||||
parser.RegisterOption("Opt1", 1);
|
||||
parser.RegisterOption("Opt2", 1);
|
||||
|
||||
// act
|
||||
parser.Parse(args);
|
||||
|
||||
// assert - Exception
|
||||
Assert.Fail();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(Exception))]
|
||||
public void ShouldTrhowExceptionOnDuplicateOption()
|
||||
{
|
||||
// arrange
|
||||
string[] args = ["/Opt:on", "--opt=off"];
|
||||
var parser = new CommandLineParser();
|
||||
parser.RegisterOption("opt", 1).Single();
|
||||
|
||||
// act
|
||||
parser.Parse(args);
|
||||
|
||||
// assert - Exception
|
||||
Assert.Fail();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(Exception))]
|
||||
public void ShouldThrowExceptionOnMissingArgument()
|
||||
{
|
||||
// arrange
|
||||
string[] args = ["/Option"];
|
||||
var parser = new CommandLineParser();
|
||||
parser.RegisterOption("option", 1);
|
||||
|
||||
// act
|
||||
parser.Parse(args);
|
||||
|
||||
// assert - Exception
|
||||
Assert.Fail();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(Exception))]
|
||||
public void ShouldThrowExceptionForMissingRequiredOption()
|
||||
{
|
||||
// arrange
|
||||
string[] args = ["/opt"];
|
||||
var parser = new CommandLineParser();
|
||||
parser.RegisterOption("opt").Required();
|
||||
parser.RegisterOption("foo").Required();
|
||||
|
||||
// act
|
||||
parser.Parse(args);
|
||||
|
||||
// assert - Exception
|
||||
Assert.Fail();
|
||||
}
|
||||
}
|
||||
}
|
||||
89
test/AMWD.Common.Tests/Cli/EnumerableWalkerTest.cs
Normal file
89
test/AMWD.Common.Tests/Cli/EnumerableWalkerTest.cs
Normal file
@@ -0,0 +1,89 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using AMWD.Common.Cli;
|
||||
|
||||
namespace AMWD.Common.Tests.Cli
|
||||
{
|
||||
[TestClass]
|
||||
public class EnumerableWalkerTest
|
||||
{
|
||||
private List<string> _list;
|
||||
|
||||
[TestInitialize]
|
||||
public void Initialize()
|
||||
{
|
||||
_list = ["one", "two", "three", "four"];
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(ArgumentNullException))]
|
||||
public void ShouldThrowExceptionOnNullReference()
|
||||
{
|
||||
// arrange
|
||||
|
||||
// act
|
||||
_ = new EnumerableWalker<object>(null);
|
||||
|
||||
// assert - ArgumentNullException
|
||||
Assert.Fail();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldReturnEnumerator()
|
||||
{
|
||||
// arrange
|
||||
var walker = new EnumerableWalker<string>(_list);
|
||||
|
||||
// act
|
||||
var enumerator = walker.GetEnumerator();
|
||||
|
||||
// assert
|
||||
Assert.IsNotNull(enumerator);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldReturnGenericEnumerator()
|
||||
{
|
||||
// arrange
|
||||
var walker = new EnumerableWalker<string>(_list);
|
||||
|
||||
// act
|
||||
var enumerator = ((IEnumerable<string>)walker).GetEnumerator();
|
||||
|
||||
// assert
|
||||
Assert.IsNotNull(enumerator);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldReturnItems()
|
||||
{
|
||||
// arrange
|
||||
var walker = new EnumerableWalker<string>(_list);
|
||||
_ = walker.GetEnumerator();
|
||||
|
||||
string[] items = new string[_list.Count];
|
||||
|
||||
// act
|
||||
for (int i = 0; i < _list.Count; i++)
|
||||
items[i] = walker.GetNext();
|
||||
|
||||
// assert
|
||||
for (int i = 0; i < _list.Count; i++)
|
||||
Assert.AreEqual(_list[i], items[i], $"Position {i} failed");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldReturnDefaultWhenNothingLeft()
|
||||
{
|
||||
// arrange
|
||||
var walker = new EnumerableWalker<string>(Array.Empty<string>());
|
||||
_ = walker.GetEnumerator();
|
||||
|
||||
// act
|
||||
string item = walker.GetNext();
|
||||
|
||||
// assert
|
||||
Assert.AreEqual(default, item);
|
||||
}
|
||||
}
|
||||
}
|
||||
58
test/AMWD.Common.Tests/Comparer/DomainComparerTest.cs
Normal file
58
test/AMWD.Common.Tests/Comparer/DomainComparerTest.cs
Normal file
@@ -0,0 +1,58 @@
|
||||
using AMWD.Common.Comparer;
|
||||
|
||||
namespace AMWD.Common.Tests.Comparer
|
||||
{
|
||||
[TestClass]
|
||||
public class DomainComparerTest
|
||||
{
|
||||
[DataTestMethod]
|
||||
[DataRow("int", "internal")]
|
||||
[DataRow("int", "dom.int")]
|
||||
[DataRow("a.ins", "a.int")]
|
||||
[DataRow("a.internal", "b.internal")]
|
||||
[DataRow("sub1.domain.internal", "sub2.domain.internal")]
|
||||
public void ShouldBeLessThan(string left, string right)
|
||||
{
|
||||
// Arrange
|
||||
var comparer = new DomainComparer();
|
||||
|
||||
// Act
|
||||
int result = comparer.Compare(left, right);
|
||||
|
||||
Assert.AreEqual(-1, result);
|
||||
}
|
||||
|
||||
[DataTestMethod]
|
||||
[DataRow("internal", "int")]
|
||||
[DataRow("dom.int", "int")]
|
||||
[DataRow("a.int", "a.ins")]
|
||||
[DataRow("b.internal", "a.internal")]
|
||||
[DataRow("sub2.domain.internal", "sub1.domain.internal")]
|
||||
public void ShouldBeGreaterThan(string left, string right)
|
||||
{
|
||||
// Arrange
|
||||
var comparer = new DomainComparer();
|
||||
|
||||
// Act
|
||||
int result = comparer.Compare(left, right);
|
||||
|
||||
Assert.AreEqual(1, result);
|
||||
}
|
||||
|
||||
[DataTestMethod]
|
||||
[DataRow("internal", "internal")]
|
||||
[DataRow("dom.int", "dom.int")]
|
||||
[DataRow("a.internal", "a.internal")]
|
||||
[DataRow("sub.domain.internal", "sub.domain.internal")]
|
||||
public void ShouldBeEqual(string left, string right)
|
||||
{
|
||||
// Arrange
|
||||
var comparer = new DomainComparer();
|
||||
|
||||
// Act
|
||||
int result = comparer.Compare(left, right);
|
||||
|
||||
Assert.AreEqual(0, result);
|
||||
}
|
||||
}
|
||||
}
|
||||
57
test/AMWD.Common.Tests/Comparer/IPAddressComparerTest.cs
Normal file
57
test/AMWD.Common.Tests/Comparer/IPAddressComparerTest.cs
Normal file
@@ -0,0 +1,57 @@
|
||||
using System.Net;
|
||||
using AMWD.Common.Comparer;
|
||||
|
||||
namespace AMWD.Common.Tests.Comparer
|
||||
{
|
||||
[TestClass]
|
||||
public class IPAddressComparerTest
|
||||
{
|
||||
[DataTestMethod]
|
||||
[DataRow("127.0.0.0", "127.0.0.1")]
|
||||
[DataRow("fe80::", "fe80::1")]
|
||||
[DataRow("::ffff:7f00:0", "127.0.0.1")]
|
||||
public void ShouldBeLessThan(string left, string right)
|
||||
{
|
||||
// Arrange
|
||||
var comparer = new IPAddressComparer();
|
||||
|
||||
// Act
|
||||
int result = comparer.Compare(IPAddress.Parse(left), IPAddress.Parse(right));
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual(-1, result);
|
||||
}
|
||||
|
||||
[DataTestMethod]
|
||||
[DataRow("127.0.0.1", "127.0.0.0")]
|
||||
[DataRow("fe80::1", "fe80::")]
|
||||
[DataRow("::ffff:7f00:1", "127.0.0.0")]
|
||||
public void ShouldBeGreaterThan(string left, string right)
|
||||
{
|
||||
// Arrange
|
||||
var comparer = new IPAddressComparer();
|
||||
|
||||
// Act
|
||||
int result = comparer.Compare(IPAddress.Parse(left), IPAddress.Parse(right));
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual(1, result);
|
||||
}
|
||||
|
||||
[DataTestMethod]
|
||||
[DataRow("127.0.0.1", "127.0.0.1")]
|
||||
[DataRow("fe80::1", "fe80::1")]
|
||||
[DataRow("::ffff:7f00:1", "127.0.0.1")]
|
||||
public void ShouldBeEqual(string left, string right)
|
||||
{
|
||||
// Arrange
|
||||
var comparer = new IPAddressComparer();
|
||||
|
||||
// Act
|
||||
int result = comparer.Compare(IPAddress.Parse(left), IPAddress.Parse(right));
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual(0, result);
|
||||
}
|
||||
}
|
||||
}
|
||||
66
test/AMWD.Common.Tests/Comparer/VersionStringComparerTest.cs
Normal file
66
test/AMWD.Common.Tests/Comparer/VersionStringComparerTest.cs
Normal file
@@ -0,0 +1,66 @@
|
||||
using AMWD.Common.Comparer;
|
||||
|
||||
namespace AMWD.Common.Tests.Comparer
|
||||
{
|
||||
[TestClass]
|
||||
public class VersionStringComparerTest
|
||||
{
|
||||
[DataTestMethod]
|
||||
[DataRow(null, "0")]
|
||||
[DataRow("", "0")]
|
||||
[DataRow("0", "1")]
|
||||
[DataRow("1.0", "1.1")]
|
||||
[DataRow("1.0.0", "1.0.1")]
|
||||
[DataRow("1.0.0.0", "1.0.0.1")]
|
||||
[DataRow("1.0.0.0-RC1", "1.0.0.0-RC2")]
|
||||
public void ShouldBeLessThan(string x, string y)
|
||||
{
|
||||
// Arrange
|
||||
var comparer = new VersionStringComparer();
|
||||
|
||||
// Act
|
||||
int result = comparer.Compare(x, y);
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual(-1, result);
|
||||
}
|
||||
|
||||
[DataTestMethod]
|
||||
[DataRow("0", null)]
|
||||
[DataRow("0", "")]
|
||||
[DataRow("1", "0")]
|
||||
[DataRow("1.1", "1.0")]
|
||||
[DataRow("1.0.1", "1.0.0")]
|
||||
[DataRow("1.0.0.1", "1.0.0.0")]
|
||||
[DataRow("1.0.0.0-RC2", "1.0.0.0-RC1")]
|
||||
public void ShouldBeGreaterThan(string x, string y)
|
||||
{
|
||||
// Arrange
|
||||
var comparer = new VersionStringComparer();
|
||||
|
||||
// Act
|
||||
int result = comparer.Compare(x, y);
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual(1, result);
|
||||
}
|
||||
|
||||
[DataTestMethod]
|
||||
[DataRow(null, null)]
|
||||
[DataRow("", "")]
|
||||
[DataRow("1", "1")]
|
||||
[DataRow("1.2.3", "1.2.3")]
|
||||
[DataRow("1.2.3-alpha", "1.2.3-alpha")]
|
||||
public void ShouldBeEqual(string x, string y)
|
||||
{
|
||||
// Arrange
|
||||
var comparer = new VersionStringComparer();
|
||||
|
||||
// Act
|
||||
int result = comparer.Compare(x, y);
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual(0, result);
|
||||
}
|
||||
}
|
||||
}
|
||||
142
test/AMWD.Common.Tests/Extensions/CollectionExtensionsTest.cs
Normal file
142
test/AMWD.Common.Tests/Extensions/CollectionExtensionsTest.cs
Normal file
@@ -0,0 +1,142 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace AMWD.Common.Tests.Extensions
|
||||
{
|
||||
[TestClass]
|
||||
public class CollectionExtensionsTest
|
||||
{
|
||||
[TestMethod]
|
||||
public void ShouldAddItem()
|
||||
{
|
||||
// Arrange
|
||||
var item = new TestItem { Number = 10, Text = "Ten" };
|
||||
ICollection<TestItem> list = new List<TestItem>
|
||||
{
|
||||
new() {
|
||||
Number = 1,
|
||||
Text = "One"
|
||||
}
|
||||
};
|
||||
|
||||
// Act
|
||||
list.AddIfNotNull(item);
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual(2, list.Count);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldNotAddItem()
|
||||
{
|
||||
// Arrange
|
||||
TestItem item = null;
|
||||
ICollection<TestItem> list = new List<TestItem>
|
||||
{
|
||||
new() {
|
||||
Number = 1,
|
||||
Text = "One"
|
||||
}
|
||||
};
|
||||
|
||||
// Act
|
||||
list.AddIfNotNull(item);
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual(1, list.Count);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(ArgumentNullException))]
|
||||
public void ShouldThrowArgumentNullExceptionForNullList()
|
||||
{
|
||||
// Arrange
|
||||
var item = new TestItem { Number = 10, Text = "Ten" };
|
||||
ICollection<TestItem> list = null;
|
||||
|
||||
// Act
|
||||
list.AddIfNotNull(item);
|
||||
|
||||
// Assert - ArgumentNullException
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldAddRange()
|
||||
{
|
||||
// Arrange
|
||||
ICollection<TestItem> items = new List<TestItem>
|
||||
{
|
||||
new() { Number = 10, Text = "Ten" },
|
||||
new() { Number = 11, Text = "Eleven" },
|
||||
};
|
||||
ICollection<TestItem> list = new List<TestItem>
|
||||
{
|
||||
new() { Number = 1, Text = "One" },
|
||||
};
|
||||
|
||||
// Act
|
||||
list.AddRange(items);
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual(3, list.Count);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(ArgumentNullException))]
|
||||
public void ShouldThrowArgumentNullExceptionForList()
|
||||
{
|
||||
// Arrange
|
||||
ICollection<TestItem> items = new List<TestItem>
|
||||
{
|
||||
new() { Number = 10, Text = "Ten" },
|
||||
new() { Number = 11, Text = "Eleven" },
|
||||
};
|
||||
ICollection<TestItem> list = null;
|
||||
|
||||
// Act
|
||||
list.AddRange(items);
|
||||
|
||||
// Assert - ArgumentNullException
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(ArgumentNullException))]
|
||||
public void ShouldThrowArgumentNullExceptionForItems()
|
||||
{
|
||||
// Arrange
|
||||
ICollection<TestItem> items = null;
|
||||
ICollection<TestItem> list = new List<TestItem>
|
||||
{
|
||||
new() { Number = 1, Text = "One" },
|
||||
};
|
||||
|
||||
// Act
|
||||
list.AddRange(items);
|
||||
|
||||
// Assert - ArgumentNullException
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldNotAddRange()
|
||||
{
|
||||
// Arrange
|
||||
ICollection<TestItem> list = new List<TestItem>
|
||||
{
|
||||
new() { Number = 1, Text = "One" },
|
||||
};
|
||||
|
||||
// Act
|
||||
list.AddRange(list);
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual(1, list.Count);
|
||||
}
|
||||
|
||||
private class TestItem
|
||||
{
|
||||
public int Number { get; set; }
|
||||
|
||||
public string Text { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
using System.Security.Cryptography;
|
||||
|
||||
namespace AMWD.Common.Tests.Extensions
|
||||
{
|
||||
[TestClass]
|
||||
public class CryptographyHelperExtensionsTest
|
||||
{
|
||||
[TestMethod]
|
||||
public void ShouldReturnMd5Hash()
|
||||
{
|
||||
// arrange
|
||||
string str = "Hello World!";
|
||||
byte[] bytes = [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 = [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 = [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 = [0xaf, 0xfe];
|
||||
|
||||
// act
|
||||
string strHash = str.Sha512();
|
||||
string byteHash = bytes.Sha512();
|
||||
|
||||
// assert
|
||||
Assert.AreEqual("861844d6704e8573fec34d967e20bcfef3d424cf48be04e6dc08f2bd58c729743371015ead891cc3cf1c9d34b49264b510751b1ff9e537937bc46b5d6ff4ecc8", strHash);
|
||||
Assert.AreEqual("591098c5d470a09f0ff48a4fdb7769ab89f803eae9e23b6f9f69dd228cca46c074bbc11a5fceaa8a5f48d14d2bf19a83a629266c2c5b7d9ef34623b64cb2f8e7", byteHash);
|
||||
}
|
||||
}
|
||||
}
|
||||
362
test/AMWD.Common.Tests/Extensions/DateTimeExtensionsTest.cs
Normal file
362
test/AMWD.Common.Tests/Extensions/DateTimeExtensionsTest.cs
Normal file
@@ -0,0 +1,362 @@
|
||||
using System;
|
||||
using AMWD.Common.Tests.Utils;
|
||||
|
||||
namespace AMWD.Common.Tests.Extensions
|
||||
{
|
||||
[TestClass]
|
||||
public class DateTimeExtensionsTest
|
||||
{
|
||||
[TestMethod]
|
||||
public void ShouldReturnUtc()
|
||||
{
|
||||
// arrange
|
||||
var timeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById("Europe/Berlin");
|
||||
using var _ = 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);
|
||||
|
||||
// act
|
||||
var utcCorrected = utc.AsUtc();
|
||||
var localCorrected = local.AsUtc();
|
||||
var unspecifiedCorrected = unspecified.AsUtc();
|
||||
|
||||
// assert
|
||||
Assert.AreEqual(DateTimeKind.Utc, utcCorrected.Kind);
|
||||
Assert.AreEqual(DateTimeKind.Utc, localCorrected.Kind);
|
||||
Assert.AreEqual(DateTimeKind.Utc, unspecifiedCorrected.Kind);
|
||||
|
||||
Assert.AreEqual(utc, utcCorrected);
|
||||
Assert.AreEqual(utc.AddHours(-1), localCorrected);
|
||||
Assert.AreEqual(utc, unspecifiedCorrected);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldReturnLocal()
|
||||
{
|
||||
// arrange
|
||||
var timeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById("Europe/Berlin");
|
||||
using var _ = 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);
|
||||
|
||||
// act
|
||||
var utcCorrected = utc.AsLocal();
|
||||
var localCorrected = local.AsLocal();
|
||||
var unspecifiedCorrected = unspecified.AsLocal();
|
||||
|
||||
// assert
|
||||
Assert.AreEqual(DateTimeKind.Local, utcCorrected.Kind);
|
||||
Assert.AreEqual(DateTimeKind.Local, localCorrected.Kind);
|
||||
Assert.AreEqual(DateTimeKind.Local, unspecifiedCorrected.Kind);
|
||||
|
||||
Assert.AreEqual(local.AddHours(1), utcCorrected);
|
||||
Assert.AreEqual(local, localCorrected);
|
||||
Assert.AreEqual(local, unspecifiedCorrected);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldReturnCorrectUtcAlignmentDaylightSavingEnd()
|
||||
{
|
||||
// arrange
|
||||
var dateTime = new DateTime(2021, 10, 30, 12, 15, 30, 45, DateTimeKind.Utc);
|
||||
|
||||
var intervalThreeSeconds = TimeSpan.FromSeconds(3);
|
||||
var intervalSixMinutes = TimeSpan.FromMinutes(6);
|
||||
var intervalTwoHours = TimeSpan.FromHours(2);
|
||||
var intervalDay = TimeSpan.FromDays(1);
|
||||
|
||||
var offsetTwoMinutes = TimeSpan.FromMinutes(2);
|
||||
var offsetFourHours = TimeSpan.FromHours(4);
|
||||
|
||||
// act
|
||||
var diffThreeSeconds = intervalThreeSeconds.GetAlignedInterval(dateTime);
|
||||
var diffSixMinutes = intervalSixMinutes.GetAlignedInterval(dateTime);
|
||||
var diffTwoHours = intervalTwoHours.GetAlignedInterval(dateTime);
|
||||
var diffDay = intervalDay.GetAlignedInterval(dateTime);
|
||||
|
||||
var diffTwoHoursOffset = intervalTwoHours.GetAlignedInterval(dateTime, offsetTwoMinutes);
|
||||
var diffDayOffset = intervalDay.GetAlignedInterval(dateTime, offsetFourHours);
|
||||
|
||||
// assert
|
||||
Assert.AreEqual(TimeSpan.Parse("00:00:02.955"), diffThreeSeconds);
|
||||
Assert.AreEqual(TimeSpan.Parse("00:02:29.955"), diffSixMinutes);
|
||||
Assert.AreEqual(TimeSpan.Parse("01:44:29.955"), diffTwoHours);
|
||||
Assert.AreEqual(TimeSpan.Parse("11:44:29.955"), diffDay);
|
||||
|
||||
Assert.AreEqual(TimeSpan.Parse("01:46:29.955"), diffTwoHoursOffset);
|
||||
Assert.AreEqual(TimeSpan.Parse("15:44:29.955"), diffDayOffset); // must be the same whether daylight saving has ended
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldReturnCorrectUtcAlignmentDaylightSavingStart()
|
||||
{
|
||||
// arrange
|
||||
var dateTime = new DateTime(2022, 3, 26, 12, 15, 30, 45, DateTimeKind.Utc);
|
||||
|
||||
var intervalThreeSeconds = TimeSpan.FromSeconds(3);
|
||||
var intervalSixMinutes = TimeSpan.FromMinutes(6);
|
||||
var intervalTwoHours = TimeSpan.FromHours(2);
|
||||
var intervalDay = TimeSpan.FromDays(1);
|
||||
|
||||
var offsetTwoMinutes = TimeSpan.FromMinutes(2);
|
||||
var offsetFourHours = TimeSpan.FromHours(4);
|
||||
|
||||
// act
|
||||
var diffThreeSeconds = intervalThreeSeconds.GetAlignedInterval(dateTime);
|
||||
var diffSixMinutes = intervalSixMinutes.GetAlignedInterval(dateTime);
|
||||
var diffTwoHours = intervalTwoHours.GetAlignedInterval(dateTime);
|
||||
var diffDay = intervalDay.GetAlignedInterval(dateTime);
|
||||
|
||||
var diffTwoHoursOffset = intervalTwoHours.GetAlignedInterval(dateTime, offsetTwoMinutes);
|
||||
var diffDayOffset = intervalDay.GetAlignedInterval(dateTime, offsetFourHours);
|
||||
|
||||
// assert
|
||||
Assert.AreEqual(TimeSpan.Parse("00:00:02.955"), diffThreeSeconds);
|
||||
Assert.AreEqual(TimeSpan.Parse("00:02:29.955"), diffSixMinutes);
|
||||
Assert.AreEqual(TimeSpan.Parse("01:44:29.955"), diffTwoHours);
|
||||
Assert.AreEqual(TimeSpan.Parse("11:44:29.955"), diffDay);
|
||||
|
||||
Assert.AreEqual(TimeSpan.Parse("01:46:29.955"), diffTwoHoursOffset);
|
||||
Assert.AreEqual(TimeSpan.Parse("15:44:29.955"), diffDayOffset); // must be the same whether daylight saving has started
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldReturnCorrectLocalAlignmentDaylightSavingEnd()
|
||||
{
|
||||
// arrange
|
||||
var timeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById("Europe/Berlin");
|
||||
using var _ = TimeZoneInfoLocalMock.Create(timeZoneInfo);
|
||||
|
||||
var dateTime = new DateTime(2021, 10, 30, 12, 15, 30, 45, DateTimeKind.Local);
|
||||
|
||||
var intervalThreeSeconds = TimeSpan.FromSeconds(3);
|
||||
var intervalSixMinutes = TimeSpan.FromMinutes(6);
|
||||
var intervalTwoHours = TimeSpan.FromHours(2);
|
||||
var intervalDay = TimeSpan.FromDays(1);
|
||||
|
||||
var offsetTwoMinutes = TimeSpan.FromMinutes(2);
|
||||
var offsetFourHours = TimeSpan.FromHours(4);
|
||||
|
||||
// act
|
||||
var diffThreeSeconds = intervalThreeSeconds.GetAlignedInterval(dateTime);
|
||||
var diffSixMinutes = intervalSixMinutes.GetAlignedInterval(dateTime);
|
||||
var diffTwoHours = intervalTwoHours.GetAlignedInterval(dateTime);
|
||||
var diffDay = intervalDay.GetAlignedInterval(dateTime);
|
||||
|
||||
var diffTwoHoursOffset = intervalTwoHours.GetAlignedInterval(dateTime, offsetTwoMinutes);
|
||||
var diffDayOffset = intervalDay.GetAlignedInterval(dateTime, offsetFourHours);
|
||||
|
||||
// assert
|
||||
Assert.AreEqual(TimeSpan.Parse("00:00:02.955"), diffThreeSeconds);
|
||||
Assert.AreEqual(TimeSpan.Parse("00:02:29.955"), diffSixMinutes);
|
||||
Assert.AreEqual(TimeSpan.Parse("01:44:29.955"), diffTwoHours);
|
||||
Assert.AreEqual(TimeSpan.Parse("11:44:29.955"), diffDay);
|
||||
|
||||
Assert.AreEqual(TimeSpan.Parse("01:46:29.955"), diffTwoHoursOffset);
|
||||
Assert.AreEqual(TimeSpan.Parse("14:44:29.955"), diffDayOffset); // has to be minus one hour due to daylight saving ended
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldReturnCorrectLocalAlignmentDaylightSavingStart()
|
||||
{
|
||||
// arrange
|
||||
var timeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById("Europe/Berlin");
|
||||
using var _ = TimeZoneInfoLocalMock.Create(timeZoneInfo);
|
||||
|
||||
var dateTime = new DateTime(2022, 3, 26, 12, 15, 30, 45, DateTimeKind.Local);
|
||||
|
||||
var intervalThreeSeconds = TimeSpan.FromSeconds(3);
|
||||
var intervalSixMinutes = TimeSpan.FromMinutes(6);
|
||||
var intervalTwoHours = TimeSpan.FromHours(2);
|
||||
var intervalDay = TimeSpan.FromDays(1);
|
||||
|
||||
var offsetTwoMinutes = TimeSpan.FromMinutes(2);
|
||||
var offsetFourHours = TimeSpan.FromHours(4);
|
||||
|
||||
// act
|
||||
var diffThreeSeconds = intervalThreeSeconds.GetAlignedInterval(dateTime);
|
||||
var diffSixMinutes = intervalSixMinutes.GetAlignedInterval(dateTime);
|
||||
var diffTwoHours = intervalTwoHours.GetAlignedInterval(dateTime);
|
||||
var diffDay = intervalDay.GetAlignedInterval(dateTime);
|
||||
|
||||
var diffTwoHoursOffset = intervalTwoHours.GetAlignedInterval(dateTime, offsetTwoMinutes);
|
||||
var diffDayOffset = intervalDay.GetAlignedInterval(dateTime, offsetFourHours);
|
||||
|
||||
// assert
|
||||
Assert.AreEqual(TimeSpan.Parse("00:00:02.955"), diffThreeSeconds);
|
||||
Assert.AreEqual(TimeSpan.Parse("00:02:29.955"), diffSixMinutes);
|
||||
Assert.AreEqual(TimeSpan.Parse("01:44:29.955"), diffTwoHours);
|
||||
Assert.AreEqual(TimeSpan.Parse("11:44:29.955"), diffDay);
|
||||
|
||||
Assert.AreEqual(TimeSpan.Parse("01:46:29.955"), diffTwoHoursOffset);
|
||||
Assert.AreEqual(TimeSpan.Parse("16:44:29.955"), diffDayOffset); // has to be plus one hour due to daylight saving started
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldReturnCorrectAlignment()
|
||||
{
|
||||
// arrange
|
||||
var timeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById("Europe/Berlin");
|
||||
using var _ = TimeZoneInfoLocalMock.Create(timeZoneInfo);
|
||||
|
||||
var interval = TimeSpan.FromDays(1);
|
||||
|
||||
// act
|
||||
var intervalUtc = interval.GetAlignedIntervalUtc();
|
||||
var expectedUtc = DateTime.UtcNow.TimeOfDay;
|
||||
var intervalLocal = interval.GetAlignedIntervalLocal();
|
||||
var expectedLocal = DateTime.Now.TimeOfDay;
|
||||
|
||||
// assert
|
||||
Assert.AreEqual(expectedUtc.RoundToSecond(), (interval - intervalUtc).RoundToSecond());
|
||||
Assert.AreEqual(expectedLocal.RoundToSecond(), (interval - intervalLocal).RoundToSecond());
|
||||
|
||||
if (DateTime.Now.TimeOfDay < DateTime.UtcNow.TimeOfDay) // case when local time has new day but UTC not
|
||||
{
|
||||
Assert.AreEqual((DateTime.Now - DateTime.UtcNow).RoundToSecond(), interval - (intervalLocal - intervalUtc).RoundToSecond());
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.AreEqual((DateTime.Now - DateTime.UtcNow).RoundToSecond(), (intervalUtc - intervalLocal).RoundToSecond());
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldReturnCorrectShortStringForTimeSpan()
|
||||
{
|
||||
// arrange
|
||||
var timeSpan = TimeSpan.Parse("1.10:11:12.345");
|
||||
var negativeTimeSpan = TimeSpan.FromDays(-1.234);
|
||||
|
||||
// act
|
||||
string shortString = timeSpan.ToShortString(withMilliseconds: false);
|
||||
string shortStringWithMillis = timeSpan.ToShortString(withMilliseconds: true);
|
||||
string shortStringNegative = negativeTimeSpan.ToShortString(withMilliseconds: true);
|
||||
|
||||
// assert
|
||||
Assert.AreEqual("1d 10h 11m 12s", shortString);
|
||||
Assert.AreEqual("1d 10h 11m 12s 345ms", shortStringWithMillis);
|
||||
Assert.AreEqual("-1d 5h 36m 57s 600ms", shortStringNegative);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldRoundToSecond()
|
||||
{
|
||||
// arrange
|
||||
var timestampUp = new DateTime(2021, 11, 16, 20, 15, 30, 500);
|
||||
var timestampDown = new DateTime(2021, 11, 16, 20, 15, 30, 499);
|
||||
|
||||
var timespanUp = new TimeSpan(1, 2, 3, 4, 500);
|
||||
var timespanDown = new TimeSpan(1, 2, 3, 4, 499);
|
||||
|
||||
// act
|
||||
var timestampUpRounded = timestampUp.RoundToSecond();
|
||||
var timestampDownRounded = timestampDown.RoundToSecond();
|
||||
|
||||
var timespanUpRounded = timespanUp.RoundToSecond();
|
||||
var timespanDownRounded = timespanDown.RoundToSecond();
|
||||
|
||||
// assert
|
||||
Assert.AreNotEqual(timestampUp, timestampUpRounded);
|
||||
Assert.AreEqual(DateTime.Parse("2021-11-16 20:15:31.000"), timestampUpRounded);
|
||||
Assert.AreNotEqual(timestampDown, timestampDownRounded);
|
||||
Assert.AreEqual(DateTime.Parse("2021-11-16 20:15:30.000"), timestampDownRounded);
|
||||
|
||||
Assert.AreNotEqual(timespanUp, timespanUpRounded);
|
||||
Assert.AreEqual(TimeSpan.Parse("1.02:03:05.000"), timespanUpRounded);
|
||||
Assert.AreNotEqual(timespanDown, timespanDownRounded);
|
||||
Assert.AreEqual(TimeSpan.Parse("1.02:03:04.000"), timespanDownRounded);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldRoundToMinute()
|
||||
{
|
||||
// arrange
|
||||
var timestampUp = new DateTime(2021, 11, 16, 20, 15, 30, 0);
|
||||
var timestampDown = new DateTime(2021, 11, 16, 20, 15, 29, 999);
|
||||
|
||||
var timespanUp = new TimeSpan(1, 2, 3, 30, 0);
|
||||
var timespanDown = new TimeSpan(1, 2, 3, 29, 999);
|
||||
|
||||
// act
|
||||
var timestampUpRounded = timestampUp.RoundToMinute();
|
||||
var timestampDownRounded = timestampDown.RoundToMinute();
|
||||
|
||||
var timespanUpRounded = timespanUp.RoundToMinute();
|
||||
var timespanDownRounded = timespanDown.RoundToMinute();
|
||||
|
||||
// assert
|
||||
Assert.AreNotEqual(timestampUp, timestampUpRounded);
|
||||
Assert.AreEqual(DateTime.Parse("2021-11-16 20:16:00.000"), timestampUpRounded);
|
||||
Assert.AreNotEqual(timestampDown, timestampDownRounded);
|
||||
Assert.AreEqual(DateTime.Parse("2021-11-16 20:15:00.000"), timestampDownRounded);
|
||||
|
||||
Assert.AreNotEqual(timespanUp, timespanUpRounded);
|
||||
Assert.AreEqual(TimeSpan.Parse("1.02:04:00.000"), timespanUpRounded);
|
||||
Assert.AreNotEqual(timespanDown, timespanDownRounded);
|
||||
Assert.AreEqual(TimeSpan.Parse("1.02:03:00.000"), timespanDownRounded);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldRoundToHour()
|
||||
{
|
||||
// arrange
|
||||
var timestampUp = new DateTime(2021, 11, 16, 20, 30, 0, 0);
|
||||
var timestampDown = new DateTime(2021, 11, 16, 20, 29, 59, 999);
|
||||
|
||||
var timespanUp = new TimeSpan(1, 2, 30, 0, 0);
|
||||
var timespanDown = new TimeSpan(1, 2, 29, 59, 999);
|
||||
|
||||
// act
|
||||
var timestampUpRounded = timestampUp.RoundToHour();
|
||||
var timestampDownRounded = timestampDown.RoundToHour();
|
||||
|
||||
var timespanUpRounded = timespanUp.RoundToHour();
|
||||
var timespanDownRounded = timespanDown.RoundToHour();
|
||||
|
||||
// assert
|
||||
Assert.AreNotEqual(timestampUp, timestampUpRounded);
|
||||
Assert.AreEqual(DateTime.Parse("2021-11-16 21:00:00.000"), timestampUpRounded);
|
||||
Assert.AreNotEqual(timestampDown, timestampDownRounded);
|
||||
Assert.AreEqual(DateTime.Parse("2021-11-16 20:00:00.000"), timestampDownRounded);
|
||||
|
||||
Assert.AreNotEqual(timespanUp, timespanUpRounded);
|
||||
Assert.AreEqual(TimeSpan.Parse("1.03:00:00.000"), timespanUpRounded);
|
||||
Assert.AreNotEqual(timespanDown, timespanDownRounded);
|
||||
Assert.AreEqual(TimeSpan.Parse("1.02:00:00.000"), timespanDownRounded);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldRoundToDay()
|
||||
{
|
||||
// arrange
|
||||
var timestampUp = new DateTime(2021, 11, 16, 12, 0, 0, 0);
|
||||
var timestampDown = new DateTime(2021, 11, 16, 11, 59, 59, 999);
|
||||
|
||||
var timespanUp = new TimeSpan(1, 12, 0, 0, 0);
|
||||
var timespanDown = new TimeSpan(1, 11, 59, 59, 999);
|
||||
|
||||
// act
|
||||
var timestampUpRounded = timestampUp.RoundToDay();
|
||||
var timestampDownRounded = timestampDown.RoundToDay();
|
||||
|
||||
var timespanUpRounded = timespanUp.RoundToDay();
|
||||
var timespanDownRounded = timespanDown.RoundToDay();
|
||||
|
||||
// assert
|
||||
Assert.AreNotEqual(timestampUp, timestampUpRounded);
|
||||
Assert.AreEqual(DateTime.Parse("2021-11-17 00:00:00.000"), timestampUpRounded);
|
||||
Assert.AreNotEqual(timestampDown, timestampDownRounded);
|
||||
Assert.AreEqual(DateTime.Parse("2021-11-16 00:00:00.000"), timestampDownRounded);
|
||||
|
||||
Assert.AreNotEqual(timespanUp, timespanUpRounded);
|
||||
Assert.AreEqual(TimeSpan.Parse("2.00:00:00.000"), timespanUpRounded);
|
||||
Assert.AreNotEqual(timespanDown, timespanDownRounded);
|
||||
Assert.AreEqual(TimeSpan.Parse("1.00:00:00.000"), timespanDownRounded);
|
||||
}
|
||||
}
|
||||
}
|
||||
119
test/AMWD.Common.Tests/Extensions/EnumExtensionsTest.cs
Normal file
119
test/AMWD.Common.Tests/Extensions/EnumExtensionsTest.cs
Normal file
@@ -0,0 +1,119 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using AMWD.Common.Tests.Utils;
|
||||
using DescriptionAttribute = System.ComponentModel.DescriptionAttribute;
|
||||
|
||||
namespace AMWD.Common.Tests.Extensions
|
||||
{
|
||||
[TestClass]
|
||||
public class EnumExtensionsTest
|
||||
{
|
||||
[TestMethod]
|
||||
public void ShouldReturnEmptyList()
|
||||
{
|
||||
// arrange
|
||||
var enumValue = TestEnum.Two;
|
||||
|
||||
// act
|
||||
var customList = enumValue.GetAttributes<CustomMultipleAttribute>();
|
||||
var descriptionList = enumValue.GetAttributes<DescriptionAttribute>();
|
||||
|
||||
// assert
|
||||
Assert.IsNotNull(customList);
|
||||
Assert.IsFalse(customList.Any());
|
||||
Assert.IsNotNull(descriptionList);
|
||||
Assert.IsFalse(descriptionList.Any());
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldReturnList()
|
||||
{
|
||||
// arrange
|
||||
var enumValue = TestEnum.Zero;
|
||||
|
||||
// act
|
||||
var customList = enumValue.GetAttributes<CustomMultipleAttribute>();
|
||||
var descriptionList = enumValue.GetAttributes<DescriptionAttribute>();
|
||||
|
||||
// assert
|
||||
Assert.IsNotNull(customList);
|
||||
Assert.IsTrue(customList.Any());
|
||||
Assert.AreEqual(2, customList.Count());
|
||||
Assert.IsNotNull(descriptionList);
|
||||
Assert.IsTrue(descriptionList.Any());
|
||||
Assert.AreEqual(1, descriptionList.Count());
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldReturnNothing()
|
||||
{
|
||||
// arrange
|
||||
var enumValue = TestEnum.Two;
|
||||
|
||||
// act
|
||||
var customAttribute = enumValue.GetAttribute<CustomMultipleAttribute>();
|
||||
var descriptionAttribute = enumValue.GetAttribute<DescriptionAttribute>();
|
||||
|
||||
// assert
|
||||
Assert.IsNull(customAttribute);
|
||||
Assert.IsNull(descriptionAttribute);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldReturnFirstAttribute()
|
||||
{
|
||||
// arrange
|
||||
var enumValue = TestEnum.Zero;
|
||||
|
||||
// act
|
||||
var customAttribute = enumValue.GetAttribute<CustomMultipleAttribute>();
|
||||
var descriptionAttribute = enumValue.GetAttribute<DescriptionAttribute>();
|
||||
|
||||
// assert
|
||||
Assert.IsNotNull(customAttribute);
|
||||
Assert.AreEqual("nix", customAttribute.Name);
|
||||
Assert.IsNotNull(descriptionAttribute);
|
||||
Assert.AreEqual("Null", descriptionAttribute.Description);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldReturnDescriptionOrStringRepresentation()
|
||||
{
|
||||
// arrange
|
||||
var enumWithDescription = TestEnum.One;
|
||||
var enumWithoutDescripton = TestEnum.Two;
|
||||
|
||||
// act
|
||||
string description = enumWithDescription.GetDescription();
|
||||
string noDescription = enumWithoutDescripton.GetDescription();
|
||||
|
||||
// assert
|
||||
Assert.AreEqual("Eins", description);
|
||||
Assert.AreEqual(enumWithoutDescripton.ToString(), noDescription);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldReturnEmptyListOnNotDefinedEnumValue()
|
||||
{
|
||||
// arrange
|
||||
var notDefinedEnum = (TestEnum)10;
|
||||
|
||||
// act
|
||||
var list = notDefinedEnum.GetAttributes<DescriptionAttribute>();
|
||||
|
||||
// assert
|
||||
Assert.IsFalse(list.Any());
|
||||
}
|
||||
|
||||
internal enum TestEnum
|
||||
{
|
||||
[CustomMultiple("nix")]
|
||||
[CustomMultiple("Null")]
|
||||
[Description("Null")]
|
||||
Zero,
|
||||
[Description("Eins")]
|
||||
One,
|
||||
Two,
|
||||
}
|
||||
}
|
||||
}
|
||||
73
test/AMWD.Common.Tests/Extensions/ExceptionExtensionsTest.cs
Normal file
73
test/AMWD.Common.Tests/Extensions/ExceptionExtensionsTest.cs
Normal file
@@ -0,0 +1,73 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace AMWD.Common.Tests.Extensions
|
||||
{
|
||||
[TestClass]
|
||||
public class ExceptionExtensionsTest
|
||||
{
|
||||
[TestMethod]
|
||||
public void ShouldReturnExceptionMessage()
|
||||
{
|
||||
// arrange
|
||||
var exception = new Exception("This is a message.");
|
||||
|
||||
// act
|
||||
string message = exception.GetMessage();
|
||||
|
||||
// assert
|
||||
Assert.AreEqual(exception.Message, message);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldReturnInnerExceptionMessage()
|
||||
{
|
||||
// arrange
|
||||
var innerException = new Exception("Message from the inner side.");
|
||||
var outerException = new Exception("Message from the outer side.", innerException);
|
||||
|
||||
// act
|
||||
string message = outerException.GetMessage();
|
||||
|
||||
// assert
|
||||
Assert.AreEqual(innerException.Message, message);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldReturnRecursiveExceptionMessageFoInnerException()
|
||||
{
|
||||
// arrange
|
||||
var innerException = new Exception("Message from the inner side.");
|
||||
var outerException = new Exception("Message from the outer side. See the inner exception for details.", innerException);
|
||||
string expectedMessage = $"Message from the outer side. Message from the inner side.";
|
||||
|
||||
// act
|
||||
string message = outerException.GetRecursiveMessage();
|
||||
|
||||
// assert
|
||||
Assert.AreEqual(expectedMessage, message);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldReturnRecursiveExceptionMessageFoInnerExceptions()
|
||||
{
|
||||
// arrange
|
||||
var innerExceptions = new List<Exception>
|
||||
{
|
||||
new("Inner Exception 1."),
|
||||
new("Inner Exception 2. See the inner exception for details.", new Exception("Inner Exception of Exception 2.")),
|
||||
new("Inner Exception 3."),
|
||||
new("Inner Exception 4."),
|
||||
new("Inner Exception 5.")
|
||||
};
|
||||
var aggregateException = new AggregateException("Lots of exceptions.", innerExceptions);
|
||||
string expectedMessage = "Inner Exception 1. Inner Exception 2. Inner Exception of Exception 2. Inner Exception 3.";
|
||||
|
||||
// act
|
||||
string message = aggregateException.GetRecursiveMessage();
|
||||
|
||||
// assert
|
||||
Assert.AreEqual(expectedMessage, message);
|
||||
}
|
||||
}
|
||||
}
|
||||
86
test/AMWD.Common.Tests/Extensions/IPAddressExtensionsTest.cs
Normal file
86
test/AMWD.Common.Tests/Extensions/IPAddressExtensionsTest.cs
Normal file
@@ -0,0 +1,86 @@
|
||||
using System.Net;
|
||||
|
||||
namespace AMWD.Common.Tests.Extensions
|
||||
{
|
||||
[TestClass]
|
||||
public class IPAddressExtensionsTest
|
||||
{
|
||||
[TestMethod]
|
||||
public void ShouldIncrementLastByte()
|
||||
{
|
||||
// arrange
|
||||
var ipAddress = IPAddress.Parse("192.168.178.22");
|
||||
|
||||
// act
|
||||
var incremented = ipAddress.Increment();
|
||||
|
||||
// assert
|
||||
Assert.AreEqual("192.168.178.23", incremented.ToString());
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldIncrementAllBytes()
|
||||
{
|
||||
// arrange
|
||||
var ipAddress = IPAddress.Parse("192.255.255.255");
|
||||
|
||||
// act
|
||||
var incremented = ipAddress.Increment();
|
||||
|
||||
// assert
|
||||
Assert.AreEqual("193.0.0.0", incremented.ToString());
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldIncrementOverflow()
|
||||
{
|
||||
// arrange
|
||||
var ipAddress = IPAddress.Parse("255.255.255.255");
|
||||
|
||||
// act
|
||||
var incremented = ipAddress.Increment();
|
||||
|
||||
// assert
|
||||
Assert.AreEqual("0.0.0.0", incremented.ToString());
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldDecrementLastByte()
|
||||
{
|
||||
// arrange
|
||||
var ipAddress = IPAddress.Parse("192.168.178.22");
|
||||
|
||||
// act
|
||||
var decremented = ipAddress.Decrement();
|
||||
|
||||
// assert
|
||||
Assert.AreEqual("192.168.178.21", decremented.ToString());
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldDecrementAllBytes()
|
||||
{
|
||||
// arrange
|
||||
var ipAddress = IPAddress.Parse("192.0.0.0");
|
||||
|
||||
// act
|
||||
var decremented = ipAddress.Decrement();
|
||||
|
||||
// assert
|
||||
Assert.AreEqual("191.255.255.255", decremented.ToString());
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldDecrementUnderflow()
|
||||
{
|
||||
// arrange
|
||||
var ipAddress = IPAddress.Parse("0.0.0.0");
|
||||
|
||||
// act
|
||||
var decremented = ipAddress.Decrement();
|
||||
|
||||
// assert
|
||||
Assert.AreEqual("255.255.255.255", decremented.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
339
test/AMWD.Common.Tests/Extensions/JsonExtensionsTest.cs
Normal file
339
test/AMWD.Common.Tests/Extensions/JsonExtensionsTest.cs
Normal file
@@ -0,0 +1,339 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using AMWD.Common.Tests.Utils;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace AMWD.Common.Tests.Extensions
|
||||
{
|
||||
[TestClass]
|
||||
public class JsonExtensionsTest
|
||||
{
|
||||
[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 = ["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);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldReturnNull()
|
||||
{
|
||||
// arrange
|
||||
object obj = null;
|
||||
IEnumerable list = null;
|
||||
JObject jObj = null;
|
||||
|
||||
// act
|
||||
var objTest = obj.ConvertToJObject();
|
||||
var listTest = list.ConvertToJArray();
|
||||
object getTest = jObj.GetValue<object>("Nothing");
|
||||
|
||||
// assert
|
||||
Assert.IsNull(objTest);
|
||||
Assert.IsNull(listTest);
|
||||
Assert.IsNull(getTest);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldHandleSerializationError()
|
||||
{
|
||||
// arrange
|
||||
var obj = new JsonErrorClass();
|
||||
|
||||
// act
|
||||
string json = obj.SerializeJson();
|
||||
|
||||
// assert
|
||||
Assert.AreEqual("{}", json);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,219 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace AMWD.Common.Tests.Extensions
|
||||
{
|
||||
[TestClass]
|
||||
public class ReaderWriterLockSlimExtensionsTest
|
||||
{
|
||||
[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 ShouldAllowWriteLockAfterUpgradableReadLock()
|
||||
{
|
||||
// arrange
|
||||
var rwLockUsing = new ReaderWriterLockSlim();
|
||||
var rwLockClassic = new ReaderWriterLockSlim();
|
||||
|
||||
// act
|
||||
using var disposableReadUsing = rwLockUsing.GetUpgradeableReadLock();
|
||||
using (rwLockUsing.GetWriteLock())
|
||||
{
|
||||
// assert
|
||||
Assert.IsTrue(rwLockUsing.IsUpgradeableReadLockHeld);
|
||||
Assert.IsTrue(rwLockUsing.IsWriteLockHeld);
|
||||
}
|
||||
// assert
|
||||
Assert.IsTrue(rwLockUsing.IsUpgradeableReadLockHeld);
|
||||
Assert.IsFalse(rwLockUsing.IsWriteLockHeld);
|
||||
|
||||
// act
|
||||
using (rwLockClassic.GetUpgradeableReadLock())
|
||||
{
|
||||
rwLockClassic.EnterWriteLock();
|
||||
|
||||
// assert
|
||||
Assert.IsTrue(rwLockClassic.IsUpgradeableReadLockHeld);
|
||||
Assert.IsTrue(rwLockClassic.IsWriteLockHeld);
|
||||
}
|
||||
// assert
|
||||
Assert.IsFalse(rwLockClassic.IsUpgradeableReadLockHeld);
|
||||
Assert.IsFalse(rwLockClassic.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 disposableWrite = rwLock.GetWriteLock();
|
||||
|
||||
// assert
|
||||
Assert.IsNotNull(disposableRead);
|
||||
Assert.IsNotNull(disposableWrite);
|
||||
Assert.IsFalse(rwLock.IsReadLockHeld);
|
||||
Assert.IsTrue(rwLock.IsUpgradeableReadLockHeld);
|
||||
Assert.IsTrue(rwLock.IsWriteLockHeld);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldGetTimeoutOnReadLock()
|
||||
{
|
||||
// arrange
|
||||
var rwLock = new ReaderWriterLockSlim();
|
||||
bool isTimeout = false;
|
||||
|
||||
// act
|
||||
using var disposableRead = rwLock.GetWriteLock();
|
||||
var awaitableTask = Task.Run(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
using var disposableRead = rwLock.GetReadLock(10);
|
||||
Assert.Fail();
|
||||
}
|
||||
catch (TimeoutException)
|
||||
{
|
||||
isTimeout = true;
|
||||
}
|
||||
catch (Exception)
|
||||
{ /* keep it quiet */ }
|
||||
});
|
||||
awaitableTask.Wait();
|
||||
|
||||
// assert
|
||||
Assert.IsTrue(isTimeout);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldGetTimeoutOnUpgradeableReadLock()
|
||||
{
|
||||
// arrange
|
||||
var rwLock = new ReaderWriterLockSlim();
|
||||
bool isTimeout = false;
|
||||
|
||||
// act
|
||||
using var disposableRead = rwLock.GetWriteLock();
|
||||
var awaitableTask = Task.Run(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
using var disposableRead = rwLock.GetUpgradeableReadLock(10);
|
||||
Assert.Fail();
|
||||
}
|
||||
catch (TimeoutException)
|
||||
{
|
||||
isTimeout = true;
|
||||
}
|
||||
catch (Exception)
|
||||
{ /* keep it quiet */ }
|
||||
});
|
||||
awaitableTask.Wait();
|
||||
|
||||
// assert
|
||||
Assert.IsTrue(isTimeout);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldGetTimeoutOnWriteLock()
|
||||
{
|
||||
// arrange
|
||||
var rwLock = new ReaderWriterLockSlim();
|
||||
bool isTimeout = false;
|
||||
|
||||
// act
|
||||
using var disposableRead = rwLock.GetReadLock();
|
||||
var awaitableTask = Task.Run(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
using var disposableRead = rwLock.GetWriteLock(10);
|
||||
Assert.Fail();
|
||||
}
|
||||
catch (TimeoutException)
|
||||
{
|
||||
isTimeout = true;
|
||||
}
|
||||
catch (Exception)
|
||||
{ /* keep it quiet */ }
|
||||
});
|
||||
awaitableTask.Wait();
|
||||
|
||||
// assert
|
||||
Assert.IsTrue(isTimeout);
|
||||
}
|
||||
}
|
||||
}
|
||||
137
test/AMWD.Common.Tests/Extensions/StreamExtensionsTest.cs
Normal file
137
test/AMWD.Common.Tests/Extensions/StreamExtensionsTest.cs
Normal file
@@ -0,0 +1,137 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace AMWD.Common.Tests.Extensions
|
||||
{
|
||||
[TestClass]
|
||||
public class StreamExtensionsTest
|
||||
{
|
||||
[TestMethod]
|
||||
public void ShouldReadLineFromStreamSynchronous()
|
||||
{
|
||||
// arrange
|
||||
var sb = new StringBuilder();
|
||||
sb.AppendLine("First Line");
|
||||
sb.AppendLine("Second Line");
|
||||
byte[] buffer = Encoding.UTF8.GetBytes(sb.ToString().Trim());
|
||||
var stream = new MemoryStream(buffer);
|
||||
|
||||
// act
|
||||
string line = stream.ReadLine();
|
||||
|
||||
// assert
|
||||
Assert.AreEqual("First Line", line);
|
||||
|
||||
stream.Dispose();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldReadUntilEndAsLineSynchronous()
|
||||
{
|
||||
// arrange
|
||||
byte[] buffer = Encoding.UTF8.GetBytes("Single Line");
|
||||
var stream = new MemoryStream(buffer);
|
||||
|
||||
// act
|
||||
string line = stream.ReadLine();
|
||||
|
||||
// assert
|
||||
Assert.AreEqual("Single Line", line);
|
||||
|
||||
stream.Dispose();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldReturnNullWhenNotReadableSynchronous()
|
||||
{
|
||||
// arrange
|
||||
var stream = new WriteOnlyStream();
|
||||
|
||||
// act
|
||||
string line = stream.ReadLine();
|
||||
|
||||
// assert
|
||||
Assert.IsNull(line);
|
||||
|
||||
stream.Dispose();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task ShouldReadLineFromStreamAsynchronous()
|
||||
{
|
||||
// arrange
|
||||
var sb = new StringBuilder();
|
||||
sb.AppendLine("First Line");
|
||||
sb.AppendLine("Second Line");
|
||||
byte[] buffer = Encoding.UTF8.GetBytes(sb.ToString().Trim());
|
||||
var stream = new MemoryStream(buffer);
|
||||
|
||||
// act
|
||||
string line = await stream.ReadLineAsync();
|
||||
|
||||
// assert
|
||||
Assert.AreEqual("First Line", line);
|
||||
|
||||
stream.Dispose();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task ShouldReadUntilEndAsLineAsynchronous()
|
||||
{
|
||||
// arrange
|
||||
byte[] buffer = Encoding.UTF8.GetBytes("Single Line");
|
||||
var stream = new MemoryStream(buffer);
|
||||
|
||||
// act
|
||||
string line = await stream.ReadLineAsync();
|
||||
|
||||
// assert
|
||||
Assert.AreEqual("Single Line", line);
|
||||
|
||||
stream.Dispose();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task ShouldReturnNullWhenNotReadableAsynchronous()
|
||||
{
|
||||
// arrange
|
||||
var stream = new WriteOnlyStream();
|
||||
|
||||
// act
|
||||
string line = await stream.ReadLineAsync();
|
||||
|
||||
// assert
|
||||
Assert.IsNull(line);
|
||||
|
||||
stream.Dispose();
|
||||
}
|
||||
|
||||
private class WriteOnlyStream : Stream
|
||||
{
|
||||
public override bool CanRead => false;
|
||||
|
||||
public override bool CanSeek => false;
|
||||
|
||||
public override bool CanWrite => true;
|
||||
|
||||
public override long Length => 0;
|
||||
|
||||
public override long Position { get => 0; set => throw new NotImplementedException(); }
|
||||
|
||||
public override void Flush()
|
||||
{ }
|
||||
|
||||
public override int Read(byte[] buffer, int offset, int count) => 0;
|
||||
|
||||
public override long Seek(long offset, SeekOrigin origin) => 0;
|
||||
|
||||
public override void SetLength(long value)
|
||||
{ }
|
||||
|
||||
public override void Write(byte[] buffer, int offset, int count)
|
||||
{ }
|
||||
}
|
||||
}
|
||||
}
|
||||
351
test/AMWD.Common.Tests/Extensions/StringExtensionsTest.cs
Normal file
351
test/AMWD.Common.Tests/Extensions/StringExtensionsTest.cs
Normal file
@@ -0,0 +1,351 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
|
||||
namespace AMWD.Common.Tests.Extensions
|
||||
{
|
||||
[TestClass]
|
||||
public class StringExtensionsTest
|
||||
{
|
||||
[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 = [];
|
||||
|
||||
// act
|
||||
string hex1 = bytes1.BytesToHex();
|
||||
string hex2 = bytes2.BytesToHex();
|
||||
|
||||
// assert
|
||||
Assert.IsNull(hex1);
|
||||
Assert.IsNull(hex2);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldReturnHexString()
|
||||
{
|
||||
// arrange
|
||||
byte[] bytes = [0xaf, 0xfe];
|
||||
|
||||
// act
|
||||
string hex = bytes.BytesToHex();
|
||||
|
||||
// assert
|
||||
Assert.IsNotNull(hex);
|
||||
Assert.AreEqual("affe", hex);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldReturnHexStringWithDelimiter()
|
||||
{
|
||||
// arrange
|
||||
byte[] bytes = [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 hex1 = plain.HexEncode();
|
||||
string hex2 = plain.HexEncode(Encoding.Default);
|
||||
|
||||
// assert
|
||||
Assert.AreEqual("48656c6c6f", hex1);
|
||||
Assert.AreEqual("48656c6c6f", hex2);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldDecodeStringFromHex()
|
||||
{
|
||||
// arrange
|
||||
string hex = "48656c6c6f";
|
||||
|
||||
// act
|
||||
string plain1 = hex.HexDecode();
|
||||
string plain2 = hex.HexDecode(Encoding.Default);
|
||||
|
||||
// assert
|
||||
Assert.AreEqual("Hello", plain1);
|
||||
Assert.AreEqual("Hello", plain2);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldEncodeStringToBase64()
|
||||
{
|
||||
// arrange
|
||||
string plain = "Hello";
|
||||
|
||||
// act
|
||||
string base641 = plain.Base64Encode();
|
||||
string base642 = plain.Base64Encode(Encoding.Default);
|
||||
|
||||
// assert
|
||||
Assert.AreEqual("SGVsbG8=", base641);
|
||||
Assert.AreEqual("SGVsbG8=", base642);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldDecodeStringFromBase64()
|
||||
{
|
||||
// arrange
|
||||
string base64 = "SGVsbG8=";
|
||||
|
||||
// act
|
||||
string plain1 = base64.Base64Decode();
|
||||
string plain2 = base64.Base64Decode(Encoding.Default);
|
||||
|
||||
// assert
|
||||
Assert.AreEqual("Hello", plain1);
|
||||
Assert.AreEqual("Hello", plain2);
|
||||
}
|
||||
|
||||
[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);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldValidateEmailAddressWithoutRecordCheck()
|
||||
{
|
||||
// arrange
|
||||
string validEmailWithoutTag = "test@gmail.com";
|
||||
string validEmailWithTag = "test+tag@not.exists";
|
||||
string invalidEmailWithoutTag = "<Test Account> test@gmail.com";
|
||||
string invalidEmailWithTag = "<Test Account> test+tag@not.exists";
|
||||
string nullStr = null;
|
||||
|
||||
// act
|
||||
bool validWithoutTag = validEmailWithoutTag.IsValidEmailAddress(checkForDnsRecord: false);
|
||||
bool validWithTag = validEmailWithTag.IsValidEmailAddress(checkForDnsRecord: false);
|
||||
bool invalidWithoutTag = !invalidEmailWithoutTag.IsValidEmailAddress(checkForDnsRecord: false);
|
||||
bool invalidWithTag = !invalidEmailWithTag.IsValidEmailAddress(checkForDnsRecord: false);
|
||||
bool nullTest = nullStr.IsValidEmailAddress(checkForDnsRecord: false);
|
||||
|
||||
// assert
|
||||
Assert.IsTrue(validWithoutTag);
|
||||
Assert.IsTrue(validWithTag);
|
||||
Assert.IsTrue(invalidWithoutTag);
|
||||
Assert.IsTrue(invalidWithTag);
|
||||
Assert.IsFalse(nullTest);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldValidateEmailAddressWithRecordCheck()
|
||||
{
|
||||
// arrange
|
||||
string validEmail = "test@gmail.com";
|
||||
string invalidEmail = "test@not.exists";
|
||||
|
||||
// act
|
||||
bool valid = validEmail.IsValidEmailAddress(checkForDnsRecord: true);
|
||||
bool invalid = !invalidEmail.IsValidEmailAddress(checkForDnsRecord: true);
|
||||
|
||||
// assert
|
||||
Assert.IsTrue(valid);
|
||||
Assert.IsTrue(invalid);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldValidateEmailAddressWithRecordCheckDefinedNameservers()
|
||||
{
|
||||
// arrange
|
||||
string validEmail = "test@gmail.com";
|
||||
string invalidEmail = "test@not.exists";
|
||||
var nameserver = new IPEndPoint(IPAddress.Parse("1.1.1.1"), 53);
|
||||
|
||||
// act
|
||||
bool valid = validEmail.IsValidEmailAddress(new[] { nameserver });
|
||||
bool invalid = !invalidEmail.IsValidEmailAddress(new[] { nameserver });
|
||||
|
||||
// assert
|
||||
Assert.IsTrue(valid);
|
||||
Assert.IsTrue(invalid);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldWorkWithNullOrEmptyString()
|
||||
{
|
||||
// arrange
|
||||
string nullStr = null;
|
||||
string emptyStr = "";
|
||||
|
||||
// act
|
||||
string hexEncodeNull = nullStr.HexEncode();
|
||||
string hexEncodeEmpty = emptyStr.HexEncode();
|
||||
string hexDecodeNull = nullStr.HexDecode();
|
||||
string hexDecodeEmpty = emptyStr.HexDecode();
|
||||
|
||||
// assert
|
||||
Assert.IsNull(hexEncodeNull);
|
||||
Assert.AreEqual("", hexEncodeEmpty);
|
||||
Assert.IsNull(hexDecodeNull);
|
||||
Assert.AreEqual("", hexDecodeEmpty);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldAddCustomLineTermination()
|
||||
{
|
||||
// arrange
|
||||
string value = "abc";
|
||||
var sb = new StringBuilder();
|
||||
|
||||
// act
|
||||
sb.AppendLine(value, "\r");
|
||||
sb.AppendLine(value, "\r");
|
||||
|
||||
// assert
|
||||
Assert.AreEqual($"{value}\r{value}\r", sb.ToString());
|
||||
Assert.IsFalse(sb.ToString().Contains('\n'));
|
||||
}
|
||||
}
|
||||
}
|
||||
370
test/AMWD.Common.Tests/Logging/FileLoggerTest.cs
Normal file
370
test/AMWD.Common.Tests/Logging/FileLoggerTest.cs
Normal file
@@ -0,0 +1,370 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using AMWD.Common.Logging;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Moq;
|
||||
|
||||
namespace AMWD.Common.Tests.Logging
|
||||
{
|
||||
[TestClass]
|
||||
public class FileLoggerTest
|
||||
{
|
||||
private Mock<StreamWriter> _streamWriterMock;
|
||||
|
||||
private List<string> _lines;
|
||||
|
||||
[TestInitialize]
|
||||
public void Initialize()
|
||||
{
|
||||
_lines = [];
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldCreateInstance()
|
||||
{
|
||||
// arrange
|
||||
string path = Path.GetTempFileName();
|
||||
|
||||
try
|
||||
{
|
||||
// act
|
||||
using var logger = new FileLogger(path);
|
||||
|
||||
// assert
|
||||
Assert.IsNotNull(logger);
|
||||
Assert.IsTrue(File.Exists(path));
|
||||
|
||||
Assert.AreEqual(path, logger.FileName);
|
||||
Assert.AreEqual(LogLevel.Trace, logger.MinLevel);
|
||||
Assert.IsNull(logger.Name);
|
||||
Assert.IsNull(logger.ParentLogger);
|
||||
Assert.IsNull(logger.TimestampFormat);
|
||||
Assert.IsNull(logger.ScopeProvider);
|
||||
}
|
||||
finally
|
||||
{
|
||||
File.Delete(path);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldCreateInstanceAsNamedInstance()
|
||||
{
|
||||
// arrange
|
||||
string name = "NamedInstance";
|
||||
string path = Path.GetTempFileName();
|
||||
|
||||
try
|
||||
{
|
||||
// act
|
||||
using var logger = new FileLogger(path, name);
|
||||
|
||||
// assert
|
||||
Assert.IsNotNull(logger);
|
||||
Assert.IsTrue(File.Exists(path));
|
||||
|
||||
Assert.AreEqual(path, logger.FileName);
|
||||
Assert.AreEqual(LogLevel.Trace, logger.MinLevel);
|
||||
Assert.AreEqual(name, logger.Name);
|
||||
|
||||
Assert.IsNull(logger.ParentLogger);
|
||||
Assert.IsNull(logger.TimestampFormat);
|
||||
Assert.IsNull(logger.ScopeProvider);
|
||||
}
|
||||
finally
|
||||
{
|
||||
File.Delete(path);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(ObjectDisposedException))]
|
||||
public void ShouldThrowDisposedOnIsEnabled()
|
||||
{
|
||||
// arrange
|
||||
var logger = GetFileLogger();
|
||||
|
||||
// act
|
||||
logger.Dispose();
|
||||
logger.IsEnabled(LogLevel.Error);
|
||||
|
||||
// assert - ObjectDisposedException
|
||||
Assert.Fail();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(ObjectDisposedException))]
|
||||
public void ShouldThrowDisposedOnLog()
|
||||
{
|
||||
// arrange
|
||||
var logger = GetFileLogger();
|
||||
|
||||
// act
|
||||
logger.Dispose();
|
||||
logger.Log(LogLevel.None, "Some Message");
|
||||
|
||||
// assert - ObjectDisposedException
|
||||
Assert.Fail();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(ObjectDisposedException))]
|
||||
public void ShouldThrowDisposedOnBeginScope()
|
||||
{
|
||||
// arrange
|
||||
var logger = GetFileLogger();
|
||||
|
||||
// act
|
||||
logger.Dispose();
|
||||
logger.BeginScope("foo");
|
||||
|
||||
// assert - ObjectDisposedException
|
||||
Assert.Fail();
|
||||
}
|
||||
|
||||
[DataTestMethod]
|
||||
[DataRow(LogLevel.Trace, false)]
|
||||
[DataRow(LogLevel.Debug, false)]
|
||||
[DataRow(LogLevel.Information, false)]
|
||||
[DataRow(LogLevel.Warning, true)]
|
||||
[DataRow(LogLevel.Error, true)]
|
||||
[DataRow(LogLevel.Critical, true)]
|
||||
[DataRow(LogLevel.None, true)]
|
||||
public void ShouldReturnIsEnabled(LogLevel logLevel, bool expectedResult)
|
||||
{
|
||||
// arrange
|
||||
using var logger = GetFileLogger();
|
||||
logger.MinLevel = LogLevel.Warning;
|
||||
|
||||
// act
|
||||
bool result = logger.IsEnabled(logLevel);
|
||||
|
||||
// assert
|
||||
Assert.AreEqual(expectedResult, result);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldLogMessage()
|
||||
{
|
||||
// arrange
|
||||
using var logger = GetFileLogger();
|
||||
|
||||
// act
|
||||
logger.Log(LogLevel.Information, "Test Message");
|
||||
SpinWait.SpinUntil(() => _lines.Count == 1);
|
||||
|
||||
// assert
|
||||
Assert.AreEqual("INFO | Test Message", _lines.First());
|
||||
|
||||
_streamWriterMock.Verify(sw => sw.WriteLineAsync(It.IsAny<string>()), Times.Once);
|
||||
_streamWriterMock.Verify(sw => sw.FlushAsync(), Times.Once);
|
||||
_streamWriterMock.VerifyNoOtherCalls();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldLogAllLevels()
|
||||
{
|
||||
// arrange
|
||||
using var logger = GetFileLogger();
|
||||
|
||||
// act
|
||||
foreach (LogLevel level in Enum.GetValues<LogLevel>())
|
||||
logger.Log(level, "Test Message");
|
||||
|
||||
SpinWait.SpinUntil(() => _lines.Count == 7);
|
||||
|
||||
// assert
|
||||
_streamWriterMock.Verify(sw => sw.WriteLineAsync("TRCE | Test Message"), Times.Once);
|
||||
_streamWriterMock.Verify(sw => sw.WriteLineAsync("DBUG | Test Message"), Times.Once);
|
||||
_streamWriterMock.Verify(sw => sw.WriteLineAsync("INFO | Test Message"), Times.Once);
|
||||
_streamWriterMock.Verify(sw => sw.WriteLineAsync("WARN | Test Message"), Times.Once);
|
||||
_streamWriterMock.Verify(sw => sw.WriteLineAsync("FAIL | Test Message"), Times.Once);
|
||||
_streamWriterMock.Verify(sw => sw.WriteLineAsync("CRIT | Test Message"), Times.Once);
|
||||
_streamWriterMock.Verify(sw => sw.WriteLineAsync(" | Test Message"), Times.Once);
|
||||
_streamWriterMock.Verify(sw => sw.FlushAsync(), Times.AtLeastOnce);
|
||||
_streamWriterMock.VerifyNoOtherCalls();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldLogOnlyAboveMinLevel()
|
||||
{
|
||||
// arrange
|
||||
using var logger = GetFileLogger();
|
||||
logger.MinLevel = LogLevel.Error;
|
||||
|
||||
// act
|
||||
foreach (LogLevel level in Enum.GetValues<LogLevel>())
|
||||
logger.Log(level, "Test Message");
|
||||
|
||||
SpinWait.SpinUntil(() => _lines.Count == 3);
|
||||
|
||||
// assert
|
||||
_streamWriterMock.Verify(sw => sw.WriteLineAsync("FAIL | Test Message"), Times.Once);
|
||||
_streamWriterMock.Verify(sw => sw.WriteLineAsync("CRIT | Test Message"), Times.Once);
|
||||
_streamWriterMock.Verify(sw => sw.WriteLineAsync(" | Test Message"), Times.Once);
|
||||
_streamWriterMock.Verify(sw => sw.FlushAsync(), Times.AtLeastOnce);
|
||||
_streamWriterMock.VerifyNoOtherCalls();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldLogWithLocalTimestamp()
|
||||
{
|
||||
// arrange
|
||||
using var logger = GetFileLogger();
|
||||
logger.UseUtcTimestamp = false;
|
||||
logger.TimestampFormat = "yyyy-MM-dd HH:mm";
|
||||
|
||||
// act
|
||||
logger.LogWarning("Some Warning");
|
||||
SpinWait.SpinUntil(() => _lines.Count == 1);
|
||||
|
||||
// assert
|
||||
|
||||
_streamWriterMock.Verify(sw => sw.WriteLineAsync($"{DateTime.Now:yyyy-MM-dd HH:mm} | WARN | Some Warning"), Times.Once);
|
||||
_streamWriterMock.Verify(sw => sw.FlushAsync(), Times.Once);
|
||||
_streamWriterMock.VerifyNoOtherCalls();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldLogWithUtcTimestamp()
|
||||
{
|
||||
// arrange
|
||||
using var logger = GetFileLogger();
|
||||
logger.UseUtcTimestamp = true;
|
||||
logger.TimestampFormat = "yyyy-MM-dd HH:mm";
|
||||
|
||||
// act
|
||||
logger.LogWarning("Some Warning");
|
||||
SpinWait.SpinUntil(() => _lines.Count == 1);
|
||||
|
||||
// assert
|
||||
_streamWriterMock.Verify(sw => sw.WriteLineAsync($"{DateTime.UtcNow:yyyy-MM-dd HH:mm} | WARN | Some Warning"), Times.Once);
|
||||
_streamWriterMock.Verify(sw => sw.FlushAsync(), Times.Once);
|
||||
_streamWriterMock.VerifyNoOtherCalls();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldUseParentLogger()
|
||||
{
|
||||
// arrange
|
||||
string file = Path.GetTempFileName();
|
||||
try
|
||||
{
|
||||
using var parent = GetFileLogger();
|
||||
parent.UseUtcTimestamp = false;
|
||||
parent.TimestampFormat = "yyyy-MM-dd HH:mm";
|
||||
|
||||
using var logger = new FileLogger(file, "NamedInstance", parent);
|
||||
|
||||
// act
|
||||
logger.LogWarning("Some Warning");
|
||||
SpinWait.SpinUntil(() => _lines.Count == 1);
|
||||
|
||||
// assert
|
||||
_streamWriterMock.Verify(sw => sw.WriteLineAsync($"{DateTime.Now:yyyy-MM-dd HH:mm} | WARN | [NamedInstance] Some Warning"), Times.Once);
|
||||
_streamWriterMock.Verify(sw => sw.FlushAsync(), Times.Once);
|
||||
_streamWriterMock.VerifyNoOtherCalls();
|
||||
|
||||
Assert.AreEqual(0, new FileInfo(file).Length);
|
||||
}
|
||||
finally
|
||||
{
|
||||
File.Delete(file);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldUseScopeProvider()
|
||||
{
|
||||
// arrange
|
||||
var scopeProvider = new Mock<IExternalScopeProvider>();
|
||||
using var logger = GetFileLogger("NamedInstance", scopeProvider.Object);
|
||||
|
||||
// act
|
||||
using (var scope = logger.BeginScope("scope"))
|
||||
{
|
||||
logger.LogError("Test");
|
||||
SpinWait.SpinUntil(() => _lines.Count == 1);
|
||||
}
|
||||
|
||||
// assert
|
||||
_streamWriterMock.Verify(sw => sw.WriteLineAsync("FAIL | [NamedInstance] Test"), Times.Once);
|
||||
_streamWriterMock.Verify(sw => sw.FlushAsync(), Times.Once);
|
||||
_streamWriterMock.VerifyNoOtherCalls();
|
||||
|
||||
scopeProvider.Verify(sp => sp.Push("scope"), Times.Once);
|
||||
scopeProvider.Verify(sp => sp.ForEachScope(It.IsAny<Action<object, It.IsAnyType>>(), It.IsAny<It.IsAnyType>()), Times.Once);
|
||||
scopeProvider.VerifyNoOtherCalls();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldLogException()
|
||||
{
|
||||
// arrange
|
||||
using var logger = GetFileLogger();
|
||||
|
||||
// act
|
||||
logger.LogCritical(new Exception("TestException"), "");
|
||||
SpinWait.SpinUntil(() => _lines.Count == 1);
|
||||
|
||||
// assert
|
||||
_streamWriterMock.Verify(sw => sw.WriteLineAsync("CRIT | System.Exception: TestException"), Times.Once);
|
||||
_streamWriterMock.Verify(sw => sw.FlushAsync(), Times.Once);
|
||||
_streamWriterMock.VerifyNoOtherCalls();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldLogExceptionWithMessage()
|
||||
{
|
||||
// arrange
|
||||
using var logger = GetFileLogger();
|
||||
|
||||
// act
|
||||
logger.LogCritical(new Exception("TestException"), "Bad things happen...");
|
||||
SpinWait.SpinUntil(() => _lines.Count == 1);
|
||||
|
||||
// assert
|
||||
_streamWriterMock.Verify(sw => sw.WriteLineAsync($"CRIT | Bad things happen...{Environment.NewLine} System.Exception: TestException"), Times.Once);
|
||||
_streamWriterMock.Verify(sw => sw.FlushAsync(), Times.Once);
|
||||
_streamWriterMock.VerifyNoOtherCalls();
|
||||
}
|
||||
|
||||
private FileLogger GetFileLogger(string name = null, IExternalScopeProvider scopeProvider = null)
|
||||
{
|
||||
string tmpFilePath = Path.GetTempFileName();
|
||||
try
|
||||
{
|
||||
_streamWriterMock = new Mock<StreamWriter>(Stream.Null);
|
||||
_streamWriterMock
|
||||
.Setup(sw => sw.WriteLineAsync(It.IsAny<string>()))
|
||||
.Callback<string>(line => _lines.Add(line))
|
||||
.Returns(Task.CompletedTask);
|
||||
|
||||
FileLogger fileLogger;
|
||||
if (name == null || scopeProvider == null)
|
||||
{
|
||||
fileLogger = new FileLogger(tmpFilePath);
|
||||
}
|
||||
else
|
||||
{
|
||||
fileLogger = new FileLogger(tmpFilePath, name, scopeProvider);
|
||||
}
|
||||
|
||||
var fieldInfo = fileLogger.GetType().GetField("_fileWriter", BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
(fieldInfo.GetValue(fileLogger) as StreamWriter).Dispose();
|
||||
fieldInfo.SetValue(fileLogger, _streamWriterMock.Object);
|
||||
|
||||
return fileLogger;
|
||||
}
|
||||
finally
|
||||
{
|
||||
File.Delete(tmpFilePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
353
test/AMWD.Common.Tests/Packing/Ar/ArReaderTest.cs
Normal file
353
test/AMWD.Common.Tests/Packing/Ar/ArReaderTest.cs
Normal file
@@ -0,0 +1,353 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using AMWD.Common.Packing.Ar;
|
||||
|
||||
namespace AMWD.Common.Tests.Packing.Ar
|
||||
{
|
||||
[TestClass]
|
||||
public class ArReaderTest
|
||||
{
|
||||
private readonly DateTime _fixedDateTime = new(2023, 03, 01, 10, 20, 30, 0, DateTimeKind.Utc);
|
||||
|
||||
private Dictionary<string, ArFileInfo> _files;
|
||||
|
||||
private MemoryStream _inStream;
|
||||
|
||||
[TestInitialize]
|
||||
public void Initialize()
|
||||
{
|
||||
_files = new Dictionary<string, ArFileInfo>
|
||||
{
|
||||
{
|
||||
"abcd.tmp",
|
||||
new ArFileInfo
|
||||
{
|
||||
FileName = "abcd.tmp",
|
||||
FileSize = 14,
|
||||
GroupId = 456,
|
||||
Mode = 33188,
|
||||
ModifyTime = _fixedDateTime,
|
||||
UserId = 123
|
||||
}
|
||||
},
|
||||
{
|
||||
"efgh.tmp",
|
||||
new ArFileInfo
|
||||
{
|
||||
FileName = "efgh.tmp",
|
||||
FileSize = 14,
|
||||
GroupId = 456,
|
||||
Mode = 33188,
|
||||
ModifyTime = _fixedDateTime,
|
||||
UserId = 123
|
||||
}
|
||||
},
|
||||
{
|
||||
"ijkl.tmp",
|
||||
new ArFileInfo
|
||||
{
|
||||
FileName = "ijkl.tmp",
|
||||
FileSize = 13,
|
||||
GroupId = 456,
|
||||
Mode = 33188,
|
||||
ModifyTime = _fixedDateTime,
|
||||
UserId = 123
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
_inStream = new MemoryStream();
|
||||
_inStream.Write(Encoding.ASCII.GetBytes("!<arch>\n"));
|
||||
|
||||
foreach (var file in _files)
|
||||
{
|
||||
int unixSeconds = (int)file.Value.ModifyTime.Subtract(DateTime.UnixEpoch).TotalSeconds;
|
||||
|
||||
_inStream.Write(Encoding.ASCII.GetBytes($"{file.Key,-16}{unixSeconds,-12}123 456 100644 {file.Value.FileSize,-10}`\n"));
|
||||
_inStream.Write(Encoding.UTF8.GetBytes(new string('a', (int)file.Value.FileSize)));
|
||||
if (file.Value.FileSize % 2 != 0)
|
||||
_inStream.Write(Encoding.ASCII.GetBytes("\n"));
|
||||
}
|
||||
|
||||
_inStream.Seek(0, SeekOrigin.Begin);
|
||||
}
|
||||
|
||||
[TestCleanup]
|
||||
public void Cleanup()
|
||||
{
|
||||
_inStream.Dispose();
|
||||
_inStream = null;
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldInitializeArchive()
|
||||
{
|
||||
// Arrange
|
||||
_inStream.Dispose();
|
||||
_inStream = new MemoryStream();
|
||||
_inStream.Write(Encoding.ASCII.GetBytes("!<arch>\n"));
|
||||
_inStream.Seek(0, SeekOrigin.Begin);
|
||||
|
||||
// Act
|
||||
var reader = new ArReader(_inStream);
|
||||
|
||||
// Assert
|
||||
Assert.IsNotNull(reader);
|
||||
Assert.IsFalse(reader.GetFileList().Any());
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldInitializeWithFiles()
|
||||
{
|
||||
// Arrange
|
||||
|
||||
// Act
|
||||
var reader = new ArReader(_inStream);
|
||||
|
||||
// Assert
|
||||
Assert.IsNotNull(reader);
|
||||
Assert.IsTrue(reader.GetFileList().Any());
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldListFileNames()
|
||||
{
|
||||
// Arrange
|
||||
var reader = new ArReader(_inStream);
|
||||
|
||||
// Act
|
||||
var fileList = reader.GetFileList().ToList();
|
||||
|
||||
// Assert
|
||||
Assert.IsNotNull(reader);
|
||||
Assert.AreEqual(_files.Count, fileList.Count);
|
||||
|
||||
foreach (string name in _files.Keys)
|
||||
Assert.IsTrue(fileList.Contains(name));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldReturnValidFileInfo()
|
||||
{
|
||||
// Arrange
|
||||
var infos = new List<ArFileInfo>();
|
||||
var reader = new ArReader(_inStream);
|
||||
|
||||
// Act
|
||||
foreach (string name in _files.Keys)
|
||||
infos.Add(reader.GetFileInfo(name));
|
||||
|
||||
// Assert
|
||||
Assert.IsNotNull(reader);
|
||||
Assert.AreEqual(_files.Count, infos.Count);
|
||||
|
||||
foreach (var expected in _files.Values)
|
||||
{
|
||||
var actual = infos.Single(fi => fi.FileName == expected.FileName);
|
||||
|
||||
Assert.AreEqual(expected.FileName, actual.FileName);
|
||||
Assert.AreEqual(expected.FileSize, actual.FileSize);
|
||||
Assert.AreEqual(expected.GroupId, actual.GroupId);
|
||||
Assert.AreEqual(expected.Mode, actual.Mode);
|
||||
Assert.AreEqual(expected.ModifyTime, actual.ModifyTime);
|
||||
Assert.AreEqual(expected.UserId, actual.UserId);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldReturnValidFileContent()
|
||||
{
|
||||
// Arrange
|
||||
var contents = new Dictionary<string, string>();
|
||||
var reader = new ArReader(_inStream);
|
||||
|
||||
// Act
|
||||
foreach (string name in _files.Keys)
|
||||
{
|
||||
using var ms = new MemoryStream();
|
||||
reader.ReadFile(name, ms);
|
||||
ms.Seek(0, SeekOrigin.Begin);
|
||||
|
||||
contents.Add(name, Encoding.UTF8.GetString(ms.ToArray()));
|
||||
}
|
||||
|
||||
// Assert
|
||||
Assert.IsNotNull(reader);
|
||||
Assert.AreEqual(_files.Count, contents.Count);
|
||||
|
||||
foreach (var expected in _files.Values)
|
||||
{
|
||||
string content = contents[expected.FileName];
|
||||
|
||||
if (expected.FileSize % 2 != 0)
|
||||
Assert.AreEqual(13, content.Length);
|
||||
else
|
||||
Assert.AreEqual(14, content.Length);
|
||||
|
||||
Assert.AreEqual(new string('a', (int)expected.FileSize), content);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldWriteFileToDisk()
|
||||
{
|
||||
// Arrange
|
||||
string tmpFile = Path.GetTempFileName();
|
||||
var reader = new ArReader(_inStream);
|
||||
|
||||
try
|
||||
{
|
||||
// Act
|
||||
using var ms = new MemoryStream();
|
||||
|
||||
reader.ReadFile("abcd.tmp", ms);
|
||||
reader.ReadFile("abcd.tmp", tmpFile);
|
||||
|
||||
// Assert
|
||||
CollectionAssert.AreEqual(ms.ToArray(), File.ReadAllBytes(tmpFile));
|
||||
}
|
||||
finally
|
||||
{
|
||||
File.Delete(tmpFile);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(ArgumentException))]
|
||||
public void ShouldThrowExceptionOnMissingRead()
|
||||
{
|
||||
// Arrange
|
||||
using var stream = new OverrideStream();
|
||||
stream.CanReadOR = false;
|
||||
stream.CanSeekOR = true;
|
||||
stream.CanWriteOR = true;
|
||||
|
||||
// Act
|
||||
var reader = new ArReader(stream);
|
||||
|
||||
// Assert - ArgumentException
|
||||
Assert.Fail();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(ArgumentException))]
|
||||
public void ShouldThrowExceptionOnMissingSeek()
|
||||
{
|
||||
// Arrange
|
||||
using var stream = new OverrideStream();
|
||||
stream.CanReadOR = true;
|
||||
stream.CanSeekOR = false;
|
||||
stream.CanWriteOR = true;
|
||||
|
||||
// Act
|
||||
var reader = new ArReader(stream);
|
||||
|
||||
// Assert - ArgumentException
|
||||
Assert.Fail();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(ArgumentException))]
|
||||
public void ShouldThrowExceptionOnMissingWrite()
|
||||
{
|
||||
// Arrange
|
||||
using var stream = new OverrideStream();
|
||||
stream.CanReadOR = true;
|
||||
stream.CanSeekOR = true;
|
||||
stream.CanWriteOR = false;
|
||||
|
||||
var reader = new ArReader(_inStream);
|
||||
|
||||
// Act
|
||||
reader.ReadFile("abcd.tmp", stream);
|
||||
|
||||
// Assert - ArgumentException
|
||||
Assert.Fail();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(FormatException))]
|
||||
public void ShouldThrowExceptionOnInvalidArchive()
|
||||
{
|
||||
// Arrange
|
||||
_inStream.Seek(8, SeekOrigin.Begin);
|
||||
|
||||
// Act
|
||||
_ = new ArReader(_inStream);
|
||||
|
||||
// Assert - FormatException
|
||||
Assert.Fail();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(FormatException))]
|
||||
public void ShouldThrowExceptionOnInvalidMagic()
|
||||
{
|
||||
// Arrange
|
||||
_inStream.Seek(0, SeekOrigin.End);
|
||||
_inStream.Write(Encoding.ASCII.GetBytes($"{"foo.bar",-16}{"123456789",-12}123 456 100644 {"0",-10}´\n"));
|
||||
_inStream.Seek(0, SeekOrigin.Begin);
|
||||
|
||||
// Act
|
||||
_ = new ArReader(_inStream);
|
||||
|
||||
// Assert - FormatException
|
||||
Assert.Fail();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldWriteNothingToStreamForMissingFile()
|
||||
{
|
||||
// Arrange
|
||||
var reader = new ArReader(_inStream);
|
||||
|
||||
// Act
|
||||
using var ms = new MemoryStream();
|
||||
|
||||
reader.ReadFile("foo.bar", ms);
|
||||
ms.Seek(0, SeekOrigin.Begin);
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual(0, ms.Length);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldWriteNothingToDiskForMissingFile()
|
||||
{
|
||||
// Arrange
|
||||
string tmpFile = Path.GetTempFileName();
|
||||
var reader = new ArReader(_inStream);
|
||||
|
||||
try
|
||||
{
|
||||
// Act
|
||||
reader.ReadFile("foo.bar", tmpFile);
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual(0, new FileInfo(tmpFile).Length);
|
||||
}
|
||||
finally
|
||||
{
|
||||
File.Delete(tmpFile);
|
||||
}
|
||||
}
|
||||
|
||||
private class OverrideStream : MemoryStream
|
||||
{
|
||||
public override bool CanWrite => CanWriteOR;
|
||||
|
||||
public bool CanWriteOR { get; set; }
|
||||
|
||||
public override bool CanSeek => CanSeekOR;
|
||||
|
||||
public bool CanSeekOR { get; set; }
|
||||
|
||||
public override bool CanRead => CanReadOR;
|
||||
|
||||
public bool CanReadOR { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
304
test/AMWD.Common.Tests/Packing/Ar/ArWriterTest.cs
Normal file
304
test/AMWD.Common.Tests/Packing/Ar/ArWriterTest.cs
Normal file
@@ -0,0 +1,304 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using AMWD.Common.Packing.Ar;
|
||||
|
||||
namespace AMWD.Common.Tests.Packing.Ar
|
||||
{
|
||||
[TestClass]
|
||||
public class ArWriterTest
|
||||
{
|
||||
private readonly DateTime _fixedDateTime = new(2023, 03, 01, 10, 20, 30, 0, DateTimeKind.Utc);
|
||||
|
||||
private readonly Dictionary<string, string> _files = [];
|
||||
|
||||
private MemoryStream _outStream;
|
||||
|
||||
[TestInitialize]
|
||||
public void Initialize()
|
||||
{
|
||||
_files.Clear();
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
var (filePath, content) = GenerateTestFile();
|
||||
_files.Add(filePath, content);
|
||||
}
|
||||
|
||||
_outStream = new MemoryStream();
|
||||
}
|
||||
|
||||
[TestCleanup]
|
||||
public void Cleanup()
|
||||
{
|
||||
foreach (var kvp in _files)
|
||||
File.Delete(kvp.Key);
|
||||
|
||||
_files.Clear();
|
||||
|
||||
_outStream.Dispose();
|
||||
_outStream = null;
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldInitializeArchive()
|
||||
{
|
||||
// Arrange
|
||||
byte[] initBytes = new byte[8];
|
||||
|
||||
// Act
|
||||
_ = new ArWriter(_outStream);
|
||||
|
||||
_outStream.Seek(0, SeekOrigin.Begin);
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual(initBytes.Length, _outStream.Length);
|
||||
|
||||
_outStream.Read(initBytes, 0, initBytes.Length);
|
||||
CollectionAssert.AreEqual(Encoding.ASCII.GetBytes("!<arch>\n"), initBytes);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldWriteOneFile()
|
||||
{
|
||||
// Arrange
|
||||
var firstFileKvp = _files.First();
|
||||
byte[] headerBytes = new byte[60];
|
||||
byte[] contentBytes = new byte[14];
|
||||
|
||||
var writer = new ArWriter(_outStream);
|
||||
|
||||
// Act
|
||||
writer.WriteFile(firstFileKvp.Key, 123, 456);
|
||||
|
||||
_outStream.Seek(8, SeekOrigin.Begin); // set behind init bytes
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual(8 + headerBytes.Length + contentBytes.Length, _outStream.Length);
|
||||
|
||||
_outStream.Read(headerBytes, 0, headerBytes.Length);
|
||||
_outStream.Read(contentBytes, 0, contentBytes.Length);
|
||||
|
||||
string header = Encoding.ASCII.GetString(headerBytes);
|
||||
string content = Encoding.UTF8.GetString(contentBytes);
|
||||
|
||||
Assert.AreEqual($"{Path.GetFileName(firstFileKvp.Key),-16}1677666030 123 456 100644 14 `\n", header);
|
||||
Assert.AreEqual(firstFileKvp.Value, content);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldWriteMultipleFiles()
|
||||
{
|
||||
// Arrange
|
||||
var writer = new ArWriter(_outStream);
|
||||
|
||||
// Act
|
||||
foreach (var kvp in _files)
|
||||
writer.WriteFile(kvp.Key, 123, 456);
|
||||
|
||||
_outStream.Seek(8, SeekOrigin.Begin);
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual(8 + 3 * 60 + 3 * 14, _outStream.Length);
|
||||
|
||||
foreach (var kvp in _files)
|
||||
{
|
||||
byte[] headerBytes = new byte[60];
|
||||
byte[] contentBytes = new byte[14];
|
||||
|
||||
_outStream.Read(headerBytes, 0, headerBytes.Length);
|
||||
_outStream.Read(contentBytes, 0, contentBytes.Length);
|
||||
|
||||
string header = Encoding.ASCII.GetString(headerBytes);
|
||||
string content = Encoding.UTF8.GetString(contentBytes);
|
||||
|
||||
Assert.AreEqual($"{Path.GetFileName(kvp.Key),-16}1677666030 123 456 100644 14 `\n", header);
|
||||
Assert.AreEqual(kvp.Value, content);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldPadToEven()
|
||||
{
|
||||
// Arrange
|
||||
var (filePath, fileContent) = GenerateTestFile(13);
|
||||
|
||||
try
|
||||
{
|
||||
byte[] headerBytes = new byte[60];
|
||||
byte[] contentBytes = new byte[14];
|
||||
|
||||
var writer = new ArWriter(_outStream);
|
||||
|
||||
// Act
|
||||
writer.WriteFile(filePath, 123, 456);
|
||||
|
||||
_outStream.Seek(8, SeekOrigin.Begin);
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual(8 + headerBytes.Length + contentBytes.Length, _outStream.Length);
|
||||
|
||||
_outStream.Read(headerBytes, 0, headerBytes.Length);
|
||||
_outStream.Read(contentBytes, 0, contentBytes.Length);
|
||||
|
||||
string header = Encoding.ASCII.GetString(headerBytes);
|
||||
string content = Encoding.UTF8.GetString(contentBytes);
|
||||
|
||||
Assert.AreEqual($"{Path.GetFileName(filePath),-16}1677666030 123 456 100644 13 `\n", header);
|
||||
Assert.AreEqual(fileContent + "\n", content);
|
||||
}
|
||||
finally
|
||||
{
|
||||
File.Delete(filePath);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(ArgumentException))]
|
||||
public void ShouldFailOnFileNameTooLong()
|
||||
{
|
||||
// Arrange
|
||||
var (filePath, _) = GenerateTestFile();
|
||||
try
|
||||
{
|
||||
string path = Path.GetDirectoryName(filePath);
|
||||
string fileName = Path.GetFileName(filePath);
|
||||
fileName = fileName.PadLeft(20, 'a');
|
||||
|
||||
File.Move(filePath, Path.Combine(path, fileName));
|
||||
filePath = Path.Combine(path, fileName);
|
||||
|
||||
var writer = new ArWriter(_outStream);
|
||||
|
||||
// Act
|
||||
writer.WriteFile(filePath, 123, 456);
|
||||
|
||||
// Assert - Exception
|
||||
Assert.Fail();
|
||||
}
|
||||
finally
|
||||
{
|
||||
File.Delete(filePath);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldWriteEmptyOnNegativeUserId()
|
||||
{
|
||||
// Arrange
|
||||
var firstFileKvp = _files.First();
|
||||
byte[] headerBytes = new byte[60];
|
||||
byte[] contentBytes = new byte[14];
|
||||
|
||||
var writer = new ArWriter(_outStream);
|
||||
|
||||
// Act
|
||||
writer.WriteFile(firstFileKvp.Key, -123, 456);
|
||||
|
||||
_outStream.Seek(8, SeekOrigin.Begin);
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual(8 + headerBytes.Length + contentBytes.Length, _outStream.Length);
|
||||
|
||||
_outStream.Read(headerBytes, 0, headerBytes.Length);
|
||||
_outStream.Read(contentBytes, 0, contentBytes.Length);
|
||||
|
||||
string header = Encoding.ASCII.GetString(headerBytes);
|
||||
string content = Encoding.UTF8.GetString(contentBytes);
|
||||
|
||||
Assert.AreEqual($"{Path.GetFileName(firstFileKvp.Key),-16}1677666030 456 100644 14 `\n", header);
|
||||
Assert.AreEqual(firstFileKvp.Value, content);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldWriteEmptyOnNegativeGroupId()
|
||||
{
|
||||
// Arrange
|
||||
var firstFileKvp = _files.First();
|
||||
byte[] headerBytes = new byte[60];
|
||||
byte[] contentBytes = new byte[14];
|
||||
|
||||
var writer = new ArWriter(_outStream);
|
||||
|
||||
// Act
|
||||
writer.WriteFile(firstFileKvp.Key, 123, -456);
|
||||
|
||||
_outStream.Seek(8, SeekOrigin.Begin);
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual(8 + headerBytes.Length + contentBytes.Length, _outStream.Length);
|
||||
|
||||
_outStream.Read(headerBytes, 0, headerBytes.Length);
|
||||
_outStream.Read(contentBytes, 0, contentBytes.Length);
|
||||
|
||||
string header = Encoding.ASCII.GetString(headerBytes);
|
||||
string content = Encoding.UTF8.GetString(contentBytes);
|
||||
|
||||
Assert.AreEqual($"{Path.GetFileName(firstFileKvp.Key),-16}1677666030 123 100644 14 `\n", header);
|
||||
Assert.AreEqual(firstFileKvp.Value, content);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldWriteEmptyOnNegativeMode()
|
||||
{
|
||||
// Arrange
|
||||
var firstFileKvp = _files.First();
|
||||
byte[] headerBytes = new byte[60];
|
||||
byte[] contentBytes = new byte[14];
|
||||
|
||||
var writer = new ArWriter(_outStream);
|
||||
|
||||
// Act
|
||||
writer.WriteFile(firstFileKvp.Key, 123, 456, -1);
|
||||
|
||||
_outStream.Seek(8, SeekOrigin.Begin);
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual(8 + headerBytes.Length + contentBytes.Length, _outStream.Length);
|
||||
|
||||
_outStream.Read(headerBytes, 0, headerBytes.Length);
|
||||
_outStream.Read(contentBytes, 0, contentBytes.Length);
|
||||
|
||||
string header = Encoding.ASCII.GetString(headerBytes);
|
||||
string content = Encoding.UTF8.GetString(contentBytes);
|
||||
|
||||
Assert.AreEqual($"{Path.GetFileName(firstFileKvp.Key),-16}1677666030 123 456 14 `\n", header);
|
||||
Assert.AreEqual(firstFileKvp.Value, content);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(ArgumentException))]
|
||||
public void ShouldFailOnNonWritableStream()
|
||||
{
|
||||
// Arrange
|
||||
using var testStream = new NonWriteStream();
|
||||
|
||||
// Act
|
||||
_ = new ArWriter(testStream);
|
||||
|
||||
// Assert - ArgumentException
|
||||
Assert.Fail();
|
||||
}
|
||||
|
||||
private (string filePath, string content) GenerateTestFile(int length = 14)
|
||||
{
|
||||
string filePath = Path.GetTempFileName();
|
||||
string text = CryptographyHelper.GetRandomString(length);
|
||||
|
||||
File.WriteAllText(filePath, text);
|
||||
File.SetLastWriteTimeUtc(filePath, _fixedDateTime);
|
||||
return (filePath, text);
|
||||
}
|
||||
|
||||
private class NonWriteStream : MemoryStream
|
||||
{
|
||||
public NonWriteStream()
|
||||
: base()
|
||||
{ }
|
||||
|
||||
public override bool CanWrite => false;
|
||||
}
|
||||
}
|
||||
}
|
||||
350
test/AMWD.Common.Tests/Utilities/AsyncQueueTest.cs
Normal file
350
test/AMWD.Common.Tests/Utilities/AsyncQueueTest.cs
Normal file
@@ -0,0 +1,350 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace AMWD.Common.Tests.Utilities
|
||||
{
|
||||
[TestClass]
|
||||
public class AsyncQueueTest
|
||||
{
|
||||
private Queue<TestElement> _internalQueue;
|
||||
|
||||
private TestElement _queueElement1;
|
||||
private TestElement _queueElement2;
|
||||
private TestElement _queueElement3;
|
||||
|
||||
[TestInitialize]
|
||||
public void InitializeTest()
|
||||
{
|
||||
_queueElement1 = new TestElement
|
||||
{
|
||||
Number = 111,
|
||||
Text = "one"
|
||||
};
|
||||
_queueElement2 = new TestElement
|
||||
{
|
||||
Number = 222,
|
||||
Text = "two"
|
||||
};
|
||||
_queueElement3 = new TestElement
|
||||
{
|
||||
Number = 333,
|
||||
Text = "three"
|
||||
};
|
||||
|
||||
_internalQueue = new Queue<TestElement>();
|
||||
_internalQueue.Enqueue(_queueElement1);
|
||||
_internalQueue.Enqueue(_queueElement2);
|
||||
_internalQueue.Enqueue(_queueElement3);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldEnqueueItem()
|
||||
{
|
||||
// arrange
|
||||
var element = new TestElement { Number = 1, Text = "Hello" };
|
||||
|
||||
_internalQueue.Clear();
|
||||
var queue = GetQueue();
|
||||
|
||||
// act
|
||||
queue.Enqueue(element);
|
||||
|
||||
// assert
|
||||
Assert.AreEqual(1, _internalQueue.Count);
|
||||
Assert.AreEqual(_internalQueue.Count, queue.Count);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldEnqueueItemAndResetAvailableToken()
|
||||
{
|
||||
// arrange
|
||||
var element = new TestElement { Number = 1, Text = "Hello" };
|
||||
bool available = false;
|
||||
|
||||
_internalQueue.Clear();
|
||||
var queue = GetQueue();
|
||||
|
||||
// act
|
||||
var task = Task.Run(async () =>
|
||||
{
|
||||
await queue.WaitAsync();
|
||||
available = true;
|
||||
});
|
||||
queue.Enqueue(element);
|
||||
task.Wait();
|
||||
|
||||
// assert
|
||||
Assert.IsTrue(available);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldEnqueueItemAndResetDequeueToken()
|
||||
{
|
||||
// arrange
|
||||
var element = new TestElement { Number = 1, Text = "Hello" };
|
||||
TestElement callback = null;
|
||||
|
||||
_internalQueue.Clear();
|
||||
var queue = GetQueue();
|
||||
|
||||
// act
|
||||
var task = Task.Run(async () =>
|
||||
{
|
||||
callback = await queue.DequeueAsync();
|
||||
});
|
||||
queue.Enqueue(element);
|
||||
task.Wait();
|
||||
|
||||
// assert
|
||||
Assert.IsNotNull(callback);
|
||||
Assert.AreEqual(element, callback);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldEnqueueMultipleItems()
|
||||
{
|
||||
// arrange
|
||||
var elements = new TestElement[]
|
||||
{
|
||||
new() { Number = 1, Text = "Hello" },
|
||||
new() { Number = 2, Text = "World" },
|
||||
};
|
||||
_internalQueue.Clear();
|
||||
var queue = GetQueue();
|
||||
|
||||
// act
|
||||
queue.Enqueue(elements);
|
||||
|
||||
// assert
|
||||
Assert.AreEqual(2, _internalQueue.Count);
|
||||
Assert.AreEqual(queue.Count, _internalQueue.Count);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldPeekAValue()
|
||||
{
|
||||
// arrange
|
||||
var queue = GetQueue();
|
||||
|
||||
// act
|
||||
bool isSuccess = queue.TryPeek(out var item);
|
||||
|
||||
// assert
|
||||
Assert.IsTrue(isSuccess);
|
||||
Assert.IsNotNull(item);
|
||||
Assert.AreEqual(_queueElement1, item);
|
||||
Assert.AreEqual(3, queue.Count);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldNotPeekAValue()
|
||||
{
|
||||
// arrange
|
||||
_internalQueue.Clear();
|
||||
var queue = GetQueue();
|
||||
|
||||
// act
|
||||
bool isSuccess = queue.TryPeek(out var item);
|
||||
|
||||
// assert
|
||||
Assert.IsFalse(isSuccess);
|
||||
Assert.IsNull(item);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldDequeueAValue()
|
||||
{
|
||||
// arrange
|
||||
var queue = GetQueue();
|
||||
|
||||
// act
|
||||
bool isSuccess = queue.TryDequeue(out var item);
|
||||
|
||||
// assert
|
||||
Assert.IsTrue(isSuccess);
|
||||
Assert.IsNotNull(item);
|
||||
Assert.AreEqual(_queueElement1, item);
|
||||
Assert.AreEqual(2, queue.Count);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldNotDequeueAValue()
|
||||
{
|
||||
// arrange
|
||||
_internalQueue.Clear();
|
||||
var queue = GetQueue();
|
||||
|
||||
// act
|
||||
bool isSuccess = queue.TryDequeue(out var item);
|
||||
|
||||
// assert
|
||||
Assert.IsFalse(isSuccess);
|
||||
Assert.IsNull(item);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldRemoveAValue()
|
||||
{
|
||||
// arrange
|
||||
var queue = GetQueue();
|
||||
|
||||
// act
|
||||
queue.Remove(_queueElement2);
|
||||
var item1 = queue.Dequeue();
|
||||
var item2 = queue.Dequeue();
|
||||
|
||||
// assert
|
||||
Assert.AreEqual(0, queue.Count);
|
||||
Assert.AreEqual(_queueElement1, item1);
|
||||
Assert.AreEqual(_queueElement3, item2);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldNotRemoveAValue()
|
||||
{
|
||||
// arrange
|
||||
var queue = GetQueue();
|
||||
|
||||
// act
|
||||
queue.Remove(null);
|
||||
var item1 = queue.Dequeue();
|
||||
var item2 = queue.Dequeue();
|
||||
var item3 = queue.Dequeue();
|
||||
|
||||
// assert
|
||||
Assert.AreEqual(0, queue.Count);
|
||||
Assert.AreEqual(_queueElement1, item1);
|
||||
Assert.AreEqual(_queueElement2, item2);
|
||||
Assert.AreEqual(_queueElement3, item3);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task ShouldAwaitOneDequeue()
|
||||
{
|
||||
// arrange
|
||||
_internalQueue.Clear();
|
||||
var queue = GetQueue();
|
||||
|
||||
var task = Task.Run(async () =>
|
||||
{
|
||||
await Task.Delay(1000);
|
||||
queue.Enqueue(new[] { _queueElement1, _queueElement2, _queueElement3 });
|
||||
});
|
||||
|
||||
// act
|
||||
var item = await queue.DequeueAsync();
|
||||
|
||||
// assert
|
||||
Assert.AreEqual(2, queue.Count);
|
||||
Assert.IsNotNull(item);
|
||||
Assert.AreEqual(_queueElement1, item);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task ShouldAwaitManyDequeue()
|
||||
{
|
||||
// arrange
|
||||
_internalQueue.Clear();
|
||||
var queue = GetQueue();
|
||||
|
||||
var task = Task.Run(async () =>
|
||||
{
|
||||
await Task.Delay(1000);
|
||||
queue.Enqueue(new[] { _queueElement1, _queueElement2, _queueElement3 });
|
||||
});
|
||||
|
||||
// act
|
||||
var items = await queue.DequeueManyAsync(2);
|
||||
|
||||
// assert
|
||||
Assert.AreEqual(1, queue.Count);
|
||||
Assert.IsNotNull(items);
|
||||
Assert.AreEqual(2, items.Length);
|
||||
Assert.AreEqual(_queueElement1, items[0]);
|
||||
Assert.AreEqual(_queueElement2, items[1]);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task ShouldAwaitAllDequeue()
|
||||
{
|
||||
// arrange
|
||||
_internalQueue.Clear();
|
||||
var queue = GetQueue();
|
||||
|
||||
var task = Task.Run(async () =>
|
||||
{
|
||||
await Task.Delay(1000);
|
||||
queue.Enqueue(new[] { _queueElement1, _queueElement2, _queueElement3 });
|
||||
});
|
||||
|
||||
// act
|
||||
var items = await queue.DequeueAvailableAsync();
|
||||
|
||||
// assert
|
||||
Assert.AreEqual(0, queue.Count);
|
||||
Assert.IsNotNull(items);
|
||||
Assert.AreEqual(3, items.Length);
|
||||
Assert.AreEqual(_queueElement1, items[0]);
|
||||
Assert.AreEqual(_queueElement2, items[1]);
|
||||
Assert.AreEqual(_queueElement3, items[2]);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task ShouldAwaitAvailableDequeue()
|
||||
{
|
||||
// arrange
|
||||
_internalQueue.Clear();
|
||||
var queue = GetQueue();
|
||||
|
||||
var task = Task.Run(async () =>
|
||||
{
|
||||
await Task.Delay(1000);
|
||||
queue.Enqueue(new[] { _queueElement1, _queueElement2, _queueElement3 });
|
||||
});
|
||||
|
||||
// act
|
||||
var items = await queue.DequeueAvailableAsync(2);
|
||||
|
||||
// assert
|
||||
Assert.AreEqual(1, queue.Count);
|
||||
Assert.IsNotNull(items);
|
||||
Assert.AreEqual(2, items.Length);
|
||||
Assert.AreEqual(_queueElement1, items[0]);
|
||||
Assert.AreEqual(_queueElement2, items[1]);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(ArgumentOutOfRangeException))]
|
||||
public async Task ShouldThrowArumentOutOfRangeException()
|
||||
{
|
||||
// arrange
|
||||
_internalQueue.Clear();
|
||||
var queue = GetQueue();
|
||||
|
||||
// act
|
||||
await queue.DequeueManyAsync(-2);
|
||||
|
||||
// assert - ArgumentOutOfRangeException expected
|
||||
Assert.Fail();
|
||||
}
|
||||
|
||||
private AsyncQueue<TestElement> GetQueue()
|
||||
{
|
||||
var asyncQueue = new AsyncQueue<TestElement>();
|
||||
|
||||
var field = asyncQueue.GetType().GetField("_queue", BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
field.SetValue(asyncQueue, _internalQueue);
|
||||
|
||||
return asyncQueue;
|
||||
}
|
||||
|
||||
private class TestElement
|
||||
{
|
||||
public int Number { get; set; }
|
||||
|
||||
public string Text { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
635
test/AMWD.Common.Tests/Utilities/CryptographyHelperTest.cs
Normal file
635
test/AMWD.Common.Tests/Utilities/CryptographyHelperTest.cs
Normal file
@@ -0,0 +1,635 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text.RegularExpressions;
|
||||
using AMWD.Common.Tests.Utils;
|
||||
|
||||
namespace UnitTests.Common.Utilities
|
||||
{
|
||||
[TestClass]
|
||||
public partial class CryptographyHelperTests
|
||||
{
|
||||
private string _keyFile;
|
||||
private CryptographyHelper _cryptoHelper;
|
||||
|
||||
[TestInitialize]
|
||||
public void Initialize()
|
||||
{
|
||||
_keyFile = Path.GetTempFileName();
|
||||
_cryptoHelper = new CryptographyHelper(_keyFile);
|
||||
}
|
||||
|
||||
[TestCleanup]
|
||||
public void Cleanup()
|
||||
{
|
||||
File.Delete(_keyFile);
|
||||
}
|
||||
|
||||
#region Static
|
||||
|
||||
#region Encryption
|
||||
|
||||
#region AES
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldEncryptAesWithoutSalt() // required to test the encryption itself
|
||||
{
|
||||
// arrange
|
||||
using var _ = CryptographyHelperSaltMock.Create(0);
|
||||
|
||||
byte[] bytes = [0xaf, 0xfe];
|
||||
string str = "ABC";
|
||||
string password1 = "P@ssw0rd!";
|
||||
string password2 = "P@ssw0rd";
|
||||
|
||||
byte[] expectedBytes = [0x7c, 0x7b, 0x77, 0x56, 0x91, 0x1a, 0xd9, 0xc0, 0x72, 0x70, 0x36, 0x88, 0x9f, 0xb4, 0xb5, 0xbc];
|
||||
|
||||
// act
|
||||
byte[] cipherBytes1 = CryptographyHelper.AesEncrypt(bytes, password1);
|
||||
string cipherStr1 = CryptographyHelper.AesEncrypt(str, password1);
|
||||
byte[] cipherBytes2 = CryptographyHelper.AesEncrypt(bytes, password2);
|
||||
string cipherStr2 = CryptographyHelper.AesEncrypt(str, password2);
|
||||
|
||||
// assert
|
||||
CollectionAssert.AreEqual(expectedBytes, cipherBytes1);
|
||||
Assert.AreEqual("ueLuhFNpCuYmx8v3hczHtg==", cipherStr1);
|
||||
|
||||
CollectionAssert.AreNotEqual(expectedBytes, cipherBytes2);
|
||||
Assert.AreNotEqual("ueLuhFNpCuYmx8v3hczHtg==", cipherStr2);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldDecryptAesWithoutSalt() // required to test the decryption itself
|
||||
{
|
||||
// arrange
|
||||
using var _ = CryptographyHelperSaltMock.Create(0);
|
||||
|
||||
string cipherStr = "ueLuhFNpCuYmx8v3hczHtg==";
|
||||
byte[] cipherBytes = [0x7c, 0x7b, 0x77, 0x56, 0x91, 0x1a, 0xd9, 0xc0, 0x72, 0x70, 0x36, 0x88, 0x9f, 0xb4, 0xb5, 0xbc];
|
||||
|
||||
string password1 = "P@ssw0rd!";
|
||||
string password2 = "P@ssw0rd";
|
||||
|
||||
byte[] expectedBytes = [0xaf, 0xfe];
|
||||
|
||||
// act
|
||||
byte[] plainBytes1 = CryptographyHelper.AesDecrypt(cipherBytes, password1);
|
||||
string plainStr1 = CryptographyHelper.AesDecrypt(cipherStr, password1);
|
||||
|
||||
try
|
||||
{
|
||||
CryptographyHelper.AesDecrypt(cipherBytes, password2);
|
||||
Assert.Fail();
|
||||
}
|
||||
catch (CryptographicException)
|
||||
{ }
|
||||
|
||||
try
|
||||
{
|
||||
CryptographyHelper.AesDecrypt(cipherStr, password2);
|
||||
Assert.Fail();
|
||||
}
|
||||
catch (CryptographicException)
|
||||
{ }
|
||||
|
||||
// assert
|
||||
CollectionAssert.AreEqual(expectedBytes, plainBytes1);
|
||||
Assert.AreEqual("ABC", plainStr1);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldEncryptDecryptAesBytes()
|
||||
{
|
||||
// arrange
|
||||
byte[] plain = [0xaf, 0xfe];
|
||||
string password = "P@ssw0rd!";
|
||||
|
||||
// act
|
||||
byte[] cipher1 = CryptographyHelper.AesEncrypt(plain, password);
|
||||
byte[] cipher2 = CryptographyHelper.AesEncrypt(plain, password);
|
||||
|
||||
byte[] plain1 = CryptographyHelper.AesDecrypt(cipher1, password);
|
||||
byte[] plain2 = CryptographyHelper.AesDecrypt(cipher2, password);
|
||||
|
||||
// assert
|
||||
CollectionAssert.AreNotEqual(cipher1, cipher2);
|
||||
Assert.AreEqual(24, cipher1.Length);
|
||||
Assert.AreEqual(24, cipher2.Length);
|
||||
CollectionAssert.AreEqual(plain, plain1);
|
||||
CollectionAssert.AreEqual(plain, plain2);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldEncryptDecryptAesString()
|
||||
{
|
||||
// arrange
|
||||
string plain = "ABC";
|
||||
string password = "P@ssw0rd!";
|
||||
|
||||
// act
|
||||
string cipher1 = CryptographyHelper.AesEncrypt(plain, password);
|
||||
string cipher2 = CryptographyHelper.AesEncrypt(plain, password);
|
||||
|
||||
string plain1 = CryptographyHelper.AesDecrypt(cipher1, password);
|
||||
string plain2 = CryptographyHelper.AesDecrypt(cipher2, password);
|
||||
|
||||
// assert
|
||||
Assert.AreNotEqual(cipher1, cipher2);
|
||||
Assert.AreEqual(32, cipher1.Length);
|
||||
Assert.AreEqual(32, cipher2.Length);
|
||||
Assert.AreEqual(plain, plain1);
|
||||
Assert.AreEqual(plain, plain2);
|
||||
}
|
||||
|
||||
#endregion AES
|
||||
|
||||
#region TripleDES
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldEncryptTdesWithoutSalt() // required to test the encryption itself
|
||||
{
|
||||
// arrange
|
||||
using var _ = CryptographyHelperSaltMock.Create(0);
|
||||
|
||||
byte[] bytes = [0xaf, 0xfe];
|
||||
string str = "ABC";
|
||||
string password1 = "P@ssw0rd!";
|
||||
string password2 = "P@ssw0rd";
|
||||
|
||||
byte[] expectedBytes = [0xbf, 0x59, 0x1f, 0x48, 0x69, 0xab, 0x18, 0xc7];
|
||||
|
||||
// act
|
||||
byte[] cipherBytes1 = CryptographyHelper.TripleDesEncrypt(bytes, password1);
|
||||
string cipherStr1 = CryptographyHelper.TripleDesEncrypt(str, password1);
|
||||
byte[] cipherBytes2 = CryptographyHelper.TripleDesEncrypt(bytes, password2);
|
||||
string cipherStr2 = CryptographyHelper.TripleDesEncrypt(str, password2);
|
||||
|
||||
// assert
|
||||
CollectionAssert.AreEqual(expectedBytes, cipherBytes1);
|
||||
Assert.AreEqual("1l74soBuuEI=", cipherStr1);
|
||||
|
||||
CollectionAssert.AreNotEqual(expectedBytes, cipherBytes2);
|
||||
Assert.AreNotEqual("1l74soBuuEI=", cipherStr2);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldDecryptTdesWithoutSalt() // required to test the decryption itself
|
||||
{
|
||||
// arrange
|
||||
using var _ = CryptographyHelperSaltMock.Create(0);
|
||||
|
||||
string cipherStr = "1l74soBuuEI=";
|
||||
byte[] cipherBytes = [0xbf, 0x59, 0x1f, 0x48, 0x69, 0xab, 0x18, 0xc7];
|
||||
|
||||
string password1 = "P@ssw0rd!";
|
||||
string password2 = "P@ssw0rd";
|
||||
|
||||
byte[] expectedBytes = [0xaf, 0xfe];
|
||||
|
||||
// act
|
||||
byte[] plainBytes1 = CryptographyHelper.TripleDesDecrypt(cipherBytes, password1);
|
||||
string plainStr1 = CryptographyHelper.TripleDesDecrypt(cipherStr, password1);
|
||||
|
||||
try
|
||||
{
|
||||
CryptographyHelper.TripleDesDecrypt(cipherBytes, password2);
|
||||
Assert.Fail();
|
||||
}
|
||||
catch (CryptographicException)
|
||||
{ }
|
||||
|
||||
try
|
||||
{
|
||||
CryptographyHelper.TripleDesDecrypt(cipherStr, password2);
|
||||
Assert.Fail();
|
||||
}
|
||||
catch (CryptographicException)
|
||||
{ }
|
||||
|
||||
// assert
|
||||
CollectionAssert.AreEqual(expectedBytes, plainBytes1);
|
||||
Assert.AreEqual("ABC", plainStr1);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldEncryptDecryptTdesBytes()
|
||||
{
|
||||
// arrange
|
||||
byte[] plain = [0xaf, 0xfe];
|
||||
string password = "P@ssw0rd!";
|
||||
|
||||
// act
|
||||
byte[] cipher1 = CryptographyHelper.TripleDesEncrypt(plain, password);
|
||||
byte[] cipher2 = CryptographyHelper.TripleDesEncrypt(plain, password);
|
||||
|
||||
byte[] plain1 = CryptographyHelper.TripleDesDecrypt(cipher1, password);
|
||||
byte[] plain2 = CryptographyHelper.TripleDesDecrypt(cipher2, password);
|
||||
|
||||
// assert
|
||||
CollectionAssert.AreNotEqual(cipher1, cipher2);
|
||||
Assert.AreEqual(16, cipher1.Length);
|
||||
Assert.AreEqual(16, cipher2.Length);
|
||||
CollectionAssert.AreEqual(plain, plain1);
|
||||
CollectionAssert.AreEqual(plain, plain2);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldEncryptDecryptTdesString()
|
||||
{
|
||||
// arrange
|
||||
string plain = "ABC";
|
||||
string password = "P@ssw0rd!";
|
||||
|
||||
// act
|
||||
string cipher1 = CryptographyHelper.TripleDesEncrypt(plain, password);
|
||||
string cipher2 = CryptographyHelper.TripleDesEncrypt(plain, password);
|
||||
|
||||
string plain1 = CryptographyHelper.TripleDesDecrypt(cipher1, password);
|
||||
string plain2 = CryptographyHelper.TripleDesDecrypt(cipher2, password);
|
||||
|
||||
// assert
|
||||
Assert.AreNotEqual(cipher1, cipher2);
|
||||
Assert.AreEqual(24, cipher1.Length);
|
||||
Assert.AreEqual(24, cipher2.Length);
|
||||
Assert.AreEqual(plain, plain1);
|
||||
Assert.AreEqual(plain, plain2);
|
||||
}
|
||||
|
||||
#endregion TripleDES
|
||||
|
||||
#endregion Encryption
|
||||
|
||||
#region Hash
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldReturnMd5Hash()
|
||||
{
|
||||
// arrange
|
||||
string text = "Hello World!";
|
||||
byte[] bytes = [0xde, 0xad, 0xbe, 0xef];
|
||||
string fileName = Path.GetTempFileName();
|
||||
|
||||
// act
|
||||
string textHash = CryptographyHelper.Md5(text);
|
||||
string bytesHash = CryptographyHelper.Md5(bytes);
|
||||
|
||||
File.WriteAllText(fileName, text);
|
||||
string fileHash = CryptographyHelper.Md5File(fileName);
|
||||
File.Delete(fileName);
|
||||
|
||||
// assert
|
||||
Assert.AreEqual("ed076287532e86365e841e92bfc50d8c", textHash);
|
||||
Assert.AreEqual("2f249230a8e7c2bf6005ccd2679259ec", bytesHash);
|
||||
Assert.AreEqual("ed076287532e86365e841e92bfc50d8c", fileHash);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldReturnSha1Hash()
|
||||
{
|
||||
// arrange
|
||||
string text = "Hello World!";
|
||||
byte[] bytes = [0xde, 0xad, 0xbe, 0xef];
|
||||
string fileName = Path.GetTempFileName();
|
||||
|
||||
// act
|
||||
string textHash = CryptographyHelper.Sha1(text);
|
||||
string bytesHash = CryptographyHelper.Sha1(bytes);
|
||||
|
||||
File.WriteAllText(fileName, text);
|
||||
string fileHash = CryptographyHelper.Sha1File(fileName);
|
||||
File.Delete(fileName);
|
||||
|
||||
// assert
|
||||
Assert.AreEqual("2ef7bde608ce5404e97d5f042f95f89f1c232871", textHash);
|
||||
Assert.AreEqual("d78f8bb992a56a597f6c7a1fb918bb78271367eb", bytesHash);
|
||||
Assert.AreEqual("2ef7bde608ce5404e97d5f042f95f89f1c232871", fileHash);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldReturnSha256Hash()
|
||||
{
|
||||
// arrange
|
||||
string text = "Hello World!";
|
||||
byte[] bytes = [0xde, 0xad, 0xbe, 0xef];
|
||||
string fileName = Path.GetTempFileName();
|
||||
|
||||
// act
|
||||
string textHash = CryptographyHelper.Sha256(text);
|
||||
string bytesHash = CryptographyHelper.Sha256(bytes);
|
||||
|
||||
File.WriteAllText(fileName, text);
|
||||
string fileHash = CryptographyHelper.Sha256File(fileName);
|
||||
File.Delete(fileName);
|
||||
|
||||
// assert
|
||||
Assert.AreEqual("7f83b1657ff1fc53b92dc18148a1d65dfc2d4b1fa3d677284addd200126d9069", textHash);
|
||||
Assert.AreEqual("5f78c33274e43fa9de5659265c1d917e25c03722dcb0b8d27db8d5feaa813953", bytesHash);
|
||||
Assert.AreEqual("7f83b1657ff1fc53b92dc18148a1d65dfc2d4b1fa3d677284addd200126d9069", fileHash);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldReturnSha512Hash()
|
||||
{
|
||||
// arrange
|
||||
string text = "Hello World!";
|
||||
byte[] bytes = [0xde, 0xad, 0xbe, 0xef];
|
||||
string fileName = Path.GetTempFileName();
|
||||
|
||||
// act
|
||||
string textHash = CryptographyHelper.Sha512(text);
|
||||
string bytesHash = CryptographyHelper.Sha512(bytes);
|
||||
|
||||
File.WriteAllText(fileName, text);
|
||||
string fileHash = CryptographyHelper.Sha512File(fileName);
|
||||
File.Delete(fileName);
|
||||
|
||||
// assert
|
||||
Assert.AreEqual("861844d6704e8573fec34d967e20bcfef3d424cf48be04e6dc08f2bd58c729743371015ead891cc3cf1c9d34b49264b510751b1ff9e537937bc46b5d6ff4ecc8", textHash);
|
||||
Assert.AreEqual("1284b2d521535196f22175d5f558104220a6ad7680e78b49fa6f20e57ea7b185d71ec1edb137e70eba528dedb141f5d2f8bb53149d262932b27cf41fed96aa7f", bytesHash);
|
||||
Assert.AreEqual("861844d6704e8573fec34d967e20bcfef3d424cf48be04e6dc08f2bd58c729743371015ead891cc3cf1c9d34b49264b510751b1ff9e537937bc46b5d6ff4ecc8", fileHash);
|
||||
}
|
||||
|
||||
#endregion Hash
|
||||
|
||||
#region Random
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldReturnRandomBytes()
|
||||
{
|
||||
// arrange
|
||||
int length1 = 12;
|
||||
int length2 = 12;
|
||||
int length3 = 42;
|
||||
|
||||
// act
|
||||
byte[] bytes1 = CryptographyHelper.GetRandomBytes(length1);
|
||||
byte[] bytes2 = CryptographyHelper.GetRandomBytes(length2);
|
||||
byte[] bytes3 = CryptographyHelper.GetRandomBytes(length3);
|
||||
|
||||
// assert
|
||||
Assert.AreEqual(length1, bytes1.Length);
|
||||
Assert.AreEqual(length2, bytes2.Length);
|
||||
Assert.AreEqual(length3, bytes3.Length);
|
||||
|
||||
Assert.IsTrue(bytes1.Length == bytes2.Length);
|
||||
CollectionAssert.AreNotEqual(bytes1, bytes2);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldReturnRandomString()
|
||||
{
|
||||
// arrange
|
||||
int length1 = 12;
|
||||
int length2 = 12;
|
||||
int length3 = 42;
|
||||
|
||||
// act
|
||||
string str1 = CryptographyHelper.GetRandomString(length1);
|
||||
string str2 = CryptographyHelper.GetRandomString(length2);
|
||||
string str3 = CryptographyHelper.GetRandomString(length3);
|
||||
|
||||
// assert
|
||||
Assert.AreEqual(length1, str1.Length);
|
||||
Assert.AreEqual(length2, str2.Length);
|
||||
Assert.AreEqual(length3, str3.Length);
|
||||
|
||||
Assert.IsTrue(str1.Length == str2.Length);
|
||||
Assert.IsFalse(str1 == str2);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldReturnRandomStringWithPool()
|
||||
{
|
||||
// arrange
|
||||
int length = 12;
|
||||
string pool = "0123456789abcdef";
|
||||
|
||||
// act
|
||||
string str1 = CryptographyHelper.GetRandomString(length, pool);
|
||||
string str2 = CryptographyHelper.GetRandomString(length, pool);
|
||||
|
||||
// assert
|
||||
Assert.AreEqual(length, str1.Length);
|
||||
Assert.AreEqual(length, str2.Length);
|
||||
Assert.IsFalse(str1 == str2);
|
||||
Assert.IsFalse(RandomStringWithPoolRegex().IsMatch(str1));
|
||||
Assert.IsFalse(RandomStringWithPoolRegex().IsMatch(str2));
|
||||
}
|
||||
|
||||
#endregion Random
|
||||
|
||||
#region Secure probing
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldTakeSameTimeToCompareString()
|
||||
{
|
||||
// arrange
|
||||
string str1 = "Hello World!";
|
||||
string str2 = "Hello World!";
|
||||
string str3 = "Hallo World!";
|
||||
string str4 = "Hello World?";
|
||||
string nullStr = null;
|
||||
string strLen = "Hello World";
|
||||
|
||||
var sw = new Stopwatch();
|
||||
|
||||
// act
|
||||
bool nullCompare = CryptographyHelper.SecureEquals(nullStr, str1);
|
||||
bool lenCompare = CryptographyHelper.SecureEquals(strLen, str1);
|
||||
|
||||
sw.Start();
|
||||
bool compare1 = CryptographyHelper.SecureEquals(str1, str2);
|
||||
long time1 = sw.ElapsedMilliseconds;
|
||||
|
||||
bool compare2 = CryptographyHelper.SecureEquals(str1, str3);
|
||||
long time2 = sw.ElapsedMilliseconds;
|
||||
|
||||
bool compare3 = CryptographyHelper.SecureEquals(str1, str4);
|
||||
long time3 = sw.ElapsedMilliseconds;
|
||||
sw.Stop();
|
||||
|
||||
// assert
|
||||
Assert.IsFalse(nullCompare);
|
||||
Assert.IsFalse(lenCompare);
|
||||
Assert.IsTrue(compare1);
|
||||
Assert.IsFalse(compare2);
|
||||
Assert.IsFalse(compare3);
|
||||
|
||||
Assert.AreEqual(time1, time2);
|
||||
Assert.AreEqual(time1, time3);
|
||||
Assert.AreEqual(time2, time3);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldTakeSameTimeToCompareBytes()
|
||||
{
|
||||
// arrange
|
||||
byte[] bytes1 = CryptographyHelper.GetRandomBytes(200);
|
||||
byte[] bytes2 = new byte[bytes1.Length];
|
||||
byte[] bytes3 = new byte[bytes1.Length];
|
||||
byte[] bytes4 = new byte[bytes1.Length];
|
||||
byte[] nullBytes = null;
|
||||
byte[] lenBytes = new byte[bytes1.Length + 1];
|
||||
|
||||
Array.Copy(bytes1, bytes2, bytes1.Length);
|
||||
Array.Copy(bytes1, bytes3, bytes1.Length);
|
||||
Array.Copy(bytes1, bytes4, bytes1.Length);
|
||||
|
||||
bytes3[10] = (byte)(bytes1[10] + 1);
|
||||
bytes4[190] = (byte)(bytes1[190] + 1);
|
||||
|
||||
var sw = new Stopwatch();
|
||||
|
||||
// act
|
||||
bool nullCompare = CryptographyHelper.SecureEquals(nullBytes, bytes1);
|
||||
bool lenCompare = CryptographyHelper.SecureEquals(lenBytes, bytes1);
|
||||
|
||||
sw.Start();
|
||||
bool compare1 = CryptographyHelper.SecureEquals(bytes1, bytes2);
|
||||
long time1 = sw.ElapsedMilliseconds;
|
||||
|
||||
bool compare2 = CryptographyHelper.SecureEquals(bytes1, bytes3);
|
||||
long time2 = sw.ElapsedMilliseconds;
|
||||
|
||||
bool compare3 = CryptographyHelper.SecureEquals(bytes1, bytes4);
|
||||
long time3 = sw.ElapsedMilliseconds;
|
||||
sw.Stop();
|
||||
|
||||
// assert
|
||||
Assert.IsFalse(nullCompare);
|
||||
Assert.IsFalse(lenCompare);
|
||||
Assert.IsTrue(compare1);
|
||||
Assert.IsFalse(compare2);
|
||||
Assert.IsFalse(compare3);
|
||||
|
||||
Assert.AreEqual(time1, time2);
|
||||
Assert.AreEqual(time1, time3);
|
||||
Assert.AreEqual(time2, time3);
|
||||
}
|
||||
|
||||
#endregion Secure probing
|
||||
|
||||
#endregion Static
|
||||
|
||||
#region Instance
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldCreateDefaultFile()
|
||||
{
|
||||
// arrange
|
||||
string executingAssemblyDir = AppContext.BaseDirectory;
|
||||
string filePath = Path.Combine(executingAssemblyDir, "crypto.key");
|
||||
|
||||
// act
|
||||
bool fileExistsBefore = File.Exists(filePath);
|
||||
var helper = new CryptographyHelper();
|
||||
bool fileExistsAfter = File.Exists(filePath);
|
||||
string content = fileExistsAfter ? File.ReadAllText(filePath) : null;
|
||||
File.Delete(filePath);
|
||||
|
||||
// assert
|
||||
Assert.IsFalse(fileExistsBefore);
|
||||
Assert.IsNotNull(helper);
|
||||
Assert.IsTrue(fileExistsAfter);
|
||||
Assert.IsTrue(!string.IsNullOrWhiteSpace(content));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldEncryptAesUsingKeyFile()
|
||||
{
|
||||
// arrange
|
||||
string str = "Hello World!";
|
||||
byte[] bytes = CryptographyHelper.GetRandomBytes(32);
|
||||
|
||||
string password = File.ReadAllText(_keyFile);
|
||||
|
||||
// act
|
||||
string cipherStr = _cryptoHelper.EncryptAes(str);
|
||||
byte[] cipherBytes = _cryptoHelper.EncryptAes(bytes);
|
||||
|
||||
string plainStr = CryptographyHelper.AesDecrypt(cipherStr, password);
|
||||
byte[] plainBytes = CryptographyHelper.AesDecrypt(cipherBytes, password);
|
||||
|
||||
// assert
|
||||
Assert.AreNotEqual(str, cipherStr);
|
||||
Assert.AreEqual(str, plainStr);
|
||||
CollectionAssert.AreNotEqual(bytes, cipherBytes);
|
||||
CollectionAssert.AreEqual(bytes, plainBytes);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldDecryptAesUsingKeyFile()
|
||||
{
|
||||
// arrange
|
||||
string str = "Hello World!";
|
||||
byte[] bytes = CryptographyHelper.GetRandomBytes(32);
|
||||
|
||||
string password = File.ReadAllText(_keyFile);
|
||||
|
||||
// act
|
||||
string cipherStr = CryptographyHelper.AesEncrypt(str, password);
|
||||
byte[] cipherBytes = CryptographyHelper.AesEncrypt(bytes, password);
|
||||
|
||||
string plainStr = _cryptoHelper.DecryptAes(cipherStr);
|
||||
byte[] plainBytes = _cryptoHelper.DecryptAes(cipherBytes);
|
||||
|
||||
// assert
|
||||
Assert.AreNotEqual(str, cipherStr);
|
||||
Assert.AreEqual(str, plainStr);
|
||||
CollectionAssert.AreNotEqual(bytes, cipherBytes);
|
||||
CollectionAssert.AreEqual(bytes, plainBytes);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldEncryptTdesUsingKeyFile()
|
||||
{
|
||||
// arrange
|
||||
string str = "Hello World!";
|
||||
byte[] bytes = CryptographyHelper.GetRandomBytes(32);
|
||||
|
||||
string password = File.ReadAllText(_keyFile);
|
||||
|
||||
// act
|
||||
string cipherStr = _cryptoHelper.EncryptTripleDes(str);
|
||||
byte[] cipherBytes = _cryptoHelper.EncryptTripleDes(bytes);
|
||||
|
||||
string plainStr = CryptographyHelper.TripleDesDecrypt(cipherStr, password);
|
||||
byte[] plainBytes = CryptographyHelper.TripleDesDecrypt(cipherBytes, password);
|
||||
|
||||
// assert
|
||||
Assert.AreNotEqual(str, cipherStr);
|
||||
Assert.AreEqual(str, plainStr);
|
||||
CollectionAssert.AreNotEqual(bytes, cipherBytes);
|
||||
CollectionAssert.AreEqual(bytes, plainBytes);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldDecryptTdesUsingKeyFile()
|
||||
{
|
||||
// arrange
|
||||
string str = "Hello World!";
|
||||
byte[] bytes = CryptographyHelper.GetRandomBytes(32);
|
||||
|
||||
string password = File.ReadAllText(_keyFile);
|
||||
|
||||
// act
|
||||
string cipherStr = CryptographyHelper.TripleDesEncrypt(str, password);
|
||||
byte[] cipherBytes = CryptographyHelper.TripleDesEncrypt(bytes, password);
|
||||
|
||||
string plainStr = _cryptoHelper.DecryptTripleDes(cipherStr);
|
||||
byte[] plainBytes = _cryptoHelper.DecryptTripleDes(cipherBytes);
|
||||
|
||||
// assert
|
||||
Assert.AreNotEqual(str, cipherStr);
|
||||
Assert.AreEqual(str, plainStr);
|
||||
CollectionAssert.AreNotEqual(bytes, cipherBytes);
|
||||
CollectionAssert.AreEqual(bytes, plainBytes);
|
||||
}
|
||||
|
||||
[GeneratedRegex("[^0-9a-f]")]
|
||||
private static partial Regex RandomStringWithPoolRegex();
|
||||
|
||||
#endregion Instance
|
||||
}
|
||||
}
|
||||
283
test/AMWD.Common.Tests/Utilities/DelayedTaskTest.cs
Normal file
283
test/AMWD.Common.Tests/Utilities/DelayedTaskTest.cs
Normal file
@@ -0,0 +1,283 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using AMWD.Common.Utilities;
|
||||
|
||||
namespace AMWD.Common.Tests.Utilities
|
||||
{
|
||||
[TestClass]
|
||||
public class DelayedTaskTest
|
||||
{
|
||||
[TestMethod]
|
||||
public void ShouldCreateNewDelayedTaskNotStarting()
|
||||
{
|
||||
// arrange
|
||||
int executionCount = 0;
|
||||
var delay = TimeSpan.FromMilliseconds(100);
|
||||
void Action() { executionCount++; }
|
||||
|
||||
// act
|
||||
var cts = new CancellationTokenSource(delay.Add(TimeSpan.FromSeconds(1)));
|
||||
var delayedTask = DelayedTask.Create(Action, delay);
|
||||
SpinWait.SpinUntil(() => executionCount > 0 || cts.IsCancellationRequested);
|
||||
|
||||
// assert
|
||||
Assert.IsNotNull(delayedTask);
|
||||
Assert.AreEqual(delay, delayedTask.Delay);
|
||||
Assert.IsFalse(delayedTask.IsRunning);
|
||||
Assert.IsFalse(delayedTask.IsWaitingToRun);
|
||||
Assert.IsNull(delayedTask.Exception);
|
||||
Assert.AreEqual(0, executionCount);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldCreateNewDelayedTaskStarting()
|
||||
{
|
||||
// arrange
|
||||
int executionCount = 0;
|
||||
var delay = TimeSpan.FromMilliseconds(100);
|
||||
void Action() { executionCount++; }
|
||||
|
||||
// act
|
||||
var cts = new CancellationTokenSource(delay.Add(TimeSpan.FromSeconds(1)));
|
||||
var delayedTask = DelayedTask.Run(Action, delay);
|
||||
SpinWait.SpinUntil(() => executionCount > 0 || cts.IsCancellationRequested);
|
||||
|
||||
// assert
|
||||
Assert.IsNotNull(delayedTask);
|
||||
Assert.AreEqual(delay, delayedTask.Delay);
|
||||
Assert.IsFalse(delayedTask.IsRunning);
|
||||
Assert.IsFalse(delayedTask.IsWaitingToRun);
|
||||
Assert.IsNull(delayedTask.Exception);
|
||||
Assert.AreEqual(1, executionCount);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldRunOnceAfterReset()
|
||||
{
|
||||
// arrange
|
||||
int executionCount = 0;
|
||||
var delay = TimeSpan.FromMilliseconds(100);
|
||||
void Action() { executionCount++; }
|
||||
|
||||
// act
|
||||
var cts = new CancellationTokenSource(delay.Add(TimeSpan.FromSeconds(1)));
|
||||
var delayedTask = DelayedTask.Create(Action, delay);
|
||||
delayedTask.Reset();
|
||||
SpinWait.SpinUntil(() => executionCount > 0 || cts.IsCancellationRequested);
|
||||
|
||||
// assert
|
||||
Assert.IsNotNull(delayedTask);
|
||||
Assert.AreEqual(delay, delayedTask.Delay);
|
||||
Assert.IsFalse(delayedTask.IsRunning);
|
||||
Assert.IsFalse(delayedTask.IsWaitingToRun);
|
||||
Assert.IsNull(delayedTask.Exception);
|
||||
Assert.AreEqual(1, executionCount);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldCancelWaitingTask()
|
||||
{
|
||||
// arrange
|
||||
int executionCount = 0;
|
||||
var delay = TimeSpan.FromMilliseconds(100);
|
||||
void Action() { executionCount++; }
|
||||
|
||||
// act
|
||||
var cts = new CancellationTokenSource(delay.Add(TimeSpan.FromSeconds(1)));
|
||||
var delayedTask = DelayedTask.Run(Action, delay);
|
||||
delayedTask.Cancel();
|
||||
SpinWait.SpinUntil(() => executionCount > 0 || cts.IsCancellationRequested);
|
||||
|
||||
// assert
|
||||
Assert.IsNotNull(delayedTask);
|
||||
Assert.AreEqual(delay, delayedTask.Delay);
|
||||
Assert.IsFalse(delayedTask.IsRunning);
|
||||
Assert.IsFalse(delayedTask.IsWaitingToRun);
|
||||
Assert.IsNull(delayedTask.Exception);
|
||||
Assert.AreEqual(0, executionCount);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task ShouldResetRunningDelay()
|
||||
{
|
||||
// arrange
|
||||
int executionCount = 0;
|
||||
var sw = new Stopwatch();
|
||||
|
||||
var delay = TimeSpan.FromMilliseconds(200);
|
||||
void Action() { sw.Stop(); executionCount++; }
|
||||
|
||||
// act
|
||||
var cts = new CancellationTokenSource(delay.Add(TimeSpan.FromSeconds(1)));
|
||||
sw.Start();
|
||||
var delayedTask = DelayedTask.Run(Action, delay);
|
||||
await Task.Delay(50);
|
||||
delayedTask.Reset();
|
||||
SpinWait.SpinUntil(() => executionCount > 0 || cts.IsCancellationRequested);
|
||||
|
||||
// assert
|
||||
Assert.IsNotNull(delayedTask);
|
||||
Assert.AreEqual(delay, delayedTask.Delay);
|
||||
Assert.IsFalse(delayedTask.IsRunning);
|
||||
Assert.IsFalse(delayedTask.IsWaitingToRun);
|
||||
Assert.IsNull(delayedTask.Exception);
|
||||
Assert.AreEqual(1, executionCount);
|
||||
// delta of 60ms as the precision below 50ms is really bad for System.Timer
|
||||
Assert.AreEqual(250, sw.ElapsedMilliseconds, 60);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task ShouldNotExecutedImmediateOnCreated()
|
||||
{
|
||||
// arrange
|
||||
int executionCount = 0;
|
||||
var sw = new Stopwatch();
|
||||
|
||||
var delay = TimeSpan.FromMilliseconds(200);
|
||||
void Action() { sw.Stop(); executionCount++; }
|
||||
|
||||
// act
|
||||
var cts = new CancellationTokenSource(delay.Add(TimeSpan.FromSeconds(1)));
|
||||
sw.Start();
|
||||
var delayedTask = DelayedTask.Create(Action, delay);
|
||||
await Task.Delay(50);
|
||||
bool isSuccess = delayedTask.ExecutePending();
|
||||
SpinWait.SpinUntil(() => executionCount > 0 || cts.IsCancellationRequested);
|
||||
sw.Stop();
|
||||
|
||||
// assert
|
||||
Assert.IsNotNull(delayedTask);
|
||||
Assert.AreEqual(delay, delayedTask.Delay);
|
||||
Assert.IsFalse(delayedTask.IsRunning);
|
||||
Assert.IsFalse(delayedTask.IsWaitingToRun);
|
||||
Assert.IsNull(delayedTask.Exception);
|
||||
Assert.AreEqual(0, executionCount);
|
||||
// delta of 60ms as the precision below 50ms is really bad for System.Timer
|
||||
Assert.AreEqual(1250, sw.ElapsedMilliseconds, 60);
|
||||
Assert.IsFalse(isSuccess);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task ShouldExecuteImmediateOnExecutePendingWhenRunning()
|
||||
{
|
||||
// arrange
|
||||
int executionCount = 0;
|
||||
var sw = new Stopwatch();
|
||||
|
||||
var delay = TimeSpan.FromMilliseconds(200);
|
||||
void Action() { sw.Stop(); executionCount++; }
|
||||
|
||||
// act
|
||||
var cts = new CancellationTokenSource(delay.Add(TimeSpan.FromSeconds(1)));
|
||||
sw.Start();
|
||||
var delayedTask = DelayedTask.Run(Action, delay);
|
||||
await Task.Delay(50);
|
||||
bool isSuccess = delayedTask.ExecutePending();
|
||||
SpinWait.SpinUntil(() => executionCount > 0 || cts.IsCancellationRequested);
|
||||
|
||||
// assert
|
||||
Assert.IsNotNull(delayedTask);
|
||||
Assert.AreEqual(delay, delayedTask.Delay);
|
||||
Assert.IsFalse(delayedTask.IsRunning);
|
||||
Assert.IsFalse(delayedTask.IsWaitingToRun);
|
||||
Assert.IsNull(delayedTask.Exception);
|
||||
Assert.AreEqual(1, executionCount);
|
||||
// delta of 60ms as the precision below 50ms is really bad for System.Timer
|
||||
Assert.AreEqual(50, sw.ElapsedMilliseconds, 60);
|
||||
Assert.IsTrue(isSuccess);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldReturnAnAwaiter()
|
||||
{
|
||||
// arrange
|
||||
int executionCount = 0;
|
||||
var delay = TimeSpan.FromMilliseconds(100);
|
||||
void Action() { executionCount++; }
|
||||
var delayedTask = DelayedTask.Create(Action, delay);
|
||||
|
||||
// act
|
||||
delayedTask.Reset();
|
||||
var awaiter = delayedTask.GetAwaiter();
|
||||
SpinWait.SpinUntil(() => awaiter.IsCompleted);
|
||||
|
||||
// assert
|
||||
Assert.IsNotNull(delayedTask);
|
||||
Assert.IsNotNull(awaiter);
|
||||
Assert.IsFalse(delayedTask.IsRunning);
|
||||
Assert.IsFalse(delayedTask.IsWaitingToRun);
|
||||
Assert.IsNull(delayedTask.Exception);
|
||||
Assert.AreEqual(1, executionCount);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldHaveAnExceptionSet()
|
||||
{
|
||||
// arrange
|
||||
var delay = TimeSpan.FromMilliseconds(100);
|
||||
static void Action() { throw new Exception("TEST :D"); }
|
||||
|
||||
// act
|
||||
var delayedTask = DelayedTask.Run(Action, delay);
|
||||
|
||||
var awaiter = delayedTask.GetAwaiter();
|
||||
SpinWait.SpinUntil(() => awaiter.IsCompleted);
|
||||
|
||||
// assert
|
||||
Assert.IsNotNull(delayedTask);
|
||||
Assert.IsFalse(delayedTask.IsRunning);
|
||||
Assert.IsFalse(delayedTask.IsWaitingToRun);
|
||||
Assert.IsNotNull(delayedTask.Exception);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldUseExceptionHandler()
|
||||
{
|
||||
// arrange
|
||||
string exceptionText = null;
|
||||
var delay = TimeSpan.FromMilliseconds(100);
|
||||
void Action() { throw new Exception("TEST :D"); }
|
||||
void ExceptionHandler(Exception ex) { exceptionText = ex.Message; }
|
||||
|
||||
// act
|
||||
var delayedTask = DelayedTask.Run(Action, delay)
|
||||
.WithExceptionHandler(ExceptionHandler);
|
||||
|
||||
var awaiter = delayedTask.GetAwaiter();
|
||||
SpinWait.SpinUntil(() => awaiter.IsCompleted);
|
||||
|
||||
// assert
|
||||
Assert.IsNotNull(delayedTask);
|
||||
Assert.IsFalse(delayedTask.IsRunning);
|
||||
Assert.IsFalse(delayedTask.IsWaitingToRun);
|
||||
Assert.IsNotNull(delayedTask.Exception);
|
||||
Assert.AreEqual("TEST :D", exceptionText);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task ShouldReturnNormalTask()
|
||||
{
|
||||
// arrange
|
||||
int executionCount = 0;
|
||||
var delay = TimeSpan.FromMilliseconds(100);
|
||||
void Action() { executionCount++; }
|
||||
var delayedTask = DelayedTask.Create(Action, delay);
|
||||
|
||||
// act
|
||||
delayedTask.Reset();
|
||||
var task = delayedTask.Task;
|
||||
await task;
|
||||
|
||||
// assert
|
||||
Assert.IsNotNull(delayedTask);
|
||||
Assert.IsNotNull(task);
|
||||
Assert.IsInstanceOfType(task, typeof(Task));
|
||||
Assert.IsFalse(delayedTask.IsRunning);
|
||||
Assert.IsFalse(delayedTask.IsWaitingToRun);
|
||||
Assert.AreEqual(1, executionCount);
|
||||
Assert.AreEqual(TaskStatus.RanToCompletion, task.Status);
|
||||
}
|
||||
}
|
||||
}
|
||||
130
test/AMWD.Common.Tests/Utilities/DelayedTaskWithResultTest.cs
Normal file
130
test/AMWD.Common.Tests/Utilities/DelayedTaskWithResultTest.cs
Normal file
@@ -0,0 +1,130 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using AMWD.Common.Utilities;
|
||||
|
||||
namespace AMWD.Common.Tests.Utilities
|
||||
{
|
||||
[TestClass]
|
||||
public class DelayedTaskWithResultTest
|
||||
{
|
||||
[TestMethod]
|
||||
public void ShouldCreateNewDelayedTaskNotStarting()
|
||||
{
|
||||
// arrange
|
||||
int executionCount = 0;
|
||||
var delay = TimeSpan.FromMilliseconds(100);
|
||||
int[] Function() { executionCount++; return [42, 21]; }
|
||||
|
||||
// act
|
||||
var cts = new CancellationTokenSource(delay.Add(TimeSpan.FromSeconds(1)));
|
||||
var delayedTaskWithResult = DelayedTask.Create(Function, delay);
|
||||
SpinWait.SpinUntil(() => executionCount > 0 || cts.IsCancellationRequested);
|
||||
|
||||
// assert
|
||||
Assert.IsNotNull(delayedTaskWithResult);
|
||||
Assert.AreEqual(delay, delayedTaskWithResult.Delay);
|
||||
Assert.IsFalse(delayedTaskWithResult.IsRunning);
|
||||
Assert.IsFalse(delayedTaskWithResult.IsWaitingToRun);
|
||||
Assert.IsNull(delayedTaskWithResult.Exception);
|
||||
Assert.AreEqual(0, executionCount);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Performance", "CA1861")]
|
||||
public async Task ShouldCreateNewDelayedTaskStarting()
|
||||
{
|
||||
// arrange
|
||||
int executionCount = 0;
|
||||
var delay = TimeSpan.FromMilliseconds(100);
|
||||
int[] Function() { executionCount++; return [42, 21]; }
|
||||
|
||||
// act
|
||||
var delayedTaskWithResult = DelayedTask.Run(Function, delay);
|
||||
int[] result = await delayedTaskWithResult;
|
||||
|
||||
// assert
|
||||
Assert.IsNotNull(delayedTaskWithResult);
|
||||
Assert.AreEqual(delay, delayedTaskWithResult.Delay);
|
||||
Assert.IsFalse(delayedTaskWithResult.IsRunning);
|
||||
Assert.IsFalse(delayedTaskWithResult.IsWaitingToRun);
|
||||
Assert.IsNull(delayedTaskWithResult.Exception);
|
||||
Assert.AreEqual(1, executionCount);
|
||||
CollectionAssert.AreEqual(new[] { 42, 21 }, result);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldHaveAnExceptionSet()
|
||||
{
|
||||
// arrange
|
||||
var delay = TimeSpan.FromMilliseconds(100);
|
||||
#pragma warning disable CS0162 // Unreachable Code detected.
|
||||
static int[] Function() { throw new Exception("TEST :D"); return [42, 21]; }
|
||||
#pragma warning restore CS0162 // Unreachable Code detected
|
||||
|
||||
// act
|
||||
var delayedTaskWithResult = DelayedTask.Run(Function, delay);
|
||||
|
||||
var awaiter = delayedTaskWithResult.GetAwaiter();
|
||||
SpinWait.SpinUntil(() => awaiter.IsCompleted);
|
||||
|
||||
// assert
|
||||
Assert.IsNotNull(delayedTaskWithResult);
|
||||
Assert.IsFalse(delayedTaskWithResult.IsRunning);
|
||||
Assert.IsFalse(delayedTaskWithResult.IsWaitingToRun);
|
||||
Assert.IsNotNull(delayedTaskWithResult.Exception);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldUseExceptionHandler()
|
||||
{
|
||||
// arrange
|
||||
string exceptionText = null;
|
||||
var delay = TimeSpan.FromMilliseconds(100);
|
||||
#pragma warning disable CS0162 // Unreachable Code detected.
|
||||
static int[] Function() { throw new Exception("TEST :D"); return [42, 21]; }
|
||||
#pragma warning restore CS0162 // Unreachable Code detected
|
||||
void ExceptionHandler(Exception ex) { exceptionText = ex.Message; }
|
||||
|
||||
// act
|
||||
var delayedTaskWithResult = DelayedTask.Run(Function, delay)
|
||||
.WithExceptionHandler(ExceptionHandler);
|
||||
|
||||
var awaiter = delayedTaskWithResult.GetAwaiter();
|
||||
SpinWait.SpinUntil(() => awaiter.IsCompleted);
|
||||
|
||||
// assert
|
||||
Assert.IsNotNull(delayedTaskWithResult);
|
||||
Assert.IsFalse(delayedTaskWithResult.IsRunning);
|
||||
Assert.IsFalse(delayedTaskWithResult.IsWaitingToRun);
|
||||
Assert.IsNotNull(delayedTaskWithResult.Exception);
|
||||
Assert.AreEqual("TEST :D", exceptionText);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Performance", "CA1861")]
|
||||
public async Task ShouldReturnNormalTask()
|
||||
{
|
||||
// arrange
|
||||
int executionCount = 0;
|
||||
var delay = TimeSpan.FromMilliseconds(100);
|
||||
int[] Function() { executionCount++; return [42, 21]; }
|
||||
var delayedTaskWithResult = DelayedTask.Create(Function, delay);
|
||||
|
||||
// act
|
||||
delayedTaskWithResult.Reset();
|
||||
var task = delayedTaskWithResult.Task;
|
||||
int[] result = await task;
|
||||
|
||||
// assert
|
||||
Assert.IsNotNull(delayedTaskWithResult);
|
||||
Assert.IsNotNull(task);
|
||||
Assert.IsInstanceOfType(task, typeof(Task));
|
||||
Assert.IsFalse(delayedTaskWithResult.IsRunning);
|
||||
Assert.IsFalse(delayedTaskWithResult.IsWaitingToRun);
|
||||
Assert.AreEqual(1, executionCount);
|
||||
Assert.AreEqual(TaskStatus.RanToCompletion, task.Status);
|
||||
CollectionAssert.AreEqual(new int[] { 42, 21 }, result);
|
||||
}
|
||||
}
|
||||
}
|
||||
27
test/AMWD.Common.Tests/Utils/CryptographyHelperSaltMock.cs
Normal file
27
test/AMWD.Common.Tests/Utils/CryptographyHelperSaltMock.cs
Normal file
@@ -0,0 +1,27 @@
|
||||
using System;
|
||||
using System.Security.Cryptography;
|
||||
using ReflectionMagic;
|
||||
|
||||
namespace AMWD.Common.Tests.Utils
|
||||
{
|
||||
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
|
||||
internal class CryptographyHelperSaltMock : IDisposable
|
||||
{
|
||||
private readonly int _saltLength;
|
||||
|
||||
private CryptographyHelperSaltMock(int saltLength)
|
||||
{
|
||||
_saltLength = typeof(CryptographyHelper).AsDynamicType()._saltLength;
|
||||
SetSaltLength(saltLength);
|
||||
}
|
||||
|
||||
public static IDisposable Create(int saltLength)
|
||||
=> new CryptographyHelperSaltMock(saltLength);
|
||||
|
||||
public void Dispose()
|
||||
=> SetSaltLength(_saltLength);
|
||||
|
||||
private static void SetSaltLength(int length)
|
||||
=> typeof(CryptographyHelper).AsDynamicType()._saltLength = length;
|
||||
}
|
||||
}
|
||||
11
test/AMWD.Common.Tests/Utils/CustomMultipleAttribute.cs
Normal file
11
test/AMWD.Common.Tests/Utils/CustomMultipleAttribute.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
using System;
|
||||
|
||||
namespace AMWD.Common.Tests.Utils
|
||||
{
|
||||
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
|
||||
[AttributeUsage(AttributeTargets.All, AllowMultiple = true)]
|
||||
internal class CustomMultipleAttribute(string name) : Attribute
|
||||
{
|
||||
public string Name { get; set; } = name;
|
||||
}
|
||||
}
|
||||
53
test/AMWD.Common.Tests/Utils/JsonTestClass.cs
Normal file
53
test/AMWD.Common.Tests/Utils/JsonTestClass.cs
Normal file
@@ -0,0 +1,53 @@
|
||||
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";
|
||||
}
|
||||
|
||||
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
|
||||
internal class JsonErrorClass
|
||||
{
|
||||
private int? _number;
|
||||
|
||||
public int Number
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_number.HasValue)
|
||||
return _number.Value;
|
||||
|
||||
throw new Exception("Null value");
|
||||
}
|
||||
set
|
||||
{
|
||||
_number = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
26
test/AMWD.Common.Tests/Utils/TimeZoneInfoLocalMock.cs
Normal file
26
test/AMWD.Common.Tests/Utils/TimeZoneInfoLocalMock.cs
Normal file
@@ -0,0 +1,26 @@
|
||||
using System;
|
||||
using ReflectionMagic;
|
||||
|
||||
namespace AMWD.Common.Tests.Utils
|
||||
{
|
||||
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
|
||||
internal 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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user