IOTests.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426
  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 = fileSystem.Open(testFile, LuaFileMode.WriteText))
  77. {
  78. Assert.That(stream.ContentType, Is.EqualTo(LuaFileContentType.Text));
  79. await stream.WriteAsync(new(testContent), CancellationToken.None);
  80. }
  81. // Read text
  82. using (var stream = fileSystem.Open(testFile, LuaFileMode.ReadText))
  83. {
  84. Assert.That(stream.ContentType, Is.EqualTo(LuaFileContentType.Text));
  85. var content = await stream.ReadAllAsync(CancellationToken.None);
  86. Assert.That(content.Type, Is.EqualTo(LuaFileContentType.Text));
  87. Assert.That(content.ReadString(), Is.EqualTo(testContent));
  88. }
  89. }
  90. [Test]
  91. public async Task BinaryStream_Write_And_Read_Bytes()
  92. {
  93. var testFile = GetTestFilePath("binary_test.bin");
  94. var testBytes = new byte[] { 0x00, 0x01, 0x02, 0xFF, 0xFE, 0xFD };
  95. // Write bytes
  96. using (var stream = fileSystem.Open(testFile, LuaFileMode.WriteBinary))
  97. {
  98. Assert.That(stream.ContentType, Is.EqualTo(LuaFileContentType.Binary));
  99. await stream.WriteAsync(new(testBytes), CancellationToken.None);
  100. }
  101. // Read bytes
  102. using (var stream = fileSystem.Open(testFile, LuaFileMode.ReadBinary))
  103. {
  104. Assert.That(stream.ContentType, Is.EqualTo(LuaFileContentType.Binary));
  105. var content = await stream.ReadAllAsync(CancellationToken.None);
  106. Assert.That(content.Type, Is.EqualTo(LuaFileContentType.Binary));
  107. Assert.That(content.ReadBytes().ToArray(), Is.EqualTo(testBytes));
  108. }
  109. }
  110. [Test]
  111. public async Task TextStream_Cannot_Write_Binary_Content()
  112. {
  113. var testFile = GetTestFilePath("text_binary_mix.txt");
  114. using var stream = fileSystem.Open(testFile, LuaFileMode.WriteText);
  115. var binaryContent = new LuaFileContent(new byte[] { 0x00, 0x01 });
  116. Assert.ThrowsAsync<InvalidOperationException>(
  117. async () => await stream.WriteAsync(binaryContent, CancellationToken.None)
  118. );
  119. }
  120. [Test]
  121. public async Task BinaryStream_Cannot_Write_Text_Content()
  122. {
  123. var testFile = GetTestFilePath("binary_text_mix.bin");
  124. using var stream = fileSystem.Open(testFile, LuaFileMode.WriteBinary);
  125. var textContent = new LuaFileContent("Hello");
  126. Assert.ThrowsAsync<InvalidOperationException>(
  127. async () => await stream.WriteAsync(textContent, CancellationToken.None)
  128. );
  129. }
  130. [Test]
  131. public async Task TextStream_ReadLine_Works()
  132. {
  133. var testFile = GetTestFilePath("multiline.txt");
  134. var lines = new[] { "Line 1", "Line 2", "Line 3" };
  135. // Write multiple lines
  136. using (var stream = fileSystem.Open(testFile, LuaFileMode.WriteText))
  137. {
  138. await stream.WriteAsync(new(string.Join("\n", lines)), CancellationToken.None);
  139. }
  140. // Read lines one by one
  141. using (var stream = fileSystem.Open(testFile, LuaFileMode.ReadText))
  142. {
  143. for (int i = 0; i < lines.Length; i++)
  144. {
  145. var line = await stream.ReadLineAsync(CancellationToken.None);
  146. Assert.That(line, Is.EqualTo(lines[i]));
  147. }
  148. // EOF should return null
  149. var eofLine = await stream.ReadLineAsync(CancellationToken.None);
  150. Assert.That(eofLine, Is.Null);
  151. }
  152. }
  153. [Test]
  154. public async Task TextStream_ReadString_Works()
  155. {
  156. var testFile = GetTestFilePath("read_string.txt");
  157. var testContent = "Hello, World!";
  158. // Write content
  159. using (var stream = fileSystem.Open(testFile, LuaFileMode.WriteText))
  160. {
  161. await stream.WriteAsync(new(testContent), CancellationToken.None);
  162. }
  163. // Read partial strings
  164. using (var stream = fileSystem.Open(testFile, LuaFileMode.ReadText))
  165. {
  166. var part1 = await stream.ReadStringAsync(5, CancellationToken.None);
  167. Assert.That(part1, Is.EqualTo("Hello"));
  168. var part2 = await stream.ReadStringAsync(7, CancellationToken.None);
  169. Assert.That(part2, Is.EqualTo(", World"));
  170. var part3 = await stream.ReadStringAsync(1, CancellationToken.None);
  171. Assert.That(part3, Is.EqualTo("!")); // Only 1 char left
  172. var eof = await stream.ReadStringAsync(10, CancellationToken.None);
  173. Assert.That(eof, Is.Null);
  174. }
  175. }
  176. [Test]
  177. public async Task BinaryStream_Cannot_Use_Text_Operations()
  178. {
  179. var testFile = GetTestFilePath("binary_no_text.bin");
  180. using (var stream = fileSystem.Open(testFile, LuaFileMode.WriteBinary))
  181. {
  182. await stream.WriteAsync(new(new byte[] { 0x01, 0x02 }), CancellationToken.None);
  183. }
  184. using (var stream = fileSystem.Open(testFile, LuaFileMode.ReadBinary))
  185. {
  186. Assert.ThrowsAsync<InvalidOperationException>(
  187. async () => await stream.ReadLineAsync(CancellationToken.None)
  188. );
  189. Assert.ThrowsAsync<InvalidOperationException>(
  190. async () => await stream.ReadStringAsync(10, CancellationToken.None)
  191. );
  192. }
  193. }
  194. [Test]
  195. public async Task Append_Mode_Appends_Content()
  196. {
  197. var testFile = GetTestFilePath("append_test.txt");
  198. // Write initial content
  199. using (var stream = fileSystem.Open(testFile, LuaFileMode.WriteText))
  200. {
  201. await stream.WriteAsync(new("Hello"), CancellationToken.None);
  202. }
  203. // Append content
  204. using (var stream = fileSystem.Open(testFile, LuaFileMode.AppendText))
  205. {
  206. await stream.WriteAsync(new(" World"), CancellationToken.None);
  207. }
  208. // Read and verify
  209. using (var stream = fileSystem.Open(testFile, LuaFileMode.ReadText))
  210. {
  211. var content = await stream.ReadAllAsync(CancellationToken.None);
  212. Assert.That(content.ReadString(), Is.EqualTo("Hello World"));
  213. }
  214. }
  215. [Test]
  216. public async Task Seek_Works_Correctly()
  217. {
  218. var testFile = GetTestFilePath("seek_test.txt");
  219. var testContent = "0123456789";
  220. // Write content
  221. using (var stream = fileSystem.Open(testFile, LuaFileMode.WriteText))
  222. {
  223. await stream.WriteAsync(new(testContent), CancellationToken.None);
  224. }
  225. // Test seeking
  226. using (var stream = fileSystem.Open(testFile, LuaFileMode.ReadText))
  227. {
  228. // Seek from beginning
  229. stream.Seek(5, SeekOrigin.Begin);
  230. var afterBegin = await stream.ReadStringAsync(3, CancellationToken.None);
  231. Assert.That(afterBegin, Is.EqualTo("567"));
  232. // Seek from current
  233. stream.Seek(-2, SeekOrigin.Current);
  234. var afterCurrent = await stream.ReadStringAsync(2, CancellationToken.None);
  235. Assert.That(afterCurrent, Is.EqualTo("67"));
  236. // Seek from end
  237. stream.Seek(-3, SeekOrigin.End);
  238. var afterEnd = await stream.ReadStringAsync(3, CancellationToken.None);
  239. Assert.That(afterEnd, Is.EqualTo("789"));
  240. }
  241. }
  242. [Test]
  243. public void FileSystem_Rename_Works()
  244. {
  245. var oldPath = GetTestFilePath("old_name.txt");
  246. var newPath = GetTestFilePath("new_name.txt");
  247. File.WriteAllText(oldPath, "test content");
  248. fileSystem.Rename(oldPath, newPath);
  249. Assert.That(File.Exists(oldPath), Is.False);
  250. Assert.That(File.Exists(newPath), Is.True);
  251. Assert.That(File.ReadAllText(newPath), Is.EqualTo("test content"));
  252. }
  253. [Test]
  254. public void FileSystem_Remove_Works()
  255. {
  256. var testFile = GetTestFilePath("remove_test.txt");
  257. File.WriteAllText(testFile, "test content");
  258. Assert.That(File.Exists(testFile), Is.True);
  259. fileSystem.Remove(testFile);
  260. Assert.That(File.Exists(testFile), Is.False);
  261. }
  262. [Test]
  263. public void FileSystem_IsReadable_Works()
  264. {
  265. var existingFile = GetTestFilePath("readable.txt");
  266. var nonExistentFile = GetTestFilePath("non_existent.txt");
  267. File.WriteAllText(existingFile, "test");
  268. Assert.That(fileSystem.IsReadable(existingFile), Is.True);
  269. Assert.That(fileSystem.IsReadable(nonExistentFile), Is.False);
  270. }
  271. [Test]
  272. public async Task FileSystem_TempFile_Works()
  273. {
  274. string? tempPath = null;
  275. try
  276. {
  277. using (var tempStream = fileSystem.OpenTempFileStream())
  278. {
  279. await tempStream.WriteAsync(new("temp content"), CancellationToken.None);
  280. // Seek and read
  281. tempStream.Seek(0, SeekOrigin.Begin);
  282. var content = await tempStream.ReadAllAsync(CancellationToken.None);
  283. Assert.That(content.ReadString(), Is.EqualTo("temp content"));
  284. }
  285. }
  286. finally
  287. {
  288. if (tempPath != null && File.Exists(tempPath))
  289. {
  290. File.Delete(tempPath);
  291. }
  292. }
  293. }
  294. [Test]
  295. public void FileSystem_DirectorySeparator_IsValid()
  296. {
  297. var separator = fileSystem.DirectorySeparator;
  298. Assert.That(separator, Is.Not.Null);
  299. Assert.That(separator, Is.Not.Empty);
  300. Assert.That(separator, Is.EqualTo(Path.DirectorySeparatorChar.ToString()));
  301. }
  302. [Test]
  303. public async Task Buffering_Modes_Work()
  304. {
  305. var testFile = GetTestFilePath("buffer_test.txt");
  306. using (var stream = fileSystem.Open(testFile, LuaFileMode.WriteText))
  307. {
  308. // Set no buffering
  309. stream.SetVBuf(LuaFileBufferingMode.NoBuffering, 0);
  310. await stream.WriteAsync(new("No buffer"), CancellationToken.None);
  311. // Set line buffering
  312. stream.SetVBuf(LuaFileBufferingMode.LineBuffering, 1024);
  313. await stream.WriteAsync(new("\nLine buffer"), CancellationToken.None);
  314. // Set full buffering
  315. stream.SetVBuf(LuaFileBufferingMode.FullBuffering, 4096);
  316. await stream.WriteAsync(new("\nFull buffer"), CancellationToken.None);
  317. // Explicit flush
  318. await stream.FlushAsync(CancellationToken.None);
  319. }
  320. // Verify content was written
  321. var writtenContent = File.ReadAllText(testFile);
  322. Assert.That(writtenContent, Does.Contain("No buffer"));
  323. Assert.That(writtenContent, Does.Contain("Line buffer"));
  324. Assert.That(writtenContent, Does.Contain("Full buffer"));
  325. }
  326. [Test]
  327. public async Task LuaFileContent_Memory_Variations()
  328. {
  329. var testFile = GetTestFilePath("memory_test.txt");
  330. // Test with char array
  331. var charArray = "Hello from char array".ToCharArray();
  332. using (var stream = fileSystem.Open(testFile, LuaFileMode.WriteText))
  333. {
  334. await stream.WriteAsync(new(charArray), CancellationToken.None);
  335. }
  336. using (var stream = fileSystem.Open(testFile, LuaFileMode.ReadText))
  337. {
  338. var content = await stream.ReadAllAsync(CancellationToken.None);
  339. Assert.That(content.ReadString(), Is.EqualTo("Hello from char array"));
  340. }
  341. // Test with partial char array
  342. var longCharArray = "Hello World!!!".ToCharArray();
  343. using (var stream = fileSystem.Open(testFile, LuaFileMode.WriteText))
  344. {
  345. await stream.WriteAsync(new(longCharArray.AsMemory(0, 11)), CancellationToken.None); // Only "Hello World"
  346. }
  347. using (var stream = fileSystem.Open(testFile, LuaFileMode.ReadText))
  348. {
  349. var content = await stream.ReadAllAsync(CancellationToken.None);
  350. Assert.That(content.ReadString(), Is.EqualTo("Hello World"));
  351. }
  352. }
  353. }