From 3bc9aded97979ca54d3542ddadb0eaf94145e5aa Mon Sep 17 00:00:00 2001 From: Liam Rougoor Date: Fri, 30 Aug 2024 11:35:21 +0200 Subject: [PATCH 1/8] feat(mutators): Add default parameter mutator --- .../Compiling/CSharpRollbackProcessTests.cs | 4 +- ...ts.cs => CSharpMutantOrchestratorTests.cs} | 6 +- .../Mutants/MutantOrchestratorTestsBase.cs | 4 +- .../Mutants/MutantPlacerTests.cs | 2 +- .../Mutators/DefaultParameterMutatorTests.cs | 153 ++++++++++++++++++ .../Inputs/IgnoreMutationsInputTests.cs | 2 +- ...strator.cs => CSharpMutantOrchestrator.cs} | 15 +- .../Mutants/ICSharpMutantOrchestrator.cs | 9 ++ .../Stryker.Core/Mutants/MutationContext.cs | 4 +- .../MutationTest/CsharpMutationProcess.cs | 2 +- .../Mutators/DefaultParameterMutator.cs | 94 +++++++++++ .../Stryker.Core/Mutators/Mutator.cs | 4 +- 12 files changed, 278 insertions(+), 21 deletions(-) rename src/Stryker.Core/Stryker.Core.UnitTest/Mutants/{CsharpMutantOrchestratorTests.cs => CSharpMutantOrchestratorTests.cs} (99%) create mode 100644 src/Stryker.Core/Stryker.Core.UnitTest/Mutators/DefaultParameterMutatorTests.cs rename src/Stryker.Core/Stryker.Core/Mutants/{CsharpMutantOrchestrator.cs => CSharpMutantOrchestrator.cs} (95%) create mode 100644 src/Stryker.Core/Stryker.Core/Mutants/ICSharpMutantOrchestrator.cs create mode 100644 src/Stryker.Core/Stryker.Core/Mutators/DefaultParameterMutator.cs diff --git a/src/Stryker.Core/Stryker.Core.UnitTest/Compiling/CSharpRollbackProcessTests.cs b/src/Stryker.Core/Stryker.Core.UnitTest/Compiling/CSharpRollbackProcessTests.cs index 1a052cc45d..12281a3ee1 100644 --- a/src/Stryker.Core/Stryker.Core.UnitTest/Compiling/CSharpRollbackProcessTests.cs +++ b/src/Stryker.Core/Stryker.Core.UnitTest/Compiling/CSharpRollbackProcessTests.cs @@ -113,7 +113,7 @@ public void SomeLinq() }; var codeInjection = new CodeInjection(); var placer = new MutantPlacer(codeInjection); - var mutator = new CsharpMutantOrchestrator( placer, options: options); + var mutator = new CSharpMutantOrchestrator( placer, options: options); var helpers = new List(); foreach (var (name, code) in codeInjection.MutantHelpers) { @@ -197,7 +197,7 @@ private void RefreshAccountNumber() }; var codeInjection = new CodeInjection(); var placer = new MutantPlacer(codeInjection); - var mutator = new CsharpMutantOrchestrator( placer, options: options); + var mutator = new CSharpMutantOrchestrator( placer, options: options); var helpers = new List(); foreach (var (name, code) in codeInjection.MutantHelpers) { diff --git a/src/Stryker.Core/Stryker.Core.UnitTest/Mutants/CsharpMutantOrchestratorTests.cs b/src/Stryker.Core/Stryker.Core.UnitTest/Mutants/CSharpMutantOrchestratorTests.cs similarity index 99% rename from src/Stryker.Core/Stryker.Core.UnitTest/Mutants/CsharpMutantOrchestratorTests.cs rename to src/Stryker.Core/Stryker.Core.UnitTest/Mutants/CSharpMutantOrchestratorTests.cs index f017051f45..32c42a8965 100644 --- a/src/Stryker.Core/Stryker.Core.UnitTest/Mutants/CsharpMutantOrchestratorTests.cs +++ b/src/Stryker.Core/Stryker.Core.UnitTest/Mutants/CSharpMutantOrchestratorTests.cs @@ -10,7 +10,7 @@ namespace Stryker.Core.UnitTest.Mutants; [TestClass] -public class CsharpMutantOrchestratorTests : MutantOrchestratorTestsBase +public class CSharpMutantOrchestratorTests : MutantOrchestratorTestsBase { [TestMethod] public void ShouldNotMutateEmptyInterfaces() @@ -86,7 +86,7 @@ public void ShouldMutateBlockStatements() MutationLevel = MutationLevel.Complete, OptimizationMode = OptimizationModes.CoverageBasedTest, }; - _target = new CsharpMutantOrchestrator(new MutantPlacer(_injector), options: options); + _target = new CSharpMutantOrchestrator(new MutantPlacer(_injector), options: options); string source = @"private void Move() { @@ -1817,7 +1817,7 @@ public void ShouldMutateChainedStringMethods() public void ShouldIncrementMutantCountUniquely() { var secondOrchestrator = - new CsharpMutantOrchestrator(new MutantPlacer(_injector), options: new StrykerOptions()); + new CSharpMutantOrchestrator(new MutantPlacer(_injector), options: new StrykerOptions()); var node = SyntaxFactory.ParseExpression("1 == 1") as BinaryExpressionSyntax; var firstMutant = _target diff --git a/src/Stryker.Core/Stryker.Core.UnitTest/Mutants/MutantOrchestratorTestsBase.cs b/src/Stryker.Core/Stryker.Core.UnitTest/Mutants/MutantOrchestratorTestsBase.cs index d1bca26df7..be30d2c0e2 100644 --- a/src/Stryker.Core/Stryker.Core.UnitTest/Mutants/MutantOrchestratorTestsBase.cs +++ b/src/Stryker.Core/Stryker.Core.UnitTest/Mutants/MutantOrchestratorTestsBase.cs @@ -16,7 +16,7 @@ public partial class MutantOrchestratorTestsBase : TestBase private static partial Regex IsActiveRegex(); private const string Replacement = "IsActive(0)"; - protected CsharpMutantOrchestrator _target; + protected CSharpMutantOrchestrator _target; protected CodeInjection _injector = new(); public MutantOrchestratorTestsBase() @@ -26,7 +26,7 @@ public MutantOrchestratorTestsBase() MutationLevel = MutationLevel.Complete, OptimizationMode = OptimizationModes.CoverageBasedTest, }; - _target = new CsharpMutantOrchestrator(new MutantPlacer(_injector), options: options); + _target = new CSharpMutantOrchestrator(new MutantPlacer(_injector), options: options); } protected void ShouldMutateSourceToExpected(string actual, string expected) diff --git a/src/Stryker.Core/Stryker.Core.UnitTest/Mutants/MutantPlacerTests.cs b/src/Stryker.Core/Stryker.Core.UnitTest/Mutants/MutantPlacerTests.cs index c9b4caaf10..fffd62426b 100644 --- a/src/Stryker.Core/Stryker.Core.UnitTest/Mutants/MutantPlacerTests.cs +++ b/src/Stryker.Core/Stryker.Core.UnitTest/Mutants/MutantPlacerTests.cs @@ -211,7 +211,7 @@ public void ShouldRollBackFailedConstructor() var source = @"class Test { static TestClass()=> Value-='a';}"; - var orchestrator = new CsharpMutantOrchestrator(placer, options: new StrykerOptions + var orchestrator = new CSharpMutantOrchestrator(placer, options: new StrykerOptions { OptimizationMode = OptimizationModes.CoverageBasedTest, MutationLevel = MutationLevel.Complete diff --git a/src/Stryker.Core/Stryker.Core.UnitTest/Mutators/DefaultParameterMutatorTests.cs b/src/Stryker.Core/Stryker.Core.UnitTest/Mutators/DefaultParameterMutatorTests.cs new file mode 100644 index 0000000000..659bfca209 --- /dev/null +++ b/src/Stryker.Core/Stryker.Core.UnitTest/Mutators/DefaultParameterMutatorTests.cs @@ -0,0 +1,153 @@ +using System.Collections.Generic; +using System.Linq; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; +using Shouldly; +using Stryker.Core.Mutants; +using Stryker.Core.Mutators; +using Stryker.Core.Options; + +namespace Stryker.Core.UnitTest.Mutators; + +[TestClass] +public class DefaultParameterMutatorTests +{ + [TestMethod] + public void ShouldBeMutationLevelComplete() + { + var options = new StrykerOptions(); + var orchestratorMock = new Mock(); + var target = new DefaultParameterMutator(orchestratorMock.Object, options); + target.MutationLevel.ShouldBe(MutationLevel.Complete); + } + + [TestMethod] + public void ShouldMutateDefaultParameter() + { + var source = @" +using System; + +class Program +{ + void Method() + { + DefaultParameterMethod(); + } + + void DefaultParameterMethod(string parameter = ""default"") + { + Console.WriteLine(parameter); + } +}""; +"; + var syntaxTree = CSharpSyntaxTree.ParseText(source); + GetMutations(syntaxTree).ShouldHaveSingleItem(); + } + + [TestMethod] + public void ShouldNotMutateExplicitlyUsedDefaultParameter() + { + var source = @" +using System; + +class Program +{ + void Method() + { + DefaultParameterMethod(""hello world""); + } + + void DefaultParameterMethod(string parameter = ""default"") + { + Console.WriteLine(parameter); + } +}""; +"; + var syntaxTree = CSharpSyntaxTree.ParseText(source); + GetMutations(syntaxTree).ShouldBeEmpty(); + } + + [TestMethod] + public void ShouldNotMutateExplicitlyUsedNamedDefaultParameter() + { + var source = @" +using System; + +class Program +{ + void Method() + { + DefaultParameterMethod(parameter2: ""hello world""); + } + + void DefaultParameterMethod(string parameter1 = ""default1"", string parameter2 = ""default2"", string parameter3 = ""default3"") + { + Console.WriteLine(parameter); + } +}""; +"; + var syntaxTree = CSharpSyntaxTree.ParseText(source); + var mutations = GetMutations(syntaxTree); + + mutations.Count().ShouldBe(2); + mutations.ShouldNotContain(x => x.DisplayName.Contains("parameter2")); + } + + [TestMethod] + public void ShouldMutateImplicitlyUsedDefaultParameter() + { + var source = @" +using System; + +class Program +{ + void Method() + { + DefaultParameterMethod(""Hello world""); + } + + void DefaultParameterMethod(string parameter1 = ""default"", string parameter2 = ""default2"", string parameter3 = ""default3"") + { + Console.WriteLine(parameter); + } +}""; +"; + var syntaxTree = CSharpSyntaxTree.ParseText(source); + + var mutations = GetMutations(syntaxTree); + + mutations.Count().ShouldBe(2); + mutations.ShouldNotContain(x => x.DisplayName.Contains("parameter1")); + } + + private IEnumerable GetMutations(SyntaxTree tree) + { + var invocations = tree + .GetRoot() + .DescendantNodes() + .OfType(); + var options = new StrykerOptions() + { + MutationLevel = MutationLevel.Complete + }; + var orchestratorMock = new Mock(); + orchestratorMock.SetupGet(m => m.Mutators).Returns(new List { new StringMutator() }); + + var target = new DefaultParameterMutator(orchestratorMock.Object, options); + + var semanticModel = GetSemanticModel(tree); + + return invocations.SelectMany(invocation => target.ApplyMutations(invocation, semanticModel)); + } + + private static SemanticModel GetSemanticModel(SyntaxTree tree) + { + var compilation = CSharpCompilation.Create("Test") + .AddReferences(MetadataReference.CreateFromFile(typeof(object).Assembly.Location)) + .AddSyntaxTrees(tree); + return compilation.GetSemanticModel(tree); + } +} diff --git a/src/Stryker.Core/Stryker.Core.UnitTest/Options/Inputs/IgnoreMutationsInputTests.cs b/src/Stryker.Core/Stryker.Core.UnitTest/Options/Inputs/IgnoreMutationsInputTests.cs index fde501fe9e..9f08779337 100644 --- a/src/Stryker.Core/Stryker.Core.UnitTest/Options/Inputs/IgnoreMutationsInputTests.cs +++ b/src/Stryker.Core/Stryker.Core.UnitTest/Options/Inputs/IgnoreMutationsInputTests.cs @@ -28,7 +28,7 @@ public void ShouldValidateExcludedMutation() var ex = Should.Throw(() => target.Validate()); - ex.Message.ShouldBe($"Invalid excluded mutation (gibberish). The excluded mutations options are [Statement, Arithmetic, Block, Equality, Boolean, Logical, Assignment, Unary, Update, Checked, Linq, String, Bitwise, Initializer, Regex, NullCoalescing, Math, StringMethod, Conditional]"); + ex.Message.ShouldBe($"Invalid excluded mutation (gibberish). The excluded mutations options are [Statement, Arithmetic, Block, Equality, Boolean, Logical, Assignment, Unary, Update, Checked, Linq, String, Bitwise, Initializer, Regex, NullCoalescing, Math, StringMethod, Conditional, DefaultParameter]"); } [TestMethod] diff --git a/src/Stryker.Core/Stryker.Core/Mutants/CsharpMutantOrchestrator.cs b/src/Stryker.Core/Stryker.Core/Mutants/CSharpMutantOrchestrator.cs similarity index 95% rename from src/Stryker.Core/Stryker.Core/Mutants/CsharpMutantOrchestrator.cs rename to src/Stryker.Core/Stryker.Core/Mutants/CSharpMutantOrchestrator.cs index 58e4225a90..2d9b263886 100644 --- a/src/Stryker.Core/Stryker.Core/Mutants/CsharpMutantOrchestrator.cs +++ b/src/Stryker.Core/Stryker.Core/Mutants/CSharpMutantOrchestrator.cs @@ -1,7 +1,6 @@ using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; -using System.Threading; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; @@ -14,27 +13,29 @@ namespace Stryker.Core.Mutants; -/// -public class CsharpMutantOrchestrator : BaseMutantOrchestrator +/// +public class CSharpMutantOrchestrator : BaseMutantOrchestrator, ICSharpMutantOrchestrator { private static readonly TypeBasedStrategy specificOrchestrator = new(); private ILogger Logger { get; } - static CsharpMutantOrchestrator() => + public IEnumerable Mutators { get; } + + static CSharpMutantOrchestrator() => // declare node specific orchestrators. Note that order is relevant, they should be declared from more specific to more generic one specificOrchestrator.RegisterHandlers(BuildOrchestratorList()); /// /// The mutators that should be active during the mutation process /// - public CsharpMutantOrchestrator(MutantPlacer placer, IEnumerable mutators = null, StrykerOptions options = null) : base(options) + public CSharpMutantOrchestrator(MutantPlacer placer, IEnumerable mutators = null, StrykerOptions options = null) : base(options) { Placer = placer; Mutators = mutators ?? DefaultMutatorList(); Mutants = new Collection(); - Logger = ApplicationLogging.LoggerFactory.CreateLogger(); + Logger = ApplicationLogging.LoggerFactory.CreateLogger(); } @@ -115,8 +116,6 @@ private static List DefaultMutatorList() => new StringMethodMutator() ]; - private IEnumerable Mutators { get; } - public MutantPlacer Placer { get; } /// diff --git a/src/Stryker.Core/Stryker.Core/Mutants/ICSharpMutantOrchestrator.cs b/src/Stryker.Core/Stryker.Core/Mutants/ICSharpMutantOrchestrator.cs new file mode 100644 index 0000000000..a9b4524e88 --- /dev/null +++ b/src/Stryker.Core/Stryker.Core/Mutants/ICSharpMutantOrchestrator.cs @@ -0,0 +1,9 @@ +using System.Collections.Generic; +using Stryker.Core.Mutators; + +namespace Stryker.Core.Mutants; + +public interface ICSharpMutantOrchestrator +{ + IEnumerable Mutators { get; } +} diff --git a/src/Stryker.Core/Stryker.Core/Mutants/MutationContext.cs b/src/Stryker.Core/Stryker.Core/Mutants/MutationContext.cs index 913b61e0ab..1ce9801b4e 100644 --- a/src/Stryker.Core/Stryker.Core/Mutants/MutationContext.cs +++ b/src/Stryker.Core/Stryker.Core/Mutants/MutationContext.cs @@ -18,7 +18,7 @@ internal class MutationContext { // main orchestrator // the orchestrator is used to perform actual mutation injections - private readonly CsharpMutantOrchestrator _mainOrchestrator; + private readonly CSharpMutantOrchestrator _mainOrchestrator; // pending mutation stacks. An entry is pushed in the stack when entering a member or function and popping it when leaving private readonly MutationStore _mutation; @@ -26,7 +26,7 @@ internal class MutationContext /// Mutation context must be created once when starting a mutation process. /// /// - public MutationContext(CsharpMutantOrchestrator mutantOrchestrator) + public MutationContext(CSharpMutantOrchestrator mutantOrchestrator) { _mainOrchestrator = mutantOrchestrator; _mutation = new MutationStore(mutantOrchestrator.Placer); diff --git a/src/Stryker.Core/Stryker.Core/MutationTest/CsharpMutationProcess.cs b/src/Stryker.Core/Stryker.Core/MutationTest/CsharpMutationProcess.cs index 1d1bf1645a..9bee3e53f4 100644 --- a/src/Stryker.Core/Stryker.Core/MutationTest/CsharpMutationProcess.cs +++ b/src/Stryker.Core/Stryker.Core/MutationTest/CsharpMutationProcess.cs @@ -54,7 +54,7 @@ public CsharpMutationProcess(StrykerOptions options) : this(null, options) public void Mutate(MutationTestInput input) { var projectInfo = input.SourceProjectInfo.ProjectContents; - var orchestrator = _orchestrator ?? new CsharpMutantOrchestrator(new MutantPlacer(input.SourceProjectInfo.CodeInjector), options: _options); + var orchestrator = _orchestrator ?? new CSharpMutantOrchestrator(new MutantPlacer(input.SourceProjectInfo.CodeInjector), options: _options); var compilingProcess = new CsharpCompilingProcess(input, options: _options); var semanticModels = compilingProcess.GetSemanticModels(projectInfo.GetAllFiles().Cast().Select(x => x.SyntaxTree)); diff --git a/src/Stryker.Core/Stryker.Core/Mutators/DefaultParameterMutator.cs b/src/Stryker.Core/Stryker.Core/Mutators/DefaultParameterMutator.cs new file mode 100644 index 0000000000..0438d147dd --- /dev/null +++ b/src/Stryker.Core/Stryker.Core/Mutators/DefaultParameterMutator.cs @@ -0,0 +1,94 @@ +using System.Collections.Generic; +using System.Linq; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Stryker.Core.Mutants; +using Stryker.Core.Options; + +namespace Stryker.Core.Mutators; + +public class DefaultParameterMutator : MutatorBase +{ + private readonly ICSharpMutantOrchestrator _orchestrator; + private readonly StrykerOptions _options; + + public override MutationLevel MutationLevel => MutationLevel.Complete; + + public DefaultParameterMutator(ICSharpMutantOrchestrator orchestrator, StrykerOptions options) + { + _orchestrator = orchestrator; + _options = options; + } + + public override IEnumerable ApplyMutations(InvocationExpressionSyntax node, SemanticModel semanticModel) + { + var methodSymbol = (IMethodSymbol)semanticModel.GetSymbolInfo(node).Symbol; + if (methodSymbol is null) + { + yield break; + } + var parameterSymbols = methodSymbol.Parameters; + + var parametersWithDefaultValues = parameterSymbols.Where(p => p.HasExplicitDefaultValue); + + var overridenDefaultParameters = new List(); + foreach (var argument in node.ArgumentList.Arguments) + { + if (argument.NameColon is not null) + { + overridenDefaultParameters.AddRange(parametersWithDefaultValues.Where(p => p.Name == argument.NameColon.Name.Identifier.ValueText)); + } + else + { + overridenDefaultParameters.AddRange(parametersWithDefaultValues.Where(p => p.Ordinal == node.ArgumentList.Arguments.IndexOf(argument))); + } + } + + var unoverridenDefaultParameters = parametersWithDefaultValues.Except(overridenDefaultParameters); + + foreach (var parameter in unoverridenDefaultParameters) + { + var parameterName = parameter.Name; + var argumentNameColon = SyntaxFactory.NameColon(parameterName); + var parameterSyntaxNode = (ParameterSyntax)parameter.DeclaringSyntaxReferences[0].GetSyntax(); + + var parameterDefaultExpressionNode = parameterSyntaxNode.Default!.Value; + var mutatedDefaultValues = MutateDefaultValueNode(parameterDefaultExpressionNode, semanticModel); + + foreach (var mutatedDefaultValue in mutatedDefaultValues) + { + var namedArgument = SyntaxFactory.Argument(nameColon: argumentNameColon, expression: mutatedDefaultValue, refKindKeyword: default); + + var arguments = node.ArgumentList.Arguments.Add(namedArgument); + var newArgumentList = SyntaxFactory.ArgumentList(arguments); + + yield return new Mutation + { + OriginalNode = node, + ReplacementNode = node.WithArgumentList(newArgumentList), + DisplayName = $"Default parameter mutation: {parameterName}", + Type = Mutator.DefaultParameter + }; + } + } + } + + private IEnumerable MutateDefaultValueNode(SyntaxNode defaultValueNode, SemanticModel semanticModel) + { + List nodeMutations = new(); + foreach (var mutator in _orchestrator.Mutators) + { + if (mutator is not DefaultParameterMutator) + { + var mutations = mutator.Mutate(defaultValueNode, semanticModel, _options); + foreach (var mutation in mutations) + { + nodeMutations.Add((ExpressionSyntax)mutation.ReplacementNode); + } + } + } + + return nodeMutations; + } +} diff --git a/src/Stryker.Core/Stryker.Core/Mutators/Mutator.cs b/src/Stryker.Core/Stryker.Core/Mutators/Mutator.cs index 2a9005ffcc..8642b36da1 100644 --- a/src/Stryker.Core/Stryker.Core/Mutators/Mutator.cs +++ b/src/Stryker.Core/Stryker.Core/Mutators/Mutator.cs @@ -46,7 +46,9 @@ public enum Mutator [MutatorDescription("String Method")] StringMethod, [MutatorDescription("Conditional operators")] - Conditional + Conditional, + [MutatorDescription("Default parameter")] + DefaultParameter } public static class EnumExtension From 75b8b647ba6bae06316de6c0fd94d8198795d572 Mon Sep 17 00:00:00 2001 From: Liam Rougoor Date: Fri, 30 Aug 2024 13:02:32 +0200 Subject: [PATCH 2/8] Add DefaultParameter construct --- .../TargetProject/Constructs/Csharp4.cs | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/integrationtest/TargetProjects/NetCore/TargetProject/Constructs/Csharp4.cs b/integrationtest/TargetProjects/NetCore/TargetProject/Constructs/Csharp4.cs index 180e14b7d2..f13bb88b8e 100644 --- a/integrationtest/TargetProjects/NetCore/TargetProject/Constructs/Csharp4.cs +++ b/integrationtest/TargetProjects/NetCore/TargetProject/Constructs/Csharp4.cs @@ -8,4 +8,27 @@ public static void NamedParameters() Console.WriteLine(format: "{0:f}", arg0: 6.02214179e23); Console.WriteLine(arg0: 6.02214179e23, format: "{0:f}"); } + + // default parameters + public static void DefaultParameters() + { + ExampleDefaultParameterMethod("Hello"); + ExampleDefaultParameterMethod("Hello", "World"); + ExampleDefaultParameterMethod("Hello", "World", "Foo"); + ExampleDefaultParameterMethod("Hello", "World", "Foo", "Bar"); + } + + // default named parameters + public static void DefaultNamedParameters() + { + ExampleDefaultParameterMethod(required1: "Hello"); + ExampleDefaultParameterMethod(optional3: "Hello", required1: "World"); + ExampleDefaultParameterMethod(optional2: "Hello", required1: "World", optional1: "Foo"); + ExampleDefaultParameterMethod(optional3: "Hello", optional2: "World", required1: "Foo", optional1: "Bar"); + } + + private static void ExampleDefaultParameterMethod(string required1, string optional1 = "optional1", + string optional2 = "optional2", string optional3 = "optional3") + { + } } From 352e2786076355749202654793a4509ad9955543 Mon Sep 17 00:00:00 2001 From: Liam Rougoor Date: Fri, 30 Aug 2024 13:02:54 +0200 Subject: [PATCH 3/8] Fix quality gate --- .../Stryker.Core/Mutants/CSharpMutantOrchestrator.cs | 2 +- .../Stryker.Core/Mutants/MutationContext.cs | 4 ++-- .../Stryker.Core/Mutators/DefaultParameterMutator.cs | 11 ++++------- 3 files changed, 7 insertions(+), 10 deletions(-) diff --git a/src/Stryker.Core/Stryker.Core/Mutants/CSharpMutantOrchestrator.cs b/src/Stryker.Core/Stryker.Core/Mutants/CSharpMutantOrchestrator.cs index 2d9b263886..9278e44f53 100644 --- a/src/Stryker.Core/Stryker.Core/Mutants/CSharpMutantOrchestrator.cs +++ b/src/Stryker.Core/Stryker.Core/Mutants/CSharpMutantOrchestrator.cs @@ -128,7 +128,7 @@ public override SyntaxTree Mutate(SyntaxTree input, SemanticModel semanticModel) // search for node specific handler input.WithRootAndOptions(GetHandler(input.GetRoot()).Mutate(input.GetRoot(), semanticModel, new MutationContext(this)), input.Options); - internal INodeOrchestrator GetHandler(SyntaxNode currentNode) => specificOrchestrator.FindHandler(currentNode); + internal static INodeOrchestrator GetHandler(SyntaxNode currentNode) => specificOrchestrator.FindHandler(currentNode); internal IEnumerable GenerateMutationsForNode(SyntaxNode current, SemanticModel semanticModel, MutationContext context) { diff --git a/src/Stryker.Core/Stryker.Core/Mutants/MutationContext.cs b/src/Stryker.Core/Stryker.Core/Mutants/MutationContext.cs index 1ce9801b4e..e6f627e0a7 100644 --- a/src/Stryker.Core/Stryker.Core/Mutants/MutationContext.cs +++ b/src/Stryker.Core/Stryker.Core/Mutants/MutationContext.cs @@ -10,7 +10,7 @@ namespace Stryker.Core.Mutants; /// /// Describe the (syntax tree) context during mutation and ensure proper mutation injection. /// It has several responsibilities: -/// 1) It is in charge of storing mutations as they are generated, inject them at the appropriate syntax level. +/// 1) It is in charge of storing mutations as they are generated, inject them at the appropriate syntax level. /// 2) it also tracks mutator disabled via comments and restores them at adequate times /// /// @@ -80,7 +80,7 @@ public IEnumerable GenerateMutantsForNode(SyntaxNode node, SemanticModel /// handler for which to find an orchestrator. /// /// A handler for this node. - public SyntaxNode Mutate(SyntaxNode node, SemanticModel model) => _mainOrchestrator.GetHandler(node).Mutate(node, model, this); + public SyntaxNode Mutate(SyntaxNode node, SemanticModel model) => CSharpMutantOrchestrator.GetHandler(node).Mutate(node, model, this); /// /// Call this to signal mutation occurs in static method or fields diff --git a/src/Stryker.Core/Stryker.Core/Mutators/DefaultParameterMutator.cs b/src/Stryker.Core/Stryker.Core/Mutators/DefaultParameterMutator.cs index 0438d147dd..4234372805 100644 --- a/src/Stryker.Core/Stryker.Core/Mutators/DefaultParameterMutator.cs +++ b/src/Stryker.Core/Stryker.Core/Mutators/DefaultParameterMutator.cs @@ -77,15 +77,12 @@ public override IEnumerable ApplyMutations(InvocationExpressionSyntax private IEnumerable MutateDefaultValueNode(SyntaxNode defaultValueNode, SemanticModel semanticModel) { List nodeMutations = new(); - foreach (var mutator in _orchestrator.Mutators) + foreach (var mutator in _orchestrator.Mutators.Where(m => m is not DefaultParameterMutator)) { - if (mutator is not DefaultParameterMutator) + var mutations = mutator.Mutate(defaultValueNode, semanticModel, _options); + foreach (var mutation in mutations) { - var mutations = mutator.Mutate(defaultValueNode, semanticModel, _options); - foreach (var mutation in mutations) - { - nodeMutations.Add((ExpressionSyntax)mutation.ReplacementNode); - } + nodeMutations.Add((ExpressionSyntax)mutation.ReplacementNode); } } From 75fcdc2255e9c6935efe92607341a862bf5def47 Mon Sep 17 00:00:00 2001 From: Liam Rougoor Date: Fri, 30 Aug 2024 13:39:55 +0200 Subject: [PATCH 4/8] Run integration test at mutation level Complete --- pipeline-templates/run-integration-test-steps.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pipeline-templates/run-integration-test-steps.yml b/pipeline-templates/run-integration-test-steps.yml index b02f7d2f28..2953c56749 100644 --- a/pipeline-templates/run-integration-test-steps.yml +++ b/pipeline-templates/run-integration-test-steps.yml @@ -4,7 +4,7 @@ parameters: strykerCommands: '' steps: -- script: $(Agent.BuildDirectory)/tools/dotnet-stryker ${{ parameters.strykerCommands }} --dev-mode -L +- script: $(Agent.BuildDirectory)/tools/dotnet-stryker ${{ parameters.strykerCommands }} --dev-mode -L --mutation-level "Complete" workingDirectory: ${{ parameters.workingDirectory }} displayName: 'Run integration test ${{ parameters.testName }}' failOnStderr: false From 708da63f4daf4d463c0aee6f1365af87c457ce49 Mon Sep 17 00:00:00 2001 From: Liam Rougoor Date: Fri, 30 Aug 2024 14:12:28 +0200 Subject: [PATCH 5/8] Undo mutation-level change in pipeline --- pipeline-templates/run-integration-test-steps.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pipeline-templates/run-integration-test-steps.yml b/pipeline-templates/run-integration-test-steps.yml index 2953c56749..b02f7d2f28 100644 --- a/pipeline-templates/run-integration-test-steps.yml +++ b/pipeline-templates/run-integration-test-steps.yml @@ -4,7 +4,7 @@ parameters: strykerCommands: '' steps: -- script: $(Agent.BuildDirectory)/tools/dotnet-stryker ${{ parameters.strykerCommands }} --dev-mode -L --mutation-level "Complete" +- script: $(Agent.BuildDirectory)/tools/dotnet-stryker ${{ parameters.strykerCommands }} --dev-mode -L workingDirectory: ${{ parameters.workingDirectory }} displayName: 'Run integration test ${{ parameters.testName }}' failOnStderr: false From da3f5b649c5bb60f3c615bf78cce3efdfd40c6b2 Mon Sep 17 00:00:00 2001 From: Liam Rougoor Date: Fri, 30 Aug 2024 14:12:42 +0200 Subject: [PATCH 6/8] Add mutator to orchestrator --- .../Stryker.Core/Mutants/CSharpMutantOrchestrator.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Stryker.Core/Stryker.Core/Mutants/CSharpMutantOrchestrator.cs b/src/Stryker.Core/Stryker.Core/Mutants/CSharpMutantOrchestrator.cs index 9278e44f53..6cb9d94bb5 100644 --- a/src/Stryker.Core/Stryker.Core/Mutants/CSharpMutantOrchestrator.cs +++ b/src/Stryker.Core/Stryker.Core/Mutants/CSharpMutantOrchestrator.cs @@ -89,11 +89,12 @@ private static List BuildOrchestratorList() => new SyntaxNodeOrchestrator() ]; - private static List DefaultMutatorList() => + private List DefaultMutatorList() => [ new BinaryExpressionMutator(), new BlockMutator(), new BooleanMutator(), + new DefaultParameterMutator(this, Options), new ConditionalExpressionMutator(), new AssignmentExpressionMutator(), new PrefixUnaryMutator(), From bd1d7500c42cb244a8d6aceabcbff1477bcd9293 Mon Sep 17 00:00:00 2001 From: Liam Rougoor Date: Fri, 30 Aug 2024 15:24:20 +0200 Subject: [PATCH 7/8] Fix empty declaring syntax issue --- .../Stryker.Core/Mutators/DefaultParameterMutator.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/Stryker.Core/Stryker.Core/Mutators/DefaultParameterMutator.cs b/src/Stryker.Core/Stryker.Core/Mutators/DefaultParameterMutator.cs index 4234372805..69c93b6cb7 100644 --- a/src/Stryker.Core/Stryker.Core/Mutators/DefaultParameterMutator.cs +++ b/src/Stryker.Core/Stryker.Core/Mutators/DefaultParameterMutator.cs @@ -51,6 +51,13 @@ public override IEnumerable ApplyMutations(InvocationExpressionSyntax { var parameterName = parameter.Name; var argumentNameColon = SyntaxFactory.NameColon(parameterName); + + // If we cannot find the parameter declaration, we should not try to mutate. + if (parameter.DeclaringSyntaxReferences.IsDefaultOrEmpty) + { + yield break; + } + var parameterSyntaxNode = (ParameterSyntax)parameter.DeclaringSyntaxReferences[0].GetSyntax(); var parameterDefaultExpressionNode = parameterSyntaxNode.Default!.Value; From a122d469b10ed835d5fbf354614061146427ddef Mon Sep 17 00:00:00 2001 From: Liam Rougoor Date: Fri, 25 Oct 2024 09:48:46 +0200 Subject: [PATCH 8/8] Fix merge with master --- .../Mutators/DefaultParameterMutatorTests.cs | 4 +++- .../Stryker.Core/Mutants/ICSharpMutantOrchestrator.cs | 2 +- .../Stryker.Core/Mutators/DefaultParameterMutator.cs | 8 +++++--- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/Stryker.Core/Stryker.Core.UnitTest/Mutators/DefaultParameterMutatorTests.cs b/src/Stryker.Core/Stryker.Core.UnitTest/Mutators/DefaultParameterMutatorTests.cs index 659bfca209..3d6363a31b 100644 --- a/src/Stryker.Core/Stryker.Core.UnitTest/Mutators/DefaultParameterMutatorTests.cs +++ b/src/Stryker.Core/Stryker.Core.UnitTest/Mutators/DefaultParameterMutatorTests.cs @@ -6,9 +6,11 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using Moq; using Shouldly; +using Stryker.Abstractions; +using Stryker.Abstractions.Mutants; +using Stryker.Abstractions.Mutators; using Stryker.Core.Mutants; using Stryker.Core.Mutators; -using Stryker.Core.Options; namespace Stryker.Core.UnitTest.Mutators; diff --git a/src/Stryker.Core/Stryker.Core/Mutants/ICSharpMutantOrchestrator.cs b/src/Stryker.Core/Stryker.Core/Mutants/ICSharpMutantOrchestrator.cs index a9b4524e88..9b12162f86 100644 --- a/src/Stryker.Core/Stryker.Core/Mutants/ICSharpMutantOrchestrator.cs +++ b/src/Stryker.Core/Stryker.Core/Mutants/ICSharpMutantOrchestrator.cs @@ -1,5 +1,5 @@ using System.Collections.Generic; -using Stryker.Core.Mutators; +using Stryker.Abstractions.Mutators; namespace Stryker.Core.Mutants; diff --git a/src/Stryker.Core/Stryker.Core/Mutators/DefaultParameterMutator.cs b/src/Stryker.Core/Stryker.Core/Mutators/DefaultParameterMutator.cs index 69c93b6cb7..26a1090c46 100644 --- a/src/Stryker.Core/Stryker.Core/Mutators/DefaultParameterMutator.cs +++ b/src/Stryker.Core/Stryker.Core/Mutators/DefaultParameterMutator.cs @@ -3,19 +3,21 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; +using Stryker.Abstractions.Mutants; +using Stryker.Abstractions.Mutators; +using Stryker.Abstractions.Options; using Stryker.Core.Mutants; -using Stryker.Core.Options; namespace Stryker.Core.Mutators; public class DefaultParameterMutator : MutatorBase { private readonly ICSharpMutantOrchestrator _orchestrator; - private readonly StrykerOptions _options; + private readonly IStrykerOptions _options; public override MutationLevel MutationLevel => MutationLevel.Complete; - public DefaultParameterMutator(ICSharpMutantOrchestrator orchestrator, StrykerOptions options) + public DefaultParameterMutator(ICSharpMutantOrchestrator orchestrator, IStrykerOptions options) { _orchestrator = orchestrator; _options = options;