Skip to content

Commit 2d5ecda

Browse files
authored
Optionally propagate source file root to Detector's scan request (#1353)
1 parent bdfdcd2 commit 2d5ecda

File tree

4 files changed

+48
-7
lines changed

4 files changed

+48
-7
lines changed

src/Microsoft.ComponentDetection.Contracts/ScanRequest.cs

+8-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ public class ScanRequest
2020
/// <param name="componentRecorder">Detector component recorder.</param>
2121
/// <param name="maxThreads">Max number of threads to use for detection.</param>
2222
/// <param name="cleanupCreatedFiles">Whether or not to cleanup files that are created during detection.</param>
23-
public ScanRequest(DirectoryInfo sourceDirectory, ExcludeDirectoryPredicate directoryExclusionPredicate, ILogger logger, IDictionary<string, string> detectorArgs, IEnumerable<string> imagesToScan, IComponentRecorder componentRecorder, int maxThreads = 5, bool cleanupCreatedFiles = true)
23+
/// <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>
24+
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)
2425
{
2526
this.SourceDirectory = sourceDirectory;
2627
this.DirectoryExclusionPredicate = directoryExclusionPredicate;
@@ -29,13 +30,19 @@ public ScanRequest(DirectoryInfo sourceDirectory, ExcludeDirectoryPredicate dire
2930
this.ComponentRecorder = componentRecorder;
3031
this.MaxThreads = maxThreads;
3132
this.CleanupCreatedFiles = cleanupCreatedFiles;
33+
this.SourceFileRoot = sourceFileRoot;
3234
}
3335

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

41+
/// <summary>
42+
/// Directory where source files can be found.
43+
/// </summary>
44+
public DirectoryInfo SourceFileRoot { get; private set; }
45+
3946
/// <summary>
4047
/// Gets a predicate which evaluates directories, if the predicate returns true the directory will be excluded.
4148
/// </summary>

src/Microsoft.ComponentDetection.Orchestrator/Services/DetectorProcessingService.cs

+2-1
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,8 @@ public async Task<DetectorProcessingResult> ProcessDetectorsAsync(
9999
settings.DockerImagesToScan,
100100
componentRecorder,
101101
settings.MaxDetectionThreads ?? DefaultMaxDetectionThreads,
102-
settings.CleanupCreatedFiles ?? true),
102+
settings.CleanupCreatedFiles ?? true,
103+
settings.SourceFileRoot),
103104
cancellationToken),
104105
isExperimentalDetector,
105106
record);

test/Microsoft.ComponentDetection.Orchestrator.Tests/Services/DetectorProcessingServiceTests.cs

+36-4
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,38 @@ public async Task ProcessDetectorsAsync_HappyPathReturnsDetectedComponentsAsync(
102102
results.ResultCode.Should().Be(ProcessingResultCode.Success);
103103
}
104104

105+
[TestMethod]
106+
public async Task ProcessDetectorsAsync_WithSourceFileLocationSetReturnsDetectedComponentsAsync()
107+
{
108+
var defaultArgs = new ScanSettings
109+
{
110+
SourceDirectory = new DirectoryInfo(Path.Combine(Environment.CurrentDirectory, "SourceDirectory")),
111+
DetectorArgs = new Dictionary<string, string>(),
112+
SourceFileRoot = new DirectoryInfo(Path.Combine(Environment.CurrentDirectory, "SourceDirectory", "SourceFileRoot")),
113+
};
114+
115+
var componentDetectorMock1 = this.SetupFileDetectorMock("firstFileDetectorId", sourceDirectory: defaultArgs.SourceDirectory);
116+
var componentDetectorMock2 = this.SetupFileDetectorMock("secondFileDetectorId", sourceDirectory: defaultArgs.SourceDirectory);
117+
118+
this.detectorsToUse =
119+
[
120+
componentDetectorMock1.Object, componentDetectorMock2.Object,
121+
];
122+
123+
var results = await this.serviceUnderTest.ProcessDetectorsAsync(defaultArgs, this.detectorsToUse, new DetectorRestrictions());
124+
125+
componentDetectorMock1.Verify(x => x.ExecuteDetectorAsync(It.Is<ScanRequest>(request => request.SourceDirectory == defaultArgs.SourceDirectory && request.SourceFileRoot == defaultArgs.SourceFileRoot), It.IsAny<CancellationToken>()), Times.Once);
126+
componentDetectorMock2.Verify(x => x.ExecuteDetectorAsync(It.Is<ScanRequest>(request => request.SourceDirectory == defaultArgs.SourceDirectory && request.SourceFileRoot == defaultArgs.SourceFileRoot), It.IsAny<CancellationToken>()), Times.Once);
127+
128+
this.ValidateExpectedComponents(results, this.detectorsToUse);
129+
this.GetDiscoveredComponentsFromDetectorProcessingResult(results).FirstOrDefault(x => x.Component?.Type == ComponentType.Npm).Component
130+
.Should().Be(this.componentDictionary[componentDetectorMock1.Object.Id].Component);
131+
this.GetDiscoveredComponentsFromDetectorProcessingResult(results).FirstOrDefault(x => x.Component?.Type == ComponentType.NuGet).Component
132+
.Should().Be(this.componentDictionary[componentDetectorMock2.Object.Id].Component);
133+
134+
results.ResultCode.Should().Be(ProcessingResultCode.Success);
135+
}
136+
105137
[TestMethod]
106138
public async Task ProcessDetectorsAsync_NullDetectedComponentsReturnIsCoalescedAsync()
107139
{
@@ -555,21 +587,21 @@ public async Task ProcessDetectorsAsync_InitializesExperimentsAsync()
555587
this.experimentServiceMock.Verify(x => x.InitializeAsync(), Times.Once);
556588
}
557589

558-
private Mock<FileComponentDetector> SetupFileDetectorMock(string id)
590+
private Mock<FileComponentDetector> SetupFileDetectorMock(string id, DirectoryInfo sourceDirectory = null)
559591
{
560592
var mockFileDetector = new Mock<FileComponentDetector>();
561593
mockFileDetector.SetupAllProperties();
562594
mockFileDetector.SetupGet(x => x.Id).Returns(id);
563595

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

567599
var expectedResult = this.ExpectedResultForDetector(id);
568600

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

572-
mockFileDetector.Setup(x => x.ExecuteDetectorAsync(It.Is<ScanRequest>(request => request.SourceDirectory == DefaultArgs.SourceDirectory && request.ComponentRecorder != null), It.IsAny<CancellationToken>())).ReturnsAsync(
604+
mockFileDetector.Setup(x => x.ExecuteDetectorAsync(It.Is<ScanRequest>(request => request.SourceDirectory == sourceDirectory && request.ComponentRecorder != null), It.IsAny<CancellationToken>())).ReturnsAsync(
573605
(ScanRequest request, CancellationToken cancellationToken) =>
574606
{
575607
this.FillComponentRecorder(request.ComponentRecorder, id);

test/Microsoft.ComponentDetection.TestsUtilities/DetectorTestUtilityBuilder.cs

+2-1
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,8 @@ public DetectorTestUtilityBuilder<T> WithScanRequest(ScanRequest scanRequest)
9595
null,
9696
new Dictionary<string, string>(),
9797
null,
98-
this.componentRecorder);
98+
this.componentRecorder,
99+
sourceFileRoot: new DirectoryInfo(Path.GetTempPath()));
99100
}
100101
else
101102
{

0 commit comments

Comments
 (0)