mirror of
https://github.com/apache/cordova-android.git
synced 2025-02-22 08:53:24 +08:00
CB-9119 Adding lib/retry.js for retrying promise-returning functions. Retrying 'adb install' in emulator.js because it sometimes hangs.
This commit is contained in:
parent
eb70f05168
commit
c0312f9b50
98
bin/templates/cordova/lib/emulator.js
vendored
98
bin/templates/cordova/lib/emulator.js
vendored
@ -21,14 +21,22 @@
|
|||||||
|
|
||||||
/* jshint sub:true */
|
/* jshint sub:true */
|
||||||
|
|
||||||
var exec = require('./exec'),
|
var exec = require('./exec');
|
||||||
Q = require('q'),
|
var appinfo = require('./appinfo');
|
||||||
os = require('os'),
|
var retry = require('./retry');
|
||||||
appinfo = require('./appinfo'),
|
var build = require('./build');
|
||||||
build = require('./build'),
|
|
||||||
child_process = require('child_process');
|
|
||||||
var check_reqs = require('./check_reqs');
|
var check_reqs = require('./check_reqs');
|
||||||
|
|
||||||
|
var Q = require('q');
|
||||||
|
var os = require('os');
|
||||||
|
var child_process = require('child_process');
|
||||||
|
|
||||||
|
// constants
|
||||||
|
var ONE_SECOND = 1000; // in milliseconds
|
||||||
|
var INSTALL_COMMAND_TIMEOUT = 120 * ONE_SECOND; // in milliseconds
|
||||||
|
var NUM_INSTALL_RETRIES = 3;
|
||||||
|
var EXEC_KILL_SIGNAL = 'SIGKILL';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a Promise for a list of emulator images in the form of objects
|
* Returns a Promise for a list of emulator images in the form of objects
|
||||||
* {
|
* {
|
||||||
@ -298,37 +306,67 @@ module.exports.resolveTarget = function(target) {
|
|||||||
* If no started emulators are found, error out.
|
* If no started emulators are found, error out.
|
||||||
* Returns a promise.
|
* Returns a promise.
|
||||||
*/
|
*/
|
||||||
module.exports.install = function(target, buildResults) {
|
module.exports.install = function(givenTarget, buildResults) {
|
||||||
return Q().then(function() {
|
|
||||||
if (target && typeof target == 'object') {
|
var target;
|
||||||
return target;
|
|
||||||
|
// resolve the target emulator
|
||||||
|
return Q().then(function () {
|
||||||
|
if (givenTarget && typeof givenTarget == 'object') {
|
||||||
|
return givenTarget;
|
||||||
|
} else {
|
||||||
|
return module.exports.resolveTarget(givenTarget);
|
||||||
}
|
}
|
||||||
return module.exports.resolveTarget(target);
|
|
||||||
}).then(function(resolvedTarget) {
|
// set the resolved target
|
||||||
var apk_path = build.findBestApkForArchitecture(buildResults, resolvedTarget.arch);
|
}).then(function (resolvedTarget) {
|
||||||
|
target = resolvedTarget;
|
||||||
|
|
||||||
|
// install the app
|
||||||
|
}).then(function () {
|
||||||
|
|
||||||
|
var apk_path = build.findBestApkForArchitecture(buildResults, target.arch);
|
||||||
|
var execOptions = {
|
||||||
|
timeout: INSTALL_COMMAND_TIMEOUT, // in milliseconds
|
||||||
|
killSignal: EXEC_KILL_SIGNAL
|
||||||
|
};
|
||||||
|
|
||||||
console.log('Installing app on emulator...');
|
console.log('Installing app on emulator...');
|
||||||
console.log('Using apk: ' + apk_path);
|
console.log('Using apk: ' + apk_path);
|
||||||
return exec('adb -s ' + resolvedTarget.target + ' install -r -d "' + apk_path + '"', os.tmpdir())
|
|
||||||
.then(function(output) {
|
var retriedInstall = retry.retryPromise(
|
||||||
|
NUM_INSTALL_RETRIES,
|
||||||
|
exec, 'adb -s ' + target.target + ' install -r -d "' + apk_path + '"', os.tmpdir(), execOptions
|
||||||
|
);
|
||||||
|
|
||||||
|
return retriedInstall.then(function (output) {
|
||||||
if (output.match(/Failure/)) {
|
if (output.match(/Failure/)) {
|
||||||
return Q.reject('Failed to install apk to emulator: ' + output);
|
return Q.reject('Failed to install apk to emulator: ' + output);
|
||||||
|
} else {
|
||||||
|
console.log('INSTALL SUCCESS');
|
||||||
}
|
}
|
||||||
return Q();
|
}, function (err) {
|
||||||
}, function(err) {
|
|
||||||
return Q.reject('Failed to install apk to emulator: ' + err);
|
return Q.reject('Failed to install apk to emulator: ' + err);
|
||||||
}).then(function() {
|
|
||||||
//unlock screen
|
|
||||||
return exec('adb -s ' + resolvedTarget.target + ' shell input keyevent 82', os.tmpdir());
|
|
||||||
}).then(function() {
|
|
||||||
// launch the application
|
|
||||||
console.log('Launching application...');
|
|
||||||
var launchName = appinfo.getActivityName();
|
|
||||||
var cmd = 'adb -s ' + resolvedTarget.target + ' shell am start -W -a android.intent.action.MAIN -n ' + launchName;
|
|
||||||
return exec(cmd, os.tmpdir());
|
|
||||||
}).then(function(output) {
|
|
||||||
console.log('LAUNCH SUCCESS');
|
|
||||||
}, function(err) {
|
|
||||||
return Q.reject('Failed to launch app on emulator: ' + err);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// unlock screen
|
||||||
|
}).then(function () {
|
||||||
|
|
||||||
|
console.log('Unlocking screen...');
|
||||||
|
return exec('adb -s ' + target.target + ' shell input keyevent 82', os.tmpdir());
|
||||||
|
|
||||||
|
// launch the application
|
||||||
|
}).then(function () {
|
||||||
|
|
||||||
|
console.log('Launching application...');
|
||||||
|
var launchName = appinfo.getActivityName();
|
||||||
|
var cmd = 'adb -s ' + target.target + ' shell am start -W -a android.intent.action.MAIN -n ' + launchName;
|
||||||
|
return exec(cmd, os.tmpdir());
|
||||||
|
|
||||||
|
// report success or failure
|
||||||
|
}).then(function (output) {
|
||||||
|
console.log('LAUNCH SUCCESS');
|
||||||
|
}, function (err) {
|
||||||
|
return Q.reject('Failed to launch app on emulator: ' + err);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
39
bin/templates/cordova/lib/exec.js
vendored
39
bin/templates/cordova/lib/exec.js
vendored
@ -19,23 +19,50 @@
|
|||||||
under the License.
|
under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var child_process = require('child_process'),
|
var child_process = require("child_process");
|
||||||
Q = require('q');
|
var Q = require("q");
|
||||||
|
|
||||||
|
// constants
|
||||||
|
var DEFAULT_MAX_BUFFER = 1024000;
|
||||||
|
|
||||||
// Takes a command and optional current working directory.
|
// Takes a command and optional current working directory.
|
||||||
// Returns a promise that either resolves with the stdout, or
|
// Returns a promise that either resolves with the stdout, or
|
||||||
// rejects with an error message and the stderr.
|
// rejects with an error message and the stderr.
|
||||||
module.exports = function(cmd, opt_cwd) {
|
//
|
||||||
|
// WARNING:
|
||||||
|
// opt_cwd is an artifact of an old design, and must
|
||||||
|
// be removed in the future; the correct solution is
|
||||||
|
// to pass the options object the same way that
|
||||||
|
// child_process.exec expects
|
||||||
|
//
|
||||||
|
// NOTE:
|
||||||
|
// exec documented here - https://nodejs.org/api/child_process.html#child_process_child_process_exec_command_options_callback
|
||||||
|
module.exports = function(cmd, opt_cwd, options) {
|
||||||
|
|
||||||
var d = Q.defer();
|
var d = Q.defer();
|
||||||
|
|
||||||
|
if (typeof options === "undefined") {
|
||||||
|
options = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
// override cwd to preserve old opt_cwd behavior
|
||||||
|
options.cwd = opt_cwd;
|
||||||
|
|
||||||
|
// set maxBuffer
|
||||||
|
if (typeof options.maxBuffer === "undefined") {
|
||||||
|
options.maxBuffer = DEFAULT_MAX_BUFFER;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
child_process.exec(cmd, {cwd: opt_cwd, maxBuffer: 1024000}, function(err, stdout, stderr) {
|
child_process.exec(cmd, options, function(err, stdout, stderr) {
|
||||||
if (err) d.reject('Error executing "' + cmd + '": ' + stderr);
|
if (err) d.reject("Error executing \"" + cmd + "\": " + stderr);
|
||||||
else d.resolve(stdout);
|
else d.resolve(stdout);
|
||||||
});
|
});
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
console.error('error caught: ' + e);
|
console.error("error caught: " + e);
|
||||||
d.reject(e);
|
d.reject(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
return d.promise;
|
return d.promise;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
66
bin/templates/cordova/lib/retry.js
vendored
Normal file
66
bin/templates/cordova/lib/retry.js
vendored
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
/*
|
||||||
|
Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
or more contributor license agreements. See the NOTICE file
|
||||||
|
distributed with this work for additional information
|
||||||
|
regarding copyright ownership. The ASF licenses this file
|
||||||
|
to you under the Apache License, Version 2.0 (the
|
||||||
|
"License"); you may not use this file except in compliance
|
||||||
|
with the License. You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing,
|
||||||
|
software distributed under the License is distributed on an
|
||||||
|
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
KIND, either express or implied. See the License for the
|
||||||
|
specific language governing permissions and limitations
|
||||||
|
under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* jshint node: true */
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Retry a promise-returning function a number of times, propagating its
|
||||||
|
* results on success or throwing its error on a failed final attempt.
|
||||||
|
*
|
||||||
|
* @arg {Number} attemts_left - The number of times to retry the passed call.
|
||||||
|
* @arg {Function} promiseFunction - A function that returns a promise.
|
||||||
|
* @arg {...} - Arguments to pass to promiseFunction.
|
||||||
|
*
|
||||||
|
* @returns {Promise}
|
||||||
|
*/
|
||||||
|
module.exports.retryPromise = function (attemts_left, promiseFunction) {
|
||||||
|
|
||||||
|
// NOTE:
|
||||||
|
// get all trailing arguments, by skipping the first two (attemts_left and
|
||||||
|
// promiseFunction) because they shouldn't get passed to promiseFunction
|
||||||
|
var promiseFunctionArguments = Array.prototype.slice.call(arguments, 2);
|
||||||
|
|
||||||
|
return promiseFunction.apply(undefined, promiseFunctionArguments).then(
|
||||||
|
|
||||||
|
// on success pass results through
|
||||||
|
function onFulfilled(value) {
|
||||||
|
return value;
|
||||||
|
},
|
||||||
|
|
||||||
|
// on rejection either retry, or throw the error
|
||||||
|
function onRejected(error) {
|
||||||
|
|
||||||
|
attemts_left -= 1;
|
||||||
|
|
||||||
|
if (attemts_left < 1) {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("A retried call failed. Retrying " + attemts_left + " more time(s).");
|
||||||
|
|
||||||
|
// retry call self again with the same arguments, except attemts_left is now lower
|
||||||
|
var fullArguments = [attemts_left, promiseFunction].concat(promiseFunctionArguments);
|
||||||
|
return module.exports.retryPromise.apply(undefined, fullArguments);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
Loading…
Reference in New Issue
Block a user