Skip to content
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ public void ScanDiff_Throws_Stryker_Input_Exception_When_Commit_null()
public void ScanDiffReturnsListOfFiles_ExcludingTestFilesInDiffIgnoreFiles()
{
// Arrange
var diffIgnoreFiles = new[] { new FilePattern(Glob.Parse("/c/Users/JohnDoe/Project/Tests/Test.cs"), false, null) };
var diffIgnoreFiles = new[] { new ExclusionPattern("/c/Users/JohnDoe/Project/Tests/Test.cs") };

var basePath = FilePathUtils.NormalizePathSeparators("/c/Users/JohnDoe/Project/Tests");
var options = new StrykerOptions()
Expand Down Expand Up @@ -289,7 +289,7 @@ public void ScanDiffReturnsListOfFiles_ExcludingTestFilesInDiffIgnoreFiles()
public void ScanDiffReturnsListOfFiles_ExcludingTestFilesInDiffIgnoreFiles_Single_Asterisk()
{
// Arrange
var diffIgnoreFiles = new[] { new FilePattern(Glob.Parse("/c/Users/JohnDoe/Project/*/Test.cs"), false, null) };
var diffIgnoreFiles = new[] { new ExclusionPattern("/c/Users/JohnDoe/Project/*/Test.cs") };

var basePath = FilePathUtils.NormalizePathSeparators("/c/Users/JohnDoe/Project/Tests");
var options = new StrykerOptions()
Expand Down Expand Up @@ -365,7 +365,7 @@ public void ScanDiffReturnsListOfFiles_ExcludingTestFilesInDiffIgnoreFiles_Singl
public void ScanDiffReturnsListOfFiles_ExcludingTestFilesInDiffIgnoreFiles_Multi_Asterisk()
{
// Arrange
var diffIgnoreFiles = new[] { new FilePattern(Glob.Parse("**/Test.cs"), false, null) };
var diffIgnoreFiles = new[] { new ExclusionPattern("**/Test.cs") };

var basePath = FilePathUtils.NormalizePathSeparators("/c/Users/JohnDoe/Project/Tests");
var options = new StrykerOptions()
Expand Down Expand Up @@ -441,7 +441,7 @@ public void ScanDiffReturnsListOfFiles_ExcludingTestFilesInDiffIgnoreFiles_Multi
public void ScanDiffReturnsListOfFiles_ExcludingFilesInDiffIgnoreFiles_Multi_Asterisk()
{
// Arrange
var diffIgnoreFiles = new[] { new FilePattern(Glob.Parse("**/file.cs"), false, null) };
var diffIgnoreFiles = new[] { new ExclusionPattern("**/file.cs") };

var basePath = FilePathUtils.NormalizePathSeparators("/c/Users/JohnDoe/Project/Tests");
var options = new StrykerOptions()
Expand Down
36 changes: 36 additions & 0 deletions src/Stryker.Core/Stryker.Core.UnitTest/ExclusionPatternTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
using System;
using Shouldly;
using Xunit;

namespace Stryker.Core.UnitTest
{
public class ExclusionPatternTests : TestBase
{
[Fact]
public void ExclusionPattern_Null()
{
_ = Assert.Throws<ArgumentNullException>(() => new ExclusionPattern(null));
}

[Fact]
public void ExclusionPattern_Globs()
{
var s1 = new ExclusionPattern(@"Person.cs");
var s2 = new ExclusionPattern(@"!Person.cs");

s1.IsExcluded.ShouldBeFalse();
s2.IsExcluded.ShouldBeTrue();
s1.Glob.ToString().ShouldBe(s2.Glob.ToString());
}

[Fact]
public void ExclusionPattern_MutantSpans()
{
var s1 = new ExclusionPattern(@"src/Person.cs{10..100}");
var s2 = new ExclusionPattern(@"src/Person.cs");

s1.MutantSpans.ShouldBe(new [] { (10, 100)});
s2.MutantSpans.ShouldBeEmpty();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
using Shouldly;
using Xunit;

namespace Stryker.Core.UnitTest.Options
namespace Stryker.Core.UnitTest
{
public class FilePatternTests : TestBase
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public void ShouldCopyValues()
DashboardUrl = "url",
DevMode = true,
Since = true,
DiffIgnoreChanges = new[] { new FilePattern(Glob.Parse("**"), true, null) },
DiffIgnoreChanges = new[] { new ExclusionPattern("**") },
ExcludedMutations = new[] { Mutator.Bitwise },
FallbackVersion = "main",
IgnoredMethods = new[] { new Regex("") },
Expand Down
49 changes: 49 additions & 0 deletions src/Stryker.Core/Stryker.Core/ExclusionPattern.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using DotNet.Globbing;

namespace Stryker.Core
{
public readonly struct ExclusionPattern
{
private static readonly Regex _mutantSpanGroupRegex = new("(\\{(\\d+)\\.\\.(\\d+)\\})+$");
private static readonly Regex _mutantSpanRegex = new Regex("\\{(\\d+)\\.\\.(\\d+)\\}");

public ExclusionPattern(string s)
{
if (s is null)
{
throw new ArgumentNullException(nameof(s));
}

IsExcluded = s.StartsWith('!');

var pattern = IsExcluded ? s[1..] : s;
var mutantSpansRegex = _mutantSpanGroupRegex.Match(pattern);
if (mutantSpansRegex.Success)
{
var filePathPart = pattern[..^mutantSpansRegex.Length];
var normalized = FilePathUtils.NormalizePathSeparators(filePathPart);
Glob = Glob.Parse(normalized);

MutantSpans = _mutantSpanRegex
.Matches(mutantSpansRegex.Value)
.Select(x => (int.Parse(x.Groups[1].Value), int.Parse(x.Groups[2].Value)));
}
else
{
var normalized = FilePathUtils.NormalizePathSeparators(pattern);
Glob = Glob.Parse(normalized);
MutantSpans = Enumerable.Empty<(int, int)>();
}
}

public bool IsExcluded { get; }

public Glob Glob { get; }

public IEnumerable<(int Start, int End)> MutantSpans { get; }
}
}
37 changes: 6 additions & 31 deletions src/Stryker.Core/Stryker.Core/FilePattern.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;

namespace Stryker.Core
{
Expand All @@ -13,8 +12,6 @@ namespace Stryker.Core
/// </summary>
public sealed class FilePattern : IEquatable<FilePattern>
{
private static readonly Regex _textSpanGroupRegex = new Regex("(\\{(\\d+)\\.\\.(\\d+)\\})+$");
private static readonly Regex _textSpanRegex = new Regex("\\{(\\d+)\\.\\.(\\d+)\\}");
private static readonly TextSpan _textSpanMaxValue = new TextSpan(0, int.MaxValue);

public FilePattern(Glob glob, bool isExclude, IReadOnlyCollection<TextSpan> textSpans)
Expand Down Expand Up @@ -45,47 +42,25 @@ public FilePattern(Glob glob, bool isExclude, IReadOnlyCollection<TextSpan> text
/// </summary>
/// <param name="pattern">The pattern to parse.</param>
/// <returns>The <see cref="FilePattern"/></returns>
public static FilePattern Parse(string pattern) => Parse(pattern, spansEnabled: true);

/// <summary>
/// Parses a given file pattern string.
/// Format: (!)&lt;glob&gt;({&lt;spanStart&gt;..&lt;spanEnd&gt;})*
/// </summary>
/// <param name="pattern">The pattern to parse.</param>
/// <param name="spansEnabled">Enable or disable span parsing.</param>
/// <returns>The <see cref="FilePattern"/></returns>
public static FilePattern Parse(string pattern, bool spansEnabled)
public static FilePattern Parse(string pattern)
{
var exclude = false;
var s = new ExclusionPattern(pattern);
IReadOnlyCollection<TextSpan> textSpans;

if (pattern.StartsWith('!'))
{
exclude = true;
pattern = pattern[1..];
}

var textSpanGroupMatch = _textSpanGroupRegex.Match(pattern);
if (!spansEnabled || !textSpanGroupMatch.Success)
if (!s.MutantSpans.Any())
{
// If there are no spans specified, we add one that will cover the whole file.
textSpans = new[] { _textSpanMaxValue };
}
else
{
// If we have one ore more spans we parse them.
var textSpansMatches = _textSpanRegex.Matches(textSpanGroupMatch.Value);
textSpans = textSpansMatches
.Select(x => TextSpan.FromBounds(int.Parse(x.Groups[1].Value), int.Parse(x.Groups[2].Value)))
textSpans = s.MutantSpans
.Select(x => TextSpan.FromBounds(x.Start, x.End))
.Reduce()
.ToList();

pattern = pattern.Substring(0, pattern.Length - textSpanGroupMatch.Length);
}

var glob = Glob.Parse(FilePathUtils.NormalizePathSeparators(pattern));

return new FilePattern(glob, exclude, textSpans);
return new FilePattern(s.Glob, s.IsExcluded, textSpans);
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,19 @@ Any non-excluded files will trigger all mutants to be tested because we cannot d
Use glob syntax for wildcards: https://en.wikipedia.org/wiki/Glob_(programming)
Example: ['**/*Assets.json','**/favicon.ico']";

public IEnumerable<FilePattern> Validate()
public IEnumerable<ExclusionPattern> Validate()
{
if (SuppliedInput is { })
{
var diffIgnoreFilePatterns = new List<FilePattern>();
var diffIgnoreStrings = new List<ExclusionPattern>();
foreach (var pattern in SuppliedInput)
{
diffIgnoreFilePatterns.Add(FilePattern.Parse(FilePathUtils.NormalizePathSeparators(pattern), spansEnabled: false));
diffIgnoreStrings.Add(new ExclusionPattern(FilePathUtils.NormalizePathSeparators(pattern)));
}

return diffIgnoreFilePatterns;
return diffIgnoreStrings;
}
return Enumerable.Empty<FilePattern>();
return Enumerable.Empty<ExclusionPattern>();
}
}
}
2 changes: 1 addition & 1 deletion src/Stryker.Core/Stryker.Core/Options/StrykerOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ public class StrykerOptions
/// Context: When using the since feature, all tests are run again if files in the test project change (as these could impact the test results)
/// When the file is present in this option the tests should not run again as the file does not impact test results.
/// </summary>
public IEnumerable<FilePattern> DiffIgnoreChanges { get; init; } = Enumerable.Empty<FilePattern>();
public IEnumerable<ExclusionPattern> DiffIgnoreChanges { get; init; } = Enumerable.Empty<ExclusionPattern>();

/// <summary>
/// When no previous report can be found for the since feature, this commitish is used to se a baseline.
Expand Down