Skip to content

Commit

Permalink
Merge branch 'achaplygin/AllowDistinctInValidations'
Browse files Browse the repository at this point in the history
  • Loading branch information
Anton92nd committed Apr 28, 2021
2 parents c4cfd0e + b99116d commit f0423a1
Show file tree
Hide file tree
Showing 7 changed files with 150 additions and 4 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# Changelog

## v1.4.xx - 2021.04.28
- Support LINQ `Distinct` and `Count` methods

## v1.3.16 - 2021.03.12
- Update dependencies.
- Run tests against net5.0 tfm.
Expand Down
21 changes: 21 additions & 0 deletions Mutators.Tests/DependenciesExtractorTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -474,6 +474,27 @@ public void TestStringLength()
DoTest(expression, a => a.S);
}

[Test]
public void TestDistinct()
{
Expression<Func<A, string[]>> expression = a => a.B.C.Select(x => x.S).Distinct().ToArray();
DoTest(expression, a => a.B.C.Each().S);
}

[Test]
public void TestDistinctCount()
{
Expression<Func<A, int>> expression = a => a.B.C.Select(x => x.S).Distinct().Count();
DoTest(expression, a => a.B.C.Each().S);
}

[Test]
public void TestWhereCount()
{
Expression<Func<A, int>> expression = a => a.B.C.Where(x => x.D.S == "zzz").Count();
DoTest(expression, a => a.B.C.Each().D.S, a => a.B.C);
}

private static Expression ClearConverts(Expression node)
{
while (node.NodeType == ExpressionType.Convert || node.NodeType == ExpressionType.ConvertChecked)
Expand Down
100 changes: 100 additions & 0 deletions Mutators.Tests/ValidatorsTest.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;

Expand All @@ -19,6 +20,18 @@ public static void AssertEquivalent<T>(this ValidationResultTreeNode tree, Valid
}
}

[MultiLanguageTextType("KeyValuePairsNotUnique")]
public class KeyValuePairsNotUnique : MultiLanguageTextBase
{
public string[] Keys { get; set; }

protected override void Register()
{
Register("RU", () => string.Format("Ключи key-value полей должны быть уникальны. Повторяющиеся ключи: {0}",
string.Join(", ", Keys.Where(key => Keys.Where(x => key == x).Count() > 1).Distinct())));
}
}

[Parallelizable(ParallelScope.All)]
public class ValidatorsTest : TestBase
{
Expand All @@ -30,6 +43,85 @@ public void TestProperty()
validator(new TestData {A = new A {S = "zzz"}}).AssertEquivalent(new ValidationResultTreeNode<TestData> {{"S", FormattedValidationResult.Error(null, null, new SimplePathFormatterText {Paths = new[] {"S"}})}});
}

[Test]
public void TestKeyValuePairs()
{
var collection = new TestDataConfiguratorCollection<TestData>(null, null, pathFormatterCollection,
configurator => configurator.GoTo(data => data.KeyValuePairs).Target(keyValuePairs => keyValuePairs)
.InvalidIf(keyValuePairs => keyValuePairs.Select(x => x.Key).Distinct().Count() != keyValuePairs.Length,
keyValuePairs => new KeyValuePairsNotUnique {Keys = keyValuePairs.Select(x => x.Key).ToArray()}));
var validator = collection.GetMutatorsTree(MutatorsContext.Empty).GetValidator();
var testData = new TestData
{
KeyValuePairs = new[]
{
new KeyValuePair
{
Key = "a",
Value = "b"
},
new KeyValuePair
{
Key = "b",
Value = "c"
}
},
};
validator(testData).AssertEquivalent(new ValidationResultTreeNode<TestData>());

testData = new TestData
{
KeyValuePairs = new[]
{
new KeyValuePair
{
Key = "b",
Value = "b"
},
new KeyValuePair
{
Key = "b",
Value = "c"
},
new KeyValuePair
{
Key = "a",
Value = "c",
}
},
};
var validationResultTreeNode = validator(testData);
validationResultTreeNode.AssertEquivalent(new ValidationResultTreeNode<TestData>
{
{
"KeyValuePairs", FormattedValidationResult.Error(
new KeyValuePairsNotUnique
{
Keys = new[] {"b", "b", "a"}
},
new[]
{
new KeyValuePair
{
Key = "b",
Value = "b"
},
new KeyValuePair
{
Key = "b",
Value = "c"
},
new KeyValuePair
{
Key = "a",
Value = "c",
}
},
new SimplePathFormatterText {Paths = new[] {"KeyValuePairs"}})
}
});
}

[Test]
public void TestArray()
{
Expand Down Expand Up @@ -927,6 +1019,8 @@ public class TestData
public string S { get; set; }
public string F { get; set; }

public KeyValuePair[] KeyValuePairs { get; set; }

public A A { get; set; }

public D D { get; set; }
Expand All @@ -939,6 +1033,12 @@ public class TestData
public Dictionary<string, string> Dict { get; set; }
}

public class KeyValuePair
{
public string Key { get; set; }
public string Value { get; set; }
}

public class A
{
public B[] B { get; set; }
Expand Down
22 changes: 22 additions & 0 deletions Mutators/Visitors/DependenciesExtractor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -486,6 +486,8 @@ private MethodProcessor GetMethodProcessor(MethodInfo method)
case "Max":
case "Min":
return ProcessLinqSum;
case "Count":
return ProcessLinqCount;
case "Single":
case "SingleOrDefault":
case "First":
Expand All @@ -506,6 +508,8 @@ private MethodProcessor GetMethodProcessor(MethodInfo method)
return ProcessLinqAggregate;
case "Cast":
return DefaultMethodProcessor;
case "Distinct":
return DistinctMethodProcessor;
default:
return null;
}
Expand All @@ -516,6 +520,14 @@ private MethodProcessor GetMethodProcessor(MethodInfo method)
return DefaultMethodProcessor;
}

private void DistinctMethodProcessor(MethodInfo method, CurrentDependencies current, Expression[] arguments)
{
if (arguments.Any())
throw new NotSupportedException("Distinct method with arguments is not supported");
current.AddDependency(MakeLambda(current.Prefix));
current.ReplaceCurrentWithEach();
}

private void ProcessIndexer(MethodInfo method, CurrentDependencies current, Expression[] arguments)
{
current.Prefix = Expression.Call(current.Prefix, method, arguments);
Expand Down Expand Up @@ -586,6 +598,16 @@ private void ProcessLinqSum(MethodInfo method, CurrentDependencies current, Expr
current.ReplaceCurrentWithEach();
}

private void ProcessLinqCount(MethodInfo method, CurrentDependencies current, Expression[] arguments)
{
var selector = (LambdaExpression)arguments.SingleOrDefault();
if (selector != null)
throw new NotSupportedException("Count method with predicate is not supported");
current.AddDependency(MakeLambda(current.Prefix));

current.ReplaceCurrentWithEach();
}

private void ProcessLinqAggregate(MethodInfo method, CurrentDependencies current, Expression[] arguments)
{
switch (arguments.Length)
Expand Down
4 changes: 2 additions & 2 deletions Mutators/Visitors/LinqEliminator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,7 @@ private Expression ProcessMethodsChain(Action<Context, ParameterExpression, Para
itemGetter = Expression.Call(array, itemProperty.GetGetMethod(), index);
break;
case CollectionKind.Enumerable:
init = Expression.Call(array, typeof(IEnumerator).GetMethod("Reset", BindingFlags.Public | BindingFlags.Instance));
init = null;
var moveNextMethod = typeof(IEnumerator).GetMethod("MoveNext", BindingFlags.Public | BindingFlags.Instance);
condition = Expression.Not(Expression.Call(array, moveNextMethod));
var currentProperty = collection.Type.GetProperty("Current", BindingFlags.Public | BindingFlags.Instance);
Expand Down Expand Up @@ -710,7 +710,7 @@ private static bool IsLinqMethod(MethodInfo method)
return true;
if (method.IsGenericMethod)
method = method.GetGenericMethodDefinition();
return method.DeclaringType == typeof(Enumerable) && method.Name != "ToArray" && method.Name != "ToList" && method.Name != "ToDictionary";
return method.DeclaringType == typeof(Enumerable) && method.Name != "ToArray" && method.Name != "ToList" && method.Name != "ToDictionary" && method.Name != "Distinct";
}

private static CollectionKind GetCollectionKind(Type type)
Expand Down
2 changes: 1 addition & 1 deletion global.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"sdk": {
"version": "5.0.103"
"version": "5.0.202"
}
}
2 changes: 1 addition & 1 deletion version.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"version": "1.3",
"version": "1.4",
"assemblyVersion": {
"precision": "build"
},
Expand Down

0 comments on commit f0423a1

Please sign in to comment.