CB-12640: flipped avd parsing logic so that it always tries to use avdmanager to retrieve avds first, then falls back to android command if avdmanager cannot be found (and errors with ENOENT). updated tests - and added explicit tests to ensure to shell out to singular forms of sub-commands when executing android

This commit is contained in:
filmaj 2017-04-06 13:48:58 -07:00
parent 765c4ee9f6
commit 6ef2f67ae8
3 changed files with 36 additions and 24 deletions

View File

@ -116,7 +116,7 @@ module.exports.list_images_using_avdmanager = function () {
}; };
module.exports.list_images_using_android = function() { module.exports.list_images_using_android = function() {
return superspawn.spawn('android', ['list', 'avds']) return superspawn.spawn('android', ['list', 'avd'])
.then(function(output) { .then(function(output) {
var response = output.split('\n'); var response = output.split('\n');
var emulator_list = []; var emulator_list = [];
@ -171,20 +171,20 @@ module.exports.list_images_using_android = function() {
} }
*/ */
module.exports.list_images = function() { module.exports.list_images = function() {
if (forgivingWhichSync('android')) { if (forgivingWhichSync('avdmanager')) {
return module.exports.list_images_using_android() return module.exports.list_images_using_avdmanager()
.catch(function(err) { .catch(function(err) {
// try to use `avdmanager` in case `android` reports it is no longer available. // try to use `avdmanager` in case `android` reports it is no longer available.
// this likely means the target machine is using a newer version of // this likely means the target machine is using a newer version of
// the android sdk, and possibly `avdmanager` is available. // the android sdk, and possibly `avdmanager` is available.
if (err.code == 1 && err.stdout.indexOf('android command is no longer available')) { if (err && err.code == 'ENOENT') {
return module.exports.list_images_using_avdmanager(); return module.exports.list_images_using_android();
} else { } else {
throw err; throw err;
} }
}); });
} else if (forgivingWhichSync('avdmanager')) { } else if (forgivingWhichSync('android')) {
return module.exports.list_images_using_avdmanager(); return module.exports.list_images_using_android();
} else { } else {
return Q().then(function() { return Q().then(function() {
throw new CordovaError('Could not find either `android` or `avdmanager` on your $PATH! Are you sure the Android SDK is installed and available?'); throw new CordovaError('Could not find either `android` or `avdmanager` on your $PATH! Are you sure the Android SDK is installed and available?');
@ -228,6 +228,7 @@ module.exports.list_started = function() {
}; };
// Returns a promise. // Returns a promise.
// TODO: we should remove this, there's a more robust method under android_sdk.js
module.exports.list_targets = function() { module.exports.list_targets = function() {
return superspawn.spawn('android', ['list', 'targets'], {cwd: os.tmpdir()}) return superspawn.spawn('android', ['list', 'targets'], {cwd: os.tmpdir()})
.then(function(output) { .then(function(output) {
@ -399,6 +400,7 @@ module.exports.create_image = function(name, target) {
}); });
} else { } else {
console.log('WARNING : Project target not found, creating avd with a different target but the project may fail to install.'); console.log('WARNING : Project target not found, creating avd with a different target but the project may fail to install.');
// TODO: there's a more robust method for finding targets in android_sdk.js
return superspawn.spawn('android', ['create', 'avd', '--name', name, '--target', this.list_targets()[0]]) return superspawn.spawn('android', ['create', 'avd', '--name', name, '--target', this.list_targets()[0]])
.then(function() { .then(function() {
// TODO: This seems like another error case, even though it always happens. // TODO: This seems like another error case, even though it always happens.

View File

@ -26,6 +26,12 @@ var Q = require("q");
describe("android_sdk", function () { describe("android_sdk", function () {
describe("list_targets_with_android", function() { describe("list_targets_with_android", function() {
it("should invoke `android` with the `list target` command and _not_ the `list targets` command, as the plural form is not supported in some Android SDK Tools versions", function() {
var deferred = Q.defer();
spyOn(superspawn, "spawn").and.returnValue(deferred.promise);
android_sdk.list_targets_with_android();
expect(superspawn.spawn).toHaveBeenCalledWith("android", ["list", "target"]);
});
it("should parse and return results from `android list targets` command", function(done) { it("should parse and return results from `android list targets` command", function(done) {
var deferred = Q.defer(); var deferred = Q.defer();
spyOn(superspawn, "spawn").and.returnValue(deferred.promise); spyOn(superspawn, "spawn").and.returnValue(deferred.promise);

View File

@ -16,8 +16,8 @@
specific language governing permissions and limitations specific language governing permissions and limitations
under the License. under the License.
*/ */
var cc = require("cordova-common");
var emu = require("../../bin/templates/cordova/lib/emulator"); var emu = require("../../bin/templates/cordova/lib/emulator");
var superspawn = require("cordova-common").superspawn;
var Q = require("q"); var Q = require("q");
var fs = require("fs"); var fs = require("fs");
var path = require("path"); var path = require("path");
@ -27,7 +27,7 @@ describe("emulator", function () {
describe("list_images_using_avdmanager", function() { describe("list_images_using_avdmanager", function() {
it("should properly parse details of SDK Tools 25.3.1 `avdmanager` output", function(done) { it("should properly parse details of SDK Tools 25.3.1 `avdmanager` output", function(done) {
var deferred = Q.defer(); var deferred = Q.defer();
spyOn(cc.superspawn, "spawn").and.returnValue(deferred.promise); spyOn(superspawn, "spawn").and.returnValue(deferred.promise);
deferred.resolve(fs.readFileSync(path.join("spec", "fixtures", "sdk25.3-avdmanager_list_avd.txt"), "utf-8")); deferred.resolve(fs.readFileSync(path.join("spec", "fixtures", "sdk25.3-avdmanager_list_avd.txt"), "utf-8"));
return emu.list_images_using_avdmanager() return emu.list_images_using_avdmanager()
.then(function(list) { .then(function(list) {
@ -44,9 +44,15 @@ describe("emulator", function () {
}); });
}); });
describe("list_images_using_android", function() { describe("list_images_using_android", function() {
it("should invoke `android` with the `list avd` command and _not_ the `list avds` command, as the plural form is not supported in some Android SDK Tools versions", function() {
var deferred = Q.defer();
spyOn(superspawn, "spawn").and.returnValue(deferred.promise);
emu.list_images_using_android();
expect(superspawn.spawn).toHaveBeenCalledWith("android", ["list", "avd"]);
});
it("should properly parse details of SDK Tools pre-25.3.1 `android list avd` output", function(done) { it("should properly parse details of SDK Tools pre-25.3.1 `android list avd` output", function(done) {
var deferred = Q.defer(); var deferred = Q.defer();
spyOn(cc.superspawn, "spawn").and.returnValue(deferred.promise); spyOn(superspawn, "spawn").and.returnValue(deferred.promise);
deferred.resolve(fs.readFileSync(path.join("spec", "fixtures", "sdk25.2-android_list_avd.txt"), "utf-8")); deferred.resolve(fs.readFileSync(path.join("spec", "fixtures", "sdk25.2-android_list_avd.txt"), "utf-8"));
return emu.list_images_using_android() return emu.list_images_using_android()
.then(function(list) { .then(function(list) {
@ -70,40 +76,38 @@ describe("emulator", function () {
return cmd; return cmd;
}); });
}); });
it("should try to parse AVD information using `android`", function() { it("should try to parse AVD information using `avdmanager` first", function() {
spyOn(shelljs, "which").and.callFake(function(cmd) { spyOn(shelljs, "which").and.callFake(function(cmd) {
if (cmd == "android") { if (cmd == "avdmanager") {
return true; return true;
} else { } else {
return false; return false;
} }
}); });
var android_spy = spyOn(emu, "list_images_using_android").and.returnValue({catch:function(){}}); var avdmanager_spy = spyOn(emu, "list_images_using_avdmanager").and.returnValue({catch:function(){}});
emu.list_images(); emu.list_images();
expect(android_spy).toHaveBeenCalled(); expect(avdmanager_spy).toHaveBeenCalled();
}); });
it("should catch if `android` exits with non-zero code and specific stdout, and delegate to `avdmanager` if it can find it", function() { it("should catch if `avdmanager` exits with non-zero code, and delegate to `android` if it can find it", function() {
spyOn(shelljs, "which").and.callFake(function(cmd) { spyOn(shelljs, "which").and.callFake(function(cmd) {
if (cmd == "android") { if (cmd == "avdmanager") {
return true; return true;
} else { } else {
return false; return false;
} }
}); });
var avdmanager_spy = spyOn(emu, "list_images_using_avdmanager"); spyOn(emu, "list_images_using_avdmanager").and.returnValue({
// Fake out the old promise to feign a failed `android` command
spyOn(emu, "list_images_using_android").and.returnValue({
catch:function(cb) { catch:function(cb) {
cb({ cb({
code: 1, code: "ENOENT"
stdout: ["The android command is no longer available.",
"For manual SDK and AVD management, please use Android Studio.",
"For command-line tools, use tools/bin/sdkmanager and tools/bin/avdmanager"].join("\n")
}); });
} }
}); });
// Fake out the old promise to feign a failed `android` command
var android_spy = spyOn(emu, "list_images_using_android");
emu.list_images(); emu.list_images();
expect(avdmanager_spy).toHaveBeenCalled(); expect(android_spy).toHaveBeenCalled();
}); });
it("should throw an error if neither `avdmanager` nor `android` are able to be found", function(done) { it("should throw an error if neither `avdmanager` nor `android` are able to be found", function(done) {
spyOn(shelljs, "which").and.returnValue(false); spyOn(shelljs, "which").and.returnValue(false);