private bool MergeChanges( IDependenciesChanges changes, IEnumerable <IDependenciesSnapshotFilter> snapshotFilters) { var worldBuilder = ImmutableDictionary.CreateBuilder <string, IDependency>( StringComparer.OrdinalIgnoreCase); worldBuilder.AddRange(DependenciesWorld); var topLevelBuilder = ImmutableHashSet.CreateBuilder <IDependency>(); topLevelBuilder.AddRange(TopLevelDependencies); var anyChanges = false; foreach (var removed in changes.RemovedNodes) { var targetedId = Dependency.GetID(TargetFramework, removed.ProviderType, removed.Id); if (!worldBuilder.TryGetValue(targetedId, out IDependency dependency)) { continue; } if (snapshotFilters != null) { foreach (var filter in snapshotFilters) { dependency = filter.BeforeRemove( ProjectPath, TargetFramework, dependency, worldBuilder, topLevelBuilder, out bool filterAnyChanges); anyChanges |= filterAnyChanges; if (dependency == null) { break; } } } if (dependency == null) { continue; } anyChanges = true; worldBuilder.Remove(targetedId); topLevelBuilder.Remove(dependency); } foreach (var added in changes.AddedNodes) { IDependency newDependency = new Dependency(added, TargetFramework); if (snapshotFilters != null) { foreach (var filter in snapshotFilters) { newDependency = filter.BeforeAdd( ProjectPath, TargetFramework, newDependency, worldBuilder, topLevelBuilder, out bool filterAnyChanges); anyChanges |= filterAnyChanges; if (newDependency == null) { break; } } } if (newDependency == null) { continue; } anyChanges = true; worldBuilder.Remove(newDependency.Id); worldBuilder.Add(newDependency.Id, newDependency); if (newDependency.TopLevel) { topLevelBuilder.Remove(newDependency); topLevelBuilder.Add(newDependency); } } DependenciesWorld = worldBuilder.ToImmutable(); TopLevelDependencies = topLevelBuilder.ToImmutable(); ConstructTopLevelDependenciesByPathMap(); return(anyChanges); }
public void FromChanges_NoChangesAfterBeforeAddFilterDeclinedChange() { const string projectPath = @"c:\somefolder\someproject\a.csproj"; var targetFramework = new TargetFramework("tfm1"); var dependencyTop1 = new TestDependency { ProviderType = "Xxx", Id = Dependency.GetID(targetFramework, "Xxx", "topdependency1"), Name = "TopDependency1", Caption = "TopDependency1", SchemaItemType = "Xxx", Resolved = true, TopLevel = true }; var dependencyChild1 = new TestDependency { ProviderType = "Xxx", Id = Dependency.GetID(targetFramework, "Xxx", "childdependency1"), Name = "ChildDependency1", Caption = "ChildDependency1", SchemaItemType = "Xxx", Resolved = true, TopLevel = false }; var dependencyModelNew1 = new TestDependencyModel { ProviderType = "Xxx", Id = "newdependency1", Name = "NewDependency1", Caption = "NewDependency1", SchemaItemType = "Xxx", Resolved = true, Icon = KnownMonikers.Uninstall, ExpandedIcon = KnownMonikers.Uninstall }; var catalogs = IProjectCatalogSnapshotFactory.Create(); var previousSnapshot = ITargetedDependenciesSnapshotFactory.Implement( projectPath: projectPath, targetFramework: targetFramework, catalogs: catalogs, dependenciesWorld: new [] { dependencyTop1, dependencyChild1 }, topLevelDependencies: new [] { dependencyTop1 }); var changes = new DependenciesChangesBuilder(); changes.Added(dependencyModelNew1); var snapshotFilter = new TestDependenciesSnapshotFilter() .BeforeAddReject(@"tfm1\xxx\newdependency1"); var snapshot = TargetedDependenciesSnapshot.FromChanges( projectPath, previousSnapshot, changes.Build(), catalogs, ImmutableArray.Create <IDependenciesSnapshotFilter>(snapshotFilter), new Dictionary <string, IProjectDependenciesSubTreeProvider>(), null); Assert.Same(previousSnapshot, snapshot); }
/// <summary> /// Returns id having full path instead of OriginalItemSpec /// </summary> public static bool TopLevelIdEquals(this IDependency self, string id) { return(string.IsNullOrEmpty(self.Path) ? string.Equals(self.Id, id, StringComparisons.DependencyTreeIds) : Dependency.IdEquals(id, self.TargetFramework, self.ProviderType, self.Path)); }
/// <summary> /// Returns id having full path instead of OriginalItemSpec /// </summary> public static string GetTopLevelId(this IDependency self) { return(string.IsNullOrEmpty(self.Path) ? self.Id : Dependency.GetID(self.TargetFramework, self.ProviderType, self.Path)); }
/// <summary> /// Applies changes to <paramref name="previousSnapshot"/> and produces a new snapshot if required. /// If no changes are made, <paramref name="previousSnapshot"/> is returned unmodified. /// </summary> /// <returns>An updated snapshot, or <paramref name="previousSnapshot"/> if no changes occured.</returns> public static ITargetedDependenciesSnapshot FromChanges( string projectPath, ITargetedDependenciesSnapshot previousSnapshot, IDependenciesChanges changes, IProjectCatalogSnapshot catalogs, ImmutableArray <IDependenciesSnapshotFilter> snapshotFilters, IReadOnlyDictionary <string, IProjectDependenciesSubTreeProvider> subTreeProviderByProviderType, IImmutableSet <string> projectItemSpecs) { Requires.NotNullOrWhiteSpace(projectPath, nameof(projectPath)); Requires.NotNull(previousSnapshot, nameof(previousSnapshot)); // catalogs can be null Requires.NotNull(changes, nameof(changes)); Requires.Argument(!snapshotFilters.IsDefault, nameof(snapshotFilters), "Cannot be default."); Requires.NotNull(subTreeProviderByProviderType, nameof(subTreeProviderByProviderType)); // projectItemSpecs can be null bool anyChanges = false; ITargetFramework targetFramework = previousSnapshot.TargetFramework; var worldBuilder = previousSnapshot.DependenciesWorld.ToBuilder(); if (changes.RemovedNodes.Count != 0) { var context = new RemoveDependencyContext(worldBuilder); foreach (IDependencyModel removed in changes.RemovedNodes) { Remove(context, removed); } } if (changes.AddedNodes.Count != 0) { var context = new AddDependencyContext(worldBuilder); foreach (IDependencyModel added in changes.AddedNodes) { Add(context, added); } } // Also factor in any changes to path/framework/catalogs anyChanges = anyChanges || !StringComparers.Paths.Equals(projectPath, previousSnapshot.ProjectPath) || !targetFramework.Equals(previousSnapshot.TargetFramework) || !Equals(catalogs, previousSnapshot.Catalogs); if (anyChanges) { return(new TargetedDependenciesSnapshot( projectPath, targetFramework, catalogs, worldBuilder.ToImmutable())); } return(previousSnapshot); void Remove(RemoveDependencyContext context, IDependencyModel dependencyModel) { string dependencyId = Dependency.GetID( targetFramework, dependencyModel.ProviderType, dependencyModel.Id); if (!context.TryGetDependency(dependencyId, out IDependency dependency)) { return; } context.Reset(); foreach (IDependenciesSnapshotFilter filter in snapshotFilters) { filter.BeforeRemove( targetFramework, dependency, context); anyChanges |= context.Changed; if (!context.GetResult(filter)) { // TODO breaking here denies later filters the opportunity to modify builders return; } } worldBuilder.Remove(dependencyId); anyChanges = true; } void Add(AddDependencyContext context, IDependencyModel dependencyModel) { // Create the unfiltered dependency IDependency dependency = new Dependency(dependencyModel, targetFramework, projectPath); context.Reset(); foreach (IDependenciesSnapshotFilter filter in snapshotFilters) { filter.BeforeAddOrUpdate( targetFramework, dependency, subTreeProviderByProviderType, projectItemSpecs, context); dependency = context.GetResult(filter); if (dependency == null) { break; } } if (dependency != null) { // A dependency was accepted worldBuilder.Remove(dependency.Id); worldBuilder.Add(dependency.Id, dependency); anyChanges = true; } else { // Even though the dependency was rejected, it's possible that filters made // changes to other dependencies. anyChanges |= context.Changed; } } }
public void GetID_CreatesCorrectString(string modelId, string expected) { Assert.Equal(expected, Dependency.GetID(TargetFramework.Any, "providerType", modelId)); }
private bool MergeChanges( IDependenciesChanges changes, IEnumerable <IDependenciesSnapshotFilter> snapshotFilters, IEnumerable <IProjectDependenciesSubTreeProvider> subTreeProviders, HashSet <string> projectItemSpecs) { var worldBuilder = DependenciesWorld.ToBuilder(); var topLevelBuilder = TopLevelDependencies.ToBuilder(); var anyChanges = false; foreach (var removed in changes.RemovedNodes) { var targetedId = Dependency.GetID(TargetFramework, removed.ProviderType, removed.Id); if (!worldBuilder.TryGetValue(targetedId, out IDependency dependency)) { continue; } if (snapshotFilters != null) { foreach (var filter in snapshotFilters) { dependency = filter.BeforeRemove( ProjectPath, TargetFramework, dependency, worldBuilder, topLevelBuilder, out bool filterAnyChanges); anyChanges |= filterAnyChanges; if (dependency == null) { break; } } } if (dependency == null) { continue; } anyChanges = true; worldBuilder.Remove(targetedId); topLevelBuilder.Remove(dependency); } var subTreeProvidersMap = GetSubTreeProviderMap(subTreeProviders); foreach (var added in changes.AddedNodes) { IDependency newDependency = new Dependency(added, TargetFramework, ProjectPath); if (snapshotFilters != null) { foreach (var filter in snapshotFilters) { newDependency = filter.BeforeAdd( ProjectPath, TargetFramework, newDependency, worldBuilder, topLevelBuilder, subTreeProvidersMap, projectItemSpecs, out bool filterAnyChanges); anyChanges |= filterAnyChanges; if (newDependency == null) { break; } } } if (newDependency == null) { continue; } anyChanges = true; worldBuilder.Remove(newDependency.Id); worldBuilder.Add(newDependency.Id, newDependency); if (newDependency.TopLevel) { topLevelBuilder.Remove(newDependency); topLevelBuilder.Add(newDependency); } } DependenciesWorld = worldBuilder.ToImmutable(); TopLevelDependencies = topLevelBuilder.ToImmutable(); ConstructTopLevelDependenciesByPathMap(); return(anyChanges); }