Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Protect compiler directives during mutation #3116

Open
wants to merge 14 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions docs/technical-reference/Mutation Orchestration Design.md
Original file line number Diff line number Diff line change
Expand Up @@ -262,8 +262,7 @@ This class is used for several constructs:
- `AttributeListSyntax`: Attributes must be defined at compile time.
- `ParameterListSyntax`: Parameters and default values must be known at compile time.
- `EnumMemberSyntax`: Enumeration value must be known at compile time
- `RecursivePatternSyntax`: Pattern syntax must be known at compile time.
- `UsingDirectiveSyntax`: using directives are fixed and critical for compilation
- `UsingDirectiveSyntax`: using directives are fixed and critical for compilation
- `FieldDeclarationSyntax` (only const fields): cannot modify const fields at run time.
- `LocalDeclarationStatementSyntax` (only const): cannot modify constants at run time.

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@

Console.WriteLine("this is a global statement");
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: 606, ignored: 252, 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: 606, ignored: 105, survived: 5, killed: 11, timeout: 2, nocoverage: 452);
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: 606, ignored: 252, survived: 4, killed: 9, timeout: 2, nocoverage: 308);
CheckReportTestCounts(report, total: 23);
}

Expand Down
41 changes: 29 additions & 12 deletions src/Stryker.Core/Stryker.Core.UnitTest/AssertExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Shouldly;
using System;
using System.Collections.Generic;
using System.IO.Abstractions.TestingHelpers;
using System.Linq;

Expand Down Expand Up @@ -32,21 +34,36 @@ public static void ShouldBeSemantically(this SyntaxTree actual, SyntaxTree expec

if (!isSame)
{
// find the different
var actualLines = actual.ToString().Split(Environment.NewLine);
var expectedLines = expected.ToString().Split(Environment.NewLine);
for (var i = 0; i < actualLines.Length; i++)
var diff = ScanDiff(actual.GetRoot(), expected.GetRoot());

Console.WriteLine(string.Join(Environment.NewLine, diff));

}
}

private static List<string> ScanDiff(SyntaxNode actual, SyntaxNode expected)
{
var actualChildren = actual.ChildNodes().ToList();
var expectedChildren = expected.ChildNodes().ToList();
var failedStatements = new List<string>();
for (var i = 0; i < actualChildren.Count; i++)
{
if (expectedChildren.Count <= i)
{
failedStatements.Add($"Extra statements: {actualChildren[i]}");
continue;
}
if ((actualChildren[i] is not StatementSyntax) || (actualChildren[i] is BlockSyntax or IfStatementSyntax or ForStatementSyntax or WhileStatementSyntax ))
{
failedStatements.AddRange(ScanDiff(actualChildren[i], expectedChildren[i]));
continue;
}
if (!actualChildren[i].IsEquivalentTo(expectedChildren[i]))
{
if (expectedLines.Length <= i)
{
isSame.ShouldBeTrue($"AST's are not equivalent. Line[{i + 1}]{Environment.NewLine}actual:{actualLines[i]}{Environment.NewLine}expect: nothing{Environment.NewLine}Actual(full):{Environment.NewLine}{actual}{Environment.NewLine}, expected:{Environment.NewLine}{expected}");
}
if (actualLines[i] != expectedLines[i])
{
isSame.ShouldBeTrue($"AST's are not equivalent. Line[{i + 1}]{Environment.NewLine}actual:{actualLines[i]}{Environment.NewLine}expect:{expectedLines[i]}{Environment.NewLine}Actual(full):{Environment.NewLine}{actual}{Environment.NewLine}, expected:{Environment.NewLine}{expected}");
}
failedStatements.Add($"Not equivalent. Actual:{Environment.NewLine}{actualChildren[i]}{Environment.NewLine}Expected:{Environment.NewLine}{expectedChildren[i]}");
}
}
return failedStatements;
}

public static void ShouldBeWithNewlineReplace(this string actual, string expected)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public class BuildAnalyzerTestsBase : TestBase
protected internal const string DefaultFramework = "net6.0";
protected readonly MockFileSystem FileSystem = new();
protected string ProjectPath;
private readonly Dictionary<string, Dictionary<string, IAnalyzerResult>> _projectCache = new();
private readonly Dictionary<string, Dictionary<string, IAnalyzerResult>> _projectCache = [];
protected readonly Mock<IBuildalyzerProvider> BuildalyzerProviderMock = new(MockBehavior.Strict);

public BuildAnalyzerTestsBase()
Expand All @@ -41,7 +41,7 @@ protected Mock<IProjectAnalyzer> SourceProjectAnalyzerMock(string csprojPathName
IEnumerable<string> projectReferences = null, string framework = DefaultFramework, Func<bool> success = null)
{
var properties = GetSourceProjectDefaultProperties();
projectReferences ??= new List<string>();
projectReferences ??= [];

return BuildProjectAnalyzerMock(csprojPathName, sourceFiles, properties, projectReferences, [framework], success);
}
Expand All @@ -58,7 +58,7 @@ protected Mock<IProjectAnalyzer> SourceProjectAnalyzerMock(string csprojPathName
IEnumerable<string> projectReferences , IEnumerable<string> frameworks, Func<bool> success = null)
{
var properties = GetSourceProjectDefaultProperties();
projectReferences??= new List<string>();
projectReferences??= [];

return BuildProjectAnalyzerMock(csprojPathName, sourceFiles, properties, projectReferences, frameworks, success);
}
Expand All @@ -81,7 +81,7 @@ public static Dictionary<string, string> GetSourceProjectDefaultProperties()
/// <remarks>the test project references the production code project and contains no source file</remarks>
protected Mock<IProjectAnalyzer> TestProjectAnalyzerMock(string testCsprojPathName, string csProj, IEnumerable<string> frameworks = null, bool success = true)
{
frameworks??=new []{DefaultFramework};
frameworks??=[DefaultFramework];
var properties = new Dictionary<string, string>{ { "IsTestProject", "True" }, { "Language", "C#" } };
var projectReferences = string.IsNullOrEmpty(csProj) ? [] : GetProjectResult(csProj, frameworks.First()).ProjectReferences.Append(csProj).ToList();
return BuildProjectAnalyzerMock(testCsprojPathName, [], properties, projectReferences, frameworks, () => success);
Expand Down Expand Up @@ -110,7 +110,7 @@ private IAnalyzerResult GetProjectResult(string projectFile, string expectedFram
/// <returns>a tuple with the framework kind first and the version next</returns>
protected static (FrameworkKind kind, decimal version) ParseFramework(string framework)
{
FrameworkKind kind;

decimal version;

if (framework.StartsWith("netcoreapp"))
Expand Down Expand Up @@ -153,7 +153,7 @@ protected enum FrameworkKind
/// <returns><paramref name="framework"/> if the framework is among the target, the best match if available, null otherwise.</returns>
protected static string PickCompatibleFramework(string framework, IEnumerable<string> frameworks)
{
var parsed = ParseFramework(framework);
var (kind, version) = ParseFramework(framework);

string bestCandidate = null;
var bestVersion = 1.0m;
Expand All @@ -164,12 +164,12 @@ protected static string PickCompatibleFramework(string framework, IEnumerable<st
return framework;
}
var parsedCandidate = ParseFramework(candidate);
if (parsedCandidate.kind != parsed.kind)
if (parsedCandidate.kind != kind)
{
continue;
}

if (parsedCandidate.version > parsed.version || parsedCandidate.version <= bestVersion)
if (parsedCandidate.version > version || parsedCandidate.version <= bestVersion)
{
continue;
}
Expand Down Expand Up @@ -264,7 +264,7 @@ internal Mock<IProjectAnalyzer> BuildProjectAnalyzerMock(string csprojPathName,
return projectAnalyzerMock;
}

private IAnalyzerResults BuildAnalyzerResultsMock(IDictionary<string, IAnalyzerResult> projectAnalyzerResults)
private static IAnalyzerResults BuildAnalyzerResultsMock(IDictionary<string, IAnalyzerResult> projectAnalyzerResults)
{
var analyzerResults = projectAnalyzerResults.Values.ToList();
var sourceProjectAnalyzerResultsMock = new Mock<IAnalyzerResults>(MockBehavior.Strict);
Expand Down
Loading
Loading