Ejemplo n.º 1
0
        [Test] public void DiscardCreateAnythingDelete()
        {
            List <FileEvent> inputEvents = new List <FileEvent>();

            inputEvents.Add(new FileEvent(FileEventType.Created, "Foo\\A", false));
            inputEvents.Add(new FileEvent(FileEventType.Changed, "Foo\\A", false));
            inputEvents.Add(new FileEvent(FileEventType.Renamed, "Foo\\A", "Foo\\B", false));
            inputEvents.Add(new FileEvent(FileEventType.Changed, "Foo\\B", false));
            inputEvents.Add(new FileEvent(FileEventType.Deleted, "Foo\\B", false));

            // Create a queue and add events in order
            FileEventQueue queue = new FileEventQueue();

            foreach (FileEvent item in inputEvents)
            {
                queue.Add(item);
            }

            // Assert that the queue discards event sequences starting with file creation
            // and ending with file deletion, as they might as well have never existed.
            // Note that we require the queue to pick up on the rename mid-sequence.
            List <FileEvent> outputEvents = new List <FileEvent>();

            CollectionAssert.AreEqual(outputEvents, queue.Items);
        }
Ejemplo n.º 2
0
        [Test] public void Basics()
        {
            List <FileEvent> eventList = new List <FileEvent>();

            eventList.Add(new FileEvent(FileEventType.Changed, "Foo\\A", false));
            eventList.Add(new FileEvent(FileEventType.Created, "Foo\\B", false));
            eventList.Add(new FileEvent(FileEventType.Deleted, "Foo\\C", false));
            eventList.Add(new FileEvent(FileEventType.Renamed, "Foo\\D", "Foo\\E", false));

            FileEventQueue queue = new FileEventQueue();

            Assert.IsTrue(queue.IsEmpty);
            Assert.AreEqual(0, queue.Items.Count);

            foreach (FileEvent item in eventList)
            {
                queue.Add(item);
            }

            Assert.IsFalse(queue.IsEmpty);
            CollectionAssert.AreEqual(eventList, queue.Items);

            queue.ApplyFilter(fileEvent => fileEvent.Type == FileEventType.Created);
            eventList.RemoveAt(1);

            Assert.IsFalse(queue.IsEmpty);
            CollectionAssert.AreEqual(eventList, queue.Items);

            queue.Clear();

            Assert.IsTrue(queue.IsEmpty);
            Assert.AreEqual(0, queue.Items.Count);
        }
Ejemplo n.º 3
0
        [Test] public void AggregateEqualEvents()
        {
            List <FileEvent> eventList = new List <FileEvent>();

            eventList.Add(new FileEvent(FileEventType.Changed, "Foo\\A", false));
            eventList.Add(new FileEvent(FileEventType.Created, "Foo\\B", false));
            eventList.Add(new FileEvent(FileEventType.Deleted, "Foo\\C", false));
            eventList.Add(new FileEvent(FileEventType.Renamed, "Foo\\D", "Foo\\E", false));

            // Create a queue and add each test event multiple times
            FileEventQueue queue = new FileEventQueue();

            for (int sequenceIndex = 0; sequenceIndex < 3; sequenceIndex++)
            {
                foreach (FileEvent item in eventList)
                {
                    for (int groupIndex = 0; groupIndex < 3; groupIndex++)
                    {
                        queue.Add(item);
                    }
                }
            }

            // Assert that the queue only contains what's in the event list, in
            // the order of latest submission
            CollectionAssert.AreEqual(eventList, queue.Items);
        }
Ejemplo n.º 4
0
        [Test] public void SimplifyPhotoshopRename()
        {
            // Photoshop, and maybe other applications, have this peculiar trick when
            // saving output files that overwrite an existing file, in which they first
            // write to a temp file, then delete the target file, then rename the temp
            // file into the target file.
            List <FileEvent> inputEvents = new List <FileEvent>();

            inputEvents.Add(new FileEvent(FileEventType.Created, "Foo\\Temp", false));
            inputEvents.Add(new FileEvent(FileEventType.Changed, "Foo\\Temp", false));
            inputEvents.Add(new FileEvent(FileEventType.Deleted, "Foo\\A", false));
            inputEvents.Add(new FileEvent(FileEventType.Renamed, "Foo\\Temp", "Foo\\A", false));

            // Create a queue and add events in order
            FileEventQueue queue = new FileEventQueue();

            foreach (FileEvent item in inputEvents)
            {
                queue.Add(item);
            }

            // Assert that the way we see these events has been normalized so we can easily
            // see that the target file was changed.
            List <FileEvent> outputEvents = new List <FileEvent>();

            outputEvents.Add(new FileEvent(FileEventType.Changed, "Foo\\A", false));
            CollectionAssert.AreEqual(outputEvents, queue.Items);
        }
Ejemplo n.º 5
0
        private static void ProcessSourceDirEvents(FileEventQueue eventQueue)
        {
            // Filter out events we don't want to process in the editor
            eventQueue.ApplyFilter(EditorSourceEventFilter);
            if (eventQueue.IsEmpty)
            {
                return;
            }

            // Process events
            foreach (FileEvent fileEvent in eventQueue.Items)
            {
                // Mind modified source files for re-import
                if (fileEvent.Type == FileEventType.Changed)
                {
                    if (File.Exists(fileEvent.Path) && PathOp.IsPathLocatedIn(fileEvent.Path, EditorHelper.ImportDirectory))
                    {
                        reimportSchedule.Add(fileEvent.Path);
                    }
                }
            }

            // Fire an editor-wide event to allow reacting to source changes and re-importing resources
            if (SourcesChanged != null)
            {
                SourcesChanged(null, new FileSystemChangedEventArgs(eventQueue));
            }

            // Handled all events, start over with an empty buffer
            eventQueue.Clear();
        }
Ejemplo n.º 6
0
        [Test] public void AggregateDeleteCreate()
        {
            List <FileEvent> inputEvents = new List <FileEvent>();

            inputEvents.Add(new FileEvent(FileEventType.Deleted, "Foo\\A", false));
            inputEvents.Add(new FileEvent(FileEventType.Created, "Bar\\A", false));
            inputEvents.Add(new FileEvent(FileEventType.Deleted, "Foo\\B", false));
            inputEvents.Add(new FileEvent(FileEventType.Created, "Foo\\B", false));

            // Create a queue and add events in order
            FileEventQueue queue = new FileEventQueue();

            foreach (FileEvent item in inputEvents)
            {
                queue.Add(item);
            }

            // Assert that the queue merges a delete-create using the same file name into a rename,
            // and using the same path into a change.
            List <FileEvent> outputEvents = new List <FileEvent>();

            outputEvents.Add(new FileEvent(FileEventType.Renamed, "Foo\\A", "Bar\\A", false));
            outputEvents.Add(new FileEvent(FileEventType.Changed, "Foo\\B", false));
            CollectionAssert.AreEqual(outputEvents, queue.Items);
        }
Ejemplo n.º 7
0
        [Test] public void DiscardAnythingAfterCreate()
        {
            List <FileEvent> inputEvents = new List <FileEvent>();

            inputEvents.Add(new FileEvent(FileEventType.Created, "Foo\\A", false));
            inputEvents.Add(new FileEvent(FileEventType.Changed, "Foo\\A", false));
            inputEvents.Add(new FileEvent(FileEventType.Renamed, "Foo\\A", "Foo\\B", false));
            inputEvents.Add(new FileEvent(FileEventType.Changed, "Foo\\B", false));

            // Create a queue and add events in order
            FileEventQueue queue = new FileEventQueue();

            foreach (FileEvent item in inputEvents)
            {
                queue.Add(item);
            }

            // Assert that we discard all folluwup events of a newly created file.
            // Since we didn't know the file existed before this queue, it's pointless
            // to know what it did until now.
            // Note that we require the queue to pick up on the rename mid-sequence.
            List <FileEvent> outputEvents = new List <FileEvent>();

            outputEvents.Add(new FileEvent(FileEventType.Created, "Foo\\B", false));
            CollectionAssert.AreEqual(outputEvents, queue.Items);
        }
Ejemplo n.º 8
0
        private static void PushFileEvent(FileEventQueue queue, FileSystemEventArgs watcherEvent, bool isDirectory)
        {
            // Do not track hidden paths
            if (!PathHelper.IsPathVisible(watcherEvent.FullPath))
            {
                return;
            }

            // Translate the file system watcher event into our own data structure
            FileEvent fileEvent = TranslateFileEvent(
                watcherEvent,
                isDirectory);

            queue.Add(fileEvent);
        }
Ejemplo n.º 9
0
        private static void ProcessPluginDirEvents(FileEventQueue eventQueue)
        {
            // Filter out events we don't want to process in the editor
            eventQueue.ApplyFilter(EditorPluginEventFilter);
            if (eventQueue.IsEmpty)
            {
                return;
            }

            // Fire an editor-wide event to allow scheduling a plugin reload
            if (PluginsChanged != null)
            {
                PluginsChanged(null, new FileSystemChangedEventArgs(eventQueue));
            }

            // Handled all events, start over with an empty buffer
            eventQueue.Clear();
        }
Ejemplo n.º 10
0
        private static void ProcessDataDirEvents(FileEventQueue eventQueue)
        {
            // Filter out events we don't want to process in the editor
            eventQueue.ApplyFilter(EditorDataEventFilter);
            if (eventQueue.IsEmpty)
            {
                return;
            }

            // System internal event processing / do all the low-level stuff
            HandleDataDirEvents(eventQueue);

            // Fire an editor-wide event to allow plugins and editor modules to react
            if (ResourcesChanged != null)
            {
                ResourcesChanged(null, new ResourceFilesChangedEventArgs(eventQueue));
            }

            // Handled all events, start over with an empty buffer
            eventQueue.Clear();
        }
Ejemplo n.º 11
0
        [Test] public void DiscardNopRenameIntoSelf()
        {
            List <FileEvent> inputEvents = new List <FileEvent>();

            inputEvents.Add(new FileEvent(FileEventType.Changed, "Foo\\A", false));
            inputEvents.Add(new FileEvent(FileEventType.Renamed, "Foo\\A", "Foo\\A", false));
            inputEvents.Add(new FileEvent(FileEventType.Renamed, "Foo\\B", "Foo\\B", false));

            // Create a queue and add events in order
            FileEventQueue queue = new FileEventQueue();

            foreach (FileEvent item in inputEvents)
            {
                queue.Add(item);
            }

            // Assert that we discard all rename events that have the same old and new path.
            List <FileEvent> outputEvents = new List <FileEvent>();

            outputEvents.Add(new FileEvent(FileEventType.Changed, "Foo\\A", false));
            CollectionAssert.AreEqual(outputEvents, queue.Items);
        }
Ejemplo n.º 12
0
        [Test] public void AggregateMultiRename()
        {
            List <FileEvent> inputEvents = new List <FileEvent>();

            inputEvents.Add(new FileEvent(FileEventType.Renamed, "Foo\\A", "Foo\\B", false));
            inputEvents.Add(new FileEvent(FileEventType.Renamed, "Foo\\B", "Foo\\C", false));
            inputEvents.Add(new FileEvent(FileEventType.Renamed, "Foo\\C", "Foo\\D", false));

            // Create a queue and add events in order
            FileEventQueue queue = new FileEventQueue();

            foreach (FileEvent item in inputEvents)
            {
                queue.Add(item);
            }

            // Assert that all rename events were aggregated into a single one
            List <FileEvent> outputEvents = new List <FileEvent>();

            outputEvents.Add(new FileEvent(FileEventType.Renamed, "Foo\\A", "Foo\\D", false));
            CollectionAssert.AreEqual(outputEvents, queue.Items);
        }
Ejemplo n.º 13
0
        [Test] public void AggregateDeleteRenameInto()
        {
            List <FileEvent> inputEvents = new List <FileEvent>();

            inputEvents.Add(new FileEvent(FileEventType.Deleted, "Foo\\A", false));
            inputEvents.Add(new FileEvent(FileEventType.Renamed, "Foo\\B", "Foo\\A", false));

            // Create a queue and add events in order
            FileEventQueue queue = new FileEventQueue();

            foreach (FileEvent item in inputEvents)
            {
                queue.Add(item);
            }

            // Assert that the delete was discarded, but a change was queued after rename
            // because we essentially still have that file, just with different contents.
            List <FileEvent> outputEvents = new List <FileEvent>();

            outputEvents.Add(new FileEvent(FileEventType.Deleted, "Foo\\B", false));
            outputEvents.Add(new FileEvent(FileEventType.Changed, "Foo\\A", false));
            CollectionAssert.AreEqual(outputEvents, queue.Items);
        }
Ejemplo n.º 14
0
        [Test] public void DiscardAnythingBeforeDelete()
        {
            List <FileEvent> inputEvents = new List <FileEvent>();

            inputEvents.Add(new FileEvent(FileEventType.Changed, "Foo\\A", false));
            inputEvents.Add(new FileEvent(FileEventType.Renamed, "Foo\\A", "Foo\\B", false));
            inputEvents.Add(new FileEvent(FileEventType.Changed, "Foo\\B", false));
            inputEvents.Add(new FileEvent(FileEventType.Deleted, "Foo\\B", false));

            // Create a queue and add events in order
            FileEventQueue queue = new FileEventQueue();

            foreach (FileEvent item in inputEvents)
            {
                queue.Add(item);
            }

            // Assert that we discard all now-irrelevant events of a deleted file.
            // Note that we require the queue to pick up on the rename mid-sequence.
            List <FileEvent> outputEvents = new List <FileEvent>();

            outputEvents.Add(new FileEvent(FileEventType.Deleted, "Foo\\A", false));
            CollectionAssert.AreEqual(outputEvents, queue.Items);
        }
Ejemplo n.º 15
0
 public FileSystemChangedEventArgs(FileEventQueue fileEventQueue) : this(fileEventQueue.Items)
 {
 }
Ejemplo n.º 16
0
        private static void HandleDataDirEvents(FileEventQueue eventQueue)
        {
            // Gather a list of all externally modified resources that are currently loaded.
            // We'll use this to later tell the editor about the changes we've applied.
            List <Resource> modifiedLoadedResources = null;

            foreach (FileEvent fileEvent in eventQueue.Items)
            {
                if (fileEvent.IsDirectory)
                {
                    continue;
                }

                // We're only interested in the path where we would find the Resource right now,
                // because we need the instance for the change notification
                string contentPath            = (fileEvent.Type == FileEventType.Renamed) ? fileEvent.OldPath : fileEvent.Path;
                ContentRef <Resource> content = new ContentRef <Resource>(contentPath);

                // Some editor modules rely on change notifications to re-establish links to previously
                // removed Resources. In order to do that, we'll speculatively load newly arrived Resources
                // so we can put out a change notification for them.
                // Note: If ObjectSelection supported ContentRefs, we could improve and do that without the
                // speculative load, triggering a load only when someone was actually interested.
                if (content.IsLoaded || fileEvent.Type == FileEventType.Created)
                {
                    if (modifiedLoadedResources == null)
                    {
                        modifiedLoadedResources = new List <Resource>();
                    }
                    modifiedLoadedResources.Add(content.Res);
                }
            }

            // Handle each event according to its type
            List <FileEvent> renameEventBuffer         = null;
            HashSet <string> sourceMediaDeleteSchedule = null;

            foreach (FileEvent fileEvent in eventQueue.Items)
            {
                if (fileEvent.Type == FileEventType.Changed)
                {
                    HandleDataDirChangeEvent(fileEvent);
                }
                else if (fileEvent.Type == FileEventType.Deleted)
                {
                    HandleDataDirDeleteEvent(fileEvent, ref sourceMediaDeleteSchedule);
                }
                else if (fileEvent.Type == FileEventType.Renamed)
                {
                    HandleDataDirRenameEvent(fileEvent, ref renameEventBuffer);
                }
            }

            // If we scheduled source / media files for deletion, do it now at once
            if (sourceMediaDeleteSchedule != null)
            {
                // Gather a list of directories from which we're removing
                HashSet <string> affectedDirectories = new HashSet <string>();
                foreach (string file in sourceMediaDeleteSchedule)
                {
                    affectedDirectories.Add(Path.GetDirectoryName(file));
                }

                // Send all the files to the recycle bin
                RecycleBin.SendSilent(sourceMediaDeleteSchedule);

                // Remove directories that are now empty
                foreach (string dir in affectedDirectories)
                {
                    PathHelper.DeleteEmptyDirectory(dir, true);
                }
            }

            // If required, perform a global rename operation in all existing content
            if (renameEventBuffer != null)
            {
                // Don't do it now - schedule it for the main form event loop so we don't block here.
                DualityEditorApp.MainForm.BeginInvoke((Action) delegate() {
                    ProcessingBigTaskDialog taskDialog = new ProcessingBigTaskDialog(
                        Properties.GeneralRes.TaskRenameContentRefs_Caption,
                        Properties.GeneralRes.TaskRenameContentRefs_Desc,
                        async_RenameContentRefs, renameEventBuffer);
                    taskDialog.ShowDialog(DualityEditorApp.MainForm);
                });
            }

            // Notify the editor about externally modified resources
            if (modifiedLoadedResources != null)
            {
                DualityEditorApp.NotifyObjPropChanged(
                    null,
                    new ObjectSelection(modifiedLoadedResources),
                    false);
            }
        }
Ejemplo n.º 17
0
 public ResourceFilesChangedEventArgs(FileEventQueue fileEventQueue) : base(fileEventQueue)
 {
 }