Skip to content

Commit 818d50c

Browse files
authored
Flags enums (#45)
* Start of flags enum support * Mapping numeric values to enums with IsDefined and a cast, instead of TryParse with the string value * Reusing Enum.IsDefined call creation method * Support for mapping numeric values to multi-value flags enums * Extending numeric to flags enum test coverage * Support for mapping a numeric string value to a flags enum * Expanding string -> flags enum test coverage
1 parent 90a94a5 commit 818d50c

File tree

6 files changed

+438
-54
lines changed

6 files changed

+438
-54
lines changed

AgileMapper.UnitTests/AgileMapper.UnitTests.csproj

+2
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,7 @@
143143
<Compile Include="Should.cs" />
144144
<Compile Include="ShouldExtensions.cs" />
145145
<Compile Include="SimpleTypeConversion\WhenConvertingToCharacters.cs" />
146+
<Compile Include="SimpleTypeConversion\WhenConvertingToFlagsEnums.cs" />
146147
<Compile Include="Structs\Configuration\WhenConfiguringStructCreationCallbacks.cs" />
147148
<Compile Include="Structs\Configuration\WhenConfiguringStructDataSources.cs" />
148149
<Compile Include="Structs\Configuration\WhenConfiguringStructMappingCallbacks.cs" />
@@ -207,6 +208,7 @@
207208
<Compile Include="TestClasses\PublicTwoParamCtor.cs" />
208209
<Compile Include="TestClasses\SaveOrderItemRequest.cs" />
209210
<Compile Include="TestClasses\SaveOrderRequest.cs" />
211+
<Compile Include="TestClasses\Status.cs" />
210212
<Compile Include="TestClasses\StringKeyedDictionary.cs" />
211213
<Compile Include="TestClasses\Wedding.cs" />
212214
<Compile Include="TestClasses\WeddingDto.cs" />

AgileMapper.UnitTests/SimpleTypeConversion/WhenConvertingToEnums.cs

+25-24
Original file line numberDiff line numberDiff line change
@@ -2,40 +2,41 @@
22
{
33
using TestClasses;
44
using Xunit;
5+
using static TestClasses.Title;
56

67
public class WhenConvertingToEnums
78
{
89
[Fact]
910
public void ShouldMapAByteToAnEnum()
1011
{
11-
var source = new PublicField<byte> { Value = (byte)Title.Dr };
12+
var source = new PublicField<byte> { Value = (byte)Dr };
1213
var result = Mapper.Map(source).ToANew<PublicField<Title>>();
1314

14-
result.Value.ShouldBe(Title.Dr);
15+
result.Value.ShouldBe(Dr);
1516
}
1617

1718
[Fact]
1819
public void ShouldMapAShortToAnEnum()
1920
{
20-
var source = new PublicField<short> { Value = (short)Title.Miss };
21+
var source = new PublicField<short> { Value = (short)Miss };
2122
var result = Mapper.Map(source).ToANew<PublicField<Title>>();
2223

23-
result.Value.ShouldBe(Title.Miss);
24+
result.Value.ShouldBe(Miss);
2425
}
2526

2627
[Fact]
2728
public void ShouldMapANullableIntToAnEnum()
2829
{
29-
var source = new PublicProperty<int?> { Value = (int)Title.Lady };
30+
var source = new PublicProperty<int?> { Value = (int)Lady };
3031
var result = Mapper.Map(source).ToANew<PublicField<Title>>();
3132

32-
result.Value.ShouldBe(Title.Lady);
33+
result.Value.ShouldBe(Lady);
3334
}
3435

3536
[Fact]
3637
public void ShouldMapAnIntToAnEnum()
3738
{
38-
var source = new PublicProperty<int> { Value = (int)Title.Dr };
39+
var source = new PublicProperty<int> { Value = (int)Dr };
3940
var result = Mapper.Map(source).ToANew<PublicField<Title>>();
4041

4142
result.Value.ShouldBe((Title)source.Value);
@@ -53,7 +54,7 @@ public void ShouldMapANullNullableIntToAnEnum()
5354
[Fact]
5455
public void ShouldMapALongToAnEnum()
5556
{
56-
var source = new PublicProperty<long> { Value = (long)Title.Miss };
57+
var source = new PublicProperty<long> { Value = (long)Miss };
5758
var result = Mapper.Map(source).ToANew<PublicField<Title>>();
5859

5960
result.Value.ShouldBe((Title)source.Value);
@@ -62,7 +63,7 @@ public void ShouldMapALongToAnEnum()
6263
[Fact]
6364
public void ShouldMapANonMatchingNullableLongToANullableEnum()
6465
{
65-
var source = new PublicProperty<long?> { Value = (long)Title.Earl };
66+
var source = new PublicProperty<long?> { Value = (long)Earl };
6667
var result = Mapper.Map(source).ToANew<PublicField<TitleShortlist?>>();
6768

6869
result.Value.ShouldBeNull();
@@ -98,28 +99,28 @@ public void ShouldMapAMatchingNullableCharacterOnToANullableEnum()
9899
[Fact]
99100
public void ShouldMapAMatchingStringOnToAnEnum()
100101
{
101-
var source = new PublicField<string> { Value = Title.Mrs.ToString() };
102+
var source = new PublicField<string> { Value = Mrs.ToString() };
102103
var result = Mapper.Map(source).OnTo(new PublicProperty<Title>());
103104

104-
result.Value.ShouldBe(Title.Mrs);
105+
result.Value.ShouldBe(Mrs);
105106
}
106107

107108
[Fact]
108109
public void ShouldMapAMatchingStringOnToAnEnumCaseInsensitively()
109110
{
110-
var source = new PublicField<string> { Value = Title.Miss.ToString().ToLowerInvariant() };
111+
var source = new PublicField<string> { Value = Miss.ToString().ToLowerInvariant() };
111112
var result = Mapper.Map(source).OnTo(new PublicProperty<Title>());
112113

113-
result.Value.ShouldBe(Title.Miss);
114+
result.Value.ShouldBe(Miss);
114115
}
115116

116117
[Fact]
117118
public void ShouldMapAMatchingNumericStringOverAnEnum()
118119
{
119-
var source = new PublicField<string> { Value = ((int)Title.Dr).ToString() };
120+
var source = new PublicField<string> { Value = ((int)Dr).ToString() };
120121
var result = Mapper.Map(source).Over(new PublicProperty<Title>());
121122

122-
result.Value.ShouldBe(Title.Dr);
123+
result.Value.ShouldBe(Dr);
123124
}
124125

125126
[Fact]
@@ -146,13 +147,13 @@ public void ShouldMapAnEnumToAnEnum()
146147
var source = new PublicProperty<TitleShortlist> { Value = TitleShortlist.Mrs };
147148
var result = Mapper.Map(source).ToANew<PublicProperty<Title>>();
148149

149-
result.Value.ShouldBe(Title.Mrs);
150+
result.Value.ShouldBe(Mrs);
150151
}
151152

152153
[Fact]
153154
public void ShouldMapANonMatchingEnumToANullableEnum()
154155
{
155-
var source = new PublicProperty<Title> { Value = Title.Lord };
156+
var source = new PublicProperty<Title> { Value = Lord };
156157
var result = Mapper.Map(source).ToANew<PublicProperty<TitleShortlist?>>();
157158

158159
result.Value.ShouldBeNull();
@@ -161,10 +162,10 @@ public void ShouldMapANonMatchingEnumToANullableEnum()
161162
[Fact]
162163
public void ShouldMapANullableEnumToAnEnum()
163164
{
164-
var source = new PublicProperty<Title?> { Value = Title.Dr };
165+
var source = new PublicProperty<Title?> { Value = Dr };
165166
var result = Mapper.Map(source).ToANew<PublicProperty<Title>>();
166167

167-
result.Value.ShouldBe(Title.Dr);
168+
result.Value.ShouldBe(Dr);
168169
}
169170

170171
[Fact]
@@ -206,7 +207,7 @@ public void ShouldMapAnObjectEnumMemberValueToAnNullableEnum()
206207
[Fact]
207208
public void ShouldMapAnObjectNullableEnumMemberValueToAnNullableEnum()
208209
{
209-
var source = new PublicProperty<object> { Value = (Title?)Title.Mr };
210+
var source = new PublicProperty<object> { Value = (Title?)Mr };
210211
var result = Mapper.Map(source).ToANew<PublicProperty<TitleShortlist>>();
211212

212213
result.Value.ShouldBe(TitleShortlist.Mr);
@@ -225,20 +226,20 @@ public void ShouldMapEnumsConditionally()
225226
.If((ptf, pf) => ptf.Value1 == null)
226227
.Map((ptf, pf) => ptf.Value2).To(pf => pf.Value)
227228
.And
228-
.If((ptf, pf) => Title.Duke == ptf.Value1)
229+
.If((ptf, pf) => Duke == ptf.Value1)
229230
.Map(TitleShortlist.Other).To(pf => pf.Value);
230231

231-
var nonNullSource = new PublicTwoFields<Title?, Title> { Value1 = Title.Dr, Value2 = Title.Count };
232+
var nonNullSource = new PublicTwoFields<Title?, Title> { Value1 = Dr, Value2 = Count };
232233
var nonNullResult = mapper.Map(nonNullSource).ToANew<PublicField<TitleShortlist>>();
233234

234235
nonNullResult.Value.ShouldBe(TitleShortlist.Dr);
235236

236-
var nullSource = new PublicTwoFields<Title?, Title> { Value1 = null, Value2 = Title.Mrs };
237+
var nullSource = new PublicTwoFields<Title?, Title> { Value1 = null, Value2 = Mrs };
237238
var nullResult = mapper.Map(nullSource).ToANew<PublicField<TitleShortlist>>();
238239

239240
nullResult.Value.ShouldBe(TitleShortlist.Mrs);
240241

241-
var dukeSource = new PublicTwoFields<Title?, Title> { Value1 = Title.Duke };
242+
var dukeSource = new PublicTwoFields<Title?, Title> { Value1 = Duke };
242243
var dukeResult = mapper.Map(dukeSource).ToANew<PublicField<TitleShortlist>>();
243244

244245
dukeResult.Value.ShouldBe(TitleShortlist.Other);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
namespace AgileObjects.AgileMapper.UnitTests.SimpleTypeConversion
2+
{
3+
using TestClasses;
4+
using Xunit;
5+
using static TestClasses.Status;
6+
7+
public class WhenConvertingToFlagsEnums
8+
{
9+
[Fact]
10+
public void ShouldMapASingleValueByteToAFlagsEnum()
11+
{
12+
var source = new PublicField<byte> { Value = (byte)InProgress };
13+
var result = Mapper.Map(source).ToANew<PublicField<Status>>();
14+
15+
result.Value.ShouldBe(InProgress);
16+
}
17+
18+
[Fact]
19+
public void ShouldMapAMultiValueShortToAFlagsEnum()
20+
{
21+
var source = new PublicField<short> { Value = (short)(InProgress | Assigned) };
22+
var result = Mapper.Map(source).ToANew<PublicField<Status>>();
23+
24+
result.Value.HasFlag(InProgress).ShouldBeTrue();
25+
result.Value.HasFlag(Assigned).ShouldBeTrue();
26+
result.Value.HasFlag(Cancelled).ShouldBeFalse();
27+
result.Value.ShouldBe(InProgress | Assigned);
28+
}
29+
30+
[Fact]
31+
public void ShouldMapAMultiValueNullableIntToAFlagsEnum()
32+
{
33+
var source = new PublicProperty<int?> { Value = (int)(New | Completed | Cancelled) };
34+
var result = Mapper.Map(source).ToANew<PublicField<Status>>();
35+
36+
result.Value.ShouldBe(New | Completed | Cancelled);
37+
}
38+
39+
[Fact]
40+
public void ShouldMapANullNullableIntToANullableFlagsEnum()
41+
{
42+
var source = new PublicProperty<int?> { Value = default(int?) };
43+
var result = Mapper.Map(source).ToANew<PublicField<Status?>>();
44+
45+
result.Value.ShouldBeNull();
46+
}
47+
48+
[Fact]
49+
public void ShouldMapASingleValueLongToANullableFlagsEnum()
50+
{
51+
var source = new PublicProperty<long> { Value = (long)Removed };
52+
var result = Mapper.Map(source).ToANew<PublicField<Status?>>();
53+
54+
result.Value.ShouldBe(Removed);
55+
}
56+
57+
[Fact]
58+
public void ShouldMapAMultiValueNumericCharacterToAFlagsEnum()
59+
{
60+
var source = new PublicProperty<char> { Value = '9' };
61+
var result = Mapper.Map(source).ToANew<PublicField<Status>>();
62+
63+
result.Value.ShouldBe(New | Completed);
64+
}
65+
66+
[Fact]
67+
public void ShouldMapASingleValueNumericStringToAFlagsEnum()
68+
{
69+
var source = new PublicProperty<string> { Value = "4" };
70+
var result = Mapper.Map(source).ToANew<PublicField<Status>>();
71+
72+
result.Value.ShouldBe((Status)4);
73+
}
74+
75+
[Fact]
76+
public void ShouldMapAMultiValueMixedStringToAFlagsEnum()
77+
{
78+
var source = new PublicProperty<string> { Value = "9, InProgress, 4" };
79+
var result = Mapper.Map(source).ToANew<PublicField<Status>>();
80+
81+
result.Value.ShouldBe(New | InProgress | Completed);
82+
}
83+
}
84+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
namespace AgileObjects.AgileMapper.UnitTests.TestClasses
2+
{
3+
using System;
4+
5+
[Flags]
6+
public enum Status
7+
{
8+
New = 1,
9+
Assigned = 2,
10+
InProgress = 4,
11+
Completed = 8,
12+
Cancelled = 16,
13+
Removed = 32
14+
}
15+
}

AgileMapper/ObjectPopulation/Enumerables/EnumerableSourcePopulationLoopData.cs

+4-5
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,6 @@ namespace AgileObjects.AgileMapper.ObjectPopulation.Enumerables
1010

1111
internal class EnumerableSourcePopulationLoopData : IPopulationLoopData
1212
{
13-
private static readonly MethodInfo _enumeratorMoveNextMethod = typeof(IEnumerator).GetPublicInstanceMethod("MoveNext");
14-
private static readonly MethodInfo _disposeMethod = typeof(IDisposable).GetPublicInstanceMethod("Dispose");
15-
1613
private readonly Expression _enumerableSubject;
1714
private readonly MethodInfo _getEnumeratorMethod;
1815
private readonly ParameterExpression _enumerator;
@@ -34,7 +31,7 @@ public EnumerableSourcePopulationLoopData(
3431
_enumerator = Expression.Variable(_getEnumeratorMethod.ReturnType, "enumerator");
3532

3633
ContinueLoopTarget = Expression.Label(typeof(void), "Continue");
37-
LoopExitCheck = Expression.Not(Expression.Call(_enumerator, _enumeratorMoveNextMethod));
34+
LoopExitCheck = Expression.Not(Expression.Call(_enumerator, typeof(IEnumerator).GetPublicInstanceMethod("MoveNext")));
3835
SourceElement = Expression.Property(_enumerator, "Current");
3936
}
4037

@@ -69,7 +66,9 @@ public BlockExpression GetLoopBlock(
6966

7067
var enumeratorAssignment = _enumerator.AssignTo(enumeratorValue);
7168

72-
Expression finallyClause = Expression.Call(_enumerator, _disposeMethod);
69+
Expression finallyClause = Expression.Call(
70+
_enumerator,
71+
typeof(IDisposable).GetPublicInstanceMethod("Dispose"));
7372

7473
if (finallyClauseFactory != null)
7574
{

0 commit comments

Comments
 (0)