Files
8bitworkshop/test/unit/testcommon.ts
T

268 lines
10 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import assert from "assert";
import { describe, it } from "mocha";
import {
lpad, rpad, byte2signed, getFilenameForPath, getFolderForPath,
getFilenamePrefix, hex, tobin, toradix, arrayCompare, invertMap,
stringToByteArray, byteArrayToString, byteArrayToUTF8, bytesToFileData, removeBOM,
isProbablyBinary, clamp, safeident, rgb2bgr, RGBA, findIntegerFactors,
replaceAll, highlightDifferences, decodeQueryString, parseBool,
safe_extend, printFlags, byteToASCII, coerceToArray
} from "../../src/common/util";
describe('String Padding Functions', function () {
it('lpad should pad strings on the left', function () {
assert.strictEqual(lpad('5', 3), ' 5');
assert.strictEqual(lpad('hello', 8), ' hello');
assert.strictEqual(lpad('x', 1), 'x');
assert.strictEqual(lpad('123', 5), ' 123');
});
it('rpad should pad strings on the right', function () {
assert.strictEqual(rpad('5', 3), '5 ');
assert.strictEqual(rpad('hello', 8), 'hello ');
assert.strictEqual(rpad('x', 1), 'x');
assert.strictEqual(rpad('123', 5), '123 ');
});
});
describe('Number Conversion Functions', function () {
it('hex should format numbers in hexadecimal', function () {
assert.strictEqual(hex(255), 'FF');
assert.strictEqual(hex(0), '00');
assert.strictEqual(hex(255, 4), '00FF');
assert.strictEqual(hex(65535, 4), 'FFFF');
assert.strictEqual(hex(0x12345678, 8), '12345678');
assert.strictEqual(hex(15, 2), '0F');
});
it('tobin should format numbers in binary', function () {
assert.strictEqual(tobin(0), '00000000');
assert.strictEqual(tobin(255), '11111111');
assert.strictEqual(tobin(5), '00000101');
assert.strictEqual(tobin(15, 4), '1111');
assert.strictEqual(tobin(128), '10000000');
});
it('toradix should convert to various bases', function () {
assert.strictEqual(toradix(10, 4, 16), '000A');
assert.strictEqual(toradix(255, 8, 16), '000000FF');
assert.strictEqual(toradix(7, 3, 2), '111');
assert.strictEqual(toradix(100, 4, 10), '0100');
});
it('byte2signed should convert unsigned to signed bytes', function () {
assert.strictEqual(byte2signed(0), 0);
assert.strictEqual(byte2signed(127), 127);
assert.strictEqual(byte2signed(128), -128);
assert.strictEqual(byte2signed(255), -1);
assert.strictEqual(byte2signed(200), -56);
assert.strictEqual(byte2signed(0x80), -128);
});
});
describe('File Path Functions', function () {
it('getFilenameForPath should extract filename from path', function () {
assert.strictEqual(getFilenameForPath('path/to/file.txt'), 'file.txt');
assert.strictEqual(getFilenameForPath('file.txt'), 'file.txt');
assert.strictEqual(getFilenameForPath('/a/b/c/d.js'), 'd.js');
assert.strictEqual(getFilenameForPath('a/b'), 'b');
});
it('getFolderForPath should extract folder from path', function () {
assert.strictEqual(getFolderForPath('path/to/file.txt'), 'path/to');
assert.strictEqual(getFolderForPath('file.txt'), '');
assert.strictEqual(getFolderForPath('/a/b/c/d.js'), '/a/b/c');
assert.strictEqual(getFolderForPath('a/b/c'), 'a/b');
});
it('getFilenamePrefix should extract name without extension', function () {
assert.strictEqual(getFilenamePrefix('file.txt'), 'file');
assert.strictEqual(getFilenamePrefix('archive.tar.gz'), 'archive.tar');
assert.strictEqual(getFilenamePrefix('noext'), 'noext');
assert.strictEqual(getFilenamePrefix('.hidden'), '.hidden');
});
});
describe('Array/Collection Functions', function () {
it('arrayCompare should compare arrays', function () {
assert.ok(arrayCompare([1, 2, 3], [1, 2, 3]));
assert.ok(!arrayCompare([1, 2, 3], [1, 2, 4]));
assert.ok(!arrayCompare([1, 2], [1, 2, 3]));
assert.ok(arrayCompare([], []));
assert.ok(!arrayCompare([1], []));
assert.ok(arrayCompare(null, null));
assert.ok(!arrayCompare(null, [1]));
assert.ok(!arrayCompare([1], null));
const arr1 = new Uint8Array([1, 2, 3]);
const arr2 = new Uint8Array([1, 2, 3]);
assert.ok(arrayCompare(arr1, arr2));
});
it('invertMap should invert key-value pairs', function () {
const map = { a: '1', b: '2', c: '3' };
const inv = invertMap(map);
assert.strictEqual(inv['1'], 'a');
assert.strictEqual(inv['2'], 'b');
assert.strictEqual(inv['3'], 'c');
});
it('coerceToArray should convert various types to arrays', function () {
assert.deepStrictEqual(coerceToArray([1, 2, 3]), [1, 2, 3]);
assert.deepStrictEqual(coerceToArray({ a: 1, b: 2 }), [1, 2]);
});
});
describe('Byte Array Conversion Functions', function () {
it('stringToByteArray should convert string to bytes', function () {
const bytes = stringToByteArray('hello');
assert.strictEqual(bytes.length, 5);
assert.strictEqual(bytes[0], 104); // 'h'
assert.strictEqual(bytes[4], 111); // 'o'
});
it('byteArrayToString should convert bytes to string', function () {
const bytes = [72, 101, 108, 108, 111]; // "Hello"
assert.strictEqual(byteArrayToString(bytes), 'Hello');
assert.strictEqual(byteArrayToString(new Uint8Array([65, 66, 67])), 'ABC');
});
it('byteArrayToUTF8 should decode UTF-8', function () {
const ascii = byteArrayToUTF8([72, 101, 108, 108, 111]);
assert.strictEqual(ascii, 'Hello');
});
it('removeBOM should strip BOM if present', function () {
assert.strictEqual(removeBOM('hello'), 'hello');
assert.strictEqual(removeBOM('hello'), 'hello');
});
});
describe('Binary Detection', function () {
it('isProbablyBinary should detect binary data', function () {
assert.ok(!isProbablyBinary('test.txt', [104, 101, 108, 108, 111])); // "hello"
assert.ok(isProbablyBinary('test.bin', [1, 2, 3]));
assert.ok(isProbablyBinary('test.chr', [255, 255]));
assert.ok(isProbablyBinary('test.dat', [0]));
assert.ok(!isProbablyBinary('test.c', [47, 47, 105, 102])); // "//if"
assert.ok(isProbablyBinary(null, [0, 0, 0, 0])); // null bytes
});
it('bytesToFileData should sniff content, not just the extension', function () {
// unregistered extension with NUL bytes must stay binary, not become text
const bin = bytesToFileData('tune.vgr3', [0x56, 0x67, 0x6d, 0x20, 0x00, 0xb3]);
assert.ok(bin instanceof Uint8Array);
// otherwise it decodes to text
assert.strictEqual(bytesToFileData('hello.c', [104, 101, 108, 108, 111]), 'hello');
assert.strictEqual(bytesToFileData('hello.txt', new Uint8Array([0x68, 0xc3, 0xa9])), 'h\u00e9');
// known binary extension wins even if the bytes happen to be text
assert.ok(bytesToFileData('test.bin', [104, 105]) instanceof Uint8Array);
});
});
describe('Color Functions', function () {
it('rgb2bgr should swap red and blue channels', function () {
assert.strictEqual(rgb2bgr(0xFF0000), 0x0000FF);
assert.strictEqual(rgb2bgr(0x00FF00), 0x00FF00);
assert.strictEqual(rgb2bgr(0x0000FF), 0xFF0000);
assert.strictEqual(rgb2bgr(0xFFFFFF), 0xFFFFFF);
});
it('RGBA should create color value', function () {
const white = RGBA(255, 255, 255);
assert.strictEqual(white & 0xFFFFFF, 0xFFFFFF);
const black = RGBA(0, 0, 0);
assert.strictEqual(black & 0xFFFFFF, 0x000000);
});
it('clamp should constrain values', function () {
assert.strictEqual(clamp(0, 10, 5), 5);
assert.strictEqual(clamp(0, 10, -5), 0);
assert.strictEqual(clamp(0, 10, 15), 10);
assert.strictEqual(clamp(1, 1, 5), 1);
});
});
describe('String Manipulation', function () {
it('safeident should create valid identifiers', function () {
assert.strictEqual(safeident('valid_name'), 'valid_name');
assert.strictEqual(safeident('123abc'), '_123abc');
assert.strictEqual(safeident('-name'), '__name');
assert.strictEqual(safeident('name-with-dashes'), 'name_with_dashes');
assert.strictEqual(safeident('name@#$%'), 'name_');
});
it('replaceAll should replace all occurrences', function () {
assert.strictEqual(replaceAll('aaa', 'a', 'b'), 'bbb');
assert.strictEqual(replaceAll('hello world', ' ', '-'), 'hello-world');
assert.strictEqual(replaceAll('test', 'x', 'y'), 'test');
assert.strictEqual(replaceAll('', 'a', 'b'), '');
});
it('byteToASCII should convert bytes to readable chars', function () {
assert.strictEqual(byteToASCII(65), 'A');
assert.strictEqual(byteToASCII(32), ' ');
assert.strictEqual(byteToASCII(0), '␀'); // NULL control char
});
});
describe('Query String Functions', function () {
it('decodeQueryString should parse query parameters', function () {
const qs1 = decodeQueryString('?key=value&foo=bar');
assert.strictEqual(qs1['key'], 'value');
assert.strictEqual(qs1['foo'], 'bar');
const qs2 = decodeQueryString('a=1&b=2');
assert.strictEqual(qs2['a'], '1');
assert.strictEqual(qs2['b'], '2');
const qs3 = decodeQueryString('key=value+with+spaces');
assert.ok(qs3['key'].includes(' '));
});
it('parseBool should parse boolean strings', function () {
assert.ok(parseBool('true'));
assert.ok(parseBool('1'));
assert.ok(parseBool('yes'));
assert.ok(!parseBool('false'));
assert.ok(!parseBool('0'));
assert.ok(!parseBool(''));
});
});
describe('Complex Utility Functions', function () {
it('findIntegerFactors should find factor pairs', function () {
const f1 = findIntegerFactors(64, 1, 1, 1);
assert.ok(f1.a >= f1.b);
assert.strictEqual(f1.a * f1.b >= 64, true);
const f2 = findIntegerFactors(100, 5, 5, 1);
assert.ok(f2.a >= 5);
assert.ok(f2.b >= 5);
});
it('safe_extend should copy primitive values and arrays', function () {
const dest = { x: 1 };
const src = { y: 2, arr: [1, 2, 3] };
safe_extend(false, dest, src);
assert.strictEqual(dest['x'], 1);
assert.strictEqual(dest['y'], 2);
assert.deepStrictEqual(dest['arr'], [1, 2, 3]);
assert.ok(dest['arr'] !== src['arr']); // should be copy
});
it('printFlags should format flag values', function () {
const names = ['CARRY', 'ZERO', 'IRQ_DISABLE', 'DECIMAL'];
const result = printFlags(0x01, names, false);
assert.ok(result.includes('CARRY'));
const result2 = printFlags(0x00, names, false);
assert.ok(result2.includes('-'));
});
it('highlightDifferences should mark changes in text', function () {
const diff = highlightDifferences('hello world', 'hello there');
assert.ok(diff.includes('hilite'));
});
});