public void FromChanges_CatalogChanged() { var targetFramework = new TargetFramework("tfm1"); var previousCatalogs = IProjectCatalogSnapshotFactory.Create(); var previousSnapshot = TargetedDependenciesSnapshot.CreateEmpty(targetFramework, previousCatalogs); var updatedCatalogs = IProjectCatalogSnapshotFactory.Create(); var snapshot = TargetedDependenciesSnapshot.FromChanges( previousSnapshot, changes: null, updatedCatalogs, ImmutableArray <IDependenciesSnapshotFilter> .Empty, new Dictionary <string, IProjectDependenciesSubTreeProvider>(), null); Assert.NotSame(previousSnapshot, snapshot); Assert.Same(updatedCatalogs, snapshot.Catalogs); Assert.Equal(previousSnapshot.Dependencies.Length, snapshot.Dependencies.Length); for (int i = 0; i < previousSnapshot.Dependencies.Length; i++) { Assert.Same(previousSnapshot.Dependencies[i], snapshot.Dependencies[i]); } Assert.Equal(DiagnosticLevel.None, snapshot.MaximumVisibleDiagnosticLevel); Assert.Empty(snapshot.Dependencies); }
public void FromChanges_ReportedChangesAfterBeforeRemoveFilterDeclinedChange() { var targetFramework = new TargetFramework("tfm1"); var dependency1 = new TestDependency { ProviderType = "Xxx", Id = "dependency1", OriginalItemSpec = "Dependency1", Caption = "Dependency1", SchemaItemType = "Xxx", Resolved = true }; var dependency2 = new TestDependency { ProviderType = "Xxx", Id = "dependency2", OriginalItemSpec = "Dependency2", Caption = "Dependency2", SchemaItemType = "Xxx", Resolved = true }; var catalogs = IProjectCatalogSnapshotFactory.Create(); var previousSnapshot = new TargetedDependenciesSnapshot( targetFramework, catalogs, ImmutableArray.Create <IDependency>(dependency1, dependency2)); var changes = new DependenciesChangesBuilder(); changes.Removed("Xxx", "dependency1"); var addedOnRemove = new TestDependency { Id = "SomethingElse" }; var snapshotFilter = new TestDependenciesSnapshotFilter() .BeforeRemoveReject("Xxx", "dependency1", addOrUpdate: addedOnRemove); var snapshot = TargetedDependenciesSnapshot.FromChanges( previousSnapshot, changes.TryBuildChanges() !, catalogs, ImmutableArray.Create <IDependenciesSnapshotFilter>(snapshotFilter), new Dictionary <string, IProjectDependenciesSubTreeProvider>(), null); Assert.True(snapshotFilter.Completed); Assert.NotSame(previousSnapshot, snapshot); Assert.Same(previousSnapshot.TargetFramework, snapshot.TargetFramework); Assert.Same(catalogs, snapshot.Catalogs); AssertEx.CollectionLength(snapshot.Dependencies, 3); Assert.Contains(addedOnRemove, snapshot.Dependencies); }
public void FromChanges_AddingToEmpty() { var targetFramework = new TargetFramework("tfm1"); var catalogs = IProjectCatalogSnapshotFactory.Create(); var previousSnapshot = TargetedDependenciesSnapshot.CreateEmpty(targetFramework, catalogs); var resolved = new TestDependencyModel { ProviderType = "Xxx", Id = "dependency1", OriginalItemSpec = "Dependency1", Caption = "Dependency1", Resolved = true, Flags = DependencyTreeFlags.Resolved, Icon = KnownMonikers.Uninstall, ExpandedIcon = KnownMonikers.Uninstall }; var unresolved = new TestDependencyModel { ProviderType = "Xxx", Id = "dependency2", OriginalItemSpec = "Dependency2", Caption = "Dependency2", Resolved = false, Flags = DependencyTreeFlags.Unresolved, Icon = KnownMonikers.Uninstall, ExpandedIcon = KnownMonikers.Uninstall }; var changes = new DependenciesChangesBuilder(); changes.Added(resolved); changes.Added(unresolved); var snapshot = TargetedDependenciesSnapshot.FromChanges( previousSnapshot, changes.TryBuildChanges() !, catalogs, ImmutableArray <IDependenciesSnapshotFilter> .Empty, new Dictionary <string, IProjectDependenciesSubTreeProvider>(), null); Assert.NotSame(previousSnapshot, snapshot); Assert.Same(catalogs, snapshot.Catalogs); Assert.True(snapshot.HasVisibleUnresolvedDependency); AssertEx.CollectionLength(snapshot.Dependencies, 2); Assert.Contains(snapshot.Dependencies, resolved.Matches); Assert.Contains(snapshot.Dependencies, unresolved.Matches); }
private static ImmutableDictionary <TargetFramework, TargetedDependenciesSnapshot> CreateDependenciesByTargetFramework( IProjectCatalogSnapshot catalogs, params TargetFramework[] targetFrameworks) { var dic = ImmutableDictionary <TargetFramework, TargetedDependenciesSnapshot> .Empty; foreach (var targetFramework in targetFrameworks) { dic = dic.Add(targetFramework, TargetedDependenciesSnapshot.CreateEmpty(targetFramework, catalogs)); } return(dic); }
public void CreateEmpty() { var targetFramework = new TargetFramework("tfm1"); var catalogs = IProjectCatalogSnapshotFactory.Create(); var snapshot = TargetedDependenciesSnapshot.CreateEmpty(targetFramework, catalogs); Assert.Same(targetFramework, snapshot.TargetFramework); Assert.Same(catalogs, snapshot.Catalogs); Assert.Equal(DiagnosticLevel.None, snapshot.MaximumVisibleDiagnosticLevel); Assert.Empty(snapshot.Dependencies); Assert.Equal(DiagnosticLevel.None, snapshot.GetMaximumVisibleDiagnosticLevelForProvider("foo")); }
public void CreateEmpty() { var targetFramework = new TargetFramework("tfm1"); var catalogs = IProjectCatalogSnapshotFactory.Create(); var snapshot = TargetedDependenciesSnapshot.CreateEmpty(targetFramework, catalogs); Assert.Same(targetFramework, snapshot.TargetFramework); Assert.Same(catalogs, snapshot.Catalogs); Assert.False(snapshot.HasVisibleUnresolvedDependency); Assert.Empty(snapshot.Dependencies); Assert.False(snapshot.CheckForUnresolvedDependencies("foo")); }
public void FromChanges_NoChangesAfterBeforeAddFilterDeclinedChange() { var targetFramework = new TargetFramework("tfm1"); var dependency1 = new TestDependency { ProviderType = "Xxx", Id = "dependency1", OriginalItemSpec = "Dependency1", Caption = "Dependency1", SchemaItemType = "Xxx", Resolved = true }; var dependencyModelNew1 = new TestDependencyModel { ProviderType = "Xxx", Id = "newdependency1", OriginalItemSpec = "NewDependency1", Caption = "NewDependency1", SchemaItemType = "Xxx", Resolved = true, Icon = KnownMonikers.Uninstall, ExpandedIcon = KnownMonikers.Uninstall }; var catalogs = IProjectCatalogSnapshotFactory.Create(); var previousSnapshot = new TargetedDependenciesSnapshot( targetFramework, catalogs, ImmutableArray.Create <IDependency>(dependency1)); var changes = new DependenciesChangesBuilder(); changes.Added(dependencyModelNew1); var snapshotFilter = new TestDependenciesSnapshotFilter() .BeforeAddReject("Xxx", "newdependency1"); var snapshot = TargetedDependenciesSnapshot.FromChanges( previousSnapshot, changes.TryBuildChanges() !, catalogs, ImmutableArray.Create <IDependenciesSnapshotFilter>(snapshotFilter), new Dictionary <string, IProjectDependenciesSubTreeProvider>(), null); Assert.True(snapshotFilter.Completed); Assert.Same(previousSnapshot, snapshot); }
public void Constructor_ThrowsIfActiveTargetFrameworkNotEmptyAndNotInDependenciesByTargetFramework_WithTargets() { var tfm1 = new TargetFramework("tfm1"); var tfm2 = new TargetFramework("tfm2"); var tfm3 = new TargetFramework("tfm3"); var ex = Assert.Throws <ArgumentException>(() => new DependenciesSnapshot( activeTargetFramework: tfm1, dependenciesByTargetFramework: ImmutableDictionary <TargetFramework, TargetedDependenciesSnapshot> .Empty .Add(tfm2, TargetedDependenciesSnapshot.CreateEmpty(tfm1, null)) .Add(tfm3, TargetedDependenciesSnapshot.CreateEmpty(tfm1, null)))); Assert.StartsWith("Value \"tfm1\" is unexpected. Must be a key in dependenciesByTargetFramework, which contains \"tfm2\", \"tfm3\".", ex.Message); }
public void FromChanges_NoChanges() { var targetFramework = new TargetFramework("tfm1"); var catalogs = IProjectCatalogSnapshotFactory.Create(); var previousSnapshot = TargetedDependenciesSnapshot.CreateEmpty(targetFramework, catalogs); var snapshot = TargetedDependenciesSnapshot.FromChanges( previousSnapshot, changes: null, catalogs, ImmutableArray <IDependenciesSnapshotFilter> .Empty); Assert.Same(previousSnapshot, snapshot); }
public void FromChanges_NoChangesAfterBeforeRemoveFilterDeclinedChange() { var targetFramework = new TargetFramework("tfm1"); var dependency1 = new TestDependency { ProviderType = "Xxx", Id = "dependency1", OriginalItemSpec = "Dependency1", Caption = "Dependency1", SchemaItemType = "Xxx", Resolved = true }; var dependency2 = new TestDependency { ProviderType = "Xxx", Id = "dependency2", OriginalItemSpec = "Dependency2", Caption = "Dependency2", SchemaItemType = "Xxx", Resolved = true }; var catalogs = IProjectCatalogSnapshotFactory.Create(); var previousSnapshot = new TargetedDependenciesSnapshot( targetFramework, catalogs, ImmutableArray.Create <IDependency>(dependency1, dependency2)); var changes = new DependenciesChangesBuilder(); changes.Removed(dependency1.ProviderType, dependency1.Id); var snapshotFilter = new TestDependenciesSnapshotFilter(); snapshotFilter.BeforeRemoveReject("Xxx", "dependency1"); var snapshot = TargetedDependenciesSnapshot.FromChanges( previousSnapshot, changes.TryBuildChanges() !, catalogs, ImmutableArray.Create <IDependenciesSnapshotFilter>(snapshotFilter), new Dictionary <string, IProjectDependenciesSubTreeProvider>(), null); Assert.Same(previousSnapshot, snapshot); Assert.True(snapshotFilter.Completed); }
public void FromChanges_DifferentModelIdCapitalisation() { var targetFramework = new TargetFramework("tfm1"); var dependencyPrevious = new TestDependency { ProviderType = "Xxx", Id = "dependency1", Resolved = false }; var dependencyModelUpdated = new TestDependencyModel { ProviderType = "XXX", // changed case Id = "DEPENDENCY1", // changed case Resolved = true }; var catalogs = IProjectCatalogSnapshotFactory.Create(); var previousSnapshot = new TargetedDependenciesSnapshot( targetFramework, catalogs, ImmutableArray.Create <IDependency>(dependencyPrevious)); var changes = new DependenciesChangesBuilder(); changes.Added(dependencyModelUpdated); var snapshot = TargetedDependenciesSnapshot.FromChanges( previousSnapshot, changes.TryBuildChanges() !, catalogs, ImmutableArray <IDependenciesSnapshotFilter> .Empty, new Dictionary <string, IProjectDependenciesSubTreeProvider>(), null); Assert.NotSame(previousSnapshot, snapshot); var dependency = Assert.Single(snapshot.Dependencies); Assert.Equal("DEPENDENCY1", dependency.Id); Assert.Equal("XXX", dependency.ProviderType); Assert.True(dependency.Resolved); }
public DependenciesSnapshot SetTargets( ImmutableArray <ITargetFramework> targetFrameworks, ITargetFramework activeTargetFramework) { bool activeChanged = !activeTargetFramework.Equals(ActiveTargetFramework); ImmutableDictionary <ITargetFramework, TargetedDependenciesSnapshot> map = DependenciesByTargetFramework; var diff = new SetDiff <ITargetFramework>(map.Keys, targetFrameworks); map = map.RemoveRange(diff.Removed); map = map.AddRange( diff.Added.Select( added => new KeyValuePair <ITargetFramework, TargetedDependenciesSnapshot>( added, TargetedDependenciesSnapshot.CreateEmpty(added, null)))); if (activeChanged || !ReferenceEquals(map, DependenciesByTargetFramework)) { return(new DependenciesSnapshot(activeTargetFramework, map)); } return(this); }
public void FromChanges_RemovedAndAddedChanges() { var targetFramework = new TargetFramework("tfm1"); var dependency1 = new TestDependency { ProviderType = "Xxx", Id = "dependency1", OriginalItemSpec = "Dependency1", Caption = "Dependency1", SchemaItemType = "Xxx", Resolved = true }; var dependency2 = new TestDependency { ProviderType = "Xxx", Id = "dependency2", OriginalItemSpec = "Dependency2", Caption = "Dependency2", SchemaItemType = "Xxx", Resolved = true }; var dependencyModelAdded1 = new TestDependencyModel { ProviderType = "Xxx", Id = "addeddependency1", OriginalItemSpec = "AddedDependency1", Caption = "AddedDependency1", SchemaItemType = "Xxx", Resolved = true, Icon = KnownMonikers.Uninstall, ExpandedIcon = KnownMonikers.Uninstall }; var dependencyModelAdded2 = new TestDependencyModel { ProviderType = "Xxx", Id = "addeddependency2", OriginalItemSpec = "AddedDependency2", Caption = "AddedDependency2", SchemaItemType = "Xxx", Resolved = true, Icon = KnownMonikers.Uninstall, ExpandedIcon = KnownMonikers.Uninstall }; var dependencyModelAdded3 = new TestDependencyModel { ProviderType = "Xxx", Id = "addeddependency3", OriginalItemSpec = "AddedDependency3", Caption = "AddedDependency3", SchemaItemType = "Xxx", Resolved = true, Icon = KnownMonikers.Uninstall, ExpandedIcon = KnownMonikers.Uninstall }; var dependencyAdded2Changed = new TestDependency { ProviderType = "Xxx", Id = "addeddependency2", OriginalItemSpec = "AddedDependency2Changed", Caption = "AddedDependency2Changed", SchemaItemType = "Xxx", Resolved = true }; var dependencyRemoved1 = new TestDependency { ProviderType = "Xxx", Id = "Removeddependency1", OriginalItemSpec = "RemovedDependency1", Caption = "RemovedDependency1", SchemaItemType = "Xxx", Resolved = true }; var dependencyInsteadRemoved1 = new TestDependency { ProviderType = "Xxx", Id = "InsteadRemoveddependency1", OriginalItemSpec = "InsteadRemovedDependency1", Caption = "InsteadRemovedDependency1", SchemaItemType = "Xxx", Resolved = true }; var catalogs = IProjectCatalogSnapshotFactory.Create(); var previousSnapshot = new TargetedDependenciesSnapshot( targetFramework, catalogs, ImmutableArray.Create <IDependency>(dependency1, dependency2, dependencyRemoved1)); var changes = new DependenciesChangesBuilder(); changes.Added(dependencyModelAdded1); changes.Added(dependencyModelAdded2); changes.Added(dependencyModelAdded3); changes.Removed("Xxx", "Removeddependency1"); var snapshotFilter = new TestDependenciesSnapshotFilter() .BeforeAddReject("Xxx", "addeddependency1") .BeforeAddAccept("Xxx", "addeddependency2", dependencyAdded2Changed) .BeforeAddAccept("Xxx", "addeddependency3") .BeforeRemoveAccept("Xxx", "Removeddependency1", dependencyInsteadRemoved1); var snapshot = TargetedDependenciesSnapshot.FromChanges( previousSnapshot, changes.TryBuildChanges() !, catalogs, ImmutableArray.Create <IDependenciesSnapshotFilter>(snapshotFilter), new Dictionary <string, IProjectDependenciesSubTreeProvider>(), null); Assert.True(snapshotFilter.Completed); Assert.NotSame(previousSnapshot, snapshot); Assert.Same(previousSnapshot.TargetFramework, snapshot.TargetFramework); Assert.Same(catalogs, snapshot.Catalogs); AssertEx.CollectionLength(snapshot.Dependencies, 5); Assert.Contains(snapshot.Dependencies, dep => dep.Id == "dependency1"); Assert.Contains(snapshot.Dependencies, dep => dep.Id == "dependency2"); Assert.Contains(snapshot.Dependencies, dep => dep.Id == "addeddependency2"); Assert.Contains(snapshot.Dependencies, dep => dep.Id == "InsteadRemoveddependency1"); Assert.Contains(snapshot.Dependencies, dep => dep.Id == "addeddependency3"); }
/// <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 occurred.</returns> public static TargetedDependenciesSnapshot FromChanges( TargetedDependenciesSnapshot previousSnapshot, IDependenciesChanges?changes, IProjectCatalogSnapshot?catalogs, ImmutableArray <IDependenciesSnapshotFilter> snapshotFilters) { Requires.NotNull(previousSnapshot, nameof(previousSnapshot)); Requires.Argument(!snapshotFilters.IsDefault, nameof(snapshotFilters), "Cannot be default."); bool anyChanges = false; TargetFramework targetFramework = previousSnapshot.TargetFramework; var dependencyById = previousSnapshot.Dependencies.ToDictionary(IDependencyExtensions.GetDependencyId); if (changes != null && changes.RemovedNodes.Count != 0) { foreach (IDependencyModel removed in changes.RemovedNodes) { dependencyById.Remove(removed.GetDependencyId()); } anyChanges = true; } 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 Add(AddDependencyContext context, IDependencyModel dependencyModel) { // Create the unfiltered dependency IDependency?dependency = new Dependency(dependencyModel); context.Reset(); foreach (IDependenciesSnapshotFilter filter in snapshotFilters) { filter.BeforeAddOrUpdate( dependency, context); dependency = context.GetResult(filter); if (dependency == null) { break; } } if (dependency != null) { // A dependency was accepted DependencyId id = dependencyModel.GetDependencyId(); dependencyById.Remove(id); dependencyById.Add(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> /// Updates the <see cref="TargetedDependenciesSnapshot"/> corresponding to <paramref name="changedTargetFramework"/>, /// returning either: /// <list type="bullet"> /// <item>An updated <see cref="DependenciesSnapshot"/> object, or</item> /// <item>the immutable <paramref name="previousSnapshot"/> if no changes were made.</item> /// </list> /// </summary> /// <remarks> /// As part of the update, each <see cref="IDependenciesSnapshotFilter"/> in <paramref name="snapshotFilters"/> /// is given a chance to influence the addition and removal of dependency data in the returned snapshot. /// </remarks> /// <returns>An updated snapshot, or <paramref name="previousSnapshot"/> if no changes occured.</returns> public static DependenciesSnapshot FromChanges( DependenciesSnapshot previousSnapshot, ITargetFramework changedTargetFramework, IDependenciesChanges?changes, IProjectCatalogSnapshot?catalogs, ImmutableArray <ITargetFramework> targetFrameworks, ITargetFramework?activeTargetFramework, ImmutableArray <IDependenciesSnapshotFilter> snapshotFilters, IReadOnlyDictionary <string, IProjectDependenciesSubTreeProvider> subTreeProviderByProviderType, IImmutableSet <string>?projectItemSpecs) { Requires.NotNull(previousSnapshot, nameof(previousSnapshot)); Requires.NotNull(changedTargetFramework, nameof(changedTargetFramework)); Requires.Argument(!snapshotFilters.IsDefault, nameof(snapshotFilters), "Cannot be default."); Requires.NotNull(subTreeProviderByProviderType, nameof(subTreeProviderByProviderType)); var builder = previousSnapshot.DependenciesByTargetFramework.ToBuilder(); if (!builder.TryGetValue(changedTargetFramework, out TargetedDependenciesSnapshot previousTargetedSnapshot)) { previousTargetedSnapshot = TargetedDependenciesSnapshot.CreateEmpty(changedTargetFramework, catalogs); } bool builderChanged = false; var newTargetedSnapshot = TargetedDependenciesSnapshot.FromChanges( previousTargetedSnapshot, changes, catalogs, snapshotFilters, subTreeProviderByProviderType, projectItemSpecs); if (!ReferenceEquals(previousTargetedSnapshot, newTargetedSnapshot)) { builder[changedTargetFramework] = newTargetedSnapshot; builderChanged = true; } SyncTargetFrameworks(); activeTargetFramework ??= previousSnapshot.ActiveTargetFramework; if (builderChanged) { // Dependencies-by-target-framework has changed return(new DependenciesSnapshot( activeTargetFramework, builder.ToImmutable())); } if (!activeTargetFramework.Equals(previousSnapshot.ActiveTargetFramework)) { // The active target framework changed return(new DependenciesSnapshot( activeTargetFramework, previousSnapshot.DependenciesByTargetFramework)); } // Nothing has changed, so return the same snapshot return(previousSnapshot); void SyncTargetFrameworks() { // Only sync if a the full list of target frameworks has been provided if (targetFrameworks.IsDefault) { return; } // This is a long-winded way of doing this that minimises allocations // Ensure all required target frameworks are present foreach (ITargetFramework targetFramework in targetFrameworks) { if (!builder.ContainsKey(targetFramework)) { builder.Add(targetFramework, TargetedDependenciesSnapshot.CreateEmpty(targetFramework, catalogs)); builderChanged = true; } } // Remove any extra target frameworks if (builder.Count != targetFrameworks.Length) { // NOTE We need "ToList" here as "Except" is lazy, and attempts to remove from the builder // while iterating will throw "Collection was modified" IEnumerable <ITargetFramework> targetFrameworksToRemove = builder.Keys.Except(targetFrameworks).ToList(); foreach (ITargetFramework targetFramework in targetFrameworksToRemove) { builder.Remove(targetFramework); } builderChanged = true; } } }
public void FromChanges_UpdatesLevelDependencies() { var targetFramework = new TargetFramework("tfm1"); var dependencyPrevious = new TestDependency { ProviderType = "Xxx", Id = "tfm1\\xxx\\dependency1", Name = "Dependency1", Caption = "Dependency1", SchemaItemType = "Xxx", Resolved = true }; var dependencyModelAdded = new TestDependencyModel { ProviderType = "Xxx", Id = "dependency1", Name = "Dependency1", Caption = "Dependency1", SchemaItemType = "Xxx", Resolved = true, Icon = KnownMonikers.Uninstall, ExpandedIcon = KnownMonikers.Uninstall }; var dependencyUpdated = new TestDependency { ProviderType = "Xxx", Id = "tfm1\\xxx\\dependency1", Name = "Dependency1", Caption = "Dependency1", SchemaItemType = "Xxx", Resolved = true }; var catalogs = IProjectCatalogSnapshotFactory.Create(); var previousSnapshot = new TargetedDependenciesSnapshot( targetFramework, catalogs, ImmutableArray.Create <IDependency>(dependencyPrevious)); var changes = new DependenciesChangesBuilder(); changes.Added(dependencyModelAdded); var snapshotFilter = new TestDependenciesSnapshotFilter() .BeforeAddAccept(@"tfm1\xxx\dependency1", dependencyUpdated); var snapshot = TargetedDependenciesSnapshot.FromChanges( previousSnapshot, changes.TryBuildChanges() !, catalogs, ImmutableArray.Create <IDependenciesSnapshotFilter>(snapshotFilter), new Dictionary <string, IProjectDependenciesSubTreeProvider>(), null); Assert.True(snapshotFilter.Completed); Assert.NotSame(previousSnapshot, snapshot); Assert.Same(dependencyUpdated, snapshot.Dependencies.Single()); }
/// <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 void FromChanges_ReportedChangesAfterBeforeAddFilterDeclinedChange() { var targetFramework = new TargetFramework("tfm1"); var dependency1 = new TestDependency { ProviderType = "Xxx", Id = "dependency1", OriginalItemSpec = "Dependency1", Caption = "Dependency1", SchemaItemType = "Xxx", Resolved = true }; var dependency2 = new TestDependency { ProviderType = "Xxx", Id = "dependency2", OriginalItemSpec = "Dependency2", Caption = "Dependency2", SchemaItemType = "Xxx", Resolved = true }; var dependencyModelNew1 = new TestDependencyModel { ProviderType = "Xxx", Id = "newdependency1", OriginalItemSpec = "NewDependency1", Caption = "NewDependency1", SchemaItemType = "Xxx", Resolved = true, Icon = KnownMonikers.Uninstall, ExpandedIcon = KnownMonikers.Uninstall }; var catalogs = IProjectCatalogSnapshotFactory.Create(); var previousSnapshot = new TargetedDependenciesSnapshot( targetFramework, catalogs, ImmutableArray.Create <IDependency>(dependency1, dependency2)); var changes = new DependenciesChangesBuilder(); changes.Added(dependencyModelNew1); var filterAddedDependency = new TestDependency { Id = "unexpected" }; var snapshotFilter = new TestDependenciesSnapshotFilter() .BeforeAddReject("Xxx", "newdependency1", addOrUpdate: filterAddedDependency); var snapshot = TargetedDependenciesSnapshot.FromChanges( previousSnapshot, changes.TryBuildChanges() !, catalogs, ImmutableArray.Create <IDependenciesSnapshotFilter>(snapshotFilter)); Assert.True(snapshotFilter.Completed); Assert.NotSame(previousSnapshot, snapshot); Assert.Same(previousSnapshot.TargetFramework, snapshot.TargetFramework); Assert.Same(previousSnapshot.Catalogs, snapshot.Catalogs); AssertEx.CollectionLength(snapshot.Dependencies, 3); Assert.Contains(dependency1, snapshot.Dependencies); Assert.Contains(dependency2, snapshot.Dependencies); Assert.Contains(filterAddedDependency, snapshot.Dependencies); }
/// <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 occurred.</returns> public static TargetedDependenciesSnapshot FromChanges( TargetedDependenciesSnapshot previousSnapshot, IDependenciesChanges?changes, IProjectCatalogSnapshot?catalogs) { Requires.NotNull(previousSnapshot, nameof(previousSnapshot)); bool anyChanges = false; TargetFramework targetFramework = previousSnapshot.TargetFramework; var dependencyById = previousSnapshot.Dependencies.ToDictionary(IDependencyExtensions.GetDependencyId); if (changes != null && changes.RemovedNodes.Count != 0) { foreach (IDependencyModel removed in changes.RemovedNodes) { dependencyById.Remove(removed.GetDependencyId()); } anyChanges = true; } if (changes != null && changes.AddedNodes.Count != 0) { 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 IDependency dependency = new Dependency(added); DeduplicateCaptions(ref dependency, dependencyById); dependencyById[dependency.GetDependencyId()] = dependency; anyChanges = true; } } // 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); }