Files
node-metaverse/lib/classes/BinaryWriter.spec.ts
2025-01-17 23:53:31 +00:00

510 lines
16 KiB
TypeScript

import { describe, it, expect } from 'vitest';
import { BinaryReader } from './BinaryReader';
import { BinaryWriter } from './BinaryWriter';
import { UUID } from './UUID';
describe('BinaryWriter', () =>
{
const testRoundTrip = <T>(
writeFunc: (bw: BinaryWriter, value: T) => void,
readFunc: (br: BinaryReader) => T,
value: T
) =>
{
const bw = new BinaryWriter();
writeFunc(bw, value);
const buf = bw.get();
const br = new BinaryReader(buf);
const readValue = readFunc(br);
if (typeof value === 'number')
{
expect(readValue).toBeCloseTo(value);
}
else
{
expect(readValue).toBe(value);
}
};
describe('Unsigned Integers', () =>
{
it('should write and read UInt8 correctly', () =>
{
const value = 255;
testRoundTrip<number>(
(bw, val) => bw.writeUInt8(val),
(br) => br.readUInt8(),
value
);
});
it('should write and read UInt16LE correctly', () =>
{
const value = 65535;
testRoundTrip<number>(
(bw, val) => bw.writeUInt16LE(val),
(br) => br.readUInt16LE(),
value
);
});
it('should write and read UInt16BE correctly', () =>
{
const value = 65535;
testRoundTrip<number>(
(bw, val) => bw.writeUInt16BE(val),
(br) => br.readUInt16BE(),
value
);
});
it('should write and read UInt32LE correctly', () =>
{
const value = 4294967295;
testRoundTrip<number>(
(bw, val) => bw.writeUInt32LE(val),
(br) => br.readUInt32LE(),
value
);
});
it('should write and read UInt32BE correctly', () =>
{
const value = 4294967295;
testRoundTrip<number>(
(bw, val) => bw.writeUInt32BE(val),
(br) => br.readUInt32BE(),
value
);
});
it('should write and read UInt64LE correctly', () =>
{
const value = 18446744073709551615n; // Max UInt64
testRoundTrip<bigint>(
(bw, val) => bw.writeUInt64LE(val),
(br) => br.readUInt64LE(),
value
);
});
it('should write and read UInt64BE correctly', () =>
{
const value = 18446744073709551615n; // Max UInt64
testRoundTrip<bigint>(
(bw, val) => bw.writeUInt64BE(val),
(br) => br.readUInt64BE(),
value
);
});
});
describe('Signed Integers', () =>
{
it('should write and read Int8 correctly', () =>
{
const value = -128;
testRoundTrip<number>(
(bw, val) => bw.writeInt8(val),
(br) => br.readInt8(),
value
);
});
it('should write and read Int16LE correctly', () =>
{
const value = -32768;
testRoundTrip<number>(
(bw, val) => bw.writeInt16LE(val),
(br) => br.readInt16LE(),
value
);
});
it('should write and read Int16BE correctly', () =>
{
const value = -32768;
testRoundTrip<number>(
(bw, val) => bw.writeInt16BE(val),
(br) => br.readInt16BE(),
value
);
});
it('should write and read Int32LE correctly', () =>
{
const value = -2147483648;
testRoundTrip<number>(
(bw, val) => bw.writeInt32LE(val),
(br) => br.readInt32LE(),
value
);
});
it('should write and read Int32BE correctly', () =>
{
const value = -2147483648;
testRoundTrip<number>(
(bw, val) => bw.writeInt32BE(val),
(br) => br.readInt32BE(),
value
);
});
it('should write and read Int64LE correctly', () =>
{
const value = -9223372036854775808n; // Min Int64
testRoundTrip<bigint>(
(bw, val) => bw.writeInt64LE(val),
(br) => br.readInt64LE(),
value
);
});
it('should write and read Int64BE correctly', () =>
{
const value = -9223372036854775808n; // Min Int64
testRoundTrip<bigint>(
(bw, val) => bw.writeInt64BE(val),
(br) => br.readInt64BE(),
value
);
});
});
describe('Floating Point Numbers', () =>
{
it('should write and read FloatLE correctly', () =>
{
const value = 12345.6789;
testRoundTrip<number>(
(bw, val) => bw.writeFloatLE(val),
(br) => br.readFloatLE(),
value
);
});
it('should write and read FloatBE correctly', () =>
{
const value = 12345.6789;
testRoundTrip<number>(
(bw, val) => bw.writeFloatBE(val),
(br) => br.readFloatBE(),
value
);
});
it('should write and read DoubleLE correctly', () =>
{
const value = 123456789.123456789;
testRoundTrip<number>(
(bw, val) => bw.writeDoubleLE(val),
(br) => br.readDoubleLE(),
value
);
});
it('should write and read DoubleBE correctly', () =>
{
const value = 123456789.123456789;
testRoundTrip<number>(
(bw, val) => bw.writeDoubleBE(val),
(br) => br.readDoubleBE(),
value
);
});
});
describe('UUID', () =>
{
it('should write and read UUID correctly', () =>
{
const uuid = UUID.random();
const bw = new BinaryWriter();
bw.writeUUID(uuid);
const buf = bw.get();
expect(buf.length).toBe(16);
const br = new BinaryReader(buf);
const readUUID = br.readUUID();
expect(readUUID.toString()).toBe(uuid.toString());
});
it('should write and read zero UUID correctly', () =>
{
const uuid = UUID.zero();
const bw = new BinaryWriter();
bw.writeUUID(uuid);
const buf = bw.get();
expect(buf.length).toBe(16);
const br = new BinaryReader(buf);
const readUUID = br.readUUID();
expect(readUUID.toString()).toBe(uuid.toString());
});
});
describe('Date', () =>
{
it('should write and read Date correctly', () =>
{
const date = new Date();
const bw = new BinaryWriter();
bw.writeDate(date);
const buf = bw.get();
expect(buf.length).toBe(8);
const br = new BinaryReader(buf);
const readDate = br.readDate();
expect(readDate.getTime()).toBe(date.getTime());
});
it('should write and read epoch Date correctly', () =>
{
const date = new Date(0);
const bw = new BinaryWriter();
bw.writeDate(date);
const buf = bw.get();
expect(buf.length).toBe(8);
const br = new BinaryReader(buf);
const readDate = br.readDate();
expect(readDate.getTime()).toBe(date.getTime());
});
});
describe('CString', () =>
{
it('should write and read CString correctly', () =>
{
const str = 'Hello, World!';
const bw = new BinaryWriter();
bw.writeCString(str);
const buf = bw.get();
expect(buf.length).toBe(Buffer.byteLength(str, 'utf-8') + 1);
const br = new BinaryReader(buf);
const readStr = br.readCString();
expect(readStr).toBe(str);
});
it('should write and read empty CString correctly', () =>
{
const str = '';
const bw = new BinaryWriter();
bw.writeCString(str);
const buf = bw.get();
expect(buf.length).toBe(1); // Only null terminator
const br = new BinaryReader(buf);
const readStr = br.readCString();
expect(readStr).toBe(str);
});
it('should throw error when CString is not null-terminated during read', () =>
{
const buf = Buffer.from('Test without null terminator', 'utf-8');
const bw = new BinaryWriter();
bw.writeBuffer(buf);
const result = () =>
{
const br = new BinaryReader(bw.get());
br.readCString();
};
expect(result).toThrow(RangeError);
});
});
describe('String', () =>
{
it('should write and read String correctly', () =>
{
const str = 'Hello, BinaryWriter!';
const bw = new BinaryWriter();
bw.writeString(str);
const buf = bw.get();
const br = new BinaryReader(buf);
const readStr = br.readString();
expect(readStr).toBe(str);
});
it('should write and read empty String correctly', () =>
{
const str = '';
const bw = new BinaryWriter();
bw.writeString(str);
const buf = bw.get();
const br = new BinaryReader(buf);
const readStr = br.readString();
expect(readStr).toBe(str);
});
it('should handle large strings correctly', () =>
{
const str = 'a'.repeat(1000);
const bw = new BinaryWriter();
bw.writeString(str);
const buf = bw.get();
const br = new BinaryReader(buf);
const readStr = br.readString();
expect(readStr).toBe(str);
});
});
describe('Buffer', () =>
{
it('should write and read Buffer correctly', () =>
{
const buffer = Buffer.from([0x00, 0xFF, 0xAA, 0x55]);
const bw = new BinaryWriter();
bw.writeBuffer(buffer);
const buf = bw.get();
expect(buf.length).toBe(buffer.length);
const br = new BinaryReader(buf);
const readBuffer = br.readBuffer(buffer.length);
expect(readBuffer).toEqual(buffer);
});
it('should write partial Buffer correctly', () =>
{
const buffer = Buffer.from([0x01, 0x02, 0x03, 0x04, 0x05]);
const bw = new BinaryWriter();
bw.writeBuffer(buffer, 1, 4); // [0x02, 0x03, 0x04]
const buf = bw.get();
expect(buf.length).toBe(3);
expect(buf).toEqual(Buffer.from([0x02, 0x03, 0x04]));
const br = new BinaryReader(buf);
const readBuffer = br.readBuffer(3);
expect(readBuffer).toEqual(Buffer.from([0x02, 0x03, 0x04]));
});
it('should handle empty Buffer correctly', () =>
{
const buffer = Buffer.alloc(0);
const bw = new BinaryWriter();
bw.writeBuffer(buffer);
const buf = bw.get();
expect(buf.length).toBe(0);
const br = new BinaryReader(buf);
const readBuffer = br.readBuffer(0);
expect(readBuffer.length).toBe(0);
});
});
describe('Combined Writes', () =>
{
it('should handle multiple writes correctly', () =>
{
const uint8 = 255;
const int16 = -32768;
const float = 3.14;
const uuid = UUID.random();
const date = new Date();
const str = 'Test String';
const buffer = Buffer.from([0xDE, 0xAD, 0xBE, 0xEF]);
const bw = new BinaryWriter();
bw.writeUInt8(uint8);
bw.writeInt16LE(int16);
bw.writeFloatBE(float);
bw.writeUUID(uuid);
bw.writeDate(date);
bw.writeString(str);
bw.writeBuffer(buffer);
const buf = bw.get();
const br = new BinaryReader(buf);
expect(br.readUInt8()).toBe(uint8);
expect(br.readInt16LE()).toBe(int16);
expect(br.readFloatBE()).toBeCloseTo(float, 5);
const readUUID = br.readUUID();
expect(readUUID.toString()).toBe(uuid.toString());
const readDate = br.readDate();
expect(readDate.getTime()).toBe(date.getTime());
const readStr = br.readString();
expect(readStr).toBe(str);
const readBuffer = br.readBuffer(buffer.length);
expect(readBuffer).toEqual(buffer);
});
});
describe('Error Handling', () =>
{
it('should throw error when writing negative UInt values', () =>
{
const bw = new BinaryWriter();
const writeNegativeUInt = () => bw.writeUInt8(-1);
expect(writeNegativeUInt).toThrow(RangeError);
});
it('should throw error when writing string with non-UTF8 characters', () =>
{
const bw = new BinaryWriter();
const invalidStr = '\u{D800}';
const writeInvalidStr = () => bw.writeString(invalidStr);
expect(writeInvalidStr).not.toThrow();
});
});
describe('VarInt', () =>
{
it('should write various size VarInts correctly', () =>
{
const nums: [number, number | bigint, string][] = [
[1, 0, 'AA=='],
[1, 31, 'Pg=='],
[2, 7166, '/G8='],
[3, 71665, '4t8I'],
[4, 7166512, '4OjqBg=='],
[5, 716651292, 'uOy5qwU='],
[6, 19928182913, 'gsL/vJQB'],
[7, 19928182913289, 'kuSbxPyHCQ=='],
[8, Number.MAX_SAFE_INTEGER, '/v///////x8='],
[14, 79228162514264337593543950000n, '4Pr//////////////z8='],
[19, 340282366920938463463374607431768211455n, '/v//////////////////////Bw==']
];
for (const num of nums)
{
const bw = new BinaryWriter();
bw.writeVarInt(num[1]);
const buf = bw.get();
expect(buf.length).toBe(num[0]);
expect(buf.toString('base64')).toBe(num[2]);
}
});
it('should write various negative size VarInts correctly', () =>
{
const nums: [number, number | bigint, string][] = [
[1, -24, 'Lw=='],
[2, -7166, '+28='],
[3, -71665, '4d8I'],
[4, -7166512, '3+jqBg=='],
[5, -716651292, 't+y5qwU='],
[6, -19928182913, 'gcL/vJQB'],
[7, -19928182913289, 'keSbxPyHCQ=='],
[8, Number.MIN_SAFE_INTEGER, '/f///////x8='],
[14, -39614081257132168796771975168n, '/////////////////x8='],
[19, -170141183460469231731687303715884105728n, '////////////////////////Aw==']
];
for (const num of nums)
{
const bw = new BinaryWriter();
bw.writeVarInt(num[1]);
const buf = bw.get();
expect(buf.length).toBe(num[0]);
expect(buf.toString('base64')).toBe(num[2]);
}
});
});
});