/* eslint-env mocha */ import chai from 'chai' import { exec } from 'child_process' import process from 'process' import path from 'path' import { platform } from 'os' import { fileURLToPath } from 'url' const { assert } = chai const fixture1JsonString = '{"a":1,"b":[2,3],"smile":"😀"}' const fixture1JsonPrettyString = `{ "a": 1, "b": [ 2, 3 ], "smile": "😀" } ` const fixture1HexString = 'a3616101616282020365736d696c6564f09f9880' const fixture1Bin = fromHex(fixture1HexString) const fixture1BinString = new TextDecoder().decode(fixture1Bin) const fixture1DiagnosticString = `a3 # map(3) 61 # string(1) 61 # "a" 01 # uint(1) 61 # string(1) 62 # "b" 82 # array(2) 02 # uint(2) 03 # uint(3) 65 # string(5) 736d696c65 # "smile" 64 # string(2) f09f9880 # "😀" ` const fixture2HexString = 'a4616101616282020363627566440102036165736d696c6564f09f9880' const fixture2DiagnosticString = `a4 # map(4) 61 # string(1) 61 # "a" 01 # uint(1) 61 # string(1) 62 # "b" 82 # array(2) 02 # uint(2) 03 # uint(3) 63 # string(3) 627566 # "buf" 44 # bytes(4) 01020361 # "\\x01\\x02\\x03a" 65 # string(5) 736d696c65 # "smile" 64 # string(2) f09f9880 # "😀" ` const binPath = path.join(path.dirname(fileURLToPath(import.meta.url)), '../lib/bin.js') function fromHex (hex) { return new Uint8Array(hex.split('') .map((/** @type {string} */ c, /** @type {number} */ i, /** @type {string[]} */ d) => i % 2 === 0 ? `0x${c}${d[i + 1]}` : '') .filter(Boolean) .map((/** @type {string} */ e) => parseInt(e, 16))) } async function execBin (cmd, stdin) { return new Promise((resolve, reject) => { const cp = exec(`"${process.execPath}" "${binPath}" ${cmd}`, (err, stdout, stderr) => { if (err) { err.stdout = stdout err.stderr = stderr return reject(err) } resolve({ stdout, stderr }) }) if (stdin != null) { cp.on('spawn', () => { cp.stdin.write(stdin) cp.stdin.end() }) } }) } describe('Bin', () => { it('usage', async () => { try { await execBin('') assert.fail('should have errored') } catch (e) { assert.strictEqual(e.stdout, '') assert.strictEqual(e.stderr, `Usage: cborg Valid commands: \tbin2diag [binary input] \tbin2hex [binary input] \tbin2json [--pretty] [binary input] \tdiag2bin [diagnostic input] \tdiag2hex [diagnostic input] \tdiag2json [--pretty] [diagnostic input] \thex2bin [hex input] \thex2diag [hex input] \thex2json [--pretty] [hex input] \tjson2bin '[json input]' \tjson2diag '[json input]' \tjson2hex '[json input]' Input may either be supplied as an argument or piped via stdin `) } }) it('bad cmd', async () => { try { await execBin('blip') assert.fail('should have errored') } catch (e) { assert.strictEqual(e.stdout, '') assert.strictEqual(e.stderr, `Unknown command: 'blip' Usage: cborg Valid commands: \tbin2diag [binary input] \tbin2hex [binary input] \tbin2json [--pretty] [binary input] \tdiag2bin [diagnostic input] \tdiag2hex [diagnostic input] \tdiag2json [--pretty] [diagnostic input] \thex2bin [hex input] \thex2diag [hex input] \thex2json [--pretty] [hex input] \tjson2bin '[json input]' \tjson2diag '[json input]' \tjson2hex '[json input]' Input may either be supplied as an argument or piped via stdin `) } }) it('help', async () => { const { stdout, stderr } = await execBin('help') assert.strictEqual(stdout, '') assert.strictEqual(stderr, `Usage: cborg Valid commands: \tbin2diag [binary input] \tbin2hex [binary input] \tbin2json [--pretty] [binary input] \tdiag2bin [diagnostic input] \tdiag2hex [diagnostic input] \tdiag2json [--pretty] [diagnostic input] \thex2bin [hex input] \thex2diag [hex input] \thex2json [--pretty] [hex input] \tjson2bin '[json input]' \tjson2diag '[json input]' \tjson2hex '[json input]' Input may either be supplied as an argument or piped via stdin `) }) it('bin2diag (stdin)', async () => { const { stdout, stderr } = await execBin('bin2diag', fixture1Bin) assert.strictEqual(stderr, '') assert.strictEqual(stdout, fixture1DiagnosticString) }) it('bin2hex (stdin)', async () => { const { stdout, stderr } = await execBin('bin2hex', fixture1Bin) assert.strictEqual(stderr, '') assert.strictEqual(stdout, `${fixture1HexString}\n`) }) it('bin2json (stdin)', async () => { const { stdout, stderr } = await execBin('bin2json', fixture1Bin) assert.strictEqual(stderr, '') assert.strictEqual(stdout, `${fixture1JsonString}\n`) }) it('bin2json pretty (stdin)', async () => { const { stdout, stderr } = await execBin('bin2json --pretty', fixture1Bin) assert.strictEqual(stderr, '') assert.strictEqual(stdout, fixture1JsonPrettyString) }) for (const stdin of [true, false]) { if (platform() !== 'win32' || stdin) { // multiline CLI input is hard (impossible?) on Windows it(`diag2bin${stdin ? ' (stdin)' : ''}`, async () => { const { stdout, stderr } = !stdin ? await execBin(`diag2bin '${fixture1DiagnosticString}'`) : await execBin('diag2bin', fixture1DiagnosticString) assert.strictEqual(stderr, '') assert.strictEqual(stdout, fixture1BinString) }) it(`diag2hex${stdin ? ' (stdin)' : ''}`, async () => { const { stdout, stderr } = !stdin ? await execBin(`diag2hex '${fixture1DiagnosticString}'`) : await execBin('diag2hex', fixture1DiagnosticString) assert.strictEqual(stderr, '') assert.strictEqual(stdout, `${fixture1HexString}\n`) }) it(`diag2json${stdin ? ' (stdin)' : ''}`, async () => { const { stdout, stderr } = !stdin ? await execBin(`diag2json '${fixture1DiagnosticString}'`) : await execBin('diag2json', fixture1DiagnosticString) assert.strictEqual(stderr, '') assert.strictEqual(stdout, `${fixture1JsonString}\n`) }) it(`diag2json pretty${stdin ? ' (stdin)' : ''}`, async () => { const { stdout, stderr } = !stdin ? await execBin(`diag2json --pretty '${fixture1DiagnosticString}'`) : await execBin('diag2json --pretty', fixture1DiagnosticString) assert.strictEqual(stderr, '') assert.strictEqual(stdout, fixture1JsonPrettyString) }) } it(`hex2bin${stdin ? ' (stdin)' : ''}`, async () => { const { stdout, stderr } = !stdin ? await execBin(`hex2bin ${fixture1HexString}`) : await execBin('hex2bin', fixture1HexString) assert.strictEqual(stderr, '') assert.strictEqual(stdout, fixture1BinString) }) it(`hex2diag${stdin ? ' (stdin)' : ''}`, async () => { const { stdout, stderr } = !stdin ? await execBin(`hex2diag ${fixture2HexString}`) : await execBin('hex2diag', fixture2HexString) assert.strictEqual(stderr, '') assert.strictEqual(stdout, fixture2DiagnosticString) }) it(`hex2json${stdin ? ' (stdin)' : ''}`, async () => { const { stdout, stderr } = !stdin ? await execBin(`hex2json ${fixture1HexString}`) : await execBin('hex2json', fixture1HexString) assert.strictEqual(stderr, '') assert.strictEqual(stdout, `${fixture1JsonString}\n`) }) it(`hex2json pretty${stdin ? ' (stdin)' : ''}`, async () => { const { stdout, stderr } = !stdin ? await execBin(`hex2json --pretty ${fixture1HexString}`) : await execBin('hex2json --pretty', fixture1HexString) assert.strictEqual(stderr, '') assert.strictEqual(stdout, fixture1JsonPrettyString) }) it(`json2bin${stdin ? ' (stdin)' : ''}`, async () => { const { stdout, stderr } = !stdin ? await execBin('json2bin "{\\"a\\":1,\\"b\\":[2,3],\\"smile\\":\\"😀\\"}"') : await execBin('json2bin', fixture1JsonString) assert.strictEqual(stderr, '') assert.strictEqual(stdout, fixture1BinString) }) it(`json2diag${stdin ? ' (stdin)' : ''}`, async () => { const { stdout, stderr } = !stdin ? await execBin('json2diag "{\\"a\\":1,\\"b\\":[2,3],\\"smile\\":\\"😀\\"}"') : await execBin('json2diag', fixture1JsonString) assert.strictEqual(stderr, '') assert.strictEqual(stdout, fixture1DiagnosticString) }) it(`json2hex${stdin ? ' (stdin)' : ''}`, async () => { const { stdout, stderr } = !stdin ? await execBin(`json2hex "${fixture1JsonString.replace(/"/g, '\\"')}"`) : await execBin('json2hex', fixture1JsonString) assert.strictEqual(stderr, '') assert.strictEqual(stdout, `${fixture1HexString}\n`) }) } // complicated nesting to test indenting algorithm it('diag indenting', async () => { const { stdout, stderr } = await execBin('json2diag', '{"a":[],"b":{},"c":{"a":1,"b":{"a":{"a":{}}}},"d":{"a":{"a":{"a":1},"b":2,"c":[]}},"e":[[[[{"a":{}}]]]],"f":1}') assert.strictEqual(stderr, '') assert.strictEqual(stdout, `a6 # map(6) 61 # string(1) 61 # "a" 80 # array(0) 61 # string(1) 62 # "b" a0 # map(0) 61 # string(1) 63 # "c" a2 # map(2) 61 # string(1) 61 # "a" 01 # uint(1) 61 # string(1) 62 # "b" a1 # map(1) 61 # string(1) 61 # "a" a1 # map(1) 61 # string(1) 61 # "a" a0 # map(0) 61 # string(1) 64 # "d" a1 # map(1) 61 # string(1) 61 # "a" a3 # map(3) 61 # string(1) 61 # "a" a1 # map(1) 61 # string(1) 61 # "a" 01 # uint(1) 61 # string(1) 62 # "b" 02 # uint(2) 61 # string(1) 63 # "c" 80 # array(0) 61 # string(1) 65 # "e" 81 # array(1) 81 # array(1) 81 # array(1) 81 # array(1) a1 # map(1) 61 # string(1) 61 # "a" a0 # map(0) 61 # string(1) 66 # "f" 01 # uint(1) `) }) describe('diag length bytes', () => { it('compact', async () => { const { stdout, stderr } = await execBin('json2diag', '"aaaaaaaaaaaaaaaaaaaaaaa"') assert.strictEqual(stderr, '') assert.strictEqual(stdout, `77 # string(23) 6161616161616161616161616161616161616161616161 # "aaaaaaaaaaaaaaaaaaaaaaa" `) }) it('1-byte', async () => { const { stdout, stderr } = await execBin('json2diag', '"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"') assert.strictEqual(stderr, '') assert.strictEqual(stdout, `78 23 # string(35) 6161616161616161616161616161616161616161616161 # "aaaaaaaaaaaaaaaaaaaaaaa" 616161616161616161616161 # "aaaaaaaaaaaa" `) }) it('2-byte', async () => { const { stdout, stderr } = await execBin('json2diag', '"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"') assert.strictEqual(stderr, '') assert.strictEqual(stdout, `79 0100 # string(256) 6161616161616161616161616161616161616161616161 # "aaaaaaaaaaaaaaaaaaaaaaa" 6161616161616161616161616161616161616161616161 # "aaaaaaaaaaaaaaaaaaaaaaa" 6161616161616161616161616161616161616161616161 # "aaaaaaaaaaaaaaaaaaaaaaa" 6161616161616161616161616161616161616161616161 # "aaaaaaaaaaaaaaaaaaaaaaa" 6161616161616161616161616161616161616161616161 # "aaaaaaaaaaaaaaaaaaaaaaa" 6161616161616161616161616161616161616161616161 # "aaaaaaaaaaaaaaaaaaaaaaa" 6161616161616161616161616161616161616161616161 # "aaaaaaaaaaaaaaaaaaaaaaa" 6161616161616161616161616161616161616161616161 # "aaaaaaaaaaaaaaaaaaaaaaa" 6161616161616161616161616161616161616161616161 # "aaaaaaaaaaaaaaaaaaaaaaa" 6161616161616161616161616161616161616161616161 # "aaaaaaaaaaaaaaaaaaaaaaa" 6161616161616161616161616161616161616161616161 # "aaaaaaaaaaaaaaaaaaaaaaa" 616161 # "aaa" `) }) }) it('diag non-utf8 and non-printable ascii', async () => { const input = '7864f55ff8f12508b63ef2bfeca7557ae90df6311a5ec1631b4a1fa843310bd9c3a710eaace5a1bdd72ad0bfe049771c11e756338bd93865e645f1adec9b9c99ef407fbd4fc6859e7904c5ad7dc9bd10a5cc16973d5b28ec1a6dd43d9f82f9f18c3d03418e35' let { stdout, stderr } = await execBin(`hex2diag ${input}`) assert.strictEqual(stderr, '') assert.strictEqual(stdout, `78 64 # string(86) f55ff8f12508b63ef2bfeca7557ae90df6311a5ec1631b # "õ_øñ%\\x08¶>ò¿ì§Uzé\\x0dö1\\x1a^Ác\\x1b" 4a1fa843310bd9c3a710eaace5a1bdd72ad0bfe049771c # "J\\x1f¨C1\\x0bÙç\\x10ê¬å¡½×*пàIw\\x1c" 11e756338bd93865e645f1adec9b9c99ef407fbd4fc685 # "\\x11çV3\\x8bÙ8eæEñ\\xadì\\x9b\\x9c\\x99ï@\\x7f½OÆ\\x85" 9e7904c5ad7dc9bd10a5cc16973d5b28ec1a6dd43d9f82 # "\\x9ey\\x04Å\\xad}ɽ\\x10¥Ì\\x16\\x97=[(ì\\x1amÔ=\\x9f\\x82" f9f18c3d03418e35 # "ùñ\\x8c=\\x03A\\x8e5" `) // round-trip ;({ stdout, stderr } = await execBin('diag2hex', stdout)) assert.strictEqual(stderr, '') assert.strictEqual(stdout, `${input}\n`) }) })