#!/usr/bin/env node "use strict"; const test = require('tape'); const helper = require('./lib/testhelp'); global.Zmodem = require('./lib/zmodem'); var ZSession = Zmodem.Session; var receiver, sender, sender_promise, received_file; var offer; function wait(seconds) { return new Promise( resolve => setTimeout(_ => resolve("theValue"), 1000 * seconds) ); } function _init(async) { sender = null; receiver = new Zmodem.Session.Receive(); /* receiver.on("receive", function(hdr) { console.log("Receiver input", hdr); } ); receiver.on("offer", function(my_offer) { //console.log("RECEIVED OFFER (window.offer)", my_offer); offer = my_offer; }); */ var resolver; sender_promise = new Promise( (res, rej) => { resolver = res; } ); function receiver_sender(bytes_arr) { //console.log("receiver sending", String.fromCharCode.apply(String, bytes_arr), bytes_arr); if (sender) { var consumer = () => { sender.consume(bytes_arr); }; if (async) { wait(0.5).then(consumer); } else consumer(); } else { var hdr = Zmodem.Header.parse(bytes_arr)[0]; sender = new Zmodem.Session.Send(hdr); resolver(sender); sender.set_sender( function(bytes_arr) { var consumer = () => { receiver.consume(bytes_arr); }; if (async) { wait(0.5).then(consumer); } else consumer(); } ); /* sender.on("receive", function(hdr) { console.log("Sender input", hdr); } ); */ } } receiver.set_sender(receiver_sender); } test('Sender receives extra ZRPOS', (t) => { _init(); var zrinit = Zmodem.Header.build("ZRINIT", ["CANFDX", "CANOVIO", "ESCCTL"]); var mysender = new Zmodem.Session.Send(zrinit); var zrpos = Zmodem.Header.build("ZRPOS", 12345); var err; try { mysender.consume(zrpos.to_hex()); } catch(e) { err = e; } t.match(err.toString(), /header/, "error as expected"); t.match(err.toString(), /ZRPOS/, "error as expected"); return Promise.resolve(); } ); test('Offer events', (t) => { _init(); var inputs = []; var completed = false; var r_pms = receiver.start().then( (offer) => { t.deepEquals( offer.get_details(), { name: "my file", size: 32, mode: null, mtime: null, serial: null, files_remaining: null, bytes_remaining: null, }, 'get_details() returns expected values' ); offer.on("input", (payload) => { inputs.push( { offset: offer.get_offset(), payload: payload, } ); } ); offer.on("complete", () => { completed = true }); return offer.accept(); } ); var s_pms = sender.send_offer( { name: "my file", size: 32 } ).then( (sender_xfer) => { sender_xfer.send( [1, 2, 3] ); sender_xfer.send( [4, 5, 6, 7] ); sender_xfer.end( [8, 9] ).then( () => { return sender.close(); } ); } ); return Promise.all( [ r_pms, s_pms ] ).then( () => { t.deepEquals( inputs, [ { payload: [1, 2, 3], offset: 3, }, { payload: [4, 5, 6, 7], offset: 7, }, { payload: [8, 9], offset: 9, }, ], 'Offer “input” events', ); t.ok( completed, 'Offer “complete” event' ); } ); } ); test('receive one, promises', (t) => { _init(); var r_pms = receiver.start().then( (offer) => { t.deepEquals( offer.get_details(), { name: "my file", size: 32, mode: null, mtime: null, serial: null, files_remaining: null, bytes_remaining: null, }, 'get_details() returns expected values' ); return offer.accept(); } ); //r_pms.then( () => { console.log("RECEIVER DONE") } ); var s_pms = sender.send_offer( { name: "my file", size: 32 } ).then( (sender_xfer) => { sender_xfer.end( [12, 23, 34] ).then( () => { return sender.close(); } ); } ); return Promise.all( [ r_pms, s_pms ] ); } ); test('receive one, events', (t) => { _init(); var content = [ 1,2,3,4,5,6,7,8,9,2,3,5,1,5,33,2,23,7 ]; var now_epoch = Math.floor(Date.now() / 1000); receiver.on("offer", (offer) => { t.deepEquals( offer.get_details(), { name: "my file", size: content.length, mode: parseInt("100644", 8), mtime: new Date( now_epoch * 1000 ), serial: null, files_remaining: null, bytes_remaining: null, }, 'get_details() returns expected values' ); offer.accept(); } ); receiver.start(); return sender.send_offer( { name: "my file", size: content.length, mtime: now_epoch, mode: parseInt("0644", 8), } ).then( (sender_xfer) => { sender_xfer.end(content).then( sender.close.bind(sender) ); } ); } ); test('skip one, receive the next', (t) => { _init(); var r_pms = receiver.start().then( (offer) => { //console.log("first offer", offer); t.equals( offer.get_details().name, "my file", "first file’s name" ); var next_pms = offer.skip(); //console.log("next", next_pms); return next_pms; } ).then( (offer) => { t.equals( offer.get_details().name, "file 2", "second file’s name" ); return offer.skip(); } ); var s_pms = sender.send_offer( { name: "my file" } ).then( (sender_xfer) => { t.ok( !sender_xfer, "skip() -> sender sees no transfer object" ); return sender.send_offer( { name: "file 2" } ); } ).then( (xfer) => { t.ok( !xfer, "2nd skip() -> sender sees no transfer object" ); return sender.close(); } ); return Promise.all( [ r_pms, s_pms ] ); } ); test('abort mid-download', (t) => { _init(); var transferred_bytes = []; var aborted; var r_pms = receiver.start().then( (offer) => { offer.on("input", (payload) => { [].push.apply(transferred_bytes, payload); if (aborted) throw "already aborted!"; aborted = true; receiver.abort(); }); return offer.accept(); } ); var s_pms = sender.send_offer( { name: "my file" } ).then( (xfer) => { xfer.send( [1, 2, 3] ); xfer.end( [99, 99, 99] ); //should never get here } ); return Promise.all( [r_pms, s_pms] ).catch( (err) => { t.ok( err.message.match('abort'), 'error message is about abort' ); } ).then( () => { t.deepEquals( transferred_bytes, [1, 2, 3], 'abort() stopped us from sending more', ); } ); } );