//+------------------------------------------------------------------+ //| CBitBuffer_Test.mq5 | //| Copyright © 2025, Amr Ali | //| https://www.mql5.com/en/users/amrali | //+------------------------------------------------------------------+ #property copyright "Amr Ali" #property link "https://www.mql5.com/en/users/amrali" #property version "1.02" // Include the CBitBuffer class definition. // Assuming CBitBuffer.mqh is in the same directory or MQL5/Include. #include "CBitBuffer.mqh" // Define a simple structure for testing WriteStruct/ReadStruct. struct MyTestStruct { int id; double price; long volume; bool isActive; }; //+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { Print("--- CBitBuffer Capabilities Test ---"); // --- 1. Bit-Packing Operations --- TestBitPacking(); // --- 2. Basic Write and Read Operations --- TestBasicOperations(); // --- 3. Mixed Read/Write Operations --- TestMixedOperations(); // --- 4. Variable-Length Quantity (VLQ) Encoding Test --- TestVLQEncoding(); // --- 5. String and Struct Serialization Test --- TestStringAndStruct(); // --- 6. File Save and Load Test --- TestFileOperations(); // --- 7. Error Handling Test --- TestErrorHandling(); Print("--- CBitBuffer Test Complete ---"); } //+------------------------------------------------------------------+ //| Test Function: Bit-Packing Operations | //+------------------------------------------------------------------+ void TestBitPacking() { Print("\n--- Test: Bit-Packing Operations ---"); CBitBuffer buffer; // Write bits Print("Writing bits..."); buffer.WriteBit(true); // 1 buffer.WriteBit(false); // 0 buffer.WriteBit(true); // 1 // Writing a small integer (e.g., 5 bits for a value up to 31) buffer.WriteBits(27, 5); // 27 = 11011 (binary) // Writing a larger integer (e.g., 10 bits for a value up to 1023) buffer.WriteBits(789, 10); // 789 = 1100010101 (binary) // Writing an 8-bit value (byte) buffer.WriteBits(0xA5, 8); // 0xA5 = 10100101 (binary) PrintFormat("Total bits written: %d", buffer.GetTotalWrittenBits()); buffer.PrintHex(); // Debug print // Reset read position to start buffer.ResetReadPosition(); Print("Reading bits..."); // Read and verify PrintFormat("Read bit 1: %s (Expected: true)", buffer.ReadBit() ? "true" : "false"); PrintFormat("Read bit 2: %s (Expected: false)", buffer.ReadBit() ? "true" : "false"); PrintFormat("Read bit 3: %s (Expected: true)", buffer.ReadBit() ? "true" : "false"); PrintFormat("Read 5-bit integer: %d (Expected: 27)", (int)buffer.ReadBits(5)); PrintFormat("Read 10-bit integer: %d (Expected: 789)", (int)buffer.ReadBits(10)); PrintFormat("Read 8-bit value: 0x%X (Expected: 0xA5)", (int)buffer.ReadBits(8)); if (buffer.GetLastError() == BIT_BUFFER_NO_ERROR) { Print("Bit-Packing Operations test: PASSED"); } else { PrintFormat("Bit-Packing Operations test: FAILED! Error: %s", buffer.GetLastErrorString()); } } //+------------------------------------------------------------------+ //| Test Function: Basic Write and Read Operations | //+------------------------------------------------------------------+ void TestBasicOperations() { Print("\n--- Test: Basic Write and Read Operations ---"); CBitBuffer buffer; // Write various data types Print("Writing data..."); buffer.WriteBool(true); buffer.WriteBit(false); // Write a single bit buffer.WriteChar('A'); buffer.WriteUChar(255); buffer.WriteShort(-12345); buffer.WriteUShort(65530); buffer.WriteInt(123456789); buffer.WriteUInt(4294967295U); // Max uint buffer.WriteLong(9223372036854775807LL); buffer.WriteULong(18446744073709551615ULL); // Max ulong buffer.WriteFloat(3.14159f); buffer.WriteDouble(2.718281828459045); datetime now = TimeCurrent(); buffer.WriteDatetime(now); PrintFormat("Total bits written: %d", buffer.GetTotalWrittenBits()); buffer.PrintHex(); // Debug print // Reset read position to start buffer.ResetReadPosition(); Print("Reading data..."); // Read and verify PrintFormat("Read Bool: %s (Expected: true)", buffer.ReadBool() ? "true" : "false"); PrintFormat("Read Bit: %s (Expected: false)", buffer.ReadBit() ? "true" : "false"); PrintFormat("Read Char: %c (Expected: A)", buffer.ReadChar()); PrintFormat("Read UChar: %d (Expected: 255)", buffer.ReadUChar()); PrintFormat("Read Short: %d (Expected: -12345)", buffer.ReadShort()); PrintFormat("Read UShort: %d (Expected: 65530)", buffer.ReadUShort()); PrintFormat("Read Int: %d (Expected: 123456789)", buffer.ReadInt()); PrintFormat("Read UInt: %u (Expected: 4294967295)", buffer.ReadUInt()); PrintFormat("Read Long: %lli (Expected: 9223372036854775807)", buffer.ReadLong()); PrintFormat("Read ULong: %llu (Expected: 18446744073709551615)", buffer.ReadULong()); PrintFormat("Read Float: %.5f (Expected: 3.14159)", buffer.ReadFloat()); PrintFormat("Read Double: %.15f (Expected: 2.718281828459045)", buffer.ReadDouble()); PrintFormat("Read Datetime: %s (Expected: %s)", TimeToString(buffer.ReadDatetime()), TimeToString(now)); if (buffer.GetLastError() == BIT_BUFFER_NO_ERROR) { Print("Basic operations test: PASSED"); } else { PrintFormat("Basic operations test: FAILED! Error: %s", buffer.GetLastErrorString()); } } //+------------------------------------------------------------------+ //| Test Function: Mixed Read/Write Operations | //+------------------------------------------------------------------+ void TestMixedOperations() { Print("\n--- Test: Mixed Read/Write Operations ---"); CBitBuffer buffer; // Write some data buffer.WriteInt(100); buffer.WriteShort(200); PrintFormat("After initial writes: Total bits written: %d", buffer.GetTotalWrittenBits()); buffer.PrintHex(); // Read some data (this should trigger a flush) int val1 = buffer.ReadInt(); PrintFormat("Read Int: %d (Expected: 100)", val1); PrintFormat("Read position after first read: %d", buffer.GetReadPosition()); // Write more data (this should invalidate read cache) buffer.WriteLong(987654321L); buffer.WriteBool(true); PrintFormat("After mixed writes: Total bits written: %d", buffer.GetTotalWrittenBits()); buffer.PrintHex(); // Read remaining data short val2 = buffer.ReadShort(); long val3 = buffer.ReadLong(); bool val4 = buffer.ReadBool(); PrintFormat("Read Short: %d (Expected: 200)", val2); PrintFormat("Read Long: %lli (Expected: 987654321)", val3); PrintFormat("Read Bool: %s (Expected: true)", val4 ? "true" : "false"); if (buffer.GetLastError() == BIT_BUFFER_NO_ERROR) { Print("Mixed operations test: PASSED"); } else { PrintFormat("Mixed operations test: FAILED! Error: %s", buffer.GetLastErrorString()); } } //+------------------------------------------------------------------+ //| Test Function: Variable-Length Quantity (VLQ) Encoding Test | //+------------------------------------------------------------------+ void TestVLQEncoding() { Print("\n--- Test: Variable-Length Quantity (VLQ) Encoding ---"); CBitBuffer buffer; // Test values: small, medium, large, zero, negative int intValues[] = {0, 1, -1, 127, -128, 12345, -67890, 2147483647, -2147483648}; uint uintValues[] = {0, 1, 127, 12345, 4294967295U}; long longValues[] = {0L, 1L, -1L, 1234567890123L, -9876543210987L, 9223372036854775807L, -9223372036854775808L}; ulong ulongValues[] = {0ULL, 1ULL, 1234567890123ULL, 18446744073709551615ULL}; Print("Writing VLQ integers..."); for (int i = 0; i < ArraySize(intValues); i++) { buffer.WriteVarInt(intValues[i]); } for (int i = 0; i < ArraySize(uintValues); i++) { buffer.WriteVarUInt(uintValues[i]); } for (int i = 0; i < ArraySize(longValues); i++) { buffer.WriteVarLong(longValues[i]); } for (int i = 0; i < ArraySize(ulongValues); i++) { buffer.WriteVarULong(ulongValues[i]); } PrintFormat("Total bits written (VLQ): %d", buffer.GetTotalWrittenBits()); buffer.PrintHex(); buffer.ResetReadPosition(); Print("Reading VLQ integers..."); bool vlqPassed = true; for (int i = 0; i < ArraySize(intValues); i++) { int readVal = buffer.ReadVarInt(); if (readVal != intValues[i]) { PrintFormat("VLQ Int Mismatch: Expected %d, Got %d", intValues[i], readVal); vlqPassed = false; } } for (int i = 0; i < ArraySize(uintValues); i++) { uint readVal = buffer.ReadVarUInt(); if (readVal != uintValues[i]) { PrintFormat("VLQ UInt Mismatch: Expected %u, Got %u", uintValues[i], readVal); vlqPassed = false; } } for (int i = 0; i < ArraySize(longValues); i++) { long readVal = buffer.ReadVarLong(); if (readVal != longValues[i]) { PrintFormat("VLQ Long Mismatch: Expected %Ld, Got %Ld", longValues[i], readVal); vlqPassed = false; } } for (int i = 0; i < ArraySize(ulongValues); i++) { ulong readVal = buffer.ReadVarULong(); if (readVal != ulongValues[i]) { PrintFormat("VLQ ULong Mismatch: Expected %lu, Got %lu", ulongValues[i], readVal); vlqPassed = false; } } if (vlqPassed && buffer.GetLastError() == BIT_BUFFER_NO_ERROR) { Print("VLQ encoding test: PASSED"); } else { PrintFormat("VLQ encoding test: FAILED! Error: %s", buffer.GetLastErrorString()); } } //+------------------------------------------------------------------+ //| Test Function: String and Struct Serialization | //+------------------------------------------------------------------+ void TestStringAndStruct() { Print("\n--- Test: String and Struct Serialization ---"); CBitBuffer buffer; string testString1 = "Hello, MQL5 BitBuffer!"; string testString2 = "Another string with ümlauts and special chars: éàç"; string testString3 = ""; // Empty string string testString4 = NULL; // Null string MyTestStruct myStruct1; myStruct1.id = 123; myStruct1.price = 123.456; myStruct1.volume = 14432; myStruct1.isActive = true; MyTestStruct myStruct2; myStruct2.id = 789; myStruct2.price = 987.654; myStruct2.volume = 747; myStruct2.isActive = false; Print("Writing strings and structs..."); buffer.WriteString(testString1); buffer.WriteString(testString2); buffer.WriteString(testString3); buffer.WriteString(testString4); buffer.WriteStruct(myStruct1); buffer.WriteStruct(myStruct2); PrintFormat("Total bits written (Strings/Structs): %d", buffer.GetTotalWrittenBits()); buffer.PrintHex(); buffer.ResetReadPosition(); Print("Reading strings and structs..."); string readString1 = buffer.ReadString(); string readString2 = buffer.ReadString(); string readString3 = buffer.ReadString(); string readString4 = buffer.ReadString(); MyTestStruct readStruct1 = buffer.ReadStruct(); MyTestStruct readStruct2 = buffer.ReadStruct(); bool structStringPassed = true; if (readString1 != testString1) { PrintFormat("String1 Mismatch: '%s' != '%s'", readString1, testString1); structStringPassed = false; } if (readString2 != testString2) { PrintFormat("String2 Mismatch: '%s' != '%s'", readString2, testString2); structStringPassed = false; } if (readString3 != testString3) { PrintFormat("String3 Mismatch: '%s' != '%s'", readString3, testString3); structStringPassed = false; } if (readString4 != testString4) { PrintFormat("String4 Mismatch: '%s' != '%s'", readString4, testString4); structStringPassed = false; } if (readStruct1.id != myStruct1.id || readStruct1.price != myStruct1.price || readStruct1.volume != myStruct1.volume || readStruct1.isActive != myStruct1.isActive) { Print("Struct1 Mismatch!"); PrintFormat(" Expected: ID=%d, Price=%.3f, volume='%lli', Active=%s", myStruct1.id, myStruct1.price, myStruct1.volume, myStruct1.isActive ? "true" : "false"); PrintFormat(" Got: ID=%d, Price=%.3f, volume='%lli', Active=%s", readStruct1.id, readStruct1.price, readStruct1.volume, readStruct1.isActive ? "true" : "false"); structStringPassed = false; } if (readStruct2.id != myStruct2.id || readStruct2.price != myStruct2.price || readStruct2.volume != myStruct2.volume || readStruct2.isActive != myStruct2.isActive) { Print("Struct2 Mismatch!"); PrintFormat(" Expected: ID=%d, Price=%.3f, volume='%lli', Active=%s", myStruct2.id, myStruct2.price, myStruct2.volume, myStruct2.isActive ? "true" : "false"); PrintFormat(" Got: ID=%d, Price=%.3f, volume='%lli', Active=%s", readStruct2.id, readStruct2.price, readStruct2.volume, readStruct2.isActive ? "true" : "false"); structStringPassed = false; } if (structStringPassed && buffer.GetLastError() == BIT_BUFFER_NO_ERROR) { Print("String and Struct serialization test: PASSED"); } else { PrintFormat("String and Struct serialization test: FAILED! Error: %s", buffer.GetLastErrorString()); } } //+------------------------------------------------------------------+ //| Test Function: File Save and Load Operations | //+------------------------------------------------------------------+ void TestFileOperations() { Print("\n--- Test: File Save and Load Operations ---"); CBitBuffer bufferWrite; CBitBuffer bufferRead; string filename = "bitbuffer_test_data.bin"; // File in MQL5/Files/ // Write some data to bufferWrite bufferWrite.WriteInt(12345); bufferWrite.WriteString("Data for file storage."); bufferWrite.WriteDouble(987.654321); bufferWrite.WriteBool(true); bufferWrite.WriteVarInt(-500); PrintFormat("Buffer to save (bits): %d", bufferWrite.GetTotalWrittenBits()); bufferWrite.PrintHex(); // Save to file if (bufferWrite.Save(filename)) { PrintFormat("Buffer successfully saved to '%s'", filename); } else { PrintFormat("Failed to save buffer to file! Error: %s", bufferWrite.GetLastErrorString()); return; } // Load from file into bufferRead if (bufferRead.Load(filename)) { PrintFormat("Buffer successfully loaded from '%s'", filename); PrintFormat("Loaded buffer (bits): %d", bufferRead.GetTotalWrittenBits()); bufferRead.PrintHex(); // Verify loaded data int readInt = bufferRead.ReadInt(); string readString = bufferRead.ReadString(); double readDouble = bufferRead.ReadDouble(); bool readBool = bufferRead.ReadBool(); int readVarInt = bufferRead.ReadVarInt(); bool fileTestPassed = true; if (readInt != 12345) { PrintFormat("File Load Mismatch: Int. Expected 12345, Got %d", readInt); fileTestPassed = false; } if (readString != "Data for file storage.") { PrintFormat("File Load Mismatch: String. Expected 'Data for file storage.', Got '%s'", readString); fileTestPassed = false; } if (readDouble != 987.654321) { PrintFormat("File Load Mismatch: Double. Expected 987.654321, Got %.6f", readDouble); fileTestPassed = false; } if (readBool != true) { PrintFormat("File Load Mismatch: Bool. Expected true, Got %s", readBool ? "true" : "false"); fileTestPassed = false; } if (readVarInt != -500) { PrintFormat("File Load Mismatch: VarInt. Expected -500, Got %d", readVarInt); fileTestPassed = false; } if (fileTestPassed && bufferRead.GetLastError() == BIT_BUFFER_NO_ERROR) { Print("File operations test: PASSED"); } else { PrintFormat("File operations test: FAILED! Error: %s", bufferRead.GetLastErrorString()); } } else { PrintFormat("Failed to load buffer from file! Error: %s", bufferRead.GetLastErrorString()); return; } // Clean up: Delete the test file if (FileDelete(filename)) { PrintFormat("Test file '%s' deleted successfully.", filename); } else { PrintFormat("Failed to delete test file '%s'. MQL5 Error: %d", filename, ::GetLastError()); } } //+------------------------------------------------------------------+ //| Test Function: Error Handling | //+------------------------------------------------------------------+ void TestErrorHandling() { Print("\n--- Test: Error Handling ---"); CBitBuffer buffer; bool errorHandled = true; // Test 1: Read out of bounds Print("Attempting to read out of bounds..."); buffer.Clear(); buffer.WriteInt(10); // Write 32 bits buffer.ResetReadPosition(); buffer.ReadLong(); // Try to read 64 bits if (buffer.GetLastError() == BIT_BUFFER_READ_OUT_OF_BOUNDS) { PrintFormat(" Caught expected error: %s", buffer.GetLastErrorString()); } else { PrintFormat(" Failed to catch expected READ_OUT_OF_BOUNDS error. Got: %s", buffer.GetLastErrorString()); errorHandled = false; } buffer.ClearLastError(); // Test 2: Invalid bit count for WriteBits Print("Attempting to write with invalid bit count..."); buffer.WriteBits(1, 0); // 0 bits if (buffer.GetLastError() == BIT_BUFFER_INVALID_BIT_COUNT) { PrintFormat(" Caught expected error: %s", buffer.GetLastErrorString()); } else { PrintFormat(" Failed to catch expected INVALID_BIT_COUNT error. Got: %s", buffer.GetLastErrorString()); errorHandled = false; } buffer.ClearLastError(); // Test 3: Set read position out of bounds Print("Attempting to set read position out of bounds..."); buffer.Clear(); buffer.WriteInt(10); buffer.SetReadPosition(100); // Beyond total written bits if (buffer.GetLastError() == BIT_BUFFER_INVALID_READ_POSITION) { PrintFormat(" Caught expected error: %s", buffer.GetLastErrorString()); } else { PrintFormat(" Failed to catch expected INVALID_READ_POSITION error. Got: %s", buffer.GetLastErrorString()); errorHandled = false; } buffer.ClearLastError(); // Test 4: String too large Print("Attempting to write string too large for USHORT_MAX..."); string veryLongString = ""; for (int i = 0; i < 70000; i++) // Create a string longer than USHORT_MAX (65535) { veryLongString += "a"; } buffer.WriteString(veryLongString); if (buffer.GetLastError() == BIT_BUFFER_DATA_TOO_LARGE) { PrintFormat(" Caught expected error: %s", buffer.GetLastErrorString()); } else { PrintFormat(" Failed to catch expected DATA_TOO_LARGE error. Got: %s", buffer.GetLastErrorString()); errorHandled = false; } buffer.ClearLastError(); if (errorHandled) { Print("Error handling test: PASSED"); } else { Print("Error handling test: FAILED!"); } }