Skip to content

Commit 65f644c

Browse files
committed
🐛 CopyPropertiesTo should copy values from non-nullable to nullable with same underlying type
1 parent 62ae8a6 commit 65f644c

File tree

5 files changed

+111
-6
lines changed

5 files changed

+111
-6
lines changed

source/TestUtils/PeanutButter.RandomGenerators.Core.Tests/TestGetRandom.cs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,5 @@
11
using System.Collections.Generic;
2-
using NUnit.Framework;
3-
using static PeanutButter.RandomGenerators.RandomValueGen;
4-
using NExpect;
52
using PeanutButter.RandomGenerators.Core.Tests.Domain;
6-
using static NExpect.Expectations;
73

84
namespace PeanutButter.RandomGenerators.Core.Tests
95
{

source/TestUtils/PeanutButter.RandomGenerators.Core.Tests/TestObjectExtensions.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
using System;
22
using System.Collections.Generic;
33
using NSubstitute;
4-
using NUnit.Framework;
54

65
namespace PeanutButter.RandomGenerators.Core.Tests;
76

87
[TestFixture]
9-
public partial class TestObjectExtensions
8+
public class TestObjectExtensions
109
{
1110
[TestFixture]
1211
public class Randomized

source/Utils/PeanutButter.Utils.NetCore.Tests/TestDateTimeExtensions.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -731,7 +731,10 @@ private static string ZeroPad(int value, int places = 2)
731731
{
732732
var result = value.ToString();
733733
while (result.Length < places)
734+
{
734735
result = "0" + result;
736+
}
737+
735738
return result;
736739
}
737740

source/Utils/PeanutButter.Utils.NetCore.Tests/TestObjectExtensions.cs

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1114,6 +1114,7 @@ public class CopyingPropertiesFromOneObjectToAnother
11141114
[Test]
11151115
public void GivenDestWithSameProperty_CopiesValue()
11161116
{
1117+
TestCopyFor<Numbers>();
11171118
TestCopyFor<string>();
11181119
TestCopyFor<int>();
11191120
TestCopyFor<byte>();
@@ -1145,7 +1146,67 @@ private static void TestCopyFor<T>()
11451146
var src = new Simple<T>();
11461147
var dst = new Simple<T>();
11471148
while (dst.prop.Equals(src.prop))
1149+
{
1150+
dst.Randomize();
1151+
}
1152+
1153+
src.CopyPropertiesTo(dst);
1154+
1155+
//---------------Test Result -----------------------
1156+
Expect(dst.prop)
1157+
.To.Equal(src.prop);
1158+
}
1159+
1160+
[Test]
1161+
public void ShouldCopyNullableWithValueToNonNullable()
1162+
{
1163+
// Arrange
1164+
// Act
1165+
TestNullableCopyFor<Numbers>();
1166+
TestNullableCopyFor<int>();
1167+
TestNullableCopyFor<byte>();
1168+
TestNullableCopyFor<char>();
1169+
TestNullableCopyFor<long>();
1170+
TestNullableCopyFor<float>();
1171+
TestNullableCopyFor<double>();
1172+
TestNullableCopyFor<decimal>();
1173+
TestNullableCopyFor<DateTime>();
1174+
TestNullableCopyFor<bool>();
1175+
// Assert
1176+
}
1177+
1178+
public class Foo1
1179+
{
1180+
public Numbers Numbers { get; set; }
1181+
}
1182+
1183+
public class Foo2
1184+
{
1185+
public Numbers? Numbers { get; set; }
1186+
}
1187+
1188+
public enum Numbers
1189+
{
1190+
One,
1191+
Two,
1192+
Three
1193+
}
1194+
1195+
private static void TestNullableCopyFor<T>()
1196+
where T : struct
1197+
{
1198+
//---------------Set up test pack-------------------
1199+
1200+
//---------------Assert Precondition----------------
1201+
1202+
//---------------Execute Test ----------------------
1203+
var src = GetRandom<Simple<T>>();
1204+
var dst = new NullableSimple<T>();
1205+
while (dst.prop.Equals(src.prop))
1206+
{
11481207
dst.Randomize();
1208+
}
1209+
11491210
src.CopyPropertiesTo(dst);
11501211

11511212
//---------------Test Result -----------------------
@@ -1161,7 +1222,9 @@ public void ComplexTypesAreTraversedButOnlySimplePropertiesAreCopied()
11611222
var src = new Complex<int>();
11621223
var dst = new Complex<int>();
11631224
while (dst.prop.prop.Equals(src.prop.prop))
1225+
{
11641226
dst.prop.Randomize();
1227+
}
11651228

11661229
//---------------Assert Precondition----------------
11671230
Expect(dst)
@@ -1187,7 +1250,9 @@ public void WhenDeepIsFalse_ComplexTypesAreTraversedAndRefCopied()
11871250
var src = new Complex<int>();
11881251
var dst = new Complex<int>();
11891252
while (dst.prop.prop.Equals(src.prop.prop))
1253+
{
11901254
dst.prop.Randomize();
1255+
}
11911256

11921257
//---------------Assert Precondition----------------
11931258
Expect(dst)
@@ -3687,6 +3752,22 @@ public void Randomize()
36873752
}
36883753
}
36893754

3755+
public class NullableSimple<T>
3756+
where T : struct
3757+
{
3758+
public T? prop { get; set; }
3759+
3760+
public NullableSimple()
3761+
{
3762+
Randomize();
3763+
}
3764+
3765+
public void Randomize()
3766+
{
3767+
prop = GetRandom<T?>();
3768+
}
3769+
}
3770+
36903771
public class Complex<T>
36913772
{
36923773
public Simple<T> prop { get; set; }

source/Utils/PeanutButter.Utils/ObjectExtensions.cs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -430,6 +430,30 @@ public static void CopyPropertiesTo(this object src, object dst, bool deep, para
430430
pi => Tuple.Create(pi.Name, pi.PropertyType),
431431
pi => pi
432432
);
433+
foreach (var item in targetPropertyCache.ToArray())
434+
{
435+
if (!item.Value.PropertyType.IsNullableType())
436+
{
437+
continue;
438+
}
439+
440+
var underlyingType = Nullable.GetUnderlyingType(
441+
item.Value.PropertyType
442+
);
443+
if (underlyingType is null)
444+
{
445+
continue;
446+
}
447+
448+
var key = Tuple.Create(
449+
item.Key.Item1,
450+
underlyingType
451+
);
452+
targetPropertyCache.TryAdd(
453+
key,
454+
item.Value
455+
);
456+
}
433457

434458
foreach (var srcPropInfo in srcPropInfos.Where(
435459
pi => pi.CanRead &&
@@ -457,6 +481,8 @@ out var matchingTarget
457481
}
458482
}
459483

484+
private static readonly Type NullableType = typeof(Nullable<>);
485+
460486
private static readonly ConcurrentDictionary<Type, PropertyInfo[]> PropertyCache = new();
461487

462488
private static readonly Func<bool, PropertyInfo, PropertyInfo, object, object, bool>[]

0 commit comments

Comments
 (0)