Files
cell/tests/nota.cm
2025-12-05 23:21:07 -06:00

184 lines
6.1 KiB
Plaintext

var nota = use('nota');
var os = use('os');
var blob = use('blob')
var EPSILON = 1e-12
function stone_if_needed(b) { if (!stone.p(b)) stone(b) }
function bytes_to_blob(bytes) {
var b = new blob()
for (var i = 0; i < bytes.length; i++) {
var byte = bytes[i]
for (var bit = 7; bit >= 0; bit--) b.write_bit((byte >> bit) & 1)
}
stone(b)
return b
}
function deepCompare(expected, actual, path) {
path = path || ''
if (expected == actual) return { passed: true, messages: [] };
if (typeof expected == 'number' && typeof actual == 'number') {
if (isNaN(expected) && isNaN(actual))
return { passed: true, messages: [] };
var diff = Math.abs(expected - actual);
if (diff <= EPSILON)
return { passed: true, messages: [] };
return {
passed: false,
messages: [
`Value mismatch at ${path}: expected ${expected}, got ${actual}`,
`Difference of ${diff} is larger than tolerance ${EPSILON}`
]
};
}
if ((expected instanceof blob) && (actual instanceof blob)) {
stone_if_needed(expected); stone_if_needed(actual)
if (expected.length != actual.length)
return { passed: false, messages: [`blob length mismatch at ${path}: ${expected.length} vs ${actual.length}`] }
for (var i = 0; i < expected.length; i++) {
if (expected.read_logical(i) != actual.read_logical(i))
return { passed: false, messages: [`blob bit mismatch at ${path}[${i}]`] }
}
return { passed: true, messages: [] }
}
if (Array.isArray(expected) && Array.isArray(actual)) {
if (expected.length != actual.length)
return {
passed: false,
messages: [`Array length mismatch at ${path}: expected ${expected.length}, got ${actual.length}`]
};
let messages = [];
for (let i = 0; i < expected.length; i++) {
var result = deepCompare(expected[i], actual[i], `${path}[${i}]`);
if (!result.passed) {
for(var m of result.messages) messages.push(m);
}
}
return { passed: messages.length == 0, messages: messages };
}
if (typeof expected == 'object' && expected != null &&
typeof actual == 'object' && actual != null) {
var expKeys = Object.keys(expected).sort();
var actKeys = Object.keys(actual).sort();
if (JSON.stringify(expKeys) != JSON.stringify(actKeys))
return {
passed: false,
messages: [`Object keys mismatch at ${path}: expected ${expKeys}, got ${actKeys}`]
};
let messages = [];
for (let key of expKeys) {
var result = deepCompare(expected[key], actual[key], `${path}.${key}`);
if (!result.passed) {
for(var m of result.messages) messages.push(m);
}
}
return { passed: messages.length == 0, messages: messages };
}
return {
passed: false,
messages: [`Value mismatch at ${path}: expected ${JSON.stringify(expected)}, got ${JSON.stringify(actual)}`]
};
}
function makeTest(test) {
return function() {
var encoded = test.replacer ? nota.encode(test.input, test.replacer) : nota.encode(test.input);
if (!(encoded instanceof blob)){
throw "encode() should return blob";
}
var decoded = test.reviver ? nota.decode(encoded, test.reviver) : nota.decode(encoded);
var expected = test.expected || test.input;
if (expected && (expected.private || expected.system)) {
var key = expected.private ? 'private' : 'system';
expected = { [key]: expected[key] };
}
var compareResult = deepCompare(expected, decoded);
if (!compareResult.passed) {
throw compareResult.messages.join('; ');
}
};
}
var testarr = []
for (var i = 0; i < 500; i++) {
testarr.push(1)
}
var testCases = [
{ name: 'zero', input: 0 },
{ name: 'positive_2023', input: 2023 },
{ name: 'neg1', input: -1 },
{ name: 'positive_7', input: 7 },
{ name: 'neg7', input: -7 },
{ name: 'positive_1023', input: 1023 },
{ name: 'neg1023', input: -1023 },
{ name: 'null', input: null },
{ name: 'false', input: false },
{ name: 'true', input: true },
{ name: 'float_neg1_01', input: -1.01 },
{ name: 'float_98_6', input: 98.6 },
{ name: 'float_euler', input: -0.5772156649 },
{ name: 'float_precision', input: -1.00000000000001 },
{ name: 'float_large_neg', input: -10000000000000 },
{ name: 'empty_string', input: "" },
{ name: 'string_cat', input: "cat" },
{ name: 'string_unicode', input: "U+1F4A9 「うんち絵文字」 «💩»" },
{ name: 'buffer_ffaa', input: bytes_to_blob([0xFF, 0xAA]) },
{ name: 'buffer_f0e32080', input: bytes_to_blob([0b11110000, 0b11100011, 0b00100000, 0b10000000]) },
{ name: 'large_array', input: testarr },
{ name: 'empty_array', input: [] },
{ name: 'array_123', input: [1, 2, 3] },
{ name: 'array_mixed', input: [-1, 0, 1.5] },
{ name: 'empty_object', input: {} },
{ name: 'object_ab', input: { a: 1, b: 2 } },
{ name: 'object_nested', input: {
num: 42,
arr: [1, -1, 2.5],
str: "test",
obj: { x: true }
} },
{ name: 'object_private', input: { private: { address: "test" } } },
{ name: 'object_system', input: { system: { msg: "hello" } } },
{ name: 'array_system_nested', input: [{ system: {msg: "hello" } }, {
num: 42,
arr: [1, -1, 2.5],
str: "test",
obj: { x: true }
}] },
{ name: 'empty_buffer', input: new blob() },
{ name: 'nested_empty_array', input: [[]] },
{ name: 'empty_key_value', input: { "": "" } },
{ name: 'small_float', input: 1e-10 },
{ name: 'replacer_multiply', input: { a: 1, b: 2 },
replacer: (key, value) => typeof value == 'number' ? value * 2 : value,
expected: { a: 2, b: 4 } },
{ name: 'replacer_string_append', input: { x: "test", y: 5 },
replacer: (key, value) => key == 'x' ? value + "!" : value,
expected: { x: "test!", y: 5 } },
{ name: 'reviver_multiply', input: { a: 1, b: 2 },
reviver: (key, value) => typeof value == 'number' ? value * 3 : value,
expected: { a: 3, b: 6 } },
{ name: 'reviver_increment', input: { x: "test", y: 10 },
reviver: (key, value) => key == 'y' ? value + 1 : value,
expected: { x: "test", y: 11 } }
];
var tests = {};
for (var i = 0; i < testCases.length; i++) {
var t = testCases[i];
tests[t.name] = makeTest(t);
}
return tests;