Added dependencyScope detection for maven components (#87)

* Added "DependencyScope" for scanned component. Currently detection is only active for maven components.
* Added telemetry to keep track of each recorded component.
This commit is contained in:
Rushabh 2022-04-06 11:24:13 -07:00 коммит произвёл GitHub
Родитель 63dcae650a
Коммит 3a17feb7e1
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
19 изменённых файлов: 352 добавлений и 27 удалений

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

@ -41,5 +41,6 @@
<PackageVersion Include="System.Runtime.Loader" Version="4.3.0"/>
<PackageVersion Include="System.Threading.Tasks.Dataflow" Version="4.9.0"/>
<PackageVersion Include="yamldotnet" Version="11.2.1"/>
<PackageVersion Include="Faker.net" Version="2.0.154"/>
</ItemGroup>
</Project>

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

@ -5,6 +5,7 @@ using System.Collections.Immutable;
using System.Linq;
using System.Runtime.CompilerServices;
using Microsoft.ComponentDetection.Contracts;
using Microsoft.ComponentDetection.Contracts.BcdeModels;
using Microsoft.ComponentDetection.Contracts.TypedComponent;
[assembly: InternalsVisibleTo("Microsoft.ComponentDetection.Common.Tests")]
@ -133,7 +134,8 @@ namespace Microsoft.ComponentDetection.Common.DependencyGraph
DetectedComponent detectedComponent,
bool isExplicitReferencedDependency = false,
string parentComponentId = null,
bool? isDevelopmentDependency = null)
bool? isDevelopmentDependency = null,
DependencyScope? dependencyScope = null)
{
if (detectedComponent == null)
{
@ -167,7 +169,7 @@ namespace Microsoft.ComponentDetection.Common.DependencyGraph
lock (registerUsageLock)
{
storedComponent = detectedComponentsInternal.GetOrAdd(componentId, detectedComponent);
AddComponentToGraph(ManifestFileLocation, detectedComponent, isExplicitReferencedDependency, parentComponentId, isDevelopmentDependency);
AddComponentToGraph(ManifestFileLocation, detectedComponent, isExplicitReferencedDependency, parentComponentId, isDevelopmentDependency, dependencyScope);
}
}
@ -186,13 +188,20 @@ namespace Microsoft.ComponentDetection.Common.DependencyGraph
return recorder;
}
private void AddComponentToGraph(string location, DetectedComponent detectedComponent, bool isExplicitReferencedDependency, string parentComponentId, bool? isDevelopmentDependency)
private void AddComponentToGraph(
string location,
DetectedComponent detectedComponent,
bool isExplicitReferencedDependency,
string parentComponentId,
bool? isDevelopmentDependency,
DependencyScope? dependencyScope)
{
var componentNode = new DependencyGraph.ComponentRefNode
{
Id = detectedComponent.Component.Id,
IsExplicitReferencedDependency = isExplicitReferencedDependency,
IsDevelopmentDependency = isDevelopmentDependency,
DependencyScope = dependencyScope,
};
DependencyGraph.AddComponent(componentNode, parentComponentId);

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

@ -5,6 +5,7 @@ using System.Collections.Immutable;
using System.Linq;
using System.Runtime.CompilerServices;
using Microsoft.ComponentDetection.Contracts;
using Microsoft.ComponentDetection.Contracts.BcdeModels;
[assembly: InternalsVisibleTo("Microsoft.ComponentDetection.Common.Tests")]
@ -46,6 +47,11 @@ namespace Microsoft.ComponentDetection.Common.DependencyGraph
currentNode.IsDevelopmentDependency = currentNode.IsDevelopmentDependency.GetValueOrDefault(true) && componentNode.IsDevelopmentDependency.Value;
}
if (componentNode.DependencyScope.HasValue)
{
currentNode.DependencyScope = DependencyScopeComparer.GetMergedDependencyScope(currentNode.DependencyScope, componentNode.DependencyScope);
}
return currentNode;
});
@ -101,6 +107,11 @@ namespace Microsoft.ComponentDetection.Common.DependencyGraph
return componentNodes[componentId].IsDevelopmentDependency;
}
public DependencyScope? GetDependencyScope(string componentId)
{
return componentNodes[componentId].DependencyScope;
}
public IEnumerable<string> GetAllExplicitlyReferencedComponents()
{
return componentNodes.Values
@ -175,6 +186,8 @@ namespace Microsoft.ComponentDetection.Common.DependencyGraph
internal bool? IsDevelopmentDependency { get; set; }
internal DependencyScope? DependencyScope { get; set; }
internal ComponentRefNode()
{
DependencyIds = new HashSet<string>();

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

@ -0,0 +1,27 @@
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.ComponentDetection.Contracts.BcdeModels;
namespace Microsoft.ComponentDetection.Common
{
/// <summary>
/// Merges dependnecy Scope in their order of Priority.
/// Higher priority scope, as indicated by its lower enum value is given precendence.
/// </summary>
public class DependencyScopeComparer
{
public static DependencyScope? GetMergedDependencyScope(DependencyScope? scope1, DependencyScope? scope2) {
if (!scope1.HasValue)
{
return scope2;
}
else if (!scope2.HasValue)
{
return scope1;
}
return (int)scope1 < (int)scope2 ? scope1 : scope2;
}
}
}

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

@ -0,0 +1,17 @@
using System.Runtime.CompilerServices;
namespace Microsoft.ComponentDetection.Common.Telemetry.Records
{
public class DetectedComponentScopeRecord : BaseDetectionTelemetryRecord
{
public override string RecordName => "ComponentScopeRecord";
public int? MavenProvidedScopeCount { get; set; } = 0;
[MethodImpl(MethodImplOptions.Synchronized)]
public void IncrementProvidedScopeCount()
{
MavenProvidedScopeCount++;
}
}
}

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

@ -0,0 +1,28 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Microsoft.ComponentDetection.Contracts.BcdeModels
{
/// <summary>Used to communicate Dependency Scope of Component.
/// Currently only populated for Maven component.
/// The values are ordered in terms of priority, which is used to resolve the scope for duplicate component while merging them.
/// </summary>
public enum DependencyScope
{
/// <summary>default scope. dependencies are available in the project during all build tasks. propogated to dependent projects. </summary>
MavenCompile = 0,
/// <summary> Required at Runtime, but not at compile time.</summary>
MavenRuntime = 1,
/// <summary>Dependencies are available only at compile time and in the test classpath of the project. These dependencies are also not transitive.</summary>
MavenProvided = 2,
/// <summary>Similar to provided scope. Requires explicit reference to Jar. </summary>
MavenSystem = 3,
/// <summary>Used only at runtime.</summary>
MavenTest = 4,
}
}

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

@ -1,5 +1,6 @@
using System.Collections.Generic;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Serialization;
namespace Microsoft.ComponentDetection.Contracts.BcdeModels
@ -15,6 +16,9 @@ namespace Microsoft.ComponentDetection.Contracts.BcdeModels
public bool? IsDevelopmentDependency { get; set; }
[JsonConverter(typeof(StringEnumConverter))]
public DependencyScope? DependencyScope { get; set; }
public IEnumerable<TypedComponent.TypedComponent> TopLevelReferrers { get; set; }
public IEnumerable<int> ContainerDetailIds { get; set; }

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

@ -1,5 +1,6 @@
using System.Collections.Generic;
using System.Diagnostics;
using Microsoft.ComponentDetection.Contracts.BcdeModels;
namespace Microsoft.ComponentDetection.Contracts
{
@ -56,6 +57,9 @@ namespace Microsoft.ComponentDetection.Contracts
/// <summary> Gets or sets the layer within a container where this component was found.</summary>
public IDictionary<int, IEnumerable<int>> ContainerLayerIds { get; set; }
/// <summary> Gets or sets Dependency Scope of the component.</summary>
public DependencyScope? DependencyScope { get; set; }
/// <summary>Adds a filepath to the FilePaths hashset for this detected component.</summary>
/// <param name="filePath">The file path to add to the hashset.</param>

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

@ -1,4 +1,5 @@
using System.Collections.Generic;
using Microsoft.ComponentDetection.Contracts.BcdeModels;
namespace Microsoft.ComponentDetection.Contracts
{
@ -25,12 +26,14 @@ namespace Microsoft.ComponentDetection.Contracts
/// <param name="isExplicitReferencedDependency">The value define if the component was referenced manually by the user in the location where the scanning is taking place.</param>
/// <param name="parentComponentId">Id of the parent component.</param>
/// <param name="isDevelopmentDependency">Boolean value indicating whether or not a component is a development-time dependency. Null implies that the value is unknown.</param>
/// <param name="dependencyScope">Enum value indicating scope of the component. </param>
/// <returns>DetectedComponent added or updated.</returns>
void RegisterUsage(
DetectedComponent detectedComponent,
bool isExplicitReferencedDependency = false,
string parentComponentId = null,
bool? isDevelopmentDependency = null);
bool? isDevelopmentDependency = null,
DependencyScope? dependencyScope = null);
DetectedComponent GetComponent(string componentId);
@ -82,6 +85,13 @@ namespace Microsoft.ComponentDetection.Contracts
/// <returns>True if a development dependency, false if not. Null when unknown.</returns>
bool? IsDevelopmentDependency(string componentId);
/// <summary>
/// Returns DepedencyScope for the given componentId.
/// Null can be returned if a detector doesn't have the scope infromation.
/// </summary>
/// <param name="componentId">The componentId to check.</param>
DependencyScope? GetDependencyScope(string componentId);
/// <summary>
/// Gets the component IDs of all explicitly referenced components.
/// </summary>

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

@ -9,6 +9,7 @@ using System.Threading.Tasks;
using Microsoft.ComponentDetection.Contracts;
using Microsoft.ComponentDetection.Contracts.Internal;
using Microsoft.ComponentDetection.Contracts.TypedComponent;
using Microsoft.ComponentDetection.Contracts.BcdeModels;
using Newtonsoft.Json.Linq;
namespace Microsoft.ComponentDetection.Detectors.Ivy
@ -33,7 +34,7 @@ namespace Microsoft.ComponentDetection.Detectors.Ivy
/// in the project's build.xml, or if they use any file inclusion mechanism, it will fail.
///
/// The file written out by the custom Ant task is a simple JSON file representing a series of calls to be made to
/// the <see cref="ISingleFileComponentRecorder.RegisterUsage(DetectedComponent, bool, string, bool?)"/> method.
/// the <see cref="ISingleFileComponentRecorder.RegisterUsage(DetectedComponent, bool, string, bool?, DependencyScope?)"/> method.
/// </remarks>
[Export(typeof(IComponentDetector))]
public class IvyDetector : FileComponentDetector, IExperimentalDetector

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

@ -1,27 +1,39 @@
using System;
using System.Collections.Generic;
using System.Net;
using Microsoft.ComponentDetection.Contracts;
using Microsoft.ComponentDetection.Contracts.BcdeModels;
using Microsoft.ComponentDetection.Contracts.TypedComponent;
namespace Microsoft.ComponentDetection.Detectors.Maven
{
public static class MavenParsingUtilities
{
public static (DetectedComponent Component, bool? IsDevelopmentDependency) GenerateDetectedComponentFromMavenString(string key)
private static readonly Dictionary<string, DependencyScope> MavenScopeToDependencyScopeMapping = new Dictionary<string, DependencyScope>()
{
var component = GetMavenComponentFromComponentString(key);
{ "compile", DependencyScope.MavenCompile },
{ "provided", DependencyScope.MavenProvided },
{ "system", DependencyScope.MavenSystem },
{ "test", DependencyScope.MavenTest },
{ "runtime", DependencyScope.MavenRuntime },
};
var detectedComponent = new DetectedComponent(component.component);
public static (DetectedComponent Component, bool? IsDevelopmentDependency, DependencyScope? dependencyScope) GenerateDetectedComponentAndMetadataFromMavenString(string key)
{
var componentAndMetaData = GetMavenComponentAndIsDevDependencyAndScope(key);
return (detectedComponent, component.isDevDependency);
var detectedComponent = new DetectedComponent(componentAndMetaData.component);
return (detectedComponent, componentAndMetaData.isDevDependency, componentAndMetaData.dependencyScope);
}
private static (MavenComponent component, bool? isDevDependency) GetMavenComponentFromComponentString(string componentString)
private static (MavenComponent component, bool? isDevDependency, DependencyScope? dependencyScope) GetMavenComponentAndIsDevDependencyAndScope(string componentString)
{
var info = GetMavenComponentStringInfo(componentString);
return (new MavenComponent(info.groupId, info.artifactId, info.version), info.isDevelopmentDependency);
return (new MavenComponent(info.groupId, info.artifactId, info.version), info.isDevelopmentDependency, info.dependencyScope);
}
private static (string groupId, string artifactId, string version, bool? isDevelopmentDependency)
private static (string groupId, string artifactId, string version, bool? isDevelopmentDependency, DependencyScope dependencyScope)
GetMavenComponentStringInfo(string mavenComponentString)
{
var results = mavenComponentString.Split(':');
@ -37,16 +49,21 @@ namespace Microsoft.ComponentDetection.Detectors.Maven
results = new[] { results[0], results[1], results[2], results[4], results[5] };
}
// 'MavenCompile' is a default scope for maven dependencies.
DependencyScope dependencyScope = DependencyScope.MavenCompile;
var groupId = results[0];
var artifactId = results[1];
var version = results[3];
bool? isDevDependency = null;
if (results.Length == 5)
{
isDevDependency = string.Equals(results[4], "test", StringComparison.OrdinalIgnoreCase);
dependencyScope = MavenScopeToDependencyScopeMapping.TryGetValue(results[4], out dependencyScope)
? dependencyScope
: throw new InvalidOperationException($"Invalid scope ('{results[4]}') found for '{mavenComponentString}' found in generated dependency graph.");
isDevDependency = dependencyScope == DependencyScope.MavenTest;
}
return (groupId, artifactId, version, isDevDependency);
return (groupId, artifactId, version, isDevDependency, dependencyScope);
}
}
}

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

@ -58,9 +58,12 @@ namespace Microsoft.ComponentDetection.Detectors.Maven
var localLine = line.Trim(TrimCharacters);
if (!string.IsNullOrWhiteSpace(localLine) && topLevelComponent == null)
{
var topLevelMavenStringTuple = MavenParsingUtilities.GenerateDetectedComponentFromMavenString(localLine);
topLevelComponent = topLevelMavenStringTuple.Component;
singleFileComponentRecorder.RegisterUsage(topLevelMavenStringTuple.Component, isDevelopmentDependency: topLevelMavenStringTuple.IsDevelopmentDependency);
var topLevelMavenStringInfo = MavenParsingUtilities.GenerateDetectedComponentAndMetadataFromMavenString(localLine);
topLevelComponent = topLevelMavenStringInfo.Component;
singleFileComponentRecorder.RegisterUsage(
topLevelMavenStringInfo.Component,
isDevelopmentDependency: topLevelMavenStringInfo.IsDevelopmentDependency,
dependencyScope: topLevelMavenStringInfo.dependencyScope);
}
else
{
@ -104,18 +107,27 @@ namespace Microsoft.ComponentDetection.Detectors.Maven
tupleStack.Pop();
}
var componentAndDevDependencyTuple = MavenParsingUtilities.GenerateDetectedComponentFromMavenString(versionedComponent);
var componentAndDevDependencyTuple = MavenParsingUtilities.GenerateDetectedComponentAndMetadataFromMavenString(versionedComponent);
var newTuple = (ParseLevel: position, componentAndDevDependencyTuple.Component);
if (tupleStack.Count > 0)
{
var parent = tupleStack.Peek().Component;
componentRecorder.RegisterUsage(parent);
componentRecorder.RegisterUsage(newTuple.Component, parentComponentId: parent.Component.Id, isDevelopmentDependency: componentAndDevDependencyTuple.IsDevelopmentDependency);
componentRecorder.RegisterUsage(
newTuple.Component,
parentComponentId: parent.Component.Id,
isDevelopmentDependency: componentAndDevDependencyTuple.IsDevelopmentDependency,
dependencyScope: componentAndDevDependencyTuple.dependencyScope);
}
else
{
componentRecorder.RegisterUsage(newTuple.Component, isExplicitReferencedDependency: true, parentComponentId: topLevelComponent.Component.Id, isDevelopmentDependency: componentAndDevDependencyTuple.IsDevelopmentDependency);
componentRecorder.RegisterUsage(
newTuple.Component,
isExplicitReferencedDependency: true,
parentComponentId: topLevelComponent.Component.Id,
isDevelopmentDependency: componentAndDevDependencyTuple.IsDevelopmentDependency,
dependencyScope: componentAndDevDependencyTuple.dependencyScope);
}
tupleStack.Push(newTuple);

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

@ -5,10 +5,12 @@ using System.IO;
using System.Linq;
using Microsoft.ComponentDetection.Common;
using Microsoft.ComponentDetection.Common.DependencyGraph;
using Microsoft.ComponentDetection.Common.Telemetry.Records;
using Microsoft.ComponentDetection.Contracts;
using Microsoft.ComponentDetection.Contracts.BcdeModels;
using Microsoft.ComponentDetection.Contracts.TypedComponent;
using Microsoft.ComponentDetection.Orchestrator.ArgumentSets;
using System.Threading.Tasks;
namespace Microsoft.ComponentDetection.Orchestrator.Services.GraphTranslation
{
@ -24,6 +26,8 @@ namespace Microsoft.ComponentDetection.Orchestrator.Services.GraphTranslation
var mergedComponents = FlattenAndMergeComponents(unmergedComponents);
LogComponentScopeTelemetry(mergedComponents);
return new DefaultGraphScanResult
{
ComponentsFound = mergedComponents.Select(x => ConvertToContract(x)).ToList(),
@ -35,6 +39,20 @@ namespace Microsoft.ComponentDetection.Orchestrator.Services.GraphTranslation
};
}
private void LogComponentScopeTelemetry(List<DetectedComponent> components)
{
using var record = new DetectedComponentScopeRecord();
Parallel.ForEach(components, x =>
{
if (x.Component.Type.Equals(ComponentType.Maven)
&& x.DependencyScope.HasValue
&& (x.DependencyScope.Equals(DependencyScope.MavenProvided) || x.DependencyScope.Equals(DependencyScope.MavenSystem)))
{
record.IncrementProvidedScopeCount();
}
});
}
private IEnumerable<DetectedComponent> GatherSetOfDetectedComponentsUnmerged(IEnumerable<(IComponentDetector detector, ComponentRecorder recorder)> recorderDetectorPairs, DirectoryInfo rootDirectory)
{
return recorderDetectorPairs
@ -46,7 +64,7 @@ namespace Microsoft.ComponentDetection.Orchestrator.Services.GraphTranslation
var detectedComponents = componentRecorder.GetDetectedComponents();
var dependencyGraphsByLocation = componentRecorder.GetDependencyGraphsByLocation();
// Note that it looks like we are building up detected components functionally, but they are not immutable -- the code is just written
// Note that it looks like we are building up detected components functionally, but they are not immutable -- the code is just written
// to look like a pipeline.
foreach (var component in detectedComponents)
{
@ -62,6 +80,7 @@ namespace Microsoft.ComponentDetection.Orchestrator.Services.GraphTranslation
// Calculate roots of the component
AddRootsToDetectedComponent(component, dependencyGraph, componentRecorder);
component.DevelopmentDependency = MergeDevDependency(component.DevelopmentDependency, dependencyGraph.IsDevelopmentDependency(component.Component.Id));
component.DependencyScope = DependencyScopeComparer.GetMergedDependencyScope(component.DependencyScope, dependencyGraph.GetDependencyScope(component.Component.Id));
component.DetectedBy = detector;
// Return in a format that allows us to add the additional files for the components
@ -130,6 +149,7 @@ namespace Microsoft.ComponentDetection.Orchestrator.Services.GraphTranslation
}
firstComponent.DevelopmentDependency = MergeDevDependency(firstComponent.DevelopmentDependency, nextComponent.DevelopmentDependency);
firstComponent.DependencyScope = DependencyScopeComparer.GetMergedDependencyScope(firstComponent.DependencyScope, nextComponent.DependencyScope);
if (nextComponent.ContainerDetailIds.Count > 0)
{
@ -203,6 +223,7 @@ namespace Microsoft.ComponentDetection.Orchestrator.Services.GraphTranslation
{
DetectorId = component.DetectedBy.Id,
IsDevelopmentDependency = component.DevelopmentDependency,
DependencyScope = component.DependencyScope,
LocationsFoundAt = component.FilePaths,
Component = component.Component,
TopLevelReferrers = component.DependencyRoots,

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

@ -8,6 +8,7 @@
<PackageReference Include="MSTest.TestFramework"/>
<PackageReference Include="Moq"/>
<PackageReference Include="FluentAssertions"/>
<PackageReference Include="Faker.net" />
</ItemGroup>
<PropertyGroup>

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

@ -3,6 +3,7 @@ using System.Collections.Generic;
using FluentAssertions;
using Microsoft.ComponentDetection.Common.DependencyGraph;
using Microsoft.ComponentDetection.Contracts;
using Microsoft.ComponentDetection.Contracts.BcdeModels;
using Microsoft.ComponentDetection.Contracts.TypedComponent;
using Microsoft.VisualStudio.TestTools.UnitTesting;
@ -63,6 +64,20 @@ namespace Microsoft.ComponentDetection.Common.Tests
action.Should().Throw<ArgumentNullException>();
}
[TestMethod]
public void RegisterUsage_DevelopmentDependencyHasValue_componentNodeHasDependencyScope()
{
var location = "location";
var singleFileComponentRecorder = componentRecorder.CreateSingleFileComponentRecorder(location);
var detectedComponent = new DetectedComponent(new MavenComponent("org.apache.maven", "maven-artifact", "3.6.1"));
singleFileComponentRecorder.RegisterUsage(detectedComponent, dependencyScope: DependencyScope.MavenProvided);
var dependencyGraph = componentRecorder.GetDependencyGraphForLocation(location);
dependencyGraph.GetDependencyScope(detectedComponent.Component.Id).Should().NotBeNull();
dependencyGraph.GetDependencyScope(detectedComponent.Component.Id).Should().Be(DependencyScope.MavenProvided);
}
[TestMethod]
public void RegisterUsage_DetectedComponentWithNullComponent_ArgumentExceptionIsThrown()
{

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

@ -0,0 +1,63 @@
using FluentAssertions;
using FluentAssertions.Primitives;
using Microsoft.ComponentDetection.Contracts.BcdeModels;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using Faker;
using static Microsoft.ComponentDetection.Common.DependencyScopeComparer;
using System.Linq;
namespace Microsoft.ComponentDetection.Common.Tests
{
[TestClass]
[TestCategory("Governance/All")]
[TestCategory("Governance/ComponentDetection")]
public class DependencyScopeComparerTests
{
[TestMethod]
public void GetMergedDependencyScope_returnNull_IfBothNull()
{
GetMergedDependencyScope(null, null).Should().BeNull();
}
[TestMethod]
public void GetMergedDependencyScope_returnSecondIfFirstNulll()
{
DependencyScope randomDependencyScope = Faker.Enum.Random<DependencyScope>();
GetMergedDependencyScope(null, randomDependencyScope)
.Should()
.Equals(randomDependencyScope);
}
[TestMethod]
public void GetMergedDependencyScope_returnFirstIfSecondNulll()
{
DependencyScope randomDependencyScope = Faker.Enum.Random<DependencyScope>();
GetMergedDependencyScope(randomDependencyScope, null)
.Should()
.Equals(randomDependencyScope);
}
[TestMethod]
public void GetMergedDependencyScope_WhenBothNonNull_higherPriorityEnumsReturned()
{
// compare with compile
GetMergedDependencyScope(DependencyScope.MavenRuntime, DependencyScope.MavenCompile).Should().Equals(DependencyScope.MavenCompile);
GetMergedDependencyScope(DependencyScope.MavenProvided, DependencyScope.MavenCompile).Should().Equals(DependencyScope.MavenCompile);
GetMergedDependencyScope(DependencyScope.MavenSystem, DependencyScope.MavenCompile).Should().Equals(DependencyScope.MavenCompile);
GetMergedDependencyScope(DependencyScope.MavenTest, DependencyScope.MavenCompile).Should().Equals(DependencyScope.MavenCompile);
// compare with runtime
GetMergedDependencyScope(DependencyScope.MavenProvided, DependencyScope.MavenRuntime).Should().Equals(DependencyScope.MavenRuntime);
GetMergedDependencyScope(DependencyScope.MavenSystem, DependencyScope.MavenRuntime).Should().Equals(DependencyScope.MavenRuntime);
GetMergedDependencyScope(DependencyScope.MavenTest, DependencyScope.MavenRuntime).Should().Equals(DependencyScope.MavenRuntime);
// compare with provided
GetMergedDependencyScope(DependencyScope.MavenSystem, DependencyScope.MavenProvided).Should().Equals(DependencyScope.MavenProvided);
GetMergedDependencyScope(DependencyScope.MavenTest, DependencyScope.MavenProvided).Should().Equals(DependencyScope.MavenProvided);
// compare with system
GetMergedDependencyScope(DependencyScope.MavenSystem, DependencyScope.MavenTest).Should().Equals(DependencyScope.MavenSystem);
}
}
}

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

@ -28,6 +28,7 @@ namespace Microsoft.ComponentDetection.Contracts.Tests
Component = new NpmComponent("SampleNpmComponent", "1.2.3"),
DetectorId = "NpmDetectorId",
IsDevelopmentDependency = true,
DependencyScope = DependencyScope.MavenCompile,
LocationsFoundAt = new[]
{
"some/location",
@ -65,6 +66,7 @@ namespace Microsoft.ComponentDetection.Contracts.Tests
var actualDetectedComponent = actual.ComponentsFound.First();
actualDetectedComponent.DetectorId.Should().Be("NpmDetectorId");
actualDetectedComponent.IsDevelopmentDependency.Should().Be(true);
actualDetectedComponent.DependencyScope.Should().Be(DependencyScope.MavenCompile);
actualDetectedComponent.LocationsFoundAt.Contains("some/location").Should().Be(true);
var npmComponent = actualDetectedComponent.Component as NpmComponent;
@ -95,6 +97,7 @@ namespace Microsoft.ComponentDetection.Contracts.Tests
foundComponent.Value<string>("detectorId").Should().Be("NpmDetectorId");
foundComponent.Value<bool>("isDevelopmentDependency").Should().Be(true);
foundComponent.Value<string>("dependencyScope").Should().Be("MavenCompile");
foundComponent["locationsFoundAt"].First().Value<string>().Should().Be("some/location");
foundComponent["component"].Value<string>("type").Should().Be("Npm");
foundComponent["component"].Value<string>("name").Should().Be("SampleNpmComponent");

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

@ -0,0 +1,79 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Microsoft.ComponentDetection.Detectors.Maven;
using System;
using System.Collections.Generic;
using System.Text;
using static Microsoft.ComponentDetection.Detectors.Maven.MavenParsingUtilities;
using Microsoft.ComponentDetection.Contracts.TypedComponent;
using FluentAssertions.Collections;
using Microsoft.ComponentDetection.Contracts.BcdeModels;
using Microsoft.ComponentDetection.Contracts;
namespace Microsoft.ComponentDetection.Detectors.Tests
{
[TestClass]
[TestCategory("Governance/All")]
[TestCategory("Governance/ComponentDetection")]
public class MavenParsingUtilitiesTests
{
[TestMethod]
public void GenerateDetectedComponentAndIsDeveDependencyAndDependencyScope_HappyPath()
{
var componentAndMetaData =
GenerateDetectedComponentAndMetadataFromMavenString("org.apache.maven:maven-artifact:jar:3.6.1-SNAPSHOT:provided");
Assert.IsNotNull(componentAndMetaData);
Assert.IsNotNull(componentAndMetaData.Component);
Assert.IsNotNull(componentAndMetaData.IsDevelopmentDependency);
Assert.IsNotNull(componentAndMetaData.dependencyScope);
var actualComponent = (MavenComponent)componentAndMetaData.Component.Component;
Assert.IsInstanceOfType(actualComponent, typeof(MavenComponent));
var expectedComponent = new MavenComponent("org.apache.maven", "maven-artifact", "3.6.1-SNAPSHOT");
Assert.AreEqual(expectedComponent.ArtifactId, actualComponent.ArtifactId);
Assert.AreEqual(expectedComponent.GroupId, actualComponent.GroupId);
Assert.AreEqual(expectedComponent.Version, actualComponent.Version);
Assert.IsFalse(componentAndMetaData.IsDevelopmentDependency);
Assert.AreEqual(DependencyScope.MavenProvided, componentAndMetaData.dependencyScope);
}
[TestMethod]
public void GenerateDetectedComponentAndIsDeveDependencyAndDependencyScope_DefaultScopeCompile()
{
var componentAndMetaData =
GenerateDetectedComponentAndMetadataFromMavenString("org.apache.maven:maven-artifact:jar:3.6.1-SNAPSHOT");
Assert.IsNotNull(componentAndMetaData);
Assert.IsNotNull(componentAndMetaData.dependencyScope);
var actualComponent = (MavenComponent)componentAndMetaData.Component.Component;
Assert.IsInstanceOfType(actualComponent, typeof(MavenComponent));
Assert.AreEqual(DependencyScope.MavenCompile, componentAndMetaData.dependencyScope);
}
[TestMethod]
public void GenerateDetectedComponentAndIsDeveDependencyAndDependencyScope_DevelopmentDependencyTrue()
{
var componentAndMetaData =
GenerateDetectedComponentAndMetadataFromMavenString("org.apache.maven:maven-artifact:jar:3.6.1-SNAPSHOT:test");
Assert.IsNotNull(componentAndMetaData);
Assert.IsNotNull(componentAndMetaData.IsDevelopmentDependency);
var actualComponent = (MavenComponent)componentAndMetaData.Component.Component;
Assert.IsInstanceOfType(actualComponent, typeof(MavenComponent));
Assert.IsTrue(componentAndMetaData.IsDevelopmentDependency);
}
[TestMethod]
public void GenerateDetectedComponentAndIsDeveDependencyAndDependencyScope_InvalidScope()
{
var ex = Assert.ThrowsException<InvalidOperationException>(
() => GenerateDetectedComponentAndMetadataFromMavenString("org.apache.maven:maven-artifact:jar:3.6.1-SNAPSHOT:invalidScope"));
Assert.IsTrue(ex.Message.Contains("invalid scope", StringComparison.OrdinalIgnoreCase));
}
}
}

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

@ -56,15 +56,15 @@ namespace Microsoft.ComponentDetection.Detectors.Tests
var dependencyGraph = componentRecorder.GetDependencyGraphsByLocation()[pomfileLocation];
var topLevelComponentTuple = MavenParsingUtilities.GenerateDetectedComponentFromMavenString("org.apache.maven:maven-compat:jar:3.6.1-SNAPSHOT");
var topLevelComponent = topLevelComponentTuple.Component;
var mavenCoreTuple = MavenParsingUtilities.GenerateDetectedComponentFromMavenString("org.apache.maven:maven-core:jar:3.6.1-SNAPSHOT:compile");
var topLevelComponentInfo = MavenParsingUtilities.GenerateDetectedComponentAndMetadataFromMavenString("org.apache.maven:maven-compat:jar:3.6.1-SNAPSHOT");
var topLevelComponent = topLevelComponentInfo.Component;
var mavenCoreTuple = MavenParsingUtilities.GenerateDetectedComponentAndMetadataFromMavenString("org.apache.maven:maven-core:jar:3.6.1-SNAPSHOT:compile");
var mavenCore = mavenCoreTuple.Component;
var guiceTuple = MavenParsingUtilities.GenerateDetectedComponentFromMavenString("com.google.inject:guice:jar:no_aop:4.2.1:compile");
var guiceTuple = MavenParsingUtilities.GenerateDetectedComponentAndMetadataFromMavenString("com.google.inject:guice:jar:no_aop:4.2.1:compile");
var guice = guiceTuple.Component;
var guavaTuple = MavenParsingUtilities.GenerateDetectedComponentFromMavenString("com.google.guava:guava:jar:25.1-android:compile");
var guavaTuple = MavenParsingUtilities.GenerateDetectedComponentAndMetadataFromMavenString("com.google.guava:guava:jar:25.1-android:compile");
var guava = guavaTuple.Component;
var animalSnifferAnnotationsTuple = MavenParsingUtilities.GenerateDetectedComponentFromMavenString("org.codehaus.mojo:animal-sniffer-annotations:jar:1.14:compile");
var animalSnifferAnnotationsTuple = MavenParsingUtilities.GenerateDetectedComponentAndMetadataFromMavenString("org.codehaus.mojo:animal-sniffer-annotations:jar:1.14:compile");
var animalSnifferAnnotations = animalSnifferAnnotationsTuple.Component;
var topLevelComponentDependencies = dependencyGraph.GetDependenciesForComponent(topLevelComponent.Component.Id);