feat(experiments): record rust cli if `cargo` exists (#810)

This commit is contained in:
Justin Perez 2023-09-27 11:56:05 -07:00 коммит произвёл GitHub
Родитель 05cf44ae7a
Коммит c0fe4af38e
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
7 изменённых файлов: 107 добавлений и 18 удалений

Просмотреть файл

@ -1,5 +1,6 @@
namespace Microsoft.ComponentDetection.Orchestrator.Experiments.Configs;
using System.Threading.Tasks;
using Microsoft.ComponentDetection.Contracts;
/// <summary>
@ -15,6 +16,12 @@ public interface IExperimentConfiguration
/// </summary>
string Name { get; }
/// <summary>
/// Initializes the experiment configuration.
/// </summary>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
Task InitAsync() => Task.CompletedTask;
/// <summary>
/// Specifies if the detector is in the control group.
/// </summary>

Просмотреть файл

@ -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 @@ using Microsoft.ComponentDetection.Detectors.Rust;
/// </summary>
public class RustCliDetectorExperiment : IExperimentConfiguration
{
private readonly ICommandLineInvocationService commandLineInvocationService;
private bool cargoCliAvailable;
/// <summary>
/// Initializes a new instance of the <see cref="RustCliDetectorExperiment"/> class.
/// </summary>
/// <param name="commandLineInvocationService">The command line invocation service.</param>
public RustCliDetectorExperiment(ICommandLineInvocationService commandLineInvocationService) => this.commandLineInvocationService = commandLineInvocationService;
/// <inheritdoc />
public string Name => "RustCliDetector";
@ -18,5 +28,8 @@ public class RustCliDetectorExperiment : IExperimentConfiguration
public bool IsInExperimentGroup(IComponentDetector componentDetector) => componentDetector is RustCliDetector;
/// <inheritdoc />
public bool ShouldRecord(IComponentDetector componentDetector, int numComponents) => true;
public bool ShouldRecord(IComponentDetector componentDetector, int numComponents) => this.cargoCliAvailable;
/// <inheritdoc />
public async Task InitAsync() => this.cargoCliAvailable = await this.commandLineInvocationService.CanCommandBeLocatedAsync("cargo", null);
}

Просмотреть файл

@ -43,6 +43,23 @@ public class ExperimentService : IExperimentService
this.logger = logger;
}
/// <inheritdoc />
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 _);
}
}
}
/// <inheritdoc />
public void RecordDetectorRun(
IComponentDetector detector,

Просмотреть файл

@ -11,6 +11,12 @@ using Microsoft.ComponentDetection.Orchestrator.Commands;
/// </summary>
public interface IExperimentService
{
/// <summary>
/// Initializes the experiment services by preparing the experiment configurations.
/// </summary>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
Task InitializeAsync();
/// <summary>
/// Records the results of a detector execution and processes the results for any active experiments.
/// </summary>

Просмотреть файл

@ -55,6 +55,7 @@ public class DetectorProcessingService : IDetectorProcessingService
? 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<Task<(IndividualDetectorScanResult, ComponentRecorder, IComponentDetector)>> scanTasks = detectors

Просмотреть файл

@ -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 class ExperimentServiceTests
var detectorList = new List<IComponentDetector>
{
new NuGetComponentDetector(
new Mock<IComponentStreamEnumerableFactory>().Object,
new Mock<IObservableDirectoryWalkerFactory>().Object,
new Mock<ILogger<NuGetComponentDetector>>().Object), this.detectorMock.Object,
new Mock<IComponentStreamEnumerableFactory>().Object,
new Mock<IObservableDirectoryWalkerFactory>().Object,
new Mock<ILogger<NuGetComponentDetector>>().Object), this.detectorMock.Object,
};
service.RemoveUnwantedExperimentsbyDetectors(detectorList);
@ -310,9 +311,9 @@ public class ExperimentServiceTests
var detectorList = new List<IComponentDetector>
{
new NuGetComponentDetector(
new Mock<IComponentStreamEnumerableFactory>().Object,
new Mock<IObservableDirectoryWalkerFactory>().Object,
new Mock<ILogger<NuGetComponentDetector>>().Object),
new Mock<IComponentStreamEnumerableFactory>().Object,
new Mock<IObservableDirectoryWalkerFactory>().Object,
new Mock<ILogger<NuGetComponentDetector>>().Object),
};
service.RemoveUnwantedExperimentsbyDetectors(detectorList);
@ -325,4 +326,35 @@ public class ExperimentServiceTests
x => x.ProcessExperimentAsync(this.experimentConfigMock.Object, It.IsAny<ExperimentDiff>()),
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());
}
}

Просмотреть файл

@ -321,17 +321,17 @@ public class DetectorProcessingServiceTests
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 class DetectorProcessingServiceTests
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<FileComponentDetector> SetupFileDetectorMock(string id)
{
var mockFileDetector = new Mock<FileComponentDetector>();