UNPKG

gst-atom-xcuitest-driver

Version:

ATOM driver for iOS using XCUITest for backend

308 lines (239 loc) 33.1 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports.default = exports.DeviceConnectionsFactory = exports.DEVICE_CONNECTIONS_FACTORY = void 0; require("source-map-support/register"); var _lodash = _interopRequireDefault(require("lodash")); var _net = _interopRequireDefault(require("net")); var _bluebird = _interopRequireDefault(require("bluebird")); var _appiumSupport = require("appium-support"); var _gstAtomIosDevice = require("gst-atom-ios-device"); var _portscanner = require("portscanner"); var _asyncbox = require("asyncbox"); const LOCALHOST = '127.0.0.1'; class iProxy { constructor(udid, localport, deviceport, usbmuxdRemoteHost, usbmuxdRemotePort) { this.localport = parseInt(localport, 10); this.deviceport = parseInt(deviceport, 10); this.udid = udid; this.localServer = null; this.log = _appiumSupport.logger.getLogger(`iProxy@${udid.substring(0, 8)}:${this.localport}`); this.usbmuxdRemoteHost = usbmuxdRemoteHost; this.usbmuxdRemotePort = usbmuxdRemotePort; } async start() { if (this.localServer) { return; } this.localServer = _net.default.createServer(async localSocket => { let remoteSocket; try { var options = { udid: this.udid, port: this.deviceport, usbmuxdRemoteHost: this.usbmuxdRemoteHost, usbmuxdRemotePort: this.usbmuxdRemotePort }; remoteSocket = await _gstAtomIosDevice.utilities.connectPort(null, options); } catch (e) { this.log.debug(e.message); localSocket.destroy(); return; } const destroyCommChannel = () => { remoteSocket.unpipe(localSocket); localSocket.unpipe(remoteSocket); }; remoteSocket.once('close', () => { destroyCommChannel(); localSocket.destroy(); }); remoteSocket.on('error', e => this.log.debug(e)); localSocket.once('end', destroyCommChannel); localSocket.once('close', () => { destroyCommChannel(); remoteSocket.destroy(); }); localSocket.on('error', e => this.log.warn(e.message)); localSocket.pipe(remoteSocket); remoteSocket.pipe(localSocket); }); const listeningPromise = new _bluebird.default((resolve, reject) => { this.localServer.once('listening', resolve); this.localServer.once('error', reject); }); this.localServer.listen(this.localport); try { await listeningPromise; } catch (e) { this.localServer = null; throw e; } this.localServer.on('error', e => this.log.warn(e.message)); this.localServer.once('close', e => { if (e) { this.log.info(`The connection has been closed with error ${e.message}`); } else { this.log.info(`The connection has been closed`); } this.localServer = null; }); this.onBeforeProcessExit = this._closeLocalServer.bind(this); process.on('beforeExit', this.onBeforeProcessExit); } _closeLocalServer() { if (!this.localServer) { return; } this.log.debug(`Closing the connection`); this.localServer.close(); this.localServer = null; } stop() { if (this.onBeforeProcessExit) { process.off('beforeExit', this.onBeforeProcessExit); this.onBeforeProcessExit = null; } this._closeLocalServer(); } } const log = _appiumSupport.logger.getLogger('DevCon Factory'); const PORT_CLOSE_TIMEOUT = 15 * 1000; const SPLITTER = ':'; class DeviceConnectionsFactory { constructor() { this._connectionsMapping = {}; } _udidAsToken(udid) { return `${_appiumSupport.util.hasValue(udid) ? udid : ''}${SPLITTER}`; } _portAsToken(port) { return `${SPLITTER}${_appiumSupport.util.hasValue(port) ? port : ''}`; } _toKey(udid = null, port = null) { return `${_appiumSupport.util.hasValue(udid) ? udid : ''}${SPLITTER}${_appiumSupport.util.hasValue(port) ? port : ''}`; } _releaseProxiedConnections(connectionKeys) { const keys = connectionKeys.filter(k => _lodash.default.has(this._connectionsMapping[k], 'iproxy')); for (const key of keys) { log.info(`Releasing the listener for '${key}'`); try { this._connectionsMapping[key].iproxy.stop(); } catch (e) { log.debug(e); } } return keys; } listConnections(udid = null, port = null, strict = false) { if (!udid && !port) { return []; } return _lodash.default.keys(this._connectionsMapping).filter(key => strict && udid && port ? key === this._toKey(udid, port) : udid && key.startsWith(this._udidAsToken(udid)) || port && key.endsWith(this._portAsToken(port))); } async requestConnection(options = {}) { const { udid, port, usePortForwarding, devicePort, usbmuxdRemoteHost, usbmuxdRemotePort } = options; if (!udid || !port) { log.warn('Did not know how to request the connection:'); if (!udid) { log.warn('- Device UDID is unset'); } if (!port) { log.warn('- The local port number is unset'); } return; } log.info(`Requesting connection for device ${udid} on local port ${port}` + (devicePort ? `, device port ${devicePort}` : '')); log.debug(`Cached connections count: ${_lodash.default.size(this._connectionsMapping)}`); const connectionsOnPort = this.listConnections(null, port); if (!_lodash.default.isEmpty(connectionsOnPort)) { log.info(`Found cached connections on port #${port}: ${JSON.stringify(connectionsOnPort)}`); } if (usePortForwarding) { let isPortBusy = (await (0, _portscanner.checkPortStatus)(port, LOCALHOST)) === 'open'; if (isPortBusy) { log.warn(`Port #${port} is busy. Did you quit the previous driver session(s) properly?`); if (!_lodash.default.isEmpty(connectionsOnPort)) { log.info('Trying to release the port'); for (const key of this._releaseProxiedConnections(connectionsOnPort)) { delete this._connectionsMapping[key]; } const timer = new _appiumSupport.timing.Timer().start(); try { await (0, _asyncbox.waitForCondition)(async () => { try { if ((await (0, _portscanner.checkPortStatus)(port, LOCALHOST)) !== 'open') { log.info(`Port #${port} has been successfully released after ` + `${timer.getDuration().asMilliSeconds.toFixed(0)}ms`); isPortBusy = false; return true; } } catch (ign) {} return false; }, { waitMs: PORT_CLOSE_TIMEOUT, intervalMs: 300 }); } catch (ign) { log.warn(`Did not know how to release port #${port} in ` + `${timer.getDuration().asMilliSeconds.toFixed(0)}ms`); } } } if (isPortBusy) { throw new Error(`The port #${port} is occupied by an other process. ` + `You can either quit that process or select another free port.`); } } const currentKey = this._toKey(udid, port); if (usePortForwarding) { const iproxy = new iProxy(udid, port, devicePort, usbmuxdRemoteHost, usbmuxdRemotePort); try { await iproxy.start(); this._connectionsMapping[currentKey] = { iproxy }; } catch (e) { try { iproxy.stop(); } catch (e1) { log.debug(e1); } throw e; } } else { this._connectionsMapping[currentKey] = {}; } log.info(`Successfully requested the connection for ${currentKey}`); } releaseConnection(udid = null, port = null) { if (!udid && !port) { log.warn('Neither device UDID nor local port is set. ' + 'Did not know how to release the connection'); return; } log.info(`Releasing connections for ${udid || 'any'} device on ${port || 'any'} port number`); const keys = this.listConnections(udid, port, true); if (_lodash.default.isEmpty(keys)) { log.info('No cached connections have been found'); return; } log.info(`Found cached connections to release: ${JSON.stringify(keys)}`); this._releaseProxiedConnections(keys); for (const key of keys) { delete this._connectionsMapping[key]; } log.debug(`Cached connections count: ${_lodash.default.size(this._connectionsMapping)}`); } } exports.DeviceConnectionsFactory = DeviceConnectionsFactory; const DEVICE_CONNECTIONS_FACTORY = new DeviceConnectionsFactory(); exports.DEVICE_CONNECTIONS_FACTORY = DEVICE_CONNECTIONS_FACTORY; var _default = DEVICE_CONNECTIONS_FACTORY; exports.default = _default;require('source-map-support').install(); //# sourceMappingURL=data:application/json;charset=utf8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImxpYi9kZXZpY2UtY29ubmVjdGlvbnMtZmFjdG9yeS5qcyJdLCJuYW1lcyI6WyJMT0NBTEhPU1QiLCJpUHJveHkiLCJjb25zdHJ1Y3RvciIsInVkaWQiLCJsb2NhbHBvcnQiLCJkZXZpY2Vwb3J0IiwidXNibXV4ZFJlbW90ZUhvc3QiLCJ1c2JtdXhkUmVtb3RlUG9ydCIsInBhcnNlSW50IiwibG9jYWxTZXJ2ZXIiLCJsb2ciLCJsb2dnZXIiLCJnZXRMb2dnZXIiLCJzdWJzdHJpbmciLCJzdGFydCIsIm5ldCIsImNyZWF0ZVNlcnZlciIsImxvY2FsU29ja2V0IiwicmVtb3RlU29ja2V0Iiwib3B0aW9ucyIsInBvcnQiLCJ1dGlsaXRpZXMiLCJjb25uZWN0UG9ydCIsImUiLCJkZWJ1ZyIsIm1lc3NhZ2UiLCJkZXN0cm95IiwiZGVzdHJveUNvbW1DaGFubmVsIiwidW5waXBlIiwib25jZSIsIm9uIiwid2FybiIsInBpcGUiLCJsaXN0ZW5pbmdQcm9taXNlIiwiQiIsInJlc29sdmUiLCJyZWplY3QiLCJsaXN0ZW4iLCJpbmZvIiwib25CZWZvcmVQcm9jZXNzRXhpdCIsIl9jbG9zZUxvY2FsU2VydmVyIiwiYmluZCIsInByb2Nlc3MiLCJjbG9zZSIsInN0b3AiLCJvZmYiLCJQT1JUX0NMT1NFX1RJTUVPVVQiLCJTUExJVFRFUiIsIkRldmljZUNvbm5lY3Rpb25zRmFjdG9yeSIsIl9jb25uZWN0aW9uc01hcHBpbmciLCJfdWRpZEFzVG9rZW4iLCJ1dGlsIiwiaGFzVmFsdWUiLCJfcG9ydEFzVG9rZW4iLCJfdG9LZXkiLCJfcmVsZWFzZVByb3hpZWRDb25uZWN0aW9ucyIsImNvbm5lY3Rpb25LZXlzIiwia2V5cyIsImZpbHRlciIsImsiLCJfIiwiaGFzIiwia2V5IiwiaXByb3h5IiwibGlzdENvbm5lY3Rpb25zIiwic3RyaWN0Iiwic3RhcnRzV2l0aCIsImVuZHNXaXRoIiwicmVxdWVzdENvbm5lY3Rpb24iLCJ1c2VQb3J0Rm9yd2FyZGluZyIsImRldmljZVBvcnQiLCJzaXplIiwiY29ubmVjdGlvbnNPblBvcnQiLCJpc0VtcHR5IiwiSlNPTiIsInN0cmluZ2lmeSIsImlzUG9ydEJ1c3kiLCJ0aW1lciIsInRpbWluZyIsIlRpbWVyIiwiZ2V0RHVyYXRpb24iLCJhc01pbGxpU2Vjb25kcyIsInRvRml4ZWQiLCJpZ24iLCJ3YWl0TXMiLCJpbnRlcnZhbE1zIiwiRXJyb3IiLCJjdXJyZW50S2V5IiwiZTEiLCJyZWxlYXNlQ29ubmVjdGlvbiIsIkRFVklDRV9DT05ORUNUSU9OU19GQUNUT1JZIl0sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7OztBQUFBOztBQUNBOztBQUNBOztBQUNBOztBQUNBOztBQUNBOztBQUNBOztBQUdBLE1BQU1BLFNBQVMsR0FBRyxXQUFsQjs7QUFFQSxNQUFNQyxNQUFOLENBQWE7QUFDWEMsRUFBQUEsV0FBVyxDQUFFQyxJQUFGLEVBQVFDLFNBQVIsRUFBbUJDLFVBQW5CLEVBQStCQyxpQkFBL0IsRUFBa0RDLGlCQUFsRCxFQUFxRTtBQUM5RSxTQUFLSCxTQUFMLEdBQWlCSSxRQUFRLENBQUNKLFNBQUQsRUFBWSxFQUFaLENBQXpCO0FBQ0EsU0FBS0MsVUFBTCxHQUFrQkcsUUFBUSxDQUFDSCxVQUFELEVBQWEsRUFBYixDQUExQjtBQUNBLFNBQUtGLElBQUwsR0FBWUEsSUFBWjtBQUNBLFNBQUtNLFdBQUwsR0FBbUIsSUFBbkI7QUFDQSxTQUFLQyxHQUFMLEdBQVdDLHNCQUFPQyxTQUFQLENBQWtCLFVBQVNULElBQUksQ0FBQ1UsU0FBTCxDQUFlLENBQWYsRUFBa0IsQ0FBbEIsQ0FBcUIsSUFBRyxLQUFLVCxTQUFVLEVBQWxFLENBQVg7QUFDQSxTQUFLRSxpQkFBTCxHQUF5QkEsaUJBQXpCO0FBQ0EsU0FBS0MsaUJBQUwsR0FBeUJBLGlCQUF6QjtBQUNEOztBQUVELFFBQU1PLEtBQU4sR0FBZTtBQUNiLFFBQUksS0FBS0wsV0FBVCxFQUFzQjtBQUNwQjtBQUNEOztBQUVELFNBQUtBLFdBQUwsR0FBbUJNLGFBQUlDLFlBQUosQ0FBaUIsTUFBT0MsV0FB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QXhFO0FBQ0Q7O0FBeEo0Qjs7O0FBMkovQixNQUFNeUMsMEJBQTBCLEdBQUcsSUFBSTFDLHdCQUFKLEVBQW5DOztlQUdlMEMsMEIiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgXyBmcm9tICdsb2Rhc2gnO1xuaW1wb3J0IG5ldCBmcm9tICduZXQnO1xuaW1wb3J0IEIgZnJvbSAnYmx1ZWJpcmQnO1xuaW1wb3J0IHsgbG9nZ2VyLCB1dGlsLCB0aW1pbmcgfSBmcm9tICdhcHBpdW0tc3VwcG9ydCc7XG5pbXBvcnQgeyB1dGlsaXRpZXMgfSBmcm9tICdnc3QtYXRvbS1pb3MtZGV2aWNlJztcbmltcG9ydCB7IGNoZWNrUG9ydFN0YXR1cyB9IGZyb20gJ3BvcnRzY2FubmVyJztcbmltcG9ydCB7IHdhaXRGb3JDb25kaXRpb24gfSBmcm9tICdhc3luY2JveCc7XG5cblxuY29uc3QgTE9DQUxIT1NUID0gJzEyNy4wLjAuMSc7XG5cbmNsYXNzIGlQcm94eSB7XG4gIGNvbnN0cnVjdG9yICh1ZGlkLCBsb2NhbHBvcnQsIGRldmljZXBvcnQsIHVzYm11eGRSZW1vdGVIb3N0LCB1c2JtdXhkUmVtb3RlUG9ydCkge1xuICAgIHRoaXMubG9jYWxwb3J0ID0gcGFyc2VJbnQobG9jYWxwb3J0LCAxMCk7XG4gICAgdGhpcy5kZXZpY2Vwb3J0ID0gcGFyc2VJbnQoZGV2aWNlcG9ydCwgMTApO1xuICAgIHRoaXMudWRpZCA9IHVkaWQ7XG4gICAgdGhpcy5sb2NhbFNlcnZlciA9IG51bGw7XG4gICAgdGhpcy5sb2cgPSBsb2dnZXIuZ2V0TG9nZ2VyKGBpUHJveHlAJHt1ZGlkLnN1YnN0cmluZygwLCA4KX06JHt0aGlzLmxvY2FscG9ydH1gKTtcbiAgICB0aGlzLnVzYm11eGRSZW1vdGVIb3N0ID0gdXNibXV4ZFJlbW90ZUhvc3Q7XG4gICAgdGhpcy51c2JtdXhkUmVtb3RlUG9ydCA9IHVzYm11eGRSZW1vdGVQb3J0O1xuICB9XG5cbiAgYXN5bmMgc3RhcnQgKCkge1xuICAgIGlmICh0aGlzLmxvY2FsU2VydmVyKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgdGhpcy5sb2NhbFNlcnZlciA9IG5ldC5jcmVhdGVTZXJ2ZXIoYXN5bmMgKGxvY2FsU29ja2V0KSA9PiB7XG4gICAgICBsZXQgcmVtb3RlU29ja2V0O1xuICAgICAgdHJ5IHtcbiAgICAgICAgLy8gV2UgY2FuIG9ubHkgY29ubmVjdCB0byB0aGUgcmVtb3RlIHNvY2tldCBhZnRlciB0aGUgbG9jYWwgc29ja2V0IGNvbm5lY3Rpb24gc3VjY2VlZHNcbiAgICAgICAgdmFyIG9wdGlvbnMgPSB7XG4gICAgICAgICAgdWRpZDogdGhpcy51ZGlkLFxuICAgICAgICAgIHBvcnQ6IHRoaXMuZGV2aWNlcG9ydCxcbiAgICAgICAgICB1c2JtdXhkUmVtb3RlSG9zdDogdGhpcy51c2JtdXhkUmVtb3RlSG9zdCxcbiAgICAgICAgICB1c2JtdXhkUmVtb3RlUG9ydDogdGhpcy51c2JtdXhkUmVtb3RlUG9ydFxuICAgICAgICB9ICAgICAgICBcbiAgICAgICAgcmVtb3RlU29ja2V0ID0gYXdhaXQgdXRpbGl0aWVzLmNvbm5lY3RQb3J0KG51bGwsIG9wdGlvbnMpO1xuICAgICAgfSBjYXRjaCAoZSkge1xuICAgICAgICB0aGlzLmxvZy5kZWJ1ZyhlLm1lc3NhZ2UpO1xuICAgICAgICBsb2NhbFNvY2tldC5kZXN0cm95KCk7XG4gICAgICAgIHJldHVybjtcbiAgICAgIH1cblxuICAgICAgY29uc3QgZGVzdHJveUNvbW1DaGFubmVsID0gKCkgPT4ge1xuICAgICAgICByZW1vdGVTb2NrZXQudW5waXBlKGxvY2FsU29ja2V0KTtcbiAgICAgICAgbG9jYWxTb2NrZXQudW5waXBlKHJlbW90ZVNvY2tldCk7XG4gICAgICB9O1xuICAgICAgcmVtb3RlU29ja2V0Lm9uY2UoJ2Nsb3NlJywgKCkgPT4ge1xuICAgICAgICBkZXN0cm95Q29tbUNoYW5uZWwoKTtcbiAgICAgICAgbG9jYWxTb2NrZXQuZGVzdHJveSgpO1xuICAgICAgfSk7XG4gICAgICAvLyBub3QgYWxsIHJlbW90ZSBzb2NrZXQgZXJyb3JzIGFyZSBjcml0aWNhbCBmb3IgdGhlIHVzZXJcbiAgICAgIHJlbW90ZVNvY2tldC5vbignZXJyb3InLCAoZSkgPT4gdGhpcy5sb2cuZGVidWcoZSkpO1xuICAgICAgbG9jYWxTb2NrZXQub25jZSgnZW5kJywgZGVzdHJveUNvbW1DaGFubmVsKTtcbiAgICAgIGxvY2FsU29ja2V0Lm9uY2UoJ2Nsb3NlJywgKCkgPT4ge1xuICAgICAgICBkZXN0cm95Q29tbUNoYW5uZWwoKTtcbiAgICAgICAgcmVtb3RlU29ja2V0LmRlc3Ryb3koKTtcbiAgICAgIH0pO1xuICAgICAgbG9jYWxTb2NrZXQub24oJ2Vycm9yJywgKGUpID0+IHRoaXMubG9nLndhcm4oZS5tZXNzYWdlKSk7XG4gICAgICBsb2NhbFNvY2tldC5waXBlKHJlbW90ZVNvY2tldCk7XG4gICAgICByZW1vdGVTb2NrZXQucGlwZShsb2NhbFNvY2tldCk7XG4gICAgfSk7XG4gICAgY29uc3QgbGlzdGVuaW5nUHJvbWlzZSA9IG5ldyBCKChyZXNvbHZlLCByZWplY3QpID0+IHtcbiAgICAgIHRoaXMubG9jYWxTZXJ2ZXIub25jZSgnbGlzdGVuaW5nJywgcmVzb2x2ZSk7XG4gICAgICB0aGlzLmxvY2FsU2VydmVyLm9uY2UoJ2Vycm9yJywgcmVqZWN0KTtcbiAgICB9KTtcbiAgICB0aGlzLmxvY2FsU2VydmVyLmxpc3Rlbih0aGlzLmxvY2FscG9ydCk7XG4gICAgdHJ5IHtcbiAgICAgIGF3YWl0IGxpc3RlbmluZ1Byb21pc2U7XG4gICAgfSBjYXRjaCAoZSkge1xuICAgICAgdGhpcy5sb2NhbFNlcnZlciA9IG51bGw7XG4gICAgICB0aHJvdyBlO1xuICAgIH1cbiAgICB0aGlzLmxvY2FsU2VydmVyLm9uKCdlcnJvcicsIChlKSA9PiB0aGlzLmxvZy53YXJuKGUubWVzc2FnZSkpO1xuICAgIHRoaXMubG9jYWxTZXJ2ZXIub25jZSgnY2xvc2UnLCAoZSkgPT4ge1xuICAgICAgaWYgKGUpIHtcbiAgICAgICAgdGhpcy5sb2cuaW5mbyhgVGhlIGNvbm5lY3Rpb24gaGFzIGJlZW4gY2xvc2VkIHdpdGggZXJyb3IgJHtlLm1lc3NhZ2V9YCk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICB0aGlzLmxvZy5pbmZvKGBUaGUgY29ubmVjdGlvbiBoYXMgYmVlbiBjbG9zZWRgKTtcbiAgICAgIH1cbiAgICAgIHRoaXMubG9jYWxTZXJ2ZXIgPSBudWxsO1xuICAgIH0pO1xuXG4gICAgdGhpcy5vbkJlZm9yZVByb2Nlc3NFeGl0ID0gdGhpcy5fY2xvc2VMb2NhbFNlcnZlci5iaW5kKHRoaXMpO1xuICAgIC8vIE1ha2Ugc3VyZSB3ZSBmcmVlIHVwIHRoZSBzb2NrZXQgb24gcHJvY2VzcyBleGl0XG4gICAgcHJvY2Vzcy5vbignYmVmb3JlRXhpdCcsIHRoaXMub25CZWZvcmVQcm9jZXNzRXhpdCk7XG4gIH1cblxuICBfY2xvc2VMb2NhbFNlcnZlciAoKSB7XG4gICAgaWYgKCF0aGlzLmxvY2FsU2VydmVyKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgdGhpcy5sb2cuZGVidWcoYENsb3NpbmcgdGhlIGNvbm5lY3Rpb25gKTtcbiAgICB0aGlzLmxvY2FsU2VydmVyLmNsb3NlKCk7XG4gICAgdGhpcy5sb2NhbFNlcnZlciA9IG51bGw7XG4gIH1cblxuICBzdG9wICgpIHtcbiAgICBpZiAodGhpcy5vbkJlZm9yZVByb2Nlc3NFeGl0KSB7XG4gICAgICBwcm9jZXNzLm9mZignYmVmb3JlRXhpdCcsIHRoaXMub25CZWZvcmVQcm9jZXNzRXhpdCk7XG4gICAgICB0aGlzLm9uQmVmb3JlUHJvY2Vzc0V4aXQgPSBudWxsO1xuICAgIH1cblxuICAgIHRoaXMuX2Nsb3NlTG9jYWxTZXJ2ZXIoKTtcbiAgfVxufVxuXG5cbmNvbnN0IGxvZyA9IGxvZ2dlci5nZXRMb2dnZXIoJ0RldkNvbiBGYWN0b3J5Jyk7XG5jb25zdCBQT1JUX0NMT1NFX1RJTUVPVVQgPSAxNSAqIDEwMDA7IC8vIDE1IHNlY29uZHNcbmNvbnN0IFNQTElUVEVSID0gJzonO1xuXG5jbGFzcyBEZXZpY2VDb25uZWN0aW9uc0ZhY3Rvcnkge1xuICBjb25zdHJ1Y3RvciAoKSB7XG4gICAgdGhpcy5fY29ubmVjdGlvbnNNYXBwaW5nID0ge307XG4gIH1cblxuICBfdWRpZEFzVG9rZW4gKHVkaWQpIHtcbiAgICByZXR1cm4gYCR7dXRpbC5oYXNWYWx1ZSh1ZGlkKSA/IHVkaWQgOiAnJ30ke1NQTElUVEVSfWA7XG4gIH1cblxuICBfcG9ydEFzVG9rZW4gKHBvcnQpIHtcbiAgICByZXR1cm4gYCR7U1BMSVRURVJ9JHt1dGlsLmhhc1ZhbHVlKHBvcnQpID8gcG9ydCA6ICcnfWA7XG4gIH1cblxuICBfdG9LZXkgKHVkaWQgPSBudWxsLCBwb3J0ID0gbnVsbCkge1xuICAgIHJldHVybiBgJHt1dGlsLmhhc1ZhbHVlKHVkaWQpID8gdWRpZCA6ICcnfSR7U1BMSVRURVJ9JHt1dGlsLmhhc1ZhbHVlKHBvcnQpID8gcG9ydCA6ICcnfWA7XG4gIH1cblxuICBfcmVsZWFzZVByb3hpZWRDb25uZWN0aW9ucyAoY29ubmVjdGlvbktleXMpIHtcbiAgICBjb25zdCBrZXlzID0gY29ubmVjdGlvbktleXNcbiAgICAgIC5maWx0ZXIoKGspID0+IF8uaGFzKHRoaXMuX2Nvbm5lY3Rpb25zTWFwcGluZ1trXSwgJ2lwcm94eScpKTtcbiAgICBmb3IgKGNvbnN0IGtleSBvZiBrZXlzKSB7XG4gICAgICBsb2cuaW5mbyhgUmVsZWFzaW5nIHRoZSBsaXN0ZW5lciBmb3IgJyR7a2V5fSdgKTtcbiAgICAgIHRyeSB7XG4gICAgICAgIHRoaXMuX2Nvbm5lY3Rpb25zTWFwcGluZ1trZXldLmlwcm94eS5zdG9wKCk7XG4gICAgICB9IGNhdGNoIChlKSB7XG4gICAgICAgIGxvZy5kZWJ1ZyhlKTtcbiAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIGtleXM7XG4gIH1cblxuICBsaXN0Q29ubmVjdGlvbnMgKHVkaWQgPSBudWxsLCBwb3J0ID0gbnVsbCwgc3RyaWN0ID0gZmFsc2UpIHtcbiAgICBpZiAoIXVkaWQgJiYgIXBvcnQpIHtcbiAgICAgIHJldHVybiBbXTtcbiAgICB9XG5cbiAgICAvLyBgdGhpcy5fY29ubmVjdGlvbk1hcHBpbmdgIGtleXMgaGF2ZSBmb3JtYXQgYHVkaWQ6cG9ydGBcbiAgICAvLyB0aGUgYHN0cmljdGAgYXJndW1lbnQgZW5mb3JjZXMgdG8gbWF0Y2gga2V5cyBoYXZpbmcgYm90aCBgdWRpZGAgYW5kIGBwb3J0YFxuICAgIC8vIGlmIHRoZXkgYXJlIGRlZmluZWRcbiAgICAvLyB3aGlsZSBpbiBub24tc3RyaWN0IG1vZGUga2V5cyBoYXZpbmcgYW55IG9mIHRoZXNlIGFyZSBnb2luZyB0byBiZSBtYXRjaGVkXG4gICAgcmV0dXJuIF8ua2V5cyh0aGlzLl9jb25uZWN0aW9uc01hcHBpbmcpXG4gICAgICAuZmlsdGVyKChrZXkpID0+IChzdHJpY3QgJiYgdWRpZCAmJiBwb3J0KVxuICAgICAgICA/IChrZXkgPT09IHRoaXMuX3RvS2V5KHVkaWQsIHBvcnQpKVxuICAgICAgICA6ICh1ZGlkICYmIGtleS5zdGFydHNXaXRoKHRoaXMuX3VkaWRBc1Rva2VuKHVkaWQpKSB8fCBwb3J0ICYmIGtleS5lbmRzV2l0aCh0aGlzLl9wb3J0QXNUb2tlbihwb3J0KSkpXG4gICAgICApO1xuICB9XG5cbiAgYXN5bmMgcmVxdWVzdENvbm5lY3Rpb24gKG9wdGlvbnMgPSB7fSkge1xuICAgIGNvbnN0IHtcbiAgICAgIHVkaWQsXG4gICAgICBwb3J0LFxuICAgICAgdXNlUG9ydEZvcndhcmRpbmcsXG4gICAgICBkZXZpY2VQb3J0LFxuICAgICAgdXNibXV4ZFJlbW90ZUhvc3QsXG4gICAgICB1c2JtdXhkUmVtb3RlUG9ydFxuICAgIH0gPSBvcHRpb25zO1xuXG4gICAgaWYgKCF1ZGlkIHx8ICFwb3J0KSB7XG4gICAgICBsb2cud2FybignRGlkIG5vdCBrbm93IGhvdyB0byByZXF1ZXN0IHRoZSBjb25uZWN0aW9uOicpO1xuICAgICAgaWYgKCF1ZGlkKSB7XG4gICAgICAgIGxvZy53YXJuKCctIERldmljZSBVRElEIGlzIHVuc2V0Jyk7XG4gICAgICB9XG4gICAgICBpZiAoIXBvcnQpIHtcbiAgICAgICAgbG9nLndhcm4oJy0gVGhlIGxvY2FsIHBvcnQgbnVtYmVyIGlzIHVuc2V0Jyk7XG4gICAgICB9XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgbG9nLmluZm8oYFJlcXVlc3RpbmcgY29ubmVjdGlvbiBmb3IgZGV2aWNlICR7dWRpZH0gb24gbG9jYWwgcG9ydCAke3BvcnR9YCArXG4gICAgICAoZGV2aWNlUG9ydCA/IGAsIGRldmljZSBwb3J0ICR7ZGV2aWNlUG9ydH1gIDogJycpKTtcbiAgICBsb2cuZGVidWcoYENhY2hlZCBjb25uZWN0aW9ucyBjb3VudDogJHtfLnNpemUodGhpcy5fY29ubmVjdGlvbnNNYXBwaW5nKX1gKTtcbiAgICBjb25zdCBjb25uZWN0aW9uc09uUG9ydCA9IHRoaXMubGlzdENvbm5lY3Rpb25zKG51bGwsIHBvcnQpO1xuICAgIGlmICghXy5pc0VtcHR5KGNvbm5lY3Rpb25zT25Qb3J0KSkge1xuICAgICAgbG9nLmluZm8oYEZvdW5kIGNhY2hlZCBjb25uZWN0aW9ucyBvbiBwb3J0ICMke3BvcnR9OiAke0pTT04uc3RyaW5naWZ5KGNvbm5lY3Rpb25zT25Qb3J0KX1gKTtcbiAgICB9XG5cbiAgICBpZiAodXNlUG9ydEZvcndhcmRpbmcpIHtcbiAgICAgIGxldCBpc1BvcnRCdXN5ID0gKGF3YWl0IGNoZWNrUG9ydFN0YXR1cyhwb3J0LCBMT0NBTEhPU1QpKSA9PT0gJ29wZW4nO1xuICAgICAgaWYgKGlzUG9ydEJ1c3kpIHtcbiAgICAgICAgbG9nLndhcm4oYFBvcnQgIyR7cG9ydH0gaXMgYnVzeS4gRGlkIHlvdSBxdWl0IHRoZSBwcmV2aW91cyBkcml2ZXIgc2Vzc2lvbihzKSBwcm9wZXJseT9gKTtcbiAgICAgICAgaWYgKCFfLmlzRW1wdHkoY29ubmVjdGlvbnNPblBvcnQpKSB7XG4gICAgICAgICAgbG9nLmluZm8oJ1RyeWluZyB0byByZWxlYXNlIHRoZSBwb3J0Jyk7XG4gICAgICAgICAgZm9yIChjb25zdCBrZXkgb2YgdGhpcy5fcmVsZWFzZVByb3hpZWRDb25uZWN0aW9ucyhjb25uZWN0aW9uc09uUG9ydCkpIHtcbiAgICAgICAgICAgIGRlbGV0ZSB0aGlzLl9jb25uZWN0aW9uc01hcHBpbmdba2V5XTtcbiAgICAgICAgICB9XG4gICAgICAgICAgY29uc3QgdGltZXIgPSBuZXcgdGltaW5nLlRpbWVyKCkuc3RhcnQoKTtcbiAgICAgICAgICB0cnkge1xuICAgICAgICAgICAgYXdhaXQgd2FpdEZvckNvbmRpdGlvbihhc3luYyAoKSA9PiB7XG4gICAgICAgICAgICAgIHRyeSB7XG4gICAgICAgICAgICAgICAgaWYgKChhd2FpdCBjaGVja1BvcnRTdGF0dXMocG9ydCwgTE9DQUxIT1NUKSkgIT09ICdvcGVuJykge1xuICAgICAgICAgICAgICAgICAgbG9nLmluZm8oYFBvcnQgIyR7cG9ydH0gaGFzIGJlZW4gc3VjY2Vzc2Z1bGx5IHJlbGVhc2VkIGFmdGVyIGAgK1xuICAgICAgICAgICAgICAgICAgICBgJHt0aW1lci5nZXREdXJhdGlvbigpLmFzTWlsbGlTZWNvbmRzLnRvRml4ZWQoMCl9bXNgKTtcbiAgICAgICAgICAgICAgICAgIGlzUG9ydEJ1c3kgPSBmYWxzZTtcbiAgICAgICAgICAgICAgICAgIHJldHVybiB0cnVlO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgfSBjYXRjaCAoaWduKSB7fVxuICAgICAgICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgICAgICAgICB9LCB7XG4gICAgICAgICAgICAgIHdhaXRNczogUE9SVF9DTE9TRV9USU1FT1VULFxuICAgICAgICAgICAgICBpbnRlcnZhbE1zOiAzMDAsXG4gICAgICAgICAgICB9KTtcbiAgICAgICAgICB9IGNhdGNoIChpZ24pIHtcbiAgICAgICAgICAgIGxvZy53YXJuKGBEaWQgbm90IGtub3cgaG93IHRvIHJlbGVhc2UgcG9ydCAjJHtwb3J0fSBpbiBgICtcbiAgICAgICAgICAgICAgYCR7dGltZXIuZ2V0RHVyYXRpb24oKS5hc01pbGxpU2Vjb25kcy50b0ZpeGVkKDApfW1zYCk7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9XG5cbiAgICAgIGlmIChpc1BvcnRCdXN5KSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihgVGhlIHBvcnQgIyR7cG9ydH0gaXMgb2NjdXBpZWQgYnkgYW4gb3RoZXIgcHJvY2Vzcy4gYCArXG4gICAgICAgICAgYFlvdSBjYW4gZWl0aGVyIHF1aXQgdGhhdCBwcm9jZXNzIG9yIHNlbGVjdCBhbm90aGVyIGZyZWUgcG9ydC5gKTtcbiAgICAgIH1cbiAgICB9XG4gICAgY29uc3QgY3VycmVudEtleSA9IHRoaXMuX3RvS2V5KHVkaWQsIHBvcnQpO1xuICAgIGlmICh1c2VQb3J0Rm9yd2FyZGluZykge1xuICAgICAgY29uc3QgaXByb3h5ID0gbmV3IGlQcm94eSh1ZGlkLCBwb3J0LCBkZXZpY2VQb3J0LCB1c2JtdXhkUmVtb3RlSG9zdCwgdXNibXV4ZFJlbW90ZVBvcnQpO1xuICAgICAgdHJ5IHtcbiAgICAgICAgYXdhaXQgaXByb3h5LnN0YXJ0KCk7XG4gICAgICAgIHRoaXMuX2Nvbm5lY3Rpb25zTWFwcGluZ1tjdXJyZW50S2V5XSA9IHtpcHJveHl9O1xuICAgICAgfSBjYXRjaCAoZSkge1xuICAgICAgICB0cnkge1xuICAgICAgICAgIGlwcm94eS5zdG9wKCk7XG4gICAgICAgIH0gY2F0Y2ggKGUxKSB7XG4gICAgICAgICAgbG9nLmRlYnVnKGUxKTtcbiAgICAgICAgfVxuICAgICAgICB0aHJvdyBlO1xuICAgICAgfVxuICAgIH0gZWxzZSB7XG4gICAgICB0aGlzLl9jb25uZWN0aW9uc01hcHBpbmdbY3VycmVudEtleV0gPSB7fTtcbiAgICB9XG4gICAgbG9nLmluZm8oYFN1Y2Nlc3NmdWxseSByZXF1ZXN0ZWQgdGhlIGNvbm5lY3Rpb24gZm9yICR7Y3VycmVudEtleX1gKTtcbiAgfVxuXG4gIHJlbGVhc2VDb25uZWN0aW9uICh1ZGlkID0gbnVsbCwgcG9ydCA9IG51bGwpIHtcbiAgICBpZiAoIXVkaWQgJiYgIXBvcnQpIHtcbiAgICAgIGxvZy53YXJuKCdOZWl0aGVyIGRldmljZSBVRElEIG5vciBsb2NhbCBwb3J0IGlzIHNldC4gJyArXG4gICAgICAgICdEaWQgbm90IGtub3cgaG93IHRvIHJlbGVhc2UgdGhlIGNvbm5lY3Rpb24nKTtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgbG9nLmluZm8oYFJlbGVhc2luZyBjb25uZWN0aW9ucyBmb3IgJHt1ZGlkIHx8ICdhbnknfSBkZXZpY2Ugb24gJHtwb3J0IHx8ICdhbnknfSBwb3J0IG51bWJlcmApO1xuXG4gICAgY29uc3Qga2V5cyA9IHRoaXMubGlzdENvbm5lY3Rpb25zKHVkaWQsIHBvcnQsIHRydWUpO1xuICAgIGlmIChfLmlzRW1wdHkoa2V5cykpIHtcbiAgICAgIGxvZy5pbmZvKCdObyBjYWNoZWQgY29ubmVjdGlvbnMgaGF2ZSBiZWVuIGZvdW5kJyk7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIGxvZy5pbmZvKGBGb3VuZCBjYWNoZWQgY29ubmVjdGlvbnMgdG8gcmVsZWFzZTogJHtKU09OLnN0cmluZ2lmeShrZXlzKX1gKTtcbiAgICB0aGlzLl9yZWxlYXNlUHJveGllZENvbm5lY3Rpb25zKGtleXMpO1xuICAgIGZvciAoY29uc3Qga2V5IG9mIGtleXMpIHtcbiAgICAgIGRlbGV0ZSB0aGlzLl9jb25uZWN0aW9uc01hcHBpbmdba2V5XTtcbiAgICB9XG4gICAgbG9nLmRlYnVnKGBDYWNoZWQgY29ubmVjdGlvbnMgY291bnQ6ICR7Xy5zaXplKHRoaXMuX2Nvbm5lY3Rpb25zTWFwcGluZyl9YCk7XG4gIH1cbn1cblxuY29uc3QgREVWSUNFX0NPTk5FQ1RJT05TX0ZBQ1RPUlkgPSBuZXcgRGV2aWNlQ29ubmVjdGlvbnNGYWN0b3J5KCk7XG5cbmV4cG9ydCB7IERFVklDRV9DT05ORUNUSU9OU19GQUNUT1JZLCBEZXZpY2VDb25uZWN0aW9uc0ZhY3RvcnkgfTtcbmV4cG9ydCBkZWZhdWx0IERFVklDRV9DT05ORUNUSU9OU19GQUNUT1JZO1xuIl0sImZpbGUiOiJsaWIvZGV2aWNlLWNvbm5lY3Rpb25zLWZhY3RvcnkuanMiLCJzb3VyY2VSb290IjoiLi4vLi4ifQ==