public void FromChanges_AddingToEmpty() { const string projectPath = @"c:\somefolder\someproject\a.csproj"; var targetFramework = new TargetFramework("tfm1"); var catalogs = IProjectCatalogSnapshotFactory.Create(); var previousSnapshot = TargetedDependenciesSnapshot.CreateEmpty(projectPath, targetFramework, catalogs); var resolvedTop = IDependencyModelFactory.FromJson(@" { ""ProviderType"": ""Xxx"", ""Id"": ""dependency1"", ""Name"": ""Dependency1"", ""Caption"": ""Dependency1"", ""Resolved"": ""true"", ""TopLevel"": ""true"" }", icon: KnownMonikers.Uninstall, expandedIcon: KnownMonikers.Uninstall); var unresolved = IDependencyModelFactory.FromJson(@" { ""ProviderType"": ""Xxx"", ""Id"": ""dependency2"", ""Name"": ""Dependency2"", ""Caption"": ""Dependency2"", ""Resolved"": ""false"", ""TopLevel"": ""false"" }", icon: KnownMonikers.Uninstall, expandedIcon: KnownMonikers.Uninstall); var changes = new DependenciesChangesBuilder(); changes.Added(resolvedTop); changes.Added(unresolved); const string updatedProjectPath = "updatedProjectPath"; var snapshot = TargetedDependenciesSnapshot.FromChanges( updatedProjectPath, previousSnapshot, changes.Build(), catalogs, ImmutableArray <IDependenciesSnapshotFilter> .Empty, new Dictionary <string, IProjectDependenciesSubTreeProvider>(), null); Assert.NotSame(previousSnapshot, snapshot); Assert.Same(updatedProjectPath, snapshot.ProjectPath); Assert.Same(catalogs, snapshot.Catalogs); Assert.True(snapshot.HasUnresolvedDependency); AssertEx.CollectionLength(snapshot.DependenciesWorld, 2); AssertEx.CollectionLength(snapshot.TopLevelDependencies, 1); Assert.True(resolvedTop.Matches(snapshot.TopLevelDependencies.Single(), targetFramework)); Assert.True(resolvedTop.Matches(snapshot.DependenciesWorld["tfm1\\Xxx\\dependency1"], targetFramework)); Assert.True(unresolved.Matches(snapshot.DependenciesWorld["tfm1\\Xxx\\dependency2"], targetFramework)); }
public void FromChanges_NoChangesAfterBeforeRemoveFilterDeclinedChange() { const string projectPath = @"c:\somefolder\someproject\a.csproj"; var targetFramework = new TargetFramework("tfm1"); var dependencyTop1 = new TestDependency { ProviderType = "Xxx", Id = Dependency.GetID(targetFramework, "Xxx", "topdependency1"), Name = "TopDependency1", Caption = "TopDependency1", SchemaItemType = "Xxx", Resolved = true, TopLevel = true }; var dependencyChild1 = new TestDependency { ProviderType = "Xxx", Id = Dependency.GetID(targetFramework, "Xxx", "childdependency1"), Name = "ChildDependency1", Caption = "ChildDependency1", SchemaItemType = "Xxx", Resolved = true, TopLevel = false }; var catalogs = IProjectCatalogSnapshotFactory.Create(); var previousSnapshot = new TargetedDependenciesSnapshot( projectPath, targetFramework, catalogs, new IDependency[] { dependencyTop1, dependencyChild1 }.ToImmutableDictionary(d => d.Id).WithComparers(StringComparer.OrdinalIgnoreCase)); var changes = new DependenciesChangesBuilder(); changes.Removed(dependencyTop1.ProviderType, dependencyTop1.Id); var snapshotFilter = new TestDependenciesSnapshotFilter(); var snapshot = TargetedDependenciesSnapshot.FromChanges( projectPath, previousSnapshot, changes.TryBuildChanges() !, catalogs, ImmutableArray.Create <IDependenciesSnapshotFilter>(snapshotFilter), new Dictionary <string, IProjectDependenciesSubTreeProvider>(), null); Assert.Same(previousSnapshot, snapshot); Assert.True(snapshotFilter.Completed); }
public void FromChanges_NoChangesAfterBeforeRemoveFilterDeclinedChange() { const string projectPath = @"c:\somefolder\someproject\a.csproj"; var targetFramework = new TargetFramework("tfm1"); var dependencyTop1 = IDependencyFactory.FromJson(@" { ""ProviderType"": ""Xxx"", ""Id"": ""tfm1\\xxx\\topdependency1"", ""Name"":""TopDependency1"", ""Caption"":""TopDependency1"", ""SchemaItemType"":""Xxx"", ""Resolved"":""true"", ""TopLevel"":""true"" }"); var dependencyChild1 = IDependencyFactory.FromJson(@" { ""ProviderType"": ""Xxx"", ""Id"": ""tfm1\\xxx\\childdependency1"", ""Name"":""ChildDependency1"", ""Caption"":""ChildDependency1"", ""SchemaItemType"":""Xxx"", ""Resolved"":""true"", ""TopLevel"":""false"" }"); var catalogs = IProjectCatalogSnapshotFactory.Create(); var previousSnapshot = ITargetedDependenciesSnapshotFactory.Implement( projectPath: projectPath, targetFramework: targetFramework, catalogs: catalogs, dependenciesWorld: new [] { dependencyTop1, dependencyChild1 }, topLevelDependencies: new [] { dependencyTop1 }); var changes = new DependenciesChangesBuilder(); changes.Removed(dependencyTop1.ProviderType, dependencyTop1.Id); var snapshotFilter = new TestDependenciesSnapshotFilter(); var snapshot = TargetedDependenciesSnapshot.FromChanges( projectPath, previousSnapshot, changes.Build(), catalogs, ImmutableArray.Create <IDependenciesSnapshotFilter>(snapshotFilter), new Dictionary <string, IProjectDependenciesSubTreeProvider>(), null); Assert.Same(previousSnapshot, snapshot); }
public void FromChanges_NoChangesAfterBeforeRemoveFilterDeclinedChange() { const string projectPath = @"c:\somefolder\someproject\a.csproj"; var targetFramework = new TargetFramework("tfm1"); var dependencyTop1 = new TestDependency { ProviderType = "Xxx", Id = Dependency.GetID(targetFramework, "Xxx", "topdependency1"), Name = "TopDependency1", Caption = "TopDependency1", SchemaItemType = "Xxx", Resolved = true, TopLevel = true }; var dependencyChild1 = new TestDependency { ProviderType = "Xxx", Id = Dependency.GetID(targetFramework, "Xxx", "childdependency1"), Name = "ChildDependency1", Caption = "ChildDependency1", SchemaItemType = "Xxx", Resolved = true, TopLevel = false }; var catalogs = IProjectCatalogSnapshotFactory.Create(); var previousSnapshot = ITargetedDependenciesSnapshotFactory.Implement( projectPath: projectPath, targetFramework: targetFramework, catalogs: catalogs, dependenciesWorld: new [] { dependencyTop1, dependencyChild1 }, topLevelDependencies: new [] { dependencyTop1 }); var changes = new DependenciesChangesBuilder(); changes.Removed(dependencyTop1.ProviderType, dependencyTop1.Id); var snapshotFilter = new TestDependenciesSnapshotFilter(); var snapshot = TargetedDependenciesSnapshot.FromChanges( projectPath, previousSnapshot, changes.Build(), catalogs, ImmutableArray.Create <IDependenciesSnapshotFilter>(snapshotFilter), new Dictionary <string, IProjectDependenciesSubTreeProvider>(), null); Assert.Same(previousSnapshot, snapshot); }
private static ImmutableDictionary <ITargetFramework, TargetedDependenciesSnapshot> CreateDependenciesByTargetFramework( string projectPath, IProjectCatalogSnapshot catalogs, params ITargetFramework[] targetFrameworks) { var dic = ImmutableDictionary <ITargetFramework, TargetedDependenciesSnapshot> .Empty; foreach (var targetFramework in targetFrameworks) { dic = dic.Add(targetFramework, TargetedDependenciesSnapshot.CreateEmpty(projectPath, targetFramework, catalogs)); } return(dic); }
public static TargetedDependenciesSnapshot FromChanges( string projectPath, ITargetFramework targetFramework, ITargetedDependenciesSnapshot previousSnapshot, IDependenciesChanges changes, IProjectCatalogSnapshot catalogs, IEnumerable <IDependenciesSnapshotFilter> snapshotFilters, out bool anyChanges) { var newSnapshot = new TargetedDependenciesSnapshot(projectPath, targetFramework, previousSnapshot, catalogs); anyChanges = newSnapshot.MergeChanges(changes, snapshotFilters); return(newSnapshot); }
public void CreateEmpty() { const string projectPath = @"c:\somefolder\someproject\a.csproj"; var targetFramework = new TargetFramework("tfm1"); var catalogs = IProjectCatalogSnapshotFactory.Create(); var snapshot = TargetedDependenciesSnapshot.CreateEmpty(projectPath, targetFramework, catalogs); Assert.Same(projectPath, snapshot.ProjectPath); Assert.Same(targetFramework, snapshot.TargetFramework); Assert.Same(catalogs, snapshot.Catalogs); Assert.False(snapshot.HasReachableVisibleUnresolvedDependency); Assert.Empty(snapshot.TopLevelDependencies); Assert.Empty(snapshot.DependenciesWorld); Assert.False(snapshot.CheckForUnresolvedDependencies("foo")); Assert.Empty(snapshot.GetDependencyChildren(new TestDependency())); }
private bool MergeChanges( ImmutableDictionary <ITargetFramework, IDependenciesChanges> changes, IProjectCatalogSnapshot catalogs, IEnumerable <IDependenciesSnapshotFilter> snapshotFilters, IEnumerable <IProjectDependenciesSubTreeProvider> subTreeProviders, HashSet <string> projectItemSpecs) { var anyChanges = false; var builder = _targets.ToBuilder(); foreach (var change in changes) { builder.TryGetValue(change.Key, out ITargetedDependenciesSnapshot previousSnapshot); var newTargetedSnapshot = TargetedDependenciesSnapshot.FromChanges( ProjectPath, change.Key, previousSnapshot, change.Value, catalogs, snapshotFilters, subTreeProviders, projectItemSpecs, out bool anyTfmChanges); builder[change.Key] = newTargetedSnapshot; if (anyTfmChanges) { anyChanges = true; } } // now get rid of empty target frameworks (if there no any dependencies for them) foreach (var targetKvp in builder.ToList()) { if (targetKvp.Value.DependenciesWorld.Count <= 0) { anyChanges = true; builder.Remove(targetKvp.Key); } } _targets = builder.ToImmutableDictionary(); return(anyChanges); }
public void FromChanges_NullChanges_Throws() { const string projectPath = @"c:\somefolder\someproject\a.csproj"; var targetFramework = new TargetFramework("tfm1"); var catalogs = IProjectCatalogSnapshotFactory.Create(); var previousSnapshot = TargetedDependenciesSnapshot.CreateEmpty(projectPath, targetFramework, catalogs); Assert.Throws <ArgumentNullException>( "changes", () => TargetedDependenciesSnapshot.FromChanges( projectPath, previousSnapshot, null !, catalogs, ImmutableArray <IDependenciesSnapshotFilter> .Empty, new Dictionary <string, IProjectDependenciesSubTreeProvider>(), null)); }
public void FromChanges_NoChanges() { const string projectPath = @"c:\somefolder\someproject\a.csproj"; var targetFramework = new TargetFramework("tfm1"); var catalogs = IProjectCatalogSnapshotFactory.Create(); var previousSnapshot = TargetedDependenciesSnapshot.CreateEmpty(projectPath, targetFramework, catalogs); var snapshot = TargetedDependenciesSnapshot.FromChanges( projectPath, previousSnapshot, changes: null, catalogs, ImmutableArray <IDependenciesSnapshotFilter> .Empty, new Dictionary <string, IProjectDependenciesSubTreeProvider>(), null); Assert.Same(previousSnapshot, snapshot); }
public void TConstructor() { const string projectPath = @"c:\somefolder\someproject\a.csproj"; var targetFramework = new TargetFramework("tfm1"); var catalogs = IProjectCatalogSnapshotFactory.Create(); var snapshot = new TargetedDependenciesSnapshot( projectPath, targetFramework, catalogs, ImmutableDictionary <string, IDependency> .Empty); Assert.NotNull(snapshot.TargetFramework); Assert.Equal("tfm1", snapshot.TargetFramework.FullName); Assert.Equal(projectPath, snapshot.ProjectPath); Assert.Equal(catalogs, snapshot.Catalogs); Assert.Empty(snapshot.TopLevelDependencies); Assert.Empty(snapshot.DependenciesWorld); }
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(ProjectPath, added, null)))); if (activeChanged || !ReferenceEquals(map, DependenciesByTargetFramework)) { return(new DependenciesSnapshot(ProjectPath, activeTargetFramework, map)); } return(this); }
/// <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( string projectPath, DependenciesSnapshot previousSnapshot, ITargetFramework changedTargetFramework, IDependenciesChanges?changes, IProjectCatalogSnapshot?catalogs, ImmutableArray <ITargetFramework> targetFrameworks, ITargetFramework?activeTargetFramework, ImmutableArray <IDependenciesSnapshotFilter> snapshotFilters, IReadOnlyDictionary <string, IProjectDependenciesSubTreeProvider> subTreeProviderByProviderType, IImmutableSet <string>?projectItemSpecs) { Requires.NotNullOrWhiteSpace(projectPath, nameof(projectPath)); 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(projectPath, changedTargetFramework, catalogs); } bool builderChanged = false; var newTargetedSnapshot = TargetedDependenciesSnapshot.FromChanges( projectPath, 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( projectPath, activeTargetFramework, builder.ToImmutable())); } if (!activeTargetFramework.Equals(previousSnapshot.ActiveTargetFramework)) { // The active target framework changed return(new DependenciesSnapshot( projectPath, activeTargetFramework, previousSnapshot.DependenciesByTargetFramework)); } if (projectPath != previousSnapshot.ProjectPath) { // The project path changed return(new DependenciesSnapshot( projectPath, 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(projectPath, targetFramework, catalogs)); builderChanged = true; } } // Remove any extra target frameworks if (builder.Count != targetFrameworks.Length) { IEnumerable <ITargetFramework> targetFrameworksToRemove = builder.Keys.Except(targetFrameworks); foreach (ITargetFramework targetFramework in targetFrameworksToRemove) { builder.Remove(targetFramework); } builderChanged = true; } } }
/// <summary> /// Returns a IDependencyViewModel for given dependency. /// </summary> public static IDependencyViewModel ToViewModel(this IDependency dependency, TargetedDependenciesSnapshot snapshot) { bool hasUnresolvedDependency = !dependency.Resolved || snapshot.ShouldAppearUnresolved(dependency); return(new DependencyViewModel(dependency, hasUnresolvedDependency: hasUnresolvedDependency)); }
public void FromChanges_ReportedChangesAfterBeforeAddFilterDeclinedChange() { const string projectPath = @"c:\somefolder\someproject\a.csproj"; var targetFramework = new TargetFramework("tfm1"); var dependencyTop1 = IDependencyFactory.FromJson(@" { ""ProviderType"": ""Xxx"", ""Id"": ""tfm1\\xxx\\topdependency1"", ""Name"":""TopDependency1"", ""Caption"":""TopDependency1"", ""SchemaItemType"":""Xxx"", ""Resolved"":""true"", ""TopLevel"":""true"" }"); var dependencyChild1 = IDependencyFactory.FromJson(@" { ""ProviderType"": ""Xxx"", ""Id"": ""tfm1\\xxx\\childdependency1"", ""Name"":""ChildDependency1"", ""Caption"":""ChildDependency1"", ""SchemaItemType"":""Xxx"", ""Resolved"":""true"", ""TopLevel"":""false"" }"); var dependencyModelNew1 = IDependencyModelFactory.FromJson(@" { ""ProviderType"": ""Xxx"", ""Id"": ""newdependency1"", ""Name"":""NewDependency1"", ""Caption"":""NewDependency1"", ""SchemaItemType"":""Xxx"", ""Resolved"":""true"" }", icon: KnownMonikers.Uninstall, expandedIcon: KnownMonikers.Uninstall); var catalogs = IProjectCatalogSnapshotFactory.Create(); var previousSnapshot = ITargetedDependenciesSnapshotFactory.Implement( projectPath: projectPath, targetFramework: targetFramework, catalogs: catalogs, dependenciesWorld: new [] { dependencyTop1, dependencyChild1 }, topLevelDependencies: new [] { dependencyTop1 }); var changes = new DependenciesChangesBuilder(); changes.Added(dependencyModelNew1); var filterAddedDependency = new TestDependency { Id = "unexpected", TopLevel = true }; var snapshotFilter = new TestDependenciesSnapshotFilter() .BeforeAddReject(@"tfm1\xxx\newdependency1", addOrUpdate: filterAddedDependency); var snapshot = TargetedDependenciesSnapshot.FromChanges( projectPath, previousSnapshot, changes.Build(), catalogs, ImmutableArray.Create <IDependenciesSnapshotFilter>(snapshotFilter), new Dictionary <string, IProjectDependenciesSubTreeProvider>(), null); Assert.NotSame(previousSnapshot, snapshot); Assert.Same(previousSnapshot.TargetFramework, snapshot.TargetFramework); Assert.Same(previousSnapshot.ProjectPath, snapshot.ProjectPath); Assert.Same(previousSnapshot.Catalogs, snapshot.Catalogs); AssertEx.CollectionLength(snapshot.TopLevelDependencies, 2); Assert.Contains(dependencyTop1, snapshot.TopLevelDependencies); Assert.Contains(filterAddedDependency, snapshot.TopLevelDependencies); AssertEx.CollectionLength(snapshot.DependenciesWorld, 3); Assert.Contains(dependencyTop1, snapshot.DependenciesWorld.Values); Assert.Contains(dependencyChild1, snapshot.DependenciesWorld.Values); Assert.Contains(filterAddedDependency, snapshot.DependenciesWorld.Values); }
public void FromChanges_UpdatesTopLevelDependencies() { const string projectPath = @"c:\somefolder\someproject\a.csproj"; var targetFramework = new TargetFramework("tfm1"); var dependencyTopPrevious = new TestDependency { ProviderType = "Xxx", Id = "tfm1\\xxx\\topdependency1", Name = "TopDependency1", Caption = "TopDependency1", SchemaItemType = "Xxx", Resolved = true }; var dependencyModelTopAdded = new TestDependencyModel { ProviderType = "Xxx", Id = "topdependency1", Name = "TopDependency1", Caption = "TopDependency1", SchemaItemType = "Xxx", Resolved = true, Icon = KnownMonikers.Uninstall, ExpandedIcon = KnownMonikers.Uninstall }; var dependencyTopUpdated = new TestDependency { ProviderType = "Xxx", Id = "tfm1\\xxx\\topdependency1", Name = "TopDependency1", Caption = "TopDependency1", SchemaItemType = "Xxx", Resolved = true }; var catalogs = IProjectCatalogSnapshotFactory.Create(); var previousSnapshot = new TargetedDependenciesSnapshot( projectPath, targetFramework, catalogs, new IDependency[] { dependencyTopPrevious }.ToImmutableDictionary(d => d.Id).WithComparers(StringComparer.OrdinalIgnoreCase)); var changes = new DependenciesChangesBuilder(); changes.Added(dependencyModelTopAdded); var snapshotFilter = new TestDependenciesSnapshotFilter() .BeforeAddAccept(@"tfm1\xxx\topdependency1", dependencyTopUpdated); var snapshot = TargetedDependenciesSnapshot.FromChanges( projectPath, previousSnapshot, changes.TryBuildChanges() !, catalogs, ImmutableArray.Create <IDependenciesSnapshotFilter>(snapshotFilter), new Dictionary <string, IProjectDependenciesSubTreeProvider>(), null); Assert.True(snapshotFilter.Completed); Assert.NotSame(previousSnapshot, snapshot); Assert.Same(dependencyTopUpdated, snapshot.TopLevelDependencies.Single()); }
public void FromChanges_ReportedChangesAfterBeforeAddFilterDeclinedChange() { const string projectPath = @"c:\somefolder\someproject\a.csproj"; var targetFramework = new TargetFramework("tfm1"); var dependencyTop1 = new TestDependency { ProviderType = "Xxx", Id = "tfm1\\xxx\\topdependency1", Name = "TopDependency1", Caption = "TopDependency1", SchemaItemType = "Xxx", Resolved = true, TopLevel = true }; var dependencyChild1 = new TestDependency { ProviderType = "Xxx", Id = "tfm1\\xxx\\childdependency1", Name = "ChildDependency1", Caption = "ChildDependency1", SchemaItemType = "Xxx", Resolved = true, TopLevel = false }; var dependencyModelNew1 = new TestDependencyModel { ProviderType = "Xxx", Id = "newdependency1", Name = "NewDependency1", Caption = "NewDependency1", SchemaItemType = "Xxx", Resolved = true, Icon = KnownMonikers.Uninstall, ExpandedIcon = KnownMonikers.Uninstall }; var catalogs = IProjectCatalogSnapshotFactory.Create(); var previousSnapshot = new TargetedDependenciesSnapshot( projectPath, targetFramework, catalogs, new IDependency[] { dependencyTop1, dependencyChild1 }.ToImmutableDictionary(d => d.Id).WithComparers(StringComparer.OrdinalIgnoreCase)); var changes = new DependenciesChangesBuilder(); changes.Added(dependencyModelNew1); var filterAddedDependency = new TestDependency { Id = "unexpected", TopLevel = true }; var snapshotFilter = new TestDependenciesSnapshotFilter() .BeforeAddReject(@"tfm1\xxx\newdependency1", addOrUpdate: filterAddedDependency); var snapshot = TargetedDependenciesSnapshot.FromChanges( projectPath, 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(previousSnapshot.ProjectPath, snapshot.ProjectPath); Assert.Same(previousSnapshot.Catalogs, snapshot.Catalogs); AssertEx.CollectionLength(snapshot.TopLevelDependencies, 2); Assert.Contains(dependencyTop1, snapshot.TopLevelDependencies); Assert.Contains(filterAddedDependency, snapshot.TopLevelDependencies); AssertEx.CollectionLength(snapshot.DependenciesWorld, 3); Assert.Contains(dependencyTop1, snapshot.DependenciesWorld.Values); Assert.Contains(dependencyChild1, snapshot.DependenciesWorld.Values); Assert.Contains(filterAddedDependency, snapshot.DependenciesWorld.Values); }
public void FromChanges_RemovedAndAddedChanges() { const string projectPath = @"c:\somefolder\someproject\a.csproj"; var targetFramework = new TargetFramework("tfm1"); var dependencyTop1 = new TestDependency { ProviderType = "Xxx", Id = "tfm1\\xxx\\topdependency1", Name = "TopDependency1", Caption = "TopDependency1", SchemaItemType = "Xxx", Resolved = true, TopLevel = true }; var dependencyChild1 = new TestDependency { ProviderType = "Xxx", Id = "tfm1\\xxx\\childdependency1", Name = "ChildDependency1", Caption = "ChildDependency1", SchemaItemType = "Xxx", Resolved = true, TopLevel = false }; var dependencyModelAdded1 = new TestDependencyModel { ProviderType = "Xxx", Id = "addeddependency1", Name = "AddedDependency1", Caption = "AddedDependency1", SchemaItemType = "Xxx", Resolved = true, TopLevel = false, Icon = KnownMonikers.Uninstall, ExpandedIcon = KnownMonikers.Uninstall }; var dependencyModelAdded2 = new TestDependencyModel { ProviderType = "Xxx", Id = "addeddependency2", Name = "AddedDependency2", Caption = "AddedDependency2", SchemaItemType = "Xxx", Resolved = true, TopLevel = false, Icon = KnownMonikers.Uninstall, ExpandedIcon = KnownMonikers.Uninstall }; var dependencyModelAdded3 = new TestDependencyModel { ProviderType = "Xxx", Id = "addeddependency3", Name = "AddedDependency3", Caption = "AddedDependency3", SchemaItemType = "Xxx", Resolved = true, TopLevel = false, Icon = KnownMonikers.Uninstall, ExpandedIcon = KnownMonikers.Uninstall }; var dependencyAdded2Changed = new TestDependency { ProviderType = "Xxx", Id = "tfm1\\xxx\\addeddependency2", Name = "AddedDependency2Changed", Caption = "AddedDependency2Changed", SchemaItemType = "Xxx", Resolved = true, TopLevel = true }; var dependencyRemoved1 = new TestDependency { ProviderType = "Xxx", Id = "tfm1\\xxx\\Removeddependency1", Name = "RemovedDependency1", Caption = "RemovedDependency1", SchemaItemType = "Xxx", Resolved = true, TopLevel = false }; var dependencyInsteadRemoved1 = new TestDependency { ProviderType = "Xxx", Id = "tfm1\\xxx\\InsteadRemoveddependency1", Name = "InsteadRemovedDependency1", Caption = "InsteadRemovedDependency1", SchemaItemType = "Xxx", Resolved = true, TopLevel = false }; Assert.True(dependencyTop1.TopLevel); Assert.False(dependencyChild1.TopLevel); Assert.False(dependencyRemoved1.TopLevel); var catalogs = IProjectCatalogSnapshotFactory.Create(); var previousSnapshot = new TargetedDependenciesSnapshot( projectPath, targetFramework, catalogs, new IDependency[] { dependencyTop1, dependencyChild1, dependencyRemoved1 }.ToImmutableDictionary(d => d.Id).WithComparers(StringComparer.OrdinalIgnoreCase)); var changes = new DependenciesChangesBuilder(); changes.Added(dependencyModelAdded1); changes.Added(dependencyModelAdded2); changes.Added(dependencyModelAdded3); changes.Removed("Xxx", "Removeddependency1"); var snapshotFilter = new TestDependenciesSnapshotFilter() .BeforeAddReject(@"tfm1\xxx\addeddependency1") .BeforeAddAccept(@"tfm1\xxx\addeddependency2", dependencyAdded2Changed) .BeforeAddAccept(@"tfm1\xxx\addeddependency3") .BeforeRemoveAccept(@"tfm1\xxx\Removeddependency1", dependencyInsteadRemoved1); var snapshot = TargetedDependenciesSnapshot.FromChanges( projectPath, 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(projectPath, snapshot.ProjectPath); Assert.Same(catalogs, snapshot.Catalogs); AssertEx.CollectionLength(snapshot.TopLevelDependencies, 2); Assert.Contains(snapshot.TopLevelDependencies, x => x.Id.Equals(@"tfm1\xxx\topdependency1")); Assert.Contains(snapshot.TopLevelDependencies, x => x.Id.Equals(@"tfm1\xxx\addeddependency2") && x.Caption.Equals("AddedDependency2Changed")); AssertEx.CollectionLength(snapshot.DependenciesWorld, 5); Assert.True(snapshot.DependenciesWorld.ContainsKey(@"tfm1\xxx\topdependency1")); Assert.True(snapshot.DependenciesWorld.ContainsKey(@"tfm1\xxx\childdependency1")); Assert.True(snapshot.DependenciesWorld.ContainsKey(@"tfm1\xxx\addeddependency2")); Assert.True(snapshot.DependenciesWorld.ContainsKey(@"tfm1\xxx\InsteadRemoveddependency1")); Assert.True(snapshot.DependenciesWorld.ContainsKey(@"tfm1\xxx\addeddependency3")); }
public void FromChanges_RemovedAndAddedChanges() { const string projectPath = @"c:\somefolder\someproject\a.csproj"; var targetFramework = new TargetFramework("tfm1"); var dependencyTop1 = IDependencyFactory.FromJson(@" { ""ProviderType"": ""Xxx"", ""Id"": ""tfm1\\xxx\\topdependency1"", ""Name"":""TopDependency1"", ""Caption"":""TopDependency1"", ""SchemaItemType"":""Xxx"", ""Resolved"":""true"", ""TopLevel"":""true"" }"); var dependencyChild1 = IDependencyFactory.FromJson(@" { ""ProviderType"": ""Xxx"", ""Id"": ""tfm1\\xxx\\childdependency1"", ""Name"":""ChildDependency1"", ""Caption"":""ChildDependency1"", ""SchemaItemType"":""Xxx"", ""Resolved"":""true"", ""TopLevel"":""false"" }"); var dependencyModelAdded1 = IDependencyModelFactory.FromJson(@" { ""ProviderType"": ""Xxx"", ""Id"": ""addeddependency1"", ""Name"":""AddedDependency1"", ""Caption"":""AddedDependency1"", ""SchemaItemType"":""Xxx"", ""Resolved"":""true"", ""TopLevel"":""false"" }", icon: KnownMonikers.Uninstall, expandedIcon: KnownMonikers.Uninstall); var dependencyModelAdded2 = IDependencyModelFactory.FromJson(@" { ""ProviderType"": ""Xxx"", ""Id"": ""addeddependency2"", ""Name"":""AddedDependency2"", ""Caption"":""AddedDependency2"", ""SchemaItemType"":""Xxx"", ""Resolved"":""true"", ""TopLevel"":""false"" }", icon: KnownMonikers.Uninstall, expandedIcon: KnownMonikers.Uninstall); var dependencyModelAdded3 = IDependencyModelFactory.FromJson(@" { ""ProviderType"": ""Xxx"", ""Id"": ""addeddependency3"", ""Name"":""AddedDependency3"", ""Caption"":""AddedDependency3"", ""SchemaItemType"":""Xxx"", ""Resolved"":""true"", ""TopLevel"":""false"" }", icon: KnownMonikers.Uninstall, expandedIcon: KnownMonikers.Uninstall); var dependencyAdded2Changed = IDependencyFactory.FromJson(@" { ""ProviderType"": ""Xxx"", ""Id"": ""tfm1\\xxx\\addeddependency2"", ""Name"":""AddedDependency2Changed"", ""Caption"":""AddedDependency2Changed"", ""SchemaItemType"":""Xxx"", ""Resolved"":""true"", ""TopLevel"":""true"" }"); var dependencyRemoved1 = IDependencyFactory.FromJson(@" { ""ProviderType"": ""Xxx"", ""Id"": ""tfm1\\xxx\\Removeddependency1"", ""Name"":""RemovedDependency1"", ""Caption"":""RemovedDependency1"", ""SchemaItemType"":""Xxx"", ""Resolved"":""true"", ""TopLevel"":""false"" }"); var dependencyInsteadRemoved1 = IDependencyFactory.FromJson(@" { ""ProviderType"": ""Xxx"", ""Id"": ""tfm1\\xxx\\InsteadRemoveddependency1"", ""Name"":""InsteadRemovedDependency1"", ""Caption"":""InsteadRemovedDependency1"", ""SchemaItemType"":""Xxx"", ""Resolved"":""true"", ""TopLevel"":""false"" }"); Assert.True(dependencyTop1.TopLevel); Assert.False(dependencyChild1.TopLevel); Assert.False(dependencyRemoved1.TopLevel); var catalogs = IProjectCatalogSnapshotFactory.Create(); var previousSnapshot = ITargetedDependenciesSnapshotFactory.Implement( projectPath: projectPath, targetFramework: targetFramework, catalogs: catalogs, dependenciesWorld: new [] { dependencyTop1, dependencyChild1, dependencyRemoved1 }, topLevelDependencies: new [] { dependencyTop1 }); var changes = new DependenciesChangesBuilder(); changes.Added(dependencyModelAdded1); changes.Added(dependencyModelAdded2); changes.Added(dependencyModelAdded3); changes.Removed("Xxx", "Removeddependency1"); var snapshotFilter = new TestDependenciesSnapshotFilter() .BeforeAddReject(@"tfm1\xxx\addeddependency1") .BeforeAddAccept(@"tfm1\xxx\addeddependency2", dependencyAdded2Changed) .BeforeAddAccept(@"tfm1\xxx\addeddependency3") .BeforeRemoveAccept(@"tfm1\xxx\Removeddependency1", dependencyInsteadRemoved1); var snapshot = TargetedDependenciesSnapshot.FromChanges( projectPath, previousSnapshot, changes.Build(), catalogs, ImmutableArray.Create <IDependenciesSnapshotFilter>(snapshotFilter), new Dictionary <string, IProjectDependenciesSubTreeProvider>(), null); Assert.NotSame(previousSnapshot, snapshot); Assert.Same(previousSnapshot.TargetFramework, snapshot.TargetFramework); Assert.Same(projectPath, snapshot.ProjectPath); Assert.Same(catalogs, snapshot.Catalogs); AssertEx.CollectionLength(snapshot.TopLevelDependencies, 2); Assert.Contains(snapshot.TopLevelDependencies, x => x.Id.Equals(@"tfm1\xxx\topdependency1")); Assert.Contains(snapshot.TopLevelDependencies, x => x.Id.Equals(@"tfm1\xxx\addeddependency2") && x.Caption.Equals("AddedDependency2Changed")); AssertEx.CollectionLength(snapshot.DependenciesWorld, 5); Assert.True(snapshot.DependenciesWorld.ContainsKey(@"tfm1\xxx\topdependency1")); Assert.True(snapshot.DependenciesWorld.ContainsKey(@"tfm1\xxx\childdependency1")); Assert.True(snapshot.DependenciesWorld.ContainsKey(@"tfm1\xxx\addeddependency2")); Assert.True(snapshot.DependenciesWorld.ContainsKey(@"tfm1\xxx\InsteadRemoveddependency1")); Assert.True(snapshot.DependenciesWorld.ContainsKey(@"tfm1\xxx\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 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; } } }
public void FromChanges_UpdatesTopLevelDependencies() { const string projectPath = @"c:\somefolder\someproject\a.csproj"; var targetFramework = new TargetFramework("tfm1"); var dependencyTopPrevious = IDependencyFactory.FromJson(@" { ""ProviderType"": ""Xxx"", ""Id"": ""tfm1\\xxx\\topdependency1"", ""Name"": ""TopDependency1"", ""Caption"": ""TopDependency1"", ""SchemaItemType"": ""Xxx"", ""Resolved"": ""true"" }"); var dependencyModelTopAdded = IDependencyModelFactory.FromJson(@" { ""ProviderType"": ""Xxx"", ""Id"": ""topdependency1"", ""Name"": ""TopDependency1"", ""Caption"": ""TopDependency1"", ""SchemaItemType"": ""Xxx"", ""Resolved"": ""true"" }", icon: KnownMonikers.Uninstall, expandedIcon: KnownMonikers.Uninstall); var dependencyTopUpdated = IDependencyFactory.FromJson(@" { ""ProviderType"": ""Xxx"", ""Id"": ""tfm1\\xxx\\topdependency1"", ""Name"": ""TopDependency1"", ""Caption"": ""TopDependency1"", ""SchemaItemType"": ""Xxx"", ""Resolved"": ""true"" }"); var catalogs = IProjectCatalogSnapshotFactory.Create(); var previousSnapshot = ITargetedDependenciesSnapshotFactory.Implement( projectPath: projectPath, targetFramework: targetFramework, catalogs: catalogs, dependenciesWorld: new[] { dependencyTopPrevious }, topLevelDependencies: new[] { dependencyTopPrevious }); var changes = new DependenciesChangesBuilder(); changes.Added(dependencyModelTopAdded); var snapshotFilter = new TestDependenciesSnapshotFilter() .BeforeAddAccept(@"tfm1\xxx\topdependency1", dependencyTopUpdated); var snapshot = TargetedDependenciesSnapshot.FromChanges( projectPath, previousSnapshot, changes.Build(), catalogs, ImmutableArray.Create <IDependenciesSnapshotFilter>(snapshotFilter), new Dictionary <string, IProjectDependenciesSubTreeProvider>(), null); Assert.NotSame(previousSnapshot, snapshot); Assert.Same(dependencyTopUpdated, snapshot.TopLevelDependencies.Single()); }
public void FromChanges_ReportedChangesAfterBeforeRemoveFilterDeclinedChange() { const string projectPath = @"c:\somefolder\someproject\a.csproj"; var targetFramework = new TargetFramework("tfm1"); var dependencyTop1 = new TestDependency { ProviderType = "Xxx", Id = "tfm1\\xxx\\topdependency1", Name = "TopDependency1", Caption = "TopDependency1", SchemaItemType = "Xxx", Resolved = true, TopLevel = true }; var dependencyChild1 = new TestDependency { ProviderType = "Xxx", Id = "tfm1\\xxx\\childdependency1", Name = "ChildDependency1", Caption = "ChildDependency1", SchemaItemType = "Xxx", Resolved = true, TopLevel = false }; var catalogs = IProjectCatalogSnapshotFactory.Create(); var previousSnapshot = ITargetedDependenciesSnapshotFactory.Implement( projectPath: projectPath, targetFramework: targetFramework, catalogs: catalogs, dependenciesWorld: new [] { dependencyTop1, dependencyChild1 }, topLevelDependencies: new [] { dependencyTop1 }); var changes = new DependenciesChangesBuilder(); changes.Removed("Xxx", "topdependency1"); var addedOnRemove = new TestDependency { Id = "SomethingElse", TopLevel = false }; var snapshotFilter = new TestDependenciesSnapshotFilter() .BeforeRemoveReject(@"tfm1\xxx\topdependency1", addOrUpdate: addedOnRemove); var snapshot = TargetedDependenciesSnapshot.FromChanges( projectPath, previousSnapshot, changes.Build(), catalogs, ImmutableArray.Create <IDependenciesSnapshotFilter>(snapshotFilter), new Dictionary <string, IProjectDependenciesSubTreeProvider>(), null); Assert.NotSame(previousSnapshot, snapshot); Assert.Same(previousSnapshot.TargetFramework, snapshot.TargetFramework); Assert.Same(projectPath, snapshot.ProjectPath); Assert.Same(catalogs, snapshot.Catalogs); Assert.Single(snapshot.TopLevelDependencies); AssertEx.CollectionLength(snapshot.DependenciesWorld, 3); Assert.Contains(addedOnRemove, snapshot.DependenciesWorld.Values); }
/// <summary> /// For each target framework in <paramref name="changes"/>, applies the corresponding /// <see cref="IDependenciesChanges"/> to <paramref name="previousSnapshot"/> in order to produce /// and return an updated <see cref="DependenciesSnapshot"/> object. /// If no changes are made, <paramref name="previousSnapshot"/> is returned unmodified. /// </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( string projectPath, DependenciesSnapshot previousSnapshot, ImmutableDictionary <ITargetFramework, IDependenciesChanges> changes, IProjectCatalogSnapshot catalogs, ITargetFramework activeTargetFramework, 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)); // catalogs can be null Requires.Argument(!snapshotFilters.IsDefault, nameof(snapshotFilters), "Cannot be default."); Requires.NotNull(subTreeProviderByProviderType, nameof(subTreeProviderByProviderType)); // projectItemSpecs can be null var builder = previousSnapshot.Targets.ToBuilder(); bool targetChanged = false; foreach ((ITargetFramework targetFramework, IDependenciesChanges dependenciesChanges) in changes) { if (!builder.TryGetValue(targetFramework, out ITargetedDependenciesSnapshot previousTargetedSnapshot)) { previousTargetedSnapshot = TargetedDependenciesSnapshot.CreateEmpty(projectPath, targetFramework, catalogs); } ITargetedDependenciesSnapshot newTargetedSnapshot = TargetedDependenciesSnapshot.FromChanges( projectPath, previousTargetedSnapshot, dependenciesChanges, catalogs, snapshotFilters, subTreeProviderByProviderType, projectItemSpecs); if (!ReferenceEquals(previousTargetedSnapshot, newTargetedSnapshot)) { builder[targetFramework] = newTargetedSnapshot; targetChanged = true; } } targetChanged |= RemoveTargetFrameworksWithNoDependencies(); ITargetFramework activeTarget = activeTargetFramework ?? previousSnapshot.ActiveTarget; if (targetChanged) { // Targets have changed return(new DependenciesSnapshot( previousSnapshot.ProjectPath, activeTarget, builder.ToImmutable())); } if (!activeTarget.Equals(previousSnapshot.ActiveTarget)) { // The active target changed return(new DependenciesSnapshot( previousSnapshot.ProjectPath, activeTarget, previousSnapshot.Targets)); } // Nothing has changed, so return the same snapshot return(previousSnapshot); // Active target differs bool RemoveTargetFrameworksWithNoDependencies() { // This is a long-winded way of doing this that minimises allocations List <ITargetFramework> emptyFrameworks = null; bool anythingRemoved = false; foreach ((ITargetFramework targetFramework, ITargetedDependenciesSnapshot targetedSnapshot) in builder) { if (targetedSnapshot.DependenciesWorld.Count == 0) { if (emptyFrameworks == null) { anythingRemoved = true; emptyFrameworks = new List <ITargetFramework>(builder.Count); } emptyFrameworks.Add(targetFramework); } } if (emptyFrameworks != null) { foreach (ITargetFramework framework in emptyFrameworks) { builder.Remove(framework); } } return(anythingRemoved); } }