mirror of
https://github.com/appium/appium.git
synced 2026-02-09 11:18:51 -06:00
run commands from uiautomator.js, and migrate activity waiting methods
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
"use strict";
|
||||
|
||||
|
||||
var spawn = require('win-spawn')
|
||||
, exec = require('child_process').exec
|
||||
, path = require('path')
|
||||
@@ -8,7 +9,6 @@ var spawn = require('win-spawn')
|
||||
, logger = require('../../server/logger.js').get('appium')
|
||||
, async = require('async')
|
||||
, ncp = require('ncp')
|
||||
, mkdirp = require('mkdirp')
|
||||
, _ = require('underscore')
|
||||
, helpers = require('../../helpers.js')
|
||||
, unzipFile = helpers.unzipFile
|
||||
@@ -32,14 +32,6 @@ var ADB = function(opts, android) {
|
||||
this.compressXml = opts.compressXml;
|
||||
this.sdkRoot = opts.sdkRoot;
|
||||
this.udid = opts.udid;
|
||||
// Don't uninstall if using fast reset.
|
||||
// Uninstall if reset is set and fast reset isn't.
|
||||
this.skipUninstall = opts.fastReset || !opts.reset || false;
|
||||
this.fastReset = opts.fastReset;
|
||||
this.cleanApp = opts.cleanApp || this.fastReset;
|
||||
this.systemPort = opts.systemPort || 4724;
|
||||
this.internalDevicePort = opts.devicePort || 4724;
|
||||
this.avdName = opts.avdName;
|
||||
this.appPackage = opts.appPackage;
|
||||
this.appActivity = opts.appActivity;
|
||||
this.appWaitActivity = opts.appWaitActivity;
|
||||
@@ -155,32 +147,6 @@ ADB.prototype.spawn = function(args) {
|
||||
return spawn(adbCmd, args);
|
||||
};
|
||||
|
||||
ADB.prototype.insertSelendroidManifest = function(serverPath, cb) {
|
||||
logger.info("Inserting selendroid manifest");
|
||||
var newServerPath = this.selendroidServerPath
|
||||
, newPackage = this.appPackage + '.selendroid'
|
||||
, srcManifest = path.resolve(__dirname, '..', '..', '..', 'build',
|
||||
'selendroid', 'AndroidManifest.xml')
|
||||
, dstDir = path.resolve(getTempPath(), this.appPackage)
|
||||
, dstManifest = path.resolve(dstDir, 'AndroidManifest.xml');
|
||||
|
||||
try {
|
||||
fs.mkdirSync(dstDir);
|
||||
} catch (e) {
|
||||
if (e.message.indexOf("EEXIST") === -1) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
fs.writeFileSync(dstManifest, fs.readFileSync(srcManifest, "utf8"), "utf8");
|
||||
async.series([
|
||||
function(cb) { mkdirp(dstDir, cb); }.bind(this),
|
||||
function(cb) { this.checkSdkBinaryPresent("aapt", cb); }.bind(this),
|
||||
function(cb) { this.compileManifest(dstManifest, newPackage, this.appPackage, cb); }.bind(this),
|
||||
function(cb) { this.insertManifest(dstManifest, serverPath, newServerPath,
|
||||
cb); }.bind(this)
|
||||
], cb);
|
||||
};
|
||||
|
||||
ADB.prototype.compileManifest = function(manifest, manifestPackage, targetPackage, cb) {
|
||||
logger.info("Compiling manifest " + manifest);
|
||||
|
||||
@@ -342,7 +308,7 @@ ADB.prototype.sign = function(apks, cb) {
|
||||
};
|
||||
|
||||
// returns true when already signed, false otherwise.
|
||||
ADB.prototype.checkApkCert = function(apk, cb) {
|
||||
ADB.prototype.checkApkCert = function(apk, pkg, cb) {
|
||||
if (!fs.existsSync(apk)) {
|
||||
logger.debug("APK doesn't exist. " + apk);
|
||||
return cb(false);
|
||||
@@ -383,7 +349,7 @@ ADB.prototype.checkApkCert = function(apk, cb) {
|
||||
entry = entry.entryName;
|
||||
if (!rsa.test(entry)) return next();
|
||||
logger.debug("Entry: " + entry);
|
||||
var entryPath = path.join(getTempPath(), this.appPackage, 'cert');
|
||||
var entryPath = path.join(getTempPath(), pkg, 'cert');
|
||||
logger.debug("entryPath: " + entryPath);
|
||||
var entryFile = path.join(entryPath, entry);
|
||||
logger.debug("entryFile: " + entryFile);
|
||||
@@ -567,20 +533,6 @@ ADB.prototype.forwardPort = function(systemPort, devicePort, cb) {
|
||||
this.exec("forward tcp:" + systemPort + " tcp:" + devicePort, cb);
|
||||
};
|
||||
|
||||
ADB.prototype.sendShutdownCommand = function(cb) {
|
||||
setTimeout(function() {
|
||||
if (!this.alreadyExited) {
|
||||
logger.warn("Android did not shut down fast enough, calling it gone");
|
||||
this.alreadyExited = true;
|
||||
this.onExit(1);
|
||||
}
|
||||
}.bind(this), 7000);
|
||||
this.sendCommand('shutdown', null, cb);
|
||||
if (this.logcat !== null) {
|
||||
this.logcat.stopCapture();
|
||||
}
|
||||
};
|
||||
|
||||
ADB.prototype.isDeviceConnected = function(cb) {
|
||||
this.getConnectedDevices(function(err, devices) {
|
||||
if (err) {
|
||||
@@ -685,152 +637,107 @@ ADB.prototype.getLogcatLogs = function() {
|
||||
return this.logcat.getLogs();
|
||||
};
|
||||
|
||||
ADB.prototype.startApp = function(cb) {
|
||||
logger.info("Starting app");
|
||||
this.requireDeviceId();
|
||||
this.requireApp();
|
||||
var activityString = this.appActivity;
|
||||
var cmd = this.adbCmd + " shell am start -n " + this.appPackage + "/" +
|
||||
activityString;
|
||||
this.debug("Starting app\n" + cmd);
|
||||
exec(cmd, { maxBuffer: 524288 }, function(err, stdout) {
|
||||
if(err) {
|
||||
logger.error(err);
|
||||
cb(err);
|
||||
} else {
|
||||
if (stdout.indexOf("Error: Activity class") !== -1 &&
|
||||
stdout.indexOf("does not exist") !== -1) {
|
||||
if (this.appActivity[0] !== ".") {
|
||||
logger.info("We tried to start an activity that doesn't exist, " +
|
||||
"retrying with . prepended to activity");
|
||||
this.appActivity = "." + this.appActivity;
|
||||
return this.startApp(cb);
|
||||
} else {
|
||||
var msg = "Activity used to start app doesn't exist! Make sure " +
|
||||
"it exists";
|
||||
logger.error(msg);
|
||||
return cb(new Error(msg));
|
||||
}
|
||||
ADB.prototype.startApp = function(pkg, activity, waitActivity, cb) {
|
||||
var cmd = "am start -n " + pkg + "/" + activity;
|
||||
this.shell(cmd, function(err, stdout) {
|
||||
if(err) return cb(err);
|
||||
if (stdout.indexOf("Error: Activity class") !== -1 &&
|
||||
stdout.indexOf("does not exist") !== -1) {
|
||||
if (activity[0] !== ".") {
|
||||
logger.info("We tried to start an activity that doesn't exist, " +
|
||||
"retrying with . prepended to activity");
|
||||
activity = "." + activity;
|
||||
return this.startApp(pkg, activity, cb);
|
||||
} else {
|
||||
var msg = "Activity used to start app doesn't exist! Make sure " +
|
||||
"it exists";
|
||||
logger.error(msg);
|
||||
return cb(new Error(msg));
|
||||
}
|
||||
|
||||
this.waitForActivity(cb);
|
||||
}
|
||||
|
||||
this.waitForActivity(pkg, waitActivity, cb);
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
ADB.prototype.stopApp = function(cb) {
|
||||
logger.info("Killing app");
|
||||
this.requireDeviceId();
|
||||
this.requireApp();
|
||||
var cmd = this.adbCmd + " shell am force-stop " + this.appPackage;
|
||||
exec(cmd, { maxBuffer: 524288 }, function(err) {
|
||||
if (err) {
|
||||
logger.error(err);
|
||||
return cb(err);
|
||||
}
|
||||
cb();
|
||||
});
|
||||
};
|
||||
|
||||
ADB.prototype.getFocusedPackageAndActivity = function(cb) {
|
||||
logger.info("Getting focused package and activity");
|
||||
this.requireDeviceId();
|
||||
var cmd = this.adbCmd + " shell dumpsys window windows"
|
||||
var cmd = "dumpsys window windows"
|
||||
, searchRe = new RegExp(/mFocusedApp.+ ([a-zA-Z0-9\._]+)\/(\.?[^\}]+)\}/);
|
||||
|
||||
exec(cmd, { maxBuffer: 524288 }, function(err, stdout) {
|
||||
if (err) {
|
||||
logger.error(err);
|
||||
cb(err);
|
||||
} else {
|
||||
var foundMatch = false;
|
||||
_.each(stdout.split("\n"), function(line) {
|
||||
var match = searchRe.exec(line);
|
||||
if (match) {
|
||||
foundMatch = match;
|
||||
}
|
||||
});
|
||||
if (foundMatch) {
|
||||
cb(null, foundMatch[1].trim(), foundMatch[2].trim());
|
||||
} else {
|
||||
cb(new Error("Could not parse activity from dumpsys"));
|
||||
this.shell(cmd, function(err, stdout) {
|
||||
if (err) return cb(err);
|
||||
var foundMatch = false;
|
||||
_.each(stdout.split("\n"), function(line) {
|
||||
var match = searchRe.exec(line);
|
||||
if (match) {
|
||||
foundMatch = match;
|
||||
}
|
||||
});
|
||||
if (foundMatch) {
|
||||
cb(null, foundMatch[1].trim(), foundMatch[2].trim());
|
||||
} else {
|
||||
cb(new Error("Could not parse activity from dumpsys"));
|
||||
}
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
ADB.prototype.waitForNotActivity = function(cb) {
|
||||
this.requireApp();
|
||||
ADB.prototype.waitForActivityOrNot = function(pkg, activity, not,
|
||||
waitMs, cb) {
|
||||
if (typeof waitMs === "function") {
|
||||
cb = waitMs;
|
||||
waitMs = 20000;
|
||||
}
|
||||
|
||||
logger.info("Waiting for app's activity to not be focused");
|
||||
var waitMs = 20000
|
||||
, intMs = 750
|
||||
, endAt = Date.now() + waitMs
|
||||
, targetActivity = this.appWaitActivity || this.appActivity;
|
||||
if (targetActivity.indexOf(this.appPackage) === 0) {
|
||||
targetActivity = targetActivity.substring(this.appPackage.length);
|
||||
}
|
||||
var getFocusedApp = function() {
|
||||
this.getFocusedPackageAndActivity(function(err, foundPackage,
|
||||
foundActivity) {
|
||||
var notFoundAct = true;
|
||||
_.each(targetActivity.split(','), function(act) {
|
||||
act = act.trim();
|
||||
if (act === foundActivity || "." + act === foundActivity) {
|
||||
notFoundAct = false;
|
||||
}
|
||||
});
|
||||
if (foundPackage !== this.appPackage && notFoundAct) {
|
||||
cb(null);
|
||||
} else if (Date.now() < endAt) {
|
||||
if (err) logger.info(err);
|
||||
setTimeout(getFocusedApp, intMs);
|
||||
} else {
|
||||
var msg = "App never closed. appActivity: " +
|
||||
foundActivity + " != " + targetActivity;
|
||||
logger.error(msg);
|
||||
cb(new Error(msg));
|
||||
}
|
||||
var intMs = 750
|
||||
, endAt = Date.now() + waitMs;
|
||||
|
||||
}.bind(this));
|
||||
}.bind(this);
|
||||
getFocusedApp();
|
||||
};
|
||||
|
||||
ADB.prototype.waitForActivity = function(cb, waitMsOverride) {
|
||||
this.requireApp();
|
||||
logger.info("Waiting for app's activity to become focused");
|
||||
var waitMs = waitMsOverride || 20000
|
||||
, intMs = 750
|
||||
, endAt = Date.now() + waitMs
|
||||
, targetActivity = this.appWaitActivity || this.appActivity;
|
||||
if (targetActivity.indexOf(this.appPackage) === 0) {
|
||||
targetActivity = targetActivity.substring(this.appPackage.length);
|
||||
if (activity.indexOf(pkg) === 0) {
|
||||
activity = activity.substring(pkg.length);
|
||||
}
|
||||
var getFocusedApp = function() {
|
||||
this.getFocusedPackageAndActivity(function(err, foundPackage,
|
||||
foundActivity) {
|
||||
var foundAct = false;
|
||||
_.each(targetActivity.split(','), function(act) {
|
||||
|
||||
var checkForActivity = function(foundPackage, foundActivity) {
|
||||
var foundAct = false;
|
||||
if (foundPackage === pkg) {
|
||||
_.each(activity.split(','), function(act) {
|
||||
act = act.trim();
|
||||
if (act === foundActivity || "." + act === foundActivity) {
|
||||
foundAct = true;
|
||||
}
|
||||
});
|
||||
if (foundPackage === this.appPackage && foundAct) {
|
||||
cb(null);
|
||||
}
|
||||
return foundAct;
|
||||
};
|
||||
|
||||
var wait = function() {
|
||||
this.getFocusedPackageAndActivity(function(err, foundPackage,
|
||||
foundActivity) {
|
||||
var foundAct = checkForActivity(foundPackage, foundActivity);
|
||||
if ((!not && foundAct) || (not && !foundAct)) {
|
||||
cb();
|
||||
} else if (Date.now() < endAt) {
|
||||
if (err) logger.info(err);
|
||||
setTimeout(getFocusedApp, intMs);
|
||||
setTimeout(wait, intMs);
|
||||
} else {
|
||||
var msg = "App never showed up as active. appActivity: " +
|
||||
foundActivity + " != " + targetActivity;
|
||||
var verb = not ? "stopped" : "started";
|
||||
var msg = "Activity " + activity + " never " + verb + ". Current: " +
|
||||
foundPackage + "/" + foundActivity;
|
||||
logger.error(msg);
|
||||
cb(new Error(msg));
|
||||
}
|
||||
|
||||
}.bind(this));
|
||||
}.bind(this);
|
||||
getFocusedApp();
|
||||
|
||||
wait();
|
||||
};
|
||||
|
||||
ADB.prototype.waitForActivity = function(pkg, act, waitMs, cb) {
|
||||
this.waitForActivityOrNot(pkg, act, false, waitMs, cb);
|
||||
};
|
||||
|
||||
ADB.prototype.waitForActivity = function(pkg, act, waitMs, cb) {
|
||||
this.waitForActivityOrNot(pkg, act, true, waitMs, cb);
|
||||
};
|
||||
|
||||
ADB.prototype.uninstallApk = function(pkg, cb) {
|
||||
|
||||
@@ -83,7 +83,7 @@ androidCommon.installApp = function(cb) {
|
||||
if (installed && this.fastReset) {
|
||||
this.adb.stopAndClear(this.appPackage, cb);
|
||||
} else if (!installed) {
|
||||
this.adb.checkAndSignApk(this.apkPath, function(err) {
|
||||
this.adb.checkAndSignApk(this.apkPath, this.appPackage, function(err) {
|
||||
if (err) return cb(err);
|
||||
this.adb.mkdir(this.remoteTempPath(), function(err) {
|
||||
if (err) return cb(err);
|
||||
|
||||
@@ -171,8 +171,8 @@ Android.prototype.startAppium = function(onLaunch, onExit) {
|
||||
this.pushAppium.bind(this),
|
||||
this.pushUnlock.bind(this),
|
||||
this.uiautomator.start.bind(this.uiautomator),
|
||||
this.wakeUp.bind(this),
|
||||
this.unlockScreen.bind(this),
|
||||
this.adb.wakeUp.bind(this.adb),
|
||||
this.adb.unlockScreen.bind(this.adb),
|
||||
this.startApp.bind(this)
|
||||
], function(err) {
|
||||
onLaunch(err);
|
||||
@@ -224,6 +224,11 @@ Android.prototype.pushAppium = function(cb) {
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
Android.prototype.startApp = function(cb) {
|
||||
this.adb.startApp(this.appPackage, this.appActivity, this.appWaitActivity,
|
||||
cb);
|
||||
};
|
||||
|
||||
Android.prototype.timeoutWaitingForCommand = function() {
|
||||
logger.info("Didn't get a new command in " + (this.commandTimeoutMs / 1000) +
|
||||
" secs, shutting down...");
|
||||
@@ -278,8 +283,11 @@ Android.prototype.stop = function(cb) {
|
||||
};
|
||||
|
||||
Android.prototype.shutdown = function() {
|
||||
this.adb.sendShutdownCommand(function() {
|
||||
this.uiautomator.sendShutdownCommand(function() {
|
||||
logger.info("Sent shutdown command, waiting for ADB to stop...");
|
||||
if (this.logcat !== null) {
|
||||
this.logcat.stopCapture();
|
||||
}
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
@@ -324,7 +332,7 @@ Android.prototype.push = function(elem) {
|
||||
this.progress++;
|
||||
|
||||
if (this.adb && !this.shuttingDown) {
|
||||
this.adb.sendAutomatorCommand(action, params, function(response) {
|
||||
this.uiautomator.sendAction(action, params, function(response) {
|
||||
this.cbForCurrentCmd = null;
|
||||
if (typeof cb === 'function') {
|
||||
this.respond(response, cb);
|
||||
|
||||
@@ -231,7 +231,8 @@ Selendroid.prototype.createSession = function(cb) {
|
||||
if (res.statusCode === 301 && body.sessionId) {
|
||||
logger.info("Successfully started selendroid session");
|
||||
this.selendroidSessionId = body.sessionId;
|
||||
this.adb.waitForActivity(function(err) {
|
||||
this.adb.waitForActivity(this.appPackage, this.appActivity, 1800,
|
||||
function(err) {
|
||||
if (err) {
|
||||
logger.info("Selendroid hasn't started app yet, let's do it " +
|
||||
"manually with adb.startApp");
|
||||
@@ -263,6 +264,33 @@ Selendroid.prototype.proxyTo = proxyTo;
|
||||
Selendroid.prototype.getLog = getLog;
|
||||
Selendroid.prototype.getLogTypes = getLogTypes;
|
||||
|
||||
ADB.prototype.insertSelendroidManifest = function(serverPath, cb) {
|
||||
logger.info("Inserting selendroid manifest");
|
||||
var newServerPath = this.selendroidServerPath
|
||||
, newPackage = this.appPackage + '.selendroid'
|
||||
, srcManifest = path.resolve(__dirname, '..', '..', '..', 'build',
|
||||
'selendroid', 'AndroidManifest.xml')
|
||||
, dstDir = path.resolve(getTempPath(), this.appPackage)
|
||||
, dstManifest = path.resolve(dstDir, 'AndroidManifest.xml');
|
||||
|
||||
try {
|
||||
fs.mkdirSync(dstDir);
|
||||
} catch (e) {
|
||||
if (e.message.indexOf("EEXIST") === -1) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
fs.writeFileSync(dstManifest, fs.readFileSync(srcManifest, "utf8"), "utf8");
|
||||
async.series([
|
||||
function(cb) { mkdirp(dstDir, cb); }.bind(this),
|
||||
function(cb) { this.checkSdkBinaryPresent("aapt", cb); }.bind(this),
|
||||
function(cb) { this.compileManifest(dstManifest, newPackage, this.appPackage, cb); }.bind(this),
|
||||
function(cb) { this.insertManifest(dstManifest, serverPath, newServerPath,
|
||||
cb); }.bind(this)
|
||||
], cb);
|
||||
};
|
||||
|
||||
|
||||
module.exports = function(opts) {
|
||||
return new Selendroid(opts);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user