From d50e08b1e35ceedf02b9b79b7b81b4339e2447b5 Mon Sep 17 00:00:00 2001 From: Robert Dailey Date: Sat, 28 May 2022 18:12:30 -0500 Subject: [PATCH] refactor: Add MergeDirectory() method + tests --- .../Extensions/FileSystemExtensionsTest.cs | 116 ++++++++++++++++++ src/Common/Extensions/FileSystemExtensions.cs | 30 +++++ 2 files changed, 146 insertions(+) create mode 100644 src/Common.Tests/Extensions/FileSystemExtensionsTest.cs create mode 100644 src/Common/Extensions/FileSystemExtensions.cs diff --git a/src/Common.Tests/Extensions/FileSystemExtensionsTest.cs b/src/Common.Tests/Extensions/FileSystemExtensionsTest.cs new file mode 100644 index 00000000..9a093a61 --- /dev/null +++ b/src/Common.Tests/Extensions/FileSystemExtensionsTest.cs @@ -0,0 +1,116 @@ +using System.IO.Abstractions; +using System.IO.Abstractions.TestingHelpers; +using System.Text.RegularExpressions; +using Common.Extensions; +using FluentAssertions; +using NUnit.Framework; + +namespace Common.Tests.Extensions; + +[TestFixture] +[Parallelizable(ParallelScope.All)] +public class FileSystemExtensionsTest +{ + private static IEnumerable ReRootFiles(IFileSystem fs, IEnumerable files, + string oldRoot, string newRoot) + { + return files.Select(x => + { + var strippedPath = Regex.Replace(x, $"^{oldRoot}", newRoot); + return fs.Path.GetFullPath(strippedPath); + }); + } + + private static ICollection NormalizePaths(IEnumerable paths) + => paths.Select(NormalizePath).ToList(); + + private static string NormalizePath(string path) + { + if (MockUnixSupport.IsUnixPlatform()) + { + return Regex.Replace(path, @"^C:\\", "/").Replace("\\", "/"); + } + + return Regex.Replace(path, @"^/", @"C:\").Replace("/", "\\"); + } + + private static MockFileSystem NewMockFileSystem(IEnumerable files, string cwd) + { + return NewMockFileSystem(files, Array.Empty(), cwd); + } + + private static MockFileSystem NewMockFileSystem(IEnumerable files, IEnumerable dirs, string cwd) + { + var dirData = dirs.Select(x => (x, (MockFileData) new MockDirectoryData())); + var fileData = files.Select(x => (x, new MockFileData(""))); + + return new MockFileSystem(fileData.Concat(dirData) + .ToDictionary(x => x.Item1, y => y.Item2), NormalizePath(cwd)); + } + + [Test] + public void Merge_directories_works() + { + var files = NormalizePaths(new[] + { + @"path1\1\file1.txt", + @"path1\1\file2.txt", + @"path1\1\2\3\4\file3.txt", + @"path1\file4.txt" + }); + + var dirs = NormalizePaths(new[] + { + @"path1\empty1", + @"path1\empty2", + @"path1\1\2\empty3", + @"path1\1\2\3\4\empty4" + }); + + var fs = NewMockFileSystem(files, dirs, @"C:\root\path"); + + fs.MergeDirectory("path1", "path2"); + + fs.AllDirectories.Select(MockUnixSupport.Path).Should() + .NotContain(x => x.Contains("path1") || x.Contains("empty")); + + fs.AllFiles.Should().BeEquivalentTo(ReRootFiles(fs, files, "path1", "path2")); + } + + [Test] + public void Fail_if_file_already_exists() + { + var files = NormalizePaths(new[] + { + @"path1\1\file1.txt", + @"path1\1\file2.txt", + @"path2\1\file1.txt" + }); + + var fs = NewMockFileSystem(files, @"C:\root\path"); + + var act = () => fs.MergeDirectory("path1", "path2"); + + act.Should().Throw(); + } + + [Test] + public void Fail_if_directory_exists_where_file_goes() + { + var files = NormalizePaths(new[] + { + @"path1\1\file1" + }); + + var dirs = NormalizePaths(new[] + { + @"path2\1\file1" + }); + + var fs = NewMockFileSystem(files, dirs, @"C:\root\path"); + + var act = () => fs.MergeDirectory("path1", "path2"); + + act.Should().Throw(); + } +} diff --git a/src/Common/Extensions/FileSystemExtensions.cs b/src/Common/Extensions/FileSystemExtensions.cs new file mode 100644 index 00000000..f3dab8fc --- /dev/null +++ b/src/Common/Extensions/FileSystemExtensions.cs @@ -0,0 +1,30 @@ +using System.IO.Abstractions; +using System.Text.RegularExpressions; + +namespace Common.Extensions; + +public static class FileSystemExtensions +{ + public static void MergeDirectory(this IFileSystem fs, string targetDir, string destDir) + { + targetDir = fs.Path.GetFullPath(targetDir); + destDir = fs.Path.GetFullPath(destDir); + + var directories = fs.DirectoryInfo.FromDirectoryName(targetDir) + .EnumerateDirectories("*", SearchOption.AllDirectories) + .Append(fs.DirectoryInfo.FromDirectoryName(targetDir)) + .OrderByDescending(x => x.FullName.Count(y => y is '/' or '\\')); + + foreach (var dir in directories) + { + foreach (var file in dir.EnumerateFiles()) + { + var newPath = Regex.Replace(file.FullName, $"^{Regex.Escape(targetDir)}", destDir); + fs.Directory.CreateDirectory(fs.Path.GetDirectoryName(newPath)); + file.MoveTo(newPath); + } + + dir.Delete(); + } + } +}