Repository: cordova-medic
Updated Branches:
  refs/heads/master f1662f346 -> 9e3d872a3


CB-10510: Reapply Android Emulator retry logic with process fix

This brings back commits 90d06a39cbaf9313edae0fa3cc63a63003b65553
and 7682cd7205f153bc252ab8e1ef445169d34e11c2 which were reverted
and adds a fix for the errors they were causing in buildbot.


Project: http://git-wip-us.apache.org/repos/asf/cordova-medic/repo
Commit: http://git-wip-us.apache.org/repos/asf/cordova-medic/commit/9e3d872a
Tree: http://git-wip-us.apache.org/repos/asf/cordova-medic/tree/9e3d872a
Diff: http://git-wip-us.apache.org/repos/asf/cordova-medic/diff/9e3d872a

Branch: refs/heads/master
Commit: 9e3d872a3fccd483b977b7199f0900146369d1c7
Parents: f1662f3
Author: riknoll <richard.b.kn...@gmail.com>
Authored: Thu Feb 11 14:39:53 2016 -0800
Committer: riknoll <richard.b.kn...@gmail.com>
Committed: Wed Feb 17 17:23:36 2016 -0800

----------------------------------------------------------------------
 lib/start-android-emulator.js |  91 ++++++++++++++++++++++++++++++
 medic/medic-kill.js           |  34 +++++++-----
 medic/medic-run.js            | 111 +++++++++++++++++++++++++------------
 3 files changed, 187 insertions(+), 49 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cordova-medic/blob/9e3d872a/lib/start-android-emulator.js
----------------------------------------------------------------------
diff --git a/lib/start-android-emulator.js b/lib/start-android-emulator.js
new file mode 100644
index 0000000..b10530a
--- /dev/null
+++ b/lib/start-android-emulator.js
@@ -0,0 +1,91 @@
+/*
+ * 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.
+ */
+
+var medicKill   = require("../medic/medic-kill");
+var util        = require("./util");
+var path        = require("path");
+var optimist    = require("optimist");
+
+var ANDROID_EMU_START_MAX_ATTEMPTS = 3;
+var ANDROID_EMU_START_TIMEOUT      = 180000; // in milliseconds (3 minutes)
+
+
+/*
+ * Attempts to start the Android emulator by calling the emulator.js script in
+ * the Android platform directory of the app. If the emulator fails to boot, we
+ * retry a specified number of times.
+ *
+ * @param {string} appPath          An ABSOLUTE path to the app's project 
folder
+ * @param {number} numberOfTries    Number of times to attempt to start the 
emulator
+ *
+ * @returns {promise}   A promise that resolves to the ID of the emulator or
+ *                      null if it failed to start
+ */
+function startAndroidEmulator(appPath, numberOfTries, timeout) {
+    // We need to get the emulator script from within the Android platforms 
folder
+    var emuPath = path.join(appPath, "platforms", "android", "cordova", "lib", 
"emulator");
+    var emulator = require(emuPath);
+
+    var tryStart = function(numberTriesRemaining) {
+        return emulator.start(null, timeout)
+        .then(function(emulatorId) {
+            if (emulatorId) {
+                return emulatorId;
+            } else if (numberTriesRemaining > 0) {
+                // Emulator must have hung while booting, so we need to kill it
+                medicKill(util.ANDROID);
+                return tryStart(numberTriesRemaining - 1);
+            } else {
+                return null;
+            }
+        });
+    };
+
+    // Check if the emulator has already been started
+    return emulator.list_started()
+    .then(function(started) {
+        if (started && started.length > 0) {
+            return started[0];
+        } else {
+            return tryStart(numberOfTries);
+        }
+    });
+}
+
+
+function main() {
+    var argv = optimist
+        .usage("Usage: $0 {options}")
+        .demand("app")
+        .default("attempts", ANDROID_EMU_START_MAX_ATTEMPTS)
+        .default("timeout", ANDROID_EMU_START_TIMEOUT)
+        .argv;
+
+    var workingDir = process.cwd();
+    var appPath = path.isAbsolute(argv.app) ? argv.app : 
path.resolve(workingDir, argv.app);
+
+    startAndroidEmulator(appPath, argv.attempts, argv.timeout)
+    .done(function(emulatorId) {
+        if (!emulatorId) {
+            process.exit(1);
+        }
+    });
+}
+
+main();

http://git-wip-us.apache.org/repos/asf/cordova-medic/blob/9e3d872a/medic/medic-kill.js
----------------------------------------------------------------------
diff --git a/medic/medic-kill.js b/medic/medic-kill.js
index 72b957f..2a77cde 100644
--- a/medic/medic-kill.js
+++ b/medic/medic-kill.js
@@ -37,9 +37,9 @@ function tasksOnPlatform(platformName) {
             return ["iOS Simulator"];
         case util.ANDROID:
             if (util.isWindows()) {
-                return ["emulator-arm.exe", "adb.exe"];
+                return ["emulator-arm.exe"];
             } else {
-                return ["emulator64-x86", "emulator64-arm", "adb"];
+                return ["emulator64-x86", "emulator64-arm"];
             }
             break;
         case util.BLACKBERRY:
@@ -83,21 +83,11 @@ function killTasks(taskNames) {
     });
 }
 
-// main
-function main() {
-
+function killTasksForPlatform(platform) {
     // shell config
     shelljs.config.fatal  = false;
     shelljs.config.silent = false;
 
-    // get args
-    var argv = optimist
-        .usage("Usage: $0 --platform {platform}")
-        .demand("platform")
-        .argv;
-
-    var platform = argv.platform;
-
     // get platform tasks
     var platformTasks = tasksOnPlatform(platform);
 
@@ -109,4 +99,20 @@ function main() {
     killTasks(platformTasks);
 }
 
-main();
+// main
+function main() {
+    // get args
+    var argv = optimist
+        .usage("Usage: $0 --platform {platform}")
+        .demand("platform")
+        .argv;
+
+    killTasksForPlatform(argv.platform);
+}
+
+module.exports = killTasksForPlatform;
+
+// This script can be required or run directly
+if (require.main === module) {
+    main();
+}

http://git-wip-us.apache.org/repos/asf/cordova-medic/blob/9e3d872a/medic/medic-run.js
----------------------------------------------------------------------
diff --git a/medic/medic-run.js b/medic/medic-run.js
index faba135..53c89c8 100644
--- a/medic/medic-run.js
+++ b/medic/medic-run.js
@@ -25,6 +25,7 @@
 
 var fs   = require("fs");
 var path = require("path");
+var child_process = require("child_process");
 
 var shelljs  = require("shelljs");
 var optimist = require("optimist");
@@ -263,7 +264,7 @@ function failedBecauseNoDevice(output) {
 
 function tryConnect(couchdbURI, pendingNumberOfTries, callback) {
     util.medicLog("checking if " + couchdbURI + " is up.");
-       
+
     // check if results server is up
     request({
         uri:     couchdbURI,
@@ -283,10 +284,36 @@ function tryConnect(couchdbURI, pendingNumberOfTries, 
callback) {
         }
         else {
             callback();
-        }      
+        }
     });
 }
 
+/* Starts periodic polling to check for the mobilespec test results in CouchDB.
+ * After it finishes polling, it will terminate the process returning a 0 if
+ * results were found or 1 if they were not.
+ *
+ * @param {string} couchdbURI   The URL for the couchdb instance
+ * @param {string} buildId      The build ID to query the coudchdb for
+ * @param {number} timeout      The amount of time in seconds to continue 
polling
+ */
+function startPollingForTestResults(couchdbURI, buildId, timeout) {
+    testwait.init(couchdbURI);
+
+    // NOTE:
+    //      timeout needs to be in milliseconds, but it's
+    //      given in seconds, so we multiply by 1000
+    testwait.waitTestsCompleted(buildId, timeout * 1000, false).then(
+        function onFulfilled(value) {
+            util.medicLog("Successfully found test results");
+            process.exit(0);
+        },
+        function onRejected(error) {
+            util.fatal("Could not find test results. Check the output of 
medic-log to see if the app crashed before it could upload them to couchdb.");
+        }
+    );
+    util.medicLog("started waiting for test results");
+}
+
 // main
 function main() {
 
@@ -313,6 +340,8 @@ function main() {
     var entryPoint = argv.entry;
     var timeout    = argv.timeout;
 
+    var workingDir = process.cwd();
+
     var cli = getLocalCLI();
 
     // check that the app exists
@@ -336,23 +365,6 @@ function main() {
             platformArgs = windowsSpecificPreparation(argv);
         }
 
-        // start waiting for test results
-        // NOTE:
-        //      timeout needs to be in milliseconds, but it's
-        //      given in seconds, so we multiply by 1000
-        testwait.init(couchdbURI);
-        testwait.waitTestsCompleted(buildId, timeout * 1000, false).then(
-            function onFulfilled(value) {
-                util.medicLog("Successfully found test results");
-                process.exit(0);
-            },
-            function onRejected(error) {
-                console.error("Could not find test results. Check the output 
of medic-log to see if the app crashed before it could upload them to 
couchdb.");
-                process.exit(1);
-            }
-        );
-        util.medicLog("started waiting for test results");
-
         // enter the app directory
         util.medicLog("moving into " + appPath);
         shelljs.pushd(appPath);
@@ -363,8 +375,6 @@ function main() {
         var runCommandDevice   = cli + " run --device " + platform + " -- " + 
platformArgs;
 
         // build the code
-        // NOTE:
-        //      this is SYNCHRONOUS
         util.medicLog("running:");
         util.medicLog("    " + buildCommand);
         var result = shelljs.exec(buildCommand, {silent: false, async: false});
@@ -373,26 +383,57 @@ function main() {
         }
 
         // run the code
-        // NOTE:
-        //      this is ASYNCHRONOUS
         util.medicLog("running:");
         util.medicLog("    " + runCommandDevice);
-        shelljs.exec(runCommandDevice, {silent: false, async: true}, function 
(returnCode, output) {
-            if (failedBecauseNoDevice(output)) {
-                util.medicLog("no device found, so switching to emulator");
+        var runDeviceResult = shelljs.exec(runCommandDevice, {silent: false, 
async: false});
+
+        if (failedBecauseNoDevice(runDeviceResult.output)) {
+            util.medicLog("no device found, so switching to emulator");
+
+            // Because the Android emulator is started separately, we need to
+            // abstract the run step into a function
+            var runOnEmulator = function() {
                 util.medicLog("running:");
                 util.medicLog("    " + runCommandEmulator);
-                shelljs.exec(runCommandEmulator, {silent: false, async: true}, 
function (returnCode, output) {
-                    if (cordovaReturnedError(returnCode, output)) {
-                        util.fatal("running on emulator failed");
-                    }
-                });
-            } else {
-                if (cordovaReturnedError(returnCode, output)) {
-                    util.fatal("running on device failed");
+
+                var runEmulatorResult = shelljs.exec(runCommandEmulator, 
{silent: false, async: false});
+                if (cordovaReturnedError(runEmulatorResult.code, 
runEmulatorResult.output)) {
+                    util.fatal("running on emulator failed");
+                } else {
+                    startPollingForTestResults(couchdbURI, buildId, timeout);
+                }
+            };
+
+            if (platform === util.ANDROID) {
+                // We need to start the emulator first. We can't use "cordova 
run"
+                // because sometimes the Android emulator hangs on Windows
+                // (CB-10510). Buildbot doesn't like the child process that the
+                // emulator script launches because of how it sets stdio to
+                // "inherit". For that reason, we need to spawn a separate
+                // process and factor it out into a separate script.
+                // See 
https://nodejs.org/api/child_process.html#child_process_options_detached
+                util.medicLog("Attempting to start Android emulator");
+
+                var startEmuScript = path.resolve(__dirname, "..", "lib", 
"start-android-emulator.js");
+                var absoluteAppPath = path.isAbsolute(appPath) ? appPath : 
path.resolve(workingDir, appPath);
+
+                var startEmuResult = child_process.spawnSync("node", 
[startEmuScript, "--app", absoluteAppPath], {stdio: "ignore"});
+
+                if (startEmuResult.status > 0) {
+                    util.fatal("Could not start Android emulator");
+                } else {
+                    util.medicLog("Android emulator started");
+                    runOnEmulator();
                 }
+            } else {
+                runOnEmulator();
             }
-        });
+        } else if (cordovaReturnedError(runDeviceResult.code, 
runDeviceResult.output)) {
+            util.fatal("running on device failed");
+        } else {
+            util.medicLog("Finished waiting for run command");
+            startPollingForTestResults(couchdbURI, buildId, timeout);
+        }
     });
 }
 


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscr...@cordova.apache.org
For additional commands, e-mail: commits-h...@cordova.apache.org

Reply via email to