CBitBuffer/CBitBuffer_Test.mq5
2025-09-19 20:11:42 +00:00

527 lines
39 KiB
MQL5

//+------------------------------------------------------------------+
//| 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>();
MyTestStruct readStruct2 = buffer.ReadStruct<MyTestStruct>();
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!");
}
}