FileUtilsTest.cs 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262
  1. namespace MeloongCore.Tests;
  2. public class FileUtilsTest : TestWithFolder {
  3. #region 压缩包
  4. [Theory]
  5. [InlineData("GB Encoding.zip")]
  6. [InlineData("UTF8 Encoding.zip")]
  7. public void Compression_ReadZip(string testFile) {
  8. double progress = 0;
  9. string output = Path.Combine(tempFolder, "Extracted");
  10. FileUtils.ExtractToDirectory(GetTestFile(testFile), output, p => progress = p);
  11. Assert.Equal(1, progress);
  12. Assert.True(DirectoryUtils.Exists(Path.Combine(output, "文件夹")));
  13. Assert.False(DirectoryUtils.Exists(Path.Combine(output, "空文件夹")));
  14. Assert.Contains("FabricLoader", File.ReadAllText(PathUtils.WithLongPath(Path.Combine(output, "fabricloader.log"))));
  15. Assert.Contains("测试内容", File.ReadAllText(PathUtils.WithLongPath(Path.Combine(output, "文件夹", "中文文件.txt"))));
  16. }
  17. [Theory]
  18. [InlineData("GZ.gz", "LTCat")]
  19. public void Compression_ReadGz(string testFile, string containsText) {
  20. double progress = 0;
  21. string output = Path.Combine(tempFolder, "Extracted");
  22. FileUtils.ExtractToDirectory(GetTestFile(testFile), output, p => progress = p);
  23. Assert.Equal(1, progress);
  24. Assert.Contains(containsText, File.ReadAllText(PathUtils.WithLongPath(Path.Combine(output, Path.GetFileNameWithoutExtension(testFile)))));
  25. }
  26. [Theory]
  27. [InlineData("Corrupted.zip")]
  28. [InlineData("Not zip.zip")]
  29. public void Compression_ReadBad(string testFile) {
  30. Assert.Throws<InvalidDataException>(() => FileUtils.ExtractToDirectory(GetTestFile(testFile), tempFolder));
  31. }
  32. [Theory]
  33. [InlineData("DotDot ZipSlip.zip")]
  34. [InlineData("AbsPath ZipSlip.zip")]
  35. public void Compression_ZipSlip(string testFile) {
  36. Assert.Throws<UnauthorizedAccessException>(() => FileUtils.ExtractToDirectory(GetTestFile(testFile), tempFolder));
  37. }
  38. #endregion
  39. /*
  40. *
  41. #region CreateZipFromDirectory 测试
  42. public void CreateZipFromDirectory_SingleFile_ZipContainsFile() {
  43. string srcDir = CreateSourceDirectory(("hello.txt", "hello world"));
  44. string outZip = Path.Combine(_tempDir, "from_dir.zip");
  45. FileUtils.CreateZipFromDirectory(outZip, srcDir);
  46. Assert.True(File.Exists(outZip));
  47. using var archive = ZipFile.OpenRead(outZip);
  48. Assert.Single(archive.Entries);
  49. Assert.Equal("hello.txt", archive.Entries[0].Name);
  50. }
  51. public void CreateZipFromDirectory_MultipleFiles_AllFilesIncluded() {
  52. string srcDir = CreateSourceDirectory(
  53. ("a.txt", "aaa"),
  54. ("b.txt", "bbb"),
  55. ("c.txt", "ccc"));
  56. string outZip = Path.Combine(_tempDir, "from_dir_multi.zip");
  57. FileUtils.CreateZipFromDirectory(outZip, srcDir);
  58. using var archive = ZipFile.OpenRead(outZip);
  59. Assert.Equal(3, archive.Entries.Count);
  60. }
  61. public void CreateZipFromDirectory_SubdirectoryFiles_StructurePreserved() {
  62. string srcDir = CreateSourceDirectory(
  63. ("sub/file.txt", "nested"),
  64. ("root.txt", "root"));
  65. string outZip = Path.Combine(_tempDir, "from_dir_sub.zip");
  66. FileUtils.CreateZipFromDirectory(outZip, srcDir);
  67. using var archive = ZipFile.OpenRead(outZip);
  68. var names = archive.Entries.Select(e => e.FullName.Replace('\\', '/')).ToList();
  69. Assert.Contains(names, n => n.Contains("sub/file.txt"));
  70. Assert.Contains(names, n => n.Contains("root.txt"));
  71. }
  72. public void CreateZipFromDirectory_OverwritesExistingZip() {
  73. string srcDir = CreateSourceDirectory(("new.txt", "new content"));
  74. string outZip = Path.Combine(_tempDir, "overwrite_dir.zip");
  75. // 先创建一个旧 zip
  76. string oldSrcDir = CreateSourceDirectory(("old.txt", "old content"));
  77. FileUtils.CreateZipFromDirectory(outZip, oldSrcDir);
  78. // 再用新目录覆盖
  79. FileUtils.CreateZipFromDirectory(outZip, srcDir);
  80. using var archive = ZipFile.OpenRead(outZip);
  81. Assert.Single(archive.Entries);
  82. Assert.Equal("new.txt", archive.Entries[0].Name);
  83. }
  84. public void CreateZipFromDirectory_OutputDirectoryAutoCreated() {
  85. string srcDir = CreateSourceDirectory(("f.txt", "x"));
  86. string outZip = Path.Combine(_tempDir, "newdir", "nested.zip");
  87. FileUtils.CreateZipFromDirectory(outZip, srcDir);
  88. Assert.True(File.Exists(outZip));
  89. }
  90. #endregion
  91. #region CreateZipFromFiles(params 版)测试
  92. public void CreateZipFromFiles_Params_SingleFile_ZipContainsFile() {
  93. string srcFile = Path.Combine(_tempDir, "single.txt");
  94. File.WriteAllText(srcFile, "content");
  95. string outZip = Path.Combine(_tempDir, "from_files_single.zip");
  96. FileUtils.CreateZipFromFiles(outZip, srcFile);
  97. using var archive = ZipFile.OpenRead(outZip);
  98. Assert.Single(archive.Entries);
  99. Assert.Equal("single.txt", archive.Entries[0].Name);
  100. }
  101. public void CreateZipFromFiles_Params_MultipleFiles_AllIncluded() {
  102. string file1 = Path.Combine(_tempDir, "f1.txt");
  103. string file2 = Path.Combine(_tempDir, "f2.txt");
  104. File.WriteAllText(file1, "1");
  105. File.WriteAllText(file2, "2");
  106. string outZip = Path.Combine(_tempDir, "from_files_multi.zip");
  107. FileUtils.CreateZipFromFiles(outZip, file1, file2);
  108. using var archive = ZipFile.OpenRead(outZip);
  109. Assert.Equal(2, archive.Entries.Count);
  110. var names = archive.Entries.Select(e => e.Name).ToHashSet();
  111. Assert.Contains("f1.txt", names);
  112. Assert.Contains("f2.txt", names);
  113. }
  114. public void CreateZipFromFiles_Params_DuplicateFileNames_ThrowsArgumentException() {
  115. string file1 = Path.Combine(_tempDir, "dup", "a", "same.txt");
  116. string file2 = Path.Combine(_tempDir, "dup", "b", "same.txt");
  117. Directory.CreateDirectory(Path.GetDirectoryName(file1)!);
  118. Directory.CreateDirectory(Path.GetDirectoryName(file2)!);
  119. File.WriteAllText(file1, "1");
  120. File.WriteAllText(file2, "2");
  121. string outZip = Path.Combine(_tempDir, "dup_names.zip");
  122. Assert.Throws<ArgumentException>(() => FileUtils.CreateZipFromFiles(outZip, file1, file2));
  123. }
  124. public void CreateZipFromFiles_Params_OverwritesExistingZip() {
  125. string oldFile = Path.Combine(_tempDir, "old.txt");
  126. string newFile = Path.Combine(_tempDir, "new.txt");
  127. File.WriteAllText(oldFile, "old");
  128. File.WriteAllText(newFile, "new");
  129. string outZip = Path.Combine(_tempDir, "overwrite_files.zip");
  130. FileUtils.CreateZipFromFiles(outZip, oldFile);
  131. FileUtils.CreateZipFromFiles(outZip, newFile);
  132. using var archive = ZipFile.OpenRead(outZip);
  133. Assert.Single(archive.Entries);
  134. Assert.Equal("new.txt", archive.Entries[0].Name);
  135. }
  136. #endregion
  137. #region CreateZipFromFiles(IDictionary 版)测试
  138. public void CreateZipFromFiles_Dict_CustomEntryPath_EntryNameMatchesDictKey() {
  139. string srcFile = Path.Combine(_tempDir, "source.txt");
  140. File.WriteAllText(srcFile, "data");
  141. string outZip = Path.Combine(_tempDir, "from_dict.zip");
  142. var sources = new Dictionary<string, string> {
  143. ["custom/path/file.txt"] = srcFile
  144. };
  145. FileUtils.CreateZipFromFiles(outZip, sources);
  146. using var archive = ZipFile.OpenRead(outZip);
  147. Assert.Single(archive.Entries);
  148. Assert.Equal("custom/path/file.txt", archive.Entries[0].FullName);
  149. }
  150. public void CreateZipFromFiles_Dict_MultipleEntries_AllIncluded() {
  151. string f1 = Path.Combine(_tempDir, "d1.txt");
  152. string f2 = Path.Combine(_tempDir, "d2.txt");
  153. File.WriteAllText(f1, "1");
  154. File.WriteAllText(f2, "2");
  155. string outZip = Path.Combine(_tempDir, "from_dict_multi.zip");
  156. var sources = new Dictionary<string, string> {
  157. ["a/one.txt"] = f1,
  158. ["b/two.txt"] = f2
  159. };
  160. FileUtils.CreateZipFromFiles(outZip, sources);
  161. using var archive = ZipFile.OpenRead(outZip);
  162. Assert.Equal(2, archive.Entries.Count);
  163. var fullNames = archive.Entries.Select(e => e.FullName).ToHashSet();
  164. Assert.Contains("a/one.txt", fullNames);
  165. Assert.Contains("b/two.txt", fullNames);
  166. }
  167. public void CreateZipFromFiles_Dict_FileContentPreserved() {
  168. const string expectedContent = "preserved content";
  169. string srcFile = Path.Combine(_tempDir, "preserve.txt");
  170. File.WriteAllText(srcFile, expectedContent, Encoding.UTF8);
  171. string outZip = Path.Combine(_tempDir, "from_dict_content.zip");
  172. var sources = new Dictionary<string, string> { ["entry.txt"] = srcFile };
  173. FileUtils.CreateZipFromFiles(outZip, sources);
  174. string outDir = Path.Combine(_tempDir, "dict_content_out");
  175. Directory.CreateDirectory(outDir);
  176. using var archive = ZipFile.OpenRead(outZip);
  177. using var entryStream = archive.Entries[0].Open();
  178. using var reader = new StreamReader(entryStream, Encoding.UTF8);
  179. Assert.Equal(expectedContent, reader.ReadToEnd());
  180. }
  181. public void CreateZipFromFiles_Dict_BackslashInKey_NormalizedToForwardSlash() {
  182. string srcFile = Path.Combine(_tempDir, "bs.txt");
  183. File.WriteAllText(srcFile, "x");
  184. string outZip = Path.Combine(_tempDir, "backslash.zip");
  185. var sources = new Dictionary<string, string> {
  186. [@"dir\sub\file.txt"] = srcFile
  187. };
  188. FileUtils.CreateZipFromFiles(outZip, sources);
  189. using var archive = ZipFile.OpenRead(outZip);
  190. // 条目路径中的反斜杠应被规范化为正斜杠
  191. Assert.Equal("dir/sub/file.txt", archive.Entries[0].FullName);
  192. }
  193. #endregion
  194. */
  195. }