finish migrating install logic

This commit is contained in:
Jonathan Lipps
2013-10-21 09:47:40 -07:00
parent 680d09a77a
commit 65bb08d84e
3 changed files with 124 additions and 126 deletions

View File

@@ -147,6 +147,11 @@ ADB.prototype.exec = function(cmd, cb) {
});
};
ADB.prototype.shell = function(cmd, cb) {
var execCmd = 'shell ' + cmd;
this.exec(execCmd, cb);
};
ADB.prototype.insertSelendroidManifest = function(serverPath, cb) {
logger.info("Inserting selendroid manifest");
var newServerPath = this.selendroidServerPath
@@ -409,7 +414,7 @@ ADB.prototype.checkApkCert = function(apk, cb) {
function(cb) { checkKeystoreMD5(cb); },
function(cb) { checkApkMD5(cb); }
], function() { logger.debug("checkApkCert match? " + match);
cb(match); });
cb(null, match); });
// exit checkApkCert
return;
@@ -421,10 +426,10 @@ ADB.prototype.checkApkCert = function(apk, cb) {
exec(resign, { maxBuffer: 524288 }, function(err) {
if (err) {
logger.debug("App not signed with debug cert.");
return cb(false);
return cb(null, false);
}
logger.debug("App already signed.");
cb(true);
cb(null, true);
});
};
@@ -478,7 +483,7 @@ ADB.prototype.getEmulatorPort = function(cb) {
};
ADB.prototype.rimraf = function(path, cb) {
this.exec('shell rm -rf ' + path, cb);
this.shell('rm -rf ' + path, cb);
};
ADB.prototype.push = function(localPath, remotePath, cb) {
@@ -832,7 +837,7 @@ ADB.prototype.waitForDevice = function(cb) {
movedOn = true;
innerCb(err);
} else {
this.exec("shell echo 'ready'", function(err) {
this.shell("echo 'ready'", function(err) {
if (!movedOn) {
movedOn = true;
if (err) {
@@ -1123,101 +1128,38 @@ ADB.prototype.installApk = function(apk, cb) {
});
};
ADB.prototype.removeOldApks = function(cb) {
var listApks = function(cb) {
var cmd = this.adbCmd + ' shell "ls /data/local/tmp/*.apk"';
logger.info("listApks: " + cmd);
exec(cmd, { maxBuffer: 524288 }, function(err, stdout) {
if (err || stdout.indexOf("No such file") !== -1) {
return cb(null, []);
}
var apks = stdout.split("\n");
cb(null, apks);
});
}.bind(this);
var removeOtherApks = function(apks, cb) {
var matchingApkFound = false;
var removeString = "";
_.each(apks, function(path) {
path = path.trim();
if (path.indexOf(this.appMD5) === -1 && path !== '') {
removeString += ' rm \\"' + path + '\\";';
logger.info("removeOtherApks pushing: " + removeString);
} else {
matchingApkFound = true;
}
}.bind(this));
// Invoking adb shell with an empty string will open a shell console
// so return here if there's nothing to remove.
if (removeString === '') {
return cb(null, matchingApkFound);
}
var cmd = this.adbCmd + ' shell "' + removeString + '"';
logger.info("removeOtherApks: " + cmd);
exec(cmd, { maxBuffer: 524288 }, function(err, stdout) {
if (stdout) logger.info(stdout);
if (err) logger.info(err);
cb(null, matchingApkFound);
});
}.bind(this);
async.waterfall([
function(cb){ listApks(cb); },
function(apks, cb) { removeOtherApks(apks, cb); }
], function(err, matchingApkFound) { cb(null, matchingApkFound); });
ADB.prototype.installRemote = function(remoteApk, cb) {
var cmd = 'pm install -r ' + remoteApk;
this.shell(cmd, cb);
};
ADB.prototype.installApp = function(apk, cb) {
var remotePath = '';
ADB.prototype.install = function(apk, cb) {
var cmd = 'install -r ' + apk;
this.exec(cmd, cb);
};
var getMD5 = function(cb) {
fs.readFile(apk, function(err, buffer) {
this.appMD5 = md5(buffer);
remotePath = '/data/local/tmp/' + this.appMD5 + '.apk';
ADB.prototype.mkdir = function(remotePath, cb) {
this.shell('mkdir -p ' + remotePath, cb);
};
ADB.prototype.checkAndSignApk = function(apk, cb) {
this.checkApkCert(apk, function(err, appSigned) {
if (err) return cb(err);
if (!appSigned) {
this.sign([apk], cb);
} else {
cb();
}.bind(this));
}.bind(this);
var makeFolder = function(cb) {
this.exec('shell "mkdir /data/local/tmp/"', cb);
}.bind(this);
var pushApp = function(cb) {
this.push(apk, remotePath, cb);
}.bind(this);
var installFromDevice = function(cb) {
var cmd = 'shell "pm install -r ' + remotePath + '"';
this.exec(cmd, cb);
}.bind(this);
var signLocalApk = function(cb) {
this.checkApkCert(apk, function(appSigned) {
if (!appSigned) {
this.sign([apk], cb);
} else {
cb();
}
}.bind(this));
}.bind(this);
async.waterfall([
function(cb) { signLocalApk(cb); },
function(cb) { makeFolder(cb); },
function(cb) { getMD5(cb); },
function(cb) { this.removeOldApks(cb); }.bind(this),
// If the apk is already on device, then don't push it again.
function(matchingApkFound, cb) {
if (matchingApkFound) { cb(null); } else { pushApp(cb); }
},
function(cb) {
installFromDevice(cb);
}
], cb);
}.bind(this));
};
ADB.prototype.pushAndInstallApp = function(apk, remoteApk, cb) {
this.push(apk, remoteApk, function(err) {
if (err) return err;
this.installRemote(remoteApk, cb);
});
};
ADB.prototype.runFastReset = function(cb) {
@@ -1250,8 +1192,8 @@ ADB.prototype.isAppInstalled = function(pkg, cb) {
var installed = false;
logger.debug("Getting install status for " + pkg);
var listPkgCmd = "shell pm list packages -3 " + pkg;
this.exec(listPkgCmd, function(err, stdout) {
var listPkgCmd = "pm list packages -3 " + pkg;
this.shell(listPkgCmd, function(err, stdout) {
if (err) return cb(err);
var apkInstalledRgx = new RegExp('^package:' +
pkg.replace(/([^a-zA-Z])/g, "\\$1") + '$', 'm');

View File

@@ -2,6 +2,7 @@
var logger = require('../../server/logger.js').get('appium')
, _ = require('underscore')
, fs = require('fs')
, md5 = require('MD5')
, async = require('async');
var androidCommon = {};
@@ -74,44 +75,100 @@ androidCommon.prepareActiveDevice = function(cb) {
};
androidCommon.installApp = function(cb) {
var installApp = false;
if (this.apkPath === null) {
logger.info("Not installing app since we launched with a package instead " +
logger.info("Skipping install since we launched with a package instead " +
"of an app path");
return cb();
}
var determineInstallStatus = function(cb) {
logger.info("Determining app install");
this.adb.isAppInstalled(this.appPackage, function(err, installed) {
installApp = !installed;
cb();
});
}.bind(this);
var doInstall = function(cb) {
if (installApp) {
this.debug("Installing app apk");
this.adb.installApp(this.apkPath, cb);
this.adb.isAppInstalled(this.appPackage, function(err, installed) {
if (installed && this.fastReset) {
this.runFastReset(cb);
} else if (!installed) {
this.adb.checkAndSignApk(this.apkPath, function(err) {
if (err) return cb(err);
this.adb.mkdir(this.remoteTempPath(), function(err) {
if (err) return cb(err);
this.getAppMd5(function(err, md5) {
var remoteApk = this.remoteTempPath() + md5 + '.apk';
if (err) return cb(err);
this.removeTempApks([md5], function(err, appExists) {
if (err) return cb(err);
if (appExists) return cb();
this.adb.pushAndInstallApp(this.apkPath, remoteApk, cb);
}.bind(this));
}.bind(this));
}.bind(this));
}.bind(this));
} else {
cb();
}
}.bind(this);
var doFastReset = function(cb) {
// App is already installed so reset it.
if (!installApp && this.fastReset) {
this.runFastReset(cb);
} else { cb(null); }
}.bind(this);
async.series([
function(cb) { determineInstallStatus(cb); },
function(cb) { doInstall(cb); },
function(cb) { doFastReset(cb); }
], cb);
}.bind(this));
};
androidCommon.getAppMd5 = function(cb) {
fs.readFile(this.apkPath, function(err, buffer) {
if (err) return cb(err);
cb(md5(buffer));
}.bind(this));
};
androidCommon.remoteTempPath = function() {
return "/data/local/tmp/";
};
androidCommon.removeTempApks = function(exceptMd5s, cb) {
if (typeof exceptMd5s === "function") {
cb = exceptMd5s;
exceptMd5s = [];
}
var listApks = function(cb) {
var cmd = 'ls /data/local/tmp/*.apk';
this.adb.shell(cmd, function(err, stdout) {
if (err || stdout.indexOf("No such file") !== -1) {
return cb(null, []);
}
var apks = stdout.split("\n");
cb(null, apks);
});
}.bind(this);
var removeApks = function(apks, cb) {
var matchingApkFound = false;
var noMd5Matched = true;
var removes = [];
_.each(apks, function(path) {
path = path.trim();
noMd5Matched = true;
_.each(exceptMd5s, function(md5) {
if (path !== '' && path.indexOf(md5) !== -1) {
noMd5Matched = false;
}
});
if (noMd5Matched) {
removes.push('rm \\"' + path + '\\"');
} else {
matchingApkFound = true;
}
});
// Invoking adb shell with an empty string will open a shell console
// so return here if there's nothing to remove.
if (removes.length < 1) {
return cb(null, matchingApkFound);
}
var cmd = removes.join[" && "];
this.adb.shell(cmd, function() {
cb(null, matchingApkFound);
});
}.bind(this);
async.waterfall([
function(cb) { listApks(cb); },
function(apks, cb) { removeApks(apks, cb); }
], function(err, matchingApkFound) { cb(null, matchingApkFound); });
};
module.exports = androidCommon;

View File

@@ -4,7 +4,6 @@ var uuid = require('uuid-js')
, fs = require('fs')
, _ = require('underscore')
, status = require("../../server/status.js")
, async = require('async')
, logger = require('../../server/logger.js').get('appium')
, helpers = require('../../helpers.js')
, escapeSpecialChars = helpers.escapeSpecialChars