Пример #1
0
        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);
        }
Пример #9
0
        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;
                }
            }
        }
Пример #16
0
        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;
                }
            }
        }
Пример #18
0
        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);
        }