Skip to content
Draft
2 changes: 2 additions & 0 deletions docs/mutations.md
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,8 @@ Do you have a suggestion for a (new) mutator? Feel free to create an [issue](htt
| ------------- | ------------- |
| `"foo"` | `""` |
| `""` | `"Stryker was here!"` |
| `"foo"u8` | `""u8` |
| `""u8` | `"Stryker was here!"u8` |
| `$"foo {bar}"` | `$""` |
| `@"foo"` | `@""` |
| `string.Empty` | `"Stryker was here!"` |
Expand Down
3 changes: 2 additions & 1 deletion docs/regex-mutations.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ custom_edit_url: https://github.com/stryker-mutator/stryker-net/edit/master/docs
---

Stryker supports a variety of regular expression mutators, which are listed below. Do you have a suggestion for a (new) mutator? Feel free to create an [issue](https://github.com/stryker-mutator/stryker-net/issues)!
Regex mutations are applied to arguments, fields, and properties that are annotated with `[StringSyntax(StringSyntaxAttribute.Regex)]` on NET7+, and just the Regex constructor on prior targets.

## Common tokens
| Original | Mutated |
Expand Down Expand Up @@ -160,4 +161,4 @@ Change a normal group to a non-capturing group.

| Original | Mutated |
|----------|-----------|
| `(abc)` | `(?:abc)` |
| `(abc)` | `(?:abc)` |
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ public async Task CSharp_NetCore_SingleTestProject()

var report = await JsonReportSerialization.DeserializeJsonReportAsync(strykerRunOutput);

CheckReportMutants(report, total: 601, ignored: 247, survived: 4, killed: 9, timeout: 2, nocoverage: 308);
CheckReportMutants(report, total: 603, ignored: 249, survived: 4, killed: 9, timeout: 2, nocoverage: 308);
CheckReportTestCounts(report, total: 11);
}

Expand Down Expand Up @@ -122,7 +122,7 @@ public async Task CSharp_NetCore_WithTwoTestProjects()

var report = await JsonReportSerialization.DeserializeJsonReportAsync(strykerRunOutput);

CheckReportMutants(report, total: 601, ignored: 105, survived: 5, killed: 11, timeout: 2, nocoverage: 447);
CheckReportMutants(report, total: 603, ignored: 106, survived: 5, killed: 11, timeout: 2, nocoverage: 448);
CheckReportTestCounts(report, total: 21);
}

Expand All @@ -141,7 +141,7 @@ public async Task CSharp_NetCore_SolutionRun()

var report = await JsonReportSerialization.DeserializeJsonReportAsync(strykerRunOutput);

CheckReportMutants(report, total: 601, ignored: 247, survived: 4, killed: 9, timeout: 2, nocoverage: 308);
CheckReportMutants(report, total: 603, ignored: 249, survived: 4, killed: 9, timeout: 2, nocoverage: 308);
CheckReportTestCounts(report, total: 23);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1602,7 +1602,7 @@ int TestMethod()
}

[TestMethod]
public void ShouldMutatetringsInSwitchExpression()
public void ShouldMutateStringsInSwitchExpression()
{
var source = @"string TestMethod()
{
Expand Down
102 changes: 0 additions & 102 deletions src/Stryker.Core/Stryker.Core.UnitTest/Mutators/RegexMutatorTest.cs

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
using System.Linq;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Shouldly;
using Stryker.Abstractions.Mutators;
using Stryker.Core.Mutators;

namespace Stryker.Core.UnitTest.Mutators.Strings;

[TestClass]
public class RegexMutatorTest : TestBase
{

private static LiteralExpressionSyntax ParseExpression(string text)
{
var objectCreationExpression = SyntaxFactory.ParseExpression(text) as ObjectCreationExpressionSyntax;
return objectCreationExpression?.DescendantNodesAndSelf().OfType<LiteralExpressionSyntax>().FirstOrDefault();
}

[TestMethod]
public void ShouldBeMutationLevelAdvanced()
{
var target = new StringMutator();
target.RegexMutationLevel.ShouldBe(MutationLevel.Advanced);
}

[TestMethod]
[DataRow(MutationLevel.Basic)]
[DataRow(MutationLevel.Standard)]
public void ShouldNotMutateWithLowerMutationLevel(MutationLevel level)
{
var literalExpression = ParseExpression("new Regex(@\"^abc\")");
var target = new StringMutator();

var result = target.ApplyMutations(literalExpression, null, level);

result.ShouldBeEmpty();
}

[TestMethod]
public void ShouldMutateStringLiteralInRegexConstructor()
{
var literalExpression = ParseExpression("new Regex(@\"^abc\")");
var target = new StringMutator();

var result = target.ApplyMutations(literalExpression, null, MutationLevel.Advanced);

var mutation = result.ShouldHaveSingleItem();

mutation.DisplayName.ShouldBe("Regex anchor removal mutation");
var replacement = mutation.ReplacementNode.ShouldBeOfType<LiteralExpressionSyntax>();
replacement.Token.ValueText.ShouldBe("abc");
}

[TestMethod]
public void ShouldMutateStringLiteralInRegexConstructorWithFullName()
{
var literalExpression = ParseExpression("new System.Text.RegularExpressions.Regex(@\"^abc\")");
var target = new StringMutator();

var result = target.ApplyMutations(literalExpression, null, MutationLevel.Advanced);

var mutation = result.ShouldHaveSingleItem();

mutation.DisplayName.ShouldBe("Regex anchor removal mutation");
var replacement = mutation.ReplacementNode.ShouldBeOfType<LiteralExpressionSyntax>();
replacement.Token.ValueText.ShouldBe("abc");
}

[TestMethod]
public void ShouldNotMutateStringLiteralInOtherConstructor()
{
var literalExpression = ParseExpression("new Other(@\"^abc\")");
var target = new StringMutator();
var result = target.ApplyMutations(literalExpression, null, MutationLevel.Advanced);

result.Where(a => a.Type == Mutator.Regex).ShouldBeEmpty();
}

[TestMethod]
public void ShouldNotMutateAtLowerMutationLevel()
{
var literalExpression = ParseExpression("new Other(@\"^abc\")");
var target = new StringMutator();
var result = target.ApplyMutations(literalExpression, null, MutationLevel.Standard);

result.Where(a => a.Type == Mutator.Regex).ShouldBeEmpty();
}

[TestMethod]
public void ShouldMutateStringLiteralMultipleTimes()
{
var literalExpression = ParseExpression("new Regex(@\"^abc$\")");
var target = new StringMutator();

var result = target.ApplyMutations(literalExpression, null, MutationLevel.Advanced);

result.Count().ShouldBe(2);
result.ShouldAllBe(mutant => mutant.DisplayName == "Regex anchor removal mutation");
var first = result.First().ReplacementNode.ShouldBeOfType<LiteralExpressionSyntax>();
var last = result.Last().ReplacementNode.ShouldBeOfType<LiteralExpressionSyntax>();
first.Token.ValueText.ShouldBe("abc$");
last.Token.ValueText.ShouldBe("^abc");
}

[TestMethod]
public void ShouldMutateStringLiteralAsNamedArgumentPatternInRegexConstructor()
{
var literalExpression = ParseExpression("new Regex(options: RegexOptions.None, pattern: @\"^abc\")");
var target = new StringMutator();

var result = target.ApplyMutations(literalExpression, null, MutationLevel.Advanced);

var mutation = result.ShouldHaveSingleItem();

mutation.DisplayName.ShouldBe("Regex anchor removal mutation");
var replacement = mutation.ReplacementNode.ShouldBeOfType<LiteralExpressionSyntax>();
replacement.Token.ValueText.ShouldBe("abc");
}
}
Loading
Loading