forked from github/cordova-android
refactor: unify installation on devices & emulators (#1123)
* refactor: unify installation on devices & emulators This change replaces the almost identical methods `device.install` and `emulator.install` with the generic `target.install`. * fix: use unified installation in platform-centric bins
This commit is contained in:
parent
aa679ea1d6
commit
bb7d733cde
46
bin/templates/cordova/lib/device.js
vendored
46
bin/templates/cordova/lib/device.js
vendored
@ -18,11 +18,8 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
var build = require('./build');
|
var build = require('./build');
|
||||||
var path = require('path');
|
|
||||||
var Adb = require('./Adb');
|
var Adb = require('./Adb');
|
||||||
var AndroidManifest = require('./AndroidManifest');
|
|
||||||
var CordovaError = require('cordova-common').CordovaError;
|
var CordovaError = require('cordova-common').CordovaError;
|
||||||
var events = require('cordova-common').events;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a promise for the list of the device ID's found
|
* Returns a promise for the list of the device ID's found
|
||||||
@ -49,46 +46,3 @@ module.exports.resolveTarget = function (target) {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
|
||||||
* Installs a previously built application on the device
|
|
||||||
* and launches it.
|
|
||||||
* Returns a promise.
|
|
||||||
*/
|
|
||||||
module.exports.install = function (target, buildResults) {
|
|
||||||
return Promise.resolve().then(function () {
|
|
||||||
if (target && typeof target === 'object') {
|
|
||||||
return target;
|
|
||||||
}
|
|
||||||
return module.exports.resolveTarget(target);
|
|
||||||
}).then(function (resolvedTarget) {
|
|
||||||
var apk_path = build.findBestApkForArchitecture(buildResults, resolvedTarget.arch);
|
|
||||||
var manifest = new AndroidManifest(path.join(__dirname, '../../app/src/main/AndroidManifest.xml'));
|
|
||||||
var pkgName = manifest.getPackageId();
|
|
||||||
var launchName = pkgName + '/.' + manifest.getActivity().getName();
|
|
||||||
events.emit('log', 'Using apk: ' + apk_path);
|
|
||||||
events.emit('log', 'Package name: ' + pkgName);
|
|
||||||
|
|
||||||
return Adb.install(resolvedTarget.target, apk_path, { replace: true }).catch(function (error) {
|
|
||||||
// CB-9557 CB-10157 only uninstall and reinstall app if the one that
|
|
||||||
// is already installed on device was signed w/different certificate
|
|
||||||
if (!/INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES/.test(error.toString())) { throw error; }
|
|
||||||
|
|
||||||
events.emit('warn', 'Uninstalling app from device and reinstalling it again because the ' +
|
|
||||||
'installed app already signed with different key');
|
|
||||||
|
|
||||||
// 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.
|
|
||||||
return Adb.uninstall(resolvedTarget.target, pkgName).then(function () {
|
|
||||||
return Adb.install(resolvedTarget.target, apk_path, { replace: true });
|
|
||||||
});
|
|
||||||
}).then(function () {
|
|
||||||
// unlock screen
|
|
||||||
return Adb.shell(resolvedTarget.target, 'input keyevent 82');
|
|
||||||
}).then(function () {
|
|
||||||
return Adb.start(resolvedTarget.target, launchName);
|
|
||||||
}).then(function () {
|
|
||||||
events.emit('log', 'LAUNCH SUCCESS');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
88
bin/templates/cordova/lib/emulator.js
vendored
88
bin/templates/cordova/lib/emulator.js
vendored
@ -20,11 +20,9 @@
|
|||||||
const execa = require('execa');
|
const execa = require('execa');
|
||||||
const fs = require('fs-extra');
|
const fs = require('fs-extra');
|
||||||
var android_versions = require('android-versions');
|
var android_versions = require('android-versions');
|
||||||
var retry = require('./retry');
|
|
||||||
var build = require('./build');
|
var build = require('./build');
|
||||||
var path = require('path');
|
var path = require('path');
|
||||||
var Adb = require('./Adb');
|
var Adb = require('./Adb');
|
||||||
var AndroidManifest = require('./AndroidManifest');
|
|
||||||
var events = require('cordova-common').events;
|
var events = require('cordova-common').events;
|
||||||
var CordovaError = require('cordova-common').CordovaError;
|
var CordovaError = require('cordova-common').CordovaError;
|
||||||
var android_sdk = require('./android_sdk');
|
var android_sdk = require('./android_sdk');
|
||||||
@ -33,11 +31,7 @@ var which = require('which');
|
|||||||
|
|
||||||
// constants
|
// constants
|
||||||
const ONE_SECOND = 1000; // in milliseconds
|
const ONE_SECOND = 1000; // in milliseconds
|
||||||
const ONE_MINUTE = 60 * ONE_SECOND; // in milliseconds
|
|
||||||
const INSTALL_COMMAND_TIMEOUT = 5 * ONE_MINUTE; // in milliseconds
|
|
||||||
const NUM_INSTALL_RETRIES = 3;
|
|
||||||
const CHECK_BOOTED_INTERVAL = 3 * ONE_SECOND; // in milliseconds
|
const CHECK_BOOTED_INTERVAL = 3 * ONE_SECOND; // in milliseconds
|
||||||
const EXEC_KILL_SIGNAL = 'SIGKILL';
|
|
||||||
|
|
||||||
function forgivingWhichSync (cmd) {
|
function forgivingWhichSync (cmd) {
|
||||||
const whichResult = which.sync(cmd, { nothrow: true });
|
const whichResult = which.sync(cmd, { nothrow: true });
|
||||||
@ -373,85 +367,3 @@ module.exports.resolveTarget = function (target) {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
|
||||||
* Installs a previously built application on the emulator and launches it.
|
|
||||||
* If no target is specified, then it picks one.
|
|
||||||
* If no started emulators are found, error out.
|
|
||||||
* Returns a promise.
|
|
||||||
*/
|
|
||||||
module.exports.install = function (givenTarget, buildResults) {
|
|
||||||
var target;
|
|
||||||
// We need to find the proper path to the Android Manifest
|
|
||||||
const manifestPath = path.join(__dirname, '..', '..', 'app', 'src', 'main', 'AndroidManifest.xml');
|
|
||||||
const manifest = new AndroidManifest(manifestPath);
|
|
||||||
const pkgName = manifest.getPackageId();
|
|
||||||
|
|
||||||
// resolve the target emulator
|
|
||||||
return Promise.resolve().then(function () {
|
|
||||||
if (givenTarget && typeof givenTarget === 'object') {
|
|
||||||
return givenTarget;
|
|
||||||
} else {
|
|
||||||
return module.exports.resolveTarget(givenTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// set the resolved target
|
|
||||||
}).then(function (resolvedTarget) {
|
|
||||||
target = resolvedTarget;
|
|
||||||
|
|
||||||
// install the app
|
|
||||||
}).then(function () {
|
|
||||||
// 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.
|
|
||||||
return Promise.resolve().then(function () {
|
|
||||||
var apk_path = build.findBestApkForArchitecture(buildResults, target.arch);
|
|
||||||
|
|
||||||
events.emit('log', 'Using apk: ' + apk_path);
|
|
||||||
events.emit('log', 'Package name: ' + pkgName);
|
|
||||||
events.emit('verbose', 'Installing app on emulator...');
|
|
||||||
|
|
||||||
// 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
|
|
||||||
// to workaround sporadic emulator hangs
|
|
||||||
function adbInstallWithOptions (...args) {
|
|
||||||
return Adb.install(...args, {
|
|
||||||
replace: true,
|
|
||||||
execOptions: {
|
|
||||||
timeout: INSTALL_COMMAND_TIMEOUT, // in milliseconds
|
|
||||||
killSignal: EXEC_KILL_SIGNAL
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function installPromise () {
|
|
||||||
return adbInstallWithOptions(target.target, apk_path).catch(function (error) {
|
|
||||||
// CB-9557 CB-10157 only uninstall and reinstall app if the one that
|
|
||||||
// is already installed on device was signed w/different certificate
|
|
||||||
if (!/INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES/.test(error.toString())) { throw error; }
|
|
||||||
|
|
||||||
events.emit('warn', 'Uninstalling app from device and reinstalling it because the ' +
|
|
||||||
'currently installed app was signed with different key');
|
|
||||||
|
|
||||||
// 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.
|
|
||||||
return Adb.uninstall(target.target, pkgName).then(function () {
|
|
||||||
return adbInstallWithOptions(target.target, apk_path);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return retry.retryPromise(NUM_INSTALL_RETRIES, installPromise).then(function (output) {
|
|
||||||
events.emit('log', 'INSTALL SUCCESS');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
// unlock screen
|
|
||||||
}).then(function () {
|
|
||||||
events.emit('verbose', 'Unlocking screen...');
|
|
||||||
return Adb.shell(target.target, 'input keyevent 82');
|
|
||||||
}).then(function () {
|
|
||||||
Adb.start(target.target, pkgName + '/.' + manifest.getActivity().getName());
|
|
||||||
// report success or failure
|
|
||||||
}).then(function (output) {
|
|
||||||
events.emit('log', 'LAUNCH SUCCESS');
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
under the License.
|
under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
const { install } = require('./target');
|
||||||
var device = require('./device');
|
var device = require('./device');
|
||||||
var args = process.argv;
|
var args = process.argv;
|
||||||
|
|
||||||
@ -26,17 +27,13 @@ if (args.length > 2) {
|
|||||||
var install_target;
|
var install_target;
|
||||||
if (args[2].substring(0, 9) === '--target=') {
|
if (args[2].substring(0, 9) === '--target=') {
|
||||||
install_target = args[2].substring(9, args[2].length);
|
install_target = args[2].substring(9, args[2].length);
|
||||||
device.install(install_target).catch(function (err) {
|
|
||||||
console.error('ERROR: ' + err);
|
|
||||||
process.exit(2);
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
console.error('ERROR : argument \'' + args[2] + '\' not recognized.');
|
console.error('ERROR : argument \'' + args[2] + '\' not recognized.');
|
||||||
process.exit(2);
|
process.exit(2);
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
device.install().catch(function (err) {
|
|
||||||
|
device.resolveTarget(install_target).then(install).catch(err => {
|
||||||
console.error('ERROR: ' + err);
|
console.error('ERROR: ' + err);
|
||||||
process.exit(2);
|
process.exit(2);
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
under the License.
|
under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
const { install } = require('./target');
|
||||||
var emulator = require('./emulator');
|
var emulator = require('./emulator');
|
||||||
var args = process.argv;
|
var args = process.argv;
|
||||||
|
|
||||||
@ -32,7 +33,7 @@ if (args.length > 2) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
emulator.install(install_target).catch(function (err) {
|
emulator.resolveTarget(install_target).then(install).catch(err => {
|
||||||
console.error('ERROR: ' + err);
|
console.error('ERROR: ' + err);
|
||||||
process.exit(2);
|
process.exit(2);
|
||||||
});
|
});
|
||||||
|
9
bin/templates/cordova/lib/run.js
vendored
9
bin/templates/cordova/lib/run.js
vendored
@ -20,6 +20,7 @@
|
|||||||
var path = require('path');
|
var path = require('path');
|
||||||
var emulator = require('./emulator');
|
var emulator = require('./emulator');
|
||||||
var device = require('./device');
|
var device = require('./device');
|
||||||
|
const target = require('./target');
|
||||||
var PackageType = require('./PackageType');
|
var PackageType = require('./PackageType');
|
||||||
const { CordovaError, events } = require('cordova-common');
|
const { CordovaError, events } = require('cordova-common');
|
||||||
|
|
||||||
@ -110,14 +111,12 @@ module.exports.run = function (runOptions) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
resolve(self._builder.fetchBuildResults(buildOptions.buildType, buildOptions.arch));
|
resolve(self._builder.fetchBuildResults(buildOptions.buildType, buildOptions.arch));
|
||||||
}).then(function (buildResults) {
|
}).then(async function (buildResults) {
|
||||||
if (resolvedTarget && resolvedTarget.isEmulator) {
|
if (resolvedTarget && resolvedTarget.isEmulator) {
|
||||||
return emulator.wait_for_boot(resolvedTarget.target).then(function () {
|
await emulator.wait_for_boot(resolvedTarget.id);
|
||||||
return emulator.install(resolvedTarget, buildResults);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return device.install(resolvedTarget, buildResults);
|
return target.install(resolvedTarget, buildResults);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
75
bin/templates/cordova/lib/target.js
vendored
Normal file
75
bin/templates/cordova/lib/target.js
vendored
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
/*
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const path = require('path');
|
||||||
|
const Adb = require('./Adb');
|
||||||
|
const build = require('./build');
|
||||||
|
const AndroidManifest = require('./AndroidManifest');
|
||||||
|
const { retryPromise } = require('./retry');
|
||||||
|
const { events } = require('cordova-common');
|
||||||
|
|
||||||
|
const INSTALL_COMMAND_TIMEOUT = 5 * 60 * 1000;
|
||||||
|
const NUM_INSTALL_RETRIES = 3;
|
||||||
|
const EXEC_KILL_SIGNAL = 'SIGKILL';
|
||||||
|
|
||||||
|
exports.install = async function ({ target, arch, isEmulator }, buildResults) {
|
||||||
|
const apk_path = build.findBestApkForArchitecture(buildResults, arch);
|
||||||
|
const manifest = new AndroidManifest(path.join(__dirname, '../../app/src/main/AndroidManifest.xml'));
|
||||||
|
const pkgName = manifest.getPackageId();
|
||||||
|
const launchName = pkgName + '/.' + manifest.getActivity().getName();
|
||||||
|
|
||||||
|
events.emit('log', 'Using apk: ' + apk_path);
|
||||||
|
events.emit('log', 'Package name: ' + pkgName);
|
||||||
|
events.emit('verbose', `Installing app on target ${target}`);
|
||||||
|
|
||||||
|
async function doInstall (execOptions = {}) {
|
||||||
|
try {
|
||||||
|
await Adb.install(target, apk_path, { replace: true, execOptions });
|
||||||
|
} catch (error) {
|
||||||
|
// CB-9557 CB-10157 only uninstall and reinstall app if the one that
|
||||||
|
// is already installed on device was signed w/different certificate
|
||||||
|
if (!/INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES/.test(error.toString())) throw error;
|
||||||
|
|
||||||
|
events.emit('warn', 'Uninstalling app from device and reinstalling it again because the ' +
|
||||||
|
'installed app already signed with different key');
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
await Adb.uninstall(target, pkgName);
|
||||||
|
await Adb.install(target, apk_path, { replace: true });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isEmulator) {
|
||||||
|
// Work around sporadic emulator hangs: http://issues.apache.org/jira/browse/CB-9119
|
||||||
|
await retryPromise(NUM_INSTALL_RETRIES, () => doInstall({
|
||||||
|
timeout: INSTALL_COMMAND_TIMEOUT,
|
||||||
|
killSignal: EXEC_KILL_SIGNAL
|
||||||
|
}));
|
||||||
|
} else {
|
||||||
|
await doInstall();
|
||||||
|
}
|
||||||
|
events.emit('log', 'INSTALL SUCCESS');
|
||||||
|
|
||||||
|
events.emit('verbose', 'Unlocking screen...');
|
||||||
|
await Adb.shell(target, 'input keyevent 82');
|
||||||
|
|
||||||
|
await Adb.start(target, launchName);
|
||||||
|
events.emit('log', 'LAUNCH SUCCESS');
|
||||||
|
};
|
@ -103,106 +103,4 @@ describe('device', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('install', () => {
|
|
||||||
let AndroidManifestSpy;
|
|
||||||
let AndroidManifestFns;
|
|
||||||
let AndroidManifestGetActivitySpy;
|
|
||||||
let buildSpy;
|
|
||||||
let target;
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
target = { target: DEVICE_LIST[0], arch: 'arm7', isEmulator: false };
|
|
||||||
|
|
||||||
buildSpy = jasmine.createSpyObj('build', ['findBestApkForArchitecture']);
|
|
||||||
device.__set__('build', buildSpy);
|
|
||||||
|
|
||||||
AndroidManifestFns = jasmine.createSpyObj('AndroidManifestFns', ['getPackageId', 'getActivity']);
|
|
||||||
AndroidManifestGetActivitySpy = jasmine.createSpyObj('getActivity', ['getName']);
|
|
||||||
AndroidManifestFns.getActivity.and.returnValue(AndroidManifestGetActivitySpy);
|
|
||||||
AndroidManifestSpy = jasmine.createSpy('AndroidManifest').and.returnValue(AndroidManifestFns);
|
|
||||||
device.__set__('AndroidManifest', AndroidManifestSpy);
|
|
||||||
|
|
||||||
AdbSpy.install.and.returnValue(Promise.resolve());
|
|
||||||
AdbSpy.shell.and.returnValue(Promise.resolve());
|
|
||||||
AdbSpy.start.and.returnValue(Promise.resolve());
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should get the full target object if only id is specified', () => {
|
|
||||||
const targetId = DEVICE_LIST[0];
|
|
||||||
spyOn(device, 'resolveTarget').and.returnValue(Promise.resolve(target));
|
|
||||||
|
|
||||||
return device.install(targetId).then(() => {
|
|
||||||
expect(device.resolveTarget).toHaveBeenCalledWith(targetId);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should install to the passed target', () => {
|
|
||||||
return device.install(target).then(() => {
|
|
||||||
expect(AdbSpy.install).toHaveBeenCalledWith(target.target, undefined, jasmine.anything());
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should install the correct apk based on the architecture and build results', () => {
|
|
||||||
const buildResults = {
|
|
||||||
apkPaths: 'path/to/apks',
|
|
||||||
buildType: 'debug',
|
|
||||||
buildMethod: 'foo'
|
|
||||||
};
|
|
||||||
|
|
||||||
const apkPath = 'my/apk/path/app.apk';
|
|
||||||
buildSpy.findBestApkForArchitecture.and.returnValue(apkPath);
|
|
||||||
|
|
||||||
return device.install(target, buildResults).then(() => {
|
|
||||||
expect(buildSpy.findBestApkForArchitecture).toHaveBeenCalledWith(buildResults, target.arch);
|
|
||||||
expect(AdbSpy.install).toHaveBeenCalledWith(jasmine.anything(), apkPath, jasmine.anything());
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should uninstall and reinstall app if failure is due to different certificates', () => {
|
|
||||||
AdbSpy.install.and.returnValues(
|
|
||||||
Promise.reject('Failed to install: INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES'),
|
|
||||||
Promise.resolve()
|
|
||||||
);
|
|
||||||
|
|
||||||
AdbSpy.uninstall.and.callFake(() => {
|
|
||||||
expect(AdbSpy.install).toHaveBeenCalledTimes(1);
|
|
||||||
return Promise.resolve();
|
|
||||||
});
|
|
||||||
|
|
||||||
return device.install(target).then(() => {
|
|
||||||
expect(AdbSpy.install).toHaveBeenCalledTimes(2);
|
|
||||||
expect(AdbSpy.uninstall).toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should throw any error not caused by different certificates', () => {
|
|
||||||
const errorMsg = new CordovaError('Failed to install');
|
|
||||||
AdbSpy.install.and.returnValues(Promise.reject(errorMsg));
|
|
||||||
|
|
||||||
return device.install(target).then(
|
|
||||||
() => fail('Unexpectedly resolved'),
|
|
||||||
err => {
|
|
||||||
expect(err).toBe(errorMsg);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should unlock the screen on device', () => {
|
|
||||||
return device.install(target).then(() => {
|
|
||||||
expect(AdbSpy.shell).toHaveBeenCalledWith(target.target, 'input keyevent 82');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should start the newly installed app on the device', () => {
|
|
||||||
const packageId = 'unittestapp';
|
|
||||||
const activityName = 'TestActivity';
|
|
||||||
AndroidManifestFns.getPackageId.and.returnValue(packageId);
|
|
||||||
AndroidManifestGetActivitySpy.getName.and.returnValue(activityName);
|
|
||||||
|
|
||||||
return device.install(target).then(() => {
|
|
||||||
expect(AdbSpy.start).toHaveBeenCalledWith(target.target, `${packageId}/.${activityName}`);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
@ -575,107 +575,4 @@ describe('emulator', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('install', () => {
|
|
||||||
let AndroidManifestSpy;
|
|
||||||
let AndroidManifestFns;
|
|
||||||
let AndroidManifestGetActivitySpy;
|
|
||||||
let AdbSpy;
|
|
||||||
let buildSpy;
|
|
||||||
let target;
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
target = { target: EMULATOR_LIST[1], arch: 'arm7', isEmulator: true };
|
|
||||||
|
|
||||||
buildSpy = jasmine.createSpyObj('build', ['findBestApkForArchitecture']);
|
|
||||||
emu.__set__('build', buildSpy);
|
|
||||||
|
|
||||||
AndroidManifestFns = jasmine.createSpyObj('AndroidManifestFns', ['getPackageId', 'getActivity']);
|
|
||||||
AndroidManifestGetActivitySpy = jasmine.createSpyObj('getActivity', ['getName']);
|
|
||||||
AndroidManifestFns.getActivity.and.returnValue(AndroidManifestGetActivitySpy);
|
|
||||||
AndroidManifestSpy = jasmine.createSpy('AndroidManifest').and.returnValue(AndroidManifestFns);
|
|
||||||
emu.__set__('AndroidManifest', AndroidManifestSpy);
|
|
||||||
|
|
||||||
AdbSpy = jasmine.createSpyObj('Adb', ['shell', 'start', 'install', 'uninstall']);
|
|
||||||
AdbSpy.shell.and.returnValue(Promise.resolve());
|
|
||||||
AdbSpy.start.and.returnValue(Promise.resolve());
|
|
||||||
AdbSpy.install.and.returnValue(Promise.resolve());
|
|
||||||
AdbSpy.uninstall.and.returnValue(Promise.resolve());
|
|
||||||
emu.__set__('Adb', AdbSpy);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should get the full target object if only id is specified', () => {
|
|
||||||
const targetId = target.target;
|
|
||||||
spyOn(emu, 'resolveTarget').and.returnValue(Promise.resolve(target));
|
|
||||||
|
|
||||||
return emu.install(targetId, {}).then(() => {
|
|
||||||
expect(emu.resolveTarget).toHaveBeenCalledWith(targetId);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should install to the passed target', () => {
|
|
||||||
return emu.install(target, {}).then(() => {
|
|
||||||
expect(AdbSpy.install.calls.argsFor(0)[0]).toBe(target.target);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should install the correct apk based on the architecture and build results', () => {
|
|
||||||
const buildResults = {
|
|
||||||
apkPaths: 'path/to/apks',
|
|
||||||
buildType: 'debug',
|
|
||||||
buildMethod: 'foo'
|
|
||||||
};
|
|
||||||
|
|
||||||
const apkPath = 'my/apk/path/app.apk';
|
|
||||||
buildSpy.findBestApkForArchitecture.and.returnValue(apkPath);
|
|
||||||
|
|
||||||
return emu.install(target, buildResults).then(() => {
|
|
||||||
expect(buildSpy.findBestApkForArchitecture).toHaveBeenCalledWith(buildResults, target.arch);
|
|
||||||
|
|
||||||
expect(AdbSpy.install.calls.argsFor(0)[1]).toBe(apkPath);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should uninstall and reinstall app if failure is due to different certificates', () => {
|
|
||||||
AdbSpy.install.and.returnValues(
|
|
||||||
Promise.reject('Failure: INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES'),
|
|
||||||
Promise.resolve()
|
|
||||||
);
|
|
||||||
|
|
||||||
return emu.install(target, {}).then(() => {
|
|
||||||
expect(AdbSpy.install).toHaveBeenCalledTimes(2);
|
|
||||||
expect(AdbSpy.uninstall).toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should throw any error not caused by different certificates', () => {
|
|
||||||
const errorMsg = 'Failure: Failed to install';
|
|
||||||
AdbSpy.install.and.rejectWith(new CordovaError(errorMsg));
|
|
||||||
|
|
||||||
return emu.install(target, {}).then(
|
|
||||||
() => fail('Unexpectedly resolved'),
|
|
||||||
err => {
|
|
||||||
expect(err).toEqual(jasmine.any(CordovaError));
|
|
||||||
expect(err.message).toContain(errorMsg);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should unlock the screen on device', () => {
|
|
||||||
return emu.install(target, {}).then(() => {
|
|
||||||
expect(AdbSpy.shell).toHaveBeenCalledWith(target.target, 'input keyevent 82');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should start the newly installed app on the device', () => {
|
|
||||||
const packageId = 'unittestapp';
|
|
||||||
const activityName = 'TestActivity';
|
|
||||||
AndroidManifestFns.getPackageId.and.returnValue(packageId);
|
|
||||||
AndroidManifestGetActivitySpy.getName.and.returnValue(activityName);
|
|
||||||
|
|
||||||
return emu.install(target, {}).then(() => {
|
|
||||||
expect(AdbSpy.start).toHaveBeenCalledWith(target.target, `${packageId}/.${activityName}`);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
@ -45,18 +45,23 @@ describe('run', () => {
|
|||||||
describe('run method', () => {
|
describe('run method', () => {
|
||||||
let deviceSpyObj;
|
let deviceSpyObj;
|
||||||
let emulatorSpyObj;
|
let emulatorSpyObj;
|
||||||
|
let targetSpyObj;
|
||||||
let eventsSpyObj;
|
let eventsSpyObj;
|
||||||
let getInstallTargetSpy;
|
let getInstallTargetSpy;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
deviceSpyObj = jasmine.createSpyObj('deviceSpy', ['install', 'list', 'resolveTarget']);
|
deviceSpyObj = jasmine.createSpyObj('deviceSpy', ['list', 'resolveTarget']);
|
||||||
emulatorSpyObj = jasmine.createSpyObj('emulatorSpy', ['install', 'list_images', 'list_started', 'resolveTarget', 'start', 'wait_for_boot']);
|
emulatorSpyObj = jasmine.createSpyObj('emulatorSpy', ['list_images', 'list_started', 'resolveTarget', 'start', 'wait_for_boot']);
|
||||||
eventsSpyObj = jasmine.createSpyObj('eventsSpy', ['emit']);
|
eventsSpyObj = jasmine.createSpyObj('eventsSpy', ['emit']);
|
||||||
getInstallTargetSpy = jasmine.createSpy('getInstallTargetSpy');
|
getInstallTargetSpy = jasmine.createSpy('getInstallTargetSpy');
|
||||||
|
|
||||||
|
targetSpyObj = jasmine.createSpyObj('target', ['install']);
|
||||||
|
targetSpyObj.install.and.resolveTo();
|
||||||
|
|
||||||
run.__set__({
|
run.__set__({
|
||||||
device: deviceSpyObj,
|
device: deviceSpyObj,
|
||||||
emulator: emulatorSpyObj,
|
emulator: emulatorSpyObj,
|
||||||
|
target: targetSpyObj,
|
||||||
events: eventsSpyObj,
|
events: eventsSpyObj,
|
||||||
getInstallTarget: getInstallTargetSpy
|
getInstallTarget: getInstallTargetSpy
|
||||||
});
|
});
|
||||||
@ -187,7 +192,7 @@ describe('run', () => {
|
|||||||
deviceSpyObj.resolveTarget.and.returnValue(deviceTarget);
|
deviceSpyObj.resolveTarget.and.returnValue(deviceTarget);
|
||||||
|
|
||||||
return run.run().then(() => {
|
return run.run().then(() => {
|
||||||
expect(deviceSpyObj.install).toHaveBeenCalledWith(deviceTarget, { apkPaths: [], buildType: 'debug' });
|
expect(targetSpyObj.install).toHaveBeenCalledWith(deviceTarget, { apkPaths: [], buildType: 'debug' });
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -199,7 +204,7 @@ describe('run', () => {
|
|||||||
emulatorSpyObj.wait_for_boot.and.returnValue(Promise.resolve());
|
emulatorSpyObj.wait_for_boot.and.returnValue(Promise.resolve());
|
||||||
|
|
||||||
return run.run().then(() => {
|
return run.run().then(() => {
|
||||||
expect(emulatorSpyObj.install).toHaveBeenCalledWith(emulatorTarget, { apkPaths: [], buildType: 'debug' });
|
expect(targetSpyObj.install).toHaveBeenCalledWith(emulatorTarget, { apkPaths: [], buildType: 'debug' });
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
126
spec/unit/target.spec.js
Normal file
126
spec/unit/target.spec.js
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
/**
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const rewire = require('rewire');
|
||||||
|
const { CordovaError } = require('cordova-common');
|
||||||
|
|
||||||
|
describe('target', () => {
|
||||||
|
let target;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
target = rewire('../../bin/templates/cordova/lib/target');
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('install', () => {
|
||||||
|
let AndroidManifestSpy;
|
||||||
|
let AndroidManifestFns;
|
||||||
|
let AndroidManifestGetActivitySpy;
|
||||||
|
let AdbSpy;
|
||||||
|
let buildSpy;
|
||||||
|
let installTarget;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
installTarget = { target: 'emulator-5556', isEmulator: true, arch: 'atari' };
|
||||||
|
|
||||||
|
buildSpy = jasmine.createSpyObj('build', ['findBestApkForArchitecture']);
|
||||||
|
target.__set__('build', buildSpy);
|
||||||
|
|
||||||
|
AndroidManifestFns = jasmine.createSpyObj('AndroidManifestFns', ['getPackageId', 'getActivity']);
|
||||||
|
AndroidManifestGetActivitySpy = jasmine.createSpyObj('getActivity', ['getName']);
|
||||||
|
AndroidManifestFns.getActivity.and.returnValue(AndroidManifestGetActivitySpy);
|
||||||
|
AndroidManifestSpy = jasmine.createSpy('AndroidManifest').and.returnValue(AndroidManifestFns);
|
||||||
|
target.__set__('AndroidManifest', AndroidManifestSpy);
|
||||||
|
|
||||||
|
AdbSpy = jasmine.createSpyObj('Adb', ['shell', 'start', 'install', 'uninstall']);
|
||||||
|
AdbSpy.shell.and.returnValue(Promise.resolve());
|
||||||
|
AdbSpy.start.and.returnValue(Promise.resolve());
|
||||||
|
AdbSpy.install.and.returnValue(Promise.resolve());
|
||||||
|
AdbSpy.uninstall.and.returnValue(Promise.resolve());
|
||||||
|
target.__set__('Adb', AdbSpy);
|
||||||
|
|
||||||
|
// Silence output during test
|
||||||
|
spyOn(target.__get__('events'), 'emit');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should install to the passed target', () => {
|
||||||
|
return target.install(installTarget, {}).then(() => {
|
||||||
|
expect(AdbSpy.install.calls.argsFor(0)[0]).toBe(installTarget.target);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should install the correct apk based on the architecture and build results', () => {
|
||||||
|
const buildResults = {
|
||||||
|
apkPaths: 'path/to/apks',
|
||||||
|
buildType: 'debug',
|
||||||
|
buildMethod: 'foo'
|
||||||
|
};
|
||||||
|
|
||||||
|
const apkPath = 'my/apk/path/app.apk';
|
||||||
|
buildSpy.findBestApkForArchitecture.and.returnValue(apkPath);
|
||||||
|
|
||||||
|
return target.install(installTarget, buildResults).then(() => {
|
||||||
|
expect(buildSpy.findBestApkForArchitecture).toHaveBeenCalledWith(buildResults, installTarget.arch);
|
||||||
|
|
||||||
|
expect(AdbSpy.install.calls.argsFor(0)[1]).toBe(apkPath);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should uninstall and reinstall app if failure is due to different certificates', () => {
|
||||||
|
AdbSpy.install.and.returnValues(
|
||||||
|
Promise.reject('Failure: INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES'),
|
||||||
|
Promise.resolve()
|
||||||
|
);
|
||||||
|
|
||||||
|
return target.install(installTarget, {}).then(() => {
|
||||||
|
expect(AdbSpy.install).toHaveBeenCalledTimes(2);
|
||||||
|
expect(AdbSpy.uninstall).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw any error not caused by different certificates', () => {
|
||||||
|
const errorMsg = 'Failure: Failed to install';
|
||||||
|
AdbSpy.install.and.rejectWith(new CordovaError(errorMsg));
|
||||||
|
|
||||||
|
return target.install(installTarget, {}).then(
|
||||||
|
() => fail('Unexpectedly resolved'),
|
||||||
|
err => {
|
||||||
|
expect(err).toEqual(jasmine.any(CordovaError));
|
||||||
|
expect(err.message).toContain(errorMsg);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should unlock the screen on device', () => {
|
||||||
|
return target.install(installTarget, {}).then(() => {
|
||||||
|
expect(AdbSpy.shell).toHaveBeenCalledWith(installTarget.target, 'input keyevent 82');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should start the newly installed app on the device', () => {
|
||||||
|
const packageId = 'unittestapp';
|
||||||
|
const activityName = 'TestActivity';
|
||||||
|
AndroidManifestFns.getPackageId.and.returnValue(packageId);
|
||||||
|
AndroidManifestGetActivitySpy.getName.and.returnValue(activityName);
|
||||||
|
|
||||||
|
return target.install(installTarget, {}).then(() => {
|
||||||
|
expect(AdbSpy.start).toHaveBeenCalledWith(installTarget.target, `${packageId}/.${activityName}`);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
Loading…
Reference in New Issue
Block a user