296 lines
8.5 KiB
JavaScript
296 lines
8.5 KiB
JavaScript
|
#!/usr/bin/env node
|
|||
|
|
|||
|
"use strict";
|
|||
|
|
|||
|
const tape = require('blue-tape');
|
|||
|
|
|||
|
const SZ_PATH = require('which').sync('sz', {nothrow: true});
|
|||
|
|
|||
|
if (!SZ_PATH) {
|
|||
|
tape.only('SKIP: no “sz” in PATH!', (t) => {
|
|||
|
t.end();
|
|||
|
});
|
|||
|
}
|
|||
|
|
|||
|
const spawn = require('child_process').spawn;
|
|||
|
|
|||
|
var helper = require('./lib/testhelp');
|
|||
|
|
|||
|
Object.assign(
|
|||
|
global,
|
|||
|
{
|
|||
|
Zmodem: require('./lib/zmodem'),
|
|||
|
}
|
|||
|
);
|
|||
|
|
|||
|
var FILE1 = helper.make_temp_file(10 * 1024 * 1024); //10 MiB
|
|||
|
|
|||
|
function _test_steps(t, sz_args, steps) {
|
|||
|
return helper.exec_lrzsz_steps( t, SZ_PATH, sz_args, steps );
|
|||
|
}
|
|||
|
|
|||
|
tape('abort() after ZRQINIT', (t) => {
|
|||
|
return _test_steps( t, [FILE1], [
|
|||
|
(zsession, child) => {
|
|||
|
zsession.abort();
|
|||
|
return true;
|
|||
|
},
|
|||
|
] ).then( (inputs) => {
|
|||
|
//console.log("inputs", inputs);
|
|||
|
|
|||
|
var str = String.fromCharCode.apply( String, inputs[ inputs.length - 1 ]);
|
|||
|
t.ok(
|
|||
|
str.match(/\x18\x18\x18\x18\x18/),
|
|||
|
'abort() right after receipt of ZRQINIT',
|
|||
|
);
|
|||
|
} );
|
|||
|
});
|
|||
|
|
|||
|
tape('abort() after ZFILE', (t) => {
|
|||
|
return _test_steps( t, [FILE1], [
|
|||
|
(zsession) => {
|
|||
|
zsession.start();
|
|||
|
return true;
|
|||
|
},
|
|||
|
(zsession) => {
|
|||
|
zsession.abort();
|
|||
|
return true;
|
|||
|
},
|
|||
|
] ).then( (inputs) => {
|
|||
|
//console.log("inputs", inputs);
|
|||
|
|
|||
|
var str = String.fromCharCode.apply( String, inputs[ inputs.length - 1 ]);
|
|||
|
t.ok(
|
|||
|
str.match(/\x18\x18\x18\x18\x18/),
|
|||
|
'abort() right after receipt of ZFILE',
|
|||
|
);
|
|||
|
} );
|
|||
|
});
|
|||
|
|
|||
|
//NB: This test is not unlikely to flap since it depends
|
|||
|
//on sz reading the abort sequence prior to finishing its read
|
|||
|
//of the file.
|
|||
|
tape('abort() during download', { timeout: 30000 }, (t) => {
|
|||
|
var child_pms = _test_steps( t, [FILE1], [
|
|||
|
(zsession) => {
|
|||
|
zsession.on("offer", (offer) => offer.accept() );
|
|||
|
zsession.start();
|
|||
|
return true;
|
|||
|
},
|
|||
|
(zsession) => {
|
|||
|
zsession.abort();
|
|||
|
return true;
|
|||
|
},
|
|||
|
] );
|
|||
|
|
|||
|
return child_pms.then( (inputs) => {
|
|||
|
t.notEquals( inputs, undefined, 'abort() during download ends the transmission' );
|
|||
|
|
|||
|
t.ok(
|
|||
|
inputs.every( function(bytes) {
|
|||
|
var str = String.fromCharCode.apply( String, bytes );
|
|||
|
return !/THE_END/.test(str);
|
|||
|
} ),
|
|||
|
"the end of the file was not sent",
|
|||
|
);
|
|||
|
} );
|
|||
|
});
|
|||
|
|
|||
|
//This only works because we use CRC32 to receive. CRC16 in lsz has a
|
|||
|
//buffer overflow bug, fixed here:
|
|||
|
//
|
|||
|
// https://github.com/gooselinux/lrzsz/blob/master/lrzsz-0.12.20.patch
|
|||
|
//
|
|||
|
tape('skip() during download', { timeout: 30000 }, (t) => {
|
|||
|
var filenames = [FILE1, helper.make_temp_file(12345678)];
|
|||
|
//filenames = ["-vvvvvvvvvvvvv", FILE1, _make_temp_file()];
|
|||
|
|
|||
|
var started, second_offer;
|
|||
|
|
|||
|
return _test_steps( t, filenames, [
|
|||
|
(zsession) => {
|
|||
|
if (!started) {
|
|||
|
function offer_taker(offer) {
|
|||
|
offer.accept();
|
|||
|
offer.skip();
|
|||
|
zsession.off("offer", offer_taker);
|
|||
|
zsession.on("offer", (offer2) => {
|
|||
|
second_offer = offer2;
|
|||
|
offer2.skip();
|
|||
|
});
|
|||
|
}
|
|||
|
zsession.on("offer", offer_taker);
|
|||
|
zsession.start();
|
|||
|
started = true;
|
|||
|
}
|
|||
|
//return true;
|
|||
|
},
|
|||
|
] ).then( (inputs) => {
|
|||
|
var never_end = inputs.every( function(bytes) {
|
|||
|
var str = String.fromCharCode.apply( String, bytes );
|
|||
|
return !/THE_END/.test(str);
|
|||
|
} );
|
|||
|
|
|||
|
// This is race-prone.
|
|||
|
//t.ok( never_end, "the end of a file is never sent" );
|
|||
|
|
|||
|
t.ok( !!second_offer, "we got a 2nd offer after the first" );
|
|||
|
} );
|
|||
|
});
|
|||
|
|
|||
|
tape('skip() - immediately - at end of download', { timeout: 30000 }, (t) => {
|
|||
|
var filenames = [helper.make_temp_file(123)];
|
|||
|
|
|||
|
var started;
|
|||
|
|
|||
|
return _test_steps( t, filenames, [
|
|||
|
(zsession) => {
|
|||
|
if (!started) {
|
|||
|
function offer_taker(offer) {
|
|||
|
offer.accept();
|
|||
|
offer.skip();
|
|||
|
}
|
|||
|
zsession.on("offer", offer_taker);
|
|||
|
zsession.start();
|
|||
|
|
|||
|
started = true;
|
|||
|
}
|
|||
|
},
|
|||
|
] );
|
|||
|
});
|
|||
|
|
|||
|
// Verify a skip() that happens after a transfer is complete.
|
|||
|
// There are no assertions here.
|
|||
|
tape('skip() - after a parse - at end of download', { timeout: 30000 }, (t) => {
|
|||
|
var filenames = [helper.make_temp_file(123)];
|
|||
|
|
|||
|
var the_offer, started, skipped, completed;
|
|||
|
|
|||
|
return _test_steps( t, filenames, [
|
|||
|
(zsession) => {
|
|||
|
if (!started) {
|
|||
|
function offer_taker(offer) {
|
|||
|
the_offer = offer;
|
|||
|
var promise = the_offer.accept();
|
|||
|
promise.then( () => {
|
|||
|
completed = 1;
|
|||
|
} );
|
|||
|
}
|
|||
|
zsession.on("offer", offer_taker);
|
|||
|
zsession.start();
|
|||
|
started = true;
|
|||
|
}
|
|||
|
|
|||
|
return the_offer;
|
|||
|
},
|
|||
|
() => {
|
|||
|
if (!skipped && !completed) {
|
|||
|
the_offer.skip();
|
|||
|
skipped = true;
|
|||
|
}
|
|||
|
},
|
|||
|
] );
|
|||
|
});
|
|||
|
|
|||
|
var happy_filenames = [
|
|||
|
helper.make_temp_file(5),
|
|||
|
helper.make_temp_file(3),
|
|||
|
helper.make_temp_file(1),
|
|||
|
helper.make_empty_temp_file(),
|
|||
|
];
|
|||
|
|
|||
|
tape('happy-path: single batch', { timeout: 30000 }, (t) => {
|
|||
|
var started, the_offer;
|
|||
|
|
|||
|
var args = happy_filenames;
|
|||
|
|
|||
|
var buffers = [];
|
|||
|
|
|||
|
var child_pms = _test_steps( t, args, [
|
|||
|
(zsession) => {
|
|||
|
if (!started) {
|
|||
|
function offer_taker(offer) {
|
|||
|
the_offer = offer;
|
|||
|
the_offer.accept( { on_input: "spool_array" } ).then( (byte_lists) => {
|
|||
|
var flat = [].concat.apply([], byte_lists);
|
|||
|
var str = String.fromCharCode.apply( String, flat );
|
|||
|
buffers.push(str);
|
|||
|
} );
|
|||
|
}
|
|||
|
zsession.on("offer", offer_taker);
|
|||
|
zsession.start();
|
|||
|
started = true;
|
|||
|
}
|
|||
|
|
|||
|
return false;
|
|||
|
},
|
|||
|
] );
|
|||
|
|
|||
|
return child_pms.then( (inputs) => {
|
|||
|
t.equals( buffers[0], "xxxxx=THE_END", '5-byte transfer plus end' );
|
|||
|
t.equals( buffers[1], "xxx=THE_END", '3-byte transfer plus end' );
|
|||
|
t.equals( buffers[2], "x=THE_END", '1-byte transfer plus end' );
|
|||
|
t.equals( buffers[3], "", 'empty transfer plus end' );
|
|||
|
} );
|
|||
|
});
|
|||
|
|
|||
|
tape('happy-path: individual transfers', { timeout: 30000 }, (t) => {
|
|||
|
var promises = happy_filenames.map( (fn) => {
|
|||
|
var str;
|
|||
|
|
|||
|
var started;
|
|||
|
|
|||
|
var child_pms = _test_steps( t, [fn], [
|
|||
|
(zsession) => {
|
|||
|
if (!started) {
|
|||
|
function offer_taker(offer) {
|
|||
|
offer.accept( { on_input: "spool_array" } ).then( (byte_lists) => {
|
|||
|
var flat = [].concat.apply([], byte_lists);
|
|||
|
str = String.fromCharCode.apply( String, flat );
|
|||
|
} );
|
|||
|
}
|
|||
|
zsession.on("offer", offer_taker);
|
|||
|
zsession.start();
|
|||
|
started = true;
|
|||
|
}
|
|||
|
|
|||
|
return false;
|
|||
|
},
|
|||
|
] );
|
|||
|
|
|||
|
return child_pms.then( () => str );
|
|||
|
} );
|
|||
|
|
|||
|
return Promise.all(promises).then( (strs) => {
|
|||
|
t.equals( strs[0], "xxxxx=THE_END", '5-byte transfer plus end' );
|
|||
|
t.equals( strs[1], "xxx=THE_END", '3-byte transfer plus end' );
|
|||
|
t.equals( strs[2], "x=THE_END", '1-byte transfer plus end' );
|
|||
|
t.equals( strs[3], "", 'empty transfer plus end' );
|
|||
|
} );
|
|||
|
});
|
|||
|
|
|||
|
//This doesn’t work because we automatically send ZFIN once we receive it,
|
|||
|
//which prompts the child to finish up.
|
|||
|
tape.skip("abort() after ZEOF", (t) => {
|
|||
|
var received;
|
|||
|
|
|||
|
return _test_steps( t, [FILE1], [
|
|||
|
(zsession) => {
|
|||
|
zsession.on("offer", (offer) => {
|
|||
|
offer.accept().then( () => { received = true } );
|
|||
|
} );
|
|||
|
zsession.start();
|
|||
|
return true;
|
|||
|
},
|
|||
|
(zsession) => {
|
|||
|
if (received) {
|
|||
|
zsession.abort();
|
|||
|
return true;
|
|||
|
}
|
|||
|
},
|
|||
|
] ).then( (inputs) => {
|
|||
|
var str = String.fromCharCode.apply( String, inputs[ inputs.length - 1 ]);
|
|||
|
t.is( str, "OO", "successful close despite abort" );
|
|||
|
} );
|
|||
|
});
|