UNPKG

appium-ios-simulator

Version:
642 lines (496 loc) 39.3 kB
'use strict'; var _regeneratorRuntime = require('babel-runtime/regenerator')['default']; var _getIterator = require('babel-runtime/core-js/get-iterator')['default']; var _interopRequireDefault = require('babel-runtime/helpers/interop-require-default')['default']; Object.defineProperty(exports, '__esModule', { value: true }); var _logger = require('./logger'); var _logger2 = _interopRequireDefault(_logger); var _lodash = require('lodash'); var _lodash2 = _interopRequireDefault(_lodash); var _teen_process = require('teen_process'); var _asyncbox = require('asyncbox'); var _appiumXcode = require('appium-xcode'); var _nodeSimctl = require('node-simctl'); var _appiumSupport = require('appium-support'); var _certificate = require('./certificate'); var _path = require('path'); var _path2 = _interopRequireDefault(_path); var _simulatorXcode6 = require('./simulator-xcode-6'); var _simulatorXcode62 = _interopRequireDefault(_simulatorXcode6); var _fkill = require('fkill'); var _fkill2 = _interopRequireDefault(_fkill); var DEFAULT_SIM_SHUTDOWN_TIMEOUT = 30000; // pgrep/pkill exit codes: // 0 One or more processes were matched. // 1 No processes were matched. // 2 Invalid options were specified on the command line. // 3 An internal error occurred. function pkill(appName) { var forceKill = arguments.length <= 1 || arguments[1] === undefined ? false : arguments[1]; var args; return _regeneratorRuntime.async(function pkill$(context$1$0) { while (1) switch (context$1$0.prev = context$1$0.next) { case 0: args = forceKill ? ['-9'] : []; args.push('-x', appName); context$1$0.prev = 2; context$1$0.next = 5; return _regeneratorRuntime.awrap((0, _teen_process.exec)('pkill', args)); case 5: return context$1$0.abrupt('return', 0); case 8: context$1$0.prev = 8; context$1$0.t0 = context$1$0['catch'](2); if (_lodash2['default'].isUndefined(context$1$0.t0.code)) { context$1$0.next = 12; break; } throw new Error('Cannot forcefully terminate ' + appName + '. pkill error code: ' + context$1$0.t0.code); case 12: _logger2['default'].error('Received unexpected error while trying to kill ' + appName + ': ' + context$1$0.t0.message); throw context$1$0.t0; case 14: case 'end': return context$1$0.stop(); } }, null, this, [[2, 8]]); } function killAllSimulators() { var timeout = arguments.length <= 0 || arguments[0] === undefined ? DEFAULT_SIM_SHUTDOWN_TIMEOUT : arguments[0]; var xcodeVersion, appName, pids, _ref, stdout, remainingDevices, allSimsAreDown, _iteratorNormalCompletion, _didIteratorError, _iteratorError, _iterator, _step, device; return _regeneratorRuntime.async(function killAllSimulators$(context$1$0) { while (1) switch (context$1$0.prev = context$1$0.next) { case 0: allSimsAreDown = function allSimsAreDown() { var devices; return _regeneratorRuntime.async(function allSimsAreDown$(context$2$0) { while (1) switch (context$2$0.prev = context$2$0.next) { case 0: remainingDevices = []; context$2$0.next = 3; return _regeneratorRuntime.awrap((0, _nodeSimctl.getDevices)()); case 3: devices = context$2$0.sent; devices = _lodash2['default'].flatten(_lodash2['default'].values(devices)); return context$2$0.abrupt('return', _lodash2['default'].every(devices, function (sim) { var state = sim.state.toLowerCase(); var done = state === 'shutdown' || state === 'unavailable' || state === 'disconnected'; if (!done) { remainingDevices.push(sim.name + ' (' + sim.sdk + ', udid: ' + sim.udid + ') is still in state \'' + state + '\''); } return done; })); case 6: case 'end': return context$2$0.stop(); } }, null, this); }; _logger2['default'].debug('Killing all iOS Simulators'); context$1$0.next = 4; return _regeneratorRuntime.awrap((0, _appiumXcode.getVersion)(true)); case 4: xcodeVersion = context$1$0.sent; appName = xcodeVersion.major >= 7 ? 'Simulator' : 'iOS Simulator'; // later versions are slower to close timeout = timeout * (xcodeVersion.major === 8 ? 2 : 1); pids = undefined; context$1$0.prev = 8; context$1$0.next = 11; return _regeneratorRuntime.awrap((0, _teen_process.exec)('pgrep', ['-x', appName])); case 11: _ref = context$1$0.sent; stdout = _ref.stdout; pids = stdout.trim().split('\n').map(function (pid) { return parseInt(pid, 10); }); context$1$0.next = 22; break; case 16: context$1$0.prev = 16; context$1$0.t0 = context$1$0['catch'](8); if (!(context$1$0.t0.code === 1)) { context$1$0.next = 21; break; } _logger2['default'].debug(appName + ' is not running. Continuing...'); return context$1$0.abrupt('return'); case 21: _logger2['default'].warn('pgrep error ' + context$1$0.t0.code + ' while detecting whether ' + appName + ' is running. Trying to kill anyway.'); case 22: context$1$0.next = 24; return _regeneratorRuntime.awrap((function performKill() { var pids = arguments.length <= 0 || arguments[0] === undefined ? [] : arguments[0]; return _regeneratorRuntime.async(function performKill$(context$2$0) { while (1) switch (context$2$0.prev = context$2$0.next) { case 0: context$2$0.prev = 0; context$2$0.next = 3; return _regeneratorRuntime.awrap((0, _teen_process.exec)('xcrun', ['simctl', 'shutdown', 'booted'], { timeout: timeout })); case 3: context$2$0.next = 7; break; case 5: context$2$0.prev = 5; context$2$0.t0 = context$2$0['catch'](0); case 7: if (!pids.length) { context$2$0.next = 18; break; } _logger2['default'].debug('Using fkill to kill processes: ' + pids.join(', ')); context$2$0.prev = 9; context$2$0.next = 12; return _regeneratorRuntime.awrap((0, _fkill2['default'])(pids, { force: true })); case 12: context$2$0.next = 16; break; case 14: context$2$0.prev = 14; context$2$0.t1 = context$2$0['catch'](9); case 16: context$2$0.next = 21; break; case 18: _logger2['default'].debug('Using pkill to kill application: ' + appName); context$2$0.next = 21; return _regeneratorRuntime.awrap(pkill(appName, true)); case 21: case 'end': return context$2$0.stop(); } }, null, this, [[0, 5], [9, 14]]); })(pids)); case 24: remainingDevices = []; context$1$0.prev = 25; context$1$0.next = 28; return _regeneratorRuntime.awrap((0, _asyncbox.waitForCondition)(allSimsAreDown, { waitMs: timeout, intervalMs: 200 })); case 28: context$1$0.next = 54; break; case 30: context$1$0.prev = 30; context$1$0.t1 = context$1$0['catch'](25); if (!(remainingDevices.length > 0)) { context$1$0.next = 53; break; } _logger2['default'].warn('The following devices are still not in the correct state after ' + timeout + ' ms:'); _iteratorNormalCompletion = true; _didIteratorError = false; _iteratorError = undefined; context$1$0.prev = 37; for (_iterator = _getIterator(remainingDevices); !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { device = _step.value; _logger2['default'].warn(' ' + device); } context$1$0.next = 45; break; case 41: context$1$0.prev = 41; context$1$0.t2 = context$1$0['catch'](37); _didIteratorError = true; _iteratorError = context$1$0.t2; case 45: context$1$0.prev = 45; context$1$0.prev = 46; if (!_iteratorNormalCompletion && _iterator['return']) { _iterator['return'](); } case 48: context$1$0.prev = 48; if (!_didIteratorError) { context$1$0.next = 51; break; } throw _iteratorError; case 51: return context$1$0.finish(48); case 52: return context$1$0.finish(45); case 53: throw context$1$0.t1; case 54: case 'end': return context$1$0.stop(); } }, null, this, [[8, 16], [25, 30], [37, 41, 45, 53], [46,, 48, 52]]); } function endAllSimulatorDaemons() { var _arr, _i, servicePattern, launchCtlCommand, stopCmd, removeCmd; return _regeneratorRuntime.async(function endAllSimulatorDaemons$(context$1$0) { var _this = this; while (1) switch (context$1$0.prev = context$1$0.next) { case 0: _logger2['default'].debug('Ending all simulator daemons'); _arr = ['com.apple.iphonesimulator', 'com.apple.CoreSimulator']; _i = 0; case 3: if (!(_i < _arr.length)) { context$1$0.next = 28; break; } servicePattern = _arr[_i]; _logger2['default'].debug('Killing any other ' + servicePattern + ' daemons'); launchCtlCommand = 'launchctl list | grep ' + servicePattern + ' | cut -f 3 | xargs -n 1 launchctl'; context$1$0.prev = 7; stopCmd = launchCtlCommand + ' stop'; context$1$0.next = 11; return _regeneratorRuntime.awrap((0, _teen_process.exec)('bash', ['-c', stopCmd])); case 11: context$1$0.next = 16; break; case 13: context$1$0.prev = 13; context$1$0.t0 = context$1$0['catch'](7); _logger2['default'].warn('Could not stop ' + servicePattern + ' daemons, carrying on anyway!'); case 16: context$1$0.prev = 16; removeCmd = launchCtlCommand + ' remove'; context$1$0.next = 20; return _regeneratorRuntime.awrap((0, _teen_process.exec)('bash', ['-c', removeCmd])); case 20: context$1$0.next = 25; break; case 22: context$1$0.prev = 22; context$1$0.t1 = context$1$0['catch'](16); _logger2['default'].warn('Could not remove ' + servicePattern + ' daemons, carrying on anyway!'); case 25: _i++; context$1$0.next = 3; break; case 28: context$1$0.prev = 28; context$1$0.next = 31; return _regeneratorRuntime.awrap((0, _asyncbox.waitForCondition)(function callee$1$0() { var _ref2, stdout; return _regeneratorRuntime.async(function callee$1$0$(context$2$0) { while (1) switch (context$2$0.prev = context$2$0.next) { case 0: context$2$0.next = 2; return _regeneratorRuntime.awrap((0, _teen_process.exec)('bash', ['-c', 'ps -e | grep launchd_sim | grep -v bash | grep -v grep | awk {\'print$1\'}'])); case 2: _ref2 = context$2$0.sent; stdout = _ref2.stdout; return context$2$0.abrupt('return', stdout.trim().length === 0); case 5: case 'end': return context$2$0.stop(); } }, null, _this); }, { waitMs: 5000, intervalMs: 500 })); case 31: context$1$0.next = 36; break; case 33: context$1$0.prev = 33; context$1$0.t2 = context$1$0['catch'](28); _logger2['default'].warn('Could not end all simulator daemons, carrying on!'); case 36: _logger2['default'].debug('Finishing ending all simulator daemons'); case 37: case 'end': return context$1$0.stop(); } }, null, this, [[7, 13], [16, 22], [28, 33]]); } function simExists(udid) { var devices; return _regeneratorRuntime.async(function simExists$(context$1$0) { while (1) switch (context$1$0.prev = context$1$0.next) { case 0: context$1$0.next = 2; return _regeneratorRuntime.awrap((0, _nodeSimctl.getDevices)()); case 2: devices = context$1$0.sent; devices = _lodash2['default'].toPairs(devices).map(function (pair) { return pair[1]; }).reduce(function (a, b) { return a.concat(b); }, []); return context$1$0.abrupt('return', !!_lodash2['default'].find(devices, function (sim) { return sim.udid === udid; })); case 5: case 'end': return context$1$0.stop(); } }, null, this); } function safeRimRaf(delPath) { var tryNum = arguments.length <= 1 || arguments[1] === undefined ? 0 : arguments[1]; return _regeneratorRuntime.async(function safeRimRaf$(context$1$0) { while (1) switch (context$1$0.prev = context$1$0.next) { case 0: context$1$0.prev = 0; context$1$0.next = 3; return _regeneratorRuntime.awrap(_appiumSupport.fs.rimraf(delPath)); case 3: context$1$0.next = 16; break; case 5: context$1$0.prev = 5; context$1$0.t0 = context$1$0['catch'](0); if (!(tryNum < 20)) { context$1$0.next = 16; break; } if (!(context$1$0.t0.message.indexOf('ENOTEMPTY') !== -1)) { context$1$0.next = 13; break; } _logger2['default'].debug('Path \'' + delPath + '\' was not empty during delete; retrying'); return context$1$0.abrupt('return', safeRimRaf(delPath, tryNum + 1)); case 13: if (!(context$1$0.t0.message.indexOf('ENOENT') !== -1)) { context$1$0.next = 16; break; } _logger2['default'].debug('Path \'' + delPath + '\'\' did not exist when we tried to delete, ignoring'); return context$1$0.abrupt('return', safeRimRaf(delPath, tryNum + 1)); case 16: case 'end': return context$1$0.stop(); } }, null, this, [[0, 5]]); } function installSSLCert(pemText, udid) { var tempFileName, pathToKeychain, certificate; return _regeneratorRuntime.async(function installSSLCert$(context$1$0) { while (1) switch (context$1$0.prev = context$1$0.next) { case 0: context$1$0.prev = 0; context$1$0.next = 3; return _regeneratorRuntime.awrap(_appiumSupport.fs.which('openssl')); case 3: context$1$0.next = 9; break; case 5: context$1$0.prev = 5; context$1$0.t0 = context$1$0['catch'](0); _logger2['default'].debug('customSSLCert requires openssl to be available on path'); _logger2['default'].errorAndThrow('Command \'openssl\' not found'); case 9: context$1$0.prev = 9; context$1$0.next = 12; return _regeneratorRuntime.awrap(_appiumSupport.fs.which('sqlite3')); case 12: context$1$0.next = 18; break; case 14: context$1$0.prev = 14; context$1$0.t1 = context$1$0['catch'](9); _logger2['default'].debug('customSSLCert requires sqlite3 to be available on path'); _logger2['default'].errorAndThrow('Command \'sqlite3\' not found'); case 18: tempFileName = _path2['default'].resolve(__dirname + '/temp-ssl-cert.pem'); pathToKeychain = _path2['default'].resolve(new _simulatorXcode62['default'](udid).getDir()); context$1$0.next = 22; return _regeneratorRuntime.awrap(_appiumSupport.fs.writeFile(tempFileName, pemText)); case 22: context$1$0.prev = 22; context$1$0.next = 25; return _regeneratorRuntime.awrap(_appiumSupport.fs.stat(pathToKeychain)); case 25: context$1$0.next = 31; break; case 27: context$1$0.prev = 27; context$1$0.t2 = context$1$0['catch'](22); _logger2['default'].debug('Could not install SSL certificate. No simulator with udid \'' + udid + '\''); _logger2['default'].errorAndThrow(context$1$0.t2); case 31: certificate = new _certificate.Certificate(tempFileName); _logger2['default'].debug('Installing certificate to ' + pathToKeychain); context$1$0.next = 35; return _regeneratorRuntime.awrap(certificate.add(pathToKeychain)); case 35: context$1$0.next = 37; return _regeneratorRuntime.awrap(_appiumSupport.fs.unlink(tempFileName)); case 37: return context$1$0.abrupt('return', certificate); case 38: case 'end': return context$1$0.stop(); } }, null, this, [[0, 5], [9, 14], [22, 27]]); } function uninstallSSLCert(pemText, udid) { var tempFileName, pathToKeychain, certificate; return _regeneratorRuntime.async(function uninstallSSLCert$(context$1$0) { while (1) switch (context$1$0.prev = context$1$0.next) { case 0: context$1$0.prev = 0; tempFileName = _path2['default'].resolve(__dirname, 'temp-ssl-cert.pem'); pathToKeychain = _path2['default'].resolve(new _simulatorXcode62['default'](udid).getDir()); context$1$0.next = 5; return _regeneratorRuntime.awrap(_appiumSupport.fs.writeFile(tempFileName, pemText)); case 5: certificate = new _certificate.Certificate(tempFileName); context$1$0.next = 8; return _regeneratorRuntime.awrap(certificate.remove(pathToKeychain)); case 8: context$1$0.next = 10; return _regeneratorRuntime.awrap(_appiumSupport.fs.unlink(tempFileName)); case 10: return context$1$0.abrupt('return', certificate); case 13: context$1$0.prev = 13; context$1$0.t0 = context$1$0['catch'](0); _logger2['default'].debug('Could not uninstall SSL certificate. No simulator with udid \'' + udid + '\''); _logger2['default'].errorAndThrow(context$1$0.t0); case 17: case 'end': return context$1$0.stop(); } }, null, this, [[0, 13]]); } /** * Runs a command line sqlite3 query */ function execSQLiteQuery(db, query) { for (var _len = arguments.length, queryParams = Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) { queryParams[_key - 2] = arguments[_key]; } var queryTokens, formattedQuery; return _regeneratorRuntime.async(function execSQLiteQuery$(context$1$0) { while (1) switch (context$1$0.prev = context$1$0.next) { case 0: queryTokens = query.split('?'); formattedQuery = []; queryParams.forEach(function (param, i) { formattedQuery.push(queryTokens[i]); formattedQuery.push(param.replace(/'/g, "''")); }); formattedQuery.push(queryTokens[queryTokens.length - 1]); context$1$0.next = 6; return _regeneratorRuntime.awrap((0, _teen_process.exec)('sqlite3', ['-line', db, formattedQuery.join('')])); case 6: return context$1$0.abrupt('return', context$1$0.sent); case 7: case 'end': return context$1$0.stop(); } }, null, this); } exports.killAllSimulators = killAllSimulators; exports.endAllSimulatorDaemons = endAllSimulatorDaemons; exports.safeRimRaf = safeRimRaf; exports.simExists = simExists; exports.installSSLCert = installSSLCert; exports.uninstallSSLCert = uninstallSSLCert; exports.execSQLiteQuery = execSQLiteQuery; // wait for all the devices to be shutdown before Continuing // but only print out the failed ones when they are actually fully failed // waiting until the simulator service has died. // see the README for github.com/appium/node-simctl for example output of getDevices() // Check that openssl is installed on the path // Check that sqlite3 is installed on the path //# sourceMappingURL=data:application/json;charset=utf8;base64,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RSxJQUFJLFFBQUksQ0FBQztBQUNuRiw0QkFBSSxhQUFhLGdCQUFHLENBQUM7Ozs7Ozs7Q0FFeEI7Ozs7O0FBS0QsU0FBZSxlQUFlLENBQUUsRUFBRSxFQUFFLEtBQUs7b0NBQUssV0FBVztBQUFYLGVBQVc7OztNQUNuRCxXQUFXLEVBQ1gsY0FBYzs7OztBQURkLG1CQUFXLEdBQUcsS0FBSyxDQUFDLEtBQUssQ0FBQyxHQUFHLENBQUM7QUFDOUIsc0JBQWMsR0FBRyxFQUFFOztBQUN2QixtQkFBVyxDQUFDLE9BQU8sQ0FBQyxVQUFDLEtBQUssRUFBRSxDQUFDLEVBQUs7QUFDaEMsd0JBQWMsQ0FBQyxJQUFJLENBQUMsV0FBVyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUM7QUFDcEMsd0JBQWMsQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLE9BQU8sQ0FBQyxJQUFJLEVBQUUsSUFBSSxDQUFDLENBQUMsQ0FBQztTQUNoRCxDQUFDLENBQUM7QUFDSCxzQkFBYyxDQUFDLElBQUksQ0FBQyxXQUFXLENBQUMsV0FBVyxDQUFDLE1BQU0sR0FBRyxDQUFDLENBQUMsQ0FBQyxDQUFDOzs7eUNBRTVDLHdCQUFLLFNBQVMsRUFBRSxDQUFDLE9BQU8sRUFBRSxFQUFFLEVBQUUsY0FBYyxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDOzs7Ozs7Ozs7O0NBQ3JFOztRQUVRLGlCQUFpQixHQUFqQixpQkFBaUI7UUFBRSxzQkFBc0IsR0FBdEIsc0JBQXNCO1FBQUUsVUFBVSxHQUFWLFVBQVU7UUFBRSxTQUFTLEdBQVQsU0FBUztRQUFFLGNBQWMsR0FBZCxjQUFjO1FBQUUsZ0JBQWdCLEdBQWhCLGdCQUFnQjtRQUFFLGVBQWUsR0FBZixlQUFlIiwiZmlsZSI6ImxpYi91dGlscy5qcyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCBsb2cgZnJvbSAnLi9sb2dnZXInO1xuaW1wb3J0IF8gZnJvbSAnbG9kYXNoJztcbmltcG9ydCB7IGV4ZWMgfSBmcm9tICd0ZWVuX3Byb2Nlc3MnO1xuaW1wb3J0IHsgd2FpdEZvckNvbmRpdGlvbiB9IGZyb20gJ2FzeW5jYm94JztcbmltcG9ydCB7IGdldFZlcnNpb24gfSBmcm9tICdhcHBpdW0teGNvZGUnO1xuaW1wb3J0IHsgZ2V0RGV2aWNlcyB9IGZyb20gJ25vZGUtc2ltY3RsJztcbmltcG9ydCB7IGZzIH0gZnJvbSAnYXBwaXVtLXN1cHBvcnQnO1xuaW1wb3J0IHsgQ2VydGlmaWNhdGUgfSBmcm9tICcuL2NlcnRpZmljYXRlJztcbmltcG9ydCBwYXRoIGZyb20gJ3BhdGgnO1xuaW1wb3J0IFNpbXVsYXRvciBmcm9tICcuL3NpbXVsYXRvci14Y29kZS02JztcbmltcG9ydCBma2lsbCBmcm9tICdma2lsbCc7XG5cblxuY29uc3QgREVGQVVMVF9TSU1fU0hVVERPV05fVElNRU9VVCA9IDMwMDAwO1xuXG4vLyBwZ3JlcC9wa2lsbCBleGl0IGNvZGVzOlxuLy8gMCAgICAgICBPbmUgb3IgbW9yZSBwcm9jZXNzZXMgd2VyZSBtYXRjaGVkLlxuLy8gMSAgICAgICBObyBwcm9jZXNzZXMgd2VyZSBtYXRjaGVkLlxuLy8gMiAgICAgICBJbnZhbGlkIG9wdGlvbnMgd2VyZSBzcGVjaWZpZWQgb24gdGhlIGNvbW1hbmQgbGluZS5cbi8vIDMgICAgICAgQW4gaW50ZXJuYWwgZXJyb3Igb2NjdXJyZWQuXG5cbmFzeW5jIGZ1bmN0aW9uIHBraWxsIChhcHBOYW1lLCBmb3JjZUtpbGwgPSBmYWxzZSkge1xuICBsZXQgYXJncyA9IGZvcmNlS2lsbCA/IFsnLTknXSA6IFtdO1xuICBhcmdzLnB1c2goJy14JywgYXBwTmFtZSk7XG4gIHRyeSB7XG4gICAgYXdhaXQgZXhlYygncGtpbGwnLCBhcmdzKTtcbiAgICByZXR1cm4gMDtcbiAgfSBjYXRjaCAoZXJyKSB7XG4gICAgaWYgKCFfLmlzVW5kZWZpbmVkKGVyci5jb2RlKSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKGBDYW5ub3QgZm9yY2VmdWxseSB0ZXJtaW5hdGUgJHthcHBOYW1lfS4gcGtpbGwgZXJyb3IgY29kZTogJHtlcnIuY29kZX1gKTtcbiAgICB9XG4gICAgbG9nLmVycm9yKGBSZWNlaXZlZCB1bmV4cGVjdGVkIGVycm9yIHdoaWxlIHRyeWluZyB0byBraWxsICR7YXBwTmFtZX06ICR7ZXJyLm1lc3NhZ2V9YCk7XG4gICAgdGhyb3cgZXJyO1xuICB9XG59XG5cbmFzeW5jIGZ1bmN0aW9uIGtpbGxBbGxTaW11bGF0b3JzICh0aW1lb3V0ID0gREVGQVVMVF9TSU1fU0hVVERPV05fVElNRU9VVCkge1xuICBsb2cuZGVidWcoJ0tpbGxpbmcgYWxsIGlPUyBTaW11bGF0b3JzJyk7XG4gIGNvbnN0IHhjb2RlVmVyc2lvbiA9IGF3YWl0IGdldFZlcnNpb24odHJ1ZSk7XG4gIGNvbnN0IGFwcE5hbWUgPSB4Y29kZVZlcnNpb24ubWFqb3IgPj0gNyA/ICdTaW11bGF0b3InIDogJ2lPUyBTaW11bGF0b3InO1xuXG4gIC8vIGxhdGVyIHZlcnNpb25zIGFyZSBzbG93ZXIgdG8gY2xvc2VcbiAgdGltZW91dCA9IHRpbWVvdXQgKiAoeGNvZGVWZXJzaW9uLm1ham9yID09PSA4ID8gMiA6IDEpO1xuXG4gIGxldCBwaWRzO1xuICB0cnkge1xuICAgIGxldCB7c3Rkb3V0fSA9IGF3YWl0IGV4ZWMoJ3BncmVwJywgWycteCcsIGFwcE5hbWVdKTtcbiAgICBwaWRzID0gc3Rkb3V0LnRyaW0oKS5zcGxpdCgnXFxuJykubWFwKChwaWQpID0+IHBhcnNlSW50KHBpZCwgMTApKTtcbiAgfSBjYXRjaCAoZSkge1xuICAgIGlmIChlLmNvZGUgPT09IDEpIHtcbiAgICAgIGxvZy5kZWJ1ZyhgJHthcHBOYW1lfSBpcyBub3QgcnVubmluZy4gQ29udGludWluZy4uLmApO1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBsb2cud2FybihgcGdyZXAgZXJyb3IgJHtlLmNvZGV9IHdoaWxlIGRldGVjdGluZyB3aGV0aGVyICR7YXBwTmFtZX0gaXMgcnVubmluZy4gVHJ5aW5nIHRvIGtpbGwgYW55d2F5LmApO1xuICB9XG5cbiAgYXdhaXQgKGFzeW5jIGZ1bmN0aW9uIHBlcmZvcm1LaWxsIChwaWRzID0gW10pIHtcbiAgICB0cnkge1xuICAgICAgYXdhaXQgZXhlYygneGNydW4nLCBbJ3NpbWN0bCcsICdzaHV0ZG93bicsICdib290ZWQnXSwge3RpbWVvdXR9KTtcbiAgICB9IGNhdGNoIChpZ24pIHt9XG5cbiAgICBpZiAocGlkcy5sZW5ndGgpIHtcbiAgICAgIGxvZy5kZWJ1ZyhgVXNpbmcgZmtpbGwgdG8ga2lsbCBwcm9jZXNzZXM6ICR7cGlkcy5qb2luKCcsICcpfWApO1xuICAgICAgdHJ5IHtcbiAgICAgICAgYXdhaXQgZmtpbGwocGlkcywge2ZvcmNlOiB0cnVlfSk7XG4gICAgICB9IGNhdGNoIChpZ24pIHt9XG4gICAgfSBlbHNlIHtcbiAgICAgIGxvZy5kZWJ1ZyhgVXNpbmcgcGtpbGwgdG8ga2lsbCBhcHBsaWNhdGlvbjogJHthcHBOYW1lfWApO1xuICAgICAgYXdhaXQgcGtpbGwoYXBwTmFtZSwgdHJ1ZSk7XG4gICAgfVxuICB9KShwaWRzKTtcblxuICAvLyB3YWl0IGZvciBhbGwgdGhlIGRldmljZXMgdG8gYmUgc2h1dGRvd24gYmVmb3JlIENvbnRpbnVpbmdcbiAgLy8gYnV0IG9ubHkgcHJpbnQgb3V0IHRoZSBmYWlsZWQgb25lcyB3aGVuIHRoZXkgYXJlIGFjdHVhbGx5IGZ1bGx5IGZhaWxlZFxuICBsZXQgcmVtYWluaW5nRGV2aWNlcyA9IFtdO1xuICBhc3luYyBmdW5jdGlvbiBhbGxTaW1zQXJlRG93biAoKSB7XG4gICAgcmVtYWluaW5nRGV2aWNlcyA9IFtdO1xuICAgIGxldCBkZXZpY2VzID0gYXdhaXQgZ2V0RGV2aWNlcygpO1xuICAgIGRldmljZXMgPSBfLmZsYXR0ZW4oXy52YWx1ZXMoZGV2aWNlcykpO1xuICAgIHJldHVybiBfLmV2ZXJ5KGRldmljZXMsIChzaW0pID0+IHtcbiAgICAgIGxldCBzdGF0ZSA9IHNpbS5zdGF0ZS50b0xvd2VyQ2FzZSgpO1xuICAgICAgbGV0IGRvbmUgPSBzdGF0ZSA9PT0gJ3NodXRkb3duJyB8fFxuICAgICAgICAgICAgICAgICBzdGF0ZSA9PT0gJ3VuYXZhaWxhYmxlJyB8fFxuICAgICAgICAgICAgICAgICBzdGF0ZSA9PT0gJ2Rpc2Nvbm5lY3RlZCc7XG4gICAgICBpZiAoIWRvbmUpIHtcbiAgICAgICAgcmVtYWluaW5nRGV2aWNlcy5wdXNoKGAke3NpbS5uYW1lfSAoJHtzaW0uc2RrfSwgdWRpZDogJHtzaW0udWRpZH0pIGlzIHN0aWxsIGluIHN0YXRlICcke3N0YXRlfSdgKTtcbiAgICAgIH1cbiAgICAgIHJldHVybiBkb25lO1xuICAgIH0pO1xuICB9XG4gIHRyeSB7XG4gICAgYXdhaXQgd2FpdEZvckNvbmRpdGlvbihhbGxTaW1zQXJlRG93biwge1xuICAgICAgd2FpdE1zOiB0aW1lb3V0LFxuICAgICAgaW50ZXJ2YWxNczogMjAwXG4gICAgfSk7XG4gIH0gY2F0Y2ggKGVycikge1xuICAgIGlmIChyZW1haW5pbmdEZXZpY2VzLmxlbmd0aCA+IDApIHtcbiAgICAgIGxvZy53YXJuKGBUaGUgZm9sbG93aW5nIGRldmljZXMgYXJlIHN0aWxsIG5vdCBpbiB0aGUgY29ycmVjdCBzdGF0ZSBhZnRlciAke3RpbWVvdXR9IG1zOmApO1xuICAgICAgZm9yIChsZXQgZGV2aWNlIG9mIHJlbWFpbmluZ0RldmljZXMpIHtcbiAgICAgICAgbG9nLndhcm4oYCAgICAke2RldmljZX1gKTtcbiAgICAgIH1cbiAgICB9XG4gICAgdGhyb3cgZXJyO1xuICB9XG59XG5cbmFzeW5jIGZ1bmN0aW9uIGVuZEFsbFNpbXVsYXRvckRhZW1vbnMgKCkge1xuICBsb2cuZGVidWcoJ0VuZGluZyBhbGwgc2ltdWxhdG9yIGRhZW1vbnMnKTtcbiAgZm9yIChsZXQgc2VydmljZVBhdHRlcm4gb2YgWydjb20uYXBwbGUuaXBob25lc2ltdWxhdG9yJywgJ2NvbS5hcHBsZS5Db3JlU2ltdWxhdG9yJ10pIHtcbiAgICBsb2cuZGVidWcoYEtpbGxpbmcgYW55IG90aGVyICR7c2VydmljZVBhdHRlcm59IGRhZW1vbnNgKTtcbiAgICBsZXQgbGF1bmNoQ3RsQ29tbWFuZCA9IGBsYXVuY2hjdGwgbGlzdCB8IGdyZXAgJHtzZXJ2aWNlUGF0dGVybn0gfCBjdXQgLWYgMyB8IHhhcmdzIC1uIDEgbGF1bmNoY3RsYDtcbiAgICB0cnkge1xuICAgICAgbGV0IHN0b3BDbWQgPSBgJHtsYXVuY2hDdGxDb21tYW5kfSBzdG9wYDtcbiAgICAgIGF3YWl0IGV4ZWMoJ2Jhc2gnLCBbJy1jJywgc3RvcENtZF0pO1xuICAgIH0gY2F0Y2ggKGVycikge1xuICAgICAgbG9nLndhcm4oYENvdWxkIG5vdCBzdG9wICR7c2VydmljZVBhdHRlcm59IGRhZW1vbnMsIGNhcnJ5aW5nIG9uIGFueXdheSFgKTtcbiAgICB9XG4gICAgdHJ5IHtcbiAgICAgIGxldCByZW1vdmVDbWQgPSBgJHtsYXVuY2hDdGxDb21tYW5kfSByZW1vdmVgO1xuICAgICAgYXdhaXQgZXhlYygnYmFzaCcsIFsnLWMnLCByZW1vdmVDbWRdKTtcbiAgICB9IGNhdGNoIChlcnIpIHtcbiAgICAgIGxvZy53YXJuKGBDb3VsZCBub3QgcmVtb3ZlICR7c2VydmljZVBhdHRlcm59IGRhZW1vbnMsIGNhcnJ5aW5nIG9uIGFueXdheSFgKTtcbiAgICB9XG4gIH1cbiAgLy8gd2FpdGluZyB1bnRpbCB0aGUgc2ltdWxhdG9yIHNlcnZpY2UgaGFzIGRpZWQuXG4gIHRyeSB7XG4gICAgYXdhaXQgd2FpdEZvckNvbmRpdGlvbihhc3luYyAoKSA9PiB7XG4gICAgICBsZXQge3N0ZG91dH0gPSBhd2FpdCBleGVjKCdiYXNoJywgWyctYycsXG4gICAgICAgIGBwcyAtZSAgfCBncmVwIGxhdW5jaGRfc2ltIHwgZ3JlcCAtdiBiYXNoIHwgZ3JlcCAtdiBncmVwIHwgYXdrIHsncHJpbnQkMSd9YF0pO1xuICAgICAgcmV0dXJuIHN0ZG91dC50cmltKCkubGVuZ3RoID09PSAwO1xuICAgIH0sIHt3YWl0TXM6IDUwMDAsIGludGVydmFsTXM6IDUwMH0pO1xuICB9IGNhdGNoIChlcnIpIHtcbiAgICBsb2cud2FybihgQ291bGQgbm90IGVuZCBhbGwgc2ltdWxhdG9yIGRhZW1vbnMsIGNhcnJ5aW5nIG9uIWApO1xuICB9XG4gIGxvZy5kZWJ1ZygnRmluaXNoaW5nIGVuZGluZyBhbGwgc2ltdWxhdG9yIGRhZW1vbnMnKTtcbn1cblxuYXN5bmMgZnVuY3Rpb24gc2ltRXhpc3RzICh1ZGlkKSB7XG4gIC8vIHNlZSB0aGUgUkVBRE1FIGZvciBnaXRodWIuY29tL2FwcGl1bS9ub2RlLXNpbWN0bCBmb3IgZXhhbXBsZSBvdXRwdXQgb2YgZ2V0RGV2aWNlcygpXG4gIGxldCBkZXZpY2VzID0gYXdhaXQgZ2V0RGV2aWNlcygpO1xuXG4gIGRldmljZXMgPSBfLnRvUGFpcnMoZGV2aWNlcykubWFwKChwYWlyKSA9PiB7XG4gICAgcmV0dXJuIHBhaXJbMV07XG4gIH0pLnJlZHVjZSgoYSwgYikgPT4ge1xuICAgIHJldHVybiBhLmNvbmNhdChiKTtcbiAgfSwgW10pO1xuICByZXR1cm4gISFfLmZpbmQoZGV2aWNlcywgKHNpbSkgPT4ge1xuICAgIHJldHVybiBzaW0udWRpZCA9PT0gdWRpZDtcbiAgfSk7XG59XG5cbmFzeW5jIGZ1bmN0aW9uIHNhZmVSaW1SYWYgKGRlbFBhdGgsIHRyeU51bSA9IDApIHtcbiAgdHJ5IHtcbiAgICBhd2FpdCBmcy5yaW1yYWYoZGVsUGF0aCk7XG4gIH0gY2F0Y2ggKGVycikge1xuICAgIGlmICh0cnlOdW0gPCAyMCkge1xuICAgICAgaWYgKGVyci5tZXNzYWdlLmluZGV4T2YoJ0VOT1RFTVBUWScpICE9PSAtMSkge1xuICAgICAgICBsb2cuZGVidWcoYFBhdGggJyR7ZGVsUGF0aH0nIHdhcyBub3QgZW1wdHkgZHVyaW5nIGRlbGV0ZTsgcmV0cnlpbmdgKTtcbiAgICAgICAgcmV0dXJuIHNhZmVSaW1SYWYoZGVsUGF0aCwgdHJ5TnVtICsgMSk7XG4gICAgICB9IGVsc2UgaWYgKGVyci5tZXNzYWdlLmluZGV4T2YoJ0VOT0VOVCcpICE9PSAtMSkge1xuICAgICAgICBsb2cuZGVidWcoYFBhdGggJyR7ZGVsUGF0aH0nJyBkaWQgbm90IGV4aXN0IHdoZW4gd2UgdHJpZWQgdG8gZGVsZXRlLCBpZ25vcmluZ2ApO1xuICAgICAgICByZXR1cm4gc2FmZVJpbVJhZihkZWxQYXRoLCB0cnlOdW0gKyAxKTtcbiAgICAgIH1cbiAgICB9XG4gIH1cbn1cblxuYXN5bmMgZnVuY3Rpb24gaW5zdGFsbFNTTENlcnQgKHBlbVRleHQsIHVkaWQpIHtcbiAgLy8gQ2hlY2sgdGhhdCBvcGVuc3NsIGlzIGluc3RhbGxlZCBvbiB0aGUgcGF0aFxuICB0cnkge1xuICAgIGF3YWl0IGZzLndoaWNoKCdvcGVuc3NsJyk7XG4gIH0gY2F0Y2ggKGUpIHtcbiAgICBsb2cuZGVidWcoYGN1c3RvbVNTTENlcnQgcmVxdWlyZXMgb3BlbnNzbCB0byBiZSBhdmFpbGFibGUgb24gcGF0aGApO1xuICAgIGxvZy5lcnJvckFuZFRocm93KGBDb21tYW5kICdvcGVuc3NsJyBub3QgZm91bmRgKTtcbiAgfVxuXG4gIC8vIENoZWNrIHRoYXQgc3FsaXRlMyBpcyBpbnN0YWxsZWQgb24gdGhlIHBhdGhcbiAgdHJ5IHtcbiAgICBhd2FpdCBmcy53aGljaCgnc3FsaXRlMycpO1xuICB9IGNhdGNoIChlKSB7XG4gICAgbG9nLmRlYnVnKGBjdXN0b21TU0xDZXJ0IHJlcXVpcmVzIHNxbGl0ZTMgdG8gYmUgYXZhaWxhYmxlIG9uIHBhdGhgKTtcbiAgICBsb2cuZXJyb3JBbmRUaHJvdyhgQ29tbWFuZCAnc3FsaXRlMycgbm90IGZvdW5kYCk7XG4gIH1cblxuICBsZXQgdGVtcEZpbGVOYW1lID0gcGF0aC5yZXNvbHZlKGAke19fZGlybmFtZX0vdGVtcC1zc2wtY2VydC5wZW1gKTtcbiAgbGV0IHBhdGhUb0tleWNoYWluID0gcGF0aC5yZXNvbHZlKG5ldyBTaW11bGF0b3IodWRpZCkuZ2V0RGlyKCkpO1xuICBhd2FpdCBmcy53cml0ZUZpbGUodGVtcEZpbGVOYW1lLCBwZW1UZXh0KTtcbiAgdHJ5IHtcbiAgICBhd2FpdCBmcy5zdGF0KHBhdGhUb0tleWNoYWluKTtcbiAgfSBjYXRjaCAoZSkge1xuICAgIGxvZy5kZWJ1ZyhgQ291bGQgbm90IGluc3RhbGwgU1NMIGNlcnRpZmljYXRlLiBObyBzaW11bGF0b3Igd2l0aCB1ZGlkICcke3VkaWR9J2ApO1xuICAgIGxvZy5lcnJvckFuZFRocm93KGUpO1xuICB9XG4gIGxldCBjZXJ0aWZpY2F0ZSA9IG5ldyBDZXJ0aWZpY2F0ZSh0ZW1wRmlsZU5hbWUpO1xuICBsb2cuZGVidWcoYEluc3RhbGxpbmcgY2VydGlmaWNhdGUgdG8gJHtwYXRoVG9LZXljaGFpbn1gKTtcbiAgYXdhaXQgY2VydGlmaWNhdGUuYWRkKHBhdGhUb0tleWNoYWluKTtcbiAgYXdhaXQgZnMudW5saW5rKHRlbXBGaWxlTmFtZSk7XG4gIHJldHVybiBjZXJ0aWZpY2F0ZTtcbn1cblxuYXN5bmMgZnVuY3Rpb24gdW5pbnN0YWxsU1NMQ2VydCAocGVtVGV4dCwgdWRpZCkge1xuICB0cnkge1xuICAgIGxldCB0ZW1wRmlsZU5hbWUgPSBwYXRoLnJlc29sdmUoX19kaXJuYW1lLCAndGVtcC1zc2wtY2VydC5wZW0nKTtcbiAgICBsZXQgcGF0aFRvS2V5Y2hhaW4gPSBwYXRoLnJlc29sdmUobmV3IFNpbXVsYXRvcih1ZGlkKS5nZXREaXIoKSk7XG4gICAgYXdhaXQgZnMud3JpdGVGaWxlKHRlbXBGaWxlTmFtZSwgcGVtVGV4dCk7XG4gICAgbGV0IGNlcnRpZmljYXRlID0gbmV3IENlcnRpZmljYXRlKHRlbXBGaWxlTmFtZSk7XG4gICAgYXdhaXQgY2VydGlmaWNhdGUucmVtb3ZlKHBhdGhUb0tleWNoYWluKTtcbiAgICBhd2FpdCBmcy51bmxpbmsodGVtcEZpbGVOYW1lKTtcbiAgICByZXR1cm4gY2VydGlmaWNhdGU7XG4gIH0gY2F0Y2ggKGUpIHtcbiAgICBsb2cuZGVidWcoYENvdWxkIG5vdCB1bmluc3RhbGwgU1NMIGNlcnRpZmljYXRlLiBObyBzaW11bGF0b3Igd2l0aCB1ZGlkICcke3VkaWR9J2ApO1xuICAgIGxvZy5lcnJvckFuZFRocm93KGUpO1xuICB9XG59XG5cbi8qKlxuICogUnVucyBhIGNvbW1hbmQgbGluZSBzcWxpdGUzIHF1ZXJ5XG4gKi9cbmFzeW5jIGZ1bmN0aW9uIGV4ZWNTUUxpdGVRdWVyeSAoZGIsIHF1ZXJ5LCAuLi5xdWVyeVBhcmFtcykge1xuICBsZXQgcXVlcnlUb2tlbnMgPSBxdWVyeS5zcGxpdCgnPycpO1xuICBsZXQgZm9ybWF0dGVkUXVlcnkgPSBbXTtcbiAgcXVlcnlQYXJhbXMuZm9yRWFjaCgocGFyYW0sIGkpID0+IHtcbiAgICBmb3JtYXR0ZWRRdWVyeS5wdXNoKHF1ZXJ5VG9rZW5zW2ldKTtcbiAgICBmb3JtYXR0ZWRRdWVyeS5wdXNoKHBhcmFtLnJlcGxhY2UoLycvZywgXCInJ1wiKSk7XG4gIH0pO1xuICBmb3JtYXR0ZWRRdWVyeS5wdXNoKHF1ZXJ5VG9rZW5zW3F1ZXJ5VG9rZW5zLmxlbmd0aCAtIDFdKTtcblxuICByZXR1cm4gYXdhaXQgZXhlYygnc3FsaXRlMycsIFsnLWxpbmUnLCBkYiwgZm9ybWF0dGVkUXVlcnkuam9pbignJyldKTtcbn1cblxuZXhwb3J0IHsga2lsbEFsbFNpbXVsYXRvcnMsIGVuZEFsbFNpbXVsYXRvckRhZW1vbnMsIHNhZmVSaW1SYWYsIHNpbUV4aXN0cywgaW5zdGFsbFNTTENlcnQsIHVuaW5zdGFsbFNTTENlcnQsIGV4ZWNTUUxpdGVRdWVyeSB9O1xuIl0sInNvdXJjZVJvb3QiOiIuLi8uLiJ9