Files
sausage/userland/qjs/source/tests/test_bjson.js
T
surma d44f1d9677 Build separate native and WASI pipelines with shared multicall names
Checkpoint before integration and package validation.

Assisted-by: SurmAgent:unknown [read] [edit] [bash]
2026-11-12 09:36:27 +00:00

338 lines
9.1 KiB
JavaScript

import * as std from "qjs:std";
import * as bjson from "qjs:bjson";
import { assert, assertArrayEquals } from "./assert.js";
function isArrayLike(a)
{
return Array.isArray(a) ||
(a instanceof Uint8ClampedArray) ||
(a instanceof Uint8Array) ||
(a instanceof Uint16Array) ||
(a instanceof Uint32Array) ||
(a instanceof Int8Array) ||
(a instanceof Int16Array) ||
(a instanceof Int32Array) ||
(a instanceof Float16Array) ||
(a instanceof Float32Array) ||
(a instanceof Float64Array);
}
function toStr(a)
{
var s, i, props, prop;
switch(typeof(a)) {
case "object":
if (a === null)
return "null";
if (a instanceof Date) {
s = "Date(" + toStr(a.valueOf()) + ")";
} else if (a instanceof Number) {
s = "Number(" + toStr(a.valueOf()) + ")";
} else if (a instanceof String) {
s = "String(" + toStr(a.valueOf()) + ")";
} else if (a instanceof Boolean) {
s = "Boolean(" + toStr(a.valueOf()) + ")";
} else if (isArrayLike(a)) {
s = "[";
for(i = 0; i < a.length; i++) {
if (i != 0)
s += ",";
s += toStr(a[i]);
}
s += "]";
} else {
props = Object.keys(a);
s = "{";
for(i = 0; i < props.length; i++) {
if (i != 0)
s += ",";
prop = props[i];
s += prop + ":" + toStr(a[prop]);
}
s += "}";
}
return s;
case "undefined":
return "undefined";
case "string":
return JSON.stringify(a);
case "number":
if (a == 0 && 1 / a < 0)
return "-0";
else
return a.toString();
break;
default:
return a.toString();
}
}
function bjson_test(a)
{
var buf, r, a_str, r_str;
a_str = toStr(a);
buf = bjson.write(a);
if (0) {
print(a_str, "->", new Uint8Array(buf).toHex());
}
r = bjson.read(buf, 0, buf.byteLength);
r_str = toStr(r);
if (a_str != r_str) {
print(a_str);
print(r_str);
assert(false);
}
}
function bjson_test_arraybuffer()
{
var buf, array_buffer;
array_buffer = new ArrayBuffer(4);
assert(array_buffer.byteLength, 4);
assert(array_buffer.maxByteLength, 4);
assert(array_buffer.resizable, false);
buf = bjson.write(array_buffer);
array_buffer = bjson.read(buf, 0, buf.byteLength);
assert(array_buffer.byteLength, 4);
assert(array_buffer.maxByteLength, 4);
assert(array_buffer.resizable, false);
array_buffer = new ArrayBuffer(4, {maxByteLength: 4});
assert(array_buffer.byteLength, 4);
assert(array_buffer.maxByteLength, 4);
assert(array_buffer.resizable, true);
buf = bjson.write(array_buffer);
array_buffer = bjson.read(buf, 0, buf.byteLength);
assert(array_buffer.byteLength, 4);
assert(array_buffer.maxByteLength, 4);
assert(array_buffer.resizable, true);
array_buffer = new ArrayBuffer(4, {maxByteLength: 8});
assert(array_buffer.byteLength, 4);
assert(array_buffer.maxByteLength, 8);
assert(array_buffer.resizable, true);
buf = bjson.write(array_buffer);
array_buffer = bjson.read(buf, 0, buf.byteLength);
assert(array_buffer.byteLength, 4);
assert(array_buffer.maxByteLength, 8);
assert(array_buffer.resizable, true);
}
/* test multiple references to an object including circular
references */
function bjson_test_reference()
{
var array, buf, i, n, array_buffer;
n = 16;
array = [];
for(i = 0; i < n; i++)
array[i] = {};
array_buffer = new ArrayBuffer(n);
for(i = 0; i < n; i++) {
array[i].next = array[(i + 1) % n];
array[i].idx = i;
array[i].typed_array = new Uint8Array(array_buffer, i, 1);
}
buf = bjson.write(array, bjson.WRITE_OBJ_REFERENCE);
array = bjson.read(buf, 0, buf.byteLength, bjson.READ_OBJ_REFERENCE);
/* check the result */
for(i = 0; i < n; i++) {
assert(array[i].next, array[(i + 1) % n]);
assert(array[i].idx, i);
assert(array[i].typed_array.buffer, array_buffer);
assert(array[i].typed_array.length, 1);
assert(array[i].typed_array.byteOffset, i);
}
}
function bjson_test_regexp()
{
var buf, r;
bjson_test(/xyzzy/);
bjson_test(/xyzzy/digu);
buf = bjson.write(/(?<𝓓𝓸𝓰>dog)/);
r = bjson.read(buf, 0, buf.byteLength);
assert("sup dog".match(r).groups["𝓓𝓸𝓰"], "dog");
}
function bjson_test_map()
{
var buf, r, xs;
xs = [["key", "value"]];
buf = bjson.write(new Map(xs));
r = bjson.read(buf, 0, buf.byteLength);
assert(r instanceof Map);
assert([...r].toString(), xs.toString());
}
function bjson_test_set()
{
var buf, r, xs;
xs = ["one", "two", "three"];
buf = bjson.write(new Set(xs));
r = bjson.read(buf, 0, buf.byteLength);
assert(r instanceof Set);
assert([...r].toString(), xs.toString());
}
function bjson_test_symbol()
{
var buf, r, o;
o = {[Symbol.toStringTag]: "42"};
buf = bjson.write(o);
r = bjson.read(buf, 0, buf.byteLength);
assert(o.toString(), r.toString());
o = Symbol('foo');
buf = bjson.write(o);
r = bjson.read(buf, 0, buf.byteLength);
assert(o.toString(), r.toString());
assert(o !== r);
o = Symbol.for('foo');
buf = bjson.write(o);
r = bjson.read(buf, 0, buf.byteLength);
assert(o, r);
o = Symbol.toStringTag;
buf = bjson.write(o);
r = bjson.read(buf, 0, buf.byteLength);
assert(o, r);
}
function bjson_test_bytecode()
{
var buf, o, r, e, i;
o = std.evalScript(";(function f(o){ return o.i })", {compile_only: true});
buf = bjson.write(o, bjson.WRITE_OBJ_BYTECODE);
try {
bjson.read(buf, 0, buf.byteLength);
} catch (_e) {
e = _e;
}
assert(String(e), "SyntaxError: no bytecode allowed");
o = bjson.read(buf, 0, buf.byteLength, bjson.READ_OBJ_BYTECODE);
assert(String(o), "[function bytecode]");
o = std.evalScript(o, {eval_function: true});
for (i = 0; i < 42; i++) o({i}); // exercise o.i IC
}
function bjson_test_fuzz()
{
var corpus = [
["HP////8QAAAAAARg"],
["HP/////m5uaCLQ=="],
["HP////8AEQATBgYGBgYGBgYGBgb/////EAARAC8R/78vEf+/"],
["HP////8ACH8ACv////9//////////////////////////////9//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABgAAAAAAAAAAAAAA+fn5+fn5+fn5+fn5AAAAAAAGAKs="],
["HP////8ADgAAABQA", bjson.READ_OBJ_REFERENCE],
];
for (var [input, flags] of corpus) {
var buf = Uint8Array.fromBase64(input);
try {
bjson.read(buf, 0, buf.byteLength, flags);
} catch (e) {
if (/invalid version/.test(e.message)) throw e; // corpus needs update
if (/checksum error/.test(e.message)) throw e;
}
}
}
function bjson_test_csum()
{
var buf = bjson.write("fortytwo");
var tab = new Uint8Array(buf);
for (var i = 5; i < tab.length; i++) {
for (var k = 0; k < 256; k++) {
var t = tab[i];
if (t == k)
continue;
tab[i] = k;
var caught = false;
try {
bjson.read(buf, 0, buf.byteLength);
} catch (e) {
caught = /checksum error/.test(e.message);
}
assert(caught);
tab[i] = t;
}
}
}
function bjson_test_truncated_bigint()
{
var buf = bjson.write(-(2n ** 100n));
new DataView(buf).setUint32(1, 0xFFFFFFFF, true); // disable checksum
for (var n = 1; n < buf.byteLength; n++) {
var caught = false;
try {
bjson.read(buf, 0, n);
} catch (e) {
caught = /read after the end of the buffer/.test(e.message);
}
assert(caught);
}
}
function bjson_test_atom()
{
var o = {return:1, with:2, Map:3}; // Map tests LEB128 encoding
var b = bjson.write(o);
var r = bjson.read(b, 0, b.byteLength);
assertArrayEquals(Object.entries(o), Object.entries(r));
}
function bjson_test_all()
{
var obj;
bjson_test({x:1, y:2, if:3});
bjson_test([1, 2, 3]);
bjson_test([1.0, "aa", true, false, undefined, null, NaN, -Infinity, -0.0]);
if (typeof BigInt !== "undefined") {
bjson_test([BigInt("1"), -BigInt("0x123456789"),
BigInt("0x123456789abcdef123456789abcdef")]);
}
bjson_test([new Date(1234), new String("abc"), new Number(-12.1), new Boolean(true)]);
bjson_test(new Int32Array([123123, 222111, -32222]));
bjson_test(new Float16Array([1024, 1024.5]));
bjson_test(new Float64Array([123123, 222111.5]));
/* tested with a circular reference */
obj = {};
obj.x = obj;
try {
bjson.write(obj);
assert(false);
} catch(e) {
assert(e instanceof TypeError);
}
bjson_test_arraybuffer();
bjson_test_reference();
bjson_test_regexp();
bjson_test_map();
bjson_test_set();
bjson_test_symbol();
bjson_test_bytecode();
bjson_test_fuzz();
bjson_test_csum();
bjson_test_atom();
bjson_test_truncated_bigint();
}
bjson_test_all();