Skip to content

Commit 98b5ccd

Browse files
authored
ReadOnlyCollection support (#27)
* Start of ReadOnlyCollection<T> support / ReadOnlyCollectionWrapper performance improvements * Support for mapping to objects with readonly ReadOnlyCollection members * Support for mapping to new, nested ReadOnlyCollections * Removing unused code / Adding TODOs for missing test coverage * Test coverage for root source ReadOnlyCollection mapping * Test coverage for mapping to new ReadOnlyCollection members * Support for mapping over root ReadOnlyCollections / Increasing number of scenarios where local variable use is skipped * Test coverage for mapping over a root ReadOnlyCollection with identifiable elements / Using AsReadOnly to convert List<T> to ReadOnlyCollection<T> * Support for mapping over member ReadOnlyCollections * Extra test coverage for nested, multiply-recursive relationships / Simplifying detection of object caching necessity / Increasing number of scenarios where cached object dictionary creation is skipped * Test coverage for mapping over a readonly, nested ReadOnlyCollection * Test coverage for merging to a root simple-type ReadOnlyCollection * Test coverage for merging to a root complex-type ReadOnlyCollection * Test coverage for merging to a nested simple-type ReadOnlyCollection * Test coverage for merging to a complex type, nested ReadOnlyCollection
1 parent 451c0cd commit 98b5ccd

31 files changed

+554
-206
lines changed

AgileMapper.UnitTests/Dictionaries/Configuration/WhenConfiguringSourceDictionaryMapping.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ public void ShouldUseACustomMemberNameDictionaryKeyForANestedMember()
9494
}
9595

9696
[Fact]
97-
public void ShouldUseACustomMemberNameDictionaryKeyForANestedEnumerableMember()
97+
public void ShouldUseACustomMemberNameDictionaryKeyForANestedArrayMember()
9898
{
9999
using (var mapper = Mapper.CreateNew())
100100
{

AgileMapper.UnitTests/WhenMappingCircularReferences.cs

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
{
33
using System;
44
using System.Collections.Generic;
5+
using System.Collections.ObjectModel;
56
using AgileMapper.Extensions;
67
using Shouldly;
78
using TestClasses;
@@ -260,6 +261,49 @@ public void ShouldMapMultiplyRecursiveRelationships()
260261
}
261262
}
262263

264+
[Fact]
265+
public void ShouldMapNestedMultiplyRecursiveRelationships()
266+
{
267+
using (var mapper = Mapper.CreateNew())
268+
{
269+
mapper.WhenMapping
270+
.To<PublicField<ReadOnlyCollection<MultipleRecursor>>>()
271+
.TrackMappedObjects();
272+
273+
var recursorOne = new MultipleRecursor { Name = "One" };
274+
var recursorTwo = new MultipleRecursor { Name = "Two" };
275+
276+
recursorOne.ChildRecursor = recursorTwo;
277+
recursorTwo.ChildRecursor = recursorOne;
278+
279+
var source = new PublicField<MultipleRecursor[]>
280+
{
281+
Value = new[] { recursorOne, recursorTwo }
282+
};
283+
284+
var result = mapper.Map(source).ToANew<PublicField<ReadOnlyCollection<MultipleRecursor>>>();
285+
286+
result.ShouldNotBeNull();
287+
result.ShouldNotBeSameAs(source);
288+
result.Value.Count.ShouldBe(2);
289+
290+
var resultOne = result.Value.First();
291+
resultOne.ShouldNotBeSameAs(recursorOne);
292+
resultOne.Name.ShouldBe("One");
293+
resultOne.ChildRecursor.ShouldNotBeNull();
294+
resultOne.ChildRecursor.ShouldNotBeSameAs(recursorTwo);
295+
296+
var resultTwo = result.Value.Second();
297+
resultTwo.ShouldNotBeSameAs(recursorTwo);
298+
resultTwo.Name.ShouldBe("Two");
299+
resultTwo.ChildRecursor.ShouldNotBeNull();
300+
resultTwo.ChildRecursor.ShouldNotBeSameAs(recursorOne);
301+
302+
resultOne.ChildRecursor.ShouldBeSameAs(resultTwo);
303+
resultTwo.ChildRecursor.ShouldBeSameAs(resultOne);
304+
}
305+
}
306+
263307
[Fact]
264308
public void ShouldGenerateAMappingPlanForLinkRelationships()
265309
{

AgileMapper.UnitTests/WhenMappingOnToEnumerableMembers.cs

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
{
33
using System;
44
using System.Collections.Generic;
5+
using System.Collections.ObjectModel;
56
using System.Linq;
67
using Shouldly;
78
using TestClasses;
@@ -50,6 +51,26 @@ public void ShouldMergeANullableIntArray()
5051
result.Value.ShouldBe(1, 2, null, 3);
5152
}
5253

54+
[Fact]
55+
public void ShouldMergeANullableGuidReadOnlyCollection()
56+
{
57+
var source = new PublicProperty<ICollection<Guid>>
58+
{
59+
Value = new List<Guid> { Guid.NewGuid() }
60+
};
61+
62+
var target = new PublicField<ReadOnlyCollection<Guid?>>
63+
{
64+
Value = new ReadOnlyCollection<Guid?>(new[] { Guid.Empty, default(Guid?), Guid.NewGuid() })
65+
};
66+
67+
var originalCollection = target.Value;
68+
var result = Mapper.Map(source).OnTo(target);
69+
70+
result.Value.ShouldNotBeSameAs(originalCollection);
71+
result.Value.ShouldBe(target.Value.First(), default(Guid?), target.Value.Third(), source.Value.First());
72+
}
73+
5374
[Fact]
5475
public void ShouldMergeAComplexTypeCollection()
5576
{
@@ -98,6 +119,34 @@ public void ShouldMergeAnIdentifiableComplexTypeList()
98119
result.Value.ShouldBe(r => r.ProductId, "Magic", "Science");
99120
}
100121

122+
[Fact]
123+
public void ShouldMergeAnIdentifiableComplexTypeReadOnlyCollection()
124+
{
125+
var source = new PublicProperty<Product[]>
126+
{
127+
Value = new[]
128+
{
129+
new Product { ProductId = "Science", Price = 1000.00 }
130+
}
131+
};
132+
133+
var target = new PublicField<ReadOnlyCollection<Product>>
134+
{
135+
Value = new ReadOnlyCollection<Product>(new List<Product>
136+
{
137+
new Product { ProductId = "Science" },
138+
new Product { ProductId = "Magic", Price = 1.00 }
139+
})
140+
};
141+
142+
var existingProduct = target.Value.First();
143+
var result = Mapper.Map(source).OnTo(target);
144+
145+
result.Value.First().ShouldBeSameAs(existingProduct);
146+
result.Value.First().Price.ShouldBe(1000.00);
147+
result.Value.ShouldBe(r => r.ProductId, "Science", "Magic");
148+
}
149+
101150
[Fact]
102151
public void ShouldHandleANullSourceMember()
103152
{

AgileMapper.UnitTests/WhenMappingOnToEnumerables.cs

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
{
33
using System;
44
using System.Collections.Generic;
5+
using System.Collections.ObjectModel;
56
using System.Linq;
67
using Shouldly;
78
using TestClasses;
@@ -17,8 +18,18 @@ public void ShouldMergeARootSimpleTypeArray()
1718
var result = Mapper.Map(source).OnTo(target);
1819

1920
result.ShouldNotBeNull();
20-
result.ShouldNotBeSameAs(source);
21-
result.SequenceEqual(target.Concat(source)).ShouldBeTrue();
21+
result.ShouldBe(1, 2, 3, 4, 5, 6);
22+
}
23+
24+
[Fact]
25+
public void ShouldMergeARootSimpleTypeReadOnlyCollection()
26+
{
27+
var source = new[] { 2, 3, 4 };
28+
var target = new ReadOnlyCollection<string>(new[] { "1", "2" });
29+
var result = Mapper.Map(source).OnTo(target);
30+
31+
result.ShouldNotBeNull();
32+
result.ShouldBe("1", "2", "3", "4");
2233
}
2334

2435
[Fact]
@@ -84,6 +95,24 @@ public void ShouldMergeARootComplexTypeList()
8495
result.ShouldBe(p => p.Name, "Kate", "Pete");
8596
}
8697

98+
[Fact]
99+
public void ShouldMergeARootComplexTypeReadOnlyCollection()
100+
{
101+
var source = new[]
102+
{
103+
new Product { ProductId = "Pete" }
104+
};
105+
106+
var target = new ReadOnlyCollection<Product>(new[]
107+
{
108+
new Product { ProductId = "Kate" }
109+
});
110+
111+
var result = Mapper.Map(source).OnTo(target);
112+
113+
result.ShouldBe(p => p.ProductId, "Kate", "Pete");
114+
}
115+
87116
[Fact]
88117
public void ShouldUpdateAnExistingObject()
89118
{

AgileMapper.UnitTests/WhenMappingOverEnumerableMembers.cs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,24 @@ public void ShouldOverwriteAnArray()
5151
result.Value.ShouldBe(MinusOne, Zero);
5252
}
5353

54+
[Fact]
55+
public void ShouldOverwriteAReadOnlyCollection()
56+
{
57+
var source = new PublicProperty<IEnumerable<decimal>>
58+
{
59+
Value = new[] { MinValue, MaxValue }
60+
};
61+
62+
var target = new PublicField<ReadOnlyCollection<decimal>>
63+
{
64+
Value = new ReadOnlyCollection<decimal>(new[] { MinusOne, Zero })
65+
};
66+
67+
var result = Mapper.Map(source).Over(target);
68+
69+
result.Value.ShouldBe(MinValue, MaxValue);
70+
}
71+
5472
[Fact]
5573
public void ShouldOverwriteAComplexTypeCollection()
5674
{
@@ -138,5 +156,18 @@ public void ShouldOverwriteANonNullReadOnlyNestedCollection()
138156
result.Value.ShouldBeSameAs(strings);
139157
result.Value.ShouldBe("One!", "Two!", "Three");
140158
}
159+
160+
[Fact]
161+
public void ShouldHandleAReadOnlyNestedReadOnlyCollection()
162+
{
163+
var source = new PublicField<string[]> { Value = new[] { "One!", "Two!" } };
164+
var strings = new List<string> { "A", "B", "C" }.AsReadOnly();
165+
var target = new PublicReadOnlyField<ReadOnlyCollection<string>>(strings);
166+
var result = Mapper.Map(source).Over(target);
167+
168+
result.Value.ShouldNotBeNull();
169+
result.Value.ShouldBeSameAs(strings);
170+
result.Value.ShouldBe("A", "B", "C");
171+
}
141172
}
142173
}

AgileMapper.UnitTests/WhenMappingOverEnumerables.cs

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,17 @@ public void ShouldOverwriteARootSimpleTypeArray()
2222
result.ShouldBe(5, 4, 3, 2);
2323
}
2424

25+
[Fact]
26+
public void ShouldOverwriteARootSimpleTypeReadOnlyCollection()
27+
{
28+
var source = new[] { '2', '3' };
29+
var target = new ReadOnlyCollection<char>(new List<char> { '5', '4' });
30+
var result = Mapper.Map(source).Over(target);
31+
32+
result.ShouldNotBeNull();
33+
result.ShouldBe('2', '3');
34+
}
35+
2536
[Fact]
2637
public void ShouldOverwriteARootSimpleTypeList()
2738
{
@@ -91,7 +102,7 @@ public void ShouldOverwriteARootComplexTypeCollectionElementByRuntimeType()
91102
}
92103

93104
[Fact]
94-
public void ShouldOverwriteAnExistingObject()
105+
public void ShouldOverwriteAnExistingObjectById()
95106
{
96107
var source = new[]
97108
{
@@ -112,6 +123,28 @@ public void ShouldOverwriteAnExistingObject()
112123
result.ShouldBe(p => p.Name, "Lisa", "Bart");
113124
}
114125

126+
[Fact]
127+
public void ShouldOverwriteAnExistingObjectByIdInAReadOnlyCollection()
128+
{
129+
var source = new[]
130+
{
131+
new CustomerViewModel { Id = Guid.NewGuid(), Name = "Homer" }
132+
};
133+
134+
var target = new ReadOnlyCollection<CustomerViewModel>(new List<CustomerViewModel>
135+
{
136+
new CustomerViewModel { Id = source.First().Id, Name = "Maggie" }
137+
});
138+
139+
var originalObject = target.First();
140+
var result = Mapper.Map(source).Over(target);
141+
142+
result.ShouldNotBeSameAs(target);
143+
result.ShouldHaveSingleItem();
144+
result.First().ShouldBeSameAs(originalObject);
145+
result.First().Name.ShouldBe("Homer");
146+
}
147+
115148
[Fact]
116149
public void ShouldOverwriteUsingAConfiguredDataSource()
117150
{

AgileMapper.UnitTests/WhenMappingToNewEnumerableMembers.cs

Lines changed: 80 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,49 @@ public void ShouldCreateANewObjectCollection()
5858
result.Value.Third().ShouldBe(string.Empty);
5959
}
6060

61+
[Fact]
62+
public void ShouldCreateANewObjectReadOnlyCollection()
63+
{
64+
var source = new PublicField<object[]>
65+
{
66+
Value = new object[] { 9, new CustomerViewModel { Name = "Boycee" }, default(string) }
67+
};
68+
var result = Mapper.Map(source).ToANew<PublicProperty<ReadOnlyCollection<object>>>();
69+
70+
result.Value.ShouldNotBeNull();
71+
result.Value.First().ShouldBe(9);
72+
result.Value.Second().ShouldBeOfType<CustomerViewModel>();
73+
((CustomerViewModel)result.Value.Second()).Name.ShouldBe("Boycee");
74+
result.Value.Third().ShouldBeNull();
75+
}
76+
77+
[Fact]
78+
public void ShouldMapFromAReadOnlyCollection()
79+
{
80+
var source = new PublicField<ReadOnlyCollection<string>>
81+
{
82+
Value = new ReadOnlyCollection<string>(new[] { "R", "A", "T", "M" })
83+
};
84+
var result = Mapper.Map(source).ToANew<PublicProperty<object[]>>();
85+
86+
result.Value.ShouldNotBeNull();
87+
result.Value.ShouldBe("R", "A", "T", "M");
88+
}
89+
90+
[Fact]
91+
public void ShouldMapFromAnEmptyReadOnlyCollection()
92+
{
93+
var source = new PublicField<ReadOnlyCollection<string>>
94+
{
95+
Value = new ReadOnlyCollection<string>(Enumerable<string>.EmptyArray)
96+
};
97+
var result = Mapper.Map(source).ToANew<PublicProperty<ReadOnlyCollection<string>>>();
98+
99+
result.Value.ShouldNotBeNull();
100+
result.Value.ShouldNotBeSameAs(source.Value);
101+
result.Value.ShouldBeEmpty();
102+
}
103+
61104
[Fact]
62105
public void ShouldCreateANewComplexTypeEnumerable()
63106
{
@@ -192,7 +235,7 @@ public void ShouldRetainAnExistingCollection()
192235
}
193236

194237
[Fact]
195-
public void ShouldApplyAConfiguredExpressionToAnEnumerable()
238+
public void ShouldApplyAConfiguredExpressionToAnArray()
196239
{
197240
using (var mapper = Mapper.CreateNew())
198241
{
@@ -289,6 +332,42 @@ public void ShouldHandleANonNullReadOnlyNestedReadOnlyICollection()
289332
}
290333
}
291334

335+
[Fact]
336+
public void ShouldHandleANonNullReadOnlyNestedArray()
337+
{
338+
using (var mapper = Mapper.CreateNew())
339+
{
340+
var readOnlyStrings = new[] { "1", "2" };
341+
342+
mapper.CreateAReadOnlyFieldUsing(readOnlyStrings);
343+
344+
var source = new PublicField<string[]> { Value = new[] { "3" } };
345+
var result = mapper.Map(source).ToANew<PublicReadOnlyField<string[]>>();
346+
347+
result.Value.ShouldNotBeNull();
348+
result.Value.ShouldBeSameAs(readOnlyStrings);
349+
result.Value.ShouldBe("1", "2");
350+
}
351+
}
352+
353+
[Fact]
354+
public void ShouldHandleANonNullReadOnlyNestedReadOnlyCollection()
355+
{
356+
using (var mapper = Mapper.CreateNew())
357+
{
358+
var readOnlyInts = new ReadOnlyCollection<int>(new[] { 5, 5, 5 });
359+
360+
mapper.CreateAReadOnlyPropertyUsing(readOnlyInts);
361+
362+
var source = new PublicField<string[]> { Value = new[] { "3" } };
363+
var result = mapper.Map(source).ToANew<PublicReadOnlyProperty<ReadOnlyCollection<int>>>();
364+
365+
result.Value.ShouldNotBeNull();
366+
result.Value.ShouldBeSameAs(readOnlyInts);
367+
result.Value.ShouldBe(5, 5, 5);
368+
}
369+
}
370+
292371
[Fact]
293372
public void ShouldPopulateANonNullReadOnlyNestedEnumerable()
294373
{

0 commit comments

Comments
 (0)