diff --git a/src/Stryker.Core/Stryker.Core/DiffProviders/DiffResult.cs b/src/Stryker.Core/Stryker.Core/DiffProviders/DiffResult.cs index 6dc91a761..9277b754d 100644 --- a/src/Stryker.Core/Stryker.Core/DiffProviders/DiffResult.cs +++ b/src/Stryker.Core/Stryker.Core/DiffProviders/DiffResult.cs @@ -4,6 +4,6 @@ namespace Stryker.Core.DiffProviders; public class DiffResult { - public ICollection ChangedTestFiles { get; set; } - public ICollection ChangedSourceFiles { get; set; } + public IDictionary> ChangedTestFiles { get; set; } + public IDictionary> ChangedSourceFiles { get; set; } } diff --git a/src/Stryker.Core/Stryker.Core/DiffProviders/GitDiffProvider.cs b/src/Stryker.Core/Stryker.Core/DiffProviders/GitDiffProvider.cs index 1b3e0b854..4c2bb07c4 100644 --- a/src/Stryker.Core/Stryker.Core/DiffProviders/GitDiffProvider.cs +++ b/src/Stryker.Core/Stryker.Core/DiffProviders/GitDiffProvider.cs @@ -1,3 +1,4 @@ +using System.Collections.Generic; using System.Collections.ObjectModel; using System.IO; using System.Linq; @@ -27,8 +28,8 @@ public DiffResult ScanDiff() { var diffResult = new DiffResult() { - ChangedSourceFiles = new Collection(), - ChangedTestFiles = new Collection() + ChangedSourceFiles = new Dictionary>(), + ChangedTestFiles = new Dictionary>(), }; // A git repository has been detected, calculate the diff to filter @@ -58,13 +59,44 @@ public DiffResult ScanDiff() if (testPaths.Any(testPath => diffPath.StartsWith(testPath))) { - diffResult.ChangedTestFiles.Add(diffPath); + for (var i = 0; i < patchChanges.AddedLines.Count; i++) + { + if (!diffResult.ChangedTestFiles.ContainsKey(diffPath)) + { + diffResult.ChangedTestFiles.Add(diffPath, [patchChanges.AddedLines[i].LineNumber]); + + } + else + + { + var entry = diffResult.ChangedTestFiles[diffPath]; + entry.Add(patchChanges.AddedLines[i].LineNumber); + } + } } else { - diffResult.ChangedSourceFiles.Add(diffPath); + if (patchChanges.AddedLines.Count > 0) + { + for (var i = 0; i < patchChanges.AddedLines.Count; i++) + { + if (!diffResult.ChangedSourceFiles.ContainsKey(diffPath)) + { + diffResult.ChangedSourceFiles.Add(diffPath, [patchChanges.AddedLines[i].LineNumber]); + + } + else + + { + var entry = diffResult.ChangedSourceFiles[diffPath]; + entry.Add(patchChanges.AddedLines[i].LineNumber); + } + } + + } } } + RemoveFilteredOutFiles(diffResult); return diffResult; @@ -74,8 +106,8 @@ private void RemoveFilteredOutFiles(DiffResult diffResult) { foreach (var glob in _options.DiffIgnoreChanges.Select(d => d.Glob)) { - diffResult.ChangedSourceFiles = diffResult.ChangedSourceFiles.Where(diffResultFile => !glob.IsMatch(diffResultFile)).ToList(); - diffResult.ChangedTestFiles = diffResult.ChangedTestFiles.Where(diffResultFile => !glob.IsMatch(diffResultFile)).ToList(); + diffResult.ChangedSourceFiles = diffResult.ChangedSourceFiles.Where(diffResultFile => !glob.IsMatch(diffResultFile.Key)).ToDictionary(); + diffResult.ChangedTestFiles = diffResult.ChangedTestFiles.Where(diffResultFile => !glob.IsMatch(diffResultFile.Key)).ToDictionary(); } } } diff --git a/src/Stryker.Core/Stryker.Core/MutantFilters/SinceMutantFilter.cs b/src/Stryker.Core/Stryker.Core/MutantFilters/SinceMutantFilter.cs index ca6a6e791..c2fb33e3d 100644 --- a/src/Stryker.Core/Stryker.Core/MutantFilters/SinceMutantFilter.cs +++ b/src/Stryker.Core/Stryker.Core/MutantFilters/SinceMutantFilter.cs @@ -1,4 +1,3 @@ - using System.Collections.Generic; using System.Linq; using Microsoft.CodeAnalysis; @@ -55,17 +54,37 @@ public IEnumerable FilterMutants(IEnumerable mutants, IReadOnl IEnumerable filteredMutants; // A non-csharp file is flagged by the diff result as modified. We cannot determine which mutants will be affected by this, thus all mutants have to be tested. - if (_diffResult.ChangedTestFiles is { } && _diffResult.ChangedTestFiles.Any(x => !x.EndsWith(".cs"))) + if (_diffResult.ChangedTestFiles is { } && _diffResult.ChangedTestFiles.Keys.Any(x => !x.EndsWith(".cs"))) { _logger.LogDebug("Returning all mutants in {RelativePath} because a non-source file is modified", file.RelativePath); return SetMutantStatusForNonCSharpFileChanged(mutants); } // If the diff result flags this file as modified, we want to run all mutants again - if (_diffResult.ChangedSourceFiles != null && _diffResult.ChangedSourceFiles.Contains(file.FullPath)) + if (_diffResult.ChangedSourceFiles != null && _diffResult.ChangedSourceFiles.Keys.Contains(file.FullPath)) { + foreach (var mutant in mutants) + { + foreach (var line in _diffResult.ChangedSourceFiles[file.FullPath]) + { + + var actualLineSpan = mutant.Mutation.OriginalNode.GetLocation().GetMappedLineSpan(); + + _logger.LogDebug("Original line span is found to be {actualLineSpan}", actualLineSpan); + var start = actualLineSpan.Span.Start.Line; + var end = actualLineSpan.Span.Start.Line; + if (start <= line && line <= end) + { + if (mutant.ResultStatus != MutantStatus.NoCoverage) + { + mutant.ResultStatus = MutantStatus.Pending; + mutant.ResultStatusReason = "Mutant changed compared to target commit"; + } + } + } + } _logger.LogDebug("Returning all mutants in {RelativePath} because the file is modified", file.RelativePath); - return SetMutantStatusForFileChanged(mutants); + return mutants; } else { @@ -74,12 +93,30 @@ public IEnumerable FilterMutants(IEnumerable mutants, IReadOnl // If any of the tests have been changed, we want to return all mutants covered by these testfiles. // Only check for changed c# files. Other files have already been handled. - if (_diffResult.ChangedTestFiles != null && _diffResult.ChangedTestFiles.Any(file => file.EndsWith(".cs"))) + if (_diffResult.ChangedTestFiles != null && _diffResult.ChangedTestFiles.Keys.Any(file => file.EndsWith(".cs"))) { - filteredMutants = ResetMutantStatusForChangedTests(mutants); + foreach (var mutant in mutants) + { + foreach (var line in _diffResult.ChangedTestFiles[file.FullPath]) + { + var actualLineSpan = mutant.Mutation.OriginalNode.GetLocation().GetMappedLineSpan(); + + _logger.LogDebug("Original line span is found to be {actualLineSpan}", actualLineSpan); + var start = actualLineSpan.Span.Start.Line; + var end = actualLineSpan.Span.Start.Line; + if (start <= line && line <= end) + { + if (mutant.ResultStatus != MutantStatus.NoCoverage) + { + mutant.ResultStatus = MutantStatus.Pending; + mutant.ResultStatusReason = "Mutant changed compared to target commit"; + } + } + } + } } - return filteredMutants; + return mutants; } private static IEnumerable SetNotRunMutantsToIgnored(IEnumerable mutants) @@ -128,7 +165,7 @@ private IEnumerable ResetMutantStatusForChangedTests(IEnumerable _diffResult.ChangedTestFiles.Any(changedTestFile => coveringTest.TestFilePath == changedTestFile + && coveringTests.Any(coveringTest => _diffResult.ChangedTestFiles.Keys.Any(changedTestFile => coveringTest.TestFilePath == changedTestFile || string.IsNullOrEmpty(coveringTest.TestFilePath)))) { mutant.ResultStatus = MutantStatus.Pending; @@ -141,3 +178,4 @@ private IEnumerable ResetMutantStatusForChangedTests(IEnumerable