diff --git a/src/Microsoft.ComponentDetection.Orchestrator/Experiments/Configs/IExperimentConfiguration.cs b/src/Microsoft.ComponentDetection.Orchestrator/Experiments/Configs/IExperimentConfiguration.cs
index 49a4b3f48..cbfdf8851 100644
--- a/src/Microsoft.ComponentDetection.Orchestrator/Experiments/Configs/IExperimentConfiguration.cs
+++ b/src/Microsoft.ComponentDetection.Orchestrator/Experiments/Configs/IExperimentConfiguration.cs
@@ -1,5 +1,6 @@
namespace Microsoft.ComponentDetection.Orchestrator.Experiments.Configs;
+using System.Threading.Tasks;
using Microsoft.ComponentDetection.Contracts;
///
@@ -15,6 +16,12 @@ public interface IExperimentConfiguration
///
string Name { get; }
+ ///
+ /// Initializes the experiment configuration.
+ ///
+ /// A representing the asynchronous operation.
+ Task InitAsync() => Task.CompletedTask;
+
///
/// Specifies if the detector is in the control group.
///
diff --git a/src/Microsoft.ComponentDetection.Orchestrator/Experiments/Configs/RustCliDetectorExperiment.cs b/src/Microsoft.ComponentDetection.Orchestrator/Experiments/Configs/RustCliDetectorExperiment.cs
index e17e71696..3ec089917 100644
--- a/src/Microsoft.ComponentDetection.Orchestrator/Experiments/Configs/RustCliDetectorExperiment.cs
+++ b/src/Microsoft.ComponentDetection.Orchestrator/Experiments/Configs/RustCliDetectorExperiment.cs
@@ -1,5 +1,6 @@
namespace Microsoft.ComponentDetection.Orchestrator.Experiments.Configs;
+using System.Threading.Tasks;
using Microsoft.ComponentDetection.Contracts;
using Microsoft.ComponentDetection.Detectors.Rust;
@@ -8,6 +9,15 @@ namespace Microsoft.ComponentDetection.Orchestrator.Experiments.Configs;
///
public class RustCliDetectorExperiment : IExperimentConfiguration
{
+ private readonly ICommandLineInvocationService commandLineInvocationService;
+ private bool cargoCliAvailable;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The command line invocation service.
+ public RustCliDetectorExperiment(ICommandLineInvocationService commandLineInvocationService) => this.commandLineInvocationService = commandLineInvocationService;
+
///
public string Name => "RustCliDetector";
@@ -18,5 +28,8 @@ public class RustCliDetectorExperiment : IExperimentConfiguration
public bool IsInExperimentGroup(IComponentDetector componentDetector) => componentDetector is RustCliDetector;
///
- public bool ShouldRecord(IComponentDetector componentDetector, int numComponents) => true;
+ public bool ShouldRecord(IComponentDetector componentDetector, int numComponents) => this.cargoCliAvailable;
+
+ ///
+ public async Task InitAsync() => this.cargoCliAvailable = await this.commandLineInvocationService.CanCommandBeLocatedAsync("cargo", null);
}
diff --git a/src/Microsoft.ComponentDetection.Orchestrator/Experiments/ExperimentService.cs b/src/Microsoft.ComponentDetection.Orchestrator/Experiments/ExperimentService.cs
index 68939cd28..30bcc5c44 100644
--- a/src/Microsoft.ComponentDetection.Orchestrator/Experiments/ExperimentService.cs
+++ b/src/Microsoft.ComponentDetection.Orchestrator/Experiments/ExperimentService.cs
@@ -43,6 +43,23 @@ public ExperimentService(
this.logger = logger;
}
+ ///
+ public async Task InitializeAsync()
+ {
+ foreach (var config in this.experiments.Keys)
+ {
+ try
+ {
+ await config.InitAsync();
+ }
+ catch (Exception e)
+ {
+ this.logger.LogWarning(e, "Failed to initialize experiment {Experiment}, skipping it", config.Name);
+ this.experiments.TryRemove(config, out _);
+ }
+ }
+ }
+
///
public void RecordDetectorRun(
IComponentDetector detector,
diff --git a/src/Microsoft.ComponentDetection.Orchestrator/Experiments/IExperimentService.cs b/src/Microsoft.ComponentDetection.Orchestrator/Experiments/IExperimentService.cs
index 4477abbbc..9e00a2c9c 100644
--- a/src/Microsoft.ComponentDetection.Orchestrator/Experiments/IExperimentService.cs
+++ b/src/Microsoft.ComponentDetection.Orchestrator/Experiments/IExperimentService.cs
@@ -11,6 +11,12 @@ namespace Microsoft.ComponentDetection.Orchestrator.Experiments;
///
public interface IExperimentService
{
+ ///
+ /// Initializes the experiment services by preparing the experiment configurations.
+ ///
+ /// A representing the asynchronous operation.
+ Task InitializeAsync();
+
///
/// Records the results of a detector execution and processes the results for any active experiments.
///
diff --git a/src/Microsoft.ComponentDetection.Orchestrator/Services/DetectorProcessingService.cs b/src/Microsoft.ComponentDetection.Orchestrator/Services/DetectorProcessingService.cs
index 658de9a71..93e17ea44 100644
--- a/src/Microsoft.ComponentDetection.Orchestrator/Services/DetectorProcessingService.cs
+++ b/src/Microsoft.ComponentDetection.Orchestrator/Services/DetectorProcessingService.cs
@@ -55,6 +55,7 @@ public async Task ProcessDetectorsAsync(
? this.GenerateDirectoryExclusionPredicate(settings.SourceDirectory.ToString(), settings.DirectoryExclusionList, settings.DirectoryExclusionListObsolete, allowWindowsPaths: false, ignoreCase: false)
: this.GenerateDirectoryExclusionPredicate(settings.SourceDirectory.ToString(), settings.DirectoryExclusionList, settings.DirectoryExclusionListObsolete, allowWindowsPaths: true, ignoreCase: true);
+ await this.experimentService.InitializeAsync();
this.experimentService.RemoveUnwantedExperimentsbyDetectors(detectorRestrictions.DisabledDetectors);
IEnumerable> scanTasks = detectors
diff --git a/test/Microsoft.ComponentDetection.Orchestrator.Tests/Experiments/ExperimentServiceTests.cs b/test/Microsoft.ComponentDetection.Orchestrator.Tests/Experiments/ExperimentServiceTests.cs
index 3c5df555c..c867f8ddb 100644
--- a/test/Microsoft.ComponentDetection.Orchestrator.Tests/Experiments/ExperimentServiceTests.cs
+++ b/test/Microsoft.ComponentDetection.Orchestrator.Tests/Experiments/ExperimentServiceTests.cs
@@ -1,5 +1,6 @@
namespace Microsoft.ComponentDetection.Orchestrator.Tests.Experiments;
+using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
@@ -279,9 +280,9 @@ public async Task RecordDetectorRun_CheckUnwantedDetectors_RemoveExperimentAsync
var detectorList = new List
{
new NuGetComponentDetector(
- new Mock().Object,
- new Mock().Object,
- new Mock>().Object), this.detectorMock.Object,
+ new Mock().Object,
+ new Mock().Object,
+ new Mock>().Object), this.detectorMock.Object,
};
service.RemoveUnwantedExperimentsbyDetectors(detectorList);
@@ -310,9 +311,9 @@ public async Task RecordDetectorRun_CheckUnwantedDetectors_KeepExperimentAsync()
var detectorList = new List
{
new NuGetComponentDetector(
- new Mock().Object,
- new Mock().Object,
- new Mock>().Object),
+ new Mock().Object,
+ new Mock().Object,
+ new Mock>().Object),
};
service.RemoveUnwantedExperimentsbyDetectors(detectorList);
@@ -325,4 +326,35 @@ public async Task RecordDetectorRun_CheckUnwantedDetectors_KeepExperimentAsync()
x => x.ProcessExperimentAsync(this.experimentConfigMock.Object, It.IsAny()),
Times.Once());
}
+
+ [TestMethod]
+ public async Task InitializeAsync_InitsConfigsAsync()
+ {
+ var service = new ExperimentService(
+ new[] { this.experimentConfigMock.Object },
+ new[] { this.experimentProcessorMock.Object },
+ this.graphTranslationServiceMock.Object,
+ this.loggerMock.Object);
+
+ await service.InitializeAsync();
+
+ this.experimentConfigMock.Verify(x => x.InitAsync(), Times.Once());
+ }
+
+ [TestMethod]
+ public async Task InitializeAsync_SwallowsExceptionsAsync()
+ {
+ this.experimentConfigMock.Setup(x => x.InitAsync()).ThrowsAsync(new InvalidOperationException());
+
+ var service = new ExperimentService(
+ new[] { this.experimentConfigMock.Object },
+ new[] { this.experimentProcessorMock.Object },
+ this.graphTranslationServiceMock.Object,
+ this.loggerMock.Object);
+
+ var action = async () => await service.InitializeAsync();
+
+ await action.Should().NotThrowAsync();
+ this.experimentConfigMock.Verify(x => x.InitAsync(), Times.Once());
+ }
}
diff --git a/test/Microsoft.ComponentDetection.Orchestrator.Tests/Services/DetectorProcessingServiceTests.cs b/test/Microsoft.ComponentDetection.Orchestrator.Tests/Services/DetectorProcessingServiceTests.cs
index 936384915..417570e8c 100644
--- a/test/Microsoft.ComponentDetection.Orchestrator.Tests/Services/DetectorProcessingServiceTests.cs
+++ b/test/Microsoft.ComponentDetection.Orchestrator.Tests/Services/DetectorProcessingServiceTests.cs
@@ -321,17 +321,17 @@ public async Task ProcessDetectorsAsync_DirectoryExclusionPredicateWorksAsExpect
public void GenerateDirectoryExclusionPredicate_IgnoreCaseAndAllowWindowsPathsWorksAsExpected()
{
/*
- * We can't test a scenario like:
- *
- * SourceDirectory = /Some/Source/Directory
- * DirectoryExclusionList = *Some/*
- * allowWindowsPath = false
- *
- * and expect to exclude the directory, because when
- * we pass the SourceDirectory path to DirectoryInfo and we are running the test on Windows,
- * DirectoryInfo transalate it to C:\\Some\Source\Directory
- * making the test fail
- */
+ * We can't test a scenario like:
+ *
+ * SourceDirectory = /Some/Source/Directory
+ * DirectoryExclusionList = *Some/*
+ * allowWindowsPath = false
+ *
+ * and expect to exclude the directory, because when
+ * we pass the SourceDirectory path to DirectoryInfo and we are running the test on Windows,
+ * DirectoryInfo transalate it to C:\\Some\Source\Directory
+ * making the test fail
+ */
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
@@ -534,6 +534,19 @@ public async Task ProcessDetectorsAsync_RecordsDetectorRunsAsync()
Times.Once());
}
+ [TestMethod]
+ public async Task ProcessDetectorsAsync_InitializesExperimentsAsync()
+ {
+ this.detectorsToUse = new[]
+ {
+ this.firstFileComponentDetectorMock.Object, this.secondFileComponentDetectorMock.Object,
+ };
+
+ await this.serviceUnderTest.ProcessDetectorsAsync(DefaultArgs, this.detectorsToUse, new DetectorRestrictions());
+
+ this.experimentServiceMock.Verify(x => x.InitializeAsync(), Times.Once);
+ }
+
private Mock SetupFileDetectorMock(string id)
{
var mockFileDetector = new Mock();