Skip to content

Commit

Permalink
chore: Update idb startup logic (#61)
Browse files Browse the repository at this point in the history
  • Loading branch information
mykola-mokhnach committed Dec 11, 2022
1 parent d751c95 commit c2ed8e9
Show file tree
Hide file tree
Showing 2 changed files with 17 additions and 64 deletions.
78 changes: 15 additions & 63 deletions lib/tools/system-commands.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,33 +5,16 @@ import _ from 'lodash';
import { retryInterval, waitForCondition } from 'asyncbox';
import {
getPids, DEFAULT_IDB_EXEC_TIMEOUT, IDB_EXECUTABLE,
IDB_COMPANION_EXECUTABLE, DEFAULT_IDB_PORT, DEFAULT_COMPANION_GRPC_PORT,
IDB_COMPANION_EXECUTABLE, DEFAULT_COMPANION_GRPC_PORT,
} from '../helpers';
import log from '../logger.js';


const PROCESS_INIT_TIMEOUT = 5000;
const COMPANION_PGREP_PATTERN = (udid) =>
`${IDB_COMPANION_EXECUTABLE}.*--udid[[:space:]]+${udid}`;
const COMPANION_STARTUP_REGEXP = /Started GRPC server on port (\d+)/;
const COMPANION_STARTUP_REGEXP = /"grpc_port":(\d+)/;
const COMPANION_STARTUP_ERROR_REGEXP = /New Error Built ==> (.+)/;

function buildDaemonArgs (opts = {}) {
const {
port,
grpcPort,
} = opts;

const result = ['daemon'];
if (port) {
result.push('--port', port);
}
if (grpcPort) {
result.push('--grpc-port', grpcPort);
}
return result;
}

const COMPANION_STARTUP_TIMEOUT_SEC = 30;

const systemCallMethods = {};

Expand Down Expand Up @@ -125,7 +108,10 @@ systemCallMethods.connect = async function connect (opts = {}) {
reject(new Error(message));
};
companionProc.once('exit', listeners.exit);
}).timeout(60000, 'Was unable to acquire a GRPC port after 60 seconds timeout');
}).timeout(
COMPANION_STARTUP_TIMEOUT_SEC * 1000,
`Was unable to acquire a GRPC port after ${COMPANION_STARTUP_TIMEOUT_SEC}s timeout`
);
} catch (err) {
cleanupListeners();
if (companionProc.isRunning) {
Expand All @@ -141,57 +127,21 @@ systemCallMethods.connect = async function connect (opts = {}) {

try {
try {
await tpExec(IDB_EXECUTABLE, ['connect', '127.0.0.1', grpcPort]);
await tpExec(IDB_EXECUTABLE, ['connect', this.udid, grpcPort]);
} catch (connectionError) {
await retryInterval(2, 100, async () => {
await this.disconnect();
try {
await tpExec(IDB_EXECUTABLE, ['kill']);
} catch (ign) {}
let isStartupMonitorEnabled = true;
try {
const daemon = new SubProcess(IDB_EXECUTABLE, buildDaemonArgs({
port: this.executable.port,
grpcPort,
}));
let daemonOutput = '';
daemon.on('output', (stdout, stderr) => {
if (isStartupMonitorEnabled && _.trim(stdout || stderr)) {
daemonOutput += `[daemon] ${stdout || stderr}\n`;
}
});
try {
await daemon.start(null, PROCESS_INIT_TIMEOUT);
await B.delay(300);
} catch (ign) {}

if (daemon.isRunning) {
log.debug(`${IDB_EXECUTABLE} daemon started on port ${this.executable.port || DEFAULT_IDB_PORT}`);
} else {
if (!daemonOutput.includes('address already in use')) {
const message = `${IDB_EXECUTABLE} daemon has failed to start: ${daemonOutput}`;
log.warn(message);
throw new Error(message);
}
log.debug(`The port ${this.executable.port || DEFAULT_IDB_PORT} is already in use. ` +
`Assuming it is used by ${IDB_EXECUTABLE} daemon`);
}
await tpExec(IDB_EXECUTABLE, ['connect', '127.0.0.1', grpcPort]);
} catch (connectionError2) {
if (connectionError2.stderr || connectionError2.stdout) {
log.debug(connectionError2.stderr || connectionError2.stdout);
}
throw connectionError2;
} finally {
isStartupMonitorEnabled = false;
}
await tpExec(IDB_EXECUTABLE, ['connect', this.udid, grpcPort]);
});
}
} catch (e) {
if (e.stderr) {
log.debug(e.stderr);
if (e.stderr || e.stdout) {
log.debug(e.stderr || e.stdout);
}
throw new Error(`Cannot start ${IDB_EXECUTABLE} service for '${this.udid}'. ` +
throw new Error(`Cannot start ${IDB_EXECUTABLE} service for the device '${this.udid}'. ` +
`Check the server log for more details.`);
}
log.info(`Successfully established the connection to ${IDB_EXECUTABLE} service for '${this.udid}'`);
Expand Down Expand Up @@ -221,12 +171,14 @@ systemCallMethods.waitForDevice = async function waitForDevice (timeoutMs = 1000

log.debug(`Waiting up to ${timeoutMs}ms for the device to be online`);
const timer = new timing.Timer().start();
let lastError = null;
try {
await waitForCondition(async () => {
try {
await this.exec(['ui', 'describe-all']);
return true;
} catch (e) {
lastError = e.stderr || e.message;
return false;
}
}, {
Expand All @@ -235,7 +187,7 @@ systemCallMethods.waitForDevice = async function waitForDevice (timeoutMs = 1000
});
} catch (e) {
throw new Error(`The device '${this.udid}' is not responding to idb requests after ${timeoutMs}ms timeout. ` +
`Original error: ${e.stderr || e.message}`);
`Original error: ${lastError || e.message}`);
}
log.debug(`The device '${this.udid}' is online and ready to accept idb commands in ` +
`${timer.getDuration().asSeconds.toFixed(3)}s`);
Expand Down
3 changes: 2 additions & 1 deletion test/functional/idb-e2e-specs.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ describe('idb general', function () {
before(async function () {
idb = new IDB({
udid: simctl.udid,
verbose: true,
});
await simctl.bootDevice();
await simctl.startBootMonitor();
Expand All @@ -46,7 +47,7 @@ describe('idb general', function () {

// TODO: getting the description returns data in a format that is a pain
// to parse.
it.skip('should be able to call connect/disconnect multiple times', async function () {
it('should be able to call connect/disconnect multiple times', async function () {
await idb.connect();
await assertDeviceDescription(idb, simctl.udid);
await idb.disconnect();
Expand Down

0 comments on commit c2ed8e9

Please sign in to comment.