public void Constructor_ThrowsIfActiveTargetFrameworkNotEmptyAndNotInDependenciesByTargetFramework_NoTargets()
        {
#if false
            var ex = Assert.Throws <ArgumentException>(() => new DependenciesSnapshot(
                                                           activeTargetFramework: new TargetFramework("tfm1"),
                                                           dependenciesByTargetFramework: ImmutableDictionary <TargetFramework, TargetedDependenciesSnapshot> .Empty));

            Assert.StartsWith("Value \"tfm1\" is unexpected. Must be a key in dependenciesByTargetFramework, which contains no items.", ex.Message);
#else
            _ = new DependenciesSnapshot(
                activeTargetFramework: new TargetFramework("tfm1"),
                dependenciesByTargetFramework: ImmutableDictionary <TargetFramework, TargetedDependenciesSnapshot> .Empty);
#endif
        }
        public void Constructor()
        {
            var catalogs        = IProjectCatalogSnapshotFactory.Create();
            var targetFramework = new TargetFramework("tfm1");

            var dependenciesByTargetFramework = CreateDependenciesByTargetFramework(catalogs, targetFramework);

            var snapshot = new DependenciesSnapshot(
                activeTargetFramework: targetFramework,
                dependenciesByTargetFramework);

            Assert.Same(targetFramework, snapshot.ActiveTargetFramework);
            Assert.Same(dependenciesByTargetFramework, snapshot.DependenciesByTargetFramework);
            Assert.Equal(DiagnosticLevel.None, snapshot.MaximumVisibleDiagnosticLevel);
        }
        public void Constructor()
        {
            var catalogs        = IProjectCatalogSnapshotFactory.Create();
            var targetFramework = new TargetFramework("tfm1");

            var dependenciesByTargetFramework = CreateDependenciesByTargetFramework(catalogs, targetFramework);

            var snapshot = new DependenciesSnapshot(
                activeTargetFramework: targetFramework,
                dependenciesByTargetFramework);

            Assert.Same(targetFramework, snapshot.ActiveTargetFramework);
            Assert.Same(dependenciesByTargetFramework, snapshot.DependenciesByTargetFramework);
            Assert.False(snapshot.HasVisibleUnresolvedDependency);
        }
        public void FromChanges_WithDependenciesChanges()
        {
            var catalogs        = IProjectCatalogSnapshotFactory.Create();
            var targetFramework = new TargetFramework("tfm1");
            var dependenciesByTargetFramework = CreateDependenciesByTargetFramework(catalogs, targetFramework);

            var previousSnapshot = new DependenciesSnapshot(
                activeTargetFramework: targetFramework,
                dependenciesByTargetFramework);

            var targetChanges = new DependenciesChangesBuilder();
            var model         = new TestDependencyModel
            {
                ProviderType = "Xxx",
                Id           = "dependency1"
            };

            targetChanges.Added(model);

            var snapshot = DependenciesSnapshot.FromChanges(
                previousSnapshot,
                targetFramework,
                targetChanges.TryBuildChanges() !,
                catalogs,
                targetFrameworks: ImmutableArray.Create <TargetFramework>(targetFramework),
                activeTargetFramework: targetFramework,
                ImmutableArray <IDependenciesSnapshotFilter> .Empty,
                new Dictionary <string, IProjectDependenciesSubTreeProvider>(),
                null);

            Assert.NotSame(previousSnapshot, snapshot);
            Assert.Same(targetFramework, snapshot.ActiveTargetFramework);
            Assert.NotSame(previousSnapshot.DependenciesByTargetFramework, snapshot.DependenciesByTargetFramework);

            var(actualTfm, targetedSnapshot) = Assert.Single(snapshot.DependenciesByTargetFramework);
            Assert.Same(targetFramework, actualTfm);
            var dependency = Assert.Single(targetedSnapshot.Dependencies);

            Assert.Equal("dependency1", dependency.Id);
            Assert.Equal("Xxx", dependency.ProviderType);
        }
        public void FromChanges_NoChanges()
        {
            var catalogs         = IProjectCatalogSnapshotFactory.Create();
            var targetFramework  = new TargetFramework("tfm1");
            var targetFrameworks = ImmutableArray <TargetFramework> .Empty.Add(targetFramework);

            var dependenciesByTargetFramework = CreateDependenciesByTargetFramework(catalogs, targetFramework);

            var previousSnapshot = new DependenciesSnapshot(
                activeTargetFramework: targetFramework,
                dependenciesByTargetFramework);

            var snapshot = DependenciesSnapshot.FromChanges(
                previousSnapshot,
                targetFramework,
                changes: null,
                catalogs,
                targetFrameworks,
                activeTargetFramework: targetFramework);

            Assert.Same(previousSnapshot, snapshot);
        }
 public void Constructor_NoThrowIfActiveTargetFrameworkIsUnsupportedAndNotPresentInDependenciesByTargetFramework()
 {
     _ = new DependenciesSnapshot(
         activeTargetFramework: TargetFramework.Unsupported,
         ImmutableDictionary <TargetFramework, TargetedDependenciesSnapshot> .Empty);
 }
        /// <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;
                }
            }
        }