Skip to content

Commit a0b7d9b

Browse files
authored
Todo cleanup (#28)
* Increasing test coverage, re: invalid source member specification / Removing completed TODO * Replacing uses of Linq Concat * Test coverage for specifying an un-string-formatable source type * Increasing test coverage, re: ICollection.ToArray extension method * Moving class to a dedicated file * Support for mapping from complex type members to matching-type dictionary entries / Supplying capacities for Expression replacement dictionaries
1 parent 98b5ccd commit a0b7d9b

28 files changed

+187
-104
lines changed

AgileMapper.UnitTests/Configuration/WhenConfiguringStringFormatting.cs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,5 +68,20 @@ public void ShouldErrorIfNoFormatSpecified()
6868

6969
noFormatEx.Message.ShouldContain("No format string specified");
7070
}
71+
72+
[Fact]
73+
public void ShouldErrorIfUnformattableTypeSpecified()
74+
{
75+
var noFormatEx = Should.Throw<MappingConfigurationException>(() =>
76+
{
77+
using (var mapper = Mapper.CreateNew())
78+
{
79+
mapper.WhenMapping
80+
.StringsFrom<PublicField<string>>(c => c.FormatUsing("xxx"));
81+
}
82+
});
83+
84+
noFormatEx.Message.ShouldContain("No ToString method");
85+
}
7186
}
7287
}

AgileMapper.UnitTests/Dictionaries/Configuration/WhenConfiguringDictionaryMappingIncorrectly.cs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
namespace AgileObjects.AgileMapper.UnitTests.Dictionaries.Configuration
22
{
3+
using System;
34
using AgileMapper.Configuration;
45
using Shouldly;
56
using TestClasses;
@@ -131,6 +132,32 @@ public void ShouldErrorIfCustomDataSourceMemberIsGivenCustomMemberName()
131132
configEx.Message.ShouldContain("has a configured data source");
132133
}
133134

135+
[Fact]
136+
public void ShouldErrorIfAnInvalidSourceMemberIsSpecified()
137+
{
138+
var configEx = Should.Throw<NotSupportedException>(() =>
139+
Mapper.WhenMapping
140+
.From<PublicField<string>>()
141+
.ToDictionaries
142+
.MapMember(pf => pf.Value + "!")
143+
.ToFullKey("That won't work"));
144+
145+
configEx.Message.ShouldContain("Unable to get member access");
146+
}
147+
148+
[Fact]
149+
public void ShouldErrorIfAnUnreadableSourceMemberIsSpecified()
150+
{
151+
var configEx = Should.Throw<MappingConfigurationException>(() =>
152+
Mapper.WhenMapping
153+
.From<PublicWriteOnlyProperty<string>>()
154+
.ToDictionaries
155+
.MapMember(pf => pf.Value)
156+
.ToFullKey("That won't work"));
157+
158+
configEx.Message.ShouldContain("is not readable");
159+
}
160+
134161
[Fact]
135162
public void ShouldErrorIfMemberNamesAreFlattenedAndSeparatedGlobally()
136163
{

AgileMapper.UnitTests/Dictionaries/WhenMappingToNewDictionaries.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,16 @@ public void ShouldMapASimpleTypeMemberToATypedDictionary()
2828
result["Value"].ShouldBe(int.MaxValue);
2929
}
3030

31+
[Fact]
32+
public void ShouldMapAComplexTypeMemberToATypedDictionary()
33+
{
34+
var source = new PublicProperty<Product> { Value = new Product { ProductId = "xxx" } };
35+
var result = Mapper.Map(source).ToANew<Dictionary<string, Product>>();
36+
37+
result.ContainsKey("Value").ShouldBeTrue();
38+
result["Value"].ShouldBeOfType<Product>();
39+
}
40+
3141
[Fact]
3242
public void ShouldMapASimpleTypeMemberToAConvertibleTypedDictionary()
3343
{

AgileMapper.UnitTests/WhenMappingOverEnumerableMembers.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ public void ShouldOverwriteAnArray()
5454
[Fact]
5555
public void ShouldOverwriteAReadOnlyCollection()
5656
{
57-
var source = new PublicProperty<IEnumerable<decimal>>
57+
var source = new PublicProperty<ICollection<decimal>>
5858
{
5959
Value = new[] { MinValue, MaxValue }
6060
};

AgileMapper/Api/Configuration/Dictionaries/DictionaryMappingConfigContinuation.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ public DictionaryMappingConfigContinuation(MappingConfigInfo configInfo)
1616
ISourceDictionaryMappingConfigurator<TFirst, TSecond> ISourceDictionaryMappingConfigContinuation<TFirst, TSecond>.And
1717
=> new SourceDictionaryMappingConfigurator<TFirst, TSecond>(_configInfo.Clone());
1818

19-
// TODO: Code coverage
2019
ITargetDictionaryMappingConfigurator<TFirst, TSecond> ITargetDictionaryMappingConfigContinuation<TFirst, TSecond>.And
2120
=> new TargetDictionaryMappingConfigurator<TFirst, TSecond>(_configInfo.Clone());
2221
}

AgileMapper/Api/Configuration/Dictionaries/TargetDictionaryMappingConfigurator.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,6 @@ private QualifiedMember GetSourceMemberOrThrow(LambdaExpression lambda)
5858
return sourceMember;
5959
}
6060

61-
// TODO: test coverage - invalid source member expression
6261
throw new MappingConfigurationException(
6362
$"Source member {lambda.Body.ToReadableString()} is not readable.");
6463
}

AgileMapper/Configuration/DerivedTypePairSet.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
internal class DerivedTypePairSet
1414
{
1515
private static readonly object _lookupSync = new object();
16-
private static readonly DerivedTypePair[] _noPairs = Enumerable<DerivedTypePair>.EmptyArray;
16+
1717
private readonly Dictionary<Type, List<DerivedTypePair>> _typePairsByTargetType;
1818
private readonly List<SourceAndTargetTypesKey> _autoCheckedTypes;
1919

@@ -64,23 +64,23 @@ private static void RemoveConflictingPairIfAppropriate(
6464
}
6565
}
6666

67-
public ICollection<DerivedTypePair> GetDerivedTypePairsFor(
67+
public IList<DerivedTypePair> GetDerivedTypePairsFor(
6868
IBasicMapperData mapperData,
6969
MapperContext mapperContext)
7070
{
7171
LookForDerivedTypePairs(mapperData, mapperContext);
7272

7373
if (_typePairsByTargetType.None())
7474
{
75-
return _noPairs;
75+
return Enumerable<DerivedTypePair>.EmptyArray;
7676
}
7777

7878
if (_typePairsByTargetType.TryGetValue(mapperData.TargetType, out var typePairs))
7979
{
8080
return typePairs.Where(tp => tp.AppliesTo(mapperData)).ToArray();
8181
}
8282

83-
return _noPairs;
83+
return Enumerable<DerivedTypePair>.EmptyArray;
8484
}
8585

8686
#region Auto-Registration

AgileMapper/Configuration/ParametersSwapper.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ private static Expression SwapForContextParameter(SwapArgs swapArgs)
9797
var indexProperty = memberContextType.GetPublicInstanceProperty("EnumerableIndex");
9898
var parentProperty = memberContextType.GetPublicInstanceProperty("Parent");
9999

100-
var replacementsByTarget = new ExpressionReplacementDictionary
100+
var replacementsByTarget = new ExpressionReplacementDictionary(5)
101101
{
102102
[Expression.Property(contextParameter, sourceProperty)] = contextInfo.SourceAccess,
103103
[Expression.Property(contextParameter, targetProperty)] = contextInfo.TargetAccess,

AgileMapper/Constants.cs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,6 @@ internal static class Constants
1313
{
1414
public static readonly bool ReflectionNotPermitted = ReflectionExtensions.ReflectionNotPermitted;
1515

16-
public static readonly string[] EmptyStringArray = Enumerable<string>.EmptyArray;
17-
1816
public static readonly string EnumerableElementName = "[i]";
1917

2018
public static readonly Type[] NoTypeArguments = Enumerable<Type>.EmptyArray;
@@ -54,8 +52,7 @@ internal static class Constants
5452
};
5553

5654
public static readonly Type[] NumericTypes = WholeNumberNumericTypes
57-
.Concat(typeof(float), typeof(decimal), typeof(double))
58-
.ToArray();
55+
.Append(typeof(float), typeof(decimal), typeof(double));
5956

6057
public static readonly IDictionary<Type, double> NumericTypeMaxValuesByType = GetValuesByType("MaxValue");
6158
public static readonly IDictionary<Type, double> NumericTypeMinValuesByType = GetValuesByType("MinValue");

AgileMapper/DataSources/DataSourceBase.cs

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,8 @@ protected DataSourceBase(
4444
ProcessMemberAccesses(
4545
mapperData,
4646
ref value,
47-
out IList<Expression> nestedAccesses,
48-
out ICollection<ParameterExpression> variables);
47+
out var nestedAccesses,
48+
out var variables);
4949

5050
Condition = nestedAccesses.GetIsNotDefaultComparisonsOrNull();
5151
Variables = variables;
@@ -58,7 +58,7 @@ private static void ProcessMemberAccesses(
5858
IMemberMapperData mapperData,
5959
ref Expression value,
6060
out IList<Expression> nestedAccesses,
61-
out ICollection<ParameterExpression> variables)
61+
out IList<ParameterExpression> variables)
6262
{
6363
var valueInfo = mapperData.GetExpressionInfoFor(value, targetCanBeNull: false);
6464
nestedAccesses = valueInfo.NestedAccesses;
@@ -69,22 +69,24 @@ private static void ProcessMemberAccesses(
6969
return;
7070
}
7171

72-
variables = new List<ParameterExpression>();
73-
var cacheVariablesByValue = new Dictionary<Expression, Expression>();
74-
var valueExpressions = new List<Expression>(valueInfo.MultiInvocations.Count + 1);
72+
var numberOfInvocations = valueInfo.MultiInvocations.Count;
73+
variables = new ParameterExpression[numberOfInvocations];
74+
var cacheVariablesByValue = new Dictionary<Expression, Expression>(numberOfInvocations);
75+
var valueExpressions = new Expression[numberOfInvocations + 1];
7576

76-
foreach (var invocation in valueInfo.MultiInvocations)
77+
for (var i = 0; i < numberOfInvocations; i++)
7778
{
79+
var invocation = valueInfo.MultiInvocations[i];
7880
var valueVariableName = invocation.Type.GetFriendlyName().ToCamelCase() + "Value";
7981
var valueVariable = Expression.Variable(invocation.Type, valueVariableName);
8082
var valueVariableValue = invocation.Replace(cacheVariablesByValue);
8183

8284
cacheVariablesByValue.Add(invocation, valueVariable);
83-
variables.Add(valueVariable);
84-
valueExpressions.Add(valueVariable.AssignTo(valueVariableValue));
85+
variables[i] = valueVariable;
86+
valueExpressions[i] = valueVariable.AssignTo(valueVariableValue);
8587
}
8688

87-
valueExpressions.Add(value.Replace(cacheVariablesByValue));
89+
valueExpressions[numberOfInvocations] = value.Replace(cacheVariablesByValue);
8890
value = Expression.Block(valueExpressions);
8991
}
9092

0 commit comments

Comments
 (0)