From 70e3fc16addf0517ff8ef8ef1e9bcd6dee2a440f Mon Sep 17 00:00:00 2001 From: Alain94W Date: Sun, 8 Dec 2019 19:09:25 +0100 Subject: [PATCH 01/26] Added backup functions, device status (recovery,device,fastboot), folder or partition size info --- src/adb.js | 166 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 166 insertions(+) diff --git a/src/adb.js b/src/adb.js index ef4d66a..23ea994 100644 --- a/src/adb.js +++ b/src/adb.js @@ -32,11 +32,23 @@ const DEFAULT_EXEC = (args, callback) => { callback ); }; + +// AW : Allow execution of custom command, not only adb +const DEFAULT_CUTOMEXEC = (args, callback) => { + exec( + [""].concat(args).join(" "), + { options: { maxBuffer: 1024 * 1024 * 2 } }, + callback + ); +}; + const DEFAULT_LOG = console.log; const DEFAULT_PORT = 5037; class Adb { constructor(options) { + // AW : custom command execution + this.execCustom = DEFAULT_CUTOMEXEC; this.exec = DEFAULT_EXEC; this.log = DEFAULT_LOG; this.port = DEFAULT_PORT; @@ -82,6 +94,32 @@ class Adb { }); } + // AW : Cutsom Command Execution + execCommandCustom(args) { + var _this = this; + return new Promise(function(resolve, reject) { + _this.execCustom([""].concat(args), (error, stdout, stderr) => { + if (error) { + reject( + common.handleError( + error, + stdout, + stderr ? stderr.trim() : undefined + ) + ); + } else if (stdout) { + if (stdout.includes("no permissions")) { + reject("no permissions"); + } else { + resolve(stdout.trim()); + } + } else { + resolve(); + } + }); + }); + } + // Kill all adb servers and start a new one to rule them all startServer() { var _this = this; @@ -487,6 +525,134 @@ class Adb { ); }); } + + // AW : return the file size of a complete folder + getFileSize(file) { + var _this = this; + return new Promise(function(resolve, reject) { + _this.getAdbState().then((stdout)=>{ + if (stdout == 1) { // Recovery + _this.shell('du -shk '+file).then( (stdout)=>{ + stdout = parseFloat(stdout); + _this.log("FileSize is "+stdout+ " Ko"); + resolve(stdout); + }).catch (reject); + } + else if (stdout == 2) { // device (type : used, size) + _this.shell('df -hBK '+file+' --output=used|tail -n1').then( (stdout)=>{ + stdout = parseFloat(stdout); + _this.log("FileSize is "+stdout+ " Ko"); + resolve(stdout); + }).catch (reject); + } + }); + }).catch (e=>{_this.log(e); reject;}); + } + + // AW : Return the status of the device (0 : bootloader, 1 : recovery, 2 : device ...) + getAdbState() { + var _this = this; + return new Promise(function(resolve, reject) { + _this.execCommand(["get-state"]).then( (stdout)=>{ + _this.log("Device state is "+stdout); + switch (stdout) { + case "device" : resolve(2); + case "bootloader" : resolve(0); + case "recovery" : resolve(1); + default : resolve(3); // Unknown + } + }).catch (e=>{ + // Try with recovery command + _this.log("Unknown error :"+e); + reject + }); + }); + } + +// AW : Backup file "srcfile" from the phone to "destfile" localy +// TO-DO : This function should be moved to devices.js + backup(srcfile,destfile, adbpath, progress) { + var _this = this; + _this.log("Backup Function Entry, will backup "+srcfile+" to "+destfile+" adbpath:"+adbpath); + return new Promise(function(resolve, reject) { + _this.fileSize = 9999999999999; // Init to file size to a very high value while waiting for the real size. + // Get file size + _this.getFileSize(srcfile).then(stdout=>{ + _this.log("Returned FileSize is "+stdout+ " Ko"); + _this.fileSize = stdout; + // Creating pipe + _this.shell("mkfifo /backup.pipe").then((stdout) => { + _this.log("Pipe created !"); + var lastSize = 0; + var progressInterval = setInterval(() => { + _this.execCommandCustom(["stat", "-t", destfile]).then(stat => { + progress ((lastSize / _this.fileSize)*100); + lastSize = eval(stat.split(" ")[1])/1024; + //_this.log("pushing: " + lastSize+"/"+_this.fileSize); + }).catch(e => { clearInterval(progressInterval);_this.log("failed to stat: " + e); + }); + }, 1000); + + // Start the backup + _this.log("Starting Backup..."); + _this.execCommand(["exec-out 'tar -cvp ",srcfile," 2>/backup.pipe' | dd of="+destfile, + " & ",(adbpath+"/adb -P"),_this.port, + " shell cat /backup.pipe", process.platform == "win32" ? ' | findstr /v "%]"' : ' | grep -v "%]"']).then( (error, stdout, stderr) => { + _this.log("Backup Ended"); + clearInterval(progressInterval); + _this.shell("rm /backup.pipe").then( ()=> { + _this.log("Pipe released."); + resolve(); // Everything's Fine ! + }).catch(e=>{utils.log.warn; reject(e+" Unable to delete the pipe "+e.length);}); // Pipe Deletion + }).catch(e=>{utils.log.warn; reject(e+" Unable to backuping the device "+e.length);}); // Backup start + }).catch(e=>{utils.log.warn; reject(e+" Pipe creation failed "+e.length);}); // Pipe Creation + }).catch(e=>{utils.log.warn; reject(e+" Unable to get the partition's filesize "+e.length);}); // Get file size + }); + } + + +// AW : Restore file "srcfile" from the computer to "destfile" on the phone +// TO-DO : This function should be moved to devices.js + restore(srcfile,destfile, adbpath, progress) { + var _this = this; + _this.log("Restore Function Entry, will restore "+srcfile+" on "+destfile+" adbpath:"+adbpath); + return new Promise(function(resolve, reject) { + _this.fileSize = 9999999999999; // Init to file size to a very high value while waiting for the real size. + // Get file size + _this.getFileSize(srcfile).then(stdout=>{ + _this.log("Returned FileSize is "+stdout+ " Ko"); + _this.fileSize = stdout; + // Creating pipe + _this.shell("mkfifo /restore.pipe").then((stdout) => { + _this.log("Pipe created !"); + var lastSize = 0; + var progressInterval = setInterval(() => { + _this.execCommandCustom(["stat", "-t", destfile]).then(stat => { + progress ((lastSize / _this.fileSize)*100); + lastSize = eval(stat.split(" ")[1])/1024; + //_this.log("pushing: " + lastSize+"/"+_this.fileSize); + }).catch(e => { clearInterval(progressInterval);_this.log("failed to stat: " + e); + }); + }, 1000); + + // Start the restoration + _this.log("Starting Restore..."); //adb push user.tar /resto.pipe & adb shell 'cd /; cat /resto.pipe | tar -xv' + _this.execCommand(["push",srcfile,"/resto.pipe &", + (adbpath+"/adb -P"),_this.port, + " shell 'cd /; cat /resto.pipe | tar -xv'"]).then( (error, stdout, stderr) => { + _this.log("Restore Ended"); + clearInterval(progressInterval); + _this.shell("rm /resotre.pipe").then( ()=> { + _this.log("Pipe released."); + resolve(); // Everything's Fine ! + }).catch(e=>{utils.log.warn; reject("Unable to delete the pipe "+e);}); // Pipe Deletion + }).catch(e=>{utils.log.warn; reject("Unable to restore the backup "+e);}); // Backup start + }).catch(e=>{utils.log.warn; reject("Pipe creation failed "+e);}); // Pipe Creation + }).catch(e=>{utils.log.warn; reject("Unable to get the partition's filesize "+e);}); // Get file size + }); + } + + } module.exports = Adb; From 15bc51b01d286b2f6023a726572ee6c92466251f Mon Sep 17 00:00:00 2001 From: Alain94W Date: Mon, 9 Dec 2019 17:53:04 +0100 Subject: [PATCH 02/26] modifying backup and restore functions according to the remarks of NeoTheThird, removed customcommandexec --- src/adb.js | 333 ++++++++++++++++++++++++++++++----------------------- 1 file changed, 191 insertions(+), 142 deletions(-) diff --git a/src/adb.js b/src/adb.js index 23ea994..6733ceb 100644 --- a/src/adb.js +++ b/src/adb.js @@ -33,22 +33,11 @@ const DEFAULT_EXEC = (args, callback) => { ); }; -// AW : Allow execution of custom command, not only adb -const DEFAULT_CUTOMEXEC = (args, callback) => { - exec( - [""].concat(args).join(" "), - { options: { maxBuffer: 1024 * 1024 * 2 } }, - callback - ); -}; - const DEFAULT_LOG = console.log; const DEFAULT_PORT = 5037; class Adb { constructor(options) { - // AW : custom command execution - this.execCustom = DEFAULT_CUTOMEXEC; this.exec = DEFAULT_EXEC; this.log = DEFAULT_LOG; this.port = DEFAULT_PORT; @@ -94,32 +83,6 @@ class Adb { }); } - // AW : Cutsom Command Execution - execCommandCustom(args) { - var _this = this; - return new Promise(function(resolve, reject) { - _this.execCustom([""].concat(args), (error, stdout, stderr) => { - if (error) { - reject( - common.handleError( - error, - stdout, - stderr ? stderr.trim() : undefined - ) - ); - } else if (stdout) { - if (stdout.includes("no permissions")) { - reject("no permissions"); - } else { - resolve(stdout.trim()); - } - } else { - resolve(); - } - }); - }); - } - // Kill all adb servers and start a new one to rule them all startServer() { var _this = this; @@ -527,132 +490,218 @@ class Adb { } // AW : return the file size of a complete folder - getFileSize(file) { - var _this = this; - return new Promise(function(resolve, reject) { - _this.getAdbState().then((stdout)=>{ - if (stdout == 1) { // Recovery - _this.shell('du -shk '+file).then( (stdout)=>{ - stdout = parseFloat(stdout); - _this.log("FileSize is "+stdout+ " Ko"); - resolve(stdout); - }).catch (reject); - } - else if (stdout == 2) { // device (type : used, size) - _this.shell('df -hBK '+file+' --output=used|tail -n1').then( (stdout)=>{ - stdout = parseFloat(stdout); - _this.log("FileSize is "+stdout+ " Ko"); - resolve(stdout); - }).catch (reject); - } - }); - }).catch (e=>{_this.log(e); reject;}); - } + getFileSize(file) { + var _this = this; + return new Promise(function(resolve, reject) { + _this.getAdbState().then(stdout => { + if (stdout == 1) { + // Recovery + _this + .shell("du -shk " + file) + .then(stdout => { + stdout = parseFloat(stdout); + _this.log("FileSize is " + stdout + " Ko"); + resolve(stdout); + }) + .catch(reject); + } else if (stdout == 2) { + // device (type : used, size) + _this + .shell("df -hBK " + file + " --output=used|tail -n1") + .then(stdout => { + stdout = parseFloat(stdout); + _this.log("FileSize is " + stdout + " Ko"); + resolve(stdout); + }) + .catch(reject); + } + }); + }).catch(e => { + _this.log(e); + reject; + }); + } // AW : Return the status of the device (0 : bootloader, 1 : recovery, 2 : device ...) - getAdbState() { - var _this = this; - return new Promise(function(resolve, reject) { - _this.execCommand(["get-state"]).then( (stdout)=>{ - _this.log("Device state is "+stdout); - switch (stdout) { - case "device" : resolve(2); - case "bootloader" : resolve(0); - case "recovery" : resolve(1); - default : resolve(3); // Unknown - } - }).catch (e=>{ + getAdbState() { + var _this = this; + return new Promise(function(resolve, reject) { + _this + .execCommand(["get-state"]) + .then(stdout => { + _this.log("Device state is " + stdout); + switch (stdout) { + case "device": + resolve(2); + case "bootloader": + resolve(0); + case "recovery": + resolve(1); + default: + resolve(3); // Unknown + } + }) + .catch(e => { // Try with recovery command - _this.log("Unknown error :"+e); - reject - }); - }); - } - -// AW : Backup file "srcfile" from the phone to "destfile" localy -// TO-DO : This function should be moved to devices.js - backup(srcfile,destfile, adbpath, progress) { + _this.log("Unknown error :" + e); + reject; + }); + }); + } + + // AW : Backup file "srcfile" from the phone to "destfile" localy + // TO-DO : This function should be moved to devices.js + backupremote(srcfile, destfile, adbpath, progress) { var _this = this; - _this.log("Backup Function Entry, will backup "+srcfile+" to "+destfile+" adbpath:"+adbpath); + _this.log( + "Backup Function Entry, will backup " + + srcfile + + " to " + + destfile + + " adbpath:" + + adbpath + ); return new Promise(function(resolve, reject) { - _this.fileSize = 9999999999999; // Init to file size to a very high value while waiting for the real size. - // Get file size - _this.getFileSize(srcfile).then(stdout=>{ - _this.log("Returned FileSize is "+stdout+ " Ko"); - _this.fileSize = stdout; - // Creating pipe - _this.shell("mkfifo /backup.pipe").then((stdout) => { + _this.fileSize = 9999999999999; // Init to file size to a very high value while waiting for the real size. + // Get file size + _this + .getFileSize(srcfile) + .then(stdout => { + _this.log("Returned FileSize is " + stdout + " Ko"); + _this.fileSize = stdout; + // Creating pipe + _this + .shell("mkfifo /backup.pipe") + .then(stdout => { _this.log("Pipe created !"); var lastSize = 0; var progressInterval = setInterval(() => { - _this.execCommandCustom(["stat", "-t", destfile]).then(stat => { - progress ((lastSize / _this.fileSize)*100); - lastSize = eval(stat.split(" ")[1])/1024; - //_this.log("pushing: " + lastSize+"/"+_this.fileSize); - }).catch(e => { clearInterval(progressInterval);_this.log("failed to stat: " + e); - }); + const stats = fs.statSync(destfile); + const fileSizeInBytes = stats.size; + progress((lastSize / _this.fileSize) * 100); + lastSize = fileSizeInBytes / 1024; }, 1000); // Start the backup _this.log("Starting Backup..."); - _this.execCommand(["exec-out 'tar -cvp ",srcfile," 2>/backup.pipe' | dd of="+destfile, - " & ",(adbpath+"/adb -P"),_this.port, - " shell cat /backup.pipe", process.platform == "win32" ? ' | findstr /v "%]"' : ' | grep -v "%]"']).then( (error, stdout, stderr) => { - _this.log("Backup Ended"); - clearInterval(progressInterval); - _this.shell("rm /backup.pipe").then( ()=> { - _this.log("Pipe released."); - resolve(); // Everything's Fine ! - }).catch(e=>{utils.log.warn; reject(e+" Unable to delete the pipe "+e.length);}); // Pipe Deletion - }).catch(e=>{utils.log.warn; reject(e+" Unable to backuping the device "+e.length);}); // Backup start - }).catch(e=>{utils.log.warn; reject(e+" Pipe creation failed "+e.length);}); // Pipe Creation - }).catch(e=>{utils.log.warn; reject(e+" Unable to get the partition's filesize "+e.length);}); // Get file size - }); - } - - -// AW : Restore file "srcfile" from the computer to "destfile" on the phone -// TO-DO : This function should be moved to devices.js - restore(srcfile,destfile, adbpath, progress) { + _this + .execCommand([ + "exec-out 'tar -cvp ", + srcfile, + " 2>/backup.pipe' | dd of=" + destfile, + " & ", + adbpath + "/adb -P", + _this.port, + " shell cat /backup.pipe", + process.platform == "win32" + ? ' | findstr /v "%]"' + : ' | grep -v "%]"' + ]) + .then((error, stdout, stderr) => { + _this.log("Backup Ended"); + clearInterval(progressInterval); + _this + .shell("rm /backup.pipe") + .then(() => { + _this.log("Pipe released."); + resolve(); // Everything's Fine ! + }) + .catch(e => { + utils.log.warn; + reject(e + " Unable to delete the pipe " + e.length); + }); // Pipe Deletion + }) + .catch(e => { + utils.log.warn; + reject(e + " Unable to backuping the device " + e.length); + }); // Backup start + }) + .catch(e => { + utils.log.warn; + reject(e + " Pipe creation failed " + e.length); + }); // Pipe Creation + }) + .catch(e => { + utils.log.warn; + reject(e + " Unable to get the partition's filesize " + e.length); + }); // Get file size + }); + } + + // AW : Restore file "srcfile" from the computer to "destfile" on the phone + // TO-DO : This function should be moved to devices.js + restoreremote(srcfile, destfile, adbpath, progress) { var _this = this; - _this.log("Restore Function Entry, will restore "+srcfile+" on "+destfile+" adbpath:"+adbpath); + _this.log( + "Restore Function Entry, will restore " + + srcfile + + " on " + + destfile + + " adbpath:" + + adbpath + ); return new Promise(function(resolve, reject) { - _this.fileSize = 9999999999999; // Init to file size to a very high value while waiting for the real size. - // Get file size - _this.getFileSize(srcfile).then(stdout=>{ - _this.log("Returned FileSize is "+stdout+ " Ko"); - _this.fileSize = stdout; - // Creating pipe - _this.shell("mkfifo /restore.pipe").then((stdout) => { + _this.fileSize = 9999999999999; // Init to file size to a very high value while waiting for the real size. + // Get file size + _this + .getFileSize(srcfile) + .then(stdout => { + _this.log("Returned FileSize is " + stdout + " Ko"); + _this.fileSize = stdout; + // Creating pipe + _this + .shell("mkfifo /restore.pipe") + .then(stdout => { _this.log("Pipe created !"); var lastSize = 0; var progressInterval = setInterval(() => { - _this.execCommandCustom(["stat", "-t", destfile]).then(stat => { - progress ((lastSize / _this.fileSize)*100); - lastSize = eval(stat.split(" ")[1])/1024; - //_this.log("pushing: " + lastSize+"/"+_this.fileSize); - }).catch(e => { clearInterval(progressInterval);_this.log("failed to stat: " + e); - }); + const stats = fs.statSync(destfile); + const fileSizeInBytes = stats.size; + progress((lastSize / _this.fileSize) * 100); + lastSize = fileSizeInBytes / 1024; }, 1000); // Start the restoration _this.log("Starting Restore..."); //adb push user.tar /resto.pipe & adb shell 'cd /; cat /resto.pipe | tar -xv' - _this.execCommand(["push",srcfile,"/resto.pipe &", - (adbpath+"/adb -P"),_this.port, - " shell 'cd /; cat /resto.pipe | tar -xv'"]).then( (error, stdout, stderr) => { - _this.log("Restore Ended"); - clearInterval(progressInterval); - _this.shell("rm /resotre.pipe").then( ()=> { - _this.log("Pipe released."); - resolve(); // Everything's Fine ! - }).catch(e=>{utils.log.warn; reject("Unable to delete the pipe "+e);}); // Pipe Deletion - }).catch(e=>{utils.log.warn; reject("Unable to restore the backup "+e);}); // Backup start - }).catch(e=>{utils.log.warn; reject("Pipe creation failed "+e);}); // Pipe Creation - }).catch(e=>{utils.log.warn; reject("Unable to get the partition's filesize "+e);}); // Get file size - }); - } - - + _this + .execCommand([ + "push", + srcfile, + "/resto.pipe &", + adbpath + "/adb -P", + _this.port, + " shell 'cd /; cat /resto.pipe | tar -xv'" + ]) + .then((error, stdout, stderr) => { + _this.log("Restore Ended"); + clearInterval(progressInterval); + _this + .shell("rm /restore.pipe") + .then(() => { + _this.log("Pipe released."); + resolve(); // Everything's Fine ! + }) + .catch(e => { + utils.log.warn; + reject("Unable to delete the pipe " + e); + }); // Pipe Deletion + }) + .catch(e => { + utils.log.warn; + reject("Unable to restore the backup " + e); + }); // Backup start + }) + .catch(e => { + utils.log.warn; + reject("Pipe creation failed " + e); + }); // Pipe Creation + }) + .catch(e => { + utils.log.warn; + reject("Unable to get the partition's filesize " + e); + }); // Get file size + }); + } } module.exports = Adb; From ba90fec1fd54d0d5f928fc4ab3c261b9ed7f0a52 Mon Sep 17 00:00:00 2001 From: Alain94W Date: Sun, 29 Dec 2019 19:09:54 +0100 Subject: [PATCH 03/26] Added functions(getOSName), functions renamed (backup,restore) --- src/adb.js | 349 +++++++++++++++++++++++++---------------------------- 1 file changed, 166 insertions(+), 183 deletions(-) diff --git a/src/adb.js b/src/adb.js index 6733ceb..63a0c53 100644 --- a/src/adb.js +++ b/src/adb.js @@ -333,6 +333,30 @@ class Adb { }); } + // Get OS name + getOsName() { + var _this = this; + return new Promise(function(resolve, reject) { + _this + .shell(["cat", "/etc/system-image/channel.ini","|grep tag="]) + .then(stdout => { + if (stdout) { + resolve( + stdout + .split(",") + .filter(p => p.includes("tag="))[0] + .replace("tag=", "") + .trim() + ); + } else { + reject(new Error("failed to cat default.prop: no response")); + } + + }) + .catch(e => reject(new Error("getprop error: " + e))); + }); + } + // Find out what operating system the device is running (currently android and ubuntu touch) getOs() { return this.shell(["cat", "/etc/system-image/channel.ini"]).then(stdout => { @@ -489,219 +513,178 @@ class Adb { }); } - // AW : return the file size of a complete folder + // Return the file size of a complete folder getFileSize(file) { - var _this = this; - return new Promise(function(resolve, reject) { - _this.getAdbState().then(stdout => { - if (stdout == 1) { + return this.getState().then(stdout => { + if (stdout == "recovery") { // Recovery - _this + return this .shell("du -shk " + file) .then(stdout => { stdout = parseFloat(stdout); - _this.log("FileSize is " + stdout + " Ko"); - resolve(stdout); - }) - .catch(reject); - } else if (stdout == 2) { - // device (type : used, size) - _this - .shell("df -hBK " + file + " --output=used|tail -n1") + this.log("FileSize is " + stdout + " Ko"); + return (stdout); + }).catch(e=>{throw new Error(e)}); + } else if (stdout == "device") { + // Device + return this + .shell("du -shk " + file+ " |tail -n1")// + " --output=used|tail -n1")//df -hBK .then(stdout => { stdout = parseFloat(stdout); - _this.log("FileSize is " + stdout + " Ko"); - resolve(stdout); - }) - .catch(reject); + this.log("FileSize is " + stdout + " Ko"); + return (stdout); + }).catch(e=>{throw new Error(e)}); } - }); }).catch(e => { - _this.log(e); - reject; + throw new Error("Unable to get filesize"); }); } - // AW : Return the status of the device (0 : bootloader, 1 : recovery, 2 : device ...) - getAdbState() { - var _this = this; - return new Promise(function(resolve, reject) { - _this - .execCommand(["get-state"]) - .then(stdout => { - _this.log("Device state is " + stdout); - switch (stdout) { - case "device": - resolve(2); - case "bootloader": - resolve(0); - case "recovery": - resolve(1); - default: - resolve(3); // Unknown - } - }) - .catch(e => { - // Try with recovery command - _this.log("Unknown error :" + e); - reject; - }); + // Return the available size of a partition + getAvailableSize(partition) { + return this.getState().then(stdout => { + if (stdout == "recovery") { + // Recovery + throw new Error("You must be in device mode"); + } else if (stdout == "device") { + // Device + return this + .shell("df -hBK " + partition + " --output=avail|tail -n1") + .then(stdout => { + stdout = parseFloat(stdout); + this.log("FileSize available is " + stdout + " Ko"); + return (stdout); + }).catch(e=>{throw new Error(e)}); + } + }).catch(e => { + throw new Error("Unable to get filesize"); + }); + } + + // Return the total size of a partition + getTotalSize(partition) { + return this.getState().then(stdout => { + if (stdout == "recovery") { + // Recovery + throw new Error("You must be in device mode"); + } else if (stdout == "device") { + // Device + return this + .shell("df -hBK " + partition + " --output=size|tail -n1") + .then(stdout => { + stdout = parseFloat(stdout); + this.log("FileSize total is " + stdout + " Ko"); + return (stdout); + }).catch(e=>{throw new Error(e)}); + } + }).catch(e => { + throw new Error("Unable to get filesize"); }); } - // AW : Backup file "srcfile" from the phone to "destfile" localy - // TO-DO : This function should be moved to devices.js - backupremote(srcfile, destfile, adbpath, progress) { + // Return the status of the device (bootloader, recovery, device) + getState() { + + return this.execCommand(["get-state"]).then( (stdout)=>{ + this.log("Device state is "+stdout); + return stdout; + }).catch (e=>{ + this.log("Unknown error :"+e); + throw e; + }); + } + +// Backup file "srcfile" from the phone to "destfile" localy + createRemoteUbuntuBackup(srcfile,destfile,adbpath, progress) { var _this = this; - _this.log( - "Backup Function Entry, will backup " + - srcfile + - " to " + - destfile + - " adbpath:" + - adbpath - ); + _this.log("Backup Function Entry, will backup "+srcfile+" to "+destfile+" adbpath:"+adbpath); return new Promise(function(resolve, reject) { - _this.fileSize = 9999999999999; // Init to file size to a very high value while waiting for the real size. - // Get file size - _this - .getFileSize(srcfile) - .then(stdout => { - _this.log("Returned FileSize is " + stdout + " Ko"); - _this.fileSize = stdout; - // Creating pipe - _this - .shell("mkfifo /backup.pipe") - .then(stdout => { + _this.fileSize = 9999999999999; // Init to file size to a very high value while waiting for the real size. + // Get file size + _this.getFileSize(srcfile).then(stdout=>{ + _this.log("Returned FileSize is "+stdout+ " Ko"); + _this.fileSize = stdout; + // Creating pipe + _this.shell("mkfifo /backup.pipe").then((stdout) => { _this.log("Pipe created !"); var lastSize = 0; var progressInterval = setInterval(() => { - const stats = fs.statSync(destfile); - const fileSizeInBytes = stats.size; - progress((lastSize / _this.fileSize) * 100); - lastSize = fileSizeInBytes / 1024; + const stats = fs.statSync(destfile); + const fileSizeInBytes = stats.size; + progress ((lastSize / _this.fileSize)*100); + lastSize = fileSizeInBytes/1024; }, 1000); // Start the backup _this.log("Starting Backup..."); - _this - .execCommand([ - "exec-out 'tar -cvp ", - srcfile, - " 2>/backup.pipe' | dd of=" + destfile, - " & ", - adbpath + "/adb -P", - _this.port, - " shell cat /backup.pipe", - process.platform == "win32" - ? ' | findstr /v "%]"' - : ' | grep -v "%]"' - ]) - .then((error, stdout, stderr) => { - _this.log("Backup Ended"); - clearInterval(progressInterval); - _this - .shell("rm /backup.pipe") - .then(() => { - _this.log("Pipe released."); - resolve(); // Everything's Fine ! - }) - .catch(e => { - utils.log.warn; - reject(e + " Unable to delete the pipe " + e.length); - }); // Pipe Deletion - }) - .catch(e => { - utils.log.warn; - reject(e + " Unable to backuping the device " + e.length); - }); // Backup start - }) - .catch(e => { - utils.log.warn; - reject(e + " Pipe creation failed " + e.length); - }); // Pipe Creation - }) - .catch(e => { - utils.log.warn; - reject(e + " Unable to get the partition's filesize " + e.length); - }); // Get file size - }); - } - - // AW : Restore file "srcfile" from the computer to "destfile" on the phone - // TO-DO : This function should be moved to devices.js - restoreremote(srcfile, destfile, adbpath, progress) { + _this.execCommand(["exec-out 'tar -cvp ",srcfile," 2>/backup.pipe' | dd of="+destfile]); + //_this.execCommand(["exec-out 'tar -cvp ",srcfile," 2>/backup.pipe' | dd of="+destfile, + // " & ",(adbpath+"/adb -P"),_this.port, + _this.execCommand([" shell cat /backup.pipe", + common.stdoutFilter("%]")]).then( (error, stdout, stderr) => {//process.platform == "win32" ? ' | findstr /v "%]"' : ' | grep -v "%]"'] + _this.log("Backup Ended"); + clearInterval(progressInterval); + _this.shell("rm /backup.pipe").then( ()=> { + _this.log("Pipe released."); + resolve(); // Everything's Fine ! + }).catch(e=>{reject(e+", Unable to delete the pipe ");}); // Pipe Deletion + }).catch(e=>{reject(e+", Unable to backuping the device ");}); // Backup start + }).catch(e=>{reject(e+", Pipe creation failed ");}); // Pipe Creation + }).catch(e=>{reject(e+", Unable to get the partition's filesize ");}); // Get file size + }); + } + + +// Restore file "srcfile" from the computer to "destfile" on the phone + applyRemoteUbuntuBackup(destfile,srcfile, adbpath,bckSize, progress) { var _this = this; - _this.log( - "Restore Function Entry, will restore " + - srcfile + - " on " + - destfile + - " adbpath:" + - adbpath - ); + _this.log("Restore Function Entry, will restore "+srcfile+" on "+destfile+" adbpath:"+adbpath); return new Promise(function(resolve, reject) { - _this.fileSize = 9999999999999; // Init to file size to a very high value while waiting for the real size. - // Get file size - _this - .getFileSize(srcfile) - .then(stdout => { - _this.log("Returned FileSize is " + stdout + " Ko"); - _this.fileSize = stdout; - // Creating pipe - _this - .shell("mkfifo /restore.pipe") - .then(stdout => { + _this.fileSize = 9999999999999; // Init to file size to a very high value while waiting for the real size. + + + // Creating pipe + _this.shell("mkfifo /restore.pipe").then((stdout) => { _this.log("Pipe created !"); - var lastSize = 0; - var progressInterval = setInterval(() => { - const stats = fs.statSync(destfile); - const fileSizeInBytes = stats.size; - progress((lastSize / _this.fileSize) * 100); - lastSize = fileSizeInBytes / 1024; - }, 1000); +/* + var lastSize = 0; + _this.fileSize = bckSize; + var progressInterval = setInterval(() => { + _this + .shell([ + "stat", + "-t", + common.quotepath("/restore.pipe") + ]) + .then(stat => { + + lastSize = eval(stat.split(" ")[1]); + progress ((lastSize / _this.fileSize)*100); + //lastSize = fileSizeInBytes/1024; + }) + .catch(e => { + clearInterval(progressInterval); + _this.log("failed to stat: " + e); + }); + }, 1000); + */ // Start the restoration _this.log("Starting Restore..."); //adb push user.tar /resto.pipe & adb shell 'cd /; cat /resto.pipe | tar -xv' - _this - .execCommand([ - "push", - srcfile, - "/resto.pipe &", - adbpath + "/adb -P", - _this.port, - " shell 'cd /; cat /resto.pipe | tar -xv'" - ]) - .then((error, stdout, stderr) => { - _this.log("Restore Ended"); - clearInterval(progressInterval); - _this - .shell("rm /restore.pipe") - .then(() => { - _this.log("Pipe released."); - resolve(); // Everything's Fine ! - }) - .catch(e => { - utils.log.warn; - reject("Unable to delete the pipe " + e); - }); // Pipe Deletion - }) - .catch(e => { - utils.log.warn; - reject("Unable to restore the backup " + e); - }); // Backup start - }) - .catch(e => { - utils.log.warn; - reject("Pipe creation failed " + e); - }); // Pipe Creation - }) - .catch(e => { - utils.log.warn; - reject("Unable to get the partition's filesize " + e); - }); // Get file size - }); - } + _this.execCommand(["push",srcfile,"/restore.pipe &", + (adbpath+"/adb -P"),_this.port, + " shell 'cd /; cat /restore.pipe | tar -xv'"]).then( (error, stdout, stderr) => { + _this.log("Restore Ended"); + //clearInterval(progressInterval); + _this.shell("rm /restore.pipe").then( ()=> { + _this.log("Pipe released."); + resolve(); // Everything's Fine ! + }).catch(e=>{reject(e);}); // Pipe Deletion + }).catch(e=>{reject(e);}); // Backup start + }).catch(e=>{reject(e);}); // Pipe Creation + }); + } + } module.exports = Adb; From bde3c06d2ed1638c283fee57caf75612c7ba996d Mon Sep 17 00:00:00 2001 From: Alain94W Date: Sun, 29 Dec 2019 20:17:35 +0100 Subject: [PATCH 04/26] Added functions(getOSName), functions renamed (backup,restore), corrected with lint-fix --- src/adb.js | 288 +++++++++++++++++++++++++++++++++-------------------- 1 file changed, 180 insertions(+), 108 deletions(-) diff --git a/src/adb.js b/src/adb.js index 63a0c53..80c6a76 100644 --- a/src/adb.js +++ b/src/adb.js @@ -338,20 +338,19 @@ class Adb { var _this = this; return new Promise(function(resolve, reject) { _this - .shell(["cat", "/etc/system-image/channel.ini","|grep tag="]) + .shell(["cat", "/etc/system-image/channel.ini", "|grep tag="]) .then(stdout => { - if (stdout) { - resolve( - stdout - .split(",") - .filter(p => p.includes("tag="))[0] - .replace("tag=", "") - .trim() - ); - } else { - reject(new Error("failed to cat default.prop: no response")); - } - + if (stdout) { + resolve( + stdout + .split(",") + .filter(p => p.includes("tag="))[0] + .replace("tag=", "") + .trim() + ); + } else { + reject(new Error("failed to cat default.prop: no response")); + } }) .catch(e => reject(new Error("getprop error: " + e))); }); @@ -515,138 +514,194 @@ class Adb { // Return the file size of a complete folder getFileSize(file) { - return this.getState().then(stdout => { + return this.getState() + .then(stdout => { if (stdout == "recovery") { // Recovery - return this - .shell("du -shk " + file) + return this.shell("du -shk " + file) .then(stdout => { stdout = parseFloat(stdout); this.log("FileSize is " + stdout + " Ko"); - return (stdout); - }).catch(e=>{throw new Error(e)}); + return stdout; + }) + .catch(e => { + throw new Error(e); + }); } else if (stdout == "device") { // Device - return this - .shell("du -shk " + file+ " |tail -n1")// + " --output=used|tail -n1")//df -hBK + return this.shell("du -shk " + file + " |tail -n1") // + " --output=used|tail -n1")//df -hBK .then(stdout => { stdout = parseFloat(stdout); this.log("FileSize is " + stdout + " Ko"); - return (stdout); - }).catch(e=>{throw new Error(e)}); + return stdout; + }) + .catch(e => { + throw new Error(e); + }); } - }).catch(e => { - throw new Error("Unable to get filesize"); - }); + }) + .catch(e => { + throw new Error("Unable to get filesize"); + }); } // Return the available size of a partition getAvailableSize(partition) { - return this.getState().then(stdout => { + return this.getState() + .then(stdout => { if (stdout == "recovery") { // Recovery throw new Error("You must be in device mode"); } else if (stdout == "device") { // Device - return this - .shell("df -hBK " + partition + " --output=avail|tail -n1") + return this.shell("df -hBK " + partition + " --output=avail|tail -n1") .then(stdout => { stdout = parseFloat(stdout); this.log("FileSize available is " + stdout + " Ko"); - return (stdout); - }).catch(e=>{throw new Error(e)}); + return stdout; + }) + .catch(e => { + throw new Error(e); + }); } - }).catch(e => { - throw new Error("Unable to get filesize"); - }); + }) + .catch(e => { + throw new Error("Unable to get filesize"); + }); } // Return the total size of a partition getTotalSize(partition) { - return this.getState().then(stdout => { + return this.getState() + .then(stdout => { if (stdout == "recovery") { // Recovery throw new Error("You must be in device mode"); } else if (stdout == "device") { // Device - return this - .shell("df -hBK " + partition + " --output=size|tail -n1") + return this.shell("df -hBK " + partition + " --output=size|tail -n1") .then(stdout => { stdout = parseFloat(stdout); this.log("FileSize total is " + stdout + " Ko"); - return (stdout); - }).catch(e=>{throw new Error(e)}); + return stdout; + }) + .catch(e => { + throw new Error(e); + }); } - }).catch(e => { - throw new Error("Unable to get filesize"); - }); + }) + .catch(e => { + throw new Error("Unable to get filesize"); + }); } // Return the status of the device (bootloader, recovery, device) - getState() { - - return this.execCommand(["get-state"]).then( (stdout)=>{ - this.log("Device state is "+stdout); - return stdout; - }).catch (e=>{ - this.log("Unknown error :"+e); - throw e; - }); - } - -// Backup file "srcfile" from the phone to "destfile" localy - createRemoteUbuntuBackup(srcfile,destfile,adbpath, progress) { + getState() { + return this.execCommand(["get-state"]) + .then(stdout => { + this.log("Device state is " + stdout); + return stdout; + }) + .catch(e => { + this.log("Unknown error :" + e); + throw e; + }); + } + + // Backup file "srcfile" from the phone to "destfile" localy + createRemoteUbuntuBackup(srcfile, destfile, adbpath, progress) { var _this = this; - _this.log("Backup Function Entry, will backup "+srcfile+" to "+destfile+" adbpath:"+adbpath); + _this.log( + "Backup Function Entry, will backup " + + srcfile + + " to " + + destfile + + " adbpath:" + + adbpath + ); return new Promise(function(resolve, reject) { - _this.fileSize = 9999999999999; // Init to file size to a very high value while waiting for the real size. - // Get file size - _this.getFileSize(srcfile).then(stdout=>{ - _this.log("Returned FileSize is "+stdout+ " Ko"); - _this.fileSize = stdout; - // Creating pipe - _this.shell("mkfifo /backup.pipe").then((stdout) => { + _this.fileSize = 9999999999999; // Init to file size to a very high value while waiting for the real size. + // Get file size + _this + .getFileSize(srcfile) + .then(stdout => { + _this.log("Returned FileSize is " + stdout + " Ko"); + _this.fileSize = stdout; + // Creating pipe + _this + .shell("mkfifo /backup.pipe") + .then(stdout => { _this.log("Pipe created !"); var lastSize = 0; var progressInterval = setInterval(() => { - const stats = fs.statSync(destfile); - const fileSizeInBytes = stats.size; - progress ((lastSize / _this.fileSize)*100); - lastSize = fileSizeInBytes/1024; + const stats = fs.statSync(destfile); + const fileSizeInBytes = stats.size; + progress((lastSize / _this.fileSize) * 100); + lastSize = fileSizeInBytes / 1024; }, 1000); // Start the backup _this.log("Starting Backup..."); - _this.execCommand(["exec-out 'tar -cvp ",srcfile," 2>/backup.pipe' | dd of="+destfile]); + _this.execCommand([ + "exec-out 'tar -cvp ", + srcfile, + " 2>/backup.pipe' | dd of=" + destfile + ]); //_this.execCommand(["exec-out 'tar -cvp ",srcfile," 2>/backup.pipe' | dd of="+destfile, // " & ",(adbpath+"/adb -P"),_this.port, - _this.execCommand([" shell cat /backup.pipe", - common.stdoutFilter("%]")]).then( (error, stdout, stderr) => {//process.platform == "win32" ? ' | findstr /v "%]"' : ' | grep -v "%]"'] - _this.log("Backup Ended"); - clearInterval(progressInterval); - _this.shell("rm /backup.pipe").then( ()=> { - _this.log("Pipe released."); - resolve(); // Everything's Fine ! - }).catch(e=>{reject(e+", Unable to delete the pipe ");}); // Pipe Deletion - }).catch(e=>{reject(e+", Unable to backuping the device ");}); // Backup start - }).catch(e=>{reject(e+", Pipe creation failed ");}); // Pipe Creation - }).catch(e=>{reject(e+", Unable to get the partition's filesize ");}); // Get file size - }); - } - - -// Restore file "srcfile" from the computer to "destfile" on the phone - applyRemoteUbuntuBackup(destfile,srcfile, adbpath,bckSize, progress) { + _this + .execCommand([ + " shell cat /backup.pipe", + common.stdoutFilter("%]") + ]) + .then((error, stdout, stderr) => { + //process.platform == "win32" ? ' | findstr /v "%]"' : ' | grep -v "%]"'] + _this.log("Backup Ended"); + clearInterval(progressInterval); + _this + .shell("rm /backup.pipe") + .then(() => { + _this.log("Pipe released."); + resolve(); // Everything's Fine ! + }) + .catch(e => { + reject(e + ", Unable to delete the pipe "); + }); // Pipe Deletion + }) + .catch(e => { + reject(e + ", Unable to backuping the device "); + }); // Backup start + }) + .catch(e => { + reject(e + ", Pipe creation failed "); + }); // Pipe Creation + }) + .catch(e => { + reject(e + ", Unable to get the partition's filesize "); + }); // Get file size + }); + } + + // Restore file "srcfile" from the computer to "destfile" on the phone + applyRemoteUbuntuBackup(destfile, srcfile, adbpath, bckSize, progress) { var _this = this; - _this.log("Restore Function Entry, will restore "+srcfile+" on "+destfile+" adbpath:"+adbpath); + _this.log( + "Restore Function Entry, will restore " + + srcfile + + " on " + + destfile + + " adbpath:" + + adbpath + ); return new Promise(function(resolve, reject) { - _this.fileSize = 9999999999999; // Init to file size to a very high value while waiting for the real size. - - - // Creating pipe - _this.shell("mkfifo /restore.pipe").then((stdout) => { - _this.log("Pipe created !"); -/* + _this.fileSize = 9999999999999; // Init to file size to a very high value while waiting for the real size. + + // Creating pipe + _this + .shell("mkfifo /restore.pipe") + .then(stdout => { + _this.log("Pipe created !"); + /* var lastSize = 0; _this.fileSize = bckSize; var progressInterval = setInterval(() => { @@ -669,22 +724,39 @@ class Adb { }, 1000); */ - // Start the restoration - _this.log("Starting Restore..."); //adb push user.tar /resto.pipe & adb shell 'cd /; cat /resto.pipe | tar -xv' - _this.execCommand(["push",srcfile,"/restore.pipe &", - (adbpath+"/adb -P"),_this.port, - " shell 'cd /; cat /restore.pipe | tar -xv'"]).then( (error, stdout, stderr) => { - _this.log("Restore Ended"); - //clearInterval(progressInterval); - _this.shell("rm /restore.pipe").then( ()=> { - _this.log("Pipe released."); - resolve(); // Everything's Fine ! - }).catch(e=>{reject(e);}); // Pipe Deletion - }).catch(e=>{reject(e);}); // Backup start - }).catch(e=>{reject(e);}); // Pipe Creation - }); - } - + // Start the restoration + _this.log("Starting Restore..."); //adb push user.tar /resto.pipe & adb shell 'cd /; cat /resto.pipe | tar -xv' + _this + .execCommand([ + "push", + srcfile, + "/restore.pipe &", + adbpath + "/adb -P", + _this.port, + " shell 'cd /; cat /restore.pipe | tar -xv'" + ]) + .then((error, stdout, stderr) => { + _this.log("Restore Ended"); + //clearInterval(progressInterval); + _this + .shell("rm /restore.pipe") + .then(() => { + _this.log("Pipe released."); + resolve(); // Everything's Fine ! + }) + .catch(e => { + reject(e); + }); // Pipe Deletion + }) + .catch(e => { + reject(e); + }); // Backup start + }) + .catch(e => { + reject(e); + }); // Pipe Creation + }); + } } module.exports = Adb; From 6f7236251c8dd7678cde55bb1517a343b8e6097b Mon Sep 17 00:00:00 2001 From: Jan Sprinz Date: Thu, 22 Oct 2020 18:50:30 +0200 Subject: [PATCH 05/26] Begin code cleanup --- src/adb.js | 31 ++----------------------------- 1 file changed, 2 insertions(+), 29 deletions(-) diff --git a/src/adb.js b/src/adb.js index e995b33..da8ce6e 100644 --- a/src/adb.js +++ b/src/adb.js @@ -539,7 +539,7 @@ class Adb { }); } else if (stdout == "device") { // Device - return this.shell("du -shk " + file + " |tail -n1") // + " --output=used|tail -n1")//df -hBK + return this.shell("du -shk " + file + " |tail -n1") .then(stdout => { stdout = parseFloat(stdout); this.log("FileSize is " + stdout + " Ko"); @@ -657,15 +657,12 @@ class Adb { srcfile, " 2>/backup.pipe' | dd of=" + destfile ]); - //_this.execCommand(["exec-out 'tar -cvp ",srcfile," 2>/backup.pipe' | dd of="+destfile, - // " & ",(adbpath+"/adb -P"),_this.port, _this .execCommand([ " shell cat /backup.pipe", common.stdoutFilter("%]") ]) .then((error, stdout, stderr) => { - //process.platform == "win32" ? ' | findstr /v "%]"' : ' | grep -v "%]"'] _this.log("Backup Ended"); clearInterval(progressInterval); _this @@ -711,31 +708,8 @@ class Adb { .shell("mkfifo /restore.pipe") .then(stdout => { _this.log("Pipe created !"); - /* - var lastSize = 0; - _this.fileSize = bckSize; - var progressInterval = setInterval(() => { - _this - .shell([ - "stat", - "-t", - common.quotepath("/restore.pipe") - ]) - .then(stat => { - - lastSize = eval(stat.split(" ")[1]); - progress ((lastSize / _this.fileSize)*100); - //lastSize = fileSizeInBytes/1024; - }) - .catch(e => { - clearInterval(progressInterval); - _this.log("failed to stat: " + e); - }); - }, 1000); - */ - // Start the restoration - _this.log("Starting Restore..."); //adb push user.tar /resto.pipe & adb shell 'cd /; cat /resto.pipe | tar -xv' + _this.log("Starting Restore..."); _this .execCommand([ "push", @@ -747,7 +721,6 @@ class Adb { ]) .then((error, stdout, stderr) => { _this.log("Restore Ended"); - //clearInterval(progressInterval); _this .shell("rm /restore.pipe") .then(() => { From 314b91d414f8ae194800fce8ecdf98deed53b513 Mon Sep 17 00:00:00 2001 From: Jan Sprinz Date: Thu, 22 Oct 2020 18:56:20 +0200 Subject: [PATCH 06/26] Remove unneeded function --- src/adb.js | 23 ----------------------- 1 file changed, 23 deletions(-) diff --git a/src/adb.js b/src/adb.js index da8ce6e..d2ed49b 100644 --- a/src/adb.js +++ b/src/adb.js @@ -342,29 +342,6 @@ class Adb { }); } - // Get OS name - getOsName() { - var _this = this; - return new Promise(function(resolve, reject) { - _this - .shell(["cat", "/etc/system-image/channel.ini", "|grep tag="]) - .then(stdout => { - if (stdout) { - resolve( - stdout - .split(",") - .filter(p => p.includes("tag="))[0] - .replace("tag=", "") - .trim() - ); - } else { - reject(new Error("failed to cat default.prop: no response")); - } - }) - .catch(e => reject(new Error("getprop error: " + e))); - }); - } - // Find out what operating system the device is running (currently android and ubuntu touch) getOs() { return this.shell(["cat", "/etc/system-image/channel.ini"]).then(stdout => { From f709c3784583d409f750483eb47da69ada412705 Mon Sep 17 00:00:00 2001 From: Jan Sprinz Date: Thu, 22 Oct 2020 19:24:17 +0200 Subject: [PATCH 07/26] Reduce filesize fuckery --- src/adb.js | 44 ++++++++++++-------------------------------- 1 file changed, 12 insertions(+), 32 deletions(-) diff --git a/src/adb.js b/src/adb.js index d2ed49b..acc9a8d 100644 --- a/src/adb.js +++ b/src/adb.js @@ -508,7 +508,7 @@ class Adb { return this.shell("du -shk " + file) .then(stdout => { stdout = parseFloat(stdout); - this.log("FileSize is " + stdout + " Ko"); + this.log("size is " + stdout + " Ko"); return stdout; }) .catch(e => { @@ -519,7 +519,7 @@ class Adb { return this.shell("du -shk " + file + " |tail -n1") .then(stdout => { stdout = parseFloat(stdout); - this.log("FileSize is " + stdout + " Ko"); + this.log("size is " + stdout + " Ko"); return stdout; }) .catch(e => { @@ -528,7 +528,7 @@ class Adb { } }) .catch(e => { - throw new Error("Unable to get filesize"); + throw new Error("Unable to get size"); }); } @@ -544,7 +544,7 @@ class Adb { return this.shell("df -hBK " + partition + " --output=avail|tail -n1") .then(stdout => { stdout = parseFloat(stdout); - this.log("FileSize available is " + stdout + " Ko"); + this.log("size available is " + stdout + " Ko"); return stdout; }) .catch(e => { @@ -553,7 +553,7 @@ class Adb { } }) .catch(e => { - throw new Error("Unable to get filesize"); + throw new Error("Unable to get size"); }); } @@ -569,7 +569,7 @@ class Adb { return this.shell("df -hBK " + partition + " --output=size|tail -n1") .then(stdout => { stdout = parseFloat(stdout); - this.log("FileSize total is " + stdout + " Ko"); + this.log("size total is " + stdout + " Ko"); return stdout; }) .catch(e => { @@ -578,7 +578,7 @@ class Adb { } }) .catch(e => { - throw new Error("Unable to get filesize"); + throw new Error("Unable to get size"); }); } @@ -598,32 +598,22 @@ class Adb { // Backup file "srcfile" from the phone to "destfile" localy createRemoteUbuntuBackup(srcfile, destfile, adbpath, progress) { var _this = this; - _this.log( - "Backup Function Entry, will backup " + - srcfile + - " to " + - destfile + - " adbpath:" + - adbpath - ); return new Promise(function(resolve, reject) { - _this.fileSize = 9999999999999; // Init to file size to a very high value while waiting for the real size. // Get file size _this .getFileSize(srcfile) - .then(stdout => { - _this.log("Returned FileSize is " + stdout + " Ko"); - _this.fileSize = stdout; + .then(fileSize => { + _this.log("Returned size is " + fileSize + " Ko"); // Creating pipe _this .shell("mkfifo /backup.pipe") - .then(stdout => { + .then(() => { _this.log("Pipe created !"); var lastSize = 0; var progressInterval = setInterval(() => { const stats = fs.statSync(destfile); const fileSizeInBytes = stats.size; - progress((lastSize / _this.fileSize) * 100); + progress((lastSize / fileSize) * 100); lastSize = fileSizeInBytes / 1024; }, 1000); @@ -661,7 +651,7 @@ class Adb { }); // Pipe Creation }) .catch(e => { - reject(e + ", Unable to get the partition's filesize "); + reject(e + ", Unable to get the partition size "); }); // Get file size }); } @@ -669,17 +659,7 @@ class Adb { // Restore file "srcfile" from the computer to "destfile" on the phone applyRemoteUbuntuBackup(destfile, srcfile, adbpath, bckSize, progress) { var _this = this; - _this.log( - "Restore Function Entry, will restore " + - srcfile + - " on " + - destfile + - " adbpath:" + - adbpath - ); return new Promise(function(resolve, reject) { - _this.fileSize = 9999999999999; // Init to file size to a very high value while waiting for the real size. - // Creating pipe _this .shell("mkfifo /restore.pipe") From 8efd393868c697d78d3e964e362c3694d62da90c Mon Sep 17 00:00:00 2001 From: Jan Sprinz Date: Thu, 22 Oct 2020 19:32:27 +0200 Subject: [PATCH 08/26] Shrink code --- src/adb.js | 33 +++++++-------------------------- 1 file changed, 7 insertions(+), 26 deletions(-) diff --git a/src/adb.js b/src/adb.js index acc9a8d..b2314d0 100644 --- a/src/adb.js +++ b/src/adb.js @@ -502,33 +502,14 @@ class Adb { // Return the file size of a complete folder getFileSize(file) { return this.getState() - .then(stdout => { - if (stdout == "recovery") { - // Recovery - return this.shell("du -shk " + file) - .then(stdout => { - stdout = parseFloat(stdout); - this.log("size is " + stdout + " Ko"); - return stdout; - }) - .catch(e => { - throw new Error(e); - }); - } else if (stdout == "device") { - // Device - return this.shell("du -shk " + file + " |tail -n1") - .then(stdout => { - stdout = parseFloat(stdout); - this.log("size is " + stdout + " Ko"); - return stdout; - }) - .catch(e => { - throw new Error(e); - }); - } - }) + .then(( + state // TODO verify that state detection is needed + ) => + this.shell("du -shk " + file + (state == "device" ? " |tail -n1" : "")) + ) + .then(stdout => parseFloat(stdout)) .catch(e => { - throw new Error("Unable to get size"); + throw new Error(`Unable to get size: ${e}`); }); } From c7f25f22d23c23a170514306f0d918b8080cd561 Mon Sep 17 00:00:00 2001 From: Jan Sprinz Date: Thu, 22 Oct 2020 19:39:12 +0200 Subject: [PATCH 09/26] Shrink state function --- src/adb.js | 30 +++++++++++------------------- 1 file changed, 11 insertions(+), 19 deletions(-) diff --git a/src/adb.js b/src/adb.js index b2314d0..9474493 100644 --- a/src/adb.js +++ b/src/adb.js @@ -260,6 +260,11 @@ class Adb { ]); } + // Return the status of the device (bootloader, recovery, device) + getState() { + return this.execCommand(["get-state"]); + } + ////////////////////////////////////////////////////////////////////////////// // Convenience functions ////////////////////////////////////////////////////////////////////////////// @@ -516,11 +521,11 @@ class Adb { // Return the available size of a partition getAvailableSize(partition) { return this.getState() - .then(stdout => { - if (stdout == "recovery") { + .then(state => { + if (state == "recovery") { // Recovery throw new Error("You must be in device mode"); - } else if (stdout == "device") { + } else if (state == "device") { // Device return this.shell("df -hBK " + partition + " --output=avail|tail -n1") .then(stdout => { @@ -541,11 +546,11 @@ class Adb { // Return the total size of a partition getTotalSize(partition) { return this.getState() - .then(stdout => { - if (stdout == "recovery") { + .then(state => { + if (state == "recovery") { // Recovery throw new Error("You must be in device mode"); - } else if (stdout == "device") { + } else if (state == "device") { // Device return this.shell("df -hBK " + partition + " --output=size|tail -n1") .then(stdout => { @@ -563,19 +568,6 @@ class Adb { }); } - // Return the status of the device (bootloader, recovery, device) - getState() { - return this.execCommand(["get-state"]) - .then(stdout => { - this.log("Device state is " + stdout); - return stdout; - }) - .catch(e => { - this.log("Unknown error :" + e); - throw e; - }); - } - // Backup file "srcfile" from the phone to "destfile" localy createRemoteUbuntuBackup(srcfile, destfile, adbpath, progress) { var _this = this; From 8724f6c4cf4170fb8d18684df13e5c179a7bd284 Mon Sep 17 00:00:00 2001 From: Jan Sprinz Date: Thu, 22 Oct 2020 19:52:00 +0200 Subject: [PATCH 10/26] Add ensureState function --- src/adb.js | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/adb.js b/src/adb.js index 9474493..6ad3f13 100644 --- a/src/adb.js +++ b/src/adb.js @@ -262,13 +262,23 @@ class Adb { // Return the status of the device (bootloader, recovery, device) getState() { - return this.execCommand(["get-state"]); + return this.execCommand(["get-state"]).then(stdout => stdout.trim()); } ////////////////////////////////////////////////////////////////////////////// // Convenience functions ////////////////////////////////////////////////////////////////////////////// + // Reboot to a state (system, recovery, bootloader) + ensureState(state) { + return this.getState().then(currentState => + currentState === state || + (currentState === "device" && state === "system") + ? Promise.resolve() + : this.reboot(state) + ); + } + // Push an array of files and report progress // { src, dest } pushArray(files = [], progress = () => {}, interval) { @@ -506,10 +516,9 @@ class Adb { // Return the file size of a complete folder getFileSize(file) { + // TODO verify that state detection is needed return this.getState() - .then(( - state // TODO verify that state detection is needed - ) => + .then(state => this.shell("du -shk " + file + (state == "device" ? " |tail -n1" : "")) ) .then(stdout => parseFloat(stdout)) From 204b544bff3a3ee90a1916bfa1eeb140e56d8b6b Mon Sep 17 00:00:00 2001 From: Jan Sprinz Date: Thu, 22 Oct 2020 19:59:13 +0200 Subject: [PATCH 11/26] Adopt ensureState function --- src/adb.js | 50 ++++++++++++-------------------------------------- 1 file changed, 12 insertions(+), 38 deletions(-) diff --git a/src/adb.js b/src/adb.js index 6ad3f13..a7ce7a5 100644 --- a/src/adb.js +++ b/src/adb.js @@ -529,51 +529,25 @@ class Adb { // Return the available size of a partition getAvailableSize(partition) { - return this.getState() - .then(state => { - if (state == "recovery") { - // Recovery - throw new Error("You must be in device mode"); - } else if (state == "device") { - // Device - return this.shell("df -hBK " + partition + " --output=avail|tail -n1") - .then(stdout => { - stdout = parseFloat(stdout); - this.log("size available is " + stdout + " Ko"); - return stdout; - }) - .catch(e => { - throw new Error(e); - }); - } - }) + return this.ensureState("device") + .then(() => + this.shell("df -hBK " + partition + " --output=avail|tail -n1") + ) + .then(stdout => parseFloat(stdout)) .catch(e => { - throw new Error("Unable to get size"); + throw new Error(`Unable to get size: ${e}`); }); } // Return the total size of a partition getTotalSize(partition) { - return this.getState() - .then(state => { - if (state == "recovery") { - // Recovery - throw new Error("You must be in device mode"); - } else if (state == "device") { - // Device - return this.shell("df -hBK " + partition + " --output=size|tail -n1") - .then(stdout => { - stdout = parseFloat(stdout); - this.log("size total is " + stdout + " Ko"); - return stdout; - }) - .catch(e => { - throw new Error(e); - }); - } - }) + return this.ensureState("device") + .then(() => + this.shell("df -hBK " + partition + " --output=size|tail -n1") + ) + .then(stdout => parseFloat(stdout)) .catch(e => { - throw new Error("Unable to get size"); + throw new Error(`Unable to get size: ${e}`); }); } From 5663a5fca7d2eb2e9278ff42b0e14c29e3a9c740 Mon Sep 17 00:00:00 2001 From: Jan Sprinz Date: Fri, 23 Oct 2020 10:52:14 +0200 Subject: [PATCH 12/26] slimming --- src/adb.js | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/src/adb.js b/src/adb.js index a7ce7a5..dd77525 100644 --- a/src/adb.js +++ b/src/adb.js @@ -551,30 +551,28 @@ class Adb { }); } - // Backup file "srcfile" from the phone to "destfile" localy - createRemoteUbuntuBackup(srcfile, destfile, adbpath, progress) { + // Backup file "srcfile" from the device to "destfile" localy + createRemoteUbuntuBackup(srcfile, destfile, progress) { var _this = this; return new Promise(function(resolve, reject) { // Get file size _this .getFileSize(srcfile) .then(fileSize => { - _this.log("Returned size is " + fileSize + " Ko"); // Creating pipe _this .shell("mkfifo /backup.pipe") .then(() => { - _this.log("Pipe created !"); var lastSize = 0; var progressInterval = setInterval(() => { - const stats = fs.statSync(destfile); - const fileSizeInBytes = stats.size; + const { size } = fs.statSync(destfile); progress((lastSize / fileSize) * 100); - lastSize = fileSizeInBytes / 1024; + lastSize = size / 1024; }, 1000); // Start the backup _this.log("Starting Backup..."); + // FIXME replace shell pipe to dd with node stream _this.execCommand([ "exec-out 'tar -cvp ", srcfile, @@ -585,30 +583,31 @@ class Adb { " shell cat /backup.pipe", common.stdoutFilter("%]") ]) - .then((error, stdout, stderr) => { + .then(() => { _this.log("Backup Ended"); clearInterval(progressInterval); _this .shell("rm /backup.pipe") .then(() => { _this.log("Pipe released."); - resolve(); // Everything's Fine ! + resolve(); }) .catch(e => { + clearInterval(progressInterval); reject(e + ", Unable to delete the pipe "); - }); // Pipe Deletion + }); }) .catch(e => { reject(e + ", Unable to backuping the device "); - }); // Backup start + }); }) .catch(e => { reject(e + ", Pipe creation failed "); - }); // Pipe Creation + }); }) .catch(e => { reject(e + ", Unable to get the partition size "); - }); // Get file size + }); }); } From b205f3daba94f854bf7dde16c6267272808ad4a0 Mon Sep 17 00:00:00 2001 From: Jan Sprinz Date: Fri, 23 Oct 2020 11:38:06 +0200 Subject: [PATCH 13/26] Streamline backup creation --- src/adb.js | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/src/adb.js b/src/adb.js index dd77525..f66e59f 100644 --- a/src/adb.js +++ b/src/adb.js @@ -573,16 +573,14 @@ class Adb { // Start the backup _this.log("Starting Backup..."); // FIXME replace shell pipe to dd with node stream - _this.execCommand([ - "exec-out 'tar -cvp ", - srcfile, - " 2>/backup.pipe' | dd of=" + destfile - ]); - _this - .execCommand([ - " shell cat /backup.pipe", - common.stdoutFilter("%]") - ]) + Promise.all([ + _this.execCommand([ + "exec-out 'tar -cvp ", + srcfile, + " 2>/backup.pipe' | dd of=" + destfile + ]), + _this.shell("cat /backup.pipe") + ]) .then(() => { _this.log("Backup Ended"); clearInterval(progressInterval); @@ -593,11 +591,11 @@ class Adb { resolve(); }) .catch(e => { - clearInterval(progressInterval); reject(e + ", Unable to delete the pipe "); }); }) .catch(e => { + clearInterval(progressInterval); reject(e + ", Unable to backuping the device "); }); }) From d87c9886bd2f76fda27c1e26ed0ea47307f11181 Mon Sep 17 00:00:00 2001 From: Jan Sprinz Date: Fri, 23 Oct 2020 11:44:16 +0200 Subject: [PATCH 14/26] Reorder --- src/adb.js | 66 +++++++++++++++++++++++++----------------------------- 1 file changed, 30 insertions(+), 36 deletions(-) diff --git a/src/adb.js b/src/adb.js index f66e59f..5ce4e27 100644 --- a/src/adb.js +++ b/src/adb.js @@ -557,54 +557,48 @@ class Adb { return new Promise(function(resolve, reject) { // Get file size _this - .getFileSize(srcfile) + .shell("mkfifo /backup.pipe") + .then(() => _this.getFileSize(srcfile)) .then(fileSize => { // Creating pipe - _this - .shell("mkfifo /backup.pipe") + var lastSize = 0; + var progressInterval = setInterval(() => { + const { size } = fs.statSync(destfile); + progress((lastSize / fileSize) * 100); + lastSize = size / 1024; + }, 1000); + + // Start the backup + _this.log("Starting Backup..."); + // FIXME replace shell pipe to dd with node stream + Promise.all([ + _this.execCommand([ + "exec-out 'tar -cvp ", + srcfile, + " 2>/backup.pipe' | dd of=" + destfile + ]), + _this.shell("cat /backup.pipe") + ]) .then(() => { - var lastSize = 0; - var progressInterval = setInterval(() => { - const { size } = fs.statSync(destfile); - progress((lastSize / fileSize) * 100); - lastSize = size / 1024; - }, 1000); - - // Start the backup - _this.log("Starting Backup..."); - // FIXME replace shell pipe to dd with node stream - Promise.all([ - _this.execCommand([ - "exec-out 'tar -cvp ", - srcfile, - " 2>/backup.pipe' | dd of=" + destfile - ]), - _this.shell("cat /backup.pipe") - ]) + _this.log("Backup Ended"); + clearInterval(progressInterval); + _this + .shell("rm /backup.pipe") .then(() => { - _this.log("Backup Ended"); - clearInterval(progressInterval); - _this - .shell("rm /backup.pipe") - .then(() => { - _this.log("Pipe released."); - resolve(); - }) - .catch(e => { - reject(e + ", Unable to delete the pipe "); - }); + _this.log("Pipe released."); + resolve(); }) .catch(e => { - clearInterval(progressInterval); - reject(e + ", Unable to backuping the device "); + reject(e + ", Unable to delete the pipe "); }); }) .catch(e => { - reject(e + ", Pipe creation failed "); + clearInterval(progressInterval); + reject(e + ", Unable to backuping the device "); }); }) .catch(e => { - reject(e + ", Unable to get the partition size "); + reject(e + ", Pipe creation failed "); }); }); } From 59da96c0b0701f4281a688a7c1dbc5f99c8b7c5c Mon Sep 17 00:00:00 2001 From: Jan Sprinz Date: Fri, 23 Oct 2020 12:03:28 +0200 Subject: [PATCH 15/26] remove need for _this hack --- src/adb.js | 80 ++++++++++++++++++++++-------------------------------- 1 file changed, 32 insertions(+), 48 deletions(-) diff --git a/src/adb.js b/src/adb.js index 5ce4e27..4717824 100644 --- a/src/adb.js +++ b/src/adb.js @@ -553,54 +553,38 @@ class Adb { // Backup file "srcfile" from the device to "destfile" localy createRemoteUbuntuBackup(srcfile, destfile, progress) { - var _this = this; - return new Promise(function(resolve, reject) { - // Get file size - _this - .shell("mkfifo /backup.pipe") - .then(() => _this.getFileSize(srcfile)) - .then(fileSize => { - // Creating pipe - var lastSize = 0; - var progressInterval = setInterval(() => { - const { size } = fs.statSync(destfile); - progress((lastSize / fileSize) * 100); - lastSize = size / 1024; - }, 1000); - - // Start the backup - _this.log("Starting Backup..."); - // FIXME replace shell pipe to dd with node stream - Promise.all([ - _this.execCommand([ - "exec-out 'tar -cvp ", - srcfile, - " 2>/backup.pipe' | dd of=" + destfile - ]), - _this.shell("cat /backup.pipe") - ]) - .then(() => { - _this.log("Backup Ended"); - clearInterval(progressInterval); - _this - .shell("rm /backup.pipe") - .then(() => { - _this.log("Pipe released."); - resolve(); - }) - .catch(e => { - reject(e + ", Unable to delete the pipe "); - }); - }) - .catch(e => { - clearInterval(progressInterval); - reject(e + ", Unable to backuping the device "); - }); - }) - .catch(e => { - reject(e + ", Pipe creation failed "); - }); - }); + return this.shell("mkfifo /backup.pipe") + .then(() => this.getFileSize(srcfile)) + .then(fileSize => { + var lastSize = 0; + var progressInterval = setInterval(() => { + const { size } = fs.statSync(destfile); + progress((lastSize / fileSize) * 100); + lastSize = size / 1024; + }, 1000); + + // FIXME replace shell pipe to dd with node stream + return Promise.all([ + this.execCommand([ + "exec-out 'tar -cvp ", + srcfile, + " 2>/backup.pipe' | dd of=" + destfile + ]), + this.shell("cat /backup.pipe") + ]) + .then(() => { + clearInterval(progressInterval); + progress(100); + }) + .catch(e => { + clearInterval(progressInterval); + throw new Error(e); + }); + }) + .then(() => this.shell("rm /backup.pipe")) + .catch(e => { + throw new Error(`Backup failed: ${e}`); + }); } // Restore file "srcfile" from the computer to "destfile" on the phone From 149843662c72dcdf59e0b06b744ef778e87a41d3 Mon Sep 17 00:00:00 2001 From: Jan Sprinz Date: Fri, 23 Oct 2020 12:54:45 +0200 Subject: [PATCH 16/26] Streamline restore function --- src/adb.js | 69 +++++++++++++++++------------------------------------- 1 file changed, 22 insertions(+), 47 deletions(-) diff --git a/src/adb.js b/src/adb.js index 4717824..fd1c869 100644 --- a/src/adb.js +++ b/src/adb.js @@ -173,6 +173,7 @@ class Adb { } var fileSize = fs.statSync(file)["size"]; var lastSize = 0; + // FIXME use stream and parse stdout instead of polling with stat var progressInterval = setInterval(() => { _this .shell([ @@ -551,16 +552,16 @@ class Adb { }); } - // Backup file "srcfile" from the device to "destfile" localy - createRemoteUbuntuBackup(srcfile, destfile, progress) { - return this.shell("mkfifo /backup.pipe") + // Backup "srcfile" from the device to local "destfile" + createBackupTar(srcfile, destfile, progress) { + return this.ensureState("recovery") + .then(() => this.shell("mkfifo /backup.pipe")) .then(() => this.getFileSize(srcfile)) .then(fileSize => { - var lastSize = 0; - var progressInterval = setInterval(() => { + progress(0); + const progressInterval = setInterval(() => { const { size } = fs.statSync(destfile); - progress((lastSize / fileSize) * 100); - lastSize = size / 1024; + progress((size / 1024 / fileSize) * 100); }, 1000); // FIXME replace shell pipe to dd with node stream @@ -587,46 +588,20 @@ class Adb { }); } - // Restore file "srcfile" from the computer to "destfile" on the phone - applyRemoteUbuntuBackup(destfile, srcfile, adbpath, bckSize, progress) { - var _this = this; - return new Promise(function(resolve, reject) { - // Creating pipe - _this - .shell("mkfifo /restore.pipe") - .then(stdout => { - _this.log("Pipe created !"); - // Start the restoration - _this.log("Starting Restore..."); - _this - .execCommand([ - "push", - srcfile, - "/restore.pipe &", - adbpath + "/adb -P", - _this.port, - " shell 'cd /; cat /restore.pipe | tar -xv'" - ]) - .then((error, stdout, stderr) => { - _this.log("Restore Ended"); - _this - .shell("rm /restore.pipe") - .then(() => { - _this.log("Pipe released."); - resolve(); // Everything's Fine ! - }) - .catch(e => { - reject(e); - }); // Pipe Deletion - }) - .catch(e => { - reject(e); - }); // Backup start - }) - .catch(e => { - reject(e); - }); // Pipe Creation - }); + // Restore file "srcfile" + restoreBackupTar(srcfile, progress) { + return this.ensureState("recovery") + .then(() => this.shell("mkfifo /restore.pipe")) + .then(() => + Promise.all([ + this.push(srcfile, "/restore.pipe"), + this.shell(["'cd /; cat /restore.pipe | tar -xv'"]) + ]) + ) + .then(() => this.shell(["rm", "/restore.pipe"])) + .catch(e => { + throw new Error(`Restore failed: ${e}`); + }); } } From 8c77510c81122216b45b4a9f56ab9fdfc0dc6ae3 Mon Sep 17 00:00:00 2001 From: Jan Sprinz Date: Fri, 23 Oct 2020 15:02:07 +0200 Subject: [PATCH 17/26] Add convenience function for full backup and listing of available backups --- package-lock.json | 33 +++++++++++++- package.json | 4 +- src/adb.js | 110 ++++++++++++++++++++++++++++++++++------------ 3 files changed, 116 insertions(+), 31 deletions(-) diff --git a/package-lock.json b/package-lock.json index aad0b32..48c1c94 100644 --- a/package-lock.json +++ b/package-lock.json @@ -519,6 +519,11 @@ "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", "dev": true }, + "at-least-node": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", + "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==" + }, "aws-sign2": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", @@ -1263,6 +1268,17 @@ "integrity": "sha512-33X7H/wdfO99GdRLLgkjUrD4geAFdq/Uv0kl3HD4da6HDixd2GUg8Mw7dahLCV9r/EARkmtYBB6Tch4EEokFTQ==", "dev": true }, + "fs-extra": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.0.1.tgz", + "integrity": "sha512-h2iAoN838FqAFJY2/qVpzFXy+EBxfVE220PalAqQLDVsFOHLJrZvut5puAbCdNv6WJk+B8ihI+k0c7JK5erwqQ==", + "requires": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^1.0.0" + } + }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -1352,8 +1368,7 @@ "graceful-fs": { "version": "4.2.4", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", - "dev": true + "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==" }, "growl": { "version": "1.10.5", @@ -1862,6 +1877,15 @@ "minimist": "^1.2.5" } }, + "jsonfile": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.0.1.tgz", + "integrity": "sha512-jR2b5v7d2vIOust+w3wtFKZIfpC2pnRmFAhAC/BuweZFQR8qZzxH1OyrQ10HmdVYiXWkYUqPVsz91cG7EL2FBg==", + "requires": { + "graceful-fs": "^4.1.6", + "universalify": "^1.0.0" + } + }, "jsprim": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", @@ -3092,6 +3116,11 @@ "is-typedarray": "^1.0.0" } }, + "universalify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-1.0.0.tgz", + "integrity": "sha512-rb6X1W158d7pRQBg5gkR8uPaSfiids68LTJQYOtEUhoJUWBdaQHsuT/EUduxXYxcrt4r5PJ4fuHW1MHT6p0qug==" + }, "uri-js": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", diff --git a/package.json b/package.json index 72e4ab8..a7a43d3 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,9 @@ "ubuntu-touch" ], "homepage": "https://github.com/ubports/promise-android-tools#readme", - "dependencies": {}, + "dependencies": { + "fs-extra": "^9.0.1" + }, "devDependencies": { "chai": "^4.1.2", "chai-as-promised": "^7.1.1", diff --git a/src/adb.js b/src/adb.js index fd1c869..9df7740 100644 --- a/src/adb.js +++ b/src/adb.js @@ -17,7 +17,7 @@ * along with this program. If not, see . */ -const fs = require("fs"); +const fs = require("fs-extra"); const path = require("path"); const exec = require("child_process").exec; const events = require("events"); @@ -515,49 +515,44 @@ class Adb { }); } - // Return the file size of a complete folder + // size of a file or directory getFileSize(file) { - // TODO verify that state detection is needed - return this.getState() - .then(state => - this.shell("du -shk " + file + (state == "device" ? " |tail -n1" : "")) - ) + return this.shell("du -shk " + file) .then(stdout => parseFloat(stdout)) .catch(e => { throw new Error(`Unable to get size: ${e}`); }); } - // Return the available size of a partition - getAvailableSize(partition) { - return this.ensureState("device") - .then(() => - this.shell("df -hBK " + partition + " --output=avail|tail -n1") - ) - .then(stdout => parseFloat(stdout)) + // available size of a partition + getAvailablePartitionSize(partition) { + return this.shell("df -k -P " + partition) + .then(stdout => stdout.split(/[ ,]+/)) + .then(arr => parseInt(arr[arr.length - 3])) .catch(e => { throw new Error(`Unable to get size: ${e}`); }); } - // Return the total size of a partition - getTotalSize(partition) { - return this.ensureState("device") - .then(() => - this.shell("df -hBK " + partition + " --output=size|tail -n1") - ) - .then(stdout => parseFloat(stdout)) + // total size of a partition + getTotalPartitionSize(partition) { + return this.shell("df -k -P " + partition) + .then(stdout => stdout.split(/[ ,]+/)) + .then(arr => parseInt(arr[arr.length - 5])) .catch(e => { throw new Error(`Unable to get size: ${e}`); }); } - // Backup "srcfile" from the device to local "destfile" + // Backup "srcfile" from the device to local tar "destfile" createBackupTar(srcfile, destfile, progress) { - return this.ensureState("recovery") - .then(() => this.shell("mkfifo /backup.pipe")) - .then(() => this.getFileSize(srcfile)) - .then(fileSize => { + return Promise.all([ + this.ensureState("recovery") + .then(() => this.shell("mkfifo /backup.pipe")) + .then(() => this.getFileSize(srcfile)), + fs.ensureFile(destfile) + ]) + .then(([fileSize]) => { progress(0); const progressInterval = setInterval(() => { const { size } = fs.statSync(destfile); @@ -588,8 +583,8 @@ class Adb { }); } - // Restore file "srcfile" - restoreBackupTar(srcfile, progress) { + // Restore tar "srcfile" + restoreBackupTar(srcfile) { return this.ensureState("recovery") .then(() => this.shell("mkfifo /restore.pipe")) .then(() => @@ -603,6 +598,65 @@ class Adb { throw new Error(`Restore failed: ${e}`); }); } + + listUbuntuBackups(backupBaseDir = "/tmp/utbackups") { + return fs.readdir(backupBaseDir).then(backups => + Promise.all( + backups.map(backup => + fs + .readFile(path.join(backupBaseDir, backup, "metadata.json")) + .then(metadataBuffer => ({ + ...JSON.parse(metadataBuffer.toString()), + dir: path.join(backupBaseDir, backup) + })) + .catch(() => null) + ) + ).then(r => r.filter(r => r)) + ); + } + + async createUbuntuBackup( + backupBaseDir = "/tmp/utbackups", + dataPartition = "/data", + progress = () => {} + ) { + const codename = await this.getDeviceName(); + const serialno = await this.getSerialno(); + const size = + (await this.getFileSize("/data/user-data")) + + (await this.getFileSize("/data/system-data")); + const time = new Date(); + const dir = await fs.ensureDir( + path.join(backupBaseDir, time.toISOString()) + ); + return this.ensureState("recovery") + .then(() => + Promise.all([ + this.shell(["stat", "/data/user-data"]), + this.shell(["stat", "/data/syste-mdata"]) + ]).catch(() => this.shell(["mount", dataPartition, "/data"])) + ) + .then(() => + this.createBackupTar( + "/data/system-data", + path.join(dir, "system.tar"), + p => progress(p * 0.5) + ) + ) + .then(() => + this.createBackupTar("/data/user-data", path.join(dir, "user.tar"), p => + progress(50 + p * 0.5) + ) + ) + .then(() => { + fs.writeJSON(path.join(dir, "metadata.json"), { + codename, + serialno, + size, + time + }); + }); + } } module.exports = Adb; From 7cbc8cd1219dba7f0240c94e1b186e96c9d7c468 Mon Sep 17 00:00:00 2001 From: Jan Sprinz Date: Fri, 23 Oct 2020 19:54:28 +0200 Subject: [PATCH 18/26] Add restore function and enable gzip compression --- src/adb.js | 124 +++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 87 insertions(+), 37 deletions(-) diff --git a/src/adb.js b/src/adb.js index 9df7740..7bbd475 100644 --- a/src/adb.js +++ b/src/adb.js @@ -276,7 +276,7 @@ class Adb { currentState === state || (currentState === "device" && state === "system") ? Promise.resolve() - : this.reboot(state) + : this.reboot(state).then(() => this.waitForDevice()) ); } @@ -518,7 +518,11 @@ class Adb { // size of a file or directory getFileSize(file) { return this.shell("du -shk " + file) - .then(stdout => parseFloat(stdout)) + .then(size => { + if (isNaN(parseFloat(size))) + throw new Error(`Cannot parse size from ${size}`); + else return parseFloat(size); + }) .catch(e => { throw new Error(`Unable to get size: ${e}`); }); @@ -554,6 +558,7 @@ class Adb { ]) .then(([fileSize]) => { progress(0); + // FIXME with gzip compression (the -z flag on tar), the progress estimate is way off. It's still beneficial to enable it, because it saves a lot of space. const progressInterval = setInterval(() => { const { size } = fs.statSync(destfile); progress((size / 1024 / fileSize) * 100); @@ -562,7 +567,13 @@ class Adb { // FIXME replace shell pipe to dd with node stream return Promise.all([ this.execCommand([ - "exec-out 'tar -cvp ", + "exec-out 'tar -cpz " + + "--exclude=*/var/cache " + + "--exclude=*/var/log " + + "--exclude=*/.cache/upstart " + + "--exclude=*/.cache/*.qmlc " + + "--exclude=*/.cache/*/qmlcache " + + "--exclude=*/.cache/*/qml_cache", srcfile, " 2>/backup.pipe' | dd of=" + destfile ]), @@ -590,7 +601,7 @@ class Adb { .then(() => Promise.all([ this.push(srcfile, "/restore.pipe"), - this.shell(["'cd /; cat /restore.pipe | tar -xv'"]) + this.shell(["'cd /; cat /restore.pipe | tar -xvz'"]) ]) ) .then(() => this.shell(["rm", "/restore.pipe"])) @@ -599,37 +610,35 @@ class Adb { }); } - listUbuntuBackups(backupBaseDir = "/tmp/utbackups") { - return fs.readdir(backupBaseDir).then(backups => - Promise.all( - backups.map(backup => - fs - .readFile(path.join(backupBaseDir, backup, "metadata.json")) - .then(metadataBuffer => ({ - ...JSON.parse(metadataBuffer.toString()), - dir: path.join(backupBaseDir, backup) - })) - .catch(() => null) - ) - ).then(r => r.filter(r => r)) - ); + listUbuntuBackups(backupBaseDir) { + return fs + .readdir(backupBaseDir) + .then(backups => + Promise.all( + backups.map(backup => + fs + .readFile(path.join(backupBaseDir, backup, "metadata.json")) + .then(metadataBuffer => ({ + ...JSON.parse(metadataBuffer.toString()), + dir: path.join(backupBaseDir, backup) + })) + .catch(() => null) + ) + ).then(r => r.filter(r => r)) + ) + .catch(() => []); } - async createUbuntuBackup( - backupBaseDir = "/tmp/utbackups", + async createUbuntuTouchBackup( + backupBaseDir, + comment, dataPartition = "/data", progress = () => {} ) { - const codename = await this.getDeviceName(); - const serialno = await this.getSerialno(); - const size = - (await this.getFileSize("/data/user-data")) + - (await this.getFileSize("/data/system-data")); const time = new Date(); - const dir = await fs.ensureDir( - path.join(backupBaseDir, time.toISOString()) - ); + const dir = path.join(backupBaseDir, time.toISOString()); return this.ensureState("recovery") + .then(() => fs.ensureDir(dir)) .then(() => Promise.all([ this.shell(["stat", "/data/user-data"]), @@ -639,22 +648,63 @@ class Adb { .then(() => this.createBackupTar( "/data/system-data", - path.join(dir, "system.tar"), + path.join(dir, "system.tar.gz"), p => progress(p * 0.5) ) ) .then(() => - this.createBackupTar("/data/user-data", path.join(dir, "user.tar"), p => - progress(50 + p * 0.5) + this.createBackupTar( + "/data/user-data", + path.join(dir, "user.tar.gz"), + p => progress(50 + p * 0.5) ) ) - .then(() => { - fs.writeJSON(path.join(dir, "metadata.json"), { - codename, - serialno, - size, - time + .then(async () => { + const metadata = { + codename: await this.getDeviceName(), + serialno: await this.getSerialno(), + size: + (await this.getFileSize("/data/user-data")) + + (await this.getFileSize("/data/system-data")), + time, + comment: + comment || `Ubuntu Touch backup created on ${time.toISOString()}`, + restorations: [] + }; + return fs + .writeJSON(path.join(dir, "metadata.json"), metadata) + .then(() => ({ ...metadata, dir })) + .catch(e => { + throw new Error(`Failed to restore: ${e}`); + }); + }); + } + + async restoreUbuntuTouchBackup(dir, progress = () => {}) { + progress(0); // FIXME report actual push progress + let metadata = JSON.parse( + await fs.readFile(path.join(dir, "metadata.json")) + ); + return this.ensureState("recovery") + .then(async () => { + metadata.restorations = metadata.restorations || []; + metadata.restorations.push({ + codename: await this.getDeviceName(), + serialno: await this.getSerialno(), + time: new Date().toISOString() }); + }) + .then(() => progress(10)) + .then(() => this.restoreBackupTar(path.join(dir, "system.tar.gz"))) + .then(() => progress(50)) + .then(() => this.restoreBackupTar(path.join(dir, "user.tar.gz"))) + .then(() => progress(90)) + .then(() => fs.writeJSON(path.join(dir, "metadata.json"), metadata)) + .then(() => this.reboot("system")) + .then(() => progress(100)) + .then(() => ({ ...metadata, dir })) + .catch(e => { + throw new Error(`Failed to restore: ${e}`); }); } } From 9b5635786629b124ed55b3a36a9e744ca7d55ce4 Mon Sep 17 00:00:00 2001 From: Jan Sprinz Date: Fri, 23 Oct 2020 20:01:37 +0200 Subject: [PATCH 19/26] Remove node 8 --- appveyor.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index 4238b84..feb669c 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -3,7 +3,6 @@ cache: - node_modules environment: matrix: - - nodejs_version: 8 - nodejs_version: 10 - nodejs_version: 12 - nodejs_version: 14 From 2da33127d15ef8f0e2612700220dda700ed255bb Mon Sep 17 00:00:00 2001 From: Jan Sprinz Date: Fri, 23 Oct 2020 20:01:56 +0200 Subject: [PATCH 20/26] Remove node 8 --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 549aeb5..1954c70 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,7 +9,6 @@ matrix: - os: osx - os: linux node_js: - - "8" - "10" - "12" - "14" From 0342d634f8828afa858d51142ed0177a8e6018f2 Mon Sep 17 00:00:00 2001 From: Jan Sprinz Date: Fri, 23 Oct 2020 20:08:08 +0200 Subject: [PATCH 21/26] Add test for getState() --- tests/unit-tests/test_adb.js | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/tests/unit-tests/test_adb.js b/tests/unit-tests/test_adb.js index cb70ffc..ad06dc0 100644 --- a/tests/unit-tests/test_adb.js +++ b/tests/unit-tests/test_adb.js @@ -519,6 +519,22 @@ describe("Adb module", function() { it("should reject if package inaccessible"); it("should reject on error"); }); + describe("getState()", function() { + it("should resolve state", function() { + const execFake = sinon.fake((args, callback) => { + callback(null, "recovery", null); + }); + const logSpy = sinon.spy(); + const adb = new Adb({ exec: execFake, log: logSpy }); + return adb.getState().then(() => { + expect(execFake).to.have.been.calledWith([ + "-P", + 5037, + "get-state" + ]); + }); + }); + }); }); describe("convenience functions", function() { From 77e15892a5ed94fe987fdb3c8a754d6bae7d0a43 Mon Sep 17 00:00:00 2001 From: Jan Sprinz Date: Fri, 23 Oct 2020 20:19:29 +0200 Subject: [PATCH 22/26] Add test for ensureState --- tests/unit-tests/test_adb.js | 40 +++++++++++++++++++++++++++++++----- 1 file changed, 35 insertions(+), 5 deletions(-) diff --git a/tests/unit-tests/test_adb.js b/tests/unit-tests/test_adb.js index ad06dc0..a4632bc 100644 --- a/tests/unit-tests/test_adb.js +++ b/tests/unit-tests/test_adb.js @@ -527,17 +527,47 @@ describe("Adb module", function() { const logSpy = sinon.spy(); const adb = new Adb({ exec: execFake, log: logSpy }); return adb.getState().then(() => { - expect(execFake).to.have.been.calledWith([ - "-P", - 5037, - "get-state" - ]); + expect(execFake).to.have.been.calledWith(["-P", 5037, "get-state"]); }); }); }); }); describe("convenience functions", function() { + describe("ensureState()", function() { + it("should resolve if already in requested state", function() { + const execFake = sinon.fake((args, callback) => { + callback(null, "recovery", null); + }); + const logSpy = sinon.spy(); + const adb = new Adb({ exec: execFake, log: logSpy }); + return adb.ensureState("recovery").then(() => { + expect(execFake).to.have.been.calledWith(["-P", 5037, "get-state"]); + }); + }); + it("should properly handle device state", function() { + const execFake = sinon.fake((args, callback) => { + callback(null, "device", null); + }); + const logSpy = sinon.spy(); + const adb = new Adb({ exec: execFake, log: logSpy }); + return adb.ensureState("system").then(() => { + expect(execFake).to.have.been.calledWith(["-P", 5037, "get-state"]); + }); + }); + it("should reboot to correct state", function() { + const execFake = sinon.fake((args, callback) => { + callback(null, "recovery", null); + }); + const logSpy = sinon.spy(); + const adb = new Adb({ exec: execFake, log: logSpy }); + sinon.stub(adb, "reboot").resolves(); + sinon.stub(adb, "waitForDevice").resolves(); + return adb.ensureState("system").then(() => { + expect(execFake).to.have.been.calledWith(["-P", 5037, "get-state"]); + }); + }); + }); describe("pushArray()", function() { it("should resolve on empty array", function() { const execFake = sinon.spy(); From d5e54f1caf1d402f9257da6e6c35b54187df679f Mon Sep 17 00:00:00 2001 From: Jan Sprinz Date: Fri, 23 Oct 2020 20:24:50 +0200 Subject: [PATCH 23/26] More tests for reboot function --- tests/unit-tests/test_adb.js | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/tests/unit-tests/test_adb.js b/tests/unit-tests/test_adb.js index a4632bc..76e6dcf 100644 --- a/tests/unit-tests/test_adb.js +++ b/tests/unit-tests/test_adb.js @@ -379,6 +379,38 @@ describe("Adb module", function() { }); }); }); + it("should reject on failure in stdout", function(done) { + const execFake = sinon.fake((args, callback) => { + callback(null, "failed", null); + }); + const logSpy = sinon.spy(); + const adb = new Adb({ exec: execFake, log: logSpy }); + adb.reboot("bootloader").catch(() => { + expect(execFake).to.have.been.calledWith([ + "-P", + 5037, + "reboot", + "bootloader" + ]); + done(); + }); + }); + it("should reject on error", function(done) { + const execFake = sinon.fake((args, callback) => { + callback(666, "everything exploded", "what!?"); + }); + const logSpy = sinon.spy(); + const adb = new Adb({ exec: execFake, log: logSpy }); + adb.reboot("bootloader").catch(() => { + expect(execFake).to.have.been.calledWith([ + "-P", + 5037, + "reboot", + "bootloader" + ]); + done(); + }); + }); it("should reject on invalid state", function() { const execFake = sinon.spy(); const logSpy = sinon.spy(); From 6bc30bdf741504378e56b85600f349d76a440637 Mon Sep 17 00:00:00 2001 From: Jan Sprinz Date: Fri, 23 Oct 2020 20:25:24 +0200 Subject: [PATCH 24/26] Remove deprecated backup and restore functions --- tests/unit-tests/test_adb.js | 8 -------- 1 file changed, 8 deletions(-) diff --git a/tests/unit-tests/test_adb.js b/tests/unit-tests/test_adb.js index 76e6dcf..2dad66b 100644 --- a/tests/unit-tests/test_adb.js +++ b/tests/unit-tests/test_adb.js @@ -420,14 +420,6 @@ describe("Adb module", function() { ); }); }); - describe("backup()", function() { - it("should create backup"); - it("should reject if backup failed"); - }); - describe("restore()", function() { - it("should restore backup"); - it("should reject if backup failed"); - }); describe("forward()", function() { it("should create forward connection"); it("should not rebind forward connection"); From de901d5d230a8fcc3b0e23a6afd79b5d8e58d5ed Mon Sep 17 00:00:00 2001 From: Jan Sprinz Date: Fri, 23 Oct 2020 20:31:44 +0200 Subject: [PATCH 25/26] Add test for getFileSize --- tests/unit-tests/test_adb.js | 35 +++++++++++++++++++++++++++++------ 1 file changed, 29 insertions(+), 6 deletions(-) diff --git a/tests/unit-tests/test_adb.js b/tests/unit-tests/test_adb.js index 2dad66b..ae142f0 100644 --- a/tests/unit-tests/test_adb.js +++ b/tests/unit-tests/test_adb.js @@ -468,12 +468,6 @@ describe("Adb module", function() { it("should reject if no host specified"); it("should reject on error"); }); - describe("getState()", function() { - it("should resolve offline"); - it("should resolve bootloader"); - it("should resolve device"); - it("should reject on error"); - }); describe("ppp()", function() { it("should run PPP over USB"); it("should reject on error"); @@ -1111,5 +1105,34 @@ describe("Adb module", function() { }); }); }); + describe("getFileSize()", function() { + it("should resolve file size", function() { + const execFake = sinon.fake((args, callback) => { + callback(null, "1337", null); + }); + const logSpy = sinon.spy(); + const adb = new Adb({ exec: execFake, log: logSpy }); + return adb.getFileSize("/wtf").then(size => { + expect(size).to.eql(1337); + expect(execFake).to.have.been.calledWith([ + "-P", + 5037, + "shell", + "du -shk /wtf" + ]); + }); + }); + it("should reject on invalid response file size", function(done) { + const execFake = sinon.fake((args, callback) => { + callback(null, "invalid response :)", null); + }); + const logSpy = sinon.spy(); + const adb = new Adb({ exec: execFake, log: logSpy }); + adb.getFileSize().catch(() => { + expect(execFake).to.have.been.calledOnce; + done(); + }); + }); + }); }); }); From 2665fb4cd117f5472d99c1190aa92c673bbfdbee Mon Sep 17 00:00:00 2001 From: Jan Sprinz Date: Fri, 23 Oct 2020 20:42:51 +0200 Subject: [PATCH 26/26] Test size functions --- src/adb.js | 8 ++++ tests/unit-tests/test_adb.js | 80 ++++++++++++++++++++++++++++++++++++ 2 files changed, 88 insertions(+) diff --git a/src/adb.js b/src/adb.js index 7bbd475..c32d660 100644 --- a/src/adb.js +++ b/src/adb.js @@ -533,6 +533,10 @@ class Adb { return this.shell("df -k -P " + partition) .then(stdout => stdout.split(/[ ,]+/)) .then(arr => parseInt(arr[arr.length - 3])) + .then(size => { + if (isNaN(size)) throw new Error(`Cannot parse size from ${size}`); + else return size; + }) .catch(e => { throw new Error(`Unable to get size: ${e}`); }); @@ -543,6 +547,10 @@ class Adb { return this.shell("df -k -P " + partition) .then(stdout => stdout.split(/[ ,]+/)) .then(arr => parseInt(arr[arr.length - 5])) + .then(size => { + if (isNaN(size)) throw new Error(`Cannot parse size from ${size}`); + else return size; + }) .catch(e => { throw new Error(`Unable to get size: ${e}`); }); diff --git a/tests/unit-tests/test_adb.js b/tests/unit-tests/test_adb.js index ae142f0..9f2f716 100644 --- a/tests/unit-tests/test_adb.js +++ b/tests/unit-tests/test_adb.js @@ -1134,5 +1134,85 @@ describe("Adb module", function() { }); }); }); + describe("getAvailablePartitionSize()", function() { + it("should resolve available partition size", function() { + const execFake = sinon.fake((args, callback) => { + callback(null, "a\n/wtf 1337 a b", null); + }); + const logSpy = sinon.spy(); + const adb = new Adb({ exec: execFake, log: logSpy }); + return adb.getAvailablePartitionSize("/wtf").then(size => { + expect(size).to.eql(1337); + expect(execFake).to.have.been.calledWith([ + "-P", + 5037, + "shell", + "df -k -P /wtf" + ]); + }); + }); + it("should reject on invalid response", function(done) { + const execFake = sinon.fake((args, callback) => { + callback(null, "invalid response :)", null); + }); + const logSpy = sinon.spy(); + const adb = new Adb({ exec: execFake, log: logSpy }); + adb.getAvailablePartitionSize("/wtf").catch(() => { + expect(execFake).to.have.been.calledOnce; + done(); + }); + }); + it("should reject on error", function(done) { + const execFake = sinon.fake((args, callback) => { + callback(69, "invalid response :)", null); + }); + const logSpy = sinon.spy(); + const adb = new Adb({ exec: execFake, log: logSpy }); + adb.getAvailablePartitionSize().catch(() => { + expect(execFake).to.have.been.calledOnce; + done(); + }); + }); + }); + describe("getTotalPartitionSize()", function() { + it("should resolve available partition size", function() { + const execFake = sinon.fake((args, callback) => { + callback(null, "a\n/wtf 1337 a b c d", null); + }); + const logSpy = sinon.spy(); + const adb = new Adb({ exec: execFake, log: logSpy }); + return adb.getTotalPartitionSize("/wtf").then(size => { + expect(size).to.eql(1337); + expect(execFake).to.have.been.calledWith([ + "-P", + 5037, + "shell", + "df -k -P /wtf" + ]); + }); + }); + it("should reject on invalid response", function(done) { + const execFake = sinon.fake((args, callback) => { + callback(null, "invalid response :)", null); + }); + const logSpy = sinon.spy(); + const adb = new Adb({ exec: execFake, log: logSpy }); + adb.getTotalPartitionSize("/wtf").catch(() => { + expect(execFake).to.have.been.calledOnce; + done(); + }); + }); + it("should reject on error", function(done) { + const execFake = sinon.fake((args, callback) => { + callback(69, "invalid response :)", null); + }); + const logSpy = sinon.spy(); + const adb = new Adb({ exec: execFake, log: logSpy }); + adb.getTotalPartitionSize().catch(() => { + expect(execFake).to.have.been.calledOnce; + done(); + }); + }); + }); }); });