appium-instruments
Version:
IOS Instruments + instruments-without-delay launcher used by Appium
810 lines (654 loc) • 62 kB
JavaScript
// Wrapper around Apple's Instruments app
'use strict';
var _createClass = require('babel-runtime/helpers/create-class')['default'];
var _classCallCheck = require('babel-runtime/helpers/class-call-check')['default'];
var _slicedToArray = require('babel-runtime/helpers/sliced-to-array')['default'];
var _toConsumableArray = require('babel-runtime/helpers/to-consumable-array')['default'];
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 _teen_process = require('teen_process');
var _logger = require('./logger');
var _logger2 = _interopRequireDefault(_logger);
var _lodash = require('lodash');
var _lodash2 = _interopRequireDefault(_lodash);
var _through = require('through');
var _path = require('path');
var _path2 = _interopRequireDefault(_path);
var _appiumSupport = require('appium-support');
var _appiumXcode = require('appium-xcode');
var _appiumXcode2 = _interopRequireDefault(_appiumXcode);
var _bluebird = require('bluebird');
var _bluebird2 = _interopRequireDefault(_bluebird);
var _appiumIosSimulator = require('appium-ios-simulator');
var _utils = require('./utils');
var _streams = require('./streams');
require('colors');
var ERR_NEVER_CHECKED_IN = 'Instruments never checked in';
var ERR_CRASHED_ON_STARTUP = 'Instruments crashed on startup';
var ERR_AMBIGUOUS_DEVICE = 'Instruments Usage Error : Ambiguous device name/identifier';
var Instruments = (function () {
_createClass(Instruments, null, [{
key: 'quickInstruments',
// simple factory with sane defaults
value: function quickInstruments(opts) {
var xcodeTraceTemplatePath;
return _regeneratorRuntime.async(function quickInstruments$(context$2$0) {
while (1) switch (context$2$0.prev = context$2$0.next) {
case 0:
opts = _lodash2['default'].clone(opts);
context$2$0.next = 3;
return _regeneratorRuntime.awrap(_appiumXcode2['default'].getAutomationTraceTemplatePath());
case 3:
xcodeTraceTemplatePath = context$2$0.sent;
_lodash2['default'].defaults(opts, {
launchTimeout: 60000,
template: xcodeTraceTemplatePath,
withoutDelay: true,
xcodeVersion: '8.1',
webSocket: null,
flakeyRetries: 2
});
return context$2$0.abrupt('return', new Instruments(opts));
case 6:
case 'end':
return context$2$0.stop();
}
}, null, this);
}
/*
* opts:
* - app
* - termTimeout - defaults to 5000
* - flakeyRetries - defaults to 0
* - udid
* - bootstrap
* - template
* - withoutDelay
* - processArguments
* - simulatorSdkAndDevice
* - tmpDir - defaults to `/tmp/appium-instruments`
* - traceDir
* - launchTimeout - defaults to 90000
* - webSocket
* - instrumentsPath
* - realDevice - true/false, defaults to false
*/
}]);
function Instruments(opts) {
var _this = this;
_classCallCheck(this, Instruments);
opts = _lodash2['default'].cloneDeep(opts);
_lodash2['default'].defaults(opts, {
termTimeout: 5000,
tmpDir: '/tmp/appium-instruments',
launchTimeout: 90000,
flakeyRetries: 0,
realDevice: false
});
// config
var _arr = ['app', 'termTimeout', 'flakeyRetries', 'udid', 'bootstrap', 'template', 'withoutDelay', 'processArguments', 'realDevice', 'simulatorSdkAndDevice', 'tmpDir', 'traceDir', 'locale', 'language'];
for (var _i = 0; _i < _arr.length; _i++) {
var f = _arr[_i];
this[f] = opts[f];
}
this.traceDir = this.traceDir || this.tmpDir;
this.launchTimeout = (0, _utils.parseLaunchTimeout)(opts.launchTimeout);
// state
this.proc = null;
this.webSocket = opts.webSocket;
this.instrumentsPath = opts.instrumentsPath;
this.launchTries = 0;
this.socketConnectDelays = [];
this.gotFBSOpenApplicationError = false;
this.onShutdown = new _bluebird2['default'](function (resolve, reject) {
_this.onShutdownDeferred = { resolve: resolve, reject: reject };
});
// avoids UnhandledException
this.onShutdown['catch'](function () {}).done();
}
_createClass(Instruments, [{
key: 'configure',
value: function configure() {
return _regeneratorRuntime.async(function configure$(context$2$0) {
while (1) switch (context$2$0.prev = context$2$0.next) {
case 0:
if (this.xcodeVersion) {
context$2$0.next = 4;
break;
}
context$2$0.next = 3;
return _regeneratorRuntime.awrap(_appiumXcode2['default'].getVersion(true));
case 3:
this.xcodeVersion = context$2$0.sent;
case 4:
if (this.xcodeVersion.versionFloat === 6.0 && this.withoutDelay) {
_logger2['default'].info('In xcode 6.0, instruments-without-delay does not work. ' + 'If using Appium, you can disable instruments-without-delay ' + 'with the --native-instruments-lib server flag');
}
if (!(this.xcodeVersion.versionString === '5.0.1')) {
context$2$0.next = 7;
break;
}
throw new Error('Xcode 5.0.1 ships with a broken version of ' + 'Instruments. please upgrade to 5.0.2');
case 7:
if (this.template) {
context$2$0.next = 11;
break;
}
context$2$0.next = 10;
return _regeneratorRuntime.awrap(_appiumXcode2['default'].getAutomationTraceTemplatePath());
case 10:
this.template = context$2$0.sent;
case 11:
if (this.instrumentsPath) {
context$2$0.next = 15;
break;
}
context$2$0.next = 14;
return _regeneratorRuntime.awrap((0, _utils.getInstrumentsPath)());
case 14:
this.instrumentsPath = context$2$0.sent;
case 15:
case 'end':
return context$2$0.stop();
}
}, null, this);
}
}, {
key: 'launchOnce',
value: function launchOnce() {
var launchResultPromise, actOnStderr;
return _regeneratorRuntime.async(function launchOnce$(context$2$0) {
var _this2 = this;
while (1) switch (context$2$0.prev = context$2$0.next) {
case 0:
_logger2['default'].info('Launching instruments');
// prepare temp dir
context$2$0.next = 3;
return _regeneratorRuntime.awrap(_appiumSupport.fs.rimraf(this.tmpDir));
case 3:
context$2$0.next = 5;
return _regeneratorRuntime.awrap((0, _appiumSupport.mkdirp)(this.tmpDir));
case 5:
context$2$0.next = 7;
return _regeneratorRuntime.awrap((0, _appiumSupport.mkdirp)(this.traceDir));
case 7:
this.exitListener = null;
context$2$0.next = 10;
return _regeneratorRuntime.awrap(this.spawnInstruments());
case 10:
this.proc = context$2$0.sent;
this.proc.on('exit', function (code, signal) {
var msg = code !== null ? 'code: ' + code : 'signal: ' + signal;
_logger2['default'].debug('Instruments exited with ' + msg);
});
// set up the promise to handle launch
launchResultPromise = new _bluebird2['default'](function (resolve, reject) {
_this2.launchResultDeferred = { resolve: resolve, reject: reject };
});
// There was a special case for ignoreStartupExit
// but it is not needed anymore, you may just listen for exit.
this.setExitListener(function () {
_this2.proc = null;
_this2.launchResultDeferred.reject(new Error(ERR_CRASHED_ON_STARTUP));
});
this.proc.on('error', function (err) {
_logger2['default'].debug('Error with instruments proc: ' + err.message);
if (err.message.indexOf('ENOENT') !== -1) {
_this2.proc = null; // otherwise we'll try to send sigkill
_logger2['default'].error('Unable to spawn instruments: ' + err.message);
_this2.launchResultDeferred.reject(err);
}
});
this.proc.stdout.setEncoding('utf8');
this.proc.stdout.pipe((0, _streams.outputStream)()).pipe((0, _streams.dumpStream)());
this.proc.stderr.setEncoding('utf8');
actOnStderr = function actOnStderr(output) {
if (_this2.launchTimeout.afterSimLaunch && output && output.match(/CLTilesManagerClient: initialize/)) {
_this2.addSocketConnectTimer(_this2.launchTimeout.afterSimLaunch, 'afterLaunch', function callee$3$0() {
return _regeneratorRuntime.async(function callee$3$0$(context$4$0) {
while (1) switch (context$4$0.prev = context$4$0.next) {
case 0:
context$4$0.next = 2;
return _regeneratorRuntime.awrap(this.killInstruments());
case 2:
this.launchResultDeferred.reject(new Error(ERR_NEVER_CHECKED_IN));
case 3:
case 'end':
return context$4$0.stop();
}
}, null, _this2);
});
}
var fbsErrStr = '(FBSOpenApplicationErrorDomain error 8.)';
if (output.indexOf(fbsErrStr) !== -1) {
_this2.gotFBSOpenApplicationError = true;
}
if (output.indexOf(ERR_AMBIGUOUS_DEVICE) !== -1) {
var msg = ERR_AMBIGUOUS_DEVICE + ': \'' + _this2.simulatorSdkAndDevice + '\'';
_this2.launchResultDeferred.reject(new Error(msg));
}
};
this.proc.stderr.pipe((0, _through.through)(function (output) {
actOnStderr(output);
this.queue(output);
})).pipe((0, _streams.errorStream)()).pipe((0, _streams.webSocketAlertStream)(this.webSocket)).pipe((0, _streams.dumpStream)());
// start waiting for instruments to launch successfully
this.addSocketConnectTimer(this.launchTimeout.global, 'global', function callee$2$0() {
return _regeneratorRuntime.async(function callee$2$0$(context$3$0) {
while (1) switch (context$3$0.prev = context$3$0.next) {
case 0:
context$3$0.next = 2;
return _regeneratorRuntime.awrap(this.killInstruments());
case 2:
this.launchResultDeferred.reject(new Error(ERR_NEVER_CHECKED_IN));
case 3:
case 'end':
return context$3$0.stop();
}
}, null, _this2);
});
context$2$0.prev = 21;
context$2$0.next = 24;
return _regeneratorRuntime.awrap(launchResultPromise);
case 24:
context$2$0.prev = 24;
this.clearSocketConnectTimers();
return context$2$0.finish(24);
case 27:
this.setExitListener(function (code, signal) {
_this2.proc = null;
var msg = code !== null ? 'code: ' + code : 'signal: ' + signal;
_this2.onShutdownDeferred.reject(new Error('Abnormal exit with ' + msg));
});
case 28:
case 'end':
return context$2$0.stop();
}
}, null, this, [[21,, 24, 27]]);
}
}, {
key: 'launch',
value: function launch() {
var launchTries, errIsCatchable;
return _regeneratorRuntime.async(function launch$(context$2$0) {
while (1) switch (context$2$0.prev = context$2$0.next) {
case 0:
context$2$0.next = 2;
return _regeneratorRuntime.awrap(this.configure());
case 2:
launchTries = 0;
case 3:
// eslint-disable-line no-constant-condition
launchTries++;
_logger2['default'].debug('Attempting to launch instruments, this is try #' + launchTries);
context$2$0.prev = 5;
context$2$0.next = 8;
return _regeneratorRuntime.awrap(this.launchOnce());
case 8:
return context$2$0.abrupt('break', 34);
case 11:
context$2$0.prev = 11;
context$2$0.t0 = context$2$0['catch'](5);
_logger2['default'].error('Error launching instruments: ' + context$2$0.t0.message);
errIsCatchable = context$2$0.t0.message === ERR_NEVER_CHECKED_IN || context$2$0.t0.message === ERR_CRASHED_ON_STARTUP;
if (errIsCatchable) {
context$2$0.next = 17;
break;
}
throw context$2$0.t0;
case 17:
if (!(launchTries <= this.flakeyRetries)) {
context$2$0.next = 32;
break;
}
if (!this.gotFBSOpenApplicationError) {
context$2$0.next = 25;
break;
}
_logger2['default'].debug('Got the FBSOpenApplicationError, not killing the ' + 'sim but leaving it open so the app will launch');
this.gotFBSOpenApplicationError = false; // clear out for next launch
context$2$0.next = 23;
return _regeneratorRuntime.awrap(_bluebird2['default'].delay(1000));
case 23:
context$2$0.next = 30;
break;
case 25:
if (this.realDevice) {
context$2$0.next = 28;
break;
}
context$2$0.next = 28;
return _regeneratorRuntime.awrap((0, _appiumIosSimulator.killAllSimulators)());
case 28:
context$2$0.next = 30;
return _regeneratorRuntime.awrap(_bluebird2['default'].delay(5000));
case 30:
context$2$0.next = 33;
break;
case 32:
_logger2['default'].errorAndThrow('We exceeded the number of retries allowed for ' + 'instruments to successfully start; failing launch');
case 33:
if (true) {
context$2$0.next = 3;
break;
}
case 34:
case 'end':
return context$2$0.stop();
}
}, null, this, [[5, 11]]);
}
}, {
key: 'registerLaunch',
value: function registerLaunch() {
this.launchResultDeferred.resolve();
}
}, {
key: 'spawnInstruments',
value: function spawnInstruments() {
var traceDir, i, args, _iteratorNormalCompletion, _didIteratorError, _iteratorError, _iterator, _step, arg, space, flag, value, _iteratorNormalCompletion2, _didIteratorError2, _iteratorError2, _iterator2, _step2, _step2$value, env, iwdPath, instrumentsExecArgs;
return _regeneratorRuntime.async(function spawnInstruments$(context$2$0) {
while (1) switch (context$2$0.prev = context$2$0.next) {
case 0:
traceDir = undefined;
i = 0;
case 2:
// loop while there are tracedirs to delete
traceDir = _path2['default'].resolve(this.traceDir, 'instrumentscli' + i + '.trace');
context$2$0.next = 5;
return _regeneratorRuntime.awrap(_appiumSupport.fs.exists(traceDir));
case 5:
if (context$2$0.sent) {
context$2$0.next = 7;
break;
}
return context$2$0.abrupt('break', 10);
case 7:
i++;
context$2$0.next = 2;
break;
case 10:
args = ['-t', this.template, '-D', traceDir];
if (this.udid) {
// real device, so specify udid
args = args.concat(['-w', this.udid]);
_logger2['default'].debug('Attempting to run app on real device with UDID \'' + this.udid + '\'');
}
if (!this.udid && this.simulatorSdkAndDevice) {
// sim, so specify the sdk and device
args = args.concat(['-w', this.simulatorSdkAndDevice]);
_logger2['default'].debug('Attempting to run app on ' + this.simulatorSdkAndDevice);
}
args = args.concat([this.app]);
if (!this.processArguments) {
context$2$0.next = 63;
break;
}
_logger2['default'].debug('Attempting to run app with process arguments: ' + JSON.stringify(this.processArguments));
// any additional stuff specified by the user
if (!_lodash2['default'].isString(this.processArguments)) {
context$2$0.next = 44;
break;
}
if (!(this.processArguments.indexOf('-e ') === -1)) {
context$2$0.next = 22;
break;
}
_logger2['default'].debug('Plain string process arguments being pushed into arguments');
args.push(this.processArguments);
context$2$0.next = 42;
break;
case 22:
_logger2['default'].debug('Environment variables being pushed into arguments');
_iteratorNormalCompletion = true;
_didIteratorError = false;
_iteratorError = undefined;
context$2$0.prev = 26;
for (_iterator = _getIterator(this.processArguments.split('-e ')); !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
arg = _step.value;
arg = arg.trim();
if (arg.length) {
space = arg.indexOf(' ');
flag = arg.substring(0, space);
value = arg.substring(space + 1);
args.push('-e', flag, value);
}
}
context$2$0.next = 34;
break;
case 30:
context$2$0.prev = 30;
context$2$0.t0 = context$2$0['catch'](26);
_didIteratorError = true;
_iteratorError = context$2$0.t0;
case 34:
context$2$0.prev = 34;
context$2$0.prev = 35;
if (!_iteratorNormalCompletion && _iterator['return']) {
_iterator['return']();
}
case 37:
context$2$0.prev = 37;
if (!_didIteratorError) {
context$2$0.next = 40;
break;
}
throw _iteratorError;
case 40:
return context$2$0.finish(37);
case 41:
return context$2$0.finish(34);
case 42:
context$2$0.next = 63;
break;
case 44:
_iteratorNormalCompletion2 = true;
_didIteratorError2 = false;
_iteratorError2 = undefined;
context$2$0.prev = 47;
// process arguments can also be a hash of flags and values
for (_iterator2 = _getIterator(_lodash2['default'].pairs(this.processArguments)); !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
_step2$value = _slicedToArray(_step2.value, 2);
flag = _step2$value[0];
value = _step2$value[1];
args.push('-e', flag, value);
}
context$2$0.next = 55;
break;
case 51:
context$2$0.prev = 51;
context$2$0.t1 = context$2$0['catch'](47);
_didIteratorError2 = true;
_iteratorError2 = context$2$0.t1;
case 55:
context$2$0.prev = 55;
context$2$0.prev = 56;
if (!_iteratorNormalCompletion2 && _iterator2['return']) {
_iterator2['return']();
}
case 58:
context$2$0.prev = 58;
if (!_didIteratorError2) {
context$2$0.next = 61;
break;
}
throw _iteratorError2;
case 61:
return context$2$0.finish(58);
case 62:
return context$2$0.finish(55);
case 63:
args = args.concat(['-e', 'UIASCRIPT', this.bootstrap]);
args = args.concat(['-e', 'UIARESULTSPATH', this.tmpDir]);
if (this.language) {
args = args.concat(['-AppleLanguages (' + this.language + ')']);
args = args.concat(['-NSLanguages (' + this.language + ')']);
}
if (this.locale) {
args = args.concat(['-AppleLocale ' + this.locale]);
}
env = _lodash2['default'].clone(process.env);
if (this.xcodeVersion.major >= 7 && !this.udid) {
// iwd currently does not work with xcode7, setting withoutDelay to false
_logger2['default'].info("On xcode 7.0+, instruments-without-delay does not work, " + "skipping instruments-without-delay");
this.withoutDelay = false;
}
context$2$0.next = 71;
return _regeneratorRuntime.awrap((0, _utils.getIwdPath)(this.xcodeVersion.major));
case 71:
iwdPath = context$2$0.sent;
env.CA_DEBUG_TRANSACTIONS = 1;
if (this.withoutDelay && !this.udid) {
// sim, and using i-w-d
env.DYLD_INSERT_LIBRARIES = _path2['default'].resolve(iwdPath, 'InstrumentsShim.dylib');
env.LIB_PATH = iwdPath;
}
instrumentsExecArgs = [this.instrumentsPath].concat(_toConsumableArray(args));
instrumentsExecArgs = _lodash2['default'].map(instrumentsExecArgs, function (arg) {
if (arg === null) {
throw new Error('A null value was passed as an arg to execute ' + 'instruments on the command line. A letiable is ' + 'probably not getting set. Array of command args: ' + JSON.stringify(instrumentsExecArgs));
}
// escape any argument that has a space in it
if (_lodash2['default'].isString(arg) && arg.indexOf(' ') !== -1) {
return '"' + arg + '"';
}
// otherwise just use the argument
return arg;
});
_logger2['default'].debug('Spawning instruments with command: \'' + instrumentsExecArgs.join(' ') + '\'');
if (this.withoutDelay) {
_logger2['default'].debug('And extra without-delay env: ' + JSON.stringify({
DYLD_INSERT_LIBRARIES: env.DYLD_INSERT_LIBRARIES,
LIB_PATH: env.LIB_PATH
}));
}
_logger2['default'].debug('And launch timeouts (in ms): ' + JSON.stringify(this.launchTimeout));
context$2$0.next = 81;
return _regeneratorRuntime.awrap((0, _teen_process.spawn)(this.instrumentsPath, args, { env: env }));
case 81:
return context$2$0.abrupt('return', context$2$0.sent);
case 82:
case 'end':
return context$2$0.stop();
}
}, null, this, [[26, 30, 34, 42], [35,, 37, 41], [47, 51, 55, 63], [56,, 58, 62]]);
}
}, {
key: 'addSocketConnectTimer',
value: function addSocketConnectTimer(delay, type, doAction) {
var socketConnectDelay = (0, _appiumSupport.cancellableDelay)(delay);
socketConnectDelay.then(function () {
_logger2['default'].warn('Instruments socket client never checked in; timing out (' + type + ')');
return doAction();
})['catch'](_bluebird2['default'].CancellationError, function () {}).done();
this.socketConnectDelays.push(socketConnectDelay);
}
}, {
key: 'clearSocketConnectTimers',
value: function clearSocketConnectTimers() {
var _iteratorNormalCompletion3 = true;
var _didIteratorError3 = false;
var _iteratorError3 = undefined;
try {
for (var _iterator3 = _getIterator(this.socketConnectDelays), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) {
var delay = _step3.value;
delay.cancel();
}
} catch (err) {
_didIteratorError3 = true;
_iteratorError3 = err;
} finally {
try {
if (!_iteratorNormalCompletion3 && _iterator3['return']) {
_iterator3['return']();
}
} finally {
if (_didIteratorError3) {
throw _iteratorError3;
}
}
}
this.socketConnectDelays = [];
}
}, {
key: 'setExitListener',
value: function setExitListener(exitListener) {
if (!this.proc) return;
if (this.exitListener) {
this.proc.removeListener('exit', this.exitListener);
}
this.exitListener = exitListener;
this.proc.on('exit', exitListener);
}
}, {
key: 'killInstruments',
value: function killInstruments() {
var _this4 = this;
if (!this.proc) return;
_logger2['default'].debug('Kill Instruments process (pid: ' + this.proc.pid + ')');
return new _bluebird2['default'](function callee$2$0(resolve) {
var wasTerminated, termDelay, termPromise;
return _regeneratorRuntime.async(function callee$2$0$(context$3$0) {
var _this3 = this;
while (1) switch (context$3$0.prev = context$3$0.next) {
case 0:
wasTerminated = false;
termDelay = (0, _appiumSupport.cancellableDelay)(this.termTimeout);
termPromise = termDelay['catch'](_bluebird2['default'].CancellationError, function () {});
this.setExitListener(function () {
_this3.proc = null;
wasTerminated = true;
termDelay.cancel();
resolve();
});
_logger2['default'].debug('Sending SIGTERM');
this.proc.kill('SIGTERM');
context$3$0.next = 8;
return _regeneratorRuntime.awrap(termPromise);
case 8:
if (!wasTerminated) {
_logger2['default'].warn('Instruments did not terminate after ' + this.termTimeout / 1000 + ' seconds!');
_logger2['default'].debug('Sending SIGKILL');
this.proc.kill('SIGKILL');
if (_lodash2['default'].isFunction(this.exitListener)) {
this.exitListener();
}
}
case 9:
case 'end':
return context$3$0.stop();
}
}, null, _this4);
});
}
/* PROCESS MANAGEMENT */
}, {
key: 'shutdown',
value: function shutdown() {
return _regeneratorRuntime.async(function shutdown$(context$2$0) {
while (1) switch (context$2$0.prev = context$2$0.next) {
case 0:
_logger2['default'].debug('Starting shutdown.');
context$2$0.next = 3;
return _regeneratorRuntime.awrap(this.killInstruments());
case 3:
this.onShutdownDeferred.resolve();
case 4:
case 'end':
return context$2$0.stop();
}
}, null, this);
}
}]);
return Instruments;
})();
exports['default'] = Instruments;
module.exports = exports['default'];
// build up the arguments to use
// {"processArguments": {"flag1": "value1", "flag2": "value2"}}
// monitoring process termination
//# sourceMappingURL=data:application/json;charset=utf8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImxpYi9pbnN0cnVtZW50cy5qcyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OzRCQUVzQixjQUFjOztzQkFDcEIsVUFBVTs7OztzQkFDWixRQUFROzs7O3VCQUNFLFNBQVM7O29CQUNoQixNQUFNOzs7OzZCQUNzQixnQkFBZ0I7OzJCQUMzQyxjQUFjOzs7O3dCQUNsQixVQUFVOzs7O2tDQUNVLHNCQUFzQjs7cUJBQ1csU0FBUzs7dUJBQ0EsV0FBVzs7UUFDaEYsUUFBUTs7QUFHZixJQUFNLG9CQUFvQixHQUFHLDhCQUE4QixDQUFDO0FBQzVELElBQU0sc0JBQXNCLEdBQUcsZ0NBQWdDLENBQUM7QUFDaEUsSUFBTSxvQkFBb0IsR0FBRyw0REFBNEQsQ0FBQzs7SUFFcEYsV0FBVztlQUFYLFdBQVc7Ozs7V0FFZSwwQkFBQyxJQUFJO1VBRTdCLHNCQUFzQjs7OztBQUQxQixnQkFBSSxHQUFHLG9CQUFFLEtBQUssQ0FBQyxJQUFJLENBQUMsQ0FBQzs7NkNBQ2MseUJBQU0sOEJBQThCLEVBQUU7OztBQUFyRSxrQ0FBc0I7O0FBQzFCLGdDQUFFLFFBQVEsQ0FBQyxJQUFJLEVBQUU7QUFDZiwyQkFBYSxFQUFFLEtBQUs7QUFDcEIsc0JBQVEsRUFBRSxzQkFBc0I7QUFDaEMsMEJBQVksRUFBRSxJQUFJO0FBQ2xCLDBCQUFZLEVBQUUsS0FBSztBQUNuQix1QkFBUyxFQUFFLElBQUk7QUFDZiwyQkFBYSxFQUFFLENBQUM7YUFDakIsQ0FBQyxDQUFDO2dEQUNJLElBQUksV0FBVyxDQUFDLElBQUksQ0FBQzs7Ozs7OztLQUM3Qjs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQW9CVyxXQWxDUixXQUFXLENBa0NGLElBQUksRUFBRTs7OzBCQWxDZixXQUFXOztBQW1DYixRQUFJLEdBQUcsb0JBQUUsU0FBUyxDQUFDLElBQUksQ0FBQyxDQUFDO0FBQ3pCLHdCQUFFLFFBQVEsQ0FBQyxJQUFJLEVBQUU7QUFDZixpQkFBVyxFQUFFLElBQUk7QUFD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QUFHLElBQUksZUFBWSxDQUFDO0FBQ3BGLG9DQUFJLEtBQUssQ0FBQyxpQkFBaUIsQ0FBQyxDQUFDO0FBQzdCLG9CQUFJLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxTQUFTLENBQUMsQ0FBQztBQUMxQixvQkFBSSxvQkFBRSxVQUFVLENBQUMsSUFBSSxDQUFDLFlBQVksQ0FBQyxFQUFFO0FBQ25DLHNCQUFJLENBQUMsWUFBWSxFQUFFLENBQUM7aUJBQ3JCO2VBQ0Y7Ozs7Ozs7T0FDRixDQUFDLENBQUM7S0FDSjs7Ozs7V0FHYzs7OztBQUNiLGdDQUFJLEtBQUssQ0FBQyxvQkFBb0IsQ0FBQyxDQUFDOzs2Q0FDMUIsSUFBSSxDQUFDLGVBQWUsRUFBRTs7O0FBQzVCLGdCQUFJLENBQUMsa0JBQWtCLENBQUMsT0FBTyxFQUFFLENBQUM7Ozs7Ozs7S0FDbkM7OztTQW5YRyxXQUFXOzs7cUJBc1hGLFdBQVciLCJmaWxlIjoibGliL2luc3RydW1lbnRzLmpzIiwic291cmNlc0NvbnRlbnQiOlsiLy8gV3JhcHBlciBhcm91bmQgQXBwbGUncyBJbnN0cnVtZW50cyBhcHBcblxuaW1wb3J0IHsgc3Bhd24gfSBmcm9tICd0ZWVuX3Byb2Nlc3MnO1xuaW1wb3J0IGxvZyBmcm9tICcuL2xvZ2dlcic7XG5pbXBvcnQgXyBmcm9tICdsb2Rhc2gnO1xuaW1wb3J0IHsgdGhyb3VnaCB9IGZyb20gJ3Rocm91Z2gnO1xuaW1wb3J0IHBhdGggZnJvbSAncGF0aCc7XG5pbXBvcnQgeyBta2RpcnAsIGZzLCBjYW5jZWxsYWJsZURlbGF5IH0gZnJvbSAnYXBwaXVtLXN1cHBvcnQnO1xuaW1wb3J0IHhjb2RlIGZyb20gJ2FwcGl1bS14Y29kZSc7XG5pbXBvcnQgQiBmcm9tICdibHVlYmlyZCc7XG5pbXBvcnQgeyBraWxsQWxsU2ltdWxhdG9ycyB9IGZyb20gJ2FwcGl1bS1pb3Mtc2ltdWxhdG9yJztcbmltcG9ydCB7IGdldEluc3RydW1lbnRzUGF0aCwgcGFyc2VMYXVuY2hUaW1lb3V0LCBnZXRJd2RQYXRoIH0gZnJvbSAnLi91dGlscyc7XG5pbXBvcnQgeyBvdXRwdXRTdHJlYW0sIGVycm9yU3RyZWFtLCB3ZWJTb2NrZXRBbGVydFN0cmVhbSwgZHVtcFN0cmVhbSB9IGZyb20gJy4vc3RyZWFtcyc7XG5pbXBvcnQgJ2NvbG9ycyc7XG5cblxuY29uc3QgRVJSX05FVkVSX0NIRUNLRURfSU4gPSAnSW5zdHJ1bWVudHMgbmV2ZXIgY2hlY2tlZCBpbic7XG5jb25zdCBFUlJfQ1JBU0hFRF9PTl9TVEFSVFVQID0gJ0luc3RydW1lbnRzIGNyYXNoZWQgb24gc3RhcnR1cCc7XG5jb25zdCBFUlJfQU1CSUdVT1VTX0RFVklDRSA9ICdJbnN0cnVtZW50cyBVc2FnZSBFcnJvciA6IEFtYmlndW91cyBkZXZpY2UgbmFtZS9pZGVudGlmaWVyJztcblxuY2xhc3MgSW5zdHJ1bWVudHMge1xuICAvLyBzaW1wbGUgZmFjdG9yeSB3aXRoIHNhbmUgZGVmYXVsdHNcbiAgc3RhdGljIGFzeW5jIHF1aWNrSW5zdHJ1bWVudHMgKG9wdHMpIHtcbiAgICBvcHRzID0gXy5jbG9uZShvcHRzKTtcbiAgICBsZXQgeGNvZGVUcmFjZVRlbXBsYXRlUGF0aCA9IGF3YWl0IHhjb2RlLmdldEF1dG9tYXRpb25UcmFjZVRlbXBsYXRlUGF0aCgpO1xuICAgIF8uZGVmYXVsdHMob3B0cywge1xuICAgICAgbGF1bmNoVGltZW91dDogNjAwMDAsXG4gICAgICB0ZW1wbGF0ZTogeGNvZGVUcmFjZVRlbXBsYXRlUGF0aCxcbiAgICAgIHdpdGhvdXREZWxheTogdHJ1ZSxcbiAgICAgIHhjb2RlVmVyc2lvbjogJzguMScsXG4gICAgICB3ZWJTb2NrZXQ6IG51bGwsXG4gICAgICBmbGFrZXlSZXRyaWVzOiAyXG4gICAgfSk7XG4gICAgcmV0dXJuIG5ldyBJbnN0cnVtZW50cyhvcHRzKTtcbiAgfVxuXG4gIC8qXG4gICAqIG9wdHM6XG4gICAqICAgLSBhcHBcbiAgICogICAtIHRlcm1UaW1lb3V0IC0gZGVmYXVsdHMgdG8gNTAwMFxuICAgKiAgIC0gZmxha2V5UmV0cmllcyAtIGRlZmF1bHRzIHRvIDBcbiAgICogICAtIHVkaWRcbiAgICogICAtIGJvb3RzdHJhcFxuICAgKiAgIC0gdGVtcGxhdGVcbiAgICogICAtIHdpdGhvdXREZWxheVxuICAgKiAgIC0gcHJvY2Vzc0FyZ3VtZW50c1xuICAgKiAgIC0gc2ltdWxhdG9yU2RrQW5kRGV2aWNlXG4gICAqICAgLSB0bXBEaXIgLSBkZWZhdWx0cyB0byBgL3RtcC9hcHBpdW0taW5zdHJ1bWVudHNgXG4gICAqICAgLSB0cmFjZURpclxuICAgKiAgIC0gbGF1bmNoVGltZW91dCAtIGRlZmF1bHRzIHRvIDkwMDAwXG4gICAqICAgLSB3ZWJTb2NrZXRcbiAgICogICAtIGluc3RydW1lbnRzUGF0aFxuICAgKiAgIC0gcmVhbERldmljZSAtIHRydWUvZmFsc2UsIGRlZmF1bHRzIHRvIGZhbHNlXG4gICAqL1xuICBjb25zdHJ1Y3RvciAob3B0cykge1xuICAgIG9wdHMgPSBfLmNsb25lRGVlcChvcHRzKTtcbiAgICBfLmRlZmF1bHRzKG9wdHMsIHtcbiAgICAgIHRlcm1UaW1lb3V0OiA1MDAwLFxuICAgICAgdG1wRGlyOiAnL3RtcC9hcHBpdW0taW5zdHJ1bWVudHMnLFxuICAgICAgbGF1bmNoVGltZW91dDogOTAwMDAsXG4gICAgICBmbGFrZXlSZXRyaWVzOiAwLFxuICAgICAgcmVhbERldmljZTogZmFsc2VcbiAgICB9KTtcblxuICAgIC8vIGNvbmZpZ1xuICAgIGZvciAobGV0IGYgb2YgWydhcHAnLCAndGVybVRpbWVvdXQnLCAnZmxha2V5UmV0cmllcycsICd1ZGlkJywgJ2Jvb3RzdHJhcCcsXG4gICAgICAgICAgICAgICAgICAgJ3RlbXBsYXRlJywgJ3dpdGhvdXREZWxheScsICdwcm9jZXNzQXJndW1lbnRzJywgJ3JlYWxEZXZpY2UnLFxuICAgICAgICAgICAgICAgICAgICdzaW11bGF0b3JTZGtBbmREZXZpY2UnLCAndG1wRGlyJywgJ3RyYWNlRGlyJywgJ2xvY2FsZScsICdsYW5ndWFnZSddKSB7XG4gICAgICB0aGlzW2ZdID0gb3B0c1tmXTtcbiAgICB9XG4gICAgdGhpcy50cmFjZURpciA9IHRoaXMudHJhY2VEaXIgfHwgdGhpcy50bXBEaXI7XG4gICAgdGhpcy5sYXVuY2hUaW1lb3V0ID0gcGFyc2VMYXVuY2hUaW1lb3V0KG9wdHMubGF1bmNoVGltZW91dCk7XG5cbiAgICAvLyBzdGF0ZVxuICAgIHRoaXMucHJvYyA9IG51bGw7XG4gICAgdGhpcy53ZWJTb2NrZXQgPSBvcHRzLndlYlNvY2tldDtcbiAgICB0aGlzLmluc3RydW1lbnRzUGF0aCA9IG9wdHMuaW5zdHJ1bWVudHNQYXRoO1xuICAgIHRoaXMubGF1bmNoVHJpZXMgPSAwO1xuICAgIHRoaXMuc29ja2V0Q29ubmVjdERlbGF5cyA9IFtdO1xuICAgIHRoaXMuZ290RkJTT3BlbkFwcGxpY2F0aW9uRXJyb3IgPSBmYWxzZTtcbiAgICB0aGlzLm9uU2h1dGRvd24gPSBuZXcgQigocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XG4gICAgICB0aGlzLm9uU2h1dGRvd25EZWZlcnJlZCA9IHtyZXNvbHZlLCByZWplY3R9O1xuICAgIH0pO1xuICAgIC8vIGF2b2lkcyBVbmhhbmRsZWRFeGNlcHRpb25cbiAgICB0aGlzLm9uU2h1dGRvd24uY2F0Y2goKCkgPT4ge30pLmRvbmUoKTtcbiAgfVxuXG4gIGFzeW5jIGNvbmZpZ3VyZSAoKSB7XG4gICAgaWYgKCF0aGlzLnhjb2RlVmVyc2lvbikge1xuICAgICAgdGhpcy54Y29kZVZlcnNpb24gPSBhd2FpdCB4Y29kZS5nZXRWZXJzaW9uKHRydWUpO1xuICAgIH1cbiAgICBpZiAodGhpcy54Y29kZVZlcnNpb24udmVyc2lvbkZsb2F0ID09PSA2LjAgJiYgdGhpcy53aXRob3V0RGVsYXkpIHtcbiAgICAgIGxvZy5pbmZvKCdJbiB4Y29kZSA2LjAsIGluc3RydW1lbnRzLXdpdGhvdXQtZGVsYXkgZG9lcyBub3Qgd29yay4gJyArXG4gICAgICAgICAgICAgICAnSWYgdXNpbmcgQXBwaXVtLCB5b3UgY2FuIGRpc2FibGUgaW5zdHJ1bWVudHMtd2l0aG91dC1kZWxheSAnICtcbiAgICAgICAgICAgICAgICd3aXRoIHRoZSAtLW5hdGl2ZS1pbnN0cnVtZW50cy1saWIgc2VydmVyIGZsYWcnKTtcbiAgICB9XG4gICAgaWYgKHRoaXMueGNvZGVWZXJzaW9uLnZlcnNpb25TdHJpbmcgPT09ICc1LjAuMScpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcignWGNvZGUgNS4wLjEgc2hpcHMgd2l0aCBhIGJyb2tlbiB2ZXJzaW9uIG9mICcgK1xuICAgICAgICAgICAgICAgICAgICAgICdJbnN0cnVtZW50cy4gcGxlYXNlIHVwZ3JhZGUgdG8gNS4wLjInKTtcbiAgICB9XG5cbiAgICBpZiAoIXRoaXMudGVtcGxhdGUpIHtcbiAgICAgIHRoaXMudGVtcGxhdGUgPSBhd2FpdCB4Y29kZS5nZXRBdXRvbWF0aW9uVHJhY2VUZW1wbGF0ZVBhdGgoKTtcbiAgICB9XG5cbiAgICBpZiAoIXRoaXMuaW5zdHJ1bWVudHNQYXRoKSB7XG4gICAgICB0aGlzLmluc3RydW1lbnRzUGF0aCA9IGF3YWl0IGdldEluc3RydW1lbnRzUGF0aCgpO1xuICAgIH1cbiAgfVxuXG4gIGFzeW5jIGxhdW5jaE9uY2UgKCkge1xuICAgIGxvZy5pbmZvKCdMYXVuY2hpbmcgaW5zdHJ1bWVudHMnKTtcbiAgICAvLyBwcmVwYXJlIHRlbXAgZGlyXG4gICAgYXdhaXQgZnMucmltcmFmKHRoaXMudG1wRGlyKTtcbiAgICBhd2FpdCBta2RpcnAodGhpcy50bXBEaXIpO1xuICAgIGF3YWl0IG1rZGlycCh0aGlzLnRyYWNlRGlyKTtcblxuICAgIHRoaXMuZXhpdExpc3RlbmVyID0gbnVsbDtcbiAgICB0aGlzLnByb2MgPSBhd2FpdCB0aGlzLnNwYXduSW5zdHJ1bWVudHMoKTtcbiAgICB0aGlzLnByb2Mub24oJ2V4aXQnLCAoY29kZSwgc2lnbmFsKSA9PiB7XG4gICAgICBjb25zdCBtc2cgPSBjb2RlICE9PSBudWxsID8gYGNvZGU6ICR7Y29kZX1gIDogYHNpZ25hbDogJHtzaWduYWx9YDtcbiAgICAgIGxvZy5kZWJ1ZyhgSW5zdHJ1bWVudHMgZXhpdGVkIHdpdGggJHttc2d9YCk7XG4gICAgfSk7XG5cbiAgICAvLyBzZXQgdXAgdGhlIHByb21pc2UgdG8gaGFuZGxlIGxhdW5jaFxuICAgIGxldCBsYXVuY2hSZXN1bHRQcm9taXNlID0gbmV3IEIoKHJlc29sdmUsIHJlamVjdCkgPT4ge1xuICAgICAgdGhpcy5sYXVuY2hSZXN1bHREZWZlcnJlZCA9IHtyZXNvbHZlLCByZWplY3R9O1xuICAgIH0pO1xuXG4gICAgLy8gVGhlcmUgd2FzIGEgc3BlY2lhbCBjYXNlIGZvciBpZ25vcmVTdGFydHVwRXhpdFxuICAgIC8vIGJ1dCBpdCBpcyBub3QgbmVlZGVkIGFueW1vcmUsIHlvdSBtYXkganVzdCBsaXN0ZW4gZm9yIGV4aXQuXG4gICAgdGhpcy5zZXRFeGl0TGlzdGVuZXIoKCkgPT4ge1xuICAgICAgdGhpcy5wcm9jID0gbnVsbDtcbiAgICAgIHRoaXMubGF1bmNoUmVzdWx0RGVmZXJyZWQucmVqZWN0KG5ldyBFcnJvcihFUlJfQ1JBU0hFRF9PTl9TVEFSVFVQKSk7XG4gICAgfSk7XG5cbiAgICB0aGlzLnByb2Mub24oJ2Vycm9yJywgKGVycikgPT4ge1xuICAgICAgbG9nLmRlYnVnKGBFcnJvciB3aXRoIGluc3RydW1lbnRzIHByb2M6ICR7ZXJyLm1lc3NhZ2V9YCk7XG4gICAgICBpZiAoZXJyLm1lc3NhZ2UuaW5kZXhPZignRU5PRU5UJykgIT09IC0xKSB7XG4gICAgICAgIHRoaXMucHJvYyA9IG51bGw7IC8vIG90aGVyd2lzZSB3ZSdsbCB0cnkgdG8gc2VuZCBzaWdraWxsXG4gICAgICAgIGxvZy5lcnJvcihgVW5hYmxlIHRvIHNwYXduIGluc3RydW1lbnRzOiAke2Vyci5tZXNzYWdlfWApO1xuICAgICAgICB0aGlzLmxhdW5jaFJlc3VsdERlZmVycmVkLnJlamVjdChlcnIpO1xuICAgICAgfVxuICAgIH0pO1xuXG4gICAgdGhpcy5wcm9jLnN0ZG91dC5zZXRFbmNvZGluZygndXRmOCcpO1xuICAgIHRoaXMucHJvYy5zdGRvdXQucGlwZShvdXRwdXRTdHJlYW0oKSkucGlwZShkdW1wU3RyZWFtKCkpO1xuXG4gICAgdGhpcy5wcm9jLnN0ZGVyci5zZXRFbmNvZGluZygndXRmOCcpO1xuICAgIGxldCBhY3RPblN0ZGVyciA9IChvdXRwdXQpID0+IHtcbiAgICAgIGlmICh0aGlzLmxhdW5jaFRpbWVvdXQuYWZ0ZXJTaW1MYXVuY2ggJiYgb3V0cHV0ICYmIG91dHB1dC5tYXRjaCgvQ0xUaWxlc01hbmFnZXJDbGllbnQ6IGluaXRpYWxpemUvKSkge1xuICAgICAgICB0aGlzLmFkZFNvY2tldE