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 = number.abs(expected - actual) if (diff <= EPSILON) return { passed: true, messages: [] } return { passed: false, messages: [`Value mismatch at ${path}: ${expected} vs ${actual} (diff ${diff})`] } } 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 (is_array(expected) && is_array(actual)) { if (expected.length != actual.length) return { passed: false, messages: [`Array length mismatch at ${path}: ${expected.length} vs ${actual.length}`] } var msgs = [] for (var i = 0; i < expected.length; i++) { var res = deep_compare(expected[i], actual[i], `${path}[${i}]`) if (!res.passed) { for(var m of res.messages) msgs.push(m) } } return { passed: msgs.length == 0, messages: msgs } } if (is_object(expected) && is_object(actual)) { var expKeys = array(expected).sort() var actKeys = array(actual).sort() if (JSON.stringify(expKeys) != JSON.stringify(actKeys)) return { passed: false, messages: [`Object keys mismatch at ${path}: ${expKeys} vs ${actKeys}`] } var msgs = [] for (var k of expKeys) { var res = deep_compare(expected[k], actual[k], `${path}.${k}`) if (!res.passed) { for(var m of res.messages) msgs.push(m) } } return { passed: msgs.length == 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++) { testarr.push(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: new 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 (!(enc instanceof blob)) throw 'encode() should return a blob' var dec = wota.decode(enc) var cmp = deep_compare(t.input, dec) if (!cmp.passed) throw cmp.messages.join('; ') } } var tests = {} for (var i = 0; i < testCases.length; i++) { var t = testCases[i] var name = t.name || ('case_' + i) tests[name] = make_test(t) } return tests