public void BeforeRemove_WhenPackageRemoving_ShouldCleanupSdk() { const string packageName = "packageName"; var targetFramework = new TargetFramework("tfm"); var sdkDependency = new TestDependency { Id = Dependency.GetID(targetFramework, SdkRuleHandler.ProviderTypeString, packageName), TopLevel = false, Resolved = true, Flags = DependencyTreeFlags.SdkSubTreeNodeFlags.Union(DependencyTreeFlags.ResolvedFlags) }; var packageDependency = new TestDependency { Id = "packageId", Name = packageName, Flags = DependencyTreeFlags.PackageNodeFlags, TopLevel = true, Resolved = true }; var worldBuilder = new IDependency[] { packageDependency, sdkDependency }.ToImmutableDictionary(d => d.Id).ToBuilder(); var context = new RemoveDependencyContext(worldBuilder); var filter = new SdkAndPackagesDependenciesSnapshotFilter(); filter.BeforeRemove( projectPath: null, targetFramework: targetFramework, dependency: packageDependency, context); // Accepts removal Assert.True(context.GetResult(filter)); // Makes other changes too Assert.True(context.Changed); Assert.True(worldBuilder.TryGetValue(packageDependency.Id, out var afterPackageDependency)); Assert.Same(packageDependency, afterPackageDependency); Assert.True(worldBuilder.TryGetValue(sdkDependency.Id, out var afterSdkDependency)); afterSdkDependency.AssertEqualTo( afterSdkDependency.ToUnresolved( SdkReference.SchemaName, dependencyIDs: ImmutableList <string> .Empty)); }
public void BeforeRemove_WhenPackageRemoving_ShouldCleanupSdk() { const string packageName = "packageName"; var targetFramework = new TargetFramework("tfm"); var sdkDependency = new TestDependency { Id = Dependency.GetID(targetFramework, SdkRuleHandler.ProviderTypeString, packageName), Resolved = true, Flags = DependencyTreeFlags.SdkDependency.Union(DependencyTreeFlags.Resolved) }; var packageDependency = new TestDependency { Id = "packageId", Name = packageName, Flags = DependencyTreeFlags.PackageDependency, Resolved = true }; var builder = new IDependency[] { packageDependency, sdkDependency }.ToDictionary(d => d.Id); var context = new RemoveDependencyContext(builder); var filter = new SdkAndPackagesDependenciesSnapshotFilter(); filter.BeforeRemove( targetFramework: targetFramework, dependency: packageDependency, context); // Accepts removal Assert.True(context.GetResult(filter)); // Makes other changes too Assert.True(context.Changed); Assert.True(builder.TryGetValue(packageDependency.Id, out var afterPackageDependency)); Assert.Same(packageDependency, afterPackageDependency); Assert.True(builder.TryGetValue(sdkDependency.Id, out var afterSdkDependency)); DependencyAssert.Equal( afterSdkDependency.ToUnresolved( SdkReference.SchemaName), afterSdkDependency); }
public void BeforeRemove_WhenPackageRemoving_ShouldCleanupSdk() { const string packageName = "packageName"; var sdkDependency = new TestDependency { Id = packageName, ProviderType = SdkRuleHandler.ProviderTypeString, Resolved = true, Flags = DependencyTreeFlags.SdkDependency.Union(ProjectTreeFlags.ResolvedReference) }; var packageDependency = new TestDependency { Id = packageName, ProviderType = PackageRuleHandler.ProviderTypeString, Flags = DependencyTreeFlags.PackageDependency, Resolved = true }; var builder = new IDependency[] { packageDependency, sdkDependency }.ToDictionary(IDependencyExtensions.GetDependencyId); var context = new RemoveDependencyContext(builder); var filter = new SdkAndPackagesDependenciesSnapshotFilter(); filter.BeforeRemove( dependency: packageDependency, context); // Accepts removal Assert.True(context.GetResult(filter)); // Makes other changes too Assert.True(context.Changed); Assert.True(builder.TryGetValue(packageDependency.GetDependencyId(), out var afterPackageDependency)); Assert.Same(packageDependency, afterPackageDependency); Assert.True(builder.TryGetValue(sdkDependency.GetDependencyId(), out var afterSdkDependency)); DependencyAssert.Equal( afterSdkDependency.ToUnresolved(SdkReference.SchemaName), afterSdkDependency); }
/// <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 TargetedDependenciesSnapshot FromChanges( string projectPath, TargetedDependenciesSnapshot 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)); Requires.NotNull(changes, nameof(changes)); Requires.Argument(!snapshotFilters.IsDefault, nameof(snapshotFilters), "Cannot be default."); Requires.NotNull(subTreeProviderByProviderType, nameof(subTreeProviderByProviderType)); 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; } } }
/// <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 TargetedDependenciesSnapshot FromChanges( TargetedDependenciesSnapshot previousSnapshot, IDependenciesChanges?changes, IProjectCatalogSnapshot?catalogs, ImmutableArray <IDependenciesSnapshotFilter> snapshotFilters, IReadOnlyDictionary <string, IProjectDependenciesSubTreeProvider> subTreeProviderByProviderType, IImmutableSet <string>?projectItemSpecs) { Requires.NotNull(previousSnapshot, nameof(previousSnapshot)); Requires.Argument(!snapshotFilters.IsDefault, nameof(snapshotFilters), "Cannot be default."); Requires.NotNull(subTreeProviderByProviderType, nameof(subTreeProviderByProviderType)); bool anyChanges = false; ITargetFramework targetFramework = previousSnapshot.TargetFramework; var dependencyById = previousSnapshot.Dependencies.ToDictionary(d => d.Id, StringComparers.DependencyTreeIds); if (changes != null && changes.RemovedNodes.Count != 0) { var context = new RemoveDependencyContext(dependencyById); foreach (IDependencyModel removed in changes.RemovedNodes) { Remove(context, removed); } } if (changes != null && changes.AddedNodes.Count != 0) { var context = new AddDependencyContext(dependencyById); foreach (IDependencyModel added in changes.AddedNodes) { #pragma warning disable CS0618 // Type or member is obsolete // NOTE we still need to check this in case extensions (eg. WebTools) provide us with top level items that need to be filtered out if (!added.TopLevel) { continue; } #pragma warning restore CS0618 // Type or member is obsolete Add(context, added); } } // Also factor in any changes to path/framework/catalogs anyChanges = anyChanges || !targetFramework.Equals(previousSnapshot.TargetFramework) || !Equals(catalogs, previousSnapshot.Catalogs); if (anyChanges) { return(new TargetedDependenciesSnapshot( targetFramework, catalogs, dependencyById.ToImmutableValueArray())); } 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; } } dependencyById.Remove(dependencyId); anyChanges = true; } void Add(AddDependencyContext context, IDependencyModel dependencyModel) { // Create the unfiltered dependency IDependency?dependency = new Dependency(dependencyModel, targetFramework); 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 dependencyById.Remove(dependency.Id); dependencyById.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 virtual void BeforeRemove( IDependency dependency, RemoveDependencyContext context) { context.Accept(); }