diff --git a/src/Microsoft.ComponentDetection.Common/FastDirectoryWalkerFactory.cs b/src/Microsoft.ComponentDetection.Common/FastDirectoryWalkerFactory.cs index 3dd13995f..a1f2fa2e5 100644 --- a/src/Microsoft.ComponentDetection.Common/FastDirectoryWalkerFactory.cs +++ b/src/Microsoft.ComponentDetection.Common/FastDirectoryWalkerFactory.cs @@ -39,15 +39,12 @@ public IObservable GetDirectoryScanner(DirectoryInfo root, Concu return Task.CompletedTask; } - PatternMatchingUtility.FilePatternMatcher fileIsMatch = null; + PatternMatchingUtility.CompiledMatcher fileIsMatch = null; + var patternsArray = filePatterns?.ToArray(); - if (filePatterns == null || !filePatterns.Any()) + if (patternsArray is { Length: > 0 }) { - fileIsMatch = span => true; - } - else - { - fileIsMatch = PatternMatchingUtility.GetFilePatternMatcher(filePatterns); + fileIsMatch = PatternMatchingUtility.Compile(patternsArray); } var sw = Stopwatch.StartNew(); @@ -100,7 +97,7 @@ public IObservable GetDirectoryScanner(DirectoryInfo root, Concu { ShouldIncludePredicate = (ref FileSystemEntry entry) => { - if (!entry.IsDirectory && fileIsMatch(entry.FileName)) + if (!entry.IsDirectory && (fileIsMatch == null || fileIsMatch.IsMatch(entry.FileName))) { return true; } @@ -210,46 +207,26 @@ public void Initialize(DirectoryInfo root, ExcludeDirectoryPredicate directoryEx public IObservable Subscribe(DirectoryInfo root, IEnumerable patterns) { - var patternArray = patterns.ToArray(); - - if (this.pendingScans.TryGetValue(root, out var scannerObservable)) - { - this.logger.LogDebug("Logging patterns {Patterns} for {Root}", string.Join(":", patterns), root.FullName); - - var inner = scannerObservable.Value.Where(fsi => - { - if (fsi is FileInfo fi) - { - return this.MatchesAnyPattern(fi, patternArray); - } - else - { - return true; - } - }); - - return inner; - } - - throw new InvalidOperationException("Subscribe called without initializing scanner"); + var patternsArray = patterns as string[] ?? patterns.ToArray(); + var compiled = PatternMatchingUtility.Compile(patternsArray); + return this.Subscribe(root, patternsArray, compiled); } public IObservable GetFilteredComponentStreamObservable(DirectoryInfo root, IEnumerable patterns, IComponentRecorder componentRecorder) { - var observable = this.Subscribe(root, patterns).OfType().SelectMany(f => patterns.Select(sp => new - { - SearchPattern = sp, - File = f, - })).Where(x => - { - var searchPattern = x.SearchPattern; - var fileName = x.File.Name; + var patternsArray = patterns as string[] ?? patterns.ToArray(); + var compiled = PatternMatchingUtility.Compile(patternsArray); - return this.pathUtilityService.MatchesPattern(searchPattern, fileName); - }).Where(x => x.File.Exists) + var observable = this.Subscribe(root, patternsArray, compiled).OfType() + .Select(f => new + { + File = f, + MatchedPattern = compiled.GetMatchingPattern(f.Name), + }) + .Where(x => x.MatchedPattern != null && x.File.Exists) .Select(x => { - var lazyComponentStream = new LazyComponentStream(x.File, x.SearchPattern, this.logger); + var lazyComponentStream = new LazyComponentStream(x.File, x.MatchedPattern, this.logger); return new ProcessRequest { ComponentStream = lazyComponentStream, @@ -280,14 +257,31 @@ private FileSystemInfo Transform(ref FileSystemEntry entry) return entry.ToFileSystemInfo(); } - private IObservable CreateDirectoryWalker(DirectoryInfo di, ExcludeDirectoryPredicate directoryExclusionPredicate, int minimumConnectionCount, IEnumerable filePatterns) + private IObservable Subscribe(DirectoryInfo root, string[] patterns, PatternMatchingUtility.CompiledMatcher compiled) { - return this.GetDirectoryScanner(di, new ConcurrentDictionary(), directoryExclusionPredicate, filePatterns, true).Replay() // Returns a replay subject which will republish anything found to new subscribers. - .AutoConnect(minimumConnectionCount); // Specifies that this connectable observable should start when minimumConnectionCount subscribe. + if (this.pendingScans.TryGetValue(root, out var scannerObservable)) + { + this.logger.LogDebug("Logging patterns {Patterns} for {Root}", string.Join(":", patterns), root.FullName); + + var inner = scannerObservable.Value.Where(fsi => + { + if (fsi is FileInfo fi) + { + return compiled.IsMatch(fi.Name.AsSpan()); + } + + return true; + }); + + return inner; + } + + throw new InvalidOperationException("Subscribe called without initializing scanner"); } - private bool MatchesAnyPattern(FileInfo fi, params string[] searchPatterns) + private IObservable CreateDirectoryWalker(DirectoryInfo di, ExcludeDirectoryPredicate directoryExclusionPredicate, int minimumConnectionCount, IEnumerable filePatterns) { - return searchPatterns != null && searchPatterns.Any(sp => this.pathUtilityService.MatchesPattern(sp, fi.Name)); + return this.GetDirectoryScanner(di, new ConcurrentDictionary(), directoryExclusionPredicate, filePatterns, true).Replay() // Returns a replay subject which will republish anything found to new subscribers. + .AutoConnect(minimumConnectionCount); // Specifies that this connectable observable should start when minimumConnectionCount subscribe. } } diff --git a/src/Microsoft.ComponentDetection.Common/PathUtilityService.cs b/src/Microsoft.ComponentDetection.Common/PathUtilityService.cs index f55228b40..a3c56c4af 100644 --- a/src/Microsoft.ComponentDetection.Common/PathUtilityService.cs +++ b/src/Microsoft.ComponentDetection.Common/PathUtilityService.cs @@ -3,7 +3,6 @@ namespace Microsoft.ComponentDetection.Common; using System; using System.IO; -using System.IO.Enumeration; using Microsoft.ComponentDetection.Contracts; using Microsoft.Extensions.Logging; @@ -22,21 +21,6 @@ internal class PathUtilityService : IPathUtilityService public PathUtilityService(ILogger logger) => this.logger = logger; - public static bool MatchesPattern(string searchPattern, ref FileSystemEntry fse) - { - if (searchPattern.StartsWith('*') && fse.FileName.EndsWith(searchPattern.AsSpan()[1..], StringComparison.OrdinalIgnoreCase)) - { - return true; - } - - if (searchPattern.EndsWith('*') && fse.FileName.StartsWith(searchPattern.AsSpan()[..^1], StringComparison.OrdinalIgnoreCase)) - { - return true; - } - - return fse.FileName.Equals(searchPattern.AsSpan(), StringComparison.OrdinalIgnoreCase); - } - public string GetParentDirectory(string path) => Path.GetDirectoryName(path); public bool IsFileBelowAnother(string aboveFilePath, string belowFilePath) @@ -48,21 +32,6 @@ public bool IsFileBelowAnother(string aboveFilePath, string belowFilePath) return (aboveDirectoryPath.Length != belowDirectoryPath.Length) && belowDirectoryPath.StartsWith(aboveDirectoryPath); } - public bool MatchesPattern(string searchPattern, string fileName) - { - if (searchPattern.StartsWith('*') && fileName.EndsWith(searchPattern[1..], StringComparison.OrdinalIgnoreCase)) - { - return true; - } - - if (searchPattern.EndsWith('*') && fileName.StartsWith(searchPattern[..^1], StringComparison.OrdinalIgnoreCase)) - { - return true; - } - - return searchPattern.Equals(fileName, StringComparison.OrdinalIgnoreCase); - } - public string ResolvePhysicalPath(string path) { var directoryInfo = new DirectoryInfo(path); @@ -75,6 +44,9 @@ public string ResolvePhysicalPath(string path) return fileInfo.Exists ? this.ResolvePathFromInfo(fileInfo) : null; } + [Obsolete("Use PatternMatchingUtility.MatchesPattern instead.")] + public bool MatchesPattern(string pattern, string fileName) => PatternMatchingUtility.MatchesPattern(pattern, fileName); + private string ResolvePathFromInfo(FileSystemInfo info) => info.LinkTarget ?? info.FullName; public string NormalizePath(string path) diff --git a/src/Microsoft.ComponentDetection.Common/PatternMatchingUtility.cs b/src/Microsoft.ComponentDetection.Common/PatternMatchingUtility.cs index c39cf7dd7..dc6cda863 100644 --- a/src/Microsoft.ComponentDetection.Common/PatternMatchingUtility.cs +++ b/src/Microsoft.ComponentDetection.Common/PatternMatchingUtility.cs @@ -2,38 +2,91 @@ namespace Microsoft.ComponentDetection.Common; using System; using System.Collections.Generic; +using System.IO.Enumeration; using System.Linq; public static class PatternMatchingUtility { - public delegate bool FilePatternMatcher(ReadOnlySpan span); + public static bool MatchesPattern(string pattern, string fileName) + { + ArgumentNullException.ThrowIfNull(pattern); + ArgumentNullException.ThrowIfNull(fileName); + + return IsPatternMatch(pattern, fileName.AsSpan()); + } - public static FilePatternMatcher GetFilePatternMatcher(IEnumerable patterns) + /// + /// Returns the first matching pattern for . + /// Earlier patterns in have higher priority when multiple match. + /// + /// The first matching pattern, or if no patterns match. + public static string? GetMatchingPattern(string fileName, IEnumerable patterns) { - var matchers = patterns.Select(pattern => pattern switch - { - _ when pattern.StartsWith('*') && pattern.EndsWith('*') => - pattern.Length <= 2 - ? _ => true - : span => span.Contains(pattern.AsSpan(1, pattern.Length - 2), StringComparison.Ordinal), - _ when pattern.StartsWith('*') => - span => span.EndsWith(pattern.AsSpan(1), StringComparison.Ordinal), - _ when pattern.EndsWith('*') => - span => span.StartsWith(pattern.AsSpan(0, pattern.Length - 1), StringComparison.Ordinal), - _ => span => span.Equals(pattern.AsSpan(), StringComparison.Ordinal), - }).ToList(); - - return span => + ArgumentNullException.ThrowIfNull(fileName); + ArgumentNullException.ThrowIfNull(patterns); + + return Compile(patterns).GetMatchingPattern(fileName.AsSpan()); + } + + public static CompiledMatcher Compile(IEnumerable patterns) + { + ArgumentNullException.ThrowIfNull(patterns); + return patterns is string[] array ? Compile(array) : new(patterns.ToArray()); + } + + public static CompiledMatcher Compile(string[] patterns) + { + ArgumentNullException.ThrowIfNull(patterns); + return new(patterns); + } + + private static string? GetFirstMatchingPattern(ReadOnlySpan fileName, string[] patterns) + { + foreach (var pattern in patterns) { - foreach (var matcher in matchers) + if (IsPatternMatch(pattern, fileName)) { - if (matcher(span)) - { - return true; - } + return pattern; } + } - return false; - }; + return null; + } + + private static bool IsPatternMatch(string pattern, ReadOnlySpan fileName) => + FileSystemName.MatchesSimpleExpression(pattern, fileName, ignoreCase: true); + + public sealed class CompiledMatcher + { + private readonly string[] patterns; + + public CompiledMatcher(IEnumerable patterns) + : this(patterns is string[] arr ? arr : (patterns ?? throw new ArgumentNullException(nameof(patterns))).ToArray()) + { + } + + internal CompiledMatcher(string[] patterns) + { + ArgumentNullException.ThrowIfNull(patterns); + this.patterns = (string[])patterns.Clone(); + ValidatePatternElements(this.patterns); + } + + public bool IsMatch(ReadOnlySpan fileName) => GetFirstMatchingPattern(fileName, this.patterns) is not null; + + /// + /// Returns the first matching pattern for . + /// Earlier patterns in the compiled set have higher priority when multiple match. + /// + /// The first matching pattern, or if no patterns match. + public string? GetMatchingPattern(ReadOnlySpan fileName) => GetFirstMatchingPattern(fileName, this.patterns); + + private static void ValidatePatternElements(string[] patterns) + { + foreach (var pattern in patterns) + { + ArgumentNullException.ThrowIfNull(pattern); + } + } } } diff --git a/src/Microsoft.ComponentDetection.Common/SafeFileEnumerable.cs b/src/Microsoft.ComponentDetection.Common/SafeFileEnumerable.cs index ebc7f020a..4d6c7c872 100644 --- a/src/Microsoft.ComponentDetection.Common/SafeFileEnumerable.cs +++ b/src/Microsoft.ComponentDetection.Common/SafeFileEnumerable.cs @@ -11,12 +11,12 @@ namespace Microsoft.ComponentDetection.Common; public class SafeFileEnumerable : IEnumerable { - private readonly IEnumerable searchPatterns; private readonly ExcludeDirectoryPredicate directoryExclusionPredicate; private readonly DirectoryInfo directory; private readonly IPathUtilityService pathUtilityService; private readonly bool recursivelyScanDirectories; private readonly Func fileMatchingPredicate; + private readonly PatternMatchingUtility.CompiledMatcher compiledMatcher; private readonly EnumerationOptions enumerationOptions; @@ -27,11 +27,11 @@ public SafeFileEnumerable(DirectoryInfo directory, IEnumerable searchPat { this.directory = directory; this.logger = logger; - this.searchPatterns = searchPatterns; this.directoryExclusionPredicate = directoryExclusionPredicate; this.recursivelyScanDirectories = recursivelyScanDirectories; this.pathUtilityService = pathUtilityService; this.enumeratedDirectories = previouslyEnumeratedDirectories; + this.compiledMatcher = PatternMatchingUtility.Compile(searchPatterns); this.enumerationOptions = new EnumerationOptions() { @@ -58,14 +58,10 @@ public IEnumerator GetEnumerator() throw new InvalidOperationException("Encountered directory when expecting a file"); } - var foundPattern = entry.FileName.ToString(); - foreach (var searchPattern in this.searchPatterns) - { - if (PathUtilityService.MatchesPattern(searchPattern, ref entry)) - { - foundPattern = searchPattern; - } - } + // Pattern priority is first-match-wins: earlier entries in searchPatterns + // are treated as higher priority when multiple patterns match. + var foundPattern = this.compiledMatcher.GetMatchingPattern(entry.FileName) + ?? entry.FileName.ToString(); return new MatchedFile() { File = fi, Pattern = foundPattern }; }, @@ -78,15 +74,7 @@ public IEnumerator GetEnumerator() return false; } - foreach (var searchPattern in this.searchPatterns) - { - if (PathUtilityService.MatchesPattern(searchPattern, ref entry)) - { - return true; - } - } - - return false; + return this.compiledMatcher.IsMatch(entry.FileName); }, ShouldRecursePredicate = (ref FileSystemEntry entry) => { diff --git a/src/Microsoft.ComponentDetection.Contracts/FileComponentDetector.cs b/src/Microsoft.ComponentDetection.Contracts/FileComponentDetector.cs index 5ee8cd542..dc7ea6c26 100644 --- a/src/Microsoft.ComponentDetection.Contracts/FileComponentDetector.cs +++ b/src/Microsoft.ComponentDetection.Contracts/FileComponentDetector.cs @@ -34,7 +34,10 @@ public abstract class FileComponentDetector : IComponentDetector /// public abstract string Id { get; } - /// Gets the search patterns used to produce the list of valid folders to scan. These patterns are evaluated with .Net's Directory.EnumerateFiles function. + /// + /// Gets the search patterns used to produce the list of valid files to scan. + /// The first pattern that matches a given file will be used to determine how that file is processed, so more specific patterns should be listed before more general ones. Wildcards are accepted. + /// public abstract IList SearchPatterns { get; } /// Gets the categories this detector is considered a member of. Used by the DetectorCategories arg to include detectors. diff --git a/src/Microsoft.ComponentDetection.Contracts/IPathUtilityService.cs b/src/Microsoft.ComponentDetection.Contracts/IPathUtilityService.cs index 8115fee40..aec21ce4a 100644 --- a/src/Microsoft.ComponentDetection.Contracts/IPathUtilityService.cs +++ b/src/Microsoft.ComponentDetection.Contracts/IPathUtilityService.cs @@ -1,6 +1,8 @@ #nullable disable namespace Microsoft.ComponentDetection.Contracts; +using System; + /// /// Wraps some common folder operations, shared across command line app and service. /// @@ -29,12 +31,13 @@ public interface IPathUtilityService bool IsFileBelowAnother(string aboveFilePath, string belowFilePath); /// - /// Returns true if file name matches pattern. + /// Matches file names to simple wildcard patterns. /// - /// Search pattern. - /// File name without directory. - /// Returns true if file name matches a pattern, otherwise false. - bool MatchesPattern(string searchPattern, string fileName); + /// Pattern to match against. + /// File name to evaluate. + /// Returns true when the file name matches the pattern, otherwise false. + [Obsolete("Use PatternMatchingUtility.MatchesPattern instead.")] + bool MatchesPattern(string pattern, string fileName); /// /// Normalize the path directory seperator to / on Unix systems and on Windows. diff --git a/test/Microsoft.ComponentDetection.Common.Tests/PathUtilityServiceTests.cs b/test/Microsoft.ComponentDetection.Common.Tests/PathUtilityServiceTests.cs index 95a976621..9e6ccc3b2 100644 --- a/test/Microsoft.ComponentDetection.Common.Tests/PathUtilityServiceTests.cs +++ b/test/Microsoft.ComponentDetection.Common.Tests/PathUtilityServiceTests.cs @@ -32,4 +32,15 @@ public void AbsolutePathShouldBeNormalized() normalizedPath.Should().Be(expectedPath); } + + [TestMethod] + public void MatchesPattern_IsForwardedToPatternMatchingUtility() + { + var service = new PathUtilityService(new NullLogger()); + +#pragma warning disable CS0618 // Type or member is obsolete + service.MatchesPattern("*.json", "package.json").Should().BeTrue(); + service.MatchesPattern("*.json", "package.yaml").Should().BeFalse(); +#pragma warning restore CS0618 // Type or member is obsolete + } } diff --git a/test/Microsoft.ComponentDetection.Common.Tests/PatternMatchingUtilityTests.cs b/test/Microsoft.ComponentDetection.Common.Tests/PatternMatchingUtilityTests.cs index b3bb016f3..4489ab43d 100644 --- a/test/Microsoft.ComponentDetection.Common.Tests/PatternMatchingUtilityTests.cs +++ b/test/Microsoft.ComponentDetection.Common.Tests/PatternMatchingUtilityTests.cs @@ -1,6 +1,7 @@ #nullable disable namespace Microsoft.ComponentDetection.Common.Tests; +using System; using AwesomeAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; @@ -15,6 +16,7 @@ public class PatternMatchingUtilityTests [DataRow("*test", "123test", true)] [DataRow("*test", "test123", false)] [DataRow("test", "test", true)] + [DataRow("test", "TEST", true)] [DataRow("test", "123test", false)] [DataRow("*test*", "123test456", true)] [DataRow("*test*", "test456", true)] @@ -22,31 +24,130 @@ public class PatternMatchingUtilityTests [DataRow("*test*", "test", true)] [DataRow("*test*", "tes", false)] [DataRow("*", "anything", true)] - [DataRow("*", "", true)] + [DataRow("*", "", false)] [DataRow("**", "anything", true)] - [DataRow("**", "", true)] + [DataRow("**", "", false)] + [DataRow("*.csproj", "MyProject.csproj", true)] + [DataRow("*.csproj", "MyProject.json", false)] + [DataRow("package.json", "package.json", true)] + [DataRow("package.json", "PACKAGE.JSON", true)] + [DataRow("dockerfile.*", "dockerfile.prod", true)] + [DataRow("dockerfile.*", "dockerfile", false)] + [DataRow("*.cargo-sbom.json", "test.cargo-sbom.json", true)] + [DataRow("*values*.yaml", "myvalues.yaml", true)] + [DataRow("*values*.yaml", "values.yaml", true)] + [DataRow("*values*.yaml", "values.json", false)] public void PatternMatcher_MatchesExpected(string pattern, string input, bool expected) { - var matcher = PatternMatchingUtility.GetFilePatternMatcher([pattern]); + var matcher = PatternMatchingUtility.Compile([pattern]); - matcher(input).Should().Be(expected); + matcher.IsMatch(input.AsSpan()).Should().Be(expected); } [TestMethod] public void PatternMatcher_MultiplePatterns_MatchesAny() { - var matcher = PatternMatchingUtility.GetFilePatternMatcher(["a*", "*b"]); + var matcher = PatternMatchingUtility.Compile(["a*", "*b"]); - matcher("apple").Should().BeTrue(); - matcher("crab").Should().BeTrue(); - matcher("middle").Should().BeFalse(); + matcher.IsMatch("apple".AsSpan()).Should().BeTrue(); + matcher.IsMatch("crab".AsSpan()).Should().BeTrue(); + matcher.IsMatch("middle".AsSpan()).Should().BeFalse(); } [TestMethod] public void PatternMatcher_EmptyPatterns_DoesNotThrow() { - var matcher = PatternMatchingUtility.GetFilePatternMatcher([]); + var matcher = PatternMatchingUtility.Compile([]); - matcher("anything").Should().BeFalse(); + matcher.IsMatch("anything".AsSpan()).Should().BeFalse(); + } + + [TestMethod] + public void MatchesPattern_SinglePattern() + { + PatternMatchingUtility.MatchesPattern("*.json", "package.json").Should().BeTrue(); + PatternMatchingUtility.MatchesPattern("*.json", "package.yaml").Should().BeFalse(); + } + + [TestMethod] + public void MatchesPattern_CaseInsensitive() + { + PatternMatchingUtility.MatchesPattern("package.json", "PACKAGE.JSON").Should().BeTrue(); + } + + [TestMethod] + public void GetMatchingPattern_ReturnsFirstMatch() + { + var result = PatternMatchingUtility.GetMatchingPattern("package.json", ["*.json", "package.json"]); + result.Should().Be("*.json"); + } + + [TestMethod] + public void GetMatchingPattern_ReturnsNullWhenNoMatch() + { + var result = PatternMatchingUtility.GetMatchingPattern("package.yaml", ["*.json", "*.xml"]); + result.Should().BeNull(); + } + + [TestMethod] + public void CompiledMatcher_GetMatchingPattern_Works() + { + var compiled = PatternMatchingUtility.Compile(["*.json", "*.xml", "Cargo.toml"]); + + compiled.GetMatchingPattern("package.json").Should().Be("*.json"); + compiled.GetMatchingPattern("pom.xml").Should().Be("*.xml"); + compiled.GetMatchingPattern("Cargo.toml").Should().Be("Cargo.toml"); + compiled.GetMatchingPattern("README.md").Should().BeNull(); + } + + [TestMethod] + public void CompiledMatcher_IsMatch_SpanBased() + { + var compiled = PatternMatchingUtility.Compile(["package.json", "*.lock"]); + + compiled.IsMatch("package.json".AsSpan()).Should().BeTrue(); + compiled.IsMatch("yarn.lock".AsSpan()).Should().BeTrue(); + compiled.IsMatch("README.md".AsSpan()).Should().BeFalse(); + } + + [TestMethod] + public void MatchesPattern_ThrowsForNullInputs() + { + Action nullPattern = () => PatternMatchingUtility.MatchesPattern(null, "package.json"); + Action nullFileName = () => PatternMatchingUtility.MatchesPattern("*.json", null); + + nullPattern.Should().ThrowExactly(); + nullFileName.Should().ThrowExactly(); + } + + [TestMethod] + public void GetMatchingPattern_ThrowsForNullInputs() + { + Action nullFileName = () => PatternMatchingUtility.GetMatchingPattern(null, ["*.json"]); + Action nullPatterns = () => PatternMatchingUtility.GetMatchingPattern("package.json", null); + + nullFileName.Should().ThrowExactly(); + nullPatterns.Should().ThrowExactly(); + } + + [TestMethod] + public void Compile_ThrowsForNullPatterns() + { + Action action = () => PatternMatchingUtility.Compile(null); + action.Should().ThrowExactly(); + } + + [TestMethod] + public void Compile_ThrowsForNullPatternElement() + { + Action action = () => PatternMatchingUtility.Compile(["*.json", null, "*.xml"]); + action.Should().ThrowExactly(); + } + + [TestMethod] + public void GetMatchingPattern_ThrowsForNullPatternElement() + { + Action action = () => PatternMatchingUtility.GetMatchingPattern("package.json", ["*.xml", null, "*.yaml"]); + action.Should().ThrowExactly(); } } diff --git a/test/Microsoft.ComponentDetection.Common.Tests/SafeFileEnumerableTests.cs b/test/Microsoft.ComponentDetection.Common.Tests/SafeFileEnumerableTests.cs index f0fc16535..7180fb31c 100644 --- a/test/Microsoft.ComponentDetection.Common.Tests/SafeFileEnumerableTests.cs +++ b/test/Microsoft.ComponentDetection.Common.Tests/SafeFileEnumerableTests.cs @@ -50,7 +50,6 @@ public void GetEnumerator_WorksOverExpectedFiles() IEnumerable searchPatterns = [name]; this.pathUtilityServiceMock.Setup(x => x.ResolvePhysicalPath(It.IsAny())).Returns((s) => s); - this.pathUtilityServiceMock.Setup(x => x.MatchesPattern(name, name)).Returns(true); var enumerable = new SafeFileEnumerable(new DirectoryInfo(this.temporaryDirectory), searchPatterns, this.loggerMock.Object, this.pathUtilityServiceMock.Object, (directoryName, span) => false, true); @@ -77,8 +76,6 @@ public void GetEnumerator_IgnoresSubDirectories() IEnumerable searchPatterns = [name]; - this.pathUtilityServiceMock.Setup(x => x.MatchesPattern(name, name)).Returns(true); - var enumerable = new SafeFileEnumerable(new DirectoryInfo(this.temporaryDirectory), searchPatterns, this.loggerMock.Object, this.pathUtilityServiceMock.Object, (directoryName, span) => false, false); var filesFound = 0; diff --git a/test/Microsoft.ComponentDetection.TestsUtilities/DetectorTestUtilityBuilder.cs b/test/Microsoft.ComponentDetection.TestsUtilities/DetectorTestUtilityBuilder.cs index 1dc9f1b69..9781ecb13 100644 --- a/test/Microsoft.ComponentDetection.TestsUtilities/DetectorTestUtilityBuilder.cs +++ b/test/Microsoft.ComponentDetection.TestsUtilities/DetectorTestUtilityBuilder.cs @@ -125,9 +125,7 @@ private static IComponentStream CreateComponentStreamForFile(string pattern, str private static string FindMatchingPattern(string fileName, IEnumerable searchPatterns) { - var foundPattern = searchPatterns.FirstOrDefault(searchPattern => new PathUtilityService(null).MatchesPattern(searchPattern, fileName)); - - return foundPattern ?? fileName; + return PatternMatchingUtility.GetMatchingPattern(fileName, searchPatterns) ?? fileName; } private ProcessRequest CreateProcessRequest(string pattern, string filePath, Stream content) =>