From c96f4b7b2d6b1329027923265060bdbef7e2ac2e Mon Sep 17 00:00:00 2001 From: Anton Chaplygin Date: Mon, 26 Apr 2021 16:32:18 +0500 Subject: [PATCH 1/6] do not eliminate LINQ distinct method, do not reset enumerator --- Mutators.Tests/ValidatorsTest.cs | 100 ++++++++++++++++++++++++++++ Mutators/Visitors/LinqEliminator.cs | 4 +- 2 files changed, 102 insertions(+), 2 deletions(-) diff --git a/Mutators.Tests/ValidatorsTest.cs b/Mutators.Tests/ValidatorsTest.cs index 9c533e0..4274be0 100644 --- a/Mutators.Tests/ValidatorsTest.cs +++ b/Mutators.Tests/ValidatorsTest.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using System.Linq; @@ -19,6 +20,18 @@ public static void AssertEquivalent(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 { @@ -30,6 +43,85 @@ public void TestProperty() validator(new TestData {A = new A {S = "zzz"}}).AssertEquivalent(new ValidationResultTreeNode {{"S", FormattedValidationResult.Error(null, null, new SimplePathFormatterText {Paths = new[] {"S"}})}}); } + [Test] + public void TestKeyValuePairs() + { + var collection = new TestDataConfiguratorCollection(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 = 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 + { + { + "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() { @@ -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; } @@ -939,6 +1033,12 @@ public class TestData public Dictionary Dict { get; set; } } + public class KeyValuePair + { + public string Key { get; set; } + public string Value { get; set; } + } + public class A { public B[] B { get; set; } diff --git a/Mutators/Visitors/LinqEliminator.cs b/Mutators/Visitors/LinqEliminator.cs index 0e8efa7..cfdad9e 100644 --- a/Mutators/Visitors/LinqEliminator.cs +++ b/Mutators/Visitors/LinqEliminator.cs @@ -276,7 +276,7 @@ private Expression ProcessMethodsChain(Action Date: Mon, 26 Apr 2021 16:34:08 +0500 Subject: [PATCH 2/6] set prerelease version --- version.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.json b/version.json index abe9271..d3531f0 100644 --- a/version.json +++ b/version.json @@ -1,5 +1,5 @@ { - "version": "1.3", + "version": "1.3-pre4", "assemblyVersion": { "precision": "build" }, From 00fff93a6445e9f4fb173469ce8590f081d85803 Mon Sep 17 00:00:00 2001 From: Anton Chaplygin Date: Mon, 26 Apr 2021 16:39:12 +0500 Subject: [PATCH 3/6] change sdk version to compatible with appveyor --- global.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/global.json b/global.json index 8c50959..2d85e85 100644 --- a/global.json +++ b/global.json @@ -1,5 +1,5 @@ { "sdk": { - "version": "5.0.103" + "version": "5.0.202" } } \ No newline at end of file From 3c1f71e5671596e71186b3a448c02687f42f3513 Mon Sep 17 00:00:00 2001 From: Anton Chaplygin Date: Mon, 26 Apr 2021 18:44:47 +0500 Subject: [PATCH 4/6] supported Distinct and Count LINQ methods in DependenciesExtractor --- Mutators.Tests/DependenciesExtractorTest.cs | 21 ++++++++++++++++++++ Mutators/Visitors/DependenciesExtractor.cs | 22 +++++++++++++++++++++ 2 files changed, 43 insertions(+) diff --git a/Mutators.Tests/DependenciesExtractorTest.cs b/Mutators.Tests/DependenciesExtractorTest.cs index b93c335..a8c7dd2 100644 --- a/Mutators.Tests/DependenciesExtractorTest.cs +++ b/Mutators.Tests/DependenciesExtractorTest.cs @@ -474,6 +474,27 @@ public void TestStringLength() DoTest(expression, a => a.S); } + [Test] + public void TestDistinct() + { + Expression> expression = a => a.B.C.Select(x => x.S).Distinct().ToArray(); + DoTest(expression, a => a.B.C.Each().S); + } + + [Test] + public void TestDistinctCount() + { + Expression> expression = a => a.B.C.Select(x => x.S).Distinct().Count(); + DoTest(expression, a => a.B.C.Each().S); + } + + [Test] + public void TestWhereCount() + { + Expression> 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) diff --git a/Mutators/Visitors/DependenciesExtractor.cs b/Mutators/Visitors/DependenciesExtractor.cs index 53cfff0..9622051 100644 --- a/Mutators/Visitors/DependenciesExtractor.cs +++ b/Mutators/Visitors/DependenciesExtractor.cs @@ -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": @@ -506,6 +508,8 @@ private MethodProcessor GetMethodProcessor(MethodInfo method) return ProcessLinqAggregate; case "Cast": return DefaultMethodProcessor; + case "Distinct": + return DistinctMethodProcessor; default: return null; } @@ -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); @@ -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) From 4a22277a13d1f4e9090a47991f3c54f0255f3616 Mon Sep 17 00:00:00 2001 From: Anton Chaplygin Date: Mon, 26 Apr 2021 19:47:40 +0500 Subject: [PATCH 5/6] update version --- version.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.json b/version.json index d3531f0..1954717 100644 --- a/version.json +++ b/version.json @@ -1,5 +1,5 @@ { - "version": "1.3-pre4", + "version": "1.3-pre5", "assemblyVersion": { "precision": "build" }, From b99116d72c8d753b34e3a10061001cc2529047ed Mon Sep 17 00:00:00 2001 From: Anton Chaplygin Date: Wed, 28 Apr 2021 13:30:51 +0500 Subject: [PATCH 6/6] set release version, update changelog --- CHANGELOG.md | 3 +++ version.json | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2c386d2..5e3d21c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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. diff --git a/version.json b/version.json index 1954717..7f9bb75 100644 --- a/version.json +++ b/version.json @@ -1,5 +1,5 @@ { - "version": "1.3-pre5", + "version": "1.4", "assemblyVersion": { "precision": "build" },