var wota = use('wota') var os = use('os') var blob = use('blob') var math = use('math/radians') var EPSILON = 1e-12 function stone_if_needed(b) { if (!stone.p(b)) stone(b) } /* Deep comparison capable of blobs + tolerance for floating diff */ function deep_compare(expected, actual, path) { path = path || '' if (expected == actual) return { passed: true, messages: [] } if (is_number(expected) && is_number(actual)) { if (isNaN(expected) && isNaN(actual)) return { passed: true, messages: [] } var diff = abs(expected - actual) if (diff <= EPSILON) return { passed: true, messages: [] } return { passed: false, messages: [`Value mismatch at ${path}: ${expected} vs ${actual} (diff ${diff})`] } } if (is_blob(expected) && is_blob(actual)) { stone_if_needed(expected); stone_if_needed(actual) if (length(expected) != length(actual)) return { passed: false, messages: [`blob length mismatch at ${path}: ${length(expected)} vs ${length(actual)}`] } arrfor(array(expected), function(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 (is_array(expected) && is_array(actual)) { if (length(expected) != length(actual)) return { passed: false, messages: [`Array length mismatch at ${path}: ${length(expected)} vs ${length(actual)}`] } var msgs = [] arrfor(array(expected), function(i) { var res = deep_compare(expected[i], actual[i], `${path}[${i}]`) if (!res.passed) array(msgs, res.messages) }) return { passed: length(msgs) == 0, messages: msgs } } if (is_object(expected) && is_object(actual)) { var expKeys = sort(array(expected)) var actKeys = sort(array(actual)) if (JSON.stringify(expKeys) != JSON.stringify(actKeys)) return { passed: false, messages: [`Object keys mismatch at ${path}: ${expKeys} vs ${actKeys}`] } var msgs = [] arrfor(expKeys, function(k) { var res = deep_compare(expected[k], actual[k], `${path}.${k}`) if (!res.passed) array(msgs, res.messages) }) return { passed: length(msgs) == 0, messages: msgs } } return { passed: false, messages: [`Value mismatch at ${path}: ${JSON.stringify(expected)} vs ${JSON.stringify(actual)}`] } } var testarr = [] for (var i = 0; i < 500; i++) { push(testarr, 1) } var testCases = [ { name: 'zero', input: 0 }, { name: '2023', input: 2023 }, { name: 'neg1', input: -1 }, { name: '7', input: 7 }, { name: 'neg7', input: -7 }, { name: '1023', input: 1023 }, { name: 'neg1023', input: -1023 }, { name: 'large_pos', input: math.power(2, 55) - 1 }, { name: 'large_neg', input: -math.power(2, 55) }, { 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: 'float_pow2_55', input: math.power(2, 55) }, { name: 'empty_string', input: '' }, { name: 'string_cat', input: 'cat' }, { name: 'string_unicode', input: 'U+1F4A9 「うんち絵文字」 «💩»' }, { 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: 'nested_object', input: { num: 42, arr: [1, -1, 2.5], str: 'test', obj: { x: true } } }, { name: 'empty_blob', input: blob() }, { name: 'nested_array', input: [[]] }, { name: 'empty_key_val', input: { '': '' } }, { name: 'small_float', input: 1e-10 } ] function make_test(t) { return function() { var enc = wota.encode(t.input) if (!is_blob(enc)) throw 'encode() should return a blob' var dec = wota.decode(enc) var cmp = deep_compare(t.input, dec) if (!cmp.passed) throw text(cmp.messages, '; ') } } var tests = {} for (var i = 0; i < length(testCases); i++) { var t = testCases[i] var name = t.name || ('case_' + i) tests[name] = make_test(t) } return tests