Skip to content

Commit

Permalink
Optionally propagate source file root to Detector's scan request (#1353)
Browse files Browse the repository at this point in the history
  • Loading branch information
grvillic authored Jan 27, 2025
1 parent bdfdcd2 commit 2d5ecda
Show file tree
Hide file tree
Showing 4 changed files with 48 additions and 7 deletions.
9 changes: 8 additions & 1 deletion src/Microsoft.ComponentDetection.Contracts/ScanRequest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ public class ScanRequest
/// <param name="componentRecorder">Detector component recorder.</param>
/// <param name="maxThreads">Max number of threads to use for detection.</param>
/// <param name="cleanupCreatedFiles">Whether or not to cleanup files that are created during detection.</param>
public ScanRequest(DirectoryInfo sourceDirectory, ExcludeDirectoryPredicate directoryExclusionPredicate, ILogger logger, IDictionary<string, string> detectorArgs, IEnumerable<string> imagesToScan, IComponentRecorder componentRecorder, int maxThreads = 5, bool cleanupCreatedFiles = true)
/// <param name="sourceFileRoot">Directory where source files can be found. In most scenarios this will be the same as <paramref name="sourceDirectory"/> but source code can be a different folder.</param>
public ScanRequest(DirectoryInfo sourceDirectory, ExcludeDirectoryPredicate directoryExclusionPredicate, ILogger logger, IDictionary<string, string> detectorArgs, IEnumerable<string> imagesToScan, IComponentRecorder componentRecorder, int maxThreads = 5, bool cleanupCreatedFiles = true, DirectoryInfo sourceFileRoot = null)
{
this.SourceDirectory = sourceDirectory;
this.DirectoryExclusionPredicate = directoryExclusionPredicate;
Expand All @@ -29,13 +30,19 @@ public ScanRequest(DirectoryInfo sourceDirectory, ExcludeDirectoryPredicate dire
this.ComponentRecorder = componentRecorder;
this.MaxThreads = maxThreads;
this.CleanupCreatedFiles = cleanupCreatedFiles;
this.SourceFileRoot = sourceFileRoot;
}

/// <summary>
/// Gets the source directory to consider the working directory for the detection operation.
/// </summary>
public DirectoryInfo SourceDirectory { get; private set; }

/// <summary>
/// Directory where source files can be found.
/// </summary>
public DirectoryInfo SourceFileRoot { get; private set; }

/// <summary>
/// Gets a predicate which evaluates directories, if the predicate returns true the directory will be excluded.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,8 @@ public async Task<DetectorProcessingResult> ProcessDetectorsAsync(
settings.DockerImagesToScan,
componentRecorder,
settings.MaxDetectionThreads ?? DefaultMaxDetectionThreads,
settings.CleanupCreatedFiles ?? true),
settings.CleanupCreatedFiles ?? true,
settings.SourceFileRoot),
cancellationToken),
isExperimentalDetector,
record);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,38 @@ public async Task ProcessDetectorsAsync_HappyPathReturnsDetectedComponentsAsync(
results.ResultCode.Should().Be(ProcessingResultCode.Success);
}

[TestMethod]
public async Task ProcessDetectorsAsync_WithSourceFileLocationSetReturnsDetectedComponentsAsync()
{
var defaultArgs = new ScanSettings
{
SourceDirectory = new DirectoryInfo(Path.Combine(Environment.CurrentDirectory, "SourceDirectory")),
DetectorArgs = new Dictionary<string, string>(),
SourceFileRoot = new DirectoryInfo(Path.Combine(Environment.CurrentDirectory, "SourceDirectory", "SourceFileRoot")),
};

var componentDetectorMock1 = this.SetupFileDetectorMock("firstFileDetectorId", sourceDirectory: defaultArgs.SourceDirectory);
var componentDetectorMock2 = this.SetupFileDetectorMock("secondFileDetectorId", sourceDirectory: defaultArgs.SourceDirectory);

this.detectorsToUse =
[
componentDetectorMock1.Object, componentDetectorMock2.Object,
];

var results = await this.serviceUnderTest.ProcessDetectorsAsync(defaultArgs, this.detectorsToUse, new DetectorRestrictions());

componentDetectorMock1.Verify(x => x.ExecuteDetectorAsync(It.Is<ScanRequest>(request => request.SourceDirectory == defaultArgs.SourceDirectory && request.SourceFileRoot == defaultArgs.SourceFileRoot), It.IsAny<CancellationToken>()), Times.Once);
componentDetectorMock2.Verify(x => x.ExecuteDetectorAsync(It.Is<ScanRequest>(request => request.SourceDirectory == defaultArgs.SourceDirectory && request.SourceFileRoot == defaultArgs.SourceFileRoot), It.IsAny<CancellationToken>()), Times.Once);

this.ValidateExpectedComponents(results, this.detectorsToUse);
this.GetDiscoveredComponentsFromDetectorProcessingResult(results).FirstOrDefault(x => x.Component?.Type == ComponentType.Npm).Component
.Should().Be(this.componentDictionary[componentDetectorMock1.Object.Id].Component);
this.GetDiscoveredComponentsFromDetectorProcessingResult(results).FirstOrDefault(x => x.Component?.Type == ComponentType.NuGet).Component
.Should().Be(this.componentDictionary[componentDetectorMock2.Object.Id].Component);

results.ResultCode.Should().Be(ProcessingResultCode.Success);
}

[TestMethod]
public async Task ProcessDetectorsAsync_NullDetectedComponentsReturnIsCoalescedAsync()
{
Expand Down Expand Up @@ -555,21 +587,21 @@ public async Task ProcessDetectorsAsync_InitializesExperimentsAsync()
this.experimentServiceMock.Verify(x => x.InitializeAsync(), Times.Once);
}

private Mock<FileComponentDetector> SetupFileDetectorMock(string id)
private Mock<FileComponentDetector> SetupFileDetectorMock(string id, DirectoryInfo sourceDirectory = null)
{
var mockFileDetector = new Mock<FileComponentDetector>();
mockFileDetector.SetupAllProperties();
mockFileDetector.SetupGet(x => x.Id).Returns(id);

var sourceDirectory = new DirectoryInfo(Path.Combine(Environment.CurrentDirectory, "Some", "Source", "Directory"));
sourceDirectory ??= DefaultArgs.SourceDirectory;
this.componentDictionary.Should().ContainKey(id, $"MockDetector id:{id}, should be in mock dictionary");

var expectedResult = this.ExpectedResultForDetector(id);

mockFileDetector.Setup(x => x.ExecuteDetectorAsync(It.Is<ScanRequest>(request => request.SourceDirectory == DefaultArgs.SourceDirectory && request.ComponentRecorder != null), It.IsAny<CancellationToken>())).Returns(
mockFileDetector.Setup(x => x.ExecuteDetectorAsync(It.Is<ScanRequest>(request => request.SourceDirectory == sourceDirectory && request.ComponentRecorder != null), It.IsAny<CancellationToken>())).Returns(
(ScanRequest request, CancellationToken cancellationToken) => mockFileDetector.Object.ExecuteDetectorAsync(request, cancellationToken)).Verifiable();

mockFileDetector.Setup(x => x.ExecuteDetectorAsync(It.Is<ScanRequest>(request => request.SourceDirectory == DefaultArgs.SourceDirectory && request.ComponentRecorder != null), It.IsAny<CancellationToken>())).ReturnsAsync(
mockFileDetector.Setup(x => x.ExecuteDetectorAsync(It.Is<ScanRequest>(request => request.SourceDirectory == sourceDirectory && request.ComponentRecorder != null), It.IsAny<CancellationToken>())).ReturnsAsync(
(ScanRequest request, CancellationToken cancellationToken) =>
{
this.FillComponentRecorder(request.ComponentRecorder, id);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,8 @@ public DetectorTestUtilityBuilder<T> WithScanRequest(ScanRequest scanRequest)
null,
new Dictionary<string, string>(),
null,
this.componentRecorder);
this.componentRecorder,
sourceFileRoot: new DirectoryInfo(Path.GetTempPath()));
}
else
{
Expand Down

0 comments on commit 2d5ecda

Please sign in to comment.