From 35d4ecca02bd5528343e80b2c01294bac07aee35 Mon Sep 17 00:00:00 2001 From: Jonathan Sider Date: Sun, 14 Feb 2021 15:42:33 -0600 Subject: [PATCH 1/3] Add File Tests / Minor Tweaks -Made File Pointer Dictionary Public for tests -Changed fungetc() -> ungetc() -Updated exceptions in 2 ordinals -fputc() -fgetc() -fputs() -fgets() -fseek(0 -ungetc() --- .../ExportedModules/Majorbbs/FileTestBase.cs | 82 +++++ .../ExportedModules/Majorbbs/file_Tests.cs | 328 +++++++++++++++++- .../ExportedModules/ExportedModuleBase.cs | 2 +- .../HostProcess/ExportedModules/Majorbbs.cs | 12 +- 4 files changed, 415 insertions(+), 9 deletions(-) diff --git a/MBBSEmu.Tests/ExportedModules/Majorbbs/FileTestBase.cs b/MBBSEmu.Tests/ExportedModules/Majorbbs/FileTestBase.cs index 40539908..7ed82c1c 100644 --- a/MBBSEmu.Tests/ExportedModules/Majorbbs/FileTestBase.cs +++ b/MBBSEmu.Tests/ExportedModules/Majorbbs/FileTestBase.cs @@ -20,6 +20,13 @@ public class FileTestBase : ExportedModuleTestBase, IDisposable protected const int CLOSE_ORDINAL = 110; protected const int FILELENGTH_ORDINAL = 211; + protected const int FPUTC_ORDINAL = 227; + protected const int FGETC_ORDINAL = 19; + protected const int UNGETC_ORDINAL = 615; + protected const int FSEEK_ORDINAL = 266; + protected const int FPUTS_ORDINAL = 1125; + protected const int FGETS_ORDINAL = 210; + protected FileTestBase() : base(Path.Join(Path.GetTempPath(), $"mbbsemu{RANDOM.Next()}")) { Directory.CreateDirectory(mbbsModule.ModulePath); @@ -154,6 +161,81 @@ protected int filelength(ushort fd) return mbbsEmuCpuRegisters.GetLong(); } + protected ushort fgetc(FarPtr srcPtr) + { + ExecuteApiTest(HostProcess.ExportedModules.Majorbbs.Segment, FGETC_ORDINAL, new List + { + srcPtr + }); + + return mbbsEmuCpuRegisters.AX; + } + + protected ushort ungetc(ushort ungetChar, FarPtr srcPtr) + { + ExecuteApiTest(HostProcess.ExportedModules.Majorbbs.Segment, UNGETC_ORDINAL, new List + { + ungetChar, + srcPtr.Offset, + srcPtr.Segment + }); + + return mbbsEmuCpuRegisters.AX; + } + + protected ushort fputc(ushort putChar, FarPtr srcPtr) + { + ExecuteApiTest(HostProcess.ExportedModules.Majorbbs.Segment, FPUTC_ORDINAL, new List + { + putChar, + srcPtr.Offset, + srcPtr.Segment + }); + + return mbbsEmuCpuRegisters.AX; + } + + protected FarPtr fgets(FarPtr putStringPtr, ushort numChars, FarPtr srcPtr) + { + ExecuteApiTest(HostProcess.ExportedModules.Majorbbs.Segment, FGETS_ORDINAL, new List + { + putStringPtr.Offset, + putStringPtr.Segment, + numChars, + srcPtr.Offset, + srcPtr.Segment + }); + + return mbbsEmuCpuRegisters.GetPointer(); + } + + protected ushort fputs(FarPtr putStringPtr, FarPtr srcPtr) + { + ExecuteApiTest(HostProcess.ExportedModules.Majorbbs.Segment, FPUTS_ORDINAL, new List + { + putStringPtr, + srcPtr + }); + + return mbbsEmuCpuRegisters.AX; + } + + protected ushort fseek(FarPtr srcPtr, int offset, ushort origin) + { + + + ExecuteApiTest(HostProcess.ExportedModules.Majorbbs.Segment, FSEEK_ORDINAL, new List + { + srcPtr.Offset, + srcPtr.Segment, + (ushort)offset, + (ushort)(offset >> 16), + origin + }); + + return mbbsEmuCpuRegisters.AX; + } + protected string CreateTextFile(string filename, string contents) { var filePath = Path.Join(mbbsModule.ModulePath, filename); diff --git a/MBBSEmu.Tests/ExportedModules/Majorbbs/file_Tests.cs b/MBBSEmu.Tests/ExportedModules/Majorbbs/file_Tests.cs index 712af160..94f9b3b4 100644 --- a/MBBSEmu.Tests/ExportedModules/Majorbbs/file_Tests.cs +++ b/MBBSEmu.Tests/ExportedModules/Majorbbs/file_Tests.cs @@ -1,3 +1,5 @@ +using MBBSEmu.Extensions; +using MBBSEmu.HostProcess.Structs; using MBBSEmu.Memory; using System; using System.IO; @@ -25,8 +27,6 @@ public void length_matches_constant() Assert.Equal(LOREM_IPSUM_LENGTH, LOREM_IPSUM.Length); } - // TODO(add fgetc/fputc fgets/fputs fseek tests) - [Theory] [InlineData("r")] [InlineData("r+")] @@ -300,5 +300,329 @@ public void fclose_invalidPointer() Assert.Equal(-1, fclose(FarPtr.Empty)); } + + [Theory] + [InlineData('A', 5)] + [InlineData('R', 10)] + [InlineData('!', 20)] + [InlineData('z', 25)] + [InlineData('l', 55)] + public void fputc_fgetc_fseek_origin0_file(byte inputChar, int fseekOffset) + { + //Reset State + Reset(); + + var filePath = CreateTextFile("file.txt", LOREM_IPSUM); + + Assert.Equal(LOREM_IPSUM.Length, new FileInfo(filePath).Length); + + var filep = fopen("FILE.TXT", "w"); + Assert.NotEqual(0, filep.Segment); + Assert.NotEqual(0, filep.Offset); + + Assert.Equal(0, fseek(filep, fseekOffset, 0)); + + Assert.Equal(1, fputc(inputChar, filep)); + + Assert.Equal(0,fseek(filep, fseekOffset, 0)); + + Assert.Equal(inputChar, fgetc(filep)); + + Assert.Equal(0, fclose(filep)); + } + + [Theory] + [InlineData(4, 'm', 'n')] + [InlineData(9, 'u', 'j')] + [InlineData(14, 'l', 'k')] + [InlineData(20, 't', 'r')] + [InlineData(22, 'a', '.')] + public void fgetc_ungetc_fseek_origin0_file(int fseekOffset, ushort getValue, ushort ungetValue) + { + //Reset State + Reset(); + + var filePath = CreateTextFile("file.txt", LOREM_IPSUM); + + Assert.Equal(LOREM_IPSUM.Length, new FileInfo(filePath).Length); + + var filep = fopen("FILE.TXT", "r"); + Assert.NotEqual(0, filep.Segment); + Assert.NotEqual(0, filep.Offset); + + Assert.Equal(0, fseek(filep, fseekOffset, 0)); + + Assert.Equal((byte)getValue, fgetc(filep)); + + Assert.Equal((byte)ungetValue, ungetc(ungetValue, filep)); + + var curFileStruct = new FileStruct(mbbsEmuMemoryCore.GetArray(filep, FileStruct.Size)); + var curFileStream = majorbbs.FilePointerDictionary[curFileStruct.curp.Offset]; + Assert.Equal(ungetValue, curFileStream.ReadByte()); + + Assert.Equal(0, fclose(filep)); + } + + [Theory] + [InlineData("Mary Had a Little Lamb\0", 0)] + [InlineData("---+++---111\0", 10)] + [InlineData("((((jumbo)))))\0", 20)] + [InlineData(" yeah \0", 25)] + [InlineData("\0", 40)] + [InlineData("A\0", 0)] + public void fputs_fgets_fseek_origin0_file(string stringPut, int fseekOffset) + { + //Reset State + Reset(); + + var filePath = CreateTextFile("file.txt", LOREM_IPSUM); + Assert.Equal(LOREM_IPSUM.Length, new FileInfo(filePath).Length); + + var stringPutPtr = mbbsEmuMemoryCore.AllocateVariable("STRING_PUT", (ushort)stringPut.Length); + mbbsEmuMemoryCore.SetArray(stringPutPtr, Encoding.ASCII.GetBytes(stringPut)); + + var stringGetPtr = mbbsEmuMemoryCore.AllocateVariable("STRING_GET", (ushort)stringPut.Length); + + var filep = fopen("FILE.TXT", "w"); + Assert.NotEqual(0, filep.Segment); + Assert.NotEqual(0, filep.Offset); + + Assert.Equal(0, fseek(filep, fseekOffset, 0)); + + Assert.Equal(1, fputs(stringPutPtr, filep)); + + Assert.Equal(0, fseek(filep, fseekOffset, 0)); + + Assert.Equal(stringGetPtr, fgets(stringGetPtr, (ushort)stringPut.Length, filep)); + Assert.Equal(stringPut, Encoding.ASCII.GetString(mbbsEmuMemoryCore.GetString("STRING_GET"))); + + Assert.Equal(0, fclose(filep)); + } + + [Theory] + [InlineData(4, 'm')] + [InlineData(10, 'm')] + [InlineData(20, 't')] + [InlineData(25, 't')] + [InlineData(55, ',')] + public void fgetc_fseek_origin1_file(int fseekOffset, byte expectedChar) + { + //Reset State + Reset(); + + var filePath = CreateTextFile("file.txt", LOREM_IPSUM); + + Assert.Equal(LOREM_IPSUM.Length, new FileInfo(filePath).Length); + + var filep = fopen("FILE.TXT", "r"); + Assert.NotEqual(0, filep.Segment); + Assert.NotEqual(0, filep.Offset); + + Assert.Equal(0, fseek(filep, 0, 0)); + + Assert.Equal(0, fseek(filep, fseekOffset, 1)); + + Assert.Equal(expectedChar, fgetc(filep)); + + Assert.Equal(0, fclose(filep)); + } + + [Theory] + [InlineData(-1, '.')] + [InlineData(-10, 'c')] + [InlineData(-20, 'u')] + [InlineData(-25, 't')] + [InlineData(-55, 'c')] + public void fgetc_fseek_origin2_file(int fseekOffset, byte expectedChar) + { + //Reset State + Reset(); + + var filePath = CreateTextFile("file.txt", LOREM_IPSUM); + + Assert.Equal(LOREM_IPSUM.Length, new FileInfo(filePath).Length); + + var filep = fopen("FILE.TXT", "r"); + Assert.NotEqual(0, filep.Segment); + Assert.NotEqual(0, filep.Offset); + + Assert.Equal(0, fseek(filep, fseekOffset, 2)); + + Assert.Equal(expectedChar, fgetc(filep)); + + Assert.Equal(0, fclose(filep)); + } + + [Fact] + public void fgetc_fseek_EOF_file() + { + //Reset State + Reset(); + + var filePath = CreateTextFile("file.txt", LOREM_IPSUM); + + Assert.Equal(LOREM_IPSUM.Length, new FileInfo(filePath).Length); + + var filep = fopen("FILE.TXT", "r"); + Assert.NotEqual(0, filep.Segment); + Assert.NotEqual(0, filep.Offset); + + Assert.Equal(0, fseek(filep, 0, 2)); + + Assert.Equal(0xFFFF, fgetc(filep)); + + var curFileStruct = new FileStruct(mbbsEmuMemoryCore.GetArray(filep, FileStruct.Size)); + var curFileStream = majorbbs.FilePointerDictionary[curFileStruct.curp.Offset]; + + Assert.Equal(LOREM_IPSUM_LENGTH, curFileStream.Position); + Assert.True(curFileStruct.flags.IsFlagSet((ushort)FileStruct.EnumFileFlags.EOF)); + + Assert.Equal(0, fclose(filep)); + } + + [Fact] + public void fputc_fseek_EOF_file() + { + //Reset State + Reset(); + + var filePath = CreateTextFile("file.txt", LOREM_IPSUM); + + Assert.Equal(LOREM_IPSUM.Length, new FileInfo(filePath).Length); + + var filep = fopen("FILE.TXT", "a"); + Assert.NotEqual(0, filep.Segment); + Assert.NotEqual(0, filep.Offset); + + Assert.Equal(0, fseek(filep, 0, 2)); + + Assert.Equal(1, fputc(0, filep)); + + var curFileStruct = new FileStruct(mbbsEmuMemoryCore.GetArray(filep, FileStruct.Size)); + var curFileStream = majorbbs.FilePointerDictionary[curFileStruct.curp.Offset]; + + Assert.Equal(LOREM_IPSUM_LENGTH + 1, curFileStream.Position); + Assert.True(curFileStruct.flags.IsFlagSet((ushort)FileStruct.EnumFileFlags.EOF)); + + Assert.Equal(0, fclose(filep)); + } + + [Fact] + public void fgets_fseek_EOF_file() + { + //Reset State + Reset(); + + var filePath = CreateTextFile("file.txt", LOREM_IPSUM); + + Assert.Equal(LOREM_IPSUM.Length, new FileInfo(filePath).Length); + + var filep = fopen("FILE.TXT", "r"); + Assert.NotEqual(0, filep.Segment); + Assert.NotEqual(0, filep.Offset); + + var stringGetPtr = mbbsEmuMemoryCore.AllocateVariable("STRING_GET", (ushort)"TEST".Length); + mbbsEmuMemoryCore.SetArray(stringGetPtr, Encoding.ASCII.GetBytes("TEST")); + + Assert.Equal(0, fseek(filep, 0, 2)); + + Assert.Equal(new FarPtr(), fgets(stringGetPtr, 4, filep)); + + var curFileStruct = new FileStruct(mbbsEmuMemoryCore.GetArray(filep, FileStruct.Size)); + var curFileStream = majorbbs.FilePointerDictionary[curFileStruct.curp.Offset]; + Assert.Equal(LOREM_IPSUM_LENGTH, curFileStream.Position ); + Assert.True(curFileStruct.flags.IsFlagSet((ushort)FileStruct.EnumFileFlags.EOF)); + + Assert.Equal(0, fclose(filep)); + } + + [Fact] + public void fputs_fseek_EOF_file() + { + //Reset State + Reset(); + + var filePath = CreateTextFile("file.txt", LOREM_IPSUM); + + Assert.Equal(LOREM_IPSUM.Length, new FileInfo(filePath).Length); + + var filep = fopen("FILE.TXT", "a"); + Assert.NotEqual(0, filep.Segment); + Assert.NotEqual(0, filep.Offset); + + var stringPutPtr = mbbsEmuMemoryCore.AllocateVariable("STRING_PUT", (ushort)"TEST".Length); + mbbsEmuMemoryCore.SetArray(stringPutPtr, Encoding.ASCII.GetBytes("TEST")); + + Assert.Equal(0, fseek(filep, 0, 2)); + + Assert.Equal(1, fputs(stringPutPtr, filep)); + + var curFileStruct = new FileStruct(mbbsEmuMemoryCore.GetArray(filep, FileStruct.Size)); + var curFileStream = majorbbs.FilePointerDictionary[curFileStruct.curp.Offset]; + Assert.Equal(LOREM_IPSUM_LENGTH + 5, curFileStream.Position); + Assert.True(curFileStruct.flags.IsFlagSet((ushort)FileStruct.EnumFileFlags.EOF)); + + Assert.Equal(0, fclose(filep)); + } + + [Fact] + public void fgetc_InvalidStream_Throw() + { + //Reset State + Reset(); + + //Pass empty pointer + Assert.Throws(() => fgetc(new FarPtr())); + } + + [Fact] + public void fputc_InvalidStream_Throw() + { + //Reset State + Reset(); + + //Pass empty pointer + Assert.Throws(() => fputc(0, new FarPtr())); + } + + [Fact] + public void fgets_InvalidStream_Throw() + { + //Reset State + Reset(); + + //Pass empty pointer + Assert.Throws(() => fgets(new FarPtr(), 0, new FarPtr())); + } + + [Fact] + public void fputs_InvalidStream_Throw() + { + //Reset State + Reset(); + + //Pass empty pointer + Assert.Throws(() => fputs(new FarPtr(), new FarPtr())); + } + + [Fact] + public void ungetc_InvalidStream_Throw() + { + //Reset State + Reset(); + + //Pass empty pointer + Assert.Throws(() => ungetc(0, new FarPtr())); + } + + [Fact] + public void fseek_InvalidStream_Throw() + { + //Reset State + Reset(); + + //Pass empty pointer + Assert.Throws(() => fseek(new FarPtr(), 0, 0)); + } } } diff --git a/MBBSEmu/HostProcess/ExportedModules/ExportedModuleBase.cs b/MBBSEmu/HostProcess/ExportedModules/ExportedModuleBase.cs index 44d39ea9..d895503f 100644 --- a/MBBSEmu/HostProcess/ExportedModules/ExportedModuleBase.cs +++ b/MBBSEmu/HostProcess/ExportedModules/ExportedModuleBase.cs @@ -58,7 +58,7 @@ public LeadingNumberFromStringResult() /// /// Pointers to files opened using FOPEN /// - private protected readonly PointerDictionary FilePointerDictionary; + public readonly PointerDictionary FilePointerDictionary; public readonly PointerDictionary McvPointerDictionary; private protected readonly ILogger _logger; diff --git a/MBBSEmu/HostProcess/ExportedModules/Majorbbs.cs b/MBBSEmu/HostProcess/ExportedModules/Majorbbs.cs index 43cdf220..bd7ca3e7 100644 --- a/MBBSEmu/HostProcess/ExportedModules/Majorbbs.cs +++ b/MBBSEmu/HostProcess/ExportedModules/Majorbbs.cs @@ -941,7 +941,7 @@ public ReadOnlySpan Invoke(ushort ordinal, bool offsetsOnly = false) galmalloc(); break; case 615: - fungetc(); + ungetc(); break; case 230: galfree(); @@ -3853,7 +3853,7 @@ private void fgets() var fileStruct = new FileStruct(Module.Memory.GetArray(fileStructPointer, FileStruct.Size)); if (!FilePointerDictionary.TryGetValue(fileStruct.curp.Offset, out var fileStream)) - throw new Exception($"Unable to locate FileStream for {fileStructPointer} (Stream: {fileStruct.curp})"); + throw new FileNotFoundException($"Unable to locate FileStream for {fileStructPointer} (Stream: {fileStruct.curp})"); if (fileStream.Position == fileStream.Length) { @@ -3954,13 +3954,13 @@ private void f_printf() private void fseek() { var fileStructPointer = GetParameterPointer(0); - var offset = GetParameterULong(2); + var offset = GetParameterLong(2); var origin = GetParameter(4); var fileStruct = new FileStruct(Module.Memory.GetArray(fileStructPointer, FileStruct.Size)); if (!FilePointerDictionary.TryGetValue(fileStruct.curp.Offset, out var fileStream)) - throw new Exception($"Unable to locate FileStream for {fileStructPointer} (Stream: {fileStruct.curp})"); + throw new FileNotFoundException($"Unable to locate FileStream for {fileStructPointer} (Stream: {fileStruct.curp})"); switch (origin) { @@ -5140,7 +5140,7 @@ private void galmalloc() /// /// Signature: int ungetc(int character,FILE *stream ) /// - private void fungetc() + private void ungetc() { var character = GetParameter(0); var fileStructPointer = GetParameterPointer(1); @@ -5155,7 +5155,7 @@ private void fungetc() fileStream.WriteByte((byte)character); fileStream.Position -= 1; - //Update EOF Flag if required + //Update EOF Flag if required -- TODO DO WE NEED? if (fileStream.Position == fileStream.Length) { fileStruct.flags |= (ushort)FileStruct.EnumFileFlags.EOF; From c01c818b6c95c665667a2df5dc8805a35c094324 Mon Sep 17 00:00:00 2001 From: Jonathan Sider Date: Tue, 16 Feb 2021 10:59:38 -0600 Subject: [PATCH 2/3] Combine fseek origin1/2 tests --- .../ExportedModules/Majorbbs/file_Tests.cs | 45 +++++-------------- 1 file changed, 12 insertions(+), 33 deletions(-) diff --git a/MBBSEmu.Tests/ExportedModules/Majorbbs/file_Tests.cs b/MBBSEmu.Tests/ExportedModules/Majorbbs/file_Tests.cs index 94f9b3b4..39d00223 100644 --- a/MBBSEmu.Tests/ExportedModules/Majorbbs/file_Tests.cs +++ b/MBBSEmu.Tests/ExportedModules/Majorbbs/file_Tests.cs @@ -400,12 +400,17 @@ public void fputs_fgets_fseek_origin0_file(string stringPut, int fseekOffset) } [Theory] - [InlineData(4, 'm')] - [InlineData(10, 'm')] - [InlineData(20, 't')] - [InlineData(25, 't')] - [InlineData(55, ',')] - public void fgetc_fseek_origin1_file(int fseekOffset, byte expectedChar) + [InlineData(4, 'm', 1)] + [InlineData(10, 'm', 1)] + [InlineData(20, 't', 1)] + [InlineData(25, 't', 1)] + [InlineData(55, ',', 1)] + [InlineData(-1, '.', 2)] + [InlineData(-10, 'c', 2)] + [InlineData(-20, 'u', 2)] + [InlineData(-25, 't', 2)] + [InlineData(-55, 'c', 2)] + public void fgetc_fseek_origin1_file(int fseekOffset, byte expectedChar, ushort originNum) { //Reset State Reset(); @@ -420,33 +425,7 @@ public void fgetc_fseek_origin1_file(int fseekOffset, byte expectedChar) Assert.Equal(0, fseek(filep, 0, 0)); - Assert.Equal(0, fseek(filep, fseekOffset, 1)); - - Assert.Equal(expectedChar, fgetc(filep)); - - Assert.Equal(0, fclose(filep)); - } - - [Theory] - [InlineData(-1, '.')] - [InlineData(-10, 'c')] - [InlineData(-20, 'u')] - [InlineData(-25, 't')] - [InlineData(-55, 'c')] - public void fgetc_fseek_origin2_file(int fseekOffset, byte expectedChar) - { - //Reset State - Reset(); - - var filePath = CreateTextFile("file.txt", LOREM_IPSUM); - - Assert.Equal(LOREM_IPSUM.Length, new FileInfo(filePath).Length); - - var filep = fopen("FILE.TXT", "r"); - Assert.NotEqual(0, filep.Segment); - Assert.NotEqual(0, filep.Offset); - - Assert.Equal(0, fseek(filep, fseekOffset, 2)); + Assert.Equal(0, fseek(filep, fseekOffset, originNum)); Assert.Equal(expectedChar, fgetc(filep)); From 4fe4be122d1648b30240f75c591bbf305b883dbd Mon Sep 17 00:00:00 2001 From: Jonathan Sider Date: Tue, 16 Feb 2021 11:05:19 -0600 Subject: [PATCH 3/3] changed test name --- MBBSEmu.Tests/ExportedModules/Majorbbs/file_Tests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MBBSEmu.Tests/ExportedModules/Majorbbs/file_Tests.cs b/MBBSEmu.Tests/ExportedModules/Majorbbs/file_Tests.cs index 39d00223..abeff02c 100644 --- a/MBBSEmu.Tests/ExportedModules/Majorbbs/file_Tests.cs +++ b/MBBSEmu.Tests/ExportedModules/Majorbbs/file_Tests.cs @@ -410,7 +410,7 @@ public void fputs_fgets_fseek_origin0_file(string stringPut, int fseekOffset) [InlineData(-20, 'u', 2)] [InlineData(-25, 't', 2)] [InlineData(-55, 'c', 2)] - public void fgetc_fseek_origin1_file(int fseekOffset, byte expectedChar, ushort originNum) + public void fgetc_fseek_origin1and2_file(int fseekOffset, byte expectedChar, ushort originNum) { //Reset State Reset();