public async Task ProcessDocument_SwallowsUnauthorizedAccessExceptions()
        {
            // Arrange
            var projectManager = new TestProjectSnapshotManager(Dispatcher, Workspace);

            projectManager.ProjectAdded(HostProject1);

            var textLoader = new Mock <TextLoader>(MockBehavior.Strict);

            textLoader.Setup(loader => loader.LoadTextAndVersionAsync(It.IsAny <Workspace>(), It.IsAny <DocumentId>(), It.IsAny <CancellationToken>()))
            .Throws <UnauthorizedAccessException>();
            projectManager.DocumentAdded(HostProject1, Documents[0], textLoader.Object);

            var project = projectManager.GetLoadedProject(HostProject1.FilePath);

            var queue = new BackgroundDocumentGenerator(Dispatcher, DynamicFileInfoProvider)
            {
                Delay = TimeSpan.FromMilliseconds(1),
                NotifyBackgroundWorkCompleted = new ManualResetEventSlim(initialState: false),
                NotifyErrorBeingReported      = new ManualResetEventSlim(initialState: false),
            };

            queue.Initialize(projectManager);

            // Act & Assert
            queue.Enqueue(project, project.GetDocument(Documents[0].FilePath));

            await Task.Run(() => queue.NotifyBackgroundWorkCompleted.Wait(TimeSpan.FromSeconds(3)));

            Assert.False(queue.NotifyErrorBeingReported.IsSet);
        }
        public async Task Queue_ProcessesNotifications_AndRestarts()
        {
            // Arrange
            var projectManager = new TestProjectSnapshotManager(Dispatcher, Workspace);

            projectManager.HostProjectAdded(HostProject1);
            projectManager.HostProjectAdded(HostProject2);
            projectManager.WorkspaceProjectAdded(WorkspaceProject1);
            projectManager.WorkspaceProjectAdded(WorkspaceProject2);
            projectManager.DocumentAdded(HostProject1, Documents[0], null);
            projectManager.DocumentAdded(HostProject1, Documents[1], null);

            var project = projectManager.GetLoadedProject(HostProject1.FilePath);

            var queue = new BackgroundDocumentGenerator(Dispatcher)
            {
                Delay = TimeSpan.FromMilliseconds(1),
                BlockBackgroundWorkStart      = new ManualResetEventSlim(initialState: false),
                NotifyBackgroundWorkStarting  = new ManualResetEventSlim(initialState: false),
                BlockBackgroundWorkCompleting = new ManualResetEventSlim(initialState: false),
                NotifyBackgroundWorkCompleted = new ManualResetEventSlim(initialState: false),
            };

            // Act & Assert
            queue.Enqueue(project, project.GetDocument(Documents[0].FilePath));

            Assert.True(queue.IsScheduledOrRunning, "Queue should be scheduled during Enqueue");
            Assert.True(queue.HasPendingNotifications, "Queue should have a notification created during Enqueue");

            // Allow the background work to start.
            queue.BlockBackgroundWorkStart.Set();

            await Task.Run(() => queue.NotifyBackgroundWorkStarting.Wait(TimeSpan.FromSeconds(1)));

            Assert.True(queue.IsScheduledOrRunning, "Worker should be processing now");
            Assert.False(queue.HasPendingNotifications, "Worker should have taken all notifications");

            queue.Enqueue(project, project.GetDocument(Documents[1].FilePath));
            Assert.True(queue.HasPendingNotifications); // Now we should see the worker restart when it finishes.

            // Allow work to complete, which should restart the timer.
            queue.BlockBackgroundWorkCompleting.Set();

            await Task.Run(() => queue.NotifyBackgroundWorkCompleted.Wait(TimeSpan.FromSeconds(1)));

            queue.NotifyBackgroundWorkCompleted.Reset();

            // It should start running again right away.
            Assert.True(queue.IsScheduledOrRunning, "Queue should be scheduled during Enqueue");
            Assert.True(queue.HasPendingNotifications, "Queue should have a notification created during Enqueue");

            // Allow the background work to proceed.
            queue.BlockBackgroundWorkStart.Set();

            queue.BlockBackgroundWorkCompleting.Set();
            await Task.Run(() => queue.NotifyBackgroundWorkCompleted.Wait(TimeSpan.FromSeconds(1)));

            Assert.False(queue.IsScheduledOrRunning, "Queue should not have restarted");
            Assert.False(queue.HasPendingNotifications, "Queue should have processed all notifications");
        }
        public async Task DocumentRemoved_ReparsesRelatedFiles()
        {
            // Arrange
            var projectManager = new TestProjectSnapshotManager(Dispatcher, Workspace)
            {
                AllowNotifyListeners = true,
            };

            projectManager.ProjectAdded(HostProject1);
            projectManager.DocumentAdded(HostProject1, TestProjectData.SomeProjectComponentFile1, null);
            projectManager.DocumentAdded(HostProject1, TestProjectData.SomeProjectImportFile, null);

            var queue = new BackgroundDocumentGenerator(Dispatcher, DynamicFileInfoProvider)
            {
                Delay = TimeSpan.FromMilliseconds(1),
                BlockBackgroundWorkStart         = new ManualResetEventSlim(initialState: false),
                NotifyBackgroundWorkStarting     = new ManualResetEventSlim(initialState: false),
                NotifyBackgroundCapturedWorkload = new ManualResetEventSlim(initialState: false),
                BlockBackgroundWorkCompleting    = new ManualResetEventSlim(initialState: false),
                NotifyBackgroundWorkCompleted    = new ManualResetEventSlim(initialState: false),
            };

            queue.Initialize(projectManager);

            // Act & Assert
            projectManager.DocumentRemoved(HostProject1, TestProjectData.SomeProjectImportFile);

            Assert.True(queue.IsScheduledOrRunning, "Queue should be scheduled during Enqueue");
            Assert.True(queue.HasPendingNotifications, "Queue should have a notification created during Enqueue");

            var kvp         = Assert.Single(queue.Work);
            var expectedKey = new DocumentKey(HostProject1.FilePath, TestProjectData.SomeProjectComponentFile1.FilePath);

            Assert.Equal(expectedKey, kvp.Key);

            // Allow the background work to start.
            queue.BlockBackgroundWorkStart.Set();

            await Task.Run(() => queue.NotifyBackgroundWorkStarting.Wait(TimeSpan.FromSeconds(1)));

            Assert.True(queue.IsScheduledOrRunning, "Worker should be processing now");

            await Task.Run(() => queue.NotifyBackgroundCapturedWorkload.Wait(TimeSpan.FromSeconds(1)));

            Assert.False(queue.HasPendingNotifications, "Worker should have taken all notifications");

            // Allow work to complete
            queue.BlockBackgroundWorkCompleting.Set();

            await Task.Run(() => queue.NotifyBackgroundWorkCompleted.Wait(TimeSpan.FromSeconds(3)));

            Assert.False(queue.HasPendingNotifications, "Queue should have processed all notifications");
            Assert.False(queue.IsScheduledOrRunning, "Queue should not have restarted");
        }
        public async Task ProcessDocument_LongDocumentParse_DoesNotUpdateAfterSuppress()
        {
            // Arrange
            var projectManager = new TestProjectSnapshotManager(Dispatcher, Workspace);

            projectManager.ProjectAdded(HostProject1);

            // We utilize a task completion source here so we can "fake" a document parse taking a significant amount of time
            var tcs        = new TaskCompletionSource <TextAndVersion>();
            var textLoader = new Mock <TextLoader>(MockBehavior.Strict);

            textLoader.Setup(loader => loader.LoadTextAndVersionAsync(It.IsAny <Workspace>(), It.IsAny <DocumentId>(), It.IsAny <CancellationToken>()))
            .Returns(tcs.Task);
            var hostDocument = Documents[0];

            var project = projectManager.GetLoadedProject(HostProject1.FilePath);
            var queue   = new BackgroundDocumentGenerator(Dispatcher, DynamicFileInfoProvider)
            {
                Delay = TimeSpan.FromMilliseconds(1),
                NotifyBackgroundWorkCompleted    = new ManualResetEventSlim(initialState: false),
                NotifyBackgroundCapturedWorkload = new ManualResetEventSlim(initialState: false),
            };

            queue.Initialize(projectManager);

            // We trigger enqueued notifications via adding/opening to the project manager
            projectManager.AllowNotifyListeners = true;

            // Act & Assert
            projectManager.DocumentAdded(HostProject1, hostDocument, textLoader.Object);

            queue.NotifyBackgroundCapturedWorkload.Wait();

            projectManager.DocumentOpened(HostProject1.FilePath, hostDocument.FilePath, SourceText.From(string.Empty));

            // Verify document was suppressed because it was opened
            Assert.Null(DynamicFileInfoProvider.DynamicDocuments[hostDocument.FilePath]);

            // Unblock document processing
            tcs.SetResult(TextAndVersion.Create(SourceText.From(string.Empty), VersionStamp.Default));

            await Task.Run(() => queue.NotifyBackgroundWorkCompleted.Wait(TimeSpan.FromSeconds(3)));

            // Validate that even though document parsing took a significant amount of time that the dynamic document wasn't "unsuppressed"
            Assert.Null(DynamicFileInfoProvider.DynamicDocuments[hostDocument.FilePath]);
        }
Beispiel #5
0
        public async Task Queue_ProcessesNotifications_AndGoesBackToSleep()
        {
            // Arrange
            var projectManager = new TestProjectSnapshotManager(Dispatcher, Workspace);

            projectManager.HostProjectAdded(HostProject1);
            projectManager.HostProjectAdded(HostProject2);
            projectManager.WorkspaceProjectAdded(WorkspaceProject1);
            projectManager.WorkspaceProjectAdded(WorkspaceProject2);
            projectManager.DocumentAdded(HostProject1, Documents[0], null);
            projectManager.DocumentAdded(HostProject1, Documents[1], null);

            var project = projectManager.GetLoadedProject(HostProject1.FilePath);

            var queue = new BackgroundDocumentGenerator(Dispatcher, DynamicFileInfoProvider)
            {
                Delay = TimeSpan.FromMilliseconds(1),
                BlockBackgroundWorkStart      = new ManualResetEventSlim(initialState: false),
                NotifyBackgroundWorkStarting  = new ManualResetEventSlim(initialState: false),
                BlockBackgroundWorkCompleting = new ManualResetEventSlim(initialState: false),
                NotifyBackgroundWorkCompleted = new ManualResetEventSlim(initialState: false),
            };

            queue.Initialize(projectManager);

            // Act & Assert
            queue.Enqueue(project, project.GetDocument(Documents[0].FilePath));

            Assert.True(queue.IsScheduledOrRunning, "Queue should be scheduled during Enqueue");
            Assert.True(queue.HasPendingNotifications, "Queue should have a notification created during Enqueue");

            // Allow the background work to proceed.
            queue.BlockBackgroundWorkStart.Set();
            queue.BlockBackgroundWorkCompleting.Set();

            await Task.Run(() => queue.NotifyBackgroundWorkCompleted.Wait(TimeSpan.FromSeconds(3)));

            Assert.False(queue.IsScheduledOrRunning, "Queue should not have restarted");
            Assert.False(queue.HasPendingNotifications, "Queue should have processed all notifications");
        }
        public OmniSharpBackgroundDocumentGenerator(
            OmniSharpProjectSnapshotManagerDispatcher projectSnapshotManagerDispatcher,
            RemoteTextLoaderFactory remoteTextLoaderFactory,
            IEnumerable <OmniSharpDocumentProcessedListener> documentProcessedListeners)
        {
            if (projectSnapshotManagerDispatcher is null)
            {
                throw new ArgumentNullException(nameof(projectSnapshotManagerDispatcher));
            }

            if (remoteTextLoaderFactory is null)
            {
                throw new ArgumentNullException(nameof(remoteTextLoaderFactory));
            }

            if (documentProcessedListeners is null)
            {
                throw new ArgumentNullException(nameof(documentProcessedListeners));
            }

            var wrappedListeners = documentProcessedListeners.Select(listener => new WrappedDocumentProcessedListener(remoteTextLoaderFactory, listener));

            _backgroundDocumentGenerator = new BackgroundDocumentGenerator(projectSnapshotManagerDispatcher.InternalDispatcher, wrappedListeners);
        }
        public async Task DocumentChanged_ReparsesRelatedFiles()
        {
            // Arrange
            var projectManager = new TestProjectSnapshotManager(Dispatcher, Workspace)
            {
                AllowNotifyListeners = true,
            };
            var documents = new[]
            {
                TestProjectData.SomeProjectComponentFile1,
                TestProjectData.SomeProjectImportFile
            };

            projectManager.ProjectAdded(HostProject1);
            for (var i = 0; i < documents.Length; i++)
            {
                projectManager.DocumentAdded(HostProject1, documents[i], null);
            }

            var queue = new BackgroundDocumentGenerator(Dispatcher, DynamicFileInfoProvider)
            {
                Delay = TimeSpan.FromMilliseconds(1),
                BlockBackgroundWorkStart         = new ManualResetEventSlim(initialState: false),
                NotifyBackgroundWorkStarting     = new ManualResetEventSlim(initialState: false),
                NotifyBackgroundCapturedWorkload = new ManualResetEventSlim(initialState: false),
                BlockBackgroundWorkCompleting    = new ManualResetEventSlim(initialState: false),
                NotifyBackgroundWorkCompleted    = new ManualResetEventSlim(initialState: false),
            };

            var changedSourceText = SourceText.From("@inject DateTime Time");

            queue.Initialize(projectManager);

            // Act & Assert
            projectManager.DocumentChanged(HostProject1.FilePath, TestProjectData.SomeProjectImportFile.FilePath, changedSourceText);

            Assert.True(queue.IsScheduledOrRunning, "Queue should be scheduled during Enqueue");
            Assert.True(queue.HasPendingNotifications, "Queue should have a notification created during Enqueue");

            for (var i = 0; i < documents.Length; i++)
            {
                var key = new DocumentKey(HostProject1.FilePath, documents[i].FilePath);
                Assert.True(queue.Work.ContainsKey(key));
            }

            // Allow the background work to start.
            queue.BlockBackgroundWorkStart.Set();

            await Task.Run(() => queue.NotifyBackgroundWorkStarting.Wait(TimeSpan.FromSeconds(1)));

            Assert.True(queue.IsScheduledOrRunning, "Worker should be processing now");

            await Task.Run(() => queue.NotifyBackgroundCapturedWorkload.Wait(TimeSpan.FromSeconds(1)));

            Assert.False(queue.HasPendingNotifications, "Worker should have taken all notifications");

            // Allow work to complete
            queue.BlockBackgroundWorkCompleting.Set();

            await Task.Run(() => queue.NotifyBackgroundWorkCompleted.Wait(TimeSpan.FromSeconds(3)));

            Assert.False(queue.HasPendingNotifications, "Queue should have processed all notifications");
            Assert.False(queue.IsScheduledOrRunning, "Queue should not have restarted");
        }