diff --git a/ObjectPrinting.Tests/ObjectPrinterAcceptanceTests.cs b/ObjectPrinting.Tests/ObjectPrinterAcceptanceTests.cs new file mode 100644 index 00000000..25925005 --- /dev/null +++ b/ObjectPrinting.Tests/ObjectPrinterAcceptanceTests.cs @@ -0,0 +1,282 @@ +using System.Globalization; +using ObjectPrinting.Extensions; +using ObjectPrinting.Tests.TestObjects; + +namespace ObjectPrinting.Tests +{ + [TestFixture] + public class ObjectPrinterAcceptanceTests + { + private static readonly VerifySettings settings = new(); + + [SetUp] + public void SetUp() + { + settings.UseDirectory("TestResults"); + } + + [Test] + public Task TestObjectPrinter_ExcludingTypes_CustomSerialization_Culture_Trimming() + { + var person = new PrintingTestObject {TestString = "Alex", TestInt = 19}; + person.TestObject = person; + + var printer = ObjectPrinter.For() + .Excluding() + .Printing().Using(i => i.ToString("X")) + .Printing().Using(CultureInfo.InvariantCulture) + .Printing(p => p.TestString).TrimmedToLength(10) + .Excluding(p => p.TestInt); + + var printedString = printer.PrintToString(person); + + return Verify(printedString, settings); + } + + [Test] + public Task TestDefaultSerialization_ExtensionMethod() + { + var person = new PrintingTestObject {TestString = "Alex", TestInt = 19}; + + var printedString = person.PrintToString(); + + return Verify(printedString, settings); + } + + [Test] + public Task TestConfiguredSerialization_ExtensionMethod() + { + var person = new PrintingTestObject {TestString = "Alex", TestInt = 19}; + + var printedString = person.PrintToString(s => s.Excluding(p => p.TestString)); + + return Verify(printedString, settings); + } + + [Test] + public Task PrintNull_Should_NullString() + { + var printedString = ObjectPrinter.For().PrintToString(null); + + return Verify(printedString, settings); + } + + [Test] + public Task ExcludingProperty_Should_IgnoresSpecifiedProperty() + { + var testObject = new PrintingTestObject {TestString = "Alex", TestInt = 19}; + + var printedString = ObjectPrinter.For() + .Excluding(o => o.TestInt) + .PrintToString(testObject); + + return Verify(printedString, settings); + } + + [Test] + public Task ExcludingType_Should_IgnoresSpecifiedType() + { + var testObject = new PrintingTestObject {TestString = "Alex", TestInt = 19}; + + var printedString = ObjectPrinter.For() + .Excluding() + .PrintToString(testObject); + + return Verify(printedString, settings); + } + + [Test] + public Task UsingCustomSerialization_Should_AppliesCustomFunction() + { + var testObject = new PrintingTestObject {TestString = "Alex", TestInt = 19}; + + var printedString = ObjectPrinter.For() + .Printing(x => x.TestInt).Using(x => $"{x} years") + .PrintToString(testObject); + + return Verify(printedString, settings); + } + + [Test] + public Task UsingCustomSerializationForType_Should_AppliesCustomFunctionToAllTypeProperties() + { + var testObject = new PrintingTestObject {TestString = "Alex", TestInt = 19}; + + var printedString = ObjectPrinter.For() + .Printing().Using(x => $"{x} is int") + .PrintToString(testObject); + + return Verify(printedString, settings); + } + + [Test] + public Task UsingCommonCulture_Should_UsesSpecifiedCulture() + { + const double testDouble = 1.5; + const float testFloat = 2.5f; + var culture = CultureInfo.GetCultureInfo("en-US"); + var testObject = new PrintingTestObject {TestDouble = testDouble, TestFloat = testFloat}; + + var printedString = ObjectPrinter.For() + .UsingCommonCulture(culture) + .PrintToString(testObject); + + return Verify(printedString, settings); + } + + [Test] + public Task UsingCultureForType_Should_UsesSpecifiedCultureForSpecifiedType() + { + const double testDouble = 1.5; + const float testFloat = 2.5f; + var culture = CultureInfo.GetCultureInfo("en-Us"); + var testObject = new PrintingTestObject {TestDouble = testDouble, TestFloat = testFloat}; + + var printedString = ObjectPrinter.For() + .Printing().Using(culture) + .PrintToString(testObject); + + return Verify(printedString, settings); + } + + [Test] + public Task TrimmingStringLength_Should_TrimsToStringLength() + { + var testObject = new PrintingTestObject {TestString = "Alex", TestInt = 19}; + + var printedString = ObjectPrinter.For() + .Printing(o => o.TestString).TrimmedToLength(1) + .PrintToString(testObject); + + return Verify(printedString, settings); + } + + [Test] + [TestCase("Alex", 4, TestName = "LenEqualsStringLength")] + [TestCase("Alex", 5, TestName = "LenMoreStringLength")] + public Task TrimmingStringLength_Should_NotTrims(string testString, int maxLength) + { + var testObject = new PrintingTestObject {TestString = testString, TestInt = 19}; + + var printedString = ObjectPrinter.For() + .Printing(o => o.TestString).TrimmedToLength(maxLength) + .PrintToString(testObject); + + return Verify(printedString, settings); + } + + [Test] + public Task PrintCircularReference_Should_PrintsMessage() + { + var testObject = new PrintingTestObject {TestString = "Alex", TestInt = 19}; + testObject.TestObject = testObject; + + var printedString = ObjectPrinter.For() + .PrintToString(testObject); + + return Verify(printedString, settings); + } + + [Test] + public Task PrintNonCircularReference_Should_PrintsNormally() + { + var firstTestObject = new PrintingTestObject {TestString = "Alex", TestInt = 19}; + var secondTestObject = new PrintingTestObject {TestString = "Alex", TestInt = 19}; + firstTestObject.TestObject = secondTestObject; + + var printedString = ObjectPrinter.For() + .PrintToString(firstTestObject); + + return Verify(printedString, settings); + } + + [Test] + public Task PrintArray_Should_AllItems() + { + var testObject = new PrintingTestObject + { + TestString = "Alex", + TestInt = 19, + TestArray = ["Item1", "Item2", "Item3"] + }; + + var printedString = ObjectPrinter.For() + .PrintToString(testObject); + + return Verify(printedString, settings); + } + + [Test] + public Task PrintList_Should_AllItems() + { + var testList = new List {"ListItem1", "ListItem2", "ListItem3"}; + var testObject = new PrintingTestObject + { + TestString = "Alex", + TestInt = 19, + TestList = testList + }; + + var printedString = ObjectPrinter.For() + .PrintToString(testObject); + + return Verify(printedString, settings); + } + + [Test] + public Task PrintDictionary_Should_AllItemsWithKeys() + { + var testDictionary = new Dictionary + { + {"Key1", 1}, + {"Key2", 2}, + {"Key3", 3} + }; + var testObject = new PrintingTestObject + { + TestString = "Alex", + TestInt = 19, + TestDictionary = testDictionary + }; + + var printedString = ObjectPrinter.For() + .PrintToString(testObject); + + return Verify(printedString, settings); + } + + [Test] + public Task TestPrimitiveTypes() + { + var testObject = new PrimitiveTypesTestObject + { + BoolValue = true, + ByteValue = 255, + ShortValue = 32767, + IntValue = 2147483647, + LongValue = 9223372036854775807, + DecimalValue = 123.45m, + DoubleValue = 123.45, + FloatValue = 123.45f, + CharValue = 'A', + }; + + var printedString = ObjectPrinter.For().PrintToString(testObject); + + return Verify(printedString, settings); + } + + [Test] + public Task PrintCircularReference_Should_HandleMultipleReferences() + { + var testObject = new PrintingTestObject {TestString = "Alex", TestInt = 19}; + var otherObject = new PrintingTestObject {TestString = "Alex", TestInt = 19}; + testObject.TestObject = otherObject; + otherObject.TestObject = testObject; + + var printedString = ObjectPrinter.For().PrintToString(testObject); + + return Verify(printedString, settings); + } + } +} \ No newline at end of file diff --git a/ObjectPrinting.Tests/ObjectPrinting.Tests.csproj b/ObjectPrinting.Tests/ObjectPrinting.Tests.csproj new file mode 100644 index 00000000..b6e353fe --- /dev/null +++ b/ObjectPrinting.Tests/ObjectPrinting.Tests.csproj @@ -0,0 +1,31 @@ + + + + net8.0 + enable + enable + + false + true + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + + + + diff --git a/ObjectPrinting.Tests/TestObjects/PrimitiveTypesTestObject.cs b/ObjectPrinting.Tests/TestObjects/PrimitiveTypesTestObject.cs new file mode 100644 index 00000000..5ca2cdf6 --- /dev/null +++ b/ObjectPrinting.Tests/TestObjects/PrimitiveTypesTestObject.cs @@ -0,0 +1,14 @@ +namespace ObjectPrinting.Tests.TestObjects; + +public class PrimitiveTypesTestObject +{ + public bool BoolValue { get; set; } + public byte ByteValue { get; set; } + public short ShortValue { get; set; } + public int IntValue { get; set; } + public double DoubleValue { get; set; } + public double FloatValue { get; set; } + public long LongValue { get; set; } + public decimal DecimalValue { get; set; } + public char CharValue { get; set; } +} \ No newline at end of file diff --git a/ObjectPrinting.Tests/TestObjects/PrintingTestObject.cs b/ObjectPrinting.Tests/TestObjects/PrintingTestObject.cs new file mode 100644 index 00000000..b974fb7b --- /dev/null +++ b/ObjectPrinting.Tests/TestObjects/PrintingTestObject.cs @@ -0,0 +1,14 @@ +namespace ObjectPrinting.Tests.TestObjects; + +public class PrintingTestObject +{ + public Guid TestId { get; set; } + public string TestString { get; set; } + public double TestDouble { get; set; } + public float TestFloat { get; set; } + public int TestInt { get; set; } + public object TestObject { get; set; } + public Dictionary TestDictionary { get; set; } + public List TestList { get; set; } + public object[] TestArray { get; set; } +} \ No newline at end of file diff --git a/ObjectPrinting.Tests/TestResults/ObjectPrinterAcceptanceTests.ExcludingProperty_Should_IgnoresSpecifiedProperty.verified.txt b/ObjectPrinting.Tests/TestResults/ObjectPrinterAcceptanceTests.ExcludingProperty_Should_IgnoresSpecifiedProperty.verified.txt new file mode 100644 index 00000000..4f840af6 --- /dev/null +++ b/ObjectPrinting.Tests/TestResults/ObjectPrinterAcceptanceTests.ExcludingProperty_Should_IgnoresSpecifiedProperty.verified.txt @@ -0,0 +1,9 @@ +PrintingTestObject + TestId = 00000000-0000-0000-0000-000000000000 + TestString = Alex + TestDouble = 0 + TestFloat = 0 + TestObject = null + TestDictionary = null + TestList = null + TestArray = null diff --git a/ObjectPrinting.Tests/TestResults/ObjectPrinterAcceptanceTests.ExcludingType_Should_IgnoresSpecifiedType.verified.txt b/ObjectPrinting.Tests/TestResults/ObjectPrinterAcceptanceTests.ExcludingType_Should_IgnoresSpecifiedType.verified.txt new file mode 100644 index 00000000..59bb69eb --- /dev/null +++ b/ObjectPrinting.Tests/TestResults/ObjectPrinterAcceptanceTests.ExcludingType_Should_IgnoresSpecifiedType.verified.txt @@ -0,0 +1,9 @@ +PrintingTestObject + TestId = 00000000-0000-0000-0000-000000000000 + TestDouble = 0 + TestFloat = 0 + TestInt = 19 + TestObject = null + TestDictionary = null + TestList = null + TestArray = null diff --git a/ObjectPrinting.Tests/TestResults/ObjectPrinterAcceptanceTests.PrintArray_Should_AllItems.verified.txt b/ObjectPrinting.Tests/TestResults/ObjectPrinterAcceptanceTests.PrintArray_Should_AllItems.verified.txt new file mode 100644 index 00000000..3c1df3ef --- /dev/null +++ b/ObjectPrinting.Tests/TestResults/ObjectPrinterAcceptanceTests.PrintArray_Should_AllItems.verified.txt @@ -0,0 +1,15 @@ +PrintingTestObject + TestId = 00000000-0000-0000-0000-000000000000 + TestString = Alex + TestDouble = 0 + TestFloat = 0 + TestInt = 19 + TestObject = null + TestDictionary = null + TestList = null + TestArray = + [ + Item1 + Item2 + Item3 + ] diff --git a/ObjectPrinting.Tests/TestResults/ObjectPrinterAcceptanceTests.PrintCircularReference_Should_HandleMultipleReferences.verified.txt b/ObjectPrinting.Tests/TestResults/ObjectPrinterAcceptanceTests.PrintCircularReference_Should_HandleMultipleReferences.verified.txt new file mode 100644 index 00000000..a08bea7a --- /dev/null +++ b/ObjectPrinting.Tests/TestResults/ObjectPrinterAcceptanceTests.PrintCircularReference_Should_HandleMultipleReferences.verified.txt @@ -0,0 +1,20 @@ +PrintingTestObject + TestId = 00000000-0000-0000-0000-000000000000 + TestString = Alex + TestDouble = 0 + TestFloat = 0 + TestInt = 19 + TestObject = PrintingTestObject + TestId = 00000000-0000-0000-0000-000000000000 + TestString = Alex + TestDouble = 0 + TestFloat = 0 + TestInt = 19 + TestObject = circular reference + TestDictionary = null + TestList = null + TestArray = null + + TestDictionary = null + TestList = null + TestArray = null diff --git a/ObjectPrinting.Tests/TestResults/ObjectPrinterAcceptanceTests.PrintCircularReference_Should_PrintsMessage.verified.txt b/ObjectPrinting.Tests/TestResults/ObjectPrinterAcceptanceTests.PrintCircularReference_Should_PrintsMessage.verified.txt new file mode 100644 index 00000000..3f5dec11 --- /dev/null +++ b/ObjectPrinting.Tests/TestResults/ObjectPrinterAcceptanceTests.PrintCircularReference_Should_PrintsMessage.verified.txt @@ -0,0 +1,10 @@ +PrintingTestObject + TestId = 00000000-0000-0000-0000-000000000000 + TestString = Alex + TestDouble = 0 + TestFloat = 0 + TestInt = 19 + TestObject = circular reference + TestDictionary = null + TestList = null + TestArray = null diff --git a/ObjectPrinting.Tests/TestResults/ObjectPrinterAcceptanceTests.PrintDictionary_Should_AllItemsWithKeys.verified.txt b/ObjectPrinting.Tests/TestResults/ObjectPrinterAcceptanceTests.PrintDictionary_Should_AllItemsWithKeys.verified.txt new file mode 100644 index 00000000..7f8a35d5 --- /dev/null +++ b/ObjectPrinting.Tests/TestResults/ObjectPrinterAcceptanceTests.PrintDictionary_Should_AllItemsWithKeys.verified.txt @@ -0,0 +1,15 @@ +PrintingTestObject + TestId = 00000000-0000-0000-0000-000000000000 + TestString = Alex + TestDouble = 0 + TestFloat = 0 + TestInt = 19 + TestObject = null + TestDictionary = + [ + Key1 = 1 + Key2 = 2 + Key3 = 3 + ] + TestList = null + TestArray = null diff --git a/ObjectPrinting.Tests/TestResults/ObjectPrinterAcceptanceTests.PrintList_Should_AllItems.verified.txt b/ObjectPrinting.Tests/TestResults/ObjectPrinterAcceptanceTests.PrintList_Should_AllItems.verified.txt new file mode 100644 index 00000000..88d6be1e --- /dev/null +++ b/ObjectPrinting.Tests/TestResults/ObjectPrinterAcceptanceTests.PrintList_Should_AllItems.verified.txt @@ -0,0 +1,15 @@ +PrintingTestObject + TestId = 00000000-0000-0000-0000-000000000000 + TestString = Alex + TestDouble = 0 + TestFloat = 0 + TestInt = 19 + TestObject = null + TestDictionary = null + TestList = + [ + ListItem1 + ListItem2 + ListItem3 + ] + TestArray = null diff --git a/ObjectPrinting.Tests/TestResults/ObjectPrinterAcceptanceTests.PrintNonCircularReference_Should_PrintsNormally.verified.txt b/ObjectPrinting.Tests/TestResults/ObjectPrinterAcceptanceTests.PrintNonCircularReference_Should_PrintsNormally.verified.txt new file mode 100644 index 00000000..08bb0e0e --- /dev/null +++ b/ObjectPrinting.Tests/TestResults/ObjectPrinterAcceptanceTests.PrintNonCircularReference_Should_PrintsNormally.verified.txt @@ -0,0 +1,20 @@ +PrintingTestObject + TestId = 00000000-0000-0000-0000-000000000000 + TestString = Alex + TestDouble = 0 + TestFloat = 0 + TestInt = 19 + TestObject = PrintingTestObject + TestId = 00000000-0000-0000-0000-000000000000 + TestString = Alex + TestDouble = 0 + TestFloat = 0 + TestInt = 19 + TestObject = null + TestDictionary = null + TestList = null + TestArray = null + + TestDictionary = null + TestList = null + TestArray = null diff --git a/ObjectPrinting.Tests/TestResults/ObjectPrinterAcceptanceTests.PrintNull_Should_NullString.verified.txt b/ObjectPrinting.Tests/TestResults/ObjectPrinterAcceptanceTests.PrintNull_Should_NullString.verified.txt new file mode 100644 index 00000000..c296c2ee --- /dev/null +++ b/ObjectPrinting.Tests/TestResults/ObjectPrinterAcceptanceTests.PrintNull_Should_NullString.verified.txt @@ -0,0 +1 @@ +null \ No newline at end of file diff --git a/ObjectPrinting.Tests/TestResults/ObjectPrinterAcceptanceTests.TestConfiguredSerialization_ExtensionMethod.verified.txt b/ObjectPrinting.Tests/TestResults/ObjectPrinterAcceptanceTests.TestConfiguredSerialization_ExtensionMethod.verified.txt new file mode 100644 index 00000000..59bb69eb --- /dev/null +++ b/ObjectPrinting.Tests/TestResults/ObjectPrinterAcceptanceTests.TestConfiguredSerialization_ExtensionMethod.verified.txt @@ -0,0 +1,9 @@ +PrintingTestObject + TestId = 00000000-0000-0000-0000-000000000000 + TestDouble = 0 + TestFloat = 0 + TestInt = 19 + TestObject = null + TestDictionary = null + TestList = null + TestArray = null diff --git a/ObjectPrinting.Tests/TestResults/ObjectPrinterAcceptanceTests.TestDefaultSerialization_ExtensionMethod.verified.txt b/ObjectPrinting.Tests/TestResults/ObjectPrinterAcceptanceTests.TestDefaultSerialization_ExtensionMethod.verified.txt new file mode 100644 index 00000000..8122a053 --- /dev/null +++ b/ObjectPrinting.Tests/TestResults/ObjectPrinterAcceptanceTests.TestDefaultSerialization_ExtensionMethod.verified.txt @@ -0,0 +1,10 @@ +PrintingTestObject + TestId = 00000000-0000-0000-0000-000000000000 + TestString = Alex + TestDouble = 0 + TestFloat = 0 + TestInt = 19 + TestObject = null + TestDictionary = null + TestList = null + TestArray = null diff --git a/ObjectPrinting.Tests/TestResults/ObjectPrinterAcceptanceTests.TestObjectPrinter_ExcludingTypes_CustomSerialization_Culture_Trimming.verified.txt b/ObjectPrinting.Tests/TestResults/ObjectPrinterAcceptanceTests.TestObjectPrinter_ExcludingTypes_CustomSerialization_Culture_Trimming.verified.txt new file mode 100644 index 00000000..70c20bd3 --- /dev/null +++ b/ObjectPrinting.Tests/TestResults/ObjectPrinterAcceptanceTests.TestObjectPrinter_ExcludingTypes_CustomSerialization_Culture_Trimming.verified.txt @@ -0,0 +1,8 @@ +PrintingTestObject + TestString = Alex + TestDouble = 0 + TestFloat = 0 + TestObject = circular reference + TestDictionary = null + TestList = null + TestArray = null diff --git a/ObjectPrinting.Tests/TestResults/ObjectPrinterAcceptanceTests.TestPrimitiveTypes.verified.txt b/ObjectPrinting.Tests/TestResults/ObjectPrinterAcceptanceTests.TestPrimitiveTypes.verified.txt new file mode 100644 index 00000000..3ce4b97c --- /dev/null +++ b/ObjectPrinting.Tests/TestResults/ObjectPrinterAcceptanceTests.TestPrimitiveTypes.verified.txt @@ -0,0 +1,10 @@ +PrimitiveTypesTestObject + BoolValue = True + ByteValue = 255 + ShortValue = 32767 + IntValue = 2147483647 + DoubleValue = 123,45 + FloatValue = 123,44999694824219 + LongValue = 9223372036854775807 + DecimalValue = 123,45 + CharValue = A diff --git a/ObjectPrinting.Tests/TestResults/ObjectPrinterAcceptanceTests.TrimmingStringLength_Should_NotTrims_testString=Alex_maxLength=4.verified.txt b/ObjectPrinting.Tests/TestResults/ObjectPrinterAcceptanceTests.TrimmingStringLength_Should_NotTrims_testString=Alex_maxLength=4.verified.txt new file mode 100644 index 00000000..8122a053 --- /dev/null +++ b/ObjectPrinting.Tests/TestResults/ObjectPrinterAcceptanceTests.TrimmingStringLength_Should_NotTrims_testString=Alex_maxLength=4.verified.txt @@ -0,0 +1,10 @@ +PrintingTestObject + TestId = 00000000-0000-0000-0000-000000000000 + TestString = Alex + TestDouble = 0 + TestFloat = 0 + TestInt = 19 + TestObject = null + TestDictionary = null + TestList = null + TestArray = null diff --git a/ObjectPrinting.Tests/TestResults/ObjectPrinterAcceptanceTests.TrimmingStringLength_Should_NotTrims_testString=Alex_maxLength=5.verified.txt b/ObjectPrinting.Tests/TestResults/ObjectPrinterAcceptanceTests.TrimmingStringLength_Should_NotTrims_testString=Alex_maxLength=5.verified.txt new file mode 100644 index 00000000..8122a053 --- /dev/null +++ b/ObjectPrinting.Tests/TestResults/ObjectPrinterAcceptanceTests.TrimmingStringLength_Should_NotTrims_testString=Alex_maxLength=5.verified.txt @@ -0,0 +1,10 @@ +PrintingTestObject + TestId = 00000000-0000-0000-0000-000000000000 + TestString = Alex + TestDouble = 0 + TestFloat = 0 + TestInt = 19 + TestObject = null + TestDictionary = null + TestList = null + TestArray = null diff --git a/ObjectPrinting.Tests/TestResults/ObjectPrinterAcceptanceTests.TrimmingStringLength_Should_TrimsToStringLength.verified.txt b/ObjectPrinting.Tests/TestResults/ObjectPrinterAcceptanceTests.TrimmingStringLength_Should_TrimsToStringLength.verified.txt new file mode 100644 index 00000000..4a846c09 --- /dev/null +++ b/ObjectPrinting.Tests/TestResults/ObjectPrinterAcceptanceTests.TrimmingStringLength_Should_TrimsToStringLength.verified.txt @@ -0,0 +1,10 @@ +PrintingTestObject + TestId = 00000000-0000-0000-0000-000000000000 + TestString = A + TestDouble = 0 + TestFloat = 0 + TestInt = 19 + TestObject = null + TestDictionary = null + TestList = null + TestArray = null diff --git a/ObjectPrinting.Tests/TestResults/ObjectPrinterAcceptanceTests.UsingCommonCulture_Should_UsesSpecifiedCulture.verified.txt b/ObjectPrinting.Tests/TestResults/ObjectPrinterAcceptanceTests.UsingCommonCulture_Should_UsesSpecifiedCulture.verified.txt new file mode 100644 index 00000000..e90ee40d --- /dev/null +++ b/ObjectPrinting.Tests/TestResults/ObjectPrinterAcceptanceTests.UsingCommonCulture_Should_UsesSpecifiedCulture.verified.txt @@ -0,0 +1,10 @@ +PrintingTestObject + TestId = 00000000-0000-0000-0000-000000000000 + TestString = null + TestDouble = 1.5 + TestFloat = 2.5 + TestInt = 0 + TestObject = null + TestDictionary = null + TestList = null + TestArray = null diff --git a/ObjectPrinting.Tests/TestResults/ObjectPrinterAcceptanceTests.UsingCultureForType_Should_UsesSpecifiedCultureForSpecifiedType.verified.txt b/ObjectPrinting.Tests/TestResults/ObjectPrinterAcceptanceTests.UsingCultureForType_Should_UsesSpecifiedCultureForSpecifiedType.verified.txt new file mode 100644 index 00000000..6a92ec6b --- /dev/null +++ b/ObjectPrinting.Tests/TestResults/ObjectPrinterAcceptanceTests.UsingCultureForType_Should_UsesSpecifiedCultureForSpecifiedType.verified.txt @@ -0,0 +1,10 @@ +PrintingTestObject + TestId = 00000000-0000-0000-0000-000000000000 + TestString = null + TestDouble = 1.5 + TestFloat = 2,5 + TestInt = 0 + TestObject = null + TestDictionary = null + TestList = null + TestArray = null diff --git a/ObjectPrinting.Tests/TestResults/ObjectPrinterAcceptanceTests.UsingCustomSerializationForType_Should_AppliesCustomFunctionToAllTypeProperties.verified.txt b/ObjectPrinting.Tests/TestResults/ObjectPrinterAcceptanceTests.UsingCustomSerializationForType_Should_AppliesCustomFunctionToAllTypeProperties.verified.txt new file mode 100644 index 00000000..bbd09cc8 --- /dev/null +++ b/ObjectPrinting.Tests/TestResults/ObjectPrinterAcceptanceTests.UsingCustomSerializationForType_Should_AppliesCustomFunctionToAllTypeProperties.verified.txt @@ -0,0 +1,10 @@ +PrintingTestObject + TestId = 00000000-0000-0000-0000-000000000000 + TestString = Alex + TestDouble = 0 + TestFloat = 0 + TestInt = 19 is int + TestObject = null + TestDictionary = null + TestList = null + TestArray = null diff --git a/ObjectPrinting.Tests/TestResults/ObjectPrinterAcceptanceTests.UsingCustomSerialization_Should_AppliesCustomFunction.verified.txt b/ObjectPrinting.Tests/TestResults/ObjectPrinterAcceptanceTests.UsingCustomSerialization_Should_AppliesCustomFunction.verified.txt new file mode 100644 index 00000000..af884ee9 --- /dev/null +++ b/ObjectPrinting.Tests/TestResults/ObjectPrinterAcceptanceTests.UsingCustomSerialization_Should_AppliesCustomFunction.verified.txt @@ -0,0 +1,10 @@ +PrintingTestObject + TestId = 00000000-0000-0000-0000-000000000000 + TestString = Alex + TestDouble = 0 + TestFloat = 0 + TestInt = 19 years + TestObject = null + TestDictionary = null + TestList = null + TestArray = null diff --git a/ObjectPrinting/Configurations/PropertyPrintingConfig.cs b/ObjectPrinting/Configurations/PropertyPrintingConfig.cs new file mode 100644 index 00000000..8600377b --- /dev/null +++ b/ObjectPrinting/Configurations/PropertyPrintingConfig.cs @@ -0,0 +1,14 @@ +using System; +using System.Reflection; + +namespace ObjectPrinting.Configurations; + +public class PropertyPrintingConfig(PrintingConfig printingConfig, PropertyInfo propertyInfo) +{ + public PrintingConfig ParentConfig { get; } = printingConfig; + + public PrintingConfig Using(Func print) + { + return ParentConfig.AddPropSerialize(propertyInfo, print); + } +} \ No newline at end of file diff --git a/ObjectPrinting/Configurations/TypePrintingConfig.cs b/ObjectPrinting/Configurations/TypePrintingConfig.cs new file mode 100644 index 00000000..23113ae3 --- /dev/null +++ b/ObjectPrinting/Configurations/TypePrintingConfig.cs @@ -0,0 +1,20 @@ +using System; +using System.Globalization; + +namespace ObjectPrinting.Configurations; + +public class TypePrintingConfig(PrintingConfig printingConfig) +{ + public PrintingConfig ParentConfig { get; } = printingConfig; + + public PrintingConfig Using(Func print) + { + return ParentConfig.AddTypeSerialize(print); + } + + internal PrintingConfig Using(CultureInfo cultureInfo) + where TFType : IFormattable, TType + { + return ParentConfig.SetTypeCulture(cultureInfo); + } +} \ No newline at end of file diff --git a/ObjectPrinting/Constants/PrintingConfigConstants.cs b/ObjectPrinting/Constants/PrintingConfigConstants.cs new file mode 100644 index 00000000..f1002fa9 --- /dev/null +++ b/ObjectPrinting/Constants/PrintingConfigConstants.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; + +namespace ObjectPrinting.Constants; + +internal static class PrintingConfigConstants +{ + public static char Tab => '\t'; + public static string NewLine => Environment.NewLine; + public static string NullLine => "null"; + public static string CircularReference => "circular reference"; + + public static readonly IReadOnlySet CoreTypes = new HashSet() + { + typeof(string), + }; +} \ No newline at end of file diff --git a/ObjectPrinting/Extensions/ObjectExtensions.cs b/ObjectPrinting/Extensions/ObjectExtensions.cs new file mode 100644 index 00000000..2fa70b96 --- /dev/null +++ b/ObjectPrinting/Extensions/ObjectExtensions.cs @@ -0,0 +1,16 @@ +using System; + +namespace ObjectPrinting.Extensions; + +public static class ObjectExtensions +{ + public static string PrintToString(this TOwner obj) + => ObjectPrinter.For().PrintToString(obj); + + public static string PrintToString(this TOwner obj, + Func, PrintingConfig> config) + { + return config(ObjectPrinter.For()) + .PrintToString(obj); + } +} \ No newline at end of file diff --git a/ObjectPrinting/Extensions/PropertyPrintingConfigExtensions.cs b/ObjectPrinting/Extensions/PropertyPrintingConfigExtensions.cs new file mode 100644 index 00000000..5e1e6cb3 --- /dev/null +++ b/ObjectPrinting/Extensions/PropertyPrintingConfigExtensions.cs @@ -0,0 +1,22 @@ +using ObjectPrinting.Configurations; + +namespace ObjectPrinting.Extensions; + +public static class PropertyPrintingConfigExtensions +{ + public static PrintingConfig TrimmedToLength( + this PropertyPrintingConfig propConfig, int maxLen) + { + return propConfig.Using(startValue => TrimmedToLength(startValue, maxLen)); + } + + private static string TrimmedToLength(string startValue, int maxLen) + { + if (string.IsNullOrEmpty(startValue) || startValue.Length <= maxLen) + { + return startValue; + } + + return startValue[..maxLen]; + } +} \ No newline at end of file diff --git a/ObjectPrinting/Extensions/TypePrintingConfigExtensions.cs b/ObjectPrinting/Extensions/TypePrintingConfigExtensions.cs new file mode 100644 index 00000000..5f0aea95 --- /dev/null +++ b/ObjectPrinting/Extensions/TypePrintingConfigExtensions.cs @@ -0,0 +1,15 @@ +using System; +using System.Globalization; +using ObjectPrinting.Configurations; + +namespace ObjectPrinting.Extensions; + +public static class TypePrintingConfigExtensions +{ + public static PrintingConfig Using( + this TypePrintingConfig config, CultureInfo culture) + where TType : IFormattable + { + return config.Using(culture); + } +} \ No newline at end of file diff --git a/ObjectPrinting/Helpers/PrintingConfigHelper.cs b/ObjectPrinting/Helpers/PrintingConfigHelper.cs new file mode 100644 index 00000000..7071dd56 --- /dev/null +++ b/ObjectPrinting/Helpers/PrintingConfigHelper.cs @@ -0,0 +1,12 @@ +using System; +using ObjectPrinting.Constants; + +namespace ObjectPrinting.Helpers; + +internal static class PrintingConfigHelper +{ + public static bool IsCoreType(Type type) + { + return type.IsValueType || PrintingConfigConstants.CoreTypes.Contains(type); + } +} \ No newline at end of file diff --git a/ObjectPrinting/Helpers/ReflectionHelper.cs b/ObjectPrinting/Helpers/ReflectionHelper.cs new file mode 100644 index 00000000..b9d094c7 --- /dev/null +++ b/ObjectPrinting/Helpers/ReflectionHelper.cs @@ -0,0 +1,30 @@ +using System; +using System.Linq.Expressions; +using System.Reflection; + +namespace ObjectPrinting.Helpers; + +internal static class ReflectionHelper +{ + public static PropertyInfo GetPropertyInfoFromExpression( + Expression> memberSelector) + { + if (memberSelector.Body is not MemberExpression expression) + { + throw new ArgumentException("Expression must be a member expression", nameof(memberSelector)); + } + + if (expression.Expression == expression) + { + throw new ArgumentException("Cannot exclude the entire object. Use a property expression.", + nameof(memberSelector)); + } + + if (expression.Member is not PropertyInfo propertyInfo) + { + throw new ArgumentException("Expression must be a property expression", nameof(memberSelector)); + } + + return propertyInfo; + } +} \ No newline at end of file diff --git a/ObjectPrinting/Models/SerializeSettings.cs b/ObjectPrinting/Models/SerializeSettings.cs new file mode 100644 index 00000000..79c9970c --- /dev/null +++ b/ObjectPrinting/Models/SerializeSettings.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Frozen; +using System.Collections.Generic; +using System.Globalization; +using System.Reflection; + +namespace ObjectPrinting.Models; + +internal class SerializeSettings +{ + public CultureInfo CommonCulture { get; init; } + public IReadOnlySet ExcludeTypes { get; init; } + public IReadOnlySet ExcludeProperties { get; init; } + public IReadOnlyDictionary PropertyPrintingConfigs { get; init; } + public IReadOnlyDictionary TypePrintingConfigs { get; init; } + public IReadOnlyDictionary TypeCultureConfigs { get; init; } + + public SerializeSettings(CultureInfo commonCulture, IEnumerable excludeTypes, + IEnumerable excludeProperties, + IDictionary propertyPrintingConfigs, IDictionary typePrintingConfigs, + IDictionary typeCultureConfigs) + { + CommonCulture = commonCulture; + ExcludeTypes = excludeTypes.ToFrozenSet(); + ExcludeProperties = excludeProperties.ToFrozenSet(); + PropertyPrintingConfigs = propertyPrintingConfigs.ToFrozenDictionary(); + TypePrintingConfigs = typePrintingConfigs.ToFrozenDictionary(); + TypeCultureConfigs = typeCultureConfigs.ToFrozenDictionary(); + } +} \ No newline at end of file diff --git a/ObjectPrinting/ObjectPrinter.cs b/ObjectPrinting/ObjectPrinter.cs index 3c7867c3..caa93e56 100644 --- a/ObjectPrinting/ObjectPrinter.cs +++ b/ObjectPrinting/ObjectPrinter.cs @@ -1,10 +1,9 @@ -namespace ObjectPrinting +namespace ObjectPrinting; + +public static class ObjectPrinter { - public class ObjectPrinter + public static PrintingConfig For() { - public static PrintingConfig For() - { - return new PrintingConfig(); - } + return new PrintingConfig(); } } \ No newline at end of file diff --git a/ObjectPrinting/PrintingConfig.cs b/ObjectPrinting/PrintingConfig.cs index a9e08211..8a80420f 100644 --- a/ObjectPrinting/PrintingConfig.cs +++ b/ObjectPrinting/PrintingConfig.cs @@ -1,41 +1,80 @@ using System; -using System.Linq; -using System.Text; +using System.Collections.Generic; +using System.Globalization; +using System.Linq.Expressions; +using System.Reflection; +using ObjectPrinting.Configurations; +using ObjectPrinting.Helpers; +using ObjectPrinting.Models; -namespace ObjectPrinting +namespace ObjectPrinting; + +public class PrintingConfig() { - public class PrintingConfig - { - public string PrintToString(TOwner obj) - { - return PrintToString(obj, 0); - } - - private string PrintToString(object obj, int nestingLevel) - { - //TODO apply configurations - if (obj == null) - return "null" + Environment.NewLine; - - var finalTypes = new[] - { - typeof(int), typeof(double), typeof(float), typeof(string), - typeof(DateTime), typeof(TimeSpan) - }; - if (finalTypes.Contains(obj.GetType())) - return obj + Environment.NewLine; - - var identation = new string('\t', nestingLevel + 1); - var sb = new StringBuilder(); - var type = obj.GetType(); - sb.AppendLine(type.Name); - foreach (var propertyInfo in type.GetProperties()) - { - sb.Append(identation + propertyInfo.Name + " = " + - PrintToString(propertyInfo.GetValue(obj), - nestingLevel + 1)); - } - return sb.ToString(); - } + private readonly HashSet excludeTypes = []; + private readonly HashSet excludeProperties = []; + private readonly Dictionary propertyPrintingConfigs = new(); + private readonly Dictionary typePrintingConfigs = new(); + private readonly Dictionary typeCultureConfigs = new(); + private CultureInfo commonCulture = CultureInfo.CurrentCulture; + + public PrintingConfig UsingCommonCulture(CultureInfo culture) + { + commonCulture = culture; + return this; + } + + public PrintingConfig Excluding() + { + excludeTypes.Add(typeof(TPropType)); + return this; + } + + public PrintingConfig Excluding(Expression> memberSelector) + { + var propertyInfo = ReflectionHelper.GetPropertyInfoFromExpression(memberSelector); + excludeProperties.Add(propertyInfo); + return this; + } + + public TypePrintingConfig Printing() + { + return new TypePrintingConfig(this); + } + + public PropertyPrintingConfig Printing( + Expression> memberSelector) + { + var propertyInfo = ReflectionHelper.GetPropertyInfoFromExpression(memberSelector); + return new PropertyPrintingConfig(this, propertyInfo); + } + + internal PrintingConfig SetTypeCulture(CultureInfo culture) + where TType : IFormattable + { + typeCultureConfigs[typeof(TType)] = culture; + return this; + } + + internal PrintingConfig AddTypeSerialize(Func print) + { + var type = typeof(TType); + typePrintingConfigs[type] = print; + return this; + } + + internal PrintingConfig AddPropSerialize(PropertyInfo propertyInfo, + Func print) + { + propertyPrintingConfigs[propertyInfo] = print; + return this; + } + + public string PrintToString(TOwner obj) + { + var serializeSettings = new SerializeSettings(commonCulture, excludeTypes, excludeProperties, + propertyPrintingConfigs, typePrintingConfigs, typeCultureConfigs); + var serializer = new Serializer(serializeSettings); + return serializer.PrintToString(obj); } } \ No newline at end of file diff --git a/ObjectPrinting/Serializer.cs b/ObjectPrinting/Serializer.cs new file mode 100644 index 00000000..76141759 --- /dev/null +++ b/ObjectPrinting/Serializer.cs @@ -0,0 +1,179 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using ObjectPrinting.Constants; +using ObjectPrinting.Helpers; +using ObjectPrinting.Models; + +namespace ObjectPrinting; + +internal class Serializer(SerializeSettings settings) +{ + private readonly HashSet visitedObjects = new(ReferenceEqualityComparer.Instance); + + public string PrintToString(TType obj) => PrintToString(obj, 0); + + private string PrintToString(object? obj, int nestingLevel) + { + if (obj is null) + { + return PrintingConfigConstants.NullLine; + } + + if (visitedObjects.Contains(obj)) + { + return PrintingConfigConstants.CircularReference; + } + + var objectType = obj.GetType(); + if (settings.ExcludeTypes.Contains(objectType)) + { + return string.Empty; + } + + if (TryTypeSerializerPrintString(obj, objectType, out var serializedString)) + { + return serializedString; + } + + var culture = GetCultureInfo(objectType); + if (TryCorePrintString(obj, objectType, culture, out var finalString)) + { + return finalString; + } + + if (objectType.IsClass) + { + visitedObjects.Add(obj); + } + + if (obj is IEnumerable enumerable) + { + return SerializeItems(enumerable, nestingLevel); + } + + var nextNestingLevel = nestingLevel + 1; + return GetObjectPropertiesString(obj, objectType, nextNestingLevel); + } + + private CultureInfo GetCultureInfo(Type objectType) + { + return settings.TypeCultureConfigs.TryGetValue(objectType, out var cultureConfig) + ? cultureConfig + : settings.CommonCulture; + } + + private bool TryTypeSerializerPrintString(object obj, Type objectType, out string result) + { + result = string.Empty; + if (!settings.TypePrintingConfigs.TryGetValue(objectType, out var configPrint)) + { + return false; + } + + result = configPrint.DynamicInvoke(obj)?.ToString()!; + return true; + } + + private bool TryCorePrintString(object obj, Type objectType, CultureInfo culture, out string result) + { + result = string.Empty; + if (!PrintingConfigHelper.IsCoreType(objectType)) + { + return false; + } + + if (obj is IConvertible convertible) + { + result = convertible.ToString(culture); + } + else + { + result = obj.ToString()!; + } + + return true; + } + + private string GetObjectPropertiesString(object obj, Type objectType, int nestingLevel) + { + var indentation = GetIndentation(nestingLevel); + var propertiesStringBuilder = new StringBuilder(); + propertiesStringBuilder.AppendLine(objectType.Name); + + foreach (var propertyInfo in objectType.GetProperties()) + { + if (settings.ExcludeProperties.Contains(propertyInfo) + || settings.ExcludeTypes.Contains(propertyInfo.PropertyType)) + { + continue; + } + + propertiesStringBuilder.Append($"{indentation}{propertyInfo.Name} = "); + var propertyValue = propertyInfo.GetValue(obj); + if (settings.PropertyPrintingConfigs.TryGetValue(propertyInfo, out var configPrint) + && propertyValue is not null) + { + propertiesStringBuilder.AppendLine($"{configPrint.DynamicInvoke(propertyValue)}"); + } + else + { + propertiesStringBuilder.AppendLine($"{PrintToString(propertyValue, nestingLevel)}"); + } + } + + return propertiesStringBuilder.ToString(); + } + + private string SerializeItems(IEnumerable enumerable, int nestingLevel) + { + var indentation = GetIndentation(nestingLevel); + var nextNestingLevel = nestingLevel + 1; + string returnString; + if (enumerable is IDictionary dictionary) + { + returnString = SerializeDictionary(dictionary, nextNestingLevel); + } + else + { + returnString = SerializeEnumerable(enumerable, nextNestingLevel); + } + + return string.Format("{0}{1}[{0}{2}{1}]", PrintingConfigConstants.NewLine, indentation, returnString); + } + + private string SerializeEnumerable(IEnumerable items, int nestingLevel) + { + var enumerableSerializeBuilder = new StringBuilder(); + var indentation = GetIndentation(nestingLevel); + var nextNestingLevel = nestingLevel + 1; + foreach (var item in items) + { + enumerableSerializeBuilder.AppendLine( + $"{indentation}{PrintToString(item, nextNestingLevel)}"); + } + + return enumerableSerializeBuilder.ToString(); + } + + private string SerializeDictionary(IDictionary items, int nestingLevel) + { + var dictionarySerializeBuilder = new StringBuilder(); + var indentation = GetIndentation(nestingLevel); + var nextNestingLevel = nestingLevel + 1; + foreach (DictionaryEntry entry in items) + { + dictionarySerializeBuilder.AppendLine( + $"{indentation}{entry.Key} = {PrintToString(entry.Value, nextNestingLevel)}"); + } + + return dictionarySerializeBuilder.ToString(); + } + + private string GetIndentation(int nestingLevel) + { + return new string(PrintingConfigConstants.Tab, nestingLevel); + } +} \ No newline at end of file diff --git a/ObjectPrinting/Tests/ObjectPrinterAcceptanceTests.cs b/ObjectPrinting/Tests/ObjectPrinterAcceptanceTests.cs deleted file mode 100644 index 4c8b2445..00000000 --- a/ObjectPrinting/Tests/ObjectPrinterAcceptanceTests.cs +++ /dev/null @@ -1,27 +0,0 @@ -using NUnit.Framework; - -namespace ObjectPrinting.Tests -{ - [TestFixture] - public class ObjectPrinterAcceptanceTests - { - [Test] - public void Demo() - { - var person = new Person { Name = "Alex", Age = 19 }; - - var printer = ObjectPrinter.For(); - //1. Исключить из сериализации свойства определенного типа - //2. Указать альтернативный способ сериализации для определенного типа - //3. Для числовых типов указать культуру - //4. Настроить сериализацию конкретного свойства - //5. Настроить обрезание строковых свойств (метод должен быть виден только для строковых свойств) - //6. Исключить из сериализации конкретного свойства - - string s1 = printer.PrintToString(person); - - //7. Синтаксический сахар в виде метода расширения, сериализующего по-умолчанию - //8. ...с конфигурированием - } - } -} \ No newline at end of file diff --git a/ObjectPrinting/Tests/Person.cs b/ObjectPrinting/Tests/Person.cs deleted file mode 100644 index f9555955..00000000 --- a/ObjectPrinting/Tests/Person.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; - -namespace ObjectPrinting.Tests -{ - public class Person - { - public Guid Id { get; set; } - public string Name { get; set; } - public double Height { get; set; } - public int Age { get; set; } - } -} \ No newline at end of file diff --git a/fluent-api.sln b/fluent-api.sln index 69c8db9e..fecd0839 100644 --- a/fluent-api.sln +++ b/fluent-api.sln @@ -13,6 +13,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FluentMapping.Tests", "Samp EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spectacle", "Samples\Spectacle\Spectacle.csproj", "{EFA9335C-411B-4597-B0B6-5438D1AE04C3}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ObjectPrinting.Tests", "ObjectPrinting.Tests\ObjectPrinting.Tests.csproj", "{7A66465F-B187-4ED9-8CDE-66A0ECE4FFDE}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -35,6 +37,10 @@ Global {EFA9335C-411B-4597-B0B6-5438D1AE04C3}.Debug|Any CPU.Build.0 = Debug|Any CPU {EFA9335C-411B-4597-B0B6-5438D1AE04C3}.Release|Any CPU.ActiveCfg = Release|Any CPU {EFA9335C-411B-4597-B0B6-5438D1AE04C3}.Release|Any CPU.Build.0 = Release|Any CPU + {7A66465F-B187-4ED9-8CDE-66A0ECE4FFDE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7A66465F-B187-4ED9-8CDE-66A0ECE4FFDE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7A66465F-B187-4ED9-8CDE-66A0ECE4FFDE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7A66465F-B187-4ED9-8CDE-66A0ECE4FFDE}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE