forked from github/cordova-android
refactor: remove copied Adb.install from emulator.install (#1108)
`emulator.install` contains a copy of the code of `Adb.install` just to be able to pass custom options to `execa`. This change removes that duplicated code in favor of a new option in `Adb.install` that allows to pass options through to `execa`.
This commit is contained in:
parent
c144c08112
commit
671e1fd1c6
16
bin/templates/cordova/lib/Adb.js
vendored
16
bin/templates/cordova/lib/Adb.js
vendored
@ -53,13 +53,17 @@ Adb.devices = function (opts) {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
Adb.install = function (target, packagePath, opts) {
|
Adb.install = function (target, packagePath, { replace = false, execOptions = {} } = {}) {
|
||||||
events.emit('verbose', 'Installing apk ' + packagePath + ' on target ' + target + '...');
|
events.emit('verbose', 'Installing apk ' + packagePath + ' on target ' + target + '...');
|
||||||
|
|
||||||
var args = ['-s', target, 'install'];
|
var args = ['-s', target, 'install'];
|
||||||
if (opts && opts.replace) args.push('-r');
|
if (replace) args.push('-r');
|
||||||
return execa('adb', args.concat(packagePath), { cwd: os.tmpdir() }).then(({ stdout: output }) => {
|
|
||||||
// 'adb install' seems to always returns no error, even if installation fails
|
const opts = { cwd: os.tmpdir(), ...execOptions };
|
||||||
// so we catching output to detect installation failure
|
|
||||||
|
return execa('adb', args.concat(packagePath), opts).then(({ stdout: output }) => {
|
||||||
|
// adb does not return an error code even if installation fails. Instead it puts a specific
|
||||||
|
// message to stdout, so we have to use RegExp matching to detect installation failure.
|
||||||
if (output.match(/Failure/)) {
|
if (output.match(/Failure/)) {
|
||||||
if (output.match(/INSTALL_PARSE_FAILED_NO_CERTIFICATES/)) {
|
if (output.match(/INSTALL_PARSE_FAILED_NO_CERTIFICATES/)) {
|
||||||
output += '\n\n' + 'Sign the build using \'-- --keystore\' or \'--buildConfig\'' +
|
output += '\n\n' + 'Sign the build using \'-- --keystore\' or \'--buildConfig\'' +
|
||||||
@ -69,7 +73,7 @@ Adb.install = function (target, packagePath, opts) {
|
|||||||
'\nEither uninstall an app or increment the versionCode.';
|
'\nEither uninstall an app or increment the versionCode.';
|
||||||
}
|
}
|
||||||
|
|
||||||
return Promise.reject(new CordovaError('Failed to install apk to device: ' + output));
|
throw new CordovaError('Failed to install apk to target: ' + output);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
33
bin/templates/cordova/lib/emulator.js
vendored
33
bin/templates/cordova/lib/emulator.js
vendored
@ -30,7 +30,6 @@ var CordovaError = require('cordova-common').CordovaError;
|
|||||||
var android_sdk = require('./android_sdk');
|
var android_sdk = require('./android_sdk');
|
||||||
var check_reqs = require('./check_reqs');
|
var check_reqs = require('./check_reqs');
|
||||||
var which = require('which');
|
var which = require('which');
|
||||||
var os = require('os');
|
|
||||||
|
|
||||||
// constants
|
// constants
|
||||||
const ONE_SECOND = 1000; // in milliseconds
|
const ONE_SECOND = 1000; // in milliseconds
|
||||||
@ -406,11 +405,6 @@ module.exports.install = function (givenTarget, buildResults) {
|
|||||||
// or the app doesn't installed at all, so no error catching needed.
|
// or the app doesn't installed at all, so no error catching needed.
|
||||||
return Promise.resolve().then(function () {
|
return Promise.resolve().then(function () {
|
||||||
var apk_path = build.findBestApkForArchitecture(buildResults, target.arch);
|
var apk_path = build.findBestApkForArchitecture(buildResults, target.arch);
|
||||||
var execOptions = {
|
|
||||||
cwd: os.tmpdir(),
|
|
||||||
timeout: INSTALL_COMMAND_TIMEOUT, // in milliseconds
|
|
||||||
killSignal: EXEC_KILL_SIGNAL
|
|
||||||
};
|
|
||||||
|
|
||||||
events.emit('log', 'Using apk: ' + apk_path);
|
events.emit('log', 'Using apk: ' + apk_path);
|
||||||
events.emit('log', 'Package name: ' + pkgName);
|
events.emit('log', 'Package name: ' + pkgName);
|
||||||
@ -419,29 +413,18 @@ module.exports.install = function (givenTarget, buildResults) {
|
|||||||
// A special function to call adb install in specific environment w/ specific options.
|
// A special function to call adb install in specific environment w/ specific options.
|
||||||
// Introduced as a part of fix for http://issues.apache.org/jira/browse/CB-9119
|
// Introduced as a part of fix for http://issues.apache.org/jira/browse/CB-9119
|
||||||
// to workaround sporadic emulator hangs
|
// to workaround sporadic emulator hangs
|
||||||
function adbInstallWithOptions (target, apk, opts) {
|
function adbInstallWithOptions (...args) {
|
||||||
events.emit('verbose', 'Installing apk ' + apk + ' on ' + target + '...');
|
return Adb.install(...args, {
|
||||||
|
replace: true,
|
||||||
const args = ['-s', target, 'install', '-r', apk];
|
execOptions: {
|
||||||
return execa('adb', args, opts).then(({ stdout }) => {
|
timeout: INSTALL_COMMAND_TIMEOUT, // in milliseconds
|
||||||
// adb does not return an error code even if installation fails. Instead it puts a specific
|
killSignal: EXEC_KILL_SIGNAL
|
||||||
// message to stdout, so we have to use RegExp matching to detect installation failure.
|
|
||||||
if (/Failure/.test(stdout)) {
|
|
||||||
if (stdout.match(/INSTALL_PARSE_FAILED_NO_CERTIFICATES/)) {
|
|
||||||
stdout += 'Sign the build using \'-- --keystore\' or \'--buildConfig\'' +
|
|
||||||
' or sign and deploy the unsigned apk manually using Android tools.';
|
|
||||||
} else if (stdout.match(/INSTALL_FAILED_VERSION_DOWNGRADE/)) {
|
|
||||||
stdout += 'You\'re trying to install apk with a lower versionCode that is already installed.' +
|
|
||||||
'\nEither uninstall an app or increment the versionCode.';
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new CordovaError('Failed to install apk to emulator: ' + stdout);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function installPromise () {
|
function installPromise () {
|
||||||
return adbInstallWithOptions(target.target, apk_path, execOptions).catch(function (error) {
|
return adbInstallWithOptions(target.target, apk_path).catch(function (error) {
|
||||||
// CB-9557 CB-10157 only uninstall and reinstall app if the one that
|
// CB-9557 CB-10157 only uninstall and reinstall app if the one that
|
||||||
// is already installed on device was signed w/different certificate
|
// is already installed on device was signed w/different certificate
|
||||||
if (!/INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES/.test(error.toString())) { throw error; }
|
if (!/INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES/.test(error.toString())) { throw error; }
|
||||||
@ -452,7 +435,7 @@ module.exports.install = function (givenTarget, buildResults) {
|
|||||||
// This promise is always resolved, even if 'adb uninstall' fails to uninstall app
|
// This promise is always resolved, even if 'adb uninstall' fails to uninstall app
|
||||||
// or the app doesn't installed at all, so no error catching needed.
|
// or the app doesn't installed at all, so no error catching needed.
|
||||||
return Adb.uninstall(target.target, pkgName).then(function () {
|
return Adb.uninstall(target.target, pkgName).then(function () {
|
||||||
return adbInstallWithOptions(target.target, apk_path, execOptions);
|
return adbInstallWithOptions(target.target, apk_path);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -582,7 +582,6 @@ describe('emulator', () => {
|
|||||||
let AndroidManifestGetActivitySpy;
|
let AndroidManifestGetActivitySpy;
|
||||||
let AdbSpy;
|
let AdbSpy;
|
||||||
let buildSpy;
|
let buildSpy;
|
||||||
let execaSpy;
|
|
||||||
let target;
|
let target;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
@ -597,14 +596,12 @@ describe('emulator', () => {
|
|||||||
AndroidManifestSpy = jasmine.createSpy('AndroidManifest').and.returnValue(AndroidManifestFns);
|
AndroidManifestSpy = jasmine.createSpy('AndroidManifest').and.returnValue(AndroidManifestFns);
|
||||||
emu.__set__('AndroidManifest', AndroidManifestSpy);
|
emu.__set__('AndroidManifest', AndroidManifestSpy);
|
||||||
|
|
||||||
AdbSpy = jasmine.createSpyObj('Adb', ['shell', 'start', 'uninstall']);
|
AdbSpy = jasmine.createSpyObj('Adb', ['shell', 'start', 'install', 'uninstall']);
|
||||||
AdbSpy.shell.and.returnValue(Promise.resolve());
|
AdbSpy.shell.and.returnValue(Promise.resolve());
|
||||||
AdbSpy.start.and.returnValue(Promise.resolve());
|
AdbSpy.start.and.returnValue(Promise.resolve());
|
||||||
|
AdbSpy.install.and.returnValue(Promise.resolve());
|
||||||
AdbSpy.uninstall.and.returnValue(Promise.resolve());
|
AdbSpy.uninstall.and.returnValue(Promise.resolve());
|
||||||
emu.__set__('Adb', AdbSpy);
|
emu.__set__('Adb', AdbSpy);
|
||||||
|
|
||||||
execaSpy = jasmine.createSpy('execa').and.resolveTo({});
|
|
||||||
emu.__set__('execa', execaSpy);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should get the full target object if only id is specified', () => {
|
it('should get the full target object if only id is specified', () => {
|
||||||
@ -618,8 +615,7 @@ describe('emulator', () => {
|
|||||||
|
|
||||||
it('should install to the passed target', () => {
|
it('should install to the passed target', () => {
|
||||||
return emu.install(target, {}).then(() => {
|
return emu.install(target, {}).then(() => {
|
||||||
const execCmd = execaSpy.calls.argsFor(0)[1].join(' ');
|
expect(AdbSpy.install.calls.argsFor(0)[0]).toBe(target.target);
|
||||||
expect(execCmd).toContain(`-s ${target.target} install`);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -636,26 +632,25 @@ describe('emulator', () => {
|
|||||||
return emu.install(target, buildResults).then(() => {
|
return emu.install(target, buildResults).then(() => {
|
||||||
expect(buildSpy.findBestApkForArchitecture).toHaveBeenCalledWith(buildResults, target.arch);
|
expect(buildSpy.findBestApkForArchitecture).toHaveBeenCalledWith(buildResults, target.arch);
|
||||||
|
|
||||||
const execCmd = execaSpy.calls.argsFor(0)[1].join(' ');
|
expect(AdbSpy.install.calls.argsFor(0)[1]).toBe(apkPath);
|
||||||
expect(execCmd).toContain(`install -r ${apkPath}`);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should uninstall and reinstall app if failure is due to different certificates', () => {
|
it('should uninstall and reinstall app if failure is due to different certificates', () => {
|
||||||
execaSpy.and.returnValues(
|
AdbSpy.install.and.returnValues(
|
||||||
...['Failure: INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES', '']
|
Promise.reject('Failure: INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES'),
|
||||||
.map(out => Promise.resolve({ stdout: out }))
|
Promise.resolve()
|
||||||
);
|
);
|
||||||
|
|
||||||
return emu.install(target, {}).then(() => {
|
return emu.install(target, {}).then(() => {
|
||||||
expect(execaSpy).toHaveBeenCalledTimes(2);
|
expect(AdbSpy.install).toHaveBeenCalledTimes(2);
|
||||||
expect(AdbSpy.uninstall).toHaveBeenCalled();
|
expect(AdbSpy.uninstall).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should throw any error not caused by different certificates', () => {
|
it('should throw any error not caused by different certificates', () => {
|
||||||
const errorMsg = 'Failure: Failed to install';
|
const errorMsg = 'Failure: Failed to install';
|
||||||
execaSpy.and.resolveTo({ stdout: errorMsg });
|
AdbSpy.install.and.rejectWith(new CordovaError(errorMsg));
|
||||||
|
|
||||||
return emu.install(target, {}).then(
|
return emu.install(target, {}).then(
|
||||||
() => fail('Unexpectedly resolved'),
|
() => fail('Unexpectedly resolved'),
|
||||||
|
Loading…
Reference in New Issue
Block a user