IOTests.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422
  1. using Lua.IO;
  2. using System.Text;
  3. using NUnit.Framework;
  4. namespace Lua.Tests;
  5. public class IOTests : IDisposable
  6. {
  7. private readonly string testDirectory;
  8. private readonly FileSystem fileSystem;
  9. public IOTests()
  10. {
  11. testDirectory = Path.Combine(Path.GetTempPath(), $"LuaIOTests_{Guid.NewGuid()}");
  12. Directory.CreateDirectory(testDirectory);
  13. fileSystem = new();
  14. }
  15. public void Dispose()
  16. {
  17. if (Directory.Exists(testDirectory))
  18. {
  19. Directory.Delete(testDirectory, true);
  20. }
  21. }
  22. private string GetTestFilePath(string filename)
  23. {
  24. return Path.Combine(testDirectory, filename);
  25. }
  26. [Test]
  27. public void FileOpenFlags_ParseModeString_Parses_Correctly()
  28. {
  29. // Text modes
  30. Assert.That(LuaFileModeExtensions.ParseModeString("r"), Is.EqualTo(LuaFileMode.ReadText));
  31. Assert.That(LuaFileModeExtensions.ParseModeString("w"), Is.EqualTo(LuaFileMode.WriteText));
  32. Assert.That(LuaFileModeExtensions.ParseModeString("a"), Is.EqualTo(LuaFileMode.AppendText));
  33. // Binary modes
  34. Assert.That(LuaFileModeExtensions.ParseModeString("rb"), Is.EqualTo(LuaFileMode.ReadBinary));
  35. Assert.That(LuaFileModeExtensions.ParseModeString("wb"), Is.EqualTo(LuaFileMode.WriteBinary));
  36. Assert.That(LuaFileModeExtensions.ParseModeString("ab"), Is.EqualTo(LuaFileMode.AppendBinary));
  37. // Update modes
  38. Assert.That(LuaFileModeExtensions.ParseModeString("r+"), Is.EqualTo(LuaFileMode.ReadUpdateText));
  39. Assert.That(LuaFileModeExtensions.ParseModeString("w+"), Is.EqualTo(LuaFileMode.WriteUpdateText));
  40. Assert.That(LuaFileModeExtensions.ParseModeString("a+"), Is.EqualTo(LuaFileMode.AppendUpdateText));
  41. // Binary update modes
  42. Assert.That(LuaFileModeExtensions.ParseModeString("r+b"), Is.EqualTo(LuaFileMode.ReadUpdateBinary));
  43. Assert.That(LuaFileModeExtensions.ParseModeString("rb+"), Is.EqualTo(LuaFileMode.ReadUpdateBinary));
  44. Assert.That(LuaFileModeExtensions.ParseModeString("w+b"), Is.EqualTo(LuaFileMode.WriteUpdateBinary));
  45. Assert.That(LuaFileModeExtensions.ParseModeString("wb+"), Is.EqualTo(LuaFileMode.WriteUpdateBinary));
  46. // Mixed order modes
  47. Assert.That(LuaFileModeExtensions.ParseModeString("br"), Is.EqualTo(LuaFileMode.ReadBinary));
  48. Assert.That(LuaFileModeExtensions.ParseModeString("rb"), Is.EqualTo(LuaFileMode.ReadBinary));
  49. Assert.That(LuaFileModeExtensions.ParseModeString("tr"), Is.EqualTo(LuaFileMode.ReadText));
  50. Assert.That(LuaFileModeExtensions.ParseModeString("rt"), Is.EqualTo(LuaFileMode.ReadText));
  51. }
  52. [Test]
  53. public void FileOpenFlags_GetOpenMode_Returns_Correct_Mode()
  54. {
  55. Assert.That(LuaFileMode.Read.GetOpenMode(), Is.EqualTo(LuaFileOpenMode.Read));
  56. Assert.That(LuaFileMode.Write.GetOpenMode(), Is.EqualTo(LuaFileOpenMode.Write));
  57. Assert.That(LuaFileMode.Append.GetOpenMode(), Is.EqualTo(LuaFileOpenMode.Append));
  58. Assert.That(LuaFileMode.ReadUpdate.GetOpenMode(), Is.EqualTo(LuaFileOpenMode.ReadWriteOpen));
  59. Assert.That(LuaFileMode.WriteUpdate.GetOpenMode(), Is.EqualTo(LuaFileOpenMode.ReadWriteCreate));
  60. Assert.That(LuaFileMode.AppendUpdate.GetOpenMode(), Is.EqualTo(LuaFileOpenMode.ReadAppend));
  61. }
  62. [Test]
  63. public void FileOpenFlags_GetContentType_Returns_Correct_Type()
  64. {
  65. Assert.That(LuaFileMode.Read.GetContentType(), Is.EqualTo(LuaFileContentType.Text));
  66. Assert.That(LuaFileMode.ReadText.GetContentType(), Is.EqualTo(LuaFileContentType.Text));
  67. Assert.That(LuaFileMode.ReadBinary.GetContentType(), Is.EqualTo(LuaFileContentType.Binary));
  68. Assert.That(LuaFileMode.WriteBinary.GetContentType(), Is.EqualTo(LuaFileContentType.Binary));
  69. }
  70. [Test]
  71. public async Task TextStream_Write_And_Read_Text()
  72. {
  73. var testFile = GetTestFilePath("text_test.txt");
  74. var testContent = "Hello, World!\nThis is a test.";
  75. // Write text
  76. using (var stream = await fileSystem.Open(testFile, LuaFileMode.WriteText,CancellationToken.None))
  77. {
  78. await stream.WriteAsync(new(testContent), CancellationToken.None);
  79. }
  80. // Read text
  81. using (var stream = await fileSystem.Open(testFile, LuaFileMode.ReadText,CancellationToken.None))
  82. {
  83. var content = await stream.ReadAllAsync(CancellationToken.None);
  84. Assert.That(content.Type, Is.EqualTo(LuaFileContentType.Text));
  85. Assert.That(content.ReadString(), Is.EqualTo(testContent));
  86. }
  87. }
  88. [Test]
  89. public async Task BinaryStream_Write_And_Read_Bytes()
  90. {
  91. var testFile = GetTestFilePath("binary_test.bin");
  92. var testBytes = new byte[] { 0x00, 0x01, 0x02, 0xFF, 0xFE, 0xFD };
  93. // Write bytes
  94. using (var stream = await fileSystem.Open(testFile, LuaFileMode.WriteBinary,CancellationToken.None))
  95. {
  96. await stream.WriteAsync(new(testBytes), CancellationToken.None);
  97. }
  98. // Read bytes
  99. using (var stream = await fileSystem.Open(testFile, LuaFileMode.ReadBinary,CancellationToken.None))
  100. {
  101. var content = await stream.ReadAllAsync(CancellationToken.None);
  102. Assert.That(content.Type, Is.EqualTo(LuaFileContentType.Binary));
  103. Assert.That(content.ReadBytes().ToArray(), Is.EqualTo(testBytes));
  104. }
  105. }
  106. [Test]
  107. public async Task TextStream_Cannot_Write_Binary_Content()
  108. {
  109. var testFile = GetTestFilePath("text_binary_mix.txt");
  110. using var stream = await fileSystem.Open(testFile, LuaFileMode.WriteText,CancellationToken.None);
  111. var binaryContent = new LuaFileContent(new byte[] { 0x00, 0x01 });
  112. Assert.ThrowsAsync<InvalidOperationException>(
  113. async () => await stream.WriteAsync(binaryContent, CancellationToken.None)
  114. );
  115. }
  116. [Test]
  117. public async Task BinaryStream_Cannot_Write_Text_Content()
  118. {
  119. var testFile = GetTestFilePath("binary_text_mix.bin");
  120. using var stream = await fileSystem.Open(testFile, LuaFileMode.WriteBinary,CancellationToken.None);
  121. var textContent = new LuaFileContent("Hello");
  122. Assert.ThrowsAsync<InvalidOperationException>(
  123. async () => await stream.WriteAsync(textContent, CancellationToken.None)
  124. );
  125. }
  126. [Test]
  127. public async Task TextStream_ReadLine_Works()
  128. {
  129. var testFile = GetTestFilePath("multiline.txt");
  130. var lines = new[] { "Line 1", "Line 2", "Line 3" };
  131. // Write multiple lines
  132. using (var stream = await fileSystem.Open(testFile, LuaFileMode.WriteText,CancellationToken.None))
  133. {
  134. await stream.WriteAsync(new(string.Join("\n", lines)), CancellationToken.None);
  135. }
  136. // Read lines one by one
  137. using (var stream = await fileSystem.Open(testFile, LuaFileMode.ReadText,CancellationToken.None))
  138. {
  139. for (int i = 0; i < lines.Length; i++)
  140. {
  141. var line = await stream.ReadLineAsync(CancellationToken.None);
  142. Assert.That(line, Is.EqualTo(lines[i]));
  143. }
  144. // EOF should return null
  145. var eofLine = await stream.ReadLineAsync(CancellationToken.None);
  146. Assert.That(eofLine, Is.Null);
  147. }
  148. }
  149. [Test]
  150. public async Task TextStream_ReadString_Works()
  151. {
  152. var testFile = GetTestFilePath("read_string.txt");
  153. var testContent = "Hello, World!";
  154. // Write content
  155. using (var stream = await fileSystem.Open(testFile, LuaFileMode.WriteText,CancellationToken.None))
  156. {
  157. await stream.WriteAsync(new(testContent), CancellationToken.None);
  158. }
  159. // Read partial strings
  160. using (var stream = await fileSystem.Open(testFile, LuaFileMode.ReadText,CancellationToken.None))
  161. {
  162. var part1 = await stream.ReadStringAsync(5, CancellationToken.None);
  163. Assert.That(part1, Is.EqualTo("Hello"));
  164. var part2 = await stream.ReadStringAsync(7, CancellationToken.None);
  165. Assert.That(part2, Is.EqualTo(", World"));
  166. var part3 = await stream.ReadStringAsync(1, CancellationToken.None);
  167. Assert.That(part3, Is.EqualTo("!")); // Only 1 char left
  168. var eof = await stream.ReadStringAsync(10, CancellationToken.None);
  169. Assert.That(eof, Is.Null);
  170. }
  171. }
  172. [Test]
  173. public async Task BinaryStream_Cannot_Use_Text_Operations()
  174. {
  175. var testFile = GetTestFilePath("binary_no_text.bin");
  176. using (var stream = await fileSystem.Open(testFile, LuaFileMode.WriteBinary,CancellationToken.None))
  177. {
  178. await stream.WriteAsync(new(new byte[] { 0x01, 0x02 }), CancellationToken.None);
  179. }
  180. using (var stream = await fileSystem.Open(testFile, LuaFileMode.ReadBinary,CancellationToken.None))
  181. {
  182. Assert.ThrowsAsync<InvalidOperationException>(
  183. async () => await stream.ReadLineAsync(CancellationToken.None)
  184. );
  185. Assert.ThrowsAsync<InvalidOperationException>(
  186. async () => await stream.ReadStringAsync(10, CancellationToken.None)
  187. );
  188. }
  189. }
  190. [Test]
  191. public async Task Append_Mode_Appends_Content()
  192. {
  193. var testFile = GetTestFilePath("append_test.txt");
  194. // Write initial content
  195. using (var stream = await fileSystem.Open(testFile, LuaFileMode.WriteText,CancellationToken.None))
  196. {
  197. await stream.WriteAsync(new("Hello"), CancellationToken.None);
  198. }
  199. // Append content
  200. using (var stream = await fileSystem.Open(testFile, LuaFileMode.AppendText,CancellationToken.None))
  201. {
  202. await stream.WriteAsync(new(" World"), CancellationToken.None);
  203. }
  204. // Read and verify
  205. using (var stream = await fileSystem.Open(testFile, LuaFileMode.ReadText,CancellationToken.None))
  206. {
  207. var content = await stream.ReadAllAsync(CancellationToken.None);
  208. Assert.That(content.ReadString(), Is.EqualTo("Hello World"));
  209. }
  210. }
  211. [Test]
  212. public async Task Seek_Works_Correctly()
  213. {
  214. var testFile = GetTestFilePath("seek_test.txt");
  215. var testContent = "0123456789";
  216. // Write content
  217. using (var stream = await fileSystem.Open(testFile, LuaFileMode.WriteText,CancellationToken.None))
  218. {
  219. await stream.WriteAsync(new(testContent), CancellationToken.None);
  220. }
  221. // Test seeking
  222. using (var stream = await fileSystem.Open(testFile, LuaFileMode.ReadText,CancellationToken.None))
  223. {
  224. // Seek from beginning
  225. stream.Seek(5, SeekOrigin.Begin);
  226. var afterBegin = await stream.ReadStringAsync(3, CancellationToken.None);
  227. Assert.That(afterBegin, Is.EqualTo("567"));
  228. // Seek from current
  229. stream.Seek(-2, SeekOrigin.Current);
  230. var afterCurrent = await stream.ReadStringAsync(2, CancellationToken.None);
  231. Assert.That(afterCurrent, Is.EqualTo("67"));
  232. // Seek from end
  233. stream.Seek(-3, SeekOrigin.End);
  234. var afterEnd = await stream.ReadStringAsync(3, CancellationToken.None);
  235. Assert.That(afterEnd, Is.EqualTo("789"));
  236. }
  237. }
  238. [Test]
  239. public async Task FileSystem_Rename_Works()
  240. {
  241. var oldPath = GetTestFilePath("old_name.txt");
  242. var newPath = GetTestFilePath("new_name.txt");
  243. File.WriteAllText(oldPath, "test content");
  244. await fileSystem.Rename(oldPath, newPath,CancellationToken.None);
  245. Assert.That(File.Exists(oldPath), Is.False);
  246. Assert.That(File.Exists(newPath), Is.True);
  247. Assert.That(File.ReadAllText(newPath), Is.EqualTo("test content"));
  248. }
  249. [Test]
  250. public async Task FileSystem_Remove_Works()
  251. {
  252. var testFile = GetTestFilePath("remove_test.txt");
  253. File.WriteAllText(testFile, "test content");
  254. Assert.That(File.Exists(testFile), Is.True);
  255. await fileSystem.Remove(testFile,CancellationToken.None);
  256. Assert.That(File.Exists(testFile), Is.False);
  257. }
  258. [Test]
  259. public void FileSystem_IsReadable_Works()
  260. {
  261. var existingFile = GetTestFilePath("readable.txt");
  262. var nonExistentFile = GetTestFilePath("non_existent.txt");
  263. File.WriteAllText(existingFile, "test");
  264. Assert.That(fileSystem.IsReadable(existingFile), Is.True);
  265. Assert.That(fileSystem.IsReadable(nonExistentFile), Is.False);
  266. }
  267. [Test]
  268. public async Task FileSystem_TempFile_Works()
  269. {
  270. string? tempPath = null;
  271. try
  272. {
  273. using (var tempStream = await fileSystem.OpenTempFileStream(CancellationToken.None))
  274. {
  275. await tempStream.WriteAsync(new("temp content"), CancellationToken.None);
  276. // Seek and read
  277. tempStream.Seek(0, SeekOrigin.Begin);
  278. var content = await tempStream.ReadAllAsync(CancellationToken.None);
  279. Assert.That(content.ReadString(), Is.EqualTo("temp content"));
  280. }
  281. }
  282. finally
  283. {
  284. if (tempPath != null && File.Exists(tempPath))
  285. {
  286. File.Delete(tempPath);
  287. }
  288. }
  289. }
  290. [Test]
  291. public void FileSystem_DirectorySeparator_IsValid()
  292. {
  293. var separator = fileSystem.DirectorySeparator;
  294. Assert.That(separator, Is.Not.Null);
  295. Assert.That(separator, Is.Not.Empty);
  296. Assert.That(separator, Is.EqualTo(Path.DirectorySeparatorChar.ToString()));
  297. }
  298. [Test]
  299. public async Task Buffering_Modes_Work()
  300. {
  301. var testFile = GetTestFilePath("buffer_test.txt");
  302. using (var stream = await fileSystem.Open(testFile, LuaFileMode.WriteText,CancellationToken.None))
  303. {
  304. // Set no buffering
  305. stream.SetVBuf(LuaFileBufferingMode.NoBuffering, 0);
  306. await stream.WriteAsync(new("No buffer"), CancellationToken.None);
  307. // Set line buffering
  308. stream.SetVBuf(LuaFileBufferingMode.LineBuffering, 1024);
  309. await stream.WriteAsync(new("\nLine buffer"), CancellationToken.None);
  310. // Set full buffering
  311. stream.SetVBuf(LuaFileBufferingMode.FullBuffering, 4096);
  312. await stream.WriteAsync(new("\nFull buffer"), CancellationToken.None);
  313. // Explicit flush
  314. await stream.FlushAsync(CancellationToken.None);
  315. }
  316. // Verify content was written
  317. var writtenContent = File.ReadAllText(testFile);
  318. Assert.That(writtenContent, Does.Contain("No buffer"));
  319. Assert.That(writtenContent, Does.Contain("Line buffer"));
  320. Assert.That(writtenContent, Does.Contain("Full buffer"));
  321. }
  322. [Test]
  323. public async Task LuaFileContent_Memory_Variations()
  324. {
  325. var testFile = GetTestFilePath("memory_test.txt");
  326. // Test with char array
  327. var charArray = "Hello from char array".ToCharArray();
  328. using (var stream = await fileSystem.Open(testFile, LuaFileMode.WriteText, CancellationToken.None))
  329. {
  330. await stream.WriteAsync(new(charArray), CancellationToken.None);
  331. }
  332. using (var stream = await fileSystem.Open(testFile, LuaFileMode.ReadText,CancellationToken.None))
  333. {
  334. var content = await stream.ReadAllAsync(CancellationToken.None);
  335. Assert.That(content.ReadString(), Is.EqualTo("Hello from char array"));
  336. }
  337. // Test with partial char array
  338. var longCharArray = "Hello World!!!".ToCharArray();
  339. using (var stream = await fileSystem.Open(testFile, LuaFileMode.WriteText,CancellationToken.None))
  340. {
  341. await stream.WriteAsync(new(longCharArray.AsMemory(0, 11)), CancellationToken.None); // Only "Hello World"
  342. }
  343. using (var stream = await fileSystem.Open(testFile, LuaFileMode.ReadText,CancellationToken.None))
  344. {
  345. var content = await stream.ReadAllAsync(CancellationToken.None);
  346. Assert.That(content.ReadString(), Is.EqualTo("Hello World"));
  347. }
  348. }
  349. }