public void TCheckForUnresolvedDependencies_CircularDependency_DoesNotRecurseInfinitely() { const string id1 = @"tfm1\xxx\dependency1"; const string id2 = @"tfm1\xxx\dependency2"; const string providerType = "Xxx"; var dependency1 = new TestDependency { Id = id1, ProviderType = providerType, TopLevel = true, DependencyIDs = ImmutableList.Create(id2) }; var dependency2 = new TestDependency { Id = id2, ProviderType = providerType, TopLevel = true, DependencyIDs = ImmutableList.Create(id1) }; var snapshot = new TargetedDependenciesSnapshot( "ProjectPath", TargetFramework.Any, catalogs: null, dependenciesWorld: new IDependency[] { dependency1, dependency2 }.ToDictionary(d => d.Id).ToImmutableDictionary()); // verify it doesn't stack overflow snapshot.CheckForUnresolvedDependencies(dependency1); }
public void TCheckForUnresolvedDependencies_CircularDependency_DoesNotRecurseInfinitely() { var dependencyTop1 = IDependencyFactory.FromJson(@" { ""ProviderType"": ""Xxx"", ""Id"": ""tfm1\\xxx\\topdependency1"", ""Name"":""TopDependency1"", ""Caption"":""TopDependency1"", ""SchemaItemType"":""Xxx"", ""Resolved"":""true"", ""TopLevel"":""true"", ""DependencyIDs"": [ ""tfm1\\xxx\\topdependency2"" ] }", icon: KnownMonikers.Uninstall, expandedIcon: KnownMonikers.Uninstall); var dependencyTop2 = IDependencyFactory.FromJson(@" { ""ProviderType"": ""Xxx"", ""Id"": ""tfm1\\xxx\\topdependency2"", ""Name"":""TopDependency2"", ""Caption"":""TopDependency2"", ""SchemaItemType"":""Xxx"", ""Resolved"":""true"", ""TopLevel"":""false"", ""DependencyIDs"": [ ""tfm1\\xxx\\topdependency1"" ] }", icon: KnownMonikers.Uninstall, expandedIcon: KnownMonikers.Uninstall); var previousSnapshot = new TargetedDependenciesSnapshot( "ProjectPath", TargetFramework.Any, catalogs: null, dependenciesWorld: new Dictionary <string, IDependency>() { { dependencyTop1.Id, dependencyTop1 }, { dependencyTop2.Id, dependencyTop2 }, }.ToImmutableDictionary()); // verify it doesn't stack overflow previousSnapshot.CheckForUnresolvedDependencies(dependencyTop1); }
/// <summary> /// Builds all available sub trees under root: target framework or Dependencies node /// when there is only one target. /// </summary> private async Task <IProjectTree> BuildSubTreesAsync( IProjectTree rootNode, ITargetFramework activeTarget, TargetedDependenciesSnapshot targetedSnapshot, Func <IProjectTree, HashSet <IProjectTree>, IProjectTree> syncFunc) { var groupedByProviderType = new Dictionary <string, List <IDependency> >(StringComparers.DependencyProviderTypes); foreach (IDependency dependency in targetedSnapshot.TopLevelDependencies) { if (!dependency.Visible) { // If a dependency is not visible we will still register a top-level group if it // has the ShowEmptyProviderRootNode flag. if (!dependency.Flags.Contains(DependencyTreeFlags.ShowEmptyProviderRootNode)) { // No such flag, so skip it completely. continue; } } if (!groupedByProviderType.TryGetValue(dependency.ProviderType, out List <IDependency> dependencies)) { dependencies = new List <IDependency>(); groupedByProviderType.Add(dependency.ProviderType, dependencies); } // Only add visible dependencies. See note above. if (dependency.Visible) { dependencies.Add(dependency); } } var currentNodes = new HashSet <IProjectTree>(capacity: groupedByProviderType.Count); bool isActiveTarget = targetedSnapshot.TargetFramework.Equals(activeTarget); foreach ((string providerType, List <IDependency> dependencies) in groupedByProviderType) { IDependencyViewModel?subTreeViewModel = _viewModelFactory.CreateRootViewModel( providerType, targetedSnapshot.CheckForUnresolvedDependencies(providerType)); if (subTreeViewModel == null) { // In theory this should never happen, as it means we have a dependency model of a type // that no provider claims. https://github.com/dotnet/project-system/issues/3653 continue; } IProjectTree?subTreeNode = rootNode.FindChildWithCaption(subTreeViewModel.Caption); bool isNewSubTreeNode = subTreeNode == null; ProjectTreeFlags excludedFlags = targetedSnapshot.TargetFramework.Equals(TargetFramework.Any) ? ProjectTreeFlags.Create(ProjectTreeFlags.Common.BubbleUp) : ProjectTreeFlags.Empty; subTreeNode = CreateOrUpdateNode( subTreeNode, subTreeViewModel, browseObjectProperties: null, isProjectItem: false, excludedFlags: excludedFlags); subTreeNode = await BuildSubTreeAsync( subTreeNode, targetedSnapshot, dependencies, isActiveTarget, shouldCleanup : !isNewSubTreeNode); currentNodes.Add(subTreeNode); rootNode = isNewSubTreeNode ? rootNode.Add(subTreeNode).Parent ! : subTreeNode.Parent !; Assumes.NotNull(rootNode); } return(syncFunc(rootNode, currentNodes)); }