Skip to content

Commit

Permalink
Add support for Utf8 string literals
Browse files Browse the repository at this point in the history
  • Loading branch information
iamdmitrij committed May 21, 2024
1 parent 4f5fefc commit 705bc66
Show file tree
Hide file tree
Showing 6 changed files with 158 additions and 5 deletions.
2 changes: 2 additions & 0 deletions docs/mutations.md
Original file line number Diff line number Diff line change
Expand Up @@ -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!"` |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<LangVersion>latest</LangVersion>
</PropertyGroup>

<PropertyGroup>
Expand Down
Original file line number Diff line number Diff line change
@@ -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);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
using System;
using System.Linq;

namespace ExampleProject.String
{
public class Utf8StringMagic
{
public ReadOnlySpan<byte> HelloWorld()
{
var a = "";
return "Hello"u8 + " "u8 + "World!"u8;
}

public void Referenced(out ReadOnlySpan<byte> test)
{
test = "world"u8;
}

public void ReferencedEmpty(out ReadOnlySpan<byte> test)
{
test = ""u8;
}

public bool IsNullOrEmpty(ReadOnlySpan<byte> myString)
{
if (myString.IsEmpty)
{
return true;
}
return false;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,29 @@ public void ShouldBeMutationLevelStandard()
target.MutationLevel.ShouldBe(MutationLevel.Standard);
}

[Theory]
[InlineData("", """
"Stryker was here!"u8
""")]
[InlineData("foo", """
""u8
""")]
public void ShouldMutateUtf8(string original, string expected)
{
var syntaxTree = CSharpSyntaxTree.ParseText($"""var = "{original}"u8;""");

var literalExpression = syntaxTree.GetRoot().DescendantNodes().OfType<LiteralExpressionSyntax>().First();
var mutator = new StringMutator();

var result = mutator.ApplyMutations(literalExpression, null).ToList();

var mutation = result.ShouldHaveSingleItem();

mutation.ReplacementNode.ShouldBeOfType<LiteralExpressionSyntax>()
.Token.Text.ShouldBe(expected);
mutation.DisplayName.ShouldBe("String mutation");
}

[Theory]
[InlineData("", "Stryker was here!")]
[InlineData("foo", "")]
Expand Down
54 changes: 49 additions & 5 deletions src/Stryker.Core/Stryker.Core/Mutators/StringMutator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,24 +17,52 @@ public override IEnumerable<Mutation> 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!" : "";
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 = SyntaxFactory.LiteralExpression(SyntaxKind.StringLiteralExpression, SyntaxFactory.Literal(replacementValue)),
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;
}

Expand All @@ -49,4 +77,20 @@ 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);
}

}

0 comments on commit 705bc66

Please sign in to comment.