Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[TIMOB-25309] Add support for Xcode 9's multiple simulator instances. #69

Merged
merged 2 commits into from
Sep 26, 2017
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
23 changes: 23 additions & 0 deletions lib/simctl.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ const fs = require('fs');
const __ = appc.i18n(__dirname).__;

exports.activatePair = activatePair;
exports.boot = boot;
exports.create = create;
exports.getSim = getSim;
exports.install = install;
Expand Down Expand Up @@ -149,6 +150,28 @@ function launch(params, callback) {
trySimctl(params, ['launch', params.udid, params.appId], callback);
}

/**
* Boots an simulator runtime. Simulator must be running.
*
* @param {Object} params - Various parameters.
* @param {String} params.simctl - The path to the `simctl` executable.
* @param {String} params.udid - The simulator udid to launch the app on.
* @param {Function} callback(err) - A function to call when finished.
*/
function boot(params, callback) {
if (!params || typeof params !== 'object') {
return callback(new Error(__('Missing params')));
}
if (!params.simctl) {
return callback(new Error(__('Missing "simctl" param')));
}
if (!params.udid) {
return callback(new Error(__('Missing "udid" param')));
}

trySimctl(params, ['boot', params.udid], callback);
}

/**
* Returns a list of all devices, runtimes, device types, and pairs.
*
Expand Down
131 changes: 97 additions & 34 deletions lib/simulator.js
Original file line number Diff line number Diff line change
Expand Up @@ -947,7 +947,7 @@ function launch(simHandleOrUDID, options, callback) {
function checkIfRunningAndBooted(next) {
emitter.emit('log-debug', __('Checking if simulator %s is already running', handle.simulator));

isRunning(handle.simulator, function (err, pid, udid) {
isSimulatorRunning(handle.simulator, function (err, pid, udid) {
if (err) {
emitter.emit('log-debug', __('Failed to check if simulator is running: %s', err.message || err.toString()));
return next(err);
Expand All @@ -960,8 +960,8 @@ function launch(simHandleOrUDID, options, callback) {

emitter.emit('log-debug', __('Simulator is running (pid %s)', pid));

// check the udid
if (udid !== handle.udid) {
// if Xcode 8 or older and the udid doesn't match the running version, then we need to kill the simulator before continuing
if (appc.version.lt(selectedXcode.version, '9.0') && udid !== handle.udid) {
emitter.emit('log-debug', __('%s Simulator is running, but not the udid we want, stopping simulator', handle.name));
stop(handle, next);
return;
Expand All @@ -985,44 +985,75 @@ function launch(simHandleOrUDID, options, callback) {
return next(new Error(__('Simulator is not available')));
}

if (/^shutdown/i.test(sim.state)) {
// the udid that is supposed to be running isn't, kill the simulator
emitter.emit('log-debug', __('%s Simulator is running, but udid %s is shut down, stopping simulator', handle.name, handle.udid));
stop(handle, next);
return;
}
function waitToBoot() {
emitter.emit('log-debug', __('Waiting for sim to boot...'));
simctl.waitUntilBooted({ simctl: handle.simctl, udid: handle.udid, timeout: 30000 }, function (err, _booted) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This reminds me of another thing we recently discussed: Might this be a good time to launch the Simulator earlier like some (..) other platforms do? Basically trigger it as soon as we get the UUID, so we won't have any delay for the initial launch anymore.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can't launch the simulator and then later install the app. ioslib's API isn't designed to do this. I can fix it, but I'd rather allocate the time towards ioslib v2 where I'll fix all these issues.

if (err && err.code !== 666) {
emitter.emit('log-debug', __('Error while waiting for simulator to boot: %s', err.message || err.toString()));
return next(err);
}

simctl.waitUntilBooted({ simctl: handle.simctl, udid: handle.udid, timeout: 30000 }, function (err, _booted) {
if (err && err.code !== 666) {
emitter.emit('log-debug', __('Error while waiting for simulator to boot: %s', err.message || err.toString()));
return next(err);
}
booted = _booted;

emitter.emit('log-debug', booted ? __('Simulator is booted!') : __('Simulator is NOT booted!'));

booted = _booted;
if (err || !booted) {
emitter.emit('log-debug', __('%s Simulator is running, but not in a booted state, stopping simulator', handle.name));
stop(handle, next);
return;
}

emitter.emit('log-debug', booted ? __('Simulator is booted!') : __('Simulator is NOT booted!'));
emitter.emit('log-debug', __('%s Simulator already running with the correct udid', handle.name));

// because we didn't start the simulator, we have no child process to
// listen for when it exits, so we need to monitor it ourselves
setTimeout(function check() {
appc.subprocess.run('ps', ['-p', pid], function (code, out, err) {
if (code) {
simExited();
} else {
setTimeout(check, 1000);
}
});
}, 1000);

next();
});
}

if (err || !booted) {
emitter.emit('log-debug', __('%s Simulator is running, but not in a booted state, stopping simulator', handle.name));
if (appc.version.lt(selectedXcode.version, '9.0')) {
if (/^shutdown/i.test(sim.state)) {
// the udid that is supposed to be running isn't, kill the simulator
emitter.emit('log-debug', __('%s Simulator is running, but udid %s is shut down, stopping simulator', handle.name, handle.udid));
stop(handle, next);
return;
}

emitter.emit('log-debug', __('%s Simulator already running with the correct udid', handle.name));
return waitToBoot();
}

// because we didn't start the simulator, we have no child process to
// listen for when it exits, so we need to monitor it ourselves
setTimeout(function check() {
appc.subprocess.run('ps', ['-p', pid], function (code, out, err) {
if (code) {
simExited();
} else {
setTimeout(check, 1000);
}
});
}, 1000);
// Xcode 9+ path

next();
if (/^booted/i.test(sim.state)) {
return waitToBoot();
}

emitter.emit('log-debug', __('Getting all running simulator runtimes'));
getRunningSimulatorDevices(function (err, sims) {
if (err) {
return next(err);
}

if (sims.some(function (s) { return s.udid === handle.udid; } )) {
return waitToBoot();
}

simctl.boot({ simctl: handle.simctl, udid: handle.udid }, function (err) {
if (err) {
return next(err);
}
waitToBoot();
});
});
});
});
Expand Down Expand Up @@ -1678,7 +1709,7 @@ function launch(simHandleOrUDID, options, callback) {
* @param {String} proc - The path of the executable to find the pid for
* @param {Function} callback - A function to call with the pid
*/
function isRunning(proc, callback) {
function isSimulatorRunning(proc, callback) {
appc.subprocess.run('ps', '-ef', function (code, out, err) {
if (code) {
return callback(new Error(__('Failed to get process list (exit code %d)', code)));
Expand All @@ -1701,6 +1732,38 @@ function isRunning(proc, callback) {
});
}

/**
* Returns a list of running simulators consisting of their pid and udid.
*
* @param {Function} callback - A function to call with the list of running simulators.
*/
function getRunningSimulatorDevices(callback) {
appc.subprocess.run('ps', '-ef', function (code, out, err) {
if (code) {
return callback(new Error(__('Failed to get process list (exit code %d)', code)));
}

var lines = out.split('\n'),
i = 0,
l = lines.length,
m,
procRE = /^\s*\d+\s+(\d+).+ launchd_sim .+\/([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})\//,
results = [];

for (; i < l; i++) {
m = lines[i].match(procRE);
if (m) {
m.push({
pid: m[1],
udid: m[2]
});
}
}

callback(null, results);
});
}

/**
* Stops the specified iOS Simulator.
*
Expand All @@ -1724,7 +1787,7 @@ function stop(simHandle, callback) {
setTimeout(function () {
simHandle.disconnectLogServer && simHandle.disconnectLogServer();

isRunning(simHandle.simulator, function (err, pid) {
isSimulatorRunning(simHandle.simulator, function (err, pid) {
if (err) {
callback(err);
} else if (pid) {
Expand All @@ -1739,7 +1802,7 @@ function stop(simHandle, callback) {
async.whilst(
function () { return simHandle.running; },
function (cb) {
isRunning(simHandle.simulator, function (err, pid) {
isSimulatorRunning(simHandle.simulator, function (err, pid) {
if (!err && !pid) {
simHandle.running = false;
cb();
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "ioslib",
"version": "1.5.1",
"version": "1.6.0",
"description": "iOS Utility Library",
"keywords": [
"appcelerator",
Expand Down