diff --git a/src/Microsoft.ComponentDetection.Common/DependencyGraph/DependencyGraph.cs b/src/Microsoft.ComponentDetection.Common/DependencyGraph/DependencyGraph.cs index cff395fc2..b5ca144f3 100644 --- a/src/Microsoft.ComponentDetection.Common/DependencyGraph/DependencyGraph.cs +++ b/src/Microsoft.ComponentDetection.Common/DependencyGraph/DependencyGraph.cs @@ -7,6 +7,7 @@ using System.Text; using Microsoft.ComponentDetection.Contracts; using Microsoft.ComponentDetection.Contracts.BcdeModels; +using Microsoft.ComponentDetection.Contracts.TypedComponent; [assembly: InternalsVisibleTo("Microsoft.ComponentDetection.Common.Tests")] @@ -141,6 +142,34 @@ public ICollection GetAncestors(string componentId) .ToList(); } + public HashSet GetAncestorsAsTypedComponents(string componentId, Func toTypedComponent) + { + ArgumentNullException.ThrowIfNull(componentId); + return this.GetAncestors(componentId) + .Select(a => this.componentNodes.TryGetValue(a, out var component) ? component : null) + .Where(a => a != null) + .Select(a => a.TypedComponent ?? toTypedComponent(a.Id)) + .ToHashSet(new ComponentComparer()); + } + + public HashSet GetRootsAsTypedComponents(string componentId, Func toTypedComponent) + { + ArgumentNullException.ThrowIfNull(componentId); + return this.GetExplicitReferencedDependencyIds(componentId) + .Select(r => this.componentNodes.TryGetValue(r, out var component) ? component : null) + .Where(r => r != null) + .Select(r => r.TypedComponent ?? toTypedComponent(r.Id)) + .ToHashSet(new ComponentComparer()); + } + + public void FillTypedComponents(Func toTypedComponent) + { + foreach (var componentId in this.componentNodes.Values) + { + componentId.TypedComponent = toTypedComponent(componentId.Id); + } + } + IEnumerable IDependencyGraph.GetDependenciesForComponent(string componentId) { return this.GetDependenciesForComponent(componentId).ToImmutableList(); @@ -229,5 +258,7 @@ internal ComponentRefNode() internal bool? IsDevelopmentDependency { get; set; } internal DependencyScope? DependencyScope { get; set; } + + internal TypedComponent TypedComponent { get; set; } } } diff --git a/src/Microsoft.ComponentDetection.Common/Telemetry/Records/DependencyGraphTranslationRecord.cs b/src/Microsoft.ComponentDetection.Common/Telemetry/Records/DependencyGraphTranslationRecord.cs new file mode 100644 index 000000000..2a4ddae93 --- /dev/null +++ b/src/Microsoft.ComponentDetection.Common/Telemetry/Records/DependencyGraphTranslationRecord.cs @@ -0,0 +1,14 @@ +namespace Microsoft.ComponentDetection.Common.Telemetry.Records; + +using System; + +public class DependencyGraphTranslationRecord : BaseDetectionTelemetryRecord +{ + public override string RecordName => "DependencyGraphTranslationRecord"; + + public string DetectorId { get; set; } + + public TimeSpan? TimeToAddRoots { get; set; } + + public TimeSpan? TimeToAddAncestors { get; set; } +} diff --git a/src/Microsoft.ComponentDetection.Contracts/IComponentRecorder.cs b/src/Microsoft.ComponentDetection.Contracts/IComponentRecorder.cs index dafaa23f1..a0ee3ecfe 100644 --- a/src/Microsoft.ComponentDetection.Contracts/IComponentRecorder.cs +++ b/src/Microsoft.ComponentDetection.Contracts/IComponentRecorder.cs @@ -1,5 +1,6 @@ namespace Microsoft.ComponentDetection.Contracts; +using System; using System.Collections.Generic; using Microsoft.ComponentDetection.Contracts.BcdeModels; @@ -124,4 +125,28 @@ public interface IDependencyGraph /// The component id to look up ancestors for. /// The componentIds that are ancestors for a given componentId. ICollection GetAncestors(string componentId); + + /// + /// Gets the component IDs of all explicitly referenced components, and converts them to a set of typed components. + /// WARNING: Using this method without calling first will result in a performance hit. + /// + /// The component to find all roots for. + /// Function that converts the component id to the typed component object. + /// Set of TypedComponents containing the roots. + public HashSet GetRootsAsTypedComponents(string componentId, Func toTypedComponent); + + /// + /// Gets the component IDs of all ancestors for a given component id, and converts them to a set of typed components. + /// WARNING: Using this method without calling first will result in a performance hit. + /// + /// The component to find all roots for. + /// Function that converts the component id to the typed component object. + /// Set of TypedComponents containing the ancestors. + public HashSet GetAncestorsAsTypedComponents(string componentId, Func toTypedComponent); + + /// + /// This operation pre-fills all nodes with the specified typed component, which improves performance for subsequent runs + /// of and . + /// + public void FillTypedComponents(Func toTypedComponent); } diff --git a/src/Microsoft.ComponentDetection.Orchestrator/Services/GraphTranslation/DefaultGraphTranslationService.cs b/src/Microsoft.ComponentDetection.Orchestrator/Services/GraphTranslation/DefaultGraphTranslationService.cs index e9b335ff1..788b76181 100644 --- a/src/Microsoft.ComponentDetection.Orchestrator/Services/GraphTranslation/DefaultGraphTranslationService.cs +++ b/src/Microsoft.ComponentDetection.Orchestrator/Services/GraphTranslation/DefaultGraphTranslationService.cs @@ -90,11 +90,24 @@ private IEnumerable GatherSetOfDetectedComponentsUnmerged(IEn .Where(recorderDetectorPair => recorderDetectorPair.Recorder != null) .SelectMany(recorderDetectorPair => { + using var record = new DependencyGraphTranslationRecord() + { + DetectorId = recorderDetectorPair.Detector.Id, + }; + var detector = recorderDetectorPair.Detector; var componentRecorder = recorderDetectorPair.Recorder; var detectedComponents = componentRecorder.GetDetectedComponents(); var dependencyGraphsByLocation = componentRecorder.GetDependencyGraphsByLocation(); + foreach (var graph in dependencyGraphsByLocation.Values) + { + graph.FillTypedComponents(componentRecorder.GetComponent); + } + + var totalTimeToAddRoots = TimeSpan.Zero; + var totalTimeToAddAncestors = TimeSpan.Zero; + // 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) @@ -118,10 +131,17 @@ private IEnumerable GatherSetOfDetectedComponentsUnmerged(IEn var dependencyGraph = graphKvp.Value; // Calculate roots of the component + var rootStartTime = DateTime.UtcNow; this.AddRootsToDetectedComponent(component, dependencyGraph, componentRecorder); + var rootEndTime = DateTime.UtcNow; + totalTimeToAddRoots += rootEndTime - rootStartTime; // Calculate Ancestors of the component + var ancestorStartTime = DateTime.UtcNow; this.AddAncestorsToDetectedComponent(component, dependencyGraph, componentRecorder); + var ancestorEndTime = DateTime.UtcNow; + totalTimeToAddAncestors += ancestorEndTime - ancestorStartTime; + component.DevelopmentDependency = this.MergeDevDependency(component.DevelopmentDependency, dependencyGraph.IsDevelopmentDependency(component.Component.Id)); component.DependencyScope = DependencyScopeComparer.GetMergedDependencyScope(component.DependencyScope, dependencyGraph.GetDependencyScope(component.Component.Id)); component.DetectedBy = detector; @@ -151,6 +171,9 @@ private IEnumerable GatherSetOfDetectedComponentsUnmerged(IEn } } + record.TimeToAddRoots = totalTimeToAddRoots; + record.TimeToAddAncestors = totalTimeToAddAncestors; + return detectedComponents; }).ToList(); } @@ -223,35 +246,23 @@ private DetectedComponent MergeComponents(IEnumerable enumera private void AddRootsToDetectedComponent(DetectedComponent detectedComponent, IDependencyGraph dependencyGraph, IComponentRecorder componentRecorder) { detectedComponent.DependencyRoots ??= new HashSet(new ComponentComparer()); - if (dependencyGraph == null) { return; } - var roots = dependencyGraph.GetExplicitReferencedDependencyIds(detectedComponent.Component.Id); - - foreach (var rootId in roots) - { - detectedComponent.DependencyRoots.Add(componentRecorder.GetComponent(rootId)); - } + detectedComponent.DependencyRoots.UnionWith(dependencyGraph.GetRootsAsTypedComponents(detectedComponent.Component.Id, componentRecorder.GetComponent)); } private void AddAncestorsToDetectedComponent(DetectedComponent detectedComponent, IDependencyGraph dependencyGraph, IComponentRecorder componentRecorder) { detectedComponent.AncestralDependencyRoots ??= new HashSet(new ComponentComparer()); - if (dependencyGraph == null) { return; } - var roots = dependencyGraph.GetAncestors(detectedComponent.Component.Id); - - foreach (var rootId in roots) - { - detectedComponent.AncestralDependencyRoots.Add(componentRecorder.GetComponent(rootId)); - } + detectedComponent.AncestralDependencyRoots.UnionWith(dependencyGraph.GetAncestorsAsTypedComponents(detectedComponent.Component.Id, componentRecorder.GetComponent)); } private HashSet MakeFilePathsRelative(ILogger logger, DirectoryInfo rootDirectory, HashSet filePaths) diff --git a/test/Microsoft.ComponentDetection.Common.Tests/DependencyGraphTests.cs b/test/Microsoft.ComponentDetection.Common.Tests/DependencyGraphTests.cs index 200f03c5e..334a92ff5 100644 --- a/test/Microsoft.ComponentDetection.Common.Tests/DependencyGraphTests.cs +++ b/test/Microsoft.ComponentDetection.Common.Tests/DependencyGraphTests.cs @@ -1,29 +1,35 @@ namespace Microsoft.ComponentDetection.Common.Tests; using System; +using System.Collections.Generic; +using System.Linq; using FluentAssertions; +using Microsoft.ComponentDetection.Common.DependencyGraph; using Microsoft.ComponentDetection.Contracts; +using Microsoft.ComponentDetection.Contracts.TypedComponent; using Microsoft.VisualStudio.TestTools.UnitTesting; [TestClass] public class DependencyGraphTests { - private DependencyGraph.DependencyGraph dependencyGraph; + private DependencyGraph dependencyGraph; + private IComponentRecorder componentRecorder; [TestInitialize] public void TestInitializer() { // Default value of true -- some tests will create their own, though. - this.dependencyGraph = new DependencyGraph.DependencyGraph(true); + this.dependencyGraph = new DependencyGraph(true); + this.componentRecorder = new ComponentRecorder(enableManualTrackingOfExplicitReferences: false); } [TestMethod] public void AddComponent_ParentComponentIdIsPresent_DependencyRelationIsAdded() { - var componentA = new DependencyGraph.DependencyGraph.ComponentRefNode { Id = "componentA" }; - var componentB = new DependencyGraph.DependencyGraph.ComponentRefNode { Id = "componentB" }; - var componentC = new DependencyGraph.DependencyGraph.ComponentRefNode { Id = "componentC" }; - var componentD = new DependencyGraph.DependencyGraph.ComponentRefNode { Id = "componentD" }; + var componentA = new DependencyGraph.ComponentRefNode { Id = "componentA" }; + var componentB = new DependencyGraph.ComponentRefNode { Id = "componentB" }; + var componentC = new DependencyGraph.ComponentRefNode { Id = "componentC" }; + var componentD = new DependencyGraph.ComponentRefNode { Id = "componentD" }; this.dependencyGraph.AddComponent(componentD); this.dependencyGraph.AddComponent(componentB, parentComponentId: componentD.Id); @@ -51,7 +57,7 @@ public void AddComponent_ParentComponentIdIsPresent_DependencyRelationIsAdded() [TestMethod] public void AddComponent_parentComponentIdIsNotPresent_AdditionTakePlaceWithoutThrowing() { - var componentA = new DependencyGraph.DependencyGraph.ComponentRefNode { Id = "componentA", IsExplicitReferencedDependency = true }; + var componentA = new DependencyGraph.ComponentRefNode { Id = "componentA", IsExplicitReferencedDependency = true }; Action action = () => this.dependencyGraph.AddComponent(componentA); action.Should().NotThrow(); @@ -70,15 +76,15 @@ public void AddComponent_ComponentIsNull_ArgumentNullExceptionIsThrow() [TestMethod] public void AddComponent_ComponentHasNoId_ArgumentNullExceptionIsThrow() { - var component = new DependencyGraph.DependencyGraph.ComponentRefNode { Id = null }; + var component = new DependencyGraph.ComponentRefNode { Id = null }; Action action = () => this.dependencyGraph.AddComponent(component); action.Should().Throw(); - component = new DependencyGraph.DependencyGraph.ComponentRefNode { Id = string.Empty }; + component = new DependencyGraph.ComponentRefNode { Id = string.Empty }; action = () => this.dependencyGraph.AddComponent(component); action.Should().Throw(); - component = new DependencyGraph.DependencyGraph.ComponentRefNode { Id = " " }; + component = new DependencyGraph.ComponentRefNode { Id = " " }; action = () => this.dependencyGraph.AddComponent(component); action.Should().Throw(); } @@ -86,7 +92,7 @@ public void AddComponent_ComponentHasNoId_ArgumentNullExceptionIsThrow() [TestMethod] public void AddComponent_ParentComponentWasNotAddedPreviously_ArgumentExceptionIsThrown() { - var componentA = new DependencyGraph.DependencyGraph.ComponentRefNode { Id = "componentA" }; + var componentA = new DependencyGraph.ComponentRefNode { Id = "componentA" }; Action action = () => this.dependencyGraph.AddComponent(componentA, parentComponentId: "nonexistingComponent"); @@ -96,12 +102,12 @@ public void AddComponent_ParentComponentWasNotAddedPreviously_ArgumentExceptionI [TestMethod] public void GetExplicitReferencedDependencyIds_ComponentsWereAddedSpecifyingRoot_RootsAreReturned() { - var componentA = new DependencyGraph.DependencyGraph.ComponentRefNode { Id = "componentA", IsExplicitReferencedDependency = true }; - var componentB = new DependencyGraph.DependencyGraph.ComponentRefNode { Id = "componentB" }; - var componentC = new DependencyGraph.DependencyGraph.ComponentRefNode { Id = "componentC" }; - var componentD = new DependencyGraph.DependencyGraph.ComponentRefNode { Id = "componentD" }; - var componentE = new DependencyGraph.DependencyGraph.ComponentRefNode { Id = "componentE", IsExplicitReferencedDependency = true }; - var componentF = new DependencyGraph.DependencyGraph.ComponentRefNode { Id = "componentF" }; + var componentA = new DependencyGraph.ComponentRefNode { Id = "componentA", IsExplicitReferencedDependency = true }; + var componentB = new DependencyGraph.ComponentRefNode { Id = "componentB" }; + var componentC = new DependencyGraph.ComponentRefNode { Id = "componentC" }; + var componentD = new DependencyGraph.ComponentRefNode { Id = "componentD" }; + var componentE = new DependencyGraph.ComponentRefNode { Id = "componentE", IsExplicitReferencedDependency = true }; + var componentF = new DependencyGraph.ComponentRefNode { Id = "componentF" }; this.dependencyGraph.AddComponent(componentA); this.dependencyGraph.AddComponent(componentB, componentA.Id); @@ -137,53 +143,59 @@ public void GetExplicitReferencedDependencyIds_ComponentsWereAddedSpecifyingRoot } [TestMethod] - public void GetExplicitReferencedDependencyIds_ComponentsWereAddedWithoutSpecifyingRoot_RootsAreEmpty() + [DataRow(true)] + [DataRow(false)] + public void GetExplicitReferencedDependencyIds_ComponentsWereAddedWithoutSpecifyingRoot_RootsAreEmpty(bool shouldUseTypedComponents) { - var componentA = new DependencyGraph.DependencyGraph.ComponentRefNode { Id = "componentA" }; - var componentB = new DependencyGraph.DependencyGraph.ComponentRefNode { Id = "componentB" }; + var componentA = new DependencyGraph.ComponentRefNode { Id = "componentA" }; + var componentB = new DependencyGraph.ComponentRefNode { Id = "componentB" }; this.dependencyGraph.AddComponent(componentA); this.dependencyGraph.AddComponent(componentB, componentA.Id); - var rootsForComponentA = this.dependencyGraph.GetExplicitReferencedDependencyIds(componentA.Id); + var rootsForComponentA = this.GetExplicitReferencedDependencyIds(componentA.Id, shouldUseTypedComponents); rootsForComponentA.Should().BeEmpty(); - var rootsForComponentB = this.dependencyGraph.GetExplicitReferencedDependencyIds(componentB.Id); + var rootsForComponentB = this.GetExplicitReferencedDependencyIds(componentB.Id, shouldUseTypedComponents); rootsForComponentB.Should().BeEmpty(); } [TestMethod] - public void GetExplicitReferencedDependencyIds_ComponentIsRoot_ARootIsRootOfItSelf() + [DataRow(true)] + [DataRow(false)] + public void GetExplicitReferencedDependencyIds_ComponentIsRoot_ARootIsRootOfItSelf(bool shouldUseTypedComponents) { - var componentA = new DependencyGraph.DependencyGraph.ComponentRefNode { Id = "componentA", IsExplicitReferencedDependency = true }; + var componentA = new DependencyGraph.ComponentRefNode { Id = "componentA", IsExplicitReferencedDependency = true }; this.dependencyGraph.AddComponent(componentA); - var aRoots = this.dependencyGraph.GetExplicitReferencedDependencyIds(componentA.Id); + var aRoots = this.GetExplicitReferencedDependencyIds(componentA.Id, shouldUseTypedComponents); aRoots.Should().ContainSingle(); aRoots.Should().Contain(componentA.Id); } [TestMethod] - public void GetExplicitReferencedDependencyIds_RootHasParent_ReturnItselfAndItsParents() + [DataRow(true)] + [DataRow(false)] + public void GetExplicitReferencedDependencyIds_RootHasParent_ReturnItselfAndItsParents(bool shouldUseTypedComponents) { - var componentA = new DependencyGraph.DependencyGraph.ComponentRefNode { Id = "componentA", IsExplicitReferencedDependency = true }; - var componentB = new DependencyGraph.DependencyGraph.ComponentRefNode { Id = "componentB", IsExplicitReferencedDependency = true }; - var componentC = new DependencyGraph.DependencyGraph.ComponentRefNode { Id = "componentC", IsExplicitReferencedDependency = true }; + var componentA = new DependencyGraph.ComponentRefNode { Id = "componentA", IsExplicitReferencedDependency = true }; + var componentB = new DependencyGraph.ComponentRefNode { Id = "componentB", IsExplicitReferencedDependency = true }; + var componentC = new DependencyGraph.ComponentRefNode { Id = "componentC", IsExplicitReferencedDependency = true }; this.dependencyGraph.AddComponent(componentA); this.dependencyGraph.AddComponent(componentB, componentA.Id); this.dependencyGraph.AddComponent(componentC, componentB.Id); - var aRoots = this.dependencyGraph.GetExplicitReferencedDependencyIds(componentA.Id); + var aRoots = this.GetExplicitReferencedDependencyIds(componentA.Id, shouldUseTypedComponents); aRoots.Should().ContainSingle(); aRoots.Should().Contain(componentA.Id); - var bRoots = this.dependencyGraph.GetExplicitReferencedDependencyIds(componentB.Id); + var bRoots = this.GetExplicitReferencedDependencyIds(componentB.Id, shouldUseTypedComponents); bRoots.Should().HaveCount(2); bRoots.Should().Contain(componentA.Id); bRoots.Should().Contain(componentB.Id); - var cRoots = this.dependencyGraph.GetExplicitReferencedDependencyIds(componentC.Id); + var cRoots = this.GetExplicitReferencedDependencyIds(componentC.Id, shouldUseTypedComponents); cRoots.Should().HaveCount(3); cRoots.Should().Contain(componentA.Id); cRoots.Should().Contain(componentB.Id); @@ -191,89 +203,95 @@ public void GetExplicitReferencedDependencyIds_RootHasParent_ReturnItselfAndItsP } [TestMethod] - public void GetExplicitReferencedDependencyIds_InsertionOrderNotAffectedRoots() + [DataRow(true)] + [DataRow(false)] + public void GetExplicitReferencedDependencyIds_InsertionOrderNotAffectedRoots(bool shouldUseTypedComponents) { - var componentA = new DependencyGraph.DependencyGraph.ComponentRefNode { Id = "componentA", IsExplicitReferencedDependency = true }; - var componentB = new DependencyGraph.DependencyGraph.ComponentRefNode { Id = "componentB" }; - var componentC = new DependencyGraph.DependencyGraph.ComponentRefNode { Id = "componentC", IsExplicitReferencedDependency = true }; + var componentA = new DependencyGraph.ComponentRefNode { Id = "componentA", IsExplicitReferencedDependency = true }; + var componentB = new DependencyGraph.ComponentRefNode { Id = "componentB" }; + var componentC = new DependencyGraph.ComponentRefNode { Id = "componentC", IsExplicitReferencedDependency = true }; this.dependencyGraph.AddComponent(componentA); this.dependencyGraph.AddComponent(componentB, componentA.Id); this.dependencyGraph.AddComponent(componentC); this.dependencyGraph.AddComponent(componentA, componentC.Id); - componentB = new DependencyGraph.DependencyGraph.ComponentRefNode { Id = "componentB", IsExplicitReferencedDependency = true }; + componentB = new DependencyGraph.ComponentRefNode { Id = "componentB", IsExplicitReferencedDependency = true }; this.dependencyGraph.AddComponent(componentB); - var aRoots = this.dependencyGraph.GetExplicitReferencedDependencyIds(componentA.Id); + var aRoots = this.GetExplicitReferencedDependencyIds(componentA.Id, shouldUseTypedComponents); aRoots.Should().HaveCount(2); aRoots.Should().Contain(componentA.Id); aRoots.Should().Contain(componentC.Id); - var bRoots = this.dependencyGraph.GetExplicitReferencedDependencyIds(componentB.Id); + var bRoots = this.GetExplicitReferencedDependencyIds(componentB.Id, shouldUseTypedComponents); bRoots.Should().HaveCount(3); bRoots.Should().Contain(componentA.Id); bRoots.Should().Contain(componentB.Id); bRoots.Should().Contain(componentC.Id); - var cRoots = this.dependencyGraph.GetExplicitReferencedDependencyIds(componentC.Id); + var cRoots = this.GetExplicitReferencedDependencyIds(componentC.Id, shouldUseTypedComponents); cRoots.Should().ContainSingle(); cRoots.Should().Contain(componentC.Id); } [TestMethod] - public void GetExplicitReferencedDependencyIds_UseManualSelectionTurnedOff_ComponentsWithNoParentsAreSelectedAsExplicitReferencedDependencies() + [DataRow(true)] + [DataRow(false)] + public void GetExplicitReferencedDependencyIds_UseManualSelectionTurnedOff_ComponentsWithNoParentsAreSelectedAsExplicitReferencedDependencies(bool shouldUseTypedComponents) { - this.dependencyGraph = new DependencyGraph.DependencyGraph(false); - var componentA = new DependencyGraph.DependencyGraph.ComponentRefNode { Id = "componentA" }; - var componentB = new DependencyGraph.DependencyGraph.ComponentRefNode { Id = "componentB" }; - var componentC = new DependencyGraph.DependencyGraph.ComponentRefNode { Id = "componentC" }; + this.dependencyGraph = new DependencyGraph(false); + var componentA = new DependencyGraph.ComponentRefNode { Id = "componentA" }; + var componentB = new DependencyGraph.ComponentRefNode { Id = "componentB" }; + var componentC = new DependencyGraph.ComponentRefNode { Id = "componentC" }; this.dependencyGraph.AddComponent(componentA); this.dependencyGraph.AddComponent(componentB, componentA.Id); this.dependencyGraph.AddComponent(componentC); this.dependencyGraph.AddComponent(componentA, componentC.Id); - var aRoots = this.dependencyGraph.GetExplicitReferencedDependencyIds(componentA.Id); + var aRoots = this.GetExplicitReferencedDependencyIds(componentA.Id, shouldUseTypedComponents); aRoots.Should().ContainSingle(); aRoots.Should().Contain(componentC.Id); ((IDependencyGraph)this.dependencyGraph).IsComponentExplicitlyReferenced(componentA.Id).Should().BeFalse(); - var bRoots = this.dependencyGraph.GetExplicitReferencedDependencyIds(componentB.Id); + var bRoots = this.GetExplicitReferencedDependencyIds(componentB.Id, shouldUseTypedComponents); bRoots.Should().ContainSingle(); bRoots.Should().Contain(componentC.Id); ((IDependencyGraph)this.dependencyGraph).IsComponentExplicitlyReferenced(componentB.Id).Should().BeFalse(); - var cRoots = this.dependencyGraph.GetExplicitReferencedDependencyIds(componentC.Id); + var cRoots = this.GetExplicitReferencedDependencyIds(componentC.Id, shouldUseTypedComponents); cRoots.Should().ContainSingle(); cRoots.Should().Contain(componentC.Id); ((IDependencyGraph)this.dependencyGraph).IsComponentExplicitlyReferenced(componentC.Id).Should().BeTrue(); } [TestMethod] - public void GetExplicitReferencedDependencyIds_UseManualSelectionTurnedOff_PropertyIsExplicitReferencedDependencyIsIgnored() + [DataRow(true)] + [DataRow(false)] + public void GetExplicitReferencedDependencyIds_UseManualSelectionTurnedOff_PropertyIsExplicitReferencedDependencyIsIgnored(bool shouldUseTypedComponents) { - this.dependencyGraph = new DependencyGraph.DependencyGraph(false); - var componentA = new DependencyGraph.DependencyGraph.ComponentRefNode { Id = "componentA", IsExplicitReferencedDependency = true }; - var componentB = new DependencyGraph.DependencyGraph.ComponentRefNode { Id = "componentB", IsExplicitReferencedDependency = true }; - var componentC = new DependencyGraph.DependencyGraph.ComponentRefNode { Id = "componentC", IsExplicitReferencedDependency = true }; + this.dependencyGraph = new DependencyGraph(false); + var componentA = new DependencyGraph.ComponentRefNode { Id = "componentA", IsExplicitReferencedDependency = true }; + var componentB = new DependencyGraph.ComponentRefNode { Id = "componentB", IsExplicitReferencedDependency = true }; + var componentC = new DependencyGraph.ComponentRefNode { Id = "componentC", IsExplicitReferencedDependency = true }; this.dependencyGraph.AddComponent(componentA); this.dependencyGraph.AddComponent(componentB, componentA.Id); this.dependencyGraph.AddComponent(componentC); this.dependencyGraph.AddComponent(componentA, componentC.Id); - var aRoots = this.dependencyGraph.GetExplicitReferencedDependencyIds(componentA.Id); + var aRoots = this.GetExplicitReferencedDependencyIds(componentA.Id, shouldUseTypedComponents); aRoots.Should().ContainSingle(); aRoots.Should().Contain(componentC.Id); ((IDependencyGraph)this.dependencyGraph).IsComponentExplicitlyReferenced(componentA.Id).Should().BeFalse(); - var bRoots = this.dependencyGraph.GetExplicitReferencedDependencyIds(componentB.Id); + var bRoots = this.GetExplicitReferencedDependencyIds(componentB.Id, shouldUseTypedComponents); bRoots.Should().ContainSingle(); bRoots.Should().Contain(componentC.Id); ((IDependencyGraph)this.dependencyGraph).IsComponentExplicitlyReferenced(componentB.Id).Should().BeFalse(); - var cRoots = this.dependencyGraph.GetExplicitReferencedDependencyIds(componentC.Id); + var cRoots = this.GetExplicitReferencedDependencyIds(componentC.Id, shouldUseTypedComponents); cRoots.Should().ContainSingle(); cRoots.Should().Contain(componentC.Id); ((IDependencyGraph)this.dependencyGraph).IsComponentExplicitlyReferenced(componentC.Id).Should().BeTrue(); @@ -302,9 +320,9 @@ public void GetExplicitReferencedDependencyIds_ComponentIdIsNotRegisteredInGraph [TestMethod] public void IsDevelopmentDependency_ReturnsAsExpected() { - var componentA = new DependencyGraph.DependencyGraph.ComponentRefNode { Id = "componentA", IsDevelopmentDependency = true }; - var componentB = new DependencyGraph.DependencyGraph.ComponentRefNode { Id = "componentB", IsDevelopmentDependency = false }; - var componentC = new DependencyGraph.DependencyGraph.ComponentRefNode { Id = "componentC" }; + var componentA = new DependencyGraph.ComponentRefNode { Id = "componentA", IsDevelopmentDependency = true }; + var componentB = new DependencyGraph.ComponentRefNode { Id = "componentB", IsDevelopmentDependency = false }; + var componentC = new DependencyGraph.ComponentRefNode { Id = "componentC" }; this.dependencyGraph.AddComponent(componentA); this.dependencyGraph.AddComponent(componentB, componentA.Id); @@ -319,18 +337,18 @@ public void IsDevelopmentDependency_ReturnsAsExpected() [TestMethod] public void IsDevelopmentDependency_ReturnsAsExpected_AfterMerge() { - var componentA = new DependencyGraph.DependencyGraph.ComponentRefNode { Id = "componentA", IsDevelopmentDependency = true }; - var componentB = new DependencyGraph.DependencyGraph.ComponentRefNode { Id = "componentB", IsDevelopmentDependency = false }; - var componentC = new DependencyGraph.DependencyGraph.ComponentRefNode { Id = "componentC" }; + var componentA = new DependencyGraph.ComponentRefNode { Id = "componentA", IsDevelopmentDependency = true }; + var componentB = new DependencyGraph.ComponentRefNode { Id = "componentB", IsDevelopmentDependency = false }; + var componentC = new DependencyGraph.ComponentRefNode { Id = "componentC" }; this.dependencyGraph.AddComponent(componentA); this.dependencyGraph.AddComponent(componentB, componentA.Id); this.dependencyGraph.AddComponent(componentC); this.dependencyGraph.AddComponent(componentA, componentC.Id); - var componentANewValue = new DependencyGraph.DependencyGraph.ComponentRefNode { Id = "componentA", IsDevelopmentDependency = false }; - var componentBNewValue = new DependencyGraph.DependencyGraph.ComponentRefNode { Id = "componentB", IsDevelopmentDependency = true }; - var componentCNewValue = new DependencyGraph.DependencyGraph.ComponentRefNode { Id = "componentC", IsDevelopmentDependency = true }; + var componentANewValue = new DependencyGraph.ComponentRefNode { Id = "componentA", IsDevelopmentDependency = false }; + var componentBNewValue = new DependencyGraph.ComponentRefNode { Id = "componentB", IsDevelopmentDependency = true }; + var componentCNewValue = new DependencyGraph.ComponentRefNode { Id = "componentC", IsDevelopmentDependency = true }; this.dependencyGraph.AddComponent(componentANewValue); this.dependencyGraph.AddComponent(componentBNewValue); this.dependencyGraph.AddComponent(componentCNewValue); @@ -339,9 +357,9 @@ public void IsDevelopmentDependency_ReturnsAsExpected_AfterMerge() this.dependencyGraph.IsDevelopmentDependency(componentB.Id).Should().Be(false); this.dependencyGraph.IsDevelopmentDependency(componentC.Id).Should().Be(true); - var componentANullValue = new DependencyGraph.DependencyGraph.ComponentRefNode { Id = "componentA" }; - var componentBNullValue = new DependencyGraph.DependencyGraph.ComponentRefNode { Id = "componentB" }; - var componentCNullValue = new DependencyGraph.DependencyGraph.ComponentRefNode { Id = "componentC" }; + var componentANullValue = new DependencyGraph.ComponentRefNode { Id = "componentA" }; + var componentBNullValue = new DependencyGraph.ComponentRefNode { Id = "componentB" }; + var componentCNullValue = new DependencyGraph.ComponentRefNode { Id = "componentC" }; this.dependencyGraph.AddComponent(componentANullValue); this.dependencyGraph.AddComponent(componentBNullValue); this.dependencyGraph.AddComponent(componentCNullValue); @@ -352,45 +370,51 @@ public void IsDevelopmentDependency_ReturnsAsExpected_AfterMerge() } [TestMethod] - public void GetAncestors_ReturnsAsExpected() + [DataRow(true)] + [DataRow(false)] + public void GetAncestors_ReturnsAsExpected(bool shouldUseTypedComponents) { - var componentA = new DependencyGraph.DependencyGraph.ComponentRefNode { Id = "componentA" }; - var componentB = new DependencyGraph.DependencyGraph.ComponentRefNode { Id = "componentB" }; - var componentC = new DependencyGraph.DependencyGraph.ComponentRefNode { Id = "componentC" }; + var componentA = new DependencyGraph.ComponentRefNode { Id = "componentA" }; + var componentB = new DependencyGraph.ComponentRefNode { Id = "componentB" }; + var componentC = new DependencyGraph.ComponentRefNode { Id = "componentC" }; this.dependencyGraph.AddComponent(componentA); this.dependencyGraph.AddComponent(componentB, componentA.Id); this.dependencyGraph.AddComponent(componentC, componentB.Id); - var ancestors = this.dependencyGraph.GetAncestors(componentC.Id); + var ancestors = this.GetAncestors(componentC.Id, shouldUseTypedComponents); ancestors.Should().HaveCount(2); ancestors.Should().Contain(componentA.Id); ancestors.Should().Contain(componentB.Id); - ancestors = this.dependencyGraph.GetAncestors(componentB.Id); + ancestors = this.GetAncestors(componentB.Id, shouldUseTypedComponents); ancestors.Should().ContainSingle(); ancestors.Should().Contain(componentA.Id); - ancestors = this.dependencyGraph.GetAncestors(componentA.Id); + ancestors = this.GetAncestors(componentA.Id, shouldUseTypedComponents); ancestors.Should().BeEmpty(); - ancestors = this.dependencyGraph.GetAncestors("test"); + ancestors = this.GetAncestors("test", shouldUseTypedComponents); ancestors.Should().BeEmpty(); } [TestMethod] - public void GetAncestors_Null_ThrowsException() + [DataRow(true)] + [DataRow(false)] + public void GetAncestors_Null_ThrowsException(bool shouldUseTypedComponents) { - this.dependencyGraph.Invoking(d => d.GetAncestors(null)).Should().Throw(); + this.Invoking(d => d.GetAncestors(null, shouldUseTypedComponents)).Should().Throw(); } [TestMethod] - public void GetAncestors_Cyclic_ReturnsAsExpected() + [DataRow(true)] + [DataRow(false)] + public void GetAncestors_Cyclic_ReturnsAsExpected(bool shouldUseTypedComponents) { - var root = new DependencyGraph.DependencyGraph.ComponentRefNode { Id = "root" }; - var componentA = new DependencyGraph.DependencyGraph.ComponentRefNode { Id = "componentA" }; - var componentB = new DependencyGraph.DependencyGraph.ComponentRefNode { Id = "componentB" }; - var componentC = new DependencyGraph.DependencyGraph.ComponentRefNode { Id = "componentC" }; + var root = new DependencyGraph.ComponentRefNode { Id = "root" }; + var componentA = new DependencyGraph.ComponentRefNode { Id = "componentA" }; + var componentB = new DependencyGraph.ComponentRefNode { Id = "componentB" }; + var componentC = new DependencyGraph.ComponentRefNode { Id = "componentC" }; // root -> componentA -> componentB -> componentC --> loops to componentA this.dependencyGraph.AddComponent(root); @@ -399,25 +423,41 @@ public void GetAncestors_Cyclic_ReturnsAsExpected() this.dependencyGraph.AddComponent(componentC, componentB.Id); this.dependencyGraph.AddComponent(componentA, componentC.Id); - var ancestors = this.dependencyGraph.GetAncestors(componentC.Id); + var ancestors = this.GetAncestors(componentC.Id, shouldUseTypedComponents); ancestors.Should().HaveCount(3); ancestors.Should().Contain(root.Id); ancestors.Should().Contain(componentA.Id); ancestors.Should().Contain(componentB.Id); - ancestors = this.dependencyGraph.GetAncestors(componentA.Id); + ancestors = this.GetAncestors(componentA.Id, shouldUseTypedComponents); ancestors.Should().HaveCount(3); ancestors.Should().Contain(root.Id); ancestors.Should().Contain(componentB.Id); ancestors.Should().Contain(componentC.Id); - ancestors = this.dependencyGraph.GetAncestors(componentB.Id); + ancestors = this.GetAncestors(componentB.Id, shouldUseTypedComponents); ancestors.Should().HaveCount(3); ancestors.Should().Contain(root.Id); ancestors.Should().Contain(componentC.Id); ancestors.Should().Contain(componentA.Id); - ancestors = this.dependencyGraph.GetAncestors(root.Id); + ancestors = this.GetAncestors(root.Id, shouldUseTypedComponents); ancestors.Should().BeEmpty(); } + + private IEnumerable GetExplicitReferencedDependencyIds(string componentId, bool shouldUseTypedComponents) + { + this.dependencyGraph.FillTypedComponents(id => new NuGetComponent(id, "1.0.0")); + return shouldUseTypedComponents + ? this.dependencyGraph.GetRootsAsTypedComponents(componentId, id => new NuGetComponent(id, "1.0.0")).Select(x => ((NuGetComponent)x).Name) + : this.dependencyGraph.GetExplicitReferencedDependencyIds(componentId); + } + + private ICollection GetAncestors(string componentId, bool shouldUseTypedComponents) + { + this.dependencyGraph.FillTypedComponents(id => new NuGetComponent(id, "1.0.0")); + return shouldUseTypedComponents + ? this.dependencyGraph.GetAncestorsAsTypedComponents(componentId, id => new NuGetComponent(id, "1.0.0")).Select(x => ((NuGetComponent)x).Name).ToList() + : this.dependencyGraph.GetAncestors(componentId); + } }