DirectoryUtilsTest.cs 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. namespace MeloongCore.Tests;
  2. public class DirectoryUtilsTest : TestWithFolder {
  3. // 在 tempFolder 下创建固定的测试目录结构:
  4. // root/
  5. // a.txt
  6. // b.json
  7. // sub/
  8. // c.txt
  9. // nested/
  10. private void CreateTestStructure(string root) {
  11. DirectoryUtils.Create(Path.Combine(root, "sub", "nested"));
  12. FileUtils.Write(Path.Combine(root, "a.txt"), "a");
  13. FileUtils.Write(Path.Combine(root, "b.json"), "b");
  14. FileUtils.Write(Path.Combine(root, "sub", "c.txt"), "c");
  15. }
  16. #region GetFiles
  17. [Test]
  18. [Arguments(false, "*", 3)] // 递归:a.txt b.json sub/c.txt
  19. [Arguments(true, "*", 2)] // 仅顶层:a.txt b.json
  20. [Arguments(false, "*.txt", 2)] // 递归 + 过滤:a.txt sub/c.txt
  21. [Arguments(true, "*.txt", 1)] // 仅顶层 + 过滤:a.txt
  22. public async Task GetFiles_Options(bool topDirectoryOnly, string searchPattern, int expectedCount) {
  23. var root = Path.Combine(tempFolder, "root");
  24. CreateTestStructure(root);
  25. var files = DirectoryUtils.GetFiles(root, topDirectoryOnly, searchPattern).ToList();
  26. await Assert.That(files.Count).IsEqualTo(expectedCount);
  27. }
  28. [Test]
  29. public async Task GetFiles_NotExist_ReturnsEmpty() {
  30. var files = DirectoryUtils.GetFiles(Path.Combine(tempFolder, "none"));
  31. await Assert.That(files.Any()).IsFalse();
  32. }
  33. [Test]
  34. public async Task GetFiles_PathsHaveNoLongPathPrefix() {
  35. var root = Path.Combine(tempFolder, "root");
  36. CreateTestStructure(root);
  37. foreach (var file in DirectoryUtils.GetFiles(root))
  38. await Assert.That(file.StartsWithF(@"\\?\")).IsFalse();
  39. }
  40. #endregion
  41. #region GetDirectories
  42. [Test]
  43. [Arguments(false, 2)] // 递归:sub sub/nested
  44. [Arguments(true, 1)] // 仅顶层:sub
  45. public async Task GetDirectories_TopDirectoryOnly(bool topDirectoryOnly, int expectedCount) {
  46. var root = Path.Combine(tempFolder, "root");
  47. CreateTestStructure(root);
  48. var dirs = DirectoryUtils.GetDirectories(root, topDirectoryOnly).ToList();
  49. await Assert.That(dirs.Count).IsEqualTo(expectedCount);
  50. }
  51. [Test]
  52. public async Task GetDirectories_NotExist_ReturnsEmpty() {
  53. var dirs = DirectoryUtils.GetDirectories(Path.Combine(tempFolder, "none"));
  54. await Assert.That(dirs.Any()).IsFalse();
  55. }
  56. [Test]
  57. public async Task GetDirectories_PathsHaveNoTrailingSeparator() {
  58. var root = Path.Combine(tempFolder, "root");
  59. CreateTestStructure(root);
  60. foreach (var dir in DirectoryUtils.GetDirectories(root))
  61. await Assert.That(dir.EndsWithF(Path.DirectorySeparatorChar)).IsFalse();
  62. }
  63. #endregion
  64. #region IsEmpty
  65. [Test]
  66. [Arguments(false, false, true)] // 文件夹不存在 → true
  67. [Arguments(true, false, true)] // 文件夹存在但为空 → true
  68. [Arguments(true, true, false)] // 文件夹存在且含有文件 → false
  69. public async Task IsEmpty_Scenarios(bool createFolder, bool addFile, bool expectedResult) {
  70. var folder = Path.Combine(tempFolder, "target");
  71. if (createFolder) DirectoryUtils.Create(folder);
  72. if (addFile) FileUtils.Write(Path.Combine(folder, "f.txt"), "x");
  73. await Assert.That(DirectoryUtils.IsEmpty(folder)).IsEqualTo(expectedResult);
  74. }
  75. [Test]
  76. public async Task IsEmpty_WithSubDir_IsFalse() {
  77. var folder = Path.Combine(tempFolder, "target");
  78. DirectoryUtils.Create(Path.Combine(folder, "sub"));
  79. await Assert.That(DirectoryUtils.IsEmpty(folder)).IsFalse();
  80. }
  81. #endregion
  82. #region Copy
  83. [Test]
  84. public async Task Copy_SamePath_IsNoOp() {
  85. var src = Path.Combine(tempFolder, "src");
  86. FileUtils.Write(Path.Combine(src, "f.txt"), "hello");
  87. DirectoryUtils.Copy(src, src);
  88. // 来源依然存在且内容不变
  89. await Assert.That(DirectoryUtils.Exists(src)).IsTrue();
  90. await Assert.That(FileUtils.ReadAsString(Path.Combine(src, "f.txt"))).IsEqualTo("hello");
  91. }
  92. [Test]
  93. [Arguments("hello", "world")]
  94. [Arguments("内容A", "内容B")]
  95. public async Task Copy_NewPath_DuplicatesFilesWithContent(string content1, string content2) {
  96. var src = Path.Combine(tempFolder, "cp_src");
  97. var dest = Path.Combine(tempFolder, "cp_dest");
  98. FileUtils.Write(Path.Combine(src, "a.txt"), content1);
  99. FileUtils.Write(Path.Combine(src, "sub", "b.txt"), content2);
  100. DirectoryUtils.Copy(src, dest);
  101. // 来源保留,目标存在且内容一致
  102. await Assert.That(DirectoryUtils.Exists(src)).IsTrue();
  103. await Assert.That(FileUtils.ReadAsString(Path.Combine(dest, "a.txt"))).IsEqualTo(content1);
  104. await Assert.That(FileUtils.ReadAsString(Path.Combine(dest, "sub", "b.txt"))).IsEqualTo(content2);
  105. }
  106. #endregion
  107. #region Move
  108. [Test]
  109. public async Task Move_SamePath_IsNoOp() {
  110. var src = Path.Combine(tempFolder, "mv_src");
  111. FileUtils.Write(Path.Combine(src, "f.txt"), "data");
  112. DirectoryUtils.Move(src, src);
  113. await Assert.That(DirectoryUtils.Exists(src)).IsTrue();
  114. await Assert.That(FileUtils.ReadAsString(Path.Combine(src, "f.txt"))).IsEqualTo("data");
  115. }
  116. [Test]
  117. [Arguments("content1")]
  118. [Arguments("内容2")]
  119. public async Task Move_NewPath_MovesAllFiles(string fileContent) {
  120. var src = Path.Combine(tempFolder, "mv2_src");
  121. var dest = Path.Combine(tempFolder, "mv2_dest");
  122. FileUtils.Write(Path.Combine(src, "a.txt"), fileContent);
  123. FileUtils.Write(Path.Combine(src, "sub", "b.txt"), "nested");
  124. DirectoryUtils.Move(src, dest);
  125. // 来源已消失,目标文件内容正确
  126. await Assert.That(DirectoryUtils.Exists(src)).IsFalse();
  127. await Assert.That(FileUtils.ReadAsString(Path.Combine(dest, "a.txt"))).IsEqualTo(fileContent);
  128. await Assert.That(FileUtils.Exists(Path.Combine(dest, "sub", "b.txt"))).IsTrue();
  129. }
  130. [Test]
  131. public void Move_SafetyCheck_ThrowsOnRoot()
  132. => Assert.Throws<UnauthorizedAccessException>(() => DirectoryUtils.Move(@"C:\", @"C:\Temp"));
  133. #endregion
  134. #region Delete
  135. [Test]
  136. public void Delete_NotExist_NoOp() {
  137. // 不应抛出异常
  138. DirectoryUtils.Delete(Path.Combine(tempFolder, "del_none"));
  139. }
  140. [Test]
  141. [Arguments(true, false)] // 仅含文件
  142. [Arguments(false, true)] // 仅含子文件夹
  143. [Arguments(true, true)] // 文件与子文件夹均有
  144. public async Task Delete_WithContent_DeletesRecursively(bool addFile, bool addSubDir) {
  145. var folder = Path.Combine(tempFolder, "del");
  146. if (addFile) FileUtils.Write(Path.Combine(folder, "f.txt"), "x");
  147. if (addSubDir) DirectoryUtils.Create(Path.Combine(folder, "sub"));
  148. DirectoryUtils.Delete(folder);
  149. await Assert.That(DirectoryUtils.Exists(folder)).IsFalse();
  150. }
  151. [Test]
  152. public void Delete_SafetyCheck_ThrowsOnRoot()
  153. => Assert.Throws<UnauthorizedAccessException>(() => DirectoryUtils.Delete(@"C:\"));
  154. #endregion
  155. }