From bfd8c7b4cf6c423ddb769d6772e0b9ac615f37cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Valentin=20Breu=C3=9F?= Date: Wed, 31 Aug 2022 12:44:54 +0200 Subject: [PATCH 1/4] Example-Implementation for MockFileSystem events --- .../MockFileSystem.cs | 39 +++++++ .../MockFileSystemEvent.cs | 45 ++++++++ .../MockFileSystemEventTests.cs | 104 ++++++++++++++++++ 3 files changed, 188 insertions(+) create mode 100644 src/System.IO.Abstractions.TestingHelpers/MockFileSystemEvent.cs create mode 100644 tests/System.IO.Abstractions.TestingHelpers.Tests/MockFileSystemEventTests.cs diff --git a/src/System.IO.Abstractions.TestingHelpers/MockFileSystem.cs b/src/System.IO.Abstractions.TestingHelpers/MockFileSystem.cs index fa3203ac9..60bcbc2f5 100644 --- a/src/System.IO.Abstractions.TestingHelpers/MockFileSystem.cs +++ b/src/System.IO.Abstractions.TestingHelpers/MockFileSystem.cs @@ -16,6 +16,9 @@ public class MockFileSystem : FileSystemBase, IMockFileDataAccessor private readonly IDictionary files; private readonly PathVerifier pathVerifier; + private Action onFileChanging; + private Action onDirectoryChanging; + /// public MockFileSystem() : this(null) { } @@ -88,6 +91,24 @@ public MockFileSystem(IDictionary files, string currentDir /// public PathVerifier PathVerifier => pathVerifier; + /// + /// Registers a callback to be executed when a file is changing. + /// + public MockFileSystem OnFileChanging(Action callback) + { + onFileChanging = callback; + return this; + } + + /// + /// Registers a callback to be executed when a directory is changing. + /// + public MockFileSystem OnDirectoryChanging(Action callback) + { + onDirectoryChanging = callback; + return this; + } + private string FixPath(string path, bool checkCaps = false) { if (path == null) @@ -134,9 +155,27 @@ public MockFileData GetFile(string path) private void SetEntry(string path, MockFileData mockFile) { path = FixPath(path, true).TrimSlashes(); + if (mockFile is MockDirectoryData) + { + ExecuteCallbackAndCheckExceptionToThrow(onDirectoryChanging, new MockDirectoryChanging(path)); + } + else + { + ExecuteCallbackAndCheckExceptionToThrow(onFileChanging, new MockFileChanging(path)); + } files[path] = new FileSystemEntry { Path = path, Data = mockFile }; } + private void ExecuteCallbackAndCheckExceptionToThrow(Action callback, T fileChanging) + where T : MockFileSystemEvent + { + callback?.Invoke(fileChanging); + if (fileChanging.ExceptionToThrow != null) + { + throw fileChanging.ExceptionToThrow; + } + } + /// public void AddFile(string path, MockFileData mockFile) { diff --git a/src/System.IO.Abstractions.TestingHelpers/MockFileSystemEvent.cs b/src/System.IO.Abstractions.TestingHelpers/MockFileSystemEvent.cs new file mode 100644 index 000000000..19ddde0ef --- /dev/null +++ b/src/System.IO.Abstractions.TestingHelpers/MockFileSystemEvent.cs @@ -0,0 +1,45 @@ +namespace System.IO.Abstractions.TestingHelpers +{ + /// + /// Base class for mock file system events. + /// + public abstract class MockFileSystemEvent + { + /// + /// By setting the the corresponding exception will be thrown instead of changing the mock file system. + /// + public Exception ExceptionToThrow { get; set; } + } + + /// + /// Notifies about a pending change of a directory. + /// + public class MockDirectoryChanging : MockFileSystemEvent + { + /// + /// The path of the directory that is changed. + /// + public string Path { get; } + + internal MockDirectoryChanging(string path) + { + Path = path; + } + } + + /// + /// Notifies about a pending change of a file. + /// + public class MockFileChanging : MockFileSystemEvent + { + /// + /// The path of the file that is changed. + /// + public string Path { get; } + + internal MockFileChanging(string path) + { + Path = path; + } + } +} diff --git a/tests/System.IO.Abstractions.TestingHelpers.Tests/MockFileSystemEventTests.cs b/tests/System.IO.Abstractions.TestingHelpers.Tests/MockFileSystemEventTests.cs new file mode 100644 index 000000000..d417665eb --- /dev/null +++ b/tests/System.IO.Abstractions.TestingHelpers.Tests/MockFileSystemEventTests.cs @@ -0,0 +1,104 @@ +using System.Collections.Generic; +using NUnit.Framework; + +namespace System.IO.Abstractions.TestingHelpers.Tests +{ + [TestFixture] + public class MockFileSystemEventTests + { + [Test] + public void OnFileChanging_WriteAllText_ShouldBeCalledWithFullFilePath() + { + var basePath = Path.GetFullPath("/foo/bar"); + var fileName = "foo.txt"; + var expectedPath = Path.Combine(basePath, fileName); + var calledPath = string.Empty; + var fs = new MockFileSystem(null, basePath) + .OnFileChanging(f => calledPath = f.Path); + + fs.File.WriteAllText(fileName, "some content"); + + Assert.That(calledPath, Is.EqualTo(expectedPath)); + } + + [Test] + public void OnFileChanging_SetExceptionToThrow_ShouldThrowExceptionAndNotCreateFile() + { + var basePath = Path.GetFullPath("/foo/bar"); + var fileName = "foo.txt"; + var exception = new Exception("the file should not be created"); + var expectedPath = Path.Combine(basePath, fileName); + var fs = new MockFileSystem(null, basePath) + .OnFileChanging(f => f.ExceptionToThrow = exception); + + var receivedException = Assert.Throws(() => fs.File.WriteAllText(fileName, "some content")); + var result = fs.File.Exists(expectedPath); + + Assert.That(receivedException, Is.EqualTo(exception)); + Assert.That(result, Is.False); + } + + [Test] + public void OnFileChanging_ReadAllText_ShouldNotBeCalled() + { + var basePath = Path.GetFullPath("/foo/bar"); + var fileName = "foo.txt"; + bool isCalled = false; + var fs = new MockFileSystem(new Dictionary + { + { fileName, new MockFileData("some content") } + }, basePath) + .OnFileChanging(f => isCalled = true); + + _ = fs.File.ReadAllText(fileName); + + Assert.That(isCalled, Is.False); + } + + [Test] + public void OnDirectoryChanging_WriteAllText_ShouldBeCalledWithFullDirectoryPath() + { + var basePath = Path.GetFullPath("/foo/bar"); + var directoryName = "test-directory"; + var expectedPath = Path.Combine(basePath, directoryName); + var calledPath = string.Empty; + var fs = new MockFileSystem(null, basePath) + .OnDirectoryChanging(f => calledPath = f.Path); + + fs.Directory.CreateDirectory(directoryName); + + Assert.That(calledPath, Is.EqualTo(expectedPath)); + } + + [Test] + public void OnDirectoryChanging_SetExceptionToThrow_ShouldThrowExceptionAndNotCreateDirectory() + { + var basePath = Path.GetFullPath("/foo/bar"); + var directoryName = "test-directory"; + var exception = new Exception("the directory should not be created"); + var expectedPath = Path.Combine(basePath, directoryName); + var fs = new MockFileSystem(null, basePath) + .OnDirectoryChanging(f => f.ExceptionToThrow = exception); + + var receivedException = Assert.Throws(() => fs.Directory.CreateDirectory(directoryName)); + var result = fs.Directory.Exists(expectedPath); + + Assert.That(receivedException, Is.EqualTo(exception)); + Assert.That(result, Is.False); + } + + [Test] + public void OnDirectoryChanging_ReadAllText_ShouldNotBeCalled() + { + var basePath = Path.GetFullPath("/foo/bar"); + var directoryName = "test-directory"; + bool isCalled = false; + var fs = new MockFileSystem(null, basePath) + .OnDirectoryChanging(f => isCalled = true); + + _ = fs.Directory.Exists(directoryName); + + Assert.That(isCalled, Is.False); + } + } +} From 09ae644003615cb8c2b001104831429c7457fbdb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Valentin=20Breu=C3=9F?= Date: Wed, 31 Aug 2022 14:24:47 +0200 Subject: [PATCH 2/4] Add event type to MockFileEvent and MockDirectoryEvent --- .../MockDirectoryEvent.cs | 39 +++++ .../MockFileEvent.cs | 43 +++++ .../MockFileSystem.cs | 50 ++++-- .../MockFileSystemEvent.cs | 32 ---- .../MockFileSystemEventTests.cs | 165 ++++++++++++++---- 5 files changed, 253 insertions(+), 76 deletions(-) create mode 100644 src/System.IO.Abstractions.TestingHelpers/MockDirectoryEvent.cs create mode 100644 src/System.IO.Abstractions.TestingHelpers/MockFileEvent.cs diff --git a/src/System.IO.Abstractions.TestingHelpers/MockDirectoryEvent.cs b/src/System.IO.Abstractions.TestingHelpers/MockDirectoryEvent.cs new file mode 100644 index 000000000..34a50f169 --- /dev/null +++ b/src/System.IO.Abstractions.TestingHelpers/MockDirectoryEvent.cs @@ -0,0 +1,39 @@ +namespace System.IO.Abstractions.TestingHelpers +{ + /// + /// Notifies about a pending directory event. + /// + public class MockDirectoryEvent : MockFileSystemEvent + { + /// + /// The path of the directory. + /// + public string Path { get; } + + /// + /// The type of the directory event. + /// + public DirectoryEventType EventType { get; } + + internal MockDirectoryEvent(string path, DirectoryEventType eventType) + { + Path = path; + EventType = eventType; + } + + /// + /// The type of the directory event. + /// + public enum DirectoryEventType + { + /// + /// The directory is created. + /// + Created, + /// + /// The directory is deleted. + /// + Deleted + } + } +} diff --git a/src/System.IO.Abstractions.TestingHelpers/MockFileEvent.cs b/src/System.IO.Abstractions.TestingHelpers/MockFileEvent.cs new file mode 100644 index 000000000..3ea9a6719 --- /dev/null +++ b/src/System.IO.Abstractions.TestingHelpers/MockFileEvent.cs @@ -0,0 +1,43 @@ +namespace System.IO.Abstractions.TestingHelpers +{ + /// + /// Notifies about a pending file event. + /// + public class MockFileEvent : MockFileSystemEvent + { + /// + /// The path of the file. + /// + public string Path { get; } + + /// + /// The type of the file event. + /// + public FileEventType EventType { get; } + + internal MockFileEvent(string path, FileEventType changeType) + { + Path = path; + EventType = changeType; + } + + /// + /// The type of the file event. + /// + public enum FileEventType + { + /// + /// The file is created. + /// + Created, + /// + /// The file is updated. + /// + Updated, + /// + /// The file is deleted. + /// + Deleted + } + } +} diff --git a/src/System.IO.Abstractions.TestingHelpers/MockFileSystem.cs b/src/System.IO.Abstractions.TestingHelpers/MockFileSystem.cs index 60bcbc2f5..8d0c02f34 100644 --- a/src/System.IO.Abstractions.TestingHelpers/MockFileSystem.cs +++ b/src/System.IO.Abstractions.TestingHelpers/MockFileSystem.cs @@ -4,6 +4,7 @@ namespace System.IO.Abstractions.TestingHelpers { + using static System.Net.WebRequestMethods; using XFS = MockUnixSupport; /// @@ -16,8 +17,8 @@ public class MockFileSystem : FileSystemBase, IMockFileDataAccessor private readonly IDictionary files; private readonly PathVerifier pathVerifier; - private Action onFileChanging; - private Action onDirectoryChanging; + private Action onFileChanging; + private Action onDirectoryChanging; /// public MockFileSystem() : this(null) { } @@ -94,7 +95,7 @@ public MockFileSystem(IDictionary files, string currentDir /// /// Registers a callback to be executed when a file is changing. /// - public MockFileSystem OnFileChanging(Action callback) + public MockFileSystem OnFileChanging(Action callback) { onFileChanging = callback; return this; @@ -103,7 +104,7 @@ public MockFileSystem OnFileChanging(Action callback) /// /// Registers a callback to be executed when a directory is changing. /// - public MockFileSystem OnDirectoryChanging(Action callback) + public MockFileSystem OnDirectoryChanging(Action callback) { onDirectoryChanging = callback; return this; @@ -155,14 +156,6 @@ public MockFileData GetFile(string path) private void SetEntry(string path, MockFileData mockFile) { path = FixPath(path, true).TrimSlashes(); - if (mockFile is MockDirectoryData) - { - ExecuteCallbackAndCheckExceptionToThrow(onDirectoryChanging, new MockDirectoryChanging(path)); - } - else - { - ExecuteCallbackAndCheckExceptionToThrow(onFileChanging, new MockFileChanging(path)); - } files[path] = new FileSystemEntry { Path = path, Data = mockFile }; } @@ -203,6 +196,15 @@ public void AddFile(string path, MockFileData mockFile) AddDirectory(directoryPath); } + var existingFile = GetFileWithoutFixingPath(fixedPath); + if (existingFile == null) + { + ExecuteCallbackAndCheckExceptionToThrow(onFileChanging, new MockFileEvent(fixedPath, MockFileEvent.FileEventType.Created)); + } + else + { + ExecuteCallbackAndCheckExceptionToThrow(onFileChanging, new MockFileEvent(fixedPath, MockFileEvent.FileEventType.Updated)); + } SetEntry(fixedPath, mockFile ?? new MockFileData(string.Empty)); } } @@ -250,6 +252,8 @@ public void AddDirectory(string path) } var s = StringOperations.EndsWith(fixedPath, separator) ? fixedPath : fixedPath + separator; + + ExecuteCallbackAndCheckExceptionToThrow(onDirectoryChanging, new MockDirectoryEvent(s.TrimSlashes(), MockDirectoryEvent.DirectoryEventType.Created)); SetEntry(s, new MockDirectoryData()); } } @@ -308,6 +312,16 @@ public void MoveDirectory(string sourcePath, string destPath) var newPath = Path.Combine(destPath, path.Substring(sourcePath.Length).TrimStart(Path.DirectorySeparatorChar)); var entry = files[path]; entry.Path = newPath; + if (entry.Data is MockDirectoryData) + { + ExecuteCallbackAndCheckExceptionToThrow(onDirectoryChanging, new MockDirectoryEvent(path, MockDirectoryEvent.DirectoryEventType.Deleted)); + ExecuteCallbackAndCheckExceptionToThrow(onDirectoryChanging, new MockDirectoryEvent(newPath, MockDirectoryEvent.DirectoryEventType.Created)); + } + else + { + ExecuteCallbackAndCheckExceptionToThrow(onFileChanging, new MockFileEvent(path, MockFileEvent.FileEventType.Deleted)); + ExecuteCallbackAndCheckExceptionToThrow(onFileChanging, new MockFileEvent(newPath, MockFileEvent.FileEventType.Created)); + } files[newPath] = entry; files.Remove(path); } @@ -336,7 +350,7 @@ bool PathStartsWith(string path, string[] minMatch) /// public void RemoveFile(string path) { - path = FixPath(path); + path = FixPath(path).TrimSlashes(); lock (files) { @@ -345,6 +359,16 @@ public void RemoveFile(string path) throw CommonExceptions.AccessDenied(path); } + var file = GetFileWithoutFixingPath(path); + if (file is MockDirectoryData) + { + ExecuteCallbackAndCheckExceptionToThrow(onDirectoryChanging, new MockDirectoryEvent(path, MockDirectoryEvent.DirectoryEventType.Deleted)); + } + else + { + ExecuteCallbackAndCheckExceptionToThrow(onFileChanging, new MockFileEvent(path, MockFileEvent.FileEventType.Deleted)); + } + files.Remove(path); } } diff --git a/src/System.IO.Abstractions.TestingHelpers/MockFileSystemEvent.cs b/src/System.IO.Abstractions.TestingHelpers/MockFileSystemEvent.cs index 19ddde0ef..ba35455e4 100644 --- a/src/System.IO.Abstractions.TestingHelpers/MockFileSystemEvent.cs +++ b/src/System.IO.Abstractions.TestingHelpers/MockFileSystemEvent.cs @@ -10,36 +10,4 @@ public abstract class MockFileSystemEvent /// public Exception ExceptionToThrow { get; set; } } - - /// - /// Notifies about a pending change of a directory. - /// - public class MockDirectoryChanging : MockFileSystemEvent - { - /// - /// The path of the directory that is changed. - /// - public string Path { get; } - - internal MockDirectoryChanging(string path) - { - Path = path; - } - } - - /// - /// Notifies about a pending change of a file. - /// - public class MockFileChanging : MockFileSystemEvent - { - /// - /// The path of the file that is changed. - /// - public string Path { get; } - - internal MockFileChanging(string path) - { - Path = path; - } - } } diff --git a/tests/System.IO.Abstractions.TestingHelpers.Tests/MockFileSystemEventTests.cs b/tests/System.IO.Abstractions.TestingHelpers.Tests/MockFileSystemEventTests.cs index d417665eb..89cc9ddeb 100644 --- a/tests/System.IO.Abstractions.TestingHelpers.Tests/MockFileSystemEventTests.cs +++ b/tests/System.IO.Abstractions.TestingHelpers.Tests/MockFileSystemEventTests.cs @@ -7,98 +7,201 @@ namespace System.IO.Abstractions.TestingHelpers.Tests public class MockFileSystemEventTests { [Test] - public void OnFileChanging_WriteAllText_ShouldBeCalledWithFullFilePath() + public void OnFileChanging_SetExceptionToThrow_ShouldThrowExceptionAndNotCreateFile() { var basePath = Path.GetFullPath("/foo/bar"); var fileName = "foo.txt"; + var exception = new Exception("the file should not be created"); var expectedPath = Path.Combine(basePath, fileName); - var calledPath = string.Empty; var fs = new MockFileSystem(null, basePath) - .OnFileChanging(f => calledPath = f.Path); + .OnFileChanging(f => f.ExceptionToThrow = exception); - fs.File.WriteAllText(fileName, "some content"); + var receivedException = Assert.Throws(() => fs.File.WriteAllText(fileName, "some content")); + var result = fs.File.Exists(expectedPath); - Assert.That(calledPath, Is.EqualTo(expectedPath)); + Assert.That(receivedException, Is.EqualTo(exception)); + Assert.That(result, Is.False); } [Test] - public void OnFileChanging_SetExceptionToThrow_ShouldThrowExceptionAndNotCreateFile() + public void OnFileChanging_WithFileEvent_ShouldCallOnFileChangingWithFullFilePath() { var basePath = Path.GetFullPath("/foo/bar"); var fileName = "foo.txt"; - var exception = new Exception("the file should not be created"); var expectedPath = Path.Combine(basePath, fileName); + var calledPath = string.Empty; var fs = new MockFileSystem(null, basePath) - .OnFileChanging(f => f.ExceptionToThrow = exception); + .OnFileChanging(f => calledPath = f.Path); - var receivedException = Assert.Throws(() => fs.File.WriteAllText(fileName, "some content")); - var result = fs.File.Exists(expectedPath); + fs.File.WriteAllText(fileName, "some content"); - Assert.That(receivedException, Is.EqualTo(exception)); - Assert.That(result, Is.False); + Assert.That(calledPath, Is.EqualTo(expectedPath)); } [Test] - public void OnFileChanging_ReadAllText_ShouldNotBeCalled() + public void OnFileChanging_WithDirectoryEvent_ShouldNotBeCalled() { var basePath = Path.GetFullPath("/foo/bar"); - var fileName = "foo.txt"; + var directoryName = "test-directory"; bool isCalled = false; var fs = new MockFileSystem(new Dictionary { - { fileName, new MockFileData("some content") } + { directoryName, new MockFileData("some content") } }, basePath) .OnFileChanging(f => isCalled = true); - _ = fs.File.ReadAllText(fileName); + _ = fs.Directory.CreateDirectory(directoryName); Assert.That(isCalled, Is.False); } [Test] - public void OnDirectoryChanging_WriteAllText_ShouldBeCalledWithFullDirectoryPath() + public void OnDirectoryChanging_SetExceptionToThrow_ShouldThrowExceptionAndNotCreateDirectory() { var basePath = Path.GetFullPath("/foo/bar"); var directoryName = "test-directory"; + var exception = new Exception("the directory should not be created"); var expectedPath = Path.Combine(basePath, directoryName); - var calledPath = string.Empty; var fs = new MockFileSystem(null, basePath) - .OnDirectoryChanging(f => calledPath = f.Path); + .OnDirectoryChanging(f => f.ExceptionToThrow = exception); - fs.Directory.CreateDirectory(directoryName); + var receivedException = Assert.Throws(() => fs.Directory.CreateDirectory(directoryName)); + var result = fs.Directory.Exists(expectedPath); - Assert.That(calledPath, Is.EqualTo(expectedPath)); + Assert.That(receivedException, Is.EqualTo(exception)); + Assert.That(result, Is.False); } [Test] - public void OnDirectoryChanging_SetExceptionToThrow_ShouldThrowExceptionAndNotCreateDirectory() + public void OnDirectoryChanging_WithDirectoryEvent_ShouldCallOnDirectoryChangingWithFullDirectoryPath() { var basePath = Path.GetFullPath("/foo/bar"); var directoryName = "test-directory"; - var exception = new Exception("the directory should not be created"); var expectedPath = Path.Combine(basePath, directoryName); + var calledPath = string.Empty; var fs = new MockFileSystem(null, basePath) - .OnDirectoryChanging(f => f.ExceptionToThrow = exception); + .OnDirectoryChanging(f => calledPath = f.Path); - var receivedException = Assert.Throws(() => fs.Directory.CreateDirectory(directoryName)); - var result = fs.Directory.Exists(expectedPath); + fs.Directory.CreateDirectory(directoryName); - Assert.That(receivedException, Is.EqualTo(exception)); - Assert.That(result, Is.False); + Assert.That(calledPath, Is.EqualTo(expectedPath)); } [Test] - public void OnDirectoryChanging_ReadAllText_ShouldNotBeCalled() + public void OnDirectoryChanging_WithFileEvent_ShouldNotBeCalled() { var basePath = Path.GetFullPath("/foo/bar"); - var directoryName = "test-directory"; + var fileName = "test-directory"; bool isCalled = false; var fs = new MockFileSystem(null, basePath) .OnDirectoryChanging(f => isCalled = true); - _ = fs.Directory.Exists(directoryName); + fs.File.WriteAllText(fileName, "some content"); Assert.That(isCalled, Is.False); } + + [Test] + public void File_WriteAllText_NewFile_ShouldTriggerOnFileChangingWithCreatedType() + { + var fileName = "foo.txt"; + MockFileEvent.FileEventType? receivedEventType = null; + var fs = new MockFileSystem() + .OnFileChanging(f => receivedEventType = f.EventType); + + fs.File.WriteAllText(fileName, "some content"); + + Assert.That(receivedEventType, Is.EqualTo(MockFileEvent.FileEventType.Created)); + } + + [Test] + public void File_WriteAllText_ExistingFile_ShouldTriggerOnFileChangingWithUpdatedType() + { + var fileName = "foo.txt"; + MockFileEvent.FileEventType? receivedEventType = null; + var fs = new MockFileSystem(new Dictionary + { + { fileName, new MockFileData("some content") } + }).OnFileChanging(f => receivedEventType = f.EventType); + + fs.File.WriteAllText(fileName, "some content"); + + Assert.That(receivedEventType, Is.EqualTo(MockFileEvent.FileEventType.Updated)); + } + + [Test] + public void File_Delete_ShouldTriggerOnFileChangingWithDeletedType() + { + var fileName = "foo.txt"; + MockFileEvent.FileEventType? receivedEventType = null; + var fs = new MockFileSystem(new Dictionary + { + { fileName, new MockFileData("some content") } + }).OnFileChanging(f => receivedEventType = f.EventType); + + fs.File.Delete(fileName); + + Assert.That(receivedEventType, Is.EqualTo(MockFileEvent.FileEventType.Deleted)); + } + + [Test] + public void File_Move_ShouldTriggerOnFileChangingWithDeletedAndCreatedTypes() + { + var fileName = "foo.txt"; + var receivedEventTypes = new List(); + var fs = new MockFileSystem(new Dictionary + { + { fileName, new MockFileData("some content") } + }).OnFileChanging(f => receivedEventTypes.Add(f.EventType)); + + fs.File.Move(fileName, "bar.txt"); + + Assert.That(receivedEventTypes, Contains.Item(MockFileEvent.FileEventType.Deleted)); + Assert.That(receivedEventTypes, Contains.Item(MockFileEvent.FileEventType.Created)); + } + + [Test] + public void Directory_CreateDirectory_ShouldCallOnDirectoryChangingWithFullDirectoryPath() + { + var directoryName = "test-directory"; + MockDirectoryEvent.DirectoryEventType? receivedEventType = null; + var fs = new MockFileSystem() + .OnDirectoryChanging(f => receivedEventType = f.EventType); + + fs.Directory.CreateDirectory(directoryName); + + Assert.That(receivedEventType, Is.EqualTo(MockDirectoryEvent.DirectoryEventType.Created)); + } + + [Test] + public void Directory_DeleteDirectory_ShouldCallOnDirectoryChangingWithFullDirectoryPath() + { + var directoryName = "test-directory"; + MockDirectoryEvent.DirectoryEventType? receivedEventType = null; + var fs = new MockFileSystem(new Dictionary + { + { directoryName, new MockDirectoryData() } + }).OnDirectoryChanging(f => receivedEventType = f.EventType); + + fs.Directory.Delete(directoryName); + + Assert.That(receivedEventType, Is.EqualTo(MockDirectoryEvent.DirectoryEventType.Deleted)); + } + + [Test] + public void Directory_Move_ShouldTriggerOnDirectoryChangingWithDeletedAndCreatedTypes() + { + var fileName = "foo.txt"; + var receivedEventTypes = new List(); + var fs = new MockFileSystem(new Dictionary + { + { fileName, new MockDirectoryData() } + }).OnDirectoryChanging(f => receivedEventTypes.Add(f.EventType)); + + fs.Directory.Move(fileName, "bar.txt"); + + Assert.That(receivedEventTypes, Contains.Item(MockDirectoryEvent.DirectoryEventType.Deleted)); + Assert.That(receivedEventTypes, Contains.Item(MockDirectoryEvent.DirectoryEventType.Created)); + } } } From b132db87af0e981197c8b64d1183060230394bdd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Valentin=20Breu=C3=9F?= Date: Wed, 31 Aug 2022 14:31:56 +0200 Subject: [PATCH 3/4] Rename public MockFileSystem methods to not only indicate changed files, so that also file access can be triggered --- .../MockFileSystem.cs | 36 ++++++++++--------- .../MockFileSystemEventTests.cs | 26 +++++++------- 2 files changed, 32 insertions(+), 30 deletions(-) diff --git a/src/System.IO.Abstractions.TestingHelpers/MockFileSystem.cs b/src/System.IO.Abstractions.TestingHelpers/MockFileSystem.cs index 8d0c02f34..5b5651409 100644 --- a/src/System.IO.Abstractions.TestingHelpers/MockFileSystem.cs +++ b/src/System.IO.Abstractions.TestingHelpers/MockFileSystem.cs @@ -17,8 +17,8 @@ public class MockFileSystem : FileSystemBase, IMockFileDataAccessor private readonly IDictionary files; private readonly PathVerifier pathVerifier; - private Action onFileChanging; - private Action onDirectoryChanging; + private Action onFileEvent; + private Action onDirectoryEvent; /// public MockFileSystem() : this(null) { } @@ -93,20 +93,22 @@ public MockFileSystem(IDictionary files, string currentDir public PathVerifier PathVerifier => pathVerifier; /// - /// Registers a callback to be executed when a file is changing. + /// Registers a callback to be executed when a file event is triggered. + /// Enables throwing custom exceptions by setting the property of the callback parameter. /// - public MockFileSystem OnFileChanging(Action callback) + public MockFileSystem OnFileEvent(Action callback) { - onFileChanging = callback; + onFileEvent = callback; return this; } /// - /// Registers a callback to be executed when a directory is changing. + /// Registers a callback to be executed when a directory event is triggered. + /// Enables throwing custom exceptions by setting the property of the callback parameter. /// - public MockFileSystem OnDirectoryChanging(Action callback) + public MockFileSystem OnDirectoryEvent(Action callback) { - onDirectoryChanging = callback; + onDirectoryEvent = callback; return this; } @@ -199,11 +201,11 @@ public void AddFile(string path, MockFileData mockFile) var existingFile = GetFileWithoutFixingPath(fixedPath); if (existingFile == null) { - ExecuteCallbackAndCheckExceptionToThrow(onFileChanging, new MockFileEvent(fixedPath, MockFileEvent.FileEventType.Created)); + ExecuteCallbackAndCheckExceptionToThrow(onFileEvent, new MockFileEvent(fixedPath, MockFileEvent.FileEventType.Created)); } else { - ExecuteCallbackAndCheckExceptionToThrow(onFileChanging, new MockFileEvent(fixedPath, MockFileEvent.FileEventType.Updated)); + ExecuteCallbackAndCheckExceptionToThrow(onFileEvent, new MockFileEvent(fixedPath, MockFileEvent.FileEventType.Updated)); } SetEntry(fixedPath, mockFile ?? new MockFileData(string.Empty)); } @@ -253,7 +255,7 @@ public void AddDirectory(string path) var s = StringOperations.EndsWith(fixedPath, separator) ? fixedPath : fixedPath + separator; - ExecuteCallbackAndCheckExceptionToThrow(onDirectoryChanging, new MockDirectoryEvent(s.TrimSlashes(), MockDirectoryEvent.DirectoryEventType.Created)); + ExecuteCallbackAndCheckExceptionToThrow(onDirectoryEvent, new MockDirectoryEvent(s.TrimSlashes(), MockDirectoryEvent.DirectoryEventType.Created)); SetEntry(s, new MockDirectoryData()); } } @@ -314,13 +316,13 @@ public void MoveDirectory(string sourcePath, string destPath) entry.Path = newPath; if (entry.Data is MockDirectoryData) { - ExecuteCallbackAndCheckExceptionToThrow(onDirectoryChanging, new MockDirectoryEvent(path, MockDirectoryEvent.DirectoryEventType.Deleted)); - ExecuteCallbackAndCheckExceptionToThrow(onDirectoryChanging, new MockDirectoryEvent(newPath, MockDirectoryEvent.DirectoryEventType.Created)); + ExecuteCallbackAndCheckExceptionToThrow(onDirectoryEvent, new MockDirectoryEvent(path, MockDirectoryEvent.DirectoryEventType.Deleted)); + ExecuteCallbackAndCheckExceptionToThrow(onDirectoryEvent, new MockDirectoryEvent(newPath, MockDirectoryEvent.DirectoryEventType.Created)); } else { - ExecuteCallbackAndCheckExceptionToThrow(onFileChanging, new MockFileEvent(path, MockFileEvent.FileEventType.Deleted)); - ExecuteCallbackAndCheckExceptionToThrow(onFileChanging, new MockFileEvent(newPath, MockFileEvent.FileEventType.Created)); + ExecuteCallbackAndCheckExceptionToThrow(onFileEvent, new MockFileEvent(path, MockFileEvent.FileEventType.Deleted)); + ExecuteCallbackAndCheckExceptionToThrow(onFileEvent, new MockFileEvent(newPath, MockFileEvent.FileEventType.Created)); } files[newPath] = entry; files.Remove(path); @@ -362,11 +364,11 @@ public void RemoveFile(string path) var file = GetFileWithoutFixingPath(path); if (file is MockDirectoryData) { - ExecuteCallbackAndCheckExceptionToThrow(onDirectoryChanging, new MockDirectoryEvent(path, MockDirectoryEvent.DirectoryEventType.Deleted)); + ExecuteCallbackAndCheckExceptionToThrow(onDirectoryEvent, new MockDirectoryEvent(path, MockDirectoryEvent.DirectoryEventType.Deleted)); } else { - ExecuteCallbackAndCheckExceptionToThrow(onFileChanging, new MockFileEvent(path, MockFileEvent.FileEventType.Deleted)); + ExecuteCallbackAndCheckExceptionToThrow(onFileEvent, new MockFileEvent(path, MockFileEvent.FileEventType.Deleted)); } files.Remove(path); diff --git a/tests/System.IO.Abstractions.TestingHelpers.Tests/MockFileSystemEventTests.cs b/tests/System.IO.Abstractions.TestingHelpers.Tests/MockFileSystemEventTests.cs index 89cc9ddeb..504013d58 100644 --- a/tests/System.IO.Abstractions.TestingHelpers.Tests/MockFileSystemEventTests.cs +++ b/tests/System.IO.Abstractions.TestingHelpers.Tests/MockFileSystemEventTests.cs @@ -14,7 +14,7 @@ public void OnFileChanging_SetExceptionToThrow_ShouldThrowExceptionAndNotCreateF var exception = new Exception("the file should not be created"); var expectedPath = Path.Combine(basePath, fileName); var fs = new MockFileSystem(null, basePath) - .OnFileChanging(f => f.ExceptionToThrow = exception); + .OnFileEvent(f => f.ExceptionToThrow = exception); var receivedException = Assert.Throws(() => fs.File.WriteAllText(fileName, "some content")); var result = fs.File.Exists(expectedPath); @@ -31,7 +31,7 @@ public void OnFileChanging_WithFileEvent_ShouldCallOnFileChangingWithFullFilePat var expectedPath = Path.Combine(basePath, fileName); var calledPath = string.Empty; var fs = new MockFileSystem(null, basePath) - .OnFileChanging(f => calledPath = f.Path); + .OnFileEvent(f => calledPath = f.Path); fs.File.WriteAllText(fileName, "some content"); @@ -48,7 +48,7 @@ public void OnFileChanging_WithDirectoryEvent_ShouldNotBeCalled() { { directoryName, new MockFileData("some content") } }, basePath) - .OnFileChanging(f => isCalled = true); + .OnFileEvent(f => isCalled = true); _ = fs.Directory.CreateDirectory(directoryName); @@ -63,7 +63,7 @@ public void OnDirectoryChanging_SetExceptionToThrow_ShouldThrowExceptionAndNotCr var exception = new Exception("the directory should not be created"); var expectedPath = Path.Combine(basePath, directoryName); var fs = new MockFileSystem(null, basePath) - .OnDirectoryChanging(f => f.ExceptionToThrow = exception); + .OnDirectoryEvent(f => f.ExceptionToThrow = exception); var receivedException = Assert.Throws(() => fs.Directory.CreateDirectory(directoryName)); var result = fs.Directory.Exists(expectedPath); @@ -80,7 +80,7 @@ public void OnDirectoryChanging_WithDirectoryEvent_ShouldCallOnDirectoryChanging var expectedPath = Path.Combine(basePath, directoryName); var calledPath = string.Empty; var fs = new MockFileSystem(null, basePath) - .OnDirectoryChanging(f => calledPath = f.Path); + .OnDirectoryEvent(f => calledPath = f.Path); fs.Directory.CreateDirectory(directoryName); @@ -94,7 +94,7 @@ public void OnDirectoryChanging_WithFileEvent_ShouldNotBeCalled() var fileName = "test-directory"; bool isCalled = false; var fs = new MockFileSystem(null, basePath) - .OnDirectoryChanging(f => isCalled = true); + .OnDirectoryEvent(f => isCalled = true); fs.File.WriteAllText(fileName, "some content"); @@ -107,7 +107,7 @@ public void File_WriteAllText_NewFile_ShouldTriggerOnFileChangingWithCreatedType var fileName = "foo.txt"; MockFileEvent.FileEventType? receivedEventType = null; var fs = new MockFileSystem() - .OnFileChanging(f => receivedEventType = f.EventType); + .OnFileEvent(f => receivedEventType = f.EventType); fs.File.WriteAllText(fileName, "some content"); @@ -122,7 +122,7 @@ public void File_WriteAllText_ExistingFile_ShouldTriggerOnFileChangingWithUpdate var fs = new MockFileSystem(new Dictionary { { fileName, new MockFileData("some content") } - }).OnFileChanging(f => receivedEventType = f.EventType); + }).OnFileEvent(f => receivedEventType = f.EventType); fs.File.WriteAllText(fileName, "some content"); @@ -137,7 +137,7 @@ public void File_Delete_ShouldTriggerOnFileChangingWithDeletedType() var fs = new MockFileSystem(new Dictionary { { fileName, new MockFileData("some content") } - }).OnFileChanging(f => receivedEventType = f.EventType); + }).OnFileEvent(f => receivedEventType = f.EventType); fs.File.Delete(fileName); @@ -152,7 +152,7 @@ public void File_Move_ShouldTriggerOnFileChangingWithDeletedAndCreatedTypes() var fs = new MockFileSystem(new Dictionary { { fileName, new MockFileData("some content") } - }).OnFileChanging(f => receivedEventTypes.Add(f.EventType)); + }).OnFileEvent(f => receivedEventTypes.Add(f.EventType)); fs.File.Move(fileName, "bar.txt"); @@ -166,7 +166,7 @@ public void Directory_CreateDirectory_ShouldCallOnDirectoryChangingWithFullDirec var directoryName = "test-directory"; MockDirectoryEvent.DirectoryEventType? receivedEventType = null; var fs = new MockFileSystem() - .OnDirectoryChanging(f => receivedEventType = f.EventType); + .OnDirectoryEvent(f => receivedEventType = f.EventType); fs.Directory.CreateDirectory(directoryName); @@ -181,7 +181,7 @@ public void Directory_DeleteDirectory_ShouldCallOnDirectoryChangingWithFullDirec var fs = new MockFileSystem(new Dictionary { { directoryName, new MockDirectoryData() } - }).OnDirectoryChanging(f => receivedEventType = f.EventType); + }).OnDirectoryEvent(f => receivedEventType = f.EventType); fs.Directory.Delete(directoryName); @@ -196,7 +196,7 @@ public void Directory_Move_ShouldTriggerOnDirectoryChangingWithDeletedAndCreated var fs = new MockFileSystem(new Dictionary { { fileName, new MockDirectoryData() } - }).OnDirectoryChanging(f => receivedEventTypes.Add(f.EventType)); + }).OnDirectoryEvent(f => receivedEventTypes.Add(f.EventType)); fs.Directory.Move(fileName, "bar.txt"); From faa513166b87f6340ef7600633faf55e48fc8c13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Valentin=20Breu=C3=9F?= Date: Wed, 31 Aug 2022 14:54:36 +0200 Subject: [PATCH 4/4] Simplify implementation --- .../MockDirectoryEvent.cs | 2 +- .../MockFileEvent.cs | 2 +- .../MockFileSystem.cs | 35 ++++++------------- .../MockFileSystemEvent.cs | 13 ------- .../MockFileSystemEventTests.cs | 8 ++--- 5 files changed, 17 insertions(+), 43 deletions(-) delete mode 100644 src/System.IO.Abstractions.TestingHelpers/MockFileSystemEvent.cs diff --git a/src/System.IO.Abstractions.TestingHelpers/MockDirectoryEvent.cs b/src/System.IO.Abstractions.TestingHelpers/MockDirectoryEvent.cs index 34a50f169..939f512d2 100644 --- a/src/System.IO.Abstractions.TestingHelpers/MockDirectoryEvent.cs +++ b/src/System.IO.Abstractions.TestingHelpers/MockDirectoryEvent.cs @@ -3,7 +3,7 @@ /// /// Notifies about a pending directory event. /// - public class MockDirectoryEvent : MockFileSystemEvent + public class MockDirectoryEvent { /// /// The path of the directory. diff --git a/src/System.IO.Abstractions.TestingHelpers/MockFileEvent.cs b/src/System.IO.Abstractions.TestingHelpers/MockFileEvent.cs index 3ea9a6719..4a465bba1 100644 --- a/src/System.IO.Abstractions.TestingHelpers/MockFileEvent.cs +++ b/src/System.IO.Abstractions.TestingHelpers/MockFileEvent.cs @@ -3,7 +3,7 @@ /// /// Notifies about a pending file event. /// - public class MockFileEvent : MockFileSystemEvent + public class MockFileEvent { /// /// The path of the file. diff --git a/src/System.IO.Abstractions.TestingHelpers/MockFileSystem.cs b/src/System.IO.Abstractions.TestingHelpers/MockFileSystem.cs index 5b5651409..573ba6e2c 100644 --- a/src/System.IO.Abstractions.TestingHelpers/MockFileSystem.cs +++ b/src/System.IO.Abstractions.TestingHelpers/MockFileSystem.cs @@ -4,7 +4,6 @@ namespace System.IO.Abstractions.TestingHelpers { - using static System.Net.WebRequestMethods; using XFS = MockUnixSupport; /// @@ -93,8 +92,7 @@ public MockFileSystem(IDictionary files, string currentDir public PathVerifier PathVerifier => pathVerifier; /// - /// Registers a callback to be executed when a file event is triggered. - /// Enables throwing custom exceptions by setting the property of the callback parameter. + /// Registers a callback to be executed when a file event is triggered. /// public MockFileSystem OnFileEvent(Action callback) { @@ -103,8 +101,7 @@ public MockFileSystem OnFileEvent(Action callback) } /// - /// Registers a callback to be executed when a directory event is triggered. - /// Enables throwing custom exceptions by setting the property of the callback parameter. + /// Registers a callback to be executed when a directory event is triggered. /// public MockFileSystem OnDirectoryEvent(Action callback) { @@ -161,16 +158,6 @@ private void SetEntry(string path, MockFileData mockFile) files[path] = new FileSystemEntry { Path = path, Data = mockFile }; } - private void ExecuteCallbackAndCheckExceptionToThrow(Action callback, T fileChanging) - where T : MockFileSystemEvent - { - callback?.Invoke(fileChanging); - if (fileChanging.ExceptionToThrow != null) - { - throw fileChanging.ExceptionToThrow; - } - } - /// public void AddFile(string path, MockFileData mockFile) { @@ -201,11 +188,11 @@ public void AddFile(string path, MockFileData mockFile) var existingFile = GetFileWithoutFixingPath(fixedPath); if (existingFile == null) { - ExecuteCallbackAndCheckExceptionToThrow(onFileEvent, new MockFileEvent(fixedPath, MockFileEvent.FileEventType.Created)); + onFileEvent?.Invoke(new MockFileEvent(fixedPath, MockFileEvent.FileEventType.Created)); } else { - ExecuteCallbackAndCheckExceptionToThrow(onFileEvent, new MockFileEvent(fixedPath, MockFileEvent.FileEventType.Updated)); + onFileEvent?.Invoke(new MockFileEvent(fixedPath, MockFileEvent.FileEventType.Updated)); } SetEntry(fixedPath, mockFile ?? new MockFileData(string.Empty)); } @@ -255,7 +242,7 @@ public void AddDirectory(string path) var s = StringOperations.EndsWith(fixedPath, separator) ? fixedPath : fixedPath + separator; - ExecuteCallbackAndCheckExceptionToThrow(onDirectoryEvent, new MockDirectoryEvent(s.TrimSlashes(), MockDirectoryEvent.DirectoryEventType.Created)); + onDirectoryEvent?.Invoke(new MockDirectoryEvent(s.TrimSlashes(), MockDirectoryEvent.DirectoryEventType.Created)); SetEntry(s, new MockDirectoryData()); } } @@ -316,13 +303,13 @@ public void MoveDirectory(string sourcePath, string destPath) entry.Path = newPath; if (entry.Data is MockDirectoryData) { - ExecuteCallbackAndCheckExceptionToThrow(onDirectoryEvent, new MockDirectoryEvent(path, MockDirectoryEvent.DirectoryEventType.Deleted)); - ExecuteCallbackAndCheckExceptionToThrow(onDirectoryEvent, new MockDirectoryEvent(newPath, MockDirectoryEvent.DirectoryEventType.Created)); + onDirectoryEvent?.Invoke(new MockDirectoryEvent(path, MockDirectoryEvent.DirectoryEventType.Deleted)); + onDirectoryEvent?.Invoke(new MockDirectoryEvent(newPath, MockDirectoryEvent.DirectoryEventType.Created)); } else { - ExecuteCallbackAndCheckExceptionToThrow(onFileEvent, new MockFileEvent(path, MockFileEvent.FileEventType.Deleted)); - ExecuteCallbackAndCheckExceptionToThrow(onFileEvent, new MockFileEvent(newPath, MockFileEvent.FileEventType.Created)); + onFileEvent?.Invoke(new MockFileEvent(path, MockFileEvent.FileEventType.Deleted)); + onFileEvent?.Invoke(new MockFileEvent(newPath, MockFileEvent.FileEventType.Created)); } files[newPath] = entry; files.Remove(path); @@ -364,11 +351,11 @@ public void RemoveFile(string path) var file = GetFileWithoutFixingPath(path); if (file is MockDirectoryData) { - ExecuteCallbackAndCheckExceptionToThrow(onDirectoryEvent, new MockDirectoryEvent(path, MockDirectoryEvent.DirectoryEventType.Deleted)); + onDirectoryEvent?.Invoke(new MockDirectoryEvent(path, MockDirectoryEvent.DirectoryEventType.Deleted)); } else { - ExecuteCallbackAndCheckExceptionToThrow(onFileEvent, new MockFileEvent(path, MockFileEvent.FileEventType.Deleted)); + onFileEvent?.Invoke(new MockFileEvent(path, MockFileEvent.FileEventType.Deleted)); } files.Remove(path); diff --git a/src/System.IO.Abstractions.TestingHelpers/MockFileSystemEvent.cs b/src/System.IO.Abstractions.TestingHelpers/MockFileSystemEvent.cs deleted file mode 100644 index ba35455e4..000000000 --- a/src/System.IO.Abstractions.TestingHelpers/MockFileSystemEvent.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace System.IO.Abstractions.TestingHelpers -{ - /// - /// Base class for mock file system events. - /// - public abstract class MockFileSystemEvent - { - /// - /// By setting the the corresponding exception will be thrown instead of changing the mock file system. - /// - public Exception ExceptionToThrow { get; set; } - } -} diff --git a/tests/System.IO.Abstractions.TestingHelpers.Tests/MockFileSystemEventTests.cs b/tests/System.IO.Abstractions.TestingHelpers.Tests/MockFileSystemEventTests.cs index 504013d58..b12015414 100644 --- a/tests/System.IO.Abstractions.TestingHelpers.Tests/MockFileSystemEventTests.cs +++ b/tests/System.IO.Abstractions.TestingHelpers.Tests/MockFileSystemEventTests.cs @@ -7,14 +7,14 @@ namespace System.IO.Abstractions.TestingHelpers.Tests public class MockFileSystemEventTests { [Test] - public void OnFileChanging_SetExceptionToThrow_ShouldThrowExceptionAndNotCreateFile() + public void OnFileChanging_ThrowExceptionInCallback_ShouldThrowExceptionAndNotCreateFile() { var basePath = Path.GetFullPath("/foo/bar"); var fileName = "foo.txt"; var exception = new Exception("the file should not be created"); var expectedPath = Path.Combine(basePath, fileName); var fs = new MockFileSystem(null, basePath) - .OnFileEvent(f => f.ExceptionToThrow = exception); + .OnFileEvent(_ => throw exception); var receivedException = Assert.Throws(() => fs.File.WriteAllText(fileName, "some content")); var result = fs.File.Exists(expectedPath); @@ -56,14 +56,14 @@ public void OnFileChanging_WithDirectoryEvent_ShouldNotBeCalled() } [Test] - public void OnDirectoryChanging_SetExceptionToThrow_ShouldThrowExceptionAndNotCreateDirectory() + public void OnDirectoryChanging_ThrowExceptionInCallback_ShouldThrowExceptionAndNotCreateDirectory() { var basePath = Path.GetFullPath("/foo/bar"); var directoryName = "test-directory"; var exception = new Exception("the directory should not be created"); var expectedPath = Path.Combine(basePath, directoryName); var fs = new MockFileSystem(null, basePath) - .OnDirectoryEvent(f => f.ExceptionToThrow = exception); + .OnDirectoryEvent(_ => throw exception); var receivedException = Assert.Throws(() => fs.Directory.CreateDirectory(directoryName)); var result = fs.Directory.Exists(expectedPath);