Ejemplo n.º 1
0
 /// <summary>
 /// ReferencesGraphProvider supports tracking changes.
 /// ProjectContextChanged gets fired everytime dependencies change. TrackChangesAsync updates
 /// ExpandedGraphContexts as necessary to reflect changes.
 /// </summary>
 private void ProjectContextProvider_ProjectContextChanged(object sender, ProjectContextEventArgs e)
 {
     ThreadHelper.JoinableTaskFactory.RunAsync(async() =>
     {
         await TrackChangesAsync(e.Context).ConfigureAwait(false);
     });
 }
Ejemplo n.º 2
0
        /// <summary>
        /// When project context changed event received from any context, send a global level
        /// "context changed" event, to notify  <see cref="DependenciesGraphProvider"/>.
        /// </summary>
        internal void OnProjectContextChanged(object sender, ProjectContextEventArgs e)
        {
            var context = e.Context;

            if (context == null)
            {
                return;
            }

            ProjectContextChanged?.Invoke(this, e);
        }
 /// <summary>
 /// Property ExpandedGraphContexts remembers graph expanded or checked so far.
 /// Each context represents one level in the graph, i.e. a node and its first level dependencies
 /// Tracking changes over all expanded contexts ensures that all levels are processed
 /// and updated when there are any changes in nodes data.
 /// </summary>
 internal async Task TrackChangesAsync(ProjectContextEventArgs updatedProjectContext)
 {
     foreach (var graphContext in ExpandedGraphContexts.ToList())
     {
         try
         {
             await TrackChangesOnGraphContextAsync(graphContext, updatedProjectContext).ConfigureAwait(false);
         }
         finally
         {
             // Calling OnCompleted ensures that the changes are reflected in UI
             graphContext.OnCompleted();
         }
     }
 }
Ejemplo n.º 4
0
        /// <summary>
        /// When a given project context is unloaded, remove it form the cache and unregister event handlers
        /// </summary>
        internal void OnProjectContextUnloaded(object sender, ProjectContextEventArgs e)
        {
            var context = e.Context;

            if (context == null)
            {
                return;
            }

            ProjectContextUnloaded?.Invoke(this, e);
            // Remove context for the unloaded project from the cache
            ProjectContexts.TryRemove(context.ProjectFilePath, out IDependenciesGraphProjectContext removedContext);

            context.ProjectContextChanged  -= OnProjectContextChanged;
            context.ProjectContextUnloaded -= OnProjectContextUnloaded;
        }
        /// <summary>
        /// ReferencesGraphProvider supports tracking changes.
        /// ProjectContextChanged gets fired everytime dependencies change. TrackChangesAsync updates
        /// ExpandedGraphContexts as necessary to reflect changes.
        /// </summary>
        private void ProjectContextProvider_ProjectContextChanged(object sender, ProjectContextEventArgs e)
        {
            lock (_changedContextsQueueLock)
            {
                _changedContextsQueue[e.Context.ProjectFilePath] = e;

                // schedule new track changes request in the queue
                if (_trackChangesTask == null || _trackChangesTask.IsCompleted)
                {
                    _trackChangesTask = RunTrackChangesAsync();
                }
                else
                {
                    _trackChangesTask = _trackChangesTask.ContinueWith(t => RunTrackChangesAsync(), TaskScheduler.Default);
                }
            }
        }
        /// <summary>
        /// Tries to apply changes to corresponding node in given GraphContext for given changes
        /// </summary>
        private async Task TrackChangesOnGraphContextAsync(IGraphContext graphContext,
                                                           ProjectContextEventArgs changes)
        {
            var updatedProjectContext = changes.Context;

            foreach (var inputGraphNode in graphContext.InputNodes.ToList())
            {
                var existingNodeInfo = inputGraphNode.GetValue <IDependencyNode>(
                    DependenciesGraphSchema.DependencyNodeProperty);
                if (existingNodeInfo == null)
                {
                    continue;
                }

                var projectPath = GetPartialValueFromGraphNodeId(inputGraphNode.Id, CodeGraphNodeIdName.Assembly);
                if (string.IsNullOrEmpty(projectPath))
                {
                    continue;
                }

                if (!ShouldTrackChangesForNode(existingNodeInfo, projectPath, updatedProjectContext.ProjectFilePath))
                {
                    continue;
                }

                var subTreeProvider = await GetSubTreeProviderAsync(graphContext,
                                                                    inputGraphNode,
                                                                    projectPath,
                                                                    existingNodeInfo.Id).ConfigureAwait(false);

                if (subTreeProvider == null)
                {
                    continue;
                }

                var updatedNode = subTreeProvider.GetDependencyNode(existingNodeInfo.Id);
                if (updatedNode == null)
                {
                    continue;
                }

                var  newNode    = DependencyNode.Clone(existingNodeInfo);
                bool anyChanges = AnyChangesToTrack(newNode,
                                                    updatedNode,
                                                    changes.Diff,
                                                    out IEnumerable <IDependencyNode> nodesToAdd,
                                                    out IEnumerable <IDependencyNode> nodesToRemove);
                if (!anyChanges)
                {
                    continue;
                }

                // register providers for new nodes outside of scope using (it can not have await there)
                await RegisterProviders(graphContext, projectPath, nodesToAdd).ConfigureAwait(false);

                using (var scope = new GraphTransactionScope())
                {
                    foreach (var nodeToRemove in nodesToRemove)
                    {
                        RemoveGraphNode(graphContext, projectPath, nodeToRemove, inputGraphNode);
                        newNode.RemoveChild(nodeToRemove);
                    }

                    foreach (var nodeToAdd in nodesToAdd)
                    {
                        AddGraphNode(graphContext, projectPath, null, inputGraphNode, nodeToAdd);
                        newNode.AddChild(nodeToAdd);
                    }

                    // Update the node info saved on the 'inputNode'
                    inputGraphNode.SetValue(DependenciesGraphSchema.DependencyNodeProperty, newNode);

                    scope.Complete();
                }
            }
        }
Ejemplo n.º 7
0
        public async Task DependenciesGraphProvider_TrackChangesAsync_WithContextProject()
        {
            var projectPath        = @"c:\myproject\project.csproj";
            var contextProjectPath = @"c:\mycontextproject\project.csproj";
            var nodeIdString       = @"file:///[MyProvider;MyNodeItemSpec]";
            var inputNode          = IGraphContextFactory.CreateNode(projectPath, nodeIdString);

            var existingNode = IDependencyNodeFactory.FromJson(@"
{
    ""Id"": {
        ""ProviderType"": ""MyProvider"",
        ""ItemSpec"": ""MyNodeItemSpec""
    }
}", DependencyNode.DependsOnOtherProviders);

            existingNode.Id.ContextProject = contextProjectPath;
            var existingChildNode = IDependencyNodeFactory.FromJson(@"
{
    ""Id"": {
        ""ProviderType"": ""MyProvider"",
        ""ItemSpec"": ""MyChildNodeItemSpecExisting""
    }
}");

            var existingRefreshedNode = IDependencyNodeFactory.FromJson(@"
{
    ""Id"": {
        ""ProviderType"": ""MyProvider"",
        ""ItemSpec"": ""MyNodeItemSpec""
    }
}", DependencyNode.DependsOnOtherProviders);

            existingRefreshedNode.Id.ContextProject = contextProjectPath;

            var newChildNode = IDependencyNodeFactory.FromJson(@"
{
    ""Id"": {
        ""ProviderType"": ""MyProvider"",
        ""ItemSpec"": ""MyChildNodeItemSpecNew""
    }
}");

            existingNode.AddChild(existingChildNode);
            existingRefreshedNode.AddChild(newChildNode);

            var diff = IDependenciesChangeDiffFactory.Implement(new[] { newChildNode },
                                                                new IDependencyNode[] { }, new IDependencyNode[] { });

            var mockProvider = new IProjectDependenciesSubTreeProviderMock();

            mockProvider.AddTestDependencyNodes(new[] { existingRefreshedNode });

            inputNode.SetValue(DependenciesGraphSchema.DependencyNodeProperty, existingNode);
            inputNode.SetValue(DependenciesGraphSchema.ProviderProperty, mockProvider);

            var outputNodes      = new HashSet <GraphNode>();
            var mockGraphContext = IGraphContextFactory.ImplementTrackChanges(inputNode, outputNodes);

            var updatedProjectContext =
                IDependenciesGraphProjectContextProviderFactory.ImplementProjectContext(contextProjectPath);

            var provider = new TestableDependenciesGraphProvider(IDependenciesGraphProjectContextProviderFactory.Create(),
                                                                 Mock.Of <SVsServiceProvider>(),
                                                                 new IProjectThreadingServiceMock().JoinableTaskContext);

            provider.AddExpandedGraphContext(mockGraphContext);
            var changes = new ProjectContextEventArgs(updatedProjectContext, diff);

            // Act
            await provider.TrackChangesAsync(changes);

            // Assert
            Assert.Equal(1, outputNodes.Count);
            var outputNode       = outputNodes.First();
            var outputDependency = outputNode.GetValue <IDependencyNode>(DependenciesGraphSchema.DependencyNodeProperty);

            Assert.Equal(newChildNode.Id, outputDependency.Id);
            var childProjectPath = outputNode.Id.GetNestedValueByName <Uri>(CodeGraphNodeIdName.Assembly);

            Assert.Equal(projectPath.Replace('\\', '/'), childProjectPath.AbsolutePath);
        }
Ejemplo n.º 8
0
        public async Task DependenciesGraphProvider_TrackChangesAsync_InvalidNodeData(
            string projectPath, bool existingNodeSpecified, bool updatedNodeProvided)
        {
            var nodeIdString = @"file:///[MyProvider;MyNodeItemSpec]";
            var inputNode    = IGraphContextFactory.CreateNode(projectPath, nodeIdString);

            var existingNode      = IDependencyNodeFactory.FromJson(@"
{
    ""Id"": {
        ""ProviderType"": ""MyProvider"",
        ""ItemSpec"": ""MyNodeItemSpec""
    }
}");
            var existingChildNode = IDependencyNodeFactory.FromJson(@"
{
    ""Id"": {
        ""ProviderType"": ""MyProvider"",
        ""ItemSpec"": ""MyChildNodeItemSpecExisting""
    }
}");

            var existingRefreshedNode = IDependencyNodeFactory.FromJson(@"
{
    ""Id"": {
        ""ProviderType"": ""MyProvider"",
        ""ItemSpec"": ""MyNodeItemSpec""
    }
}");
            var newChildNode          = IDependencyNodeFactory.FromJson(@"
{
    ""Id"": {
        ""ProviderType"": ""MyProvider"",
        ""ItemSpec"": ""MyChildNodeItemSpecNew""
    }
}");

            existingNode.AddChild(existingChildNode);
            existingRefreshedNode.AddChild(newChildNode);

            var mockProvider = new IProjectDependenciesSubTreeProviderMock();

            if (updatedNodeProvided)
            {
                mockProvider.AddTestDependencyNodes(new[] { existingRefreshedNode });
            }

            if (existingNodeSpecified)
            {
                inputNode.SetValue(DependenciesGraphSchema.DependencyNodeProperty, existingNode);
            }

            inputNode.SetValue(DependenciesGraphSchema.ProviderProperty, mockProvider);

            var outputNodes      = new HashSet <GraphNode>();
            var mockGraphContext = IGraphContextFactory.ImplementTrackChanges(inputNode, outputNodes);

            var updatedProjectContext =
                IDependenciesGraphProjectContextProviderFactory.ImplementProjectContext(projectPath);

            var provider = new TestableDependenciesGraphProvider(IDependenciesGraphProjectContextProviderFactory.Create(),
                                                                 Mock.Of <SVsServiceProvider>(),
                                                                 new IProjectThreadingServiceMock().JoinableTaskContext);

            provider.AddExpandedGraphContext(mockGraphContext);
            var changes = new ProjectContextEventArgs(updatedProjectContext);

            // Act
            await provider.TrackChangesAsync(changes);

            // Assert
            Assert.Equal(0, outputNodes.Count);
        }