UNPKG

gst-atom-xcuitest-driver

Version:

ATOM driver for iOS using XCUITest for backend

328 lines (260 loc) 42.1 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports.default = exports.commands = void 0; require("source-map-support/register"); var _lodash = _interopRequireDefault(require("lodash")); var _appiumSupport = require("appium-support"); var _teen_process = require("teen_process"); var _logger = _interopRequireDefault(require("../logger")); var _utils = require("../utils"); var _deviceConnectionsFactory = _interopRequireDefault(require("../device-connections-factory")); var _appiumWebdriveragent = require("appium-webdriveragent"); var _asyncbox = require("asyncbox"); var _url = _interopRequireDefault(require("url")); let commands = {}; exports.commands = commands; const MAX_RECORDING_TIME_SEC = 60 * 30; const DEFAULT_RECORDING_TIME_SEC = 60 * 3; const DEFAULT_MJPEG_SERVER_PORT = 9100; const DEFAULT_FPS = 10; const DEFAULT_QUALITY = 'medium'; const DEFAULT_VCODEC = 'mjpeg'; const MP4_EXT = '.mp4'; const FFMPEG_BINARY = 'ffmpeg'; const ffmpegLogger = _appiumSupport.logger.getLogger(FFMPEG_BINARY); const QUALITY_MAPPING = { low: 10, medium: 25, high: 75, photo: 100 }; class ScreenRecorder { constructor(udid, videoPath, opts = {}) { this.videoPath = videoPath; this.opts = opts; this.udid = udid; this.mainProcess = null; this.timeoutHandler = null; } async start(timeoutMs) { try { await _appiumSupport.fs.which(FFMPEG_BINARY); } catch (err) { throw new Error(`'${FFMPEG_BINARY}' binary is not found in PATH. Install it using 'brew install ffmpeg'. ` + `Check https://www.ffmpeg.org/download.html for more details.`); } const { remotePort, remoteUrl, usePortForwarding, videoFps, videoType, videoScale, videoFilters, pixelFormat } = this.opts; try { var options = { udid: this.udid, port: remotePort, devicePort: remotePort, usePortForwarding, usbmuxdRemoteHost: this.opts.usbmuxdRemoteHost, usbmuxdRemotePort: this.opts.usbmuxdRemotePort }; await _deviceConnectionsFactory.default.requestConnection(options); } catch (err) { _logger.default.warn(`Cannot forward the local port ${remotePort} to ${remotePort} ` + `on the device ${this.udid}. Set the custom value to 'mjpegServerPort' ` + `capability if this is an undesired behavior.`); } const args = ['-f', 'mjpeg']; if (videoFps && videoType === 'libx264') { args.push('-r', videoFps); } const { protocol, hostname } = _url.default.parse(remoteUrl); args.push('-i', `${protocol}//${hostname}:${remotePort}`); if (videoFilters || videoScale) { args.push('-vf', videoFilters || `scale=${videoScale}`); } if (pixelFormat) { args.push('-pix_fmt', pixelFormat); } args.push('-vcodec', videoType, '-y', this.videoPath); this.mainProcess = new _teen_process.SubProcess(FFMPEG_BINARY, args); let isCaptureStarted = false; this.mainProcess.on('output', (stdout, stderr) => { if (stderr) { if (stderr.trim().startsWith('frame=')) { if (!isCaptureStarted) { isCaptureStarted = true; } } else { ffmpegLogger.info(`${stderr}`); } } }); await this.mainProcess.start(0); const startupTimeout = 5000; try { await (0, _asyncbox.waitForCondition)(() => isCaptureStarted, { waitMs: startupTimeout, intervalMs: 300 }); } catch (e) { _logger.default.warn(`Screen capture process did not start within ${startupTimeout}ms. Continuing anyway`); } if (!this.mainProcess.isRunning) { throw new Error(`The screen capture process '${FFMPEG_BINARY}' died unexpectedly. ` + `Check server logs for more details`); } _logger.default.info(`Starting screen capture on the device '${this.udid}' with command: '${FFMPEG_BINARY} ${args.join(' ')}'. ` + `Will timeout in ${timeoutMs}ms`); this.timeoutHandler = setTimeout(async () => { if (!(await this.interrupt())) { _logger.default.warn(`Cannot finish the active screen recording on the device '${this.udid}' after ${timeoutMs}ms timeout`); } }, timeoutMs); } async interrupt(force = false) { let result = true; if (this.timeoutHandler) { clearTimeout(this.timeoutHandler); this.timeoutHandler = null; } if (this.mainProcess && this.mainProcess.isRunning) { const interruptPromise = this.mainProcess.stop(force ? 'SIGTERM' : 'SIGINT'); this.mainProcess = null; try { await interruptPromise; } catch (e) { _logger.default.warn(`Cannot ${force ? 'terminate' : 'interrupt'} ${FFMPEG_BINARY}. ` + `Original error: ${e.message}`); result = false; } } _deviceConnectionsFactory.default.releaseConnection(this.udid, this.opts.remotePort); return result; } async finish() { await this.interrupt(); return this.videoPath; } async cleanup() { if (await _appiumSupport.fs.exists(this.videoPath)) { await _appiumSupport.fs.rimraf(this.videoPath); } } } commands.startRecordingScreen = async function startRecordingScreen(options = {}) { const { videoType = DEFAULT_VCODEC, timeLimit = DEFAULT_RECORDING_TIME_SEC, videoQuality = DEFAULT_QUALITY, videoFps = DEFAULT_FPS, videoFilters, videoScale, forceRestart, pixelFormat } = options; let result = ''; if (!forceRestart) { _logger.default.info(`Checking if there is/was a previous screen recording. ` + `Set 'forceRestart' option to 'true' if you'd like to skip this step.`); result = await this.stopRecordingScreen(options); } const videoPath = await _appiumSupport.tempDir.path({ prefix: `appium_${Math.random().toString(16).substring(2, 8)}`, suffix: MP4_EXT }); const wdaBaseUrl = this.opts.wdaBaseUrl || _appiumWebdriveragent.WDA_BASE_URL; const screenRecorder = new ScreenRecorder(this.opts.device.udid, videoPath, { remotePort: this.opts.mjpegServerPort || DEFAULT_MJPEG_SERVER_PORT, remoteUrl: wdaBaseUrl, usePortForwarding: this.isRealDevice() && (0, _utils.isLocalHost)(wdaBaseUrl), videoType, videoFilters, videoScale, videoFps, pixelFormat }); if (!(await screenRecorder.interrupt(true))) { _logger.default.errorAndThrow('Unable to stop screen recording process'); } if (this._recentScreenRecorder) { await this._recentScreenRecorder.cleanup(); this._recentScreenRecorder = null; } const timeoutSeconds = parseFloat(timeLimit); if (isNaN(timeoutSeconds) || timeoutSeconds > MAX_RECORDING_TIME_SEC || timeoutSeconds <= 0) { _logger.default.errorAndThrow(`The timeLimit value must be in range [1, ${MAX_RECORDING_TIME_SEC}] seconds. ` + `The value of '${timeLimit}' has been passed instead.`); } let { mjpegServerScreenshotQuality, mjpegServerFramerate } = await this.proxyCommand('/appium/settings', 'GET'); if (videoQuality) { const quality = _lodash.default.isInteger(videoQuality) ? videoQuality : QUALITY_MAPPING[_lodash.default.toLower(videoQuality)]; if (!quality) { throw new Error(`videoQuality value should be one of ${JSON.stringify(_lodash.default.keys(QUALITY_MAPPING))} or a number in range 1..100. ` + `'${videoQuality}' is given instead`); } mjpegServerScreenshotQuality = mjpegServerScreenshotQuality !== quality ? quality : undefined; } else { mjpegServerScreenshotQuality = undefined; } if (videoFps) { const fps = parseInt(videoFps, 10); if (isNaN(fps)) { throw new Error(`videoFps value should be a valid number in range 1..60. ` + `'${videoFps}' is given instead`); } mjpegServerFramerate = mjpegServerFramerate !== fps ? fps : undefined; } else { mjpegServerFramerate = undefined; } if (_appiumSupport.util.hasValue(mjpegServerScreenshotQuality) || _appiumSupport.util.hasValue(mjpegServerFramerate)) { await this.proxyCommand('/appium/settings', 'POST', { settings: { mjpegServerScreenshotQuality, mjpegServerFramerate } }); } try { await screenRecorder.start(timeoutSeconds * 1000); } catch (e) { await screenRecorder.interrupt(true); await screenRecorder.cleanup(); throw e; } this._recentScreenRecorder = screenRecorder; return result; }; commands.stopRecordingScreen = async function stopRecordingScreen(options = {}) { const { remotePath, user, pass, method } = options; if (!this._recentScreenRecorder) { _logger.default.info('Screen recording is not running. There is nothing to stop.'); return ''; } try { const videoPath = await this._recentScreenRecorder.finish(); if (!(await _appiumSupport.fs.exists(videoPath))) { _logger.default.errorAndThrow(`The screen recorder utility has failed ` + `to store the actual screen recording at '${videoPath}'`); } return await (0, _utils.encodeBase64OrUpload)(videoPath, remotePath, { user, pass, method }); } finally { await this._recentScreenRecorder.interrupt(true); await this._recentScreenRecorder.cleanup(); this._recentScreenRecorder = null; } }; var _default = commands; exports.default = _default;require('source-map-support').install(); //# sourceMappingURL=data:application/json;charset=utf8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImxpYi9jb21tYW5kcy9yZWNvcmRzY3JlZW4uanMiXSwibmFtZXMiOlsiY29tbWFuZHMiLCJNQVhfUkVDT1JESU5HX1RJTUVfU0VDIiwiREVGQVVMVF9SRUNPUkRJTkdfVElNRV9TRUMiLCJERUZBVUxUX01KUEVHX1NFUlZFUl9QT1JUIiwiREVGQVVMVF9GUFMiLCJERUZBVUxUX1FVQUxJVFkiLCJERUZBVUxUX1ZDT0RFQyIsIk1QNF9FWFQiLCJGRk1QRUdfQklOQVJZIiwiZmZtcGVnTG9nZ2VyIiwibG9nZ2VyIiwiZ2V0TG9nZ2VyIiwiUVVBTElUWV9NQVBQSU5HIiwibG93IiwibWVkaXVtIiwiaGlnaCIsInBob3RvIiwiU2NyZWVuUmVjb3JkZXIiLCJjb25zdHJ1Y3RvciIsInVkaWQiLCJ2aWRlb1BhdGgiLCJvcHRzIiwibWFpblByb2Nlc3MiLCJ0aW1lb3V0SGFuZGxlciIsInN0YXJ0IiwidGltZW91dE1zIiwiZnMiLCJ3aGljaCIsImVyciIsIkVycm9yIiwicmVtb3RlUG9ydCIsInJlbW90ZVVybCIsInVzZVBvcnRGb3J3YXJkaW5nIiwidmlkZW9GcHMiLCJ2aWRlb1R5cGUiLCJ2aWRlb1NjYWxlIiwidmlkZW9GaWx0ZXJzIiwicGl4ZWxGb3JtYXQiLCJvcHRpb25zIiwicG9ydCIsImRldmljZVBvcnQiLCJ1c2JtdXhkUmVtb3RlSG9zdCIsInVzYm11eGRSZW1vdGVQb3J0IiwiREVWSUNFX0NPTk5FQ1RJT05TX0ZBQ1RPUlkiLCJyZXF1ZXN0Q29ubmVjdGlvbiIsImxvZyIsIndhcm4iLCJhcmdzIiwicHVzaCIsInByb3RvY29sIiwiaG9zdG5hbWUiLCJ1cmwiLCJwYXJzZSIsIlN1YlByb2Nlc3MiLCJpc0NhcHR1cmVTdGFydGVkIiwib24iLCJzdGRvdXQiLCJzdGRlcnIiLCJ0cmltIiwic3RhcnRzV2l0aCIsImluZm8iLCJzdGFydHVwVGltZW91dCIsIndhaXRNcyIsImludGVydmFsTXMiLCJlIiwiaXNSdW5uaW5nIiwiam9pbiIsInNldFRpbWVvdXQiLCJpbnRlcnJ1cHQiLCJmb3JjZSIsInJlc3VsdCIsImNsZWFyVGltZW91dCIsImludGVycnVwdFByb21pc2UiLCJzdG9wIiwibWVzc2FnZSIsInJlbGVhc2VDb25uZWN0aW9uIiwiZmluaXNoIiwiY2xlYW51cCIsImV4aXN0cyIsInJpbXJhZiIsInN0YXJ0UmVjb3JkaW5nU2NyZWVuIiwidGltZUxpbWl0IiwidmlkZW9RdWFsaXR5IiwiZm9yY2VSZXN0YXJ0Iiwic3RvcFJlY29yZGluZ1NjcmVlbiIsInRlbXBEaXIiLCJwYXRoIiwicHJlZml4IiwiTWF0aCIsInJhbmRvbSIsInRvU3RyaW5nIiwic3Vic3RyaW5nIiwic3VmZml4Iiwid2RhQmFzZVVybCIsIldEQV9CQVNFX1VSTCIsInNjcmVlblJlY29yZGVyIiwiZGV2aWNlIiwibWpwZWdTZXJ2ZXJQb3J0IiwiaXNSZWFsRGV2aWNlIiwiZXJyb3JBbmRUaHJvdyIsIl9yZWNlbnRTY3JlZW5SZWNvcmRlciIsInRpbWVvdXRTZWNvbmRzIiwicGFyc2VGbG9hdCIsImlzTmFOIiwibWpwZWdTZXJ2ZXJTY3JlZW5zaG90UXVhbGl0eSIsIm1qcGVnU2VydmVyRnJhbWVyYXRlIiwicHJveHlDb21tYW5kIiwicXVhbGl0eSIsIl8iLCJpc0ludGVnZXIiLCJ0b0xvd2VyIiwiSlNPTiIsInN0cmluZ2lmeSIsImtleXMiLCJ1bmRlZmluZWQiLCJmcHMiLCJwYXJzZUludCIsInV0aWwiLCJoYXNWYWx1ZSIsInNldHRpbmdzIiwicmVtb3RlUGF0aCIsInVzZXIiLCJwYXNzIiwibWV0aG9kIl0sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7OztBQUFBOztBQUNBOztBQUNBOztBQUNBOztBQUNBOztBQUNBOztBQUNBOztBQUNBOztBQUNBOztBQUVBLElBQUlBLFFBQVEsR0FBRyxFQUFmOztBQUVBLE1BQU1DLHNCQUFzQixHQUFHLEtBQUssRUFBcEM7QUFDQSxNQUFNQywwQkFBMEIsR0FBRyxLQUFLLENBQXhDO0FBQ0EsTUFBTUMseUJBQXlCLEdBQUcsSUFBbEM7QUFDQSxNQUFN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QXJCLEVBQWdDb0csVUFBaEMsRUFBNEM7QUFDdkRDLE1BQUFBLElBRHVEO0FBRXZEQyxNQUFBQSxJQUZ1RDtBQUd2REMsTUFBQUE7QUFIdUQsS0FBNUMsQ0FBYjtBQUtELEdBWEQsU0FXVTtBQUNSLFVBQU0sS0FBS3ZCLHFCQUFMLENBQTJCaEMsU0FBM0IsQ0FBcUMsSUFBckMsQ0FBTjtBQUNBLFVBQU0sS0FBS2dDLHFCQUFMLENBQTJCdkIsT0FBM0IsRUFBTjtBQUNBLFNBQUt1QixxQkFBTCxHQUE2QixJQUE3QjtBQUNEO0FBQ0YsQ0E3QkQ7O2VBaUNlcEcsUSIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCBfIGZyb20gJ2xvZGFzaCc7XG5pbXBvcnQgeyBmcywgdGVtcERpciwgbG9nZ2VyLCB1dGlsIH0gZnJvbSAnYXBwaXVtLXN1cHBvcnQnO1xuaW1wb3J0IHsgU3ViUHJvY2VzcyB9IGZyb20gJ3RlZW5fcHJvY2Vzcyc7XG5pbXBvcnQgbG9nIGZyb20gJy4uL2xvZ2dlcic7XG5pbXBvcnQgeyBlbmNvZGVCYXNlNjRPclVwbG9hZCwgaXNMb2NhbEhvc3QgfSBmcm9tICcuLi91dGlscyc7XG5pbXBvcnQgREVWSUNFX0NPTk5FQ1RJT05TX0ZBQ1RPUlkgZnJvbSAnLi4vZGV2aWNlLWNvbm5lY3Rpb25zLWZhY3RvcnknO1xuaW1wb3J0IHsgV0RBX0JBU0VfVVJMIH0gZnJvbSAnYXBwaXVtLXdlYmRyaXZlcmFnZW50JztcbmltcG9ydCB7IHdhaXRGb3JDb25kaXRpb24gfSBmcm9tICdhc3luY2JveCc7XG5pbXBvcnQgdXJsIGZyb20gJ3VybCc7XG5cbmxldCBjb21tYW5kcyA9IHt9O1xuXG5jb25zdCBNQVhfUkVDT1JESU5HX1RJTUVfU0VDID0gNjAgKiAzMDtcbmNvbnN0IERFRkFVTFRfUkVDT1JESU5HX1RJTUVfU0VDID0gNjAgKiAzO1xuY29uc3QgREVGQVVMVF9NSlBFR19TRVJWRVJfUE9SVCA9IDkxMDA7XG5jb25zdCBERUZBVUxUX0ZQUyA9IDEwO1xuY29uc3QgREVGQVVMVF9RVUFMSVRZID0gJ21lZGl1bSc7XG5jb25zdCBERUZBVUxUX1ZDT0RFQyA9ICdtanBlZyc7XG5jb25zdCBNUDRfRVhUID0gJy5tcDQnO1xuY29uc3QgRkZNUEVHX0JJTkFSWSA9ICdmZm1wZWcnO1xuY29uc3QgZmZtcGVnTG9nZ2VyID0gbG9nZ2VyLmdldExvZ2dlcihGRk1QRUdfQklOQVJZKTtcbmNvbnN0IFFVQUxJVFlfTUFQUElORyA9IHtcbiAgbG93OiAxMCxcbiAgbWVkaXVtOiAyNSxcbiAgaGlnaDogNzUsXG4gIHBob3RvOiAxMDAsXG59O1xuXG5cbmNsYXNzIFNjcmVlblJlY29yZGVyIHtcbiAgY29uc3RydWN0b3IgKHVkaWQsIHZpZGVvUGF0aCwgb3B0cyA9IHt9KSB7XG4gICAgdGhpcy52aWRlb1BhdGggPSB2aWRlb1BhdGg7XG4gICAgdGhpcy5vcHRzID0gb3B0cztcbiAgICB0aGlzLnVkaWQgPSB1ZGlkO1xuICAgIHRoaXMubWFpblByb2Nlc3MgPSBudWxsO1xuICAgIHRoaXMudGltZW91dEhhbmRsZXIgPSBudWxsO1xuICB9XG5cbiAgYXN5bmMgc3RhcnQgKHRpbWVvdXRNcykge1xuICAgIHRyeSB7XG4gICAgICBhd2FpdCBmcy53aGljaChGRk1QRUdfQklOQVJZKTtcbiAgICB9IGNhdGNoIChlcnIpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihgJyR7RkZNUEVHX0JJTkFSWX0nIGJpbmFyeSBpcyBub3QgZm91bmQgaW4gUEFUSC4gSW5zdGFsbCBpdCB1c2luZyAnYnJldyBpbnN0YWxsIGZmbXBlZycuIGAgK1xuICAgICAgICBgQ2hlY2sgaHR0cHM6Ly93d3cuZmZtcGVnLm9yZy9kb3dubG9hZC5odG1sIGZvciBtb3JlIGRldGFpbHMuYCk7XG4gICAgfVxuXG4gICAgY29uc3Qge1xuICAgICAgcmVtb3RlUG9ydCxcbiAgICAgIHJlbW90ZVVybCxcbiAgICAgIHVzZVBvcnRGb3J3YXJkaW5nLFxuICAgICAgdmlkZW9GcHMsXG4gICAgICB2aWRlb1R5cGUsXG4gICAgICB2aWRlb1NjYWxlLFxuICAgICAgdmlkZW9GaWx0ZXJzLFxuICAgICAgcGl4ZWxGb3JtYXQsXG4gICAgfSA9IHRoaXMub3B0cztcblxuICAgIHRyeSB7XG4gICAgICB2YXIgb3B0aW9ucyA9IHtcbiAgICAgICAgdWRpZDogdGhpcy51ZGlkLFxuICAgICAgICBwb3J0OiByZW1vdGVQb3J0LFxuICAgICAgICBkZXZpY2VQb3J0OiByZW1vdGVQb3J0LFxuICAgICAgICB1c2VQb3J0Rm9yd2FyZGluZyxcbiAgICAgICAgdXNibXV4ZFJlbW90ZUhvc3Q6IHRoaXMub3B0cy51c2JtdXhkUmVtb3RlSG9zdCxcbiAgICAgICAgdXNibXV4ZFJlbW90ZVBvcnQ6IHRoaXMub3B0cy51c2JtdXhkUmVtb3RlUG9ydFxuICAgICAgfSAgXG4gICAgICBhd2FpdCBERVZJQ0VfQ09OTkVDVElPTlNfRkFDVE9SWS5yZXF1ZXN0Q29ubmVjdGlvbihvcHRpb25zKTtcbiAgICB9IGNhdGNoIChlcnIpIHtcbiAgICAgIGxvZy53YXJuKGBDYW5ub3QgZm9yd2FyZCB0aGUgbG9jYWwgcG9ydCAke3JlbW90ZVBvcnR9IHRvICR7cmVtb3RlUG9ydH0gYCArXG4gICAgICAgIGBvbiB0aGUgZGV2aWNlICR7dGhpcy51ZGlkfS4gU2V0IHRoZSBjdXN0b20gdmFsdWUgdG8gJ21qcGVnU2VydmVyUG9ydCcgYCArXG4gICAgICAgIGBjYXBhYmlsaXR5IGlmIHRoaXMgaXMgYW4gdW5kZXNpcmVkIGJlaGF2aW9yLmApO1xuICAgIH1cblxuICAgIGNvbnN0IGFyZ3MgPSBbXG4gICAgICAnLWYnLCAnbWpwZWcnLFxuICAgIF07XG4gICAgLy9QYXJhbWV0ZXIgYC1yYCBpcyBvcHRpb25hbC4gU2VlIGRldGFpbHM6IGh0dHBzOi8vZ2l0aHViLmNvbS9hcHBpdW0vYXBwaXVtL2lzc3Vlcy8xMjA2N1xuICAgIGlmICh2aWRlb0ZwcyAmJiB2aWRlb1R5cGUgPT09ICdsaWJ4MjY0Jykge1xuICAgICAgYXJncy5wdXNoKCctcicsIHZpZGVvRnBzKTtcbiAgICB9XG4gICAgY29uc3Qge3Byb3RvY29sLCBob3N0bmFtZX0gPSB1cmwucGFyc2UocmVtb3RlVXJsKTtcbiAgICBhcmdzLnB1c2goJy1pJywgYCR7cHJvdG9jb2x9Ly8ke2hvc3RuYW1lfToke3JlbW90ZVBvcnR9YCk7XG4gICAgaWYgKHZpZGVvRmlsdGVycyB8fCB2aWRlb1NjYWxlKSB7XG4gICAgICBhcmdzLnB1c2goJy12ZicsIHZpZGVvRmlsdGVycyB8fCBgc2NhbGU9JHt2aWRlb1NjYWxlfWApO1xuICAgIH1cbiAgICAvLyBRdWlja3RpbWUgY29tcGF0aWJpbGl0eSB2aWEgcGl4ZWxGb3JtYXQ6ICd5dXY0MjBwJ1xuICAgIGlmIChwaXhlbEZvcm1hdCkge1xuICAgICAgYXJncy5wdXNoKCctcGl4X2ZtdCcsIHBpeGVsRm9ybWF0KTtcbiAgICB9XG4gICAgYXJncy5wdXNoKFxuICAgICAgJy12Y29kZWMnLCB2aWRlb1R5cGUsXG4gICAgICAnLXknLCB0aGlzLnZpZGVvUGF0aFxuICAgICk7XG5cbiAgICB0aGlzLm1haW5Qcm9jZXNzID0gbmV3IFN1YlByb2Nlc3MoRkZNUEVHX0JJTkFSWSwgYXJncyk7XG4gICAgbGV0IGlzQ2FwdHVyZVN0YXJ0ZWQgPSBmYWxzZTtcbiAgICB0aGlzLm1haW5Qcm9jZXNzLm9uKCdvdXRwdXQnLCAoc3Rkb3V0LCBzdGRlcnIpID0+IHtcbiAgICAgIGlmIChzdGRlcnIpIHtcbiAgICAgICAgaWYgKHN0ZGVyci50cmltKCkuc3RhcnRzV2l0aCgnZnJhbWU9JykpIHtcbiAgICAgICAgICBpZiAoIWlzQ2FwdHVyZVN0YXJ0ZWQpIHtcbiAgICAgICAgICAgIGlzQ2FwdHVyZVN0YXJ0ZWQgPSB0cnVlO1xuICAgICAgICAgIH1cbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBmZm1wZWdMb2dnZXIuaW5mbyhgJHtzdGRlcnJ9YCk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9KTtcbiAgICBhd2FpdCB0aGlzLm1haW5Qcm9jZXNzLnN0YXJ0KDApO1xuICAgIGNvbnN0IHN0YXJ0dXBUaW1lb3V0ID0gNTAwMDtcbiAgICB0cnkge1xuICAgICAgYXdhaXQgd2FpdEZvckNvbmRpdGlvbigoKSA9PiBpc0NhcHR1cmVTdGFydGVkLCB7XG4gICAgICAgIHdhaXRNczogc3RhcnR1cFRpbWVvdXQsXG4gICAgICAgIGludGVydmFsTXM6IDMwMCxcbiAgICAgIH0pO1xuICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgIGxvZy53YXJuKGBTY3JlZW4gY2FwdHVyZSBwcm9jZXNzIGRpZCBub3Qgc3RhcnQgd2l0aGluICR7c3RhcnR1cFRpbWVvdXR9bXMuIENvbnRpbnVpbmcgYW55d2F5YCk7XG4gICAgfVxuICAgIGlmICghdGhpcy5tYWluUHJvY2Vzcy5pc1J1bm5pbmcpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihgVGhlIHNjcmVlbiBjYXB0dXJlIHByb2Nlc3MgJyR7RkZNUEVHX0JJTkFSWX0nIGRpZWQgdW5leHBlY3RlZGx5LiBgICtcbiAgICAgICAgYENoZWNrIHNlcnZlciBsb2dzIGZvciBtb3JlIGRldGFpbHNgKTtcbiAgICB9XG4gICAgbG9nLmluZm8oYFN0YXJ0aW5nIHNjcmVlbiBjYXB0dXJlIG9uIHRoZSBkZXZpY2UgJyR7dGhpcy51ZGlkfScgd2l0aCBjb21tYW5kOiAnJHtGRk1QRUdfQklOQVJZfSAke2FyZ3Muam9pbignICcpfScuIGAgK1xuICAgICAgYFdpbGwgdGltZW91dCBpbiAke3RpbWVvdXRNc31tc2ApO1xuXG4gICAgdGhpcy50aW1lb3V0SGFuZGxlciA9IHNldFRpbWVvdXQoYXN5bmMgKCkgPT4ge1xuICAgICAgaWYgKCFhd2FpdCB0aGlzLmludGVycnVwdCgpKSB7XG4gICAgICAgIGxvZy53YXJuKGBDYW5ub3QgZmluaXNoIHRoZSBhY3RpdmUgc2NyZWVuIHJlY29yZGluZyBvbiB0aGUgZGV2aWNlICcke3RoaXMudWRpZH0nIGFmdGVyICR7dGltZW91dE1zfW1zIHRpbWVvdXRgKTtcbiAgICAgIH1cbiAgICB9LCB0aW1lb3V0TXMpO1xuICB9XG5cbiAgYXN5bmMgaW50ZXJydXB0IChmb3JjZSA9IGZhbHNlKSB7XG4gICAgbGV0IHJlc3VsdCA9IHRydWU7XG5cbiAgICBpZiAodGhpcy50aW1lb3V0SGFuZGxlcikge1xuICAgICAgY2xlYXJUaW1lb3V0KHRoaXMudGltZW91dEhhbmRsZXIpO1xuICAgICAgdGhpcy50aW1lb3V0SGFuZGxlciA9IG51bGw7XG4gICAgfVxuXG4gICAgaWYgKHRoaXMubWFpblByb2Nlc3MgJiYgdGhpcy5tYWluUHJvY2Vzcy5pc1J1bm5pbmcpIHtcbiAgICAgIGNvbnN0IGludGVycnVwdFByb21pc2UgPSB0aGlzLm1haW5Qcm9jZXNzLnN0b3AoZm9yY2UgPyAnU0lHVEVSTScgOiAnU0lHSU5UJyk7XG4gICAgICB0aGlzLm1haW5Qcm9jZXNzID0gbnVsbDtcbiAgICAgIHRyeSB7XG4gICAgICAgIGF3YWl0IGludGVycnVwdFByb21pc2U7XG4gICAgICB9IGNhdGNoIChlKSB7XG4gICAgICAgIGxvZy53YXJuKGBDYW5ub3QgJHtmb3JjZSA/ICd0ZXJtaW5hdGUnIDogJ2ludGVycnVwdCd9ICR7RkZNUEVHX0JJTkFSWX0uIGAgK1xuICAgICAgICAgIGBPcmlnaW5hbCBlcnJvcjogJHtlLm1lc3NhZ2V9YCk7XG4gICAgICAgIHJlc3VsdCA9IGZhbHNlO1xuICAgICAgfVxuICAgIH1cblxuICAgIERFVklDRV9DT05ORUNUSU9OU19GQUNUT1JZLnJlbGVhc2VDb25uZWN0aW9uKHRoaXMudWRpZCwgdGhpcy5vcHRzLnJlbW90ZVBvcnQpO1xuXG4gICAgcmV0dXJuIHJlc3VsdDtcbiAgfVxuXG4gIGFzeW5jIGZpbmlzaCAoKSB7XG4gICAgYXdhaXQgdGhpcy5pbnRlcnJ1cHQoKTtcbiAgICByZXR1cm4gdGhpcy52aWRlb1BhdGg7XG4gIH1cblxuICBhc3luYyBjbGVhbnVwICgpIHtcbiAgICBpZiAoYXdhaXQgZnMuZXhpc3RzKHRoaXMudmlkZW9QYXRoKSkge1xuICAgICAgYXdhaXQgZnMucmltcmFmKHRoaXMudmlkZW9QYXRoKTtcbiAgICB9XG4gIH1cbn1cblxuXG4vKipcbiAqIEB0eXBlZGVmIHtPYmplY3R9IFN0YXJ0UmVjb3JkaW5nT3B0aW9uc1xuICpcbiAqIEBwcm9wZXJ0eSB7P3N0cmluZ30gcmVtb3RlUGF0aCAtIFRoZSBwYXRoIHRvIHRoZSByZW1vdGUgbG9jYXRpb24sIHdoZXJlIHRoZSByZXN1bHRpbmcgdmlkZW8gc2hvdWxkIGJlIHVwbG9hZGVkLlxuICogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVGhlIGZvbGxvd2luZyBwcm90b2NvbHMgYXJlIHN1cHBvcnRlZDogaHR0cC9odHRwcywgZnRwLlxuICogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgTnVsbCBvciBlbXB0eSBzdHJpbmcgdmFsdWUgKHRoZSBkZWZhdWx0IHNldHRpbmcpIG1lYW5zIHRoZSBjb250ZW50IG9mIHJlc3VsdGluZ1xuICogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsZSBzaG91bGQgYmUgZW5jb2RlZCBhcyBCYXNlNjQgYW5kIHBhc3NlZCBhcyB0aGUgZW5kcG9pbnQgcmVzcG9uc2UgdmFsdWUuXG4gKiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBBbiBleGNlcHRpb24gd2lsbCBiZSB0aHJvd24gaWYgdGhlIGdlbmVyYXRlZCBtZWRpYSBmaWxlIGlzIHRvbyBiaWcgdG9cbiAqICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpdCBpbnRvIHRoZSBhdmFpbGFibGUgcHJvY2VzcyBtZW1vcnkuXG4gKiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUaGlzIG9wdGlvbiBvbmx5IGhhcyBhbiBlZmZlY3QgaWYgdGhlcmUgaXMgc2NyZWVuIHJlY29yZGluZyBwcm9jZXNzIGluIHByb2dyZXNzXG4gKiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhbmQgYGZvcmNlUmVzdGFydGAgcGFyYW1ldGVyIGlzIG5vdCBzZXQgdG8gYHRydWVgLlxuICogQHByb3BlcnR5IHs/c3RyaW5nfSB1c2VyIC0gVGhlIG5hbWUgb2YgdGhlIHVzZXIgZm9yIHRoZSByZW1vdGUgYXV0aGVudGljYXRpb24uIE9ubHkgd29ya3MgaWYgYHJlbW90ZVBhdGhgIGlzIHByb3ZpZGVkLlxuICogQHByb3BlcnR5IHs/c3RyaW5nfSBwYXNzIC0gVGhlIHBhc3N3b3JkIGZvciB0aGUgcmVtb3RlIGF1dGhlbnRpY2F0aW9uLiBPbmx5IHdvcmtzIGlmIGByZW1vdGVQYXRoYCBpcyBwcm92aWRlZC5cbiAqIEBwcm9wZXJ0eSB7P3N0cmluZ30gbWV0aG9kIC0gVGhlIGh0dHAgbXVsdGlwYXJ0IHVwbG9hZCBtZXRob2QgbmFtZS4gVGhlICdQVVQnIG9uZSBpcyB1c2VkIGJ5IGRlZmF1bHQuXG4gKiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIE9ubHkgd29ya3MgaWYgYHJlbW90ZVBhdGhgIGlzIHByb3ZpZGVkLlxuICogQHByb3BlcnR5IHs/c3RyaW5nfSB2aWRlb1R5cGUgLSBUaGUgdmlkZW8gY29kZWMgdHlwZSB1c2VkIGZvciBlbmNvZGluZyBvZiB0aGUgYmUgcmVjb3JkZWQgc2NyZWVuIGNhcHR1cmUuXG4gKiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEV4ZWN1dGUgYGZmbXBlZyAtY29kZWNzYCBpbiB0aGUgdGVybWluYWwgdG8gc2VlIHRoZSBsaXN0IG9mIHN1cHBvcnRlZCB2aWRlbyBjb2RlY3MuXG4gKiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdtanBlZycgYnkgZGVmYXVsdC5cbiAqIEBwcm9wZXJ0eSB7P3N0cmluZ3xudW1iZXJ9IHZpZGVvUXVhbGl0eSAtIFRoZSB2aWRlbyBlbmNvZGluZyBxdWFsaXR5IChsb3csIG1lZGl1bSwgaGlnaCwgcGhvdG8gLSBkZWZhdWx0cyB0byBtZWRpdW0pLlxuICogQHByb3BlcnR5IHs/c3RyaW5nfG51bWJlcn0gdmlkZW9GcHMgLSBUaGUgRnJhbWVzIFBlciBTZWNvbmQgcmF0ZSBvZiB0aGUgcmVjb3JkZWQgdmlkZW8uIENoYW5nZSB0aGlzIHZhbHVlIGlmIHRoZSByZXN1bHRpbmcgdmlkZW9cbiAqICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpcyB0b28gc2xvdyBvciB0b28gZmFzdC4gRGVmYXVsdHMgdG8gMTAuXG4gKiBAcHJvcGVydHkgez9zdHJpbmd9IHZpZGVvRmlsdGVycyAtIFRoZSBGRk1QRUcgdmlkZW8gZmlsdGVycyB0byBhcHBseS4gVGhlc2UgZmlsdGVycyBhbGxvdyB0byBzY2FsZSwgZmxpcCwgcm90YXRlIGFuZCBkbyBtYW55XG4gKiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG90aGVyIHVzZWZ1bCB0cmFuc2Zvcm1hdGlvbnMgb24gdGhlIHNvdXJjZSB2aWRlbyBzdHJlYW0uIFRoZSBmb3JtYXQgb2YgdGhlIHByb3BlcnR5XG4gKiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG11c3QgY29tcGx5IHdpdGggaHR0cHM6Ly9mZm1wZWcub3JnL2ZmbXBlZy1maWx0ZXJzLmh0bWxcbiAqIEBwcm9wZXJ0eSB7P3N0cmluZ30gdmlkZW9TY2FsZSAtIFRoZSBzY2FsaW5nIHZhbHVlIHRvIGFwcGx5LiBSZWFkIGh0dHBzOi8vdHJhYy5mZm1wZWcub3JnL3dpa2kvU2NhbGluZyBmb3IgcG9zc2libGUgdmFsdWVzLlxuICogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgTm8gc2NhbGUgaXMgYXBwbGllZCBieSBkZWZhdWx0LiBJZiBib3RoIGB2aWRlb0ZpbHRlcnNgIGFuZCBgdmlkZW9TY2FsZWAgYXJlIHNldCB0aGVuXG4gKiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBvbmx5IGB2aWRlb0ZpbHRlcnNgIHZhbHVlIHdpbGwgYmUgcmVzcGVjdGVkLlxuICogQHByb3BlcnR5IHs/c3RyaW5nfSBwaXhlbEZvcm1hdCAtIE91dHB1dCBwaXhlbCBmb3JtYXQuIFJ1biBgZmZtcGVnIC1waXhfZm10c2AgdG8gbGlzdCBwb3NzaWJsZSB2YWx1ZXMuXG4gKiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgRm9yIFF1aWNrdGltZSBjb21wYXRpYmlsaXR5LCBzZXQgdG8gXCJ5dXY0MjBwXCIgYWxvbmcgd2l0aCB2aWRlb1R5cGU6IFwibGlieDI2NFwiLlxuICogQHByb3BlcnR5IHs/Ym9vbGVhbn0gZm9yY2VSZXN0YXJ0IC0gV2hldGhlciB0byB0cnkgdG8gY2F0Y2ggYW5kIHVwbG9hZC9yZXR1cm4gdGhlIGN1cnJlbnRseSBydW5uaW5nIHNjcmVlbiByZWNvcmRpbmdcbiAqICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIChgZmFsc2VgLCB0aGUgZGVmYXVsdCBzZXR0aW5nKSBvciBpZ25vcmUgdGhlIHJlc3VsdCBvZiBpdCBhbmQgc3RhcnQgYSBuZXcgcmVjb3JkaW5nXG4gKiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpbW1lZGlhdGVseS5cbiAqIEBwcm9wZXJ0eSB7P3N0cmluZ3xudW1iZXJ9IHRpbWVMaW1pdCAtIFRoZSBtYXhpbXVtIHJlY29yZGluZyB0aW1lLCBpbiBzZWNvbmRzLlxuICogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVGhlIGRlZmF1bHQgdmFsdWUgaXMgMTgwLCB0aGUgbWF4aW11bSB2YWx1ZSBpcyA2MDAgKDEwIG1pbnV0ZXMpLlxuICovXG5cbi8qKlxuICogUmVjb3JkIHRoZSBkaXNwbGF5IG9mIGRldmljZXMgcnVubmluZyBpT1MgU2ltdWxhdG9yIHNpbmNlIFhjb2RlIDkgb3IgcmVhbCBkZXZpY2VzIHNpbmNlIGlPUyAxMVxuICogKGZmbXBlZyB1dGlsaXR5IGlzIHJlcXVpcmVkOiAnYnJldyBpbnN0YWxsIGZmbXBlZycpLlxuICogSXQgcmVjb3JkcyBzY3JlZW4gYWN0aXZpdHkgdG8gYSBNUEVHLTQgZmlsZS4gQXVkaW8gaXMgbm90IHJlY29yZGVkIHdpdGggdGhlIHZpZGVvIGZpbGUuXG4gKiBJZiBzY3JlZW4gcmVjb3JkaW5nIGhhcyBiZWVuIGFscmVhZHkgc3RhcnRlZCB0aGVuIHRoZSBjb21tYW5kIHdpbGwgc3RvcCBpdCBmb3JjZWZ1bGx5IGFuZCBzdGFydCBhIG5ldyBvbmUuXG4gKiBUaGUgcHJldmlvdXNseSByZWNvcmRlZCB2aWRlbyBmaWxlIHdpbGwgYmUgZGVsZXRlZC5cbiAqXG4gKiBAcGFyYW0gez9TdGFydFJlY29yZGluZ09wdGlvbnN9IG9wdGlvbnMgLSBUaGUgYXZhaWxhYmxlIG9wdGlvbnMuXG4gKiBAcmV0dXJucyB7c3RyaW5nfSBCYXNlNjQtZW5jb2RlZCBjb250ZW50IG9mIHRoZSByZWNvcmRlZCBtZWRpYSBmaWxlIGlmXG4gKiAgICAgICAgICAgICAgICAgICBhbnkgc2NyZWVuIHJlY29yZGluZyBpcyBjdXJyZW50bHkgcnVubmluZyBvciBhbiBlbXB0eSBzdHJpbmcuXG4gKiBAdGhyb3dzIHtFcnJvcn0gSWYgc2NyZWVuIHJlY29yZGluZyBoYXMgZmFpbGVkIHRvIHN0YXJ0LlxuICovXG5jb21tYW5kcy5zdGFydFJlY29yZGluZ1NjcmVlbiA9IGFzeW5jIGZ1bmN0aW9uIHN0YXJ0UmVjb3JkaW5nU2NyZWVuIChvcHRpb25zID0ge30pIHtcbiAgY29uc3Qge1xuICAgIHZpZGVvVHlwZSA9IERFRkFVTFRfVkNPREVDLFxuICAgIHRpbWVMaW1pdCA9IERFRkFVTFRfUkVDT1JESU5HX1RJTUVfU0VDLFxuICAgIHZpZGVvUXVhbGl0eSA9IERFRkFVTFRfUVVBTElUWSxcbiAgICB2aWRlb0ZwcyA9IERFRkFVTFRfRlBTLFxuICAgIHZpZGVvRmlsdGVycyxcbiAgICB2aWRlb1NjYWxlLFxuICAgIGZvcmNlUmVzdGFydCxcbiAgICBwaXhlbEZvcm1hdFxuICB9ID0gb3B0aW9ucztcblxuICBsZXQgcmVzdWx0ID0gJyc7XG4gIGlmICghZm9yY2VSZXN0YXJ0KSB7XG4gICAgbG9nLmluZm8oYENoZWNraW5nIGlmIHRoZXJlIGlzL3dhcyBhIHByZXZpb3VzIHNjcmVlbiByZWNvcmRpbmcuIGAgK1xuICAgICAgYFNldCAnZm9yY2VSZXN0YXJ0JyBvcHRpb24gdG8gJ3RydWUnIGlmIHlvdSdkIGxpa2UgdG8gc2tpcCB0aGlzIHN0ZXAuYCk7XG4gICAgcmVzdWx0ID0gYXdhaXQgdGhpcy5zdG9wUmVjb3JkaW5nU2NyZWVuKG9wdGlvbnMpO1xuICB9XG5cbiAgY29uc3QgdmlkZW9QYXRoID0gYXdhaXQgdGVtcERpci5wYXRoKHtcbiAgICBwcmVmaXg6IGBhcHBpdW1fJHtNYXRoLnJhbmRvbSgpLnRvU3RyaW5nKDE2KS5zdWJzdHJpbmcoMiwgOCl9YCxcbiAgICBzdWZmaXg6IE1QNF9FWFQsXG4gIH0pO1xuXG4gIGNvbnN0IHdkYUJhc2VVcmwgPSB0aGlzLm9wdHMud2RhQmFzZVVybCB8fCBXREFfQkFTRV9VUkw7XG4gIGNvbnN0IHNjcmVlblJlY29yZGVyID0gbmV3IFNjcmVlblJlY29yZGVyKHRoaXMub3B0cy5kZXZpY2UudWRpZCwgdmlkZW9QYXRoLCB7XG4gICAgcmVtb3RlUG9ydDogdGhpcy5vcHRzLm1qcGVnU2VydmVyUG9ydCB8fCBERUZBVUxUX01KUEVHX1NFUlZFUl9QT1JULFxuICAgIHJlbW90ZVVybDogd2RhQmFzZVVybCxcbiAgICB1c2VQb3J0Rm9yd2FyZGluZzogdGhpcy5pc1JlYWxEZXZpY2UoKSAmJiBpc0xvY2FsSG9zdCh3ZGFCYXNlVXJsKSxcbiAgICB2aWRlb1R5cGUsXG4gICAgdmlkZW9GaWx0ZXJzLFxuICAgIHZpZGVvU2NhbGUsXG4gICAgdmlkZW9GcHMsXG4gICAgcGl4ZWxGb3JtYXRcbiAgfSk7XG4gIGlmICghYXdhaXQgc2NyZWVuUmVjb3JkZXIuaW50ZXJydXB0KHRydWUpKSB7XG4gICAgbG9nLmVycm9yQW5kVGhyb3coJ1VuYWJsZSB0byBzdG9wIHNjcmVlbiByZWNvcmRpbmcgcHJvY2VzcycpO1xuICB9XG4gIGlmICh0aGlzLl9yZWNlbnRTY3JlZW5SZWNvcmRlcikge1xuICAgIGF3YWl0IHRoaXMuX3JlY2VudFNjcmVlblJlY29yZGVyLmNsZWFudXAoKTtcbiAgICB0aGlzLl9yZWNlbnRTY3JlZW5SZWNvcmRlciA9IG51bGw7XG4gIH1cblxuICBjb25zdCB0aW1lb3V0U2Vjb25kcyA9IHBhcnNlRmxvYXQodGltZUxpbWl0KTtcbiAgaWYgKGlzTmFOKHRpbWVvdXRTZWNvbmRzKSB8fCB0aW1lb3V0U2Vjb25kcyA+IE1BWF9SRUNPUkRJTkdfVElNRV9TRUMgfHwgdGltZW91dFNlY29uZHMgPD0gMCkge1xuICAgIGxvZy5lcnJvckFuZFRocm93KGBUaGUgdGltZUxpbWl0IHZhbHVlIG11c3QgYmUgaW4gcmFuZ2UgWzEsICR7TUFYX1JFQ09SRElOR19USU1FX1NFQ31dIHNlY29uZHMuIGAgK1xuICAgICAgYFRoZSB2YWx1ZSBvZiAnJHt0aW1lTGltaXR9JyBoYXMgYmVlbiBwYXNzZWQgaW5zdGVhZC5gKTtcbiAgfVxuXG4gIGxldCB7XG4gICAgbWpwZWdTZXJ2ZXJTY3JlZW5zaG90UXVhbGl0eSxcbiAgICBtanBlZ1NlcnZlckZyYW1lcmF0ZSxcbiAgfSA9IGF3YWl0IHRoaXMucHJveHlDb21tYW5kKCcvYXBwaXVtL3NldHRpbmdzJywgJ0dFVCcpO1xuICBpZiAodmlkZW9RdWFsaXR5KSB7XG4gICAgY29uc3QgcXVhbGl0eSA9IF8uaXNJbnRlZ2VyKHZpZGVvUXVhbGl0eSkgPyB2aWRlb1F1YWxpdHkgOiBRVUFMSVRZX01BUFBJTkdbXy50b0xvd2VyKHZpZGVvUXVhbGl0eSldO1xuICAgIGlmICghcXVhbGl0eSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKGB2aWRlb1F1YWxpdHkgdmFsdWUgc2hvdWxkIGJlIG9uZSBvZiAke0pTT04uc3RyaW5naWZ5KF8ua2V5cyhRVUFMSVRZX01BUFBJTkcpKX0gb3IgYSBudW1iZXIgaW4gcmFuZ2UgMS4uMTAwLiBgICtcbiAgICAgICAgYCcke3ZpZGVvUXVhbGl0eX0nIGlzIGdpdmVuIGluc3RlYWRgKTtcbiAgICB9XG4gICAgbWpwZWdTZXJ2ZXJTY3JlZW5zaG90UXVhbGl0eSA9IG1qcGVnU2VydmVyU2NyZWVuc2hvdFF1YWxpdHkgIT09IHF1YWxpdHkgPyBxdWFsaXR5IDogdW5kZWZpbmVkO1xuICB9IGVsc2Uge1xuICAgIG1qcGVnU2VydmVyU2NyZWVuc2hvdFF1YWxpdHkgPSB1bmRlZmluZWQ7XG4gIH1cbiAgaWYgKHZpZGVvRnBzKSB7XG4gICAgY29uc3QgZnBzID0gcGFyc2VJbnQodmlkZW9GcHMsIDEwKTtcbiAgICBpZiAoaXNOYU4oZnBzKSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKGB2aWRlb0ZwcyB2YWx1ZSBzaG91bGQgYmUgYSB2YWxpZCBudW1iZXIgaW4gcmFuZ2UgMS4uNjAuIGAgK1xuICAgICAgICBgJyR7dmlkZW9GcHN9JyBpcyBnaXZlbiBpbnN0ZWFkYCk7XG4gICAgfVxuICAgIG1qcGVnU2VydmVyRnJhbWVyYXRlID0gbWpwZWdTZXJ2ZXJGcmFtZXJhdGUgIT09IGZwcyA/IGZwcyA6IHVuZGVmaW5lZDtcbiAgfSBlbHNlIHtcbiAgICBtanBlZ1NlcnZlckZyYW1lcmF0ZSA9IHVuZGVmaW5lZDtcbiAgfVxuICBpZiAodXRpbC5oYXNWYWx1ZShtanBlZ1NlcnZlclNjcmVlbnNob3RRdWFsaXR5KSB8fCB1dGlsLmhhc1ZhbHVlKG1qcGVnU2VydmVyRnJhbWVyYXRlKSkge1xuICAgIGF3YWl0IHRoaXMucHJveHlDb21tYW5kKCcvYXBwaXVtL3NldHRpbmdzJywgJ1BPU1QnLCB7XG4gICAgICBzZXR0aW5nczoge1xuICAgICAgICBtanBlZ1NlcnZlclNjcmVlbnNob3RRdWFsaXR5LFxuICAgICAgICBtanBlZ1NlcnZlckZyYW1lcmF0ZSxcbiAgICAgIH1cbiAgICB9KTtcbiAgfVxuXG4gIHRyeSB7XG4gICAgYXdhaXQgc2NyZWVuUmVjb3JkZXIuc3RhcnQodGltZW91dFNlY29uZHMgKiAxMDAwKTtcbiAgfSBjYXRjaCAoZSkge1xuICAgIGF3YWl0IHNjcmVlblJlY29yZGVyLmludGVycnVwdCh0cnVlKTtcbiAgICBhd2FpdCBzY3JlZW5SZWNvcmRlci5jbGVhbnVwKCk7XG4gICAgdGhyb3cgZTtcbiAgfVxuICB0aGlzLl9yZWNlbnRTY3JlZW5SZWNvcmRlciA9IHNjcmVlblJlY29yZGVyO1xuXG4gIHJldHVybiByZXN1bHQ7XG59O1xuXG4vKipcbiAqIEB0eXBlZGVmIHtPYmplY3R9IFN0b3BSZWNvcmRpbmdPcHRpb25zXG4gKlxuICogQHByb3BlcnR5IHs/c3RyaW5nfSByZW1vdGVQYXRoIC0gVGhlIHBhdGggdG8gdGhlIHJlbW90ZSBsb2NhdGlvbiwgd2hlcmUgdGhlIHJlc3VsdGluZyB2aWRlbyBzaG91bGQgYmUgdXBsb2FkZWQuXG4gKiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUaGUgZm9sbG93aW5nIHByb3RvY29scyBhcmUgc3VwcG9ydGVkOiBodHRwL2h0dHBzLCBmdHAuXG4gKiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBOdWxsIG9yIGVtcHR5IHN0cmluZyB2YWx1ZSAodGhlIGRlZmF1bHQgc2V0dGluZykgbWVhbnMgdGhlIGNvbnRlbnQgb2YgcmVzdWx0aW5nXG4gKiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaWxlIHNob3VsZCBiZSBlbmNvZGVkIGFzIEJhc2U2NCBhbmQgcGFzc2VkIGFzIHRoZSBlbmRwb2ludCByZXNwb25zZSB2YWx1ZS5cbiAqICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEFuIGV4Y2VwdGlvbiB3aWxsIGJlIHRocm93biBpZiB0aGUgZ2VuZXJhdGVkIG1lZGlhIGZpbGUgaXMgdG9vIGJpZyB0b1xuICogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZml0IGludG8gdGhlIGF2YWlsYWJsZSBwcm9jZXNzIG1lbW9yeS5cbiAqIEBwcm9wZXJ0eSB7P3N0cmluZ30gdXNlciAtIFRoZSBuYW1lIG9mIHRoZSB1c2VyIGZvciB0aGUgcmVtb3RlIGF1dGhlbnRpY2F0aW9uLiBPbmx5IHdvcmtzIGlmIGByZW1vdGVQYXRoYCBpcyBwcm92aWRlZC5cbiAqIEBwcm9wZXJ0eSB7P3N0cmluZ30gcGFzcyAtIFRoZSBwYXNzd29yZCBmb3IgdGhlIHJlbW90ZSBhdXRoZW50aWNhdGlvbi4gT25seSB3b3JrcyBpZiBgcmVtb3RlUGF0aGAgaXMgcHJvdmlkZWQuXG4gKiBAcHJvcGVydHkgez9zdHJpbmd9IG1ldGhvZCAtIFRoZSBodHRwIG11bHRpcGFydCB1cGxvYWQgbWV0aG9kIG5hbWUuIFRoZSAnUFVUJyBvbmUgaXMgdXNlZCBieSBkZWZhdWx0LlxuICogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBPbmx5IHdvcmtzIGlmIGByZW1vdGVQYXRoYCBpcyBwcm92aWRlZC5cbiAqL1xuXG4vKipcbiAqIFN0b3AgcmVjb3JkaW5nIHRoZSBzY3JlZW4uIElmIG5vIHNjcmVlbiByZWNvcmRpbmcgcHJvY2VzcyBpcyBydW5uaW5nIHRoZW5cbiAqIHRoZSBlbmRwb2ludCB3aWxsIHRyeSB0byBnZXQgdGhlIHJlY2VudGx5IHJlY29yZGVkIGZpbGUuXG4gKiBJZiBubyBwcmV2aW91c2x5IHJlY29yZGVkIGZpbGUgaXMgZm91bmQgYW5kIG5vIGFjdGl2ZSBzY3JlZW4gcmVjb3JkaW5nXG4gKiBwcm9jZXNzZXMgYXJlIHJ1bm5pbmcgdGhlbiB0aGUgbWV0aG9kIHJldHVybnMgYW4gZW1wdHkgc3RyaW5nLlxuICpcbiAqIEBwYXJhbSB7P1N0b3BSZWNvcmRpbmdPcHRpb25zfSBvcHRpb25zIC0gVGhlIGF2YWlsYWJsZSBvcHRpb25zLlxuICogQHJldHVybnMge3N0cmluZ30gQmFzZTY0LWVuY29kZWQgY29udGVudCBvZiB0aGUgcmVjb3JkZWQgbWVkaWEgZmlsZSBpZiAncmVtb3RlUGF0aCdcbiAqICAgICAgICAgICAgICAgICAgIHBhcmFtZXRlciBpcyBlbXB0eSBvciBudWxsIG9yIGFuIGVtcHR5IHN0cmluZy5cbiAqIEB0aHJvd3Mge0Vycm9yfSBJZiB0aGVyZSB3YXMgYW4gZXJyb3Igd2hpbGUgZ2V0dGluZyB0aGUgbmFtZSBvZiBhIG1lZGlhIGZpbGVcbiAqICAgICAgICAgICAgICAgICBvciB0aGUgZmlsZSBjb250ZW50IGNhbm5vdCBiZSB1cGxvYWRlZCB0byB0aGUgcmVtb3RlIGxvY2F0aW9uLlxuICovXG5jb21tYW5kcy5zdG9wUmVjb3JkaW5nU2NyZWVuID0gYXN5bmMgZnVuY3Rpb24gc3RvcFJlY29yZGluZ1NjcmVlbiAob3B0aW9ucyA9IHt9KSB7XG4gIGNvbnN0IHtcbiAgICByZW1vdGVQYXRoLFxuICAgIHVzZXIsXG4gICAgcGFzcyxcbiAgICBtZXRob2QsXG4gIH0gPSBvcHRpb25zO1xuXG4gIGlmICghdGhpcy5fcmVjZW50U2NyZWVuUmVjb3JkZXIpIHtcbiAgICBsb2cuaW5mbygnU2NyZWVuIHJlY29yZGluZyBpcyBub3QgcnVubmluZy4gVGhlcmUgaXMgbm90aGluZyB0byBzdG9wLicpO1xuICAgIHJldHVybiAnJztcbiAgfVxuXG4gIHRyeSB7XG4gICAgY29uc3QgdmlkZW9QYXRoID0gYXdhaXQgdGhpcy5fcmVjZW50U2NyZWVuUmVjb3JkZXIuZmluaXNoKCk7XG4gICAgaWYgKCFhd2FpdCBmcy5leGlzdHModmlkZW9QYXRoKSkge1xuICAgICAgbG9nLmVycm9yQW5kVGhyb3coYFRoZSBzY3JlZW4gcmVjb3JkZXIgdXRpbGl0eSBoYXMgZmFpbGVkIGAgK1xuICAgICAgICBgdG8gc3RvcmUgdGhlIGFjdHVhbCBzY3JlZW4gcmVjb3JkaW5nIGF0ICcke3ZpZGVvUGF0aH0nYCk7XG4gICAgfVxuICAgIHJldHVybiBhd2FpdCBlbmNvZGVCYXNlNjRPclVwbG9hZCh2aWRlb1BhdGgsIHJlbW90ZVBhdGgsIHtcbiAgICAgIHVzZXIsXG4gICAgICBwYXNzLFxuICAgICAgbWV0aG9kXG4gICAgfSk7XG4gIH0gZmluYWxseSB7XG4gICAgYXdhaXQgdGhpcy5fcmVjZW50U2NyZWVuUmVjb3JkZXIuaW50ZXJydXB0KHRydWUpO1xuICAgIGF3YWl0IHRoaXMuX3JlY2VudFNjcmVlblJlY29yZGVyLmNsZWFudXAoKTtcbiAgICB0aGlzLl9yZWNlbnRTY3JlZW5SZWNvcmRlciA9IG51bGw7XG4gIH1cbn07XG5cblxuZXhwb3J0IHsgY29tbWFuZHMgfTtcbmV4cG9ydCBkZWZhdWx0IGNvbW1hbmRzO1xuIl0sImZpbGUiOiJsaWIvY29tbWFuZHMvcmVjb3Jkc2NyZWVuLmpzIiwic291cmNlUm9vdCI6Ii4uLy4uLy4uIn0=