310 lines
8.7 KiB
JavaScript
310 lines
8.7 KiB
JavaScript
|
#!/usr/bin/env node
|
|||
|
|
|||
|
"use strict";
|
|||
|
|
|||
|
var tape = require('blue-tape');
|
|||
|
|
|||
|
var testhelp = require('./lib/testhelp');
|
|||
|
|
|||
|
global.Zmodem = require('./lib/zmodem');
|
|||
|
|
|||
|
var zdle = new Zmodem.ZDLE( { escape_ctrl_chars: true } );
|
|||
|
|
|||
|
tape('trim_leading_garbage', function(t) {
|
|||
|
var header = Zmodem.Header.build('ZACK');
|
|||
|
|
|||
|
var header_octets = new Map( [
|
|||
|
[ "hex", header.to_hex(), ],
|
|||
|
[ "b16", header.to_binary16(zdle), ],
|
|||
|
[ "b32", header.to_binary32(zdle), ],
|
|||
|
] );
|
|||
|
|
|||
|
var leading_garbage = [
|
|||
|
"",
|
|||
|
" ",
|
|||
|
"\n\n",
|
|||
|
"\r\n\r\n",
|
|||
|
"*",
|
|||
|
"**",
|
|||
|
"*\x18",
|
|||
|
"*\x18D",
|
|||
|
"**\x18",
|
|||
|
];
|
|||
|
|
|||
|
leading_garbage.forEach( (garbage) => {
|
|||
|
let garbage_json = JSON.stringify(garbage);
|
|||
|
let garbage_octets = testhelp.string_to_octets( garbage );
|
|||
|
|
|||
|
for ( let [label, hdr_octets] of header_octets ) {
|
|||
|
var input = garbage_octets.slice(0).concat( hdr_octets );
|
|||
|
var trimmed = Zmodem.Header.trim_leading_garbage(input);
|
|||
|
|
|||
|
t.deepEquals(trimmed, garbage_octets, `${garbage_json} + ${label}: garbage trimmed`);
|
|||
|
t.deepEquals(input, hdr_octets, `… leaving the header`);
|
|||
|
}
|
|||
|
} );
|
|||
|
|
|||
|
//----------------------------------------------------------------------
|
|||
|
|
|||
|
//input, number of bytes trimmed
|
|||
|
var partial_trims = [
|
|||
|
[ "*", 0 ],
|
|||
|
[ "**", 0 ],
|
|||
|
[ "***", 1 ],
|
|||
|
[ "*\x18**", 2 ],
|
|||
|
[ "*\x18*\x18", 2 ],
|
|||
|
[ "*\x18*\x18**", 4 ],
|
|||
|
[ "*\x18*\x18*\x18", 4 ],
|
|||
|
];
|
|||
|
|
|||
|
partial_trims.forEach( (cur) => {
|
|||
|
let [ input, trimmed_count ] = cur;
|
|||
|
|
|||
|
let input_json = JSON.stringify(input);
|
|||
|
|
|||
|
let input_octets = testhelp.string_to_octets(input);
|
|||
|
|
|||
|
let garbage = Zmodem.Header.trim_leading_garbage(input_octets.slice(0));
|
|||
|
|
|||
|
t.deepEquals(
|
|||
|
garbage,
|
|||
|
input_octets.slice(0, trimmed_count),
|
|||
|
`${input_json}: trim first ${trimmed_count} byte(s)`
|
|||
|
);
|
|||
|
} );
|
|||
|
|
|||
|
t.end();
|
|||
|
});
|
|||
|
|
|||
|
//Test that we parse a trailing 0x8a, since we ourselves follow the
|
|||
|
//documentation and put a plain LF (0x0a).
|
|||
|
tape('parse_hex', function(t) {
|
|||
|
var octets = testhelp.string_to_octets( "**\x18B0901020304a57f\x0d\x8a" );
|
|||
|
|
|||
|
var parsed = Zmodem.Header.parse( octets );
|
|||
|
|
|||
|
t.is( parsed[1], 16, 'CRC size' );
|
|||
|
|
|||
|
t.is(
|
|||
|
parsed[0].NAME,
|
|||
|
'ZRPOS',
|
|||
|
'parsed NAME'
|
|||
|
);
|
|||
|
|
|||
|
t.is(
|
|||
|
parsed[0].TYPENUM,
|
|||
|
9,
|
|||
|
'parsed TYPENUM'
|
|||
|
);
|
|||
|
|
|||
|
t.is(
|
|||
|
parsed[0].get_offset(),
|
|||
|
0x04030201, //it’s little-endian
|
|||
|
'parsed offset'
|
|||
|
);
|
|||
|
|
|||
|
t.end();
|
|||
|
} );
|
|||
|
|
|||
|
tape('round-trip, empty headers', function(t) {
|
|||
|
["ZRQINIT", "ZSKIP", "ZABORT", "ZFIN", "ZFERR"].forEach( (n) => {
|
|||
|
var orig = Zmodem.Header.build(n);
|
|||
|
|
|||
|
var hex = orig.to_hex();
|
|||
|
var b16 = orig.to_binary16(zdle);
|
|||
|
var b32 = orig.to_binary32(zdle);
|
|||
|
|
|||
|
var rounds = new Map( [
|
|||
|
[ "to_hex", hex ],
|
|||
|
[ "to_binary16", b16 ],
|
|||
|
[ "to_binary32", b32 ],
|
|||
|
] );
|
|||
|
|
|||
|
for ( const [ enc, h ] of rounds ) {
|
|||
|
let [ parsed, crclen ] = Zmodem.Header.parse(h);
|
|||
|
|
|||
|
t.is( parsed.NAME, orig.NAME, `${n}, ${enc}: NAME` );
|
|||
|
t.is( parsed.TYPENUM, orig.TYPENUM, `${n}, ${enc}: TYPENUM` );
|
|||
|
|
|||
|
//Here’s where we test the CRC length in the response.
|
|||
|
t.is(
|
|||
|
crclen,
|
|||
|
/32/.test(enc) ? 32 : 16,
|
|||
|
`${n}, ${enc}: CRC length`,
|
|||
|
);
|
|||
|
}
|
|||
|
} );
|
|||
|
|
|||
|
t.end();
|
|||
|
} );
|
|||
|
|
|||
|
tape('round-trip, offset headers', function(t) {
|
|||
|
["ZRPOS", "ZDATA", "ZEOF"].forEach( (n) => {
|
|||
|
var orig = Zmodem.Header.build(n, 12345);
|
|||
|
|
|||
|
var hex = orig.to_hex();
|
|||
|
var b16 = orig.to_binary16(zdle);
|
|||
|
var b32 = orig.to_binary32(zdle);
|
|||
|
|
|||
|
var rounds = new Map( [
|
|||
|
[ "to_hex", hex ],
|
|||
|
[ "to_binary16", b16 ],
|
|||
|
[ "to_binary32", b32 ],
|
|||
|
] );
|
|||
|
|
|||
|
for ( const [ enc, h ] of rounds ) {
|
|||
|
//Here’s where we test that parse() leaves in trailing bytes.
|
|||
|
let extra = [99, 99, 99];
|
|||
|
let bytes_with_extra = h.slice().concat(extra);
|
|||
|
|
|||
|
let parsed = Zmodem.Header.parse(bytes_with_extra)[0];
|
|||
|
|
|||
|
t.is( parsed.NAME, orig.NAME, `${n}, ${enc}: NAME` );
|
|||
|
t.is( parsed.TYPENUM, orig.TYPENUM, `${n}, ${enc}: TYPENUM` );
|
|||
|
t.is( parsed.get_offset(), orig.get_offset(), `${n}, ${enc}: get_offset()` );
|
|||
|
|
|||
|
let expected = extra.slice(0);
|
|||
|
if (enc === "to_hex") {
|
|||
|
expected.splice( 0, 0, Zmodem.ZMLIB.XON );
|
|||
|
}
|
|||
|
|
|||
|
t.deepEquals(
|
|||
|
bytes_with_extra,
|
|||
|
expected,
|
|||
|
`${enc}: parse() leaves in trailing bytes`,
|
|||
|
);
|
|||
|
}
|
|||
|
} );
|
|||
|
|
|||
|
t.end();
|
|||
|
} );
|
|||
|
|
|||
|
tape('round-trip, ZSINIT', function(t) {
|
|||
|
var opts = [
|
|||
|
[],
|
|||
|
["ESCCTL"],
|
|||
|
];
|
|||
|
|
|||
|
opts.forEach( (args) => {
|
|||
|
var orig = Zmodem.Header.build("ZSINIT", args);
|
|||
|
|
|||
|
var hex = orig.to_hex();
|
|||
|
var b16 = orig.to_binary16(zdle);
|
|||
|
var b32 = orig.to_binary32(zdle);
|
|||
|
|
|||
|
var rounds = new Map( [
|
|||
|
[ "to_hex", hex ],
|
|||
|
[ "to_binary16", b16 ],
|
|||
|
[ "to_binary32", b32 ],
|
|||
|
] );
|
|||
|
|
|||
|
var args_str = JSON.stringify(args);
|
|||
|
|
|||
|
for ( const [ enc, h ] of rounds ) {
|
|||
|
let parsed = Zmodem.Header.parse(h)[0];
|
|||
|
|
|||
|
t.is( parsed.NAME, orig.NAME, `opts ${args_str}: ${enc}: NAME` );
|
|||
|
t.is( parsed.TYPENUM, orig.TYPENUM, `opts ${args_str}: ${enc}: TYPENUM` );
|
|||
|
|
|||
|
t.is( parsed.escape_ctrl_chars(), orig.escape_ctrl_chars(), `opts ${args_str}: ${enc}: escape_ctrl_chars()` );
|
|||
|
t.is( parsed.escape_8th_bit(), orig.escape_8th_bit(), `opts ${args_str}: ${enc}: escape_8th_bit()` );
|
|||
|
}
|
|||
|
} );
|
|||
|
|
|||
|
t.end();
|
|||
|
} );
|
|||
|
|
|||
|
tape('round-trip, ZRINIT', function(t) {
|
|||
|
var opts = [];
|
|||
|
|
|||
|
[ [], ["CANFDX"] ].forEach( (canfdx) => {
|
|||
|
[ [], ["CANOVIO"] ].forEach( (canovio) => {
|
|||
|
[ [], ["CANBRK"] ].forEach( (canbrk) => {
|
|||
|
[ [], ["CANFC32"] ].forEach( (canfc32) => {
|
|||
|
[ [], ["ESCCTL"] ].forEach( (escctl) => {
|
|||
|
opts.push( [
|
|||
|
...canfdx,
|
|||
|
...canovio,
|
|||
|
...canbrk,
|
|||
|
...canfc32,
|
|||
|
...escctl,
|
|||
|
] );
|
|||
|
} );
|
|||
|
} );
|
|||
|
} );
|
|||
|
} );
|
|||
|
} );
|
|||
|
|
|||
|
opts.forEach( (args) => {
|
|||
|
var orig = Zmodem.Header.build("ZRINIT", args);
|
|||
|
|
|||
|
var hex = orig.to_hex();
|
|||
|
var b16 = orig.to_binary16(zdle);
|
|||
|
var b32 = orig.to_binary32(zdle);
|
|||
|
|
|||
|
var rounds = new Map( [
|
|||
|
[ "to_hex", hex ],
|
|||
|
[ "to_binary16", b16 ],
|
|||
|
[ "to_binary32", b32 ],
|
|||
|
] );
|
|||
|
|
|||
|
var args_str = JSON.stringify(args);
|
|||
|
|
|||
|
for ( const [ enc, h ] of rounds ) {
|
|||
|
let parsed = Zmodem.Header.parse(h)[0];
|
|||
|
|
|||
|
t.is( parsed.NAME, orig.NAME, `opts ${args_str}: ${enc}: NAME` );
|
|||
|
t.is( parsed.TYPENUM, orig.TYPENUM, `opts ${args_str}: ${enc}: TYPENUM` );
|
|||
|
|
|||
|
t.is( parsed.can_full_duplex(), orig.can_full_duplex(), `opts ${args_str}: ${enc}: can_full_duplex()` );
|
|||
|
t.is( parsed.can_overlap_io(), orig.can_overlap_io(), `opts ${args_str}: ${enc}: can_overlap_io()` );
|
|||
|
t.is( parsed.can_break(), orig.can_break(), `opts ${args_str}: ${enc}: can_break()` );
|
|||
|
t.is( parsed.can_fcs_32(), orig.can_fcs_32(), `opts ${args_str}: ${enc}: can_fcs_32()` );
|
|||
|
t.is( parsed.escape_ctrl_chars(), orig.escape_ctrl_chars(), `opts ${args_str}: ${enc}: escape_ctrl_chars()` );
|
|||
|
t.is( parsed.escape_8th_bit(), orig.escape_8th_bit(), `opts ${args_str}: ${enc}: escape_8th_bit()` );
|
|||
|
}
|
|||
|
} );
|
|||
|
|
|||
|
t.end();
|
|||
|
} );
|
|||
|
|
|||
|
tape('hex_final_XON', function(t) {
|
|||
|
var hex_ZFIN = Zmodem.Header.build("ZFIN").to_hex();
|
|||
|
|
|||
|
t.notEquals(
|
|||
|
hex_ZFIN.slice(-1)[0],
|
|||
|
Zmodem.ZMLIB.XON,
|
|||
|
'ZFIN hex does NOT end with XON',
|
|||
|
);
|
|||
|
|
|||
|
var hex_ZACK = Zmodem.Header.build("ZACK").to_hex();
|
|||
|
|
|||
|
t.notEquals(
|
|||
|
hex_ZACK.slice(-1)[0],
|
|||
|
Zmodem.ZMLIB.XON,
|
|||
|
'ZACK hex does NOT end with XON',
|
|||
|
);
|
|||
|
|
|||
|
var headers = [
|
|||
|
"ZRQINIT",
|
|||
|
Zmodem.Header.build("ZRINIT", []),
|
|||
|
Zmodem.Header.build("ZSINIT", []),
|
|||
|
"ZRPOS",
|
|||
|
"ZABORT",
|
|||
|
"ZFERR",
|
|||
|
];
|
|||
|
|
|||
|
//These are the only headers we expect to send as hex … right?
|
|||
|
headers.forEach( hdr => {
|
|||
|
if (typeof hdr === "string") hdr = Zmodem.Header.build(hdr);
|
|||
|
|
|||
|
t.is(
|
|||
|
hdr.to_hex().slice(-1)[0],
|
|||
|
Zmodem.ZMLIB.XON,
|
|||
|
`${hdr.NAME} hex ends with XON`
|
|||
|
);
|
|||
|
} );
|
|||
|
|
|||
|
t.end();
|
|||
|
} );
|