diff --git a/docs/mutations.md b/docs/mutations.md
index 4f20d17aaa..9fda87f165 100644
--- a/docs/mutations.md
+++ b/docs/mutations.md
@@ -157,7 +157,9 @@ Do you have a suggestion for a (new) mutator? Feel free to create an [issue](htt
| Original | Mutated |
| ------------- | ------------- |
| `"foo"` | `""` |
+| `"foo"u8` | `""u8` |
| `""` | `"Stryker was here!"` |
+| `""u8` | `"Stryker was here!"u8` |
| `$"foo {bar}"` | `$""` |
| `@"foo"` | `@""` |
| `string.Empty` | `"Stryker was here!"` |
diff --git a/integrationtest/TargetProjects/NetCoreTestProject.XUnit/NetCoreTestProject.XUnit.csproj b/integrationtest/TargetProjects/NetCoreTestProject.XUnit/NetCoreTestProject.XUnit.csproj
index 42ccc73ae5..ec65ba0feb 100644
--- a/integrationtest/TargetProjects/NetCoreTestProject.XUnit/NetCoreTestProject.XUnit.csproj
+++ b/integrationtest/TargetProjects/NetCoreTestProject.XUnit/NetCoreTestProject.XUnit.csproj
@@ -2,6 +2,7 @@
net6.0
+ latest
diff --git a/integrationtest/TargetProjects/NetCoreTestProject.XUnit/String/Utf8StringMagicTests.cs b/integrationtest/TargetProjects/NetCoreTestProject.XUnit/String/Utf8StringMagicTests.cs
new file mode 100644
index 0000000000..17c4ab4e27
--- /dev/null
+++ b/integrationtest/TargetProjects/NetCoreTestProject.XUnit/String/Utf8StringMagicTests.cs
@@ -0,0 +1,50 @@
+using ExampleProject.String;
+using Xunit;
+
+namespace ExampleProject.XUnit.String
+{
+ public class Utf8StringMagicTests
+ {
+ [Fact]
+ public void AddTwoStrings()
+ {
+ var sut = new Utf8StringMagic();
+ var actual = sut.HelloWorld();
+ Assert.Equal("Hello World!"u8, actual);
+ }
+
+ [Fact]
+ public void IsNullOrEmpty()
+ {
+ var sut = new Utf8StringMagic();
+ var actual = sut.IsNullOrEmpty("hello"u8);
+ Assert.False(actual);
+ }
+
+ [Fact]
+ public void IsNullOrEmpty_Empty()
+ {
+ var sut = new Utf8StringMagic();
+ var actual = sut.IsNullOrEmpty(""u8);
+ Assert.True(actual);
+ }
+
+ [Fact]
+ public void Referenced()
+ {
+ var sut = new Utf8StringMagic();
+ var input = "hello"u8;
+ sut.Referenced(out input);
+ Assert.Equal("world"u8, input);
+ }
+
+ [Fact]
+ public void ReferencedEmpty()
+ {
+ var sut = new Utf8StringMagic();
+ var input = "hello"u8;
+ sut.ReferencedEmpty(out input);
+ Assert.Equal(""u8, input);
+ }
+ }
+}
diff --git a/integrationtest/TargetProjects/TargetProject/String/Utf8StringMagic.cs b/integrationtest/TargetProjects/TargetProject/String/Utf8StringMagic.cs
new file mode 100644
index 0000000000..de92605da3
--- /dev/null
+++ b/integrationtest/TargetProjects/TargetProject/String/Utf8StringMagic.cs
@@ -0,0 +1,32 @@
+using System;
+using System.Linq;
+
+namespace ExampleProject.String
+{
+ public class Utf8StringMagic
+ {
+ public ReadOnlySpan HelloWorld()
+ {
+ return "Hello"u8 + " "u8 + "World!"u8;
+ }
+
+ public void Referenced(out ReadOnlySpan test)
+ {
+ test = "world"u8;
+ }
+
+ public void ReferencedEmpty(out ReadOnlySpan test)
+ {
+ test = ""u8;
+ }
+
+ public bool IsNullOrEmpty(ReadOnlySpan myString)
+ {
+ if (myString.IsEmpty)
+ {
+ return true;
+ }
+ return false;
+ }
+ }
+}
diff --git a/integrationtest/ValidationProject/ValidateStrykerResults.cs b/integrationtest/ValidationProject/ValidateStrykerResults.cs
index 2bc35f71b3..4ea4d038d5 100644
--- a/integrationtest/ValidationProject/ValidateStrykerResults.cs
+++ b/integrationtest/ValidationProject/ValidateStrykerResults.cs
@@ -82,8 +82,8 @@ public void NetCore()
var report = JsonConvert.DeserializeObject(strykerRunOutput);
- CheckReportMutants(report, total: 114, ignored: 55, survived: 4, killed: 6, timeout: 2, nocoverage: 45);
- CheckReportTestCounts(report, total: 14);
+ CheckReportMutants(report, total: 129, ignored: 59, survived: 5, killed: 10, timeout: 2, nocoverage: 45);
+ CheckReportTestCounts(report, total: 19);
}
[Fact]
@@ -121,8 +121,8 @@ public void NetCoreWithTwoTestProjects()
var report = JsonConvert.DeserializeObject(strykerRunOutput);
- CheckReportMutants(report, total: 114, ignored: 27, survived: 8, killed: 8, timeout: 2, nocoverage: 67);
- CheckReportTestCounts(report, total: 30);
+ CheckReportMutants(report, total: 129, ignored: 31, survived: 9, killed: 12, timeout: 2, nocoverage: 67);
+ CheckReportTestCounts(report, total: 35);
}
[Fact]
@@ -140,8 +140,8 @@ public void SolutionRun()
var report = JsonConvert.DeserializeObject(strykerRunOutput);
- CheckReportMutants(report, total: 114, ignored: 55, survived: 4, killed: 6, timeout: 2, nocoverage: 45);
- CheckReportTestCounts(report, total: 30);
+ CheckReportMutants(report, total: 129, ignored: 59, survived: 5, killed: 10, timeout: 2, nocoverage: 45);
+ CheckReportTestCounts(report, total: 35);
}
private void CheckMutationKindsValidity(JsonReport report)
diff --git a/src/Stryker.Core/Stryker.Core.UnitTest/Mutators/StringMutatorTests.cs b/src/Stryker.Core/Stryker.Core.UnitTest/Mutators/StringMutatorTests.cs
index 39cf4f90d9..77b400095c 100644
--- a/src/Stryker.Core/Stryker.Core.UnitTest/Mutators/StringMutatorTests.cs
+++ b/src/Stryker.Core/Stryker.Core.UnitTest/Mutators/StringMutatorTests.cs
@@ -16,6 +16,25 @@ public void ShouldBeMutationLevelStandard()
target.MutationLevel.ShouldBe(MutationLevel.Standard);
}
+ [Theory]
+ [InlineData(@"""""u8", @"""Stryker was here!""u8")]
+ [InlineData(@"""foo""u8", @"""""u8")]
+ public void ShouldMutateUtf8StringLiteral(string original, string expected)
+ {
+ var syntaxTree = CSharpSyntaxTree.ParseText($"var test = {original};");
+
+ var literalExpression = syntaxTree.GetRoot().DescendantNodes().OfType().First();
+ var mutator = new StringMutator();
+
+ var result = mutator.ApplyMutations(literalExpression, null).ToList();
+
+ var mutation = result.ShouldHaveSingleItem();
+
+ mutation.ReplacementNode.ShouldBeOfType()
+ .Token.Text.ShouldBe(expected);
+ mutation.DisplayName.ShouldBe("String mutation");
+ }
+
[Theory]
[InlineData("", "Stryker was here!")]
[InlineData("foo", "")]
diff --git a/src/Stryker.Core/Stryker.Core/Mutators/StringMutator.cs b/src/Stryker.Core/Stryker.Core/Mutators/StringMutator.cs
index f2fe819e25..0043e42f73 100644
--- a/src/Stryker.Core/Stryker.Core/Mutators/StringMutator.cs
+++ b/src/Stryker.Core/Stryker.Core/Mutators/StringMutator.cs
@@ -17,24 +17,53 @@ public override IEnumerable ApplyMutations(LiteralExpressionSyntax nod
// Get objectCreationSyntax to check if it contains a regex type.
var root = node.Parent?.Parent?.Parent;
- if (!IsSpecialType(root) && IsStringLiteral(node))
+ if (IsSpecialType(root))
{
- var currentValue = (string)node.Token.Value;
- var replacementValue = currentValue == "" ? "Stryker was here!" : "";
- yield return new Mutation
- {
- OriginalNode = node,
- ReplacementNode = SyntaxFactory.LiteralExpression(SyntaxKind.StringLiteralExpression, SyntaxFactory.Literal(replacementValue)),
- DisplayName = "String mutation",
- Type = Mutator.String
- };
+ yield break;
}
+
+ SyntaxNode syntaxNode;
+ string currentValue;
+ string replacementValue;
+
+ if (IsStringLiteral(node))
+ {
+ currentValue = (string)node.Token.Value;
+ replacementValue = currentValue == "" ? "Stryker was here!" : "";
+ syntaxNode = SyntaxFactory.LiteralExpression(SyntaxKind.StringLiteralExpression,
+ SyntaxFactory.Literal(replacementValue));
+ }
+ else if (IsUtf8StringLiteral(node))
+ {
+ currentValue = (string)node.Token.Value;
+ replacementValue = currentValue == "" ? "Stryker was here!" : "";
+ syntaxNode = CreateUtf88String(node.GetLeadingTrivia(), replacementValue, node.GetTrailingTrivia());
+ }
+ else
+ {
+ yield break;
+ }
+
+ yield return new Mutation
+ {
+ OriginalNode = node,
+ ReplacementNode = syntaxNode,
+ DisplayName = "String mutation",
+ Type = Mutator.String
+ };
+ }
+
+ private static bool IsUtf8StringLiteral(LiteralExpressionSyntax node)
+ {
+ var kind = node.Kind();
+ return kind is SyntaxKind.Utf8StringLiteralExpression
+ && node.Parent is not ConstantPatternSyntax;
}
private static bool IsStringLiteral(LiteralExpressionSyntax node)
{
var kind = node.Kind();
- return kind == SyntaxKind.StringLiteralExpression
+ return kind is SyntaxKind.StringLiteralExpression
&& node.Parent is not ConstantPatternSyntax;
}
@@ -49,4 +78,19 @@ private static bool IsCtorOfType(ObjectCreationExpressionSyntax ctor, Type type)
var ctorType = ctor.Type.ToString();
return ctorType == type.Name || ctorType == type.FullName;
}
+
+ private static LiteralExpressionSyntax CreateUtf88String(SyntaxTriviaList leadingTrivia, string stringValue, SyntaxTriviaList trailingTrivia)
+ {
+ var quoteCharacter = '"';
+ var suffix = "u8";
+
+ var literal = SyntaxFactory.Token(
+ leading: leadingTrivia,
+ kind: SyntaxKind.Utf8StringLiteralToken,
+ text: quoteCharacter + stringValue + quoteCharacter + suffix,
+ valueText: "",
+ trailing: trailingTrivia);
+
+ return SyntaxFactory.LiteralExpression(SyntaxKind.Utf8StringLiteralExpression, literal);
+ }
}