UNPKG

appium-webdriveragent-driver

Version:
678 lines (538 loc) 41 kB
'use strict'; var _get = require('babel-runtime/helpers/get')['default']; var _inherits = require('babel-runtime/helpers/inherits')['default']; 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 _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 _appiumBaseDriver = require('appium-base-driver'); var _appiumSupport = require('appium-support'); var _lodash = require('lodash'); var _lodash2 = _interopRequireDefault(_lodash); var _nodeSimctl = require('node-simctl'); var _iosAppUtils = require('ios-app-utils'); var _webdriveragent = require('./webdriveragent'); var _webdriveragent2 = _interopRequireDefault(_webdriveragent); var _logger = require('./logger'); var _logger2 = _interopRequireDefault(_logger); var _simulatorManagementJs = require('./simulatorManagement.js'); var _appiumIosSimulator = require('appium-ios-simulator'); var _asyncbox = require('asyncbox'); var _mobileJsonWireProtocol = require('mobile-json-wire-protocol'); var _appiumSafariDriver = require('appium-safari-driver'); var _commandsIndexJs = require('./commands/index.js'); var _commandsIndexJs2 = _interopRequireDefault(_commandsIndexJs); var SAFARI_BUNDLE_ID = 'com.apple.mobilesafari'; var WebDriverAgentDriver = (function (_BaseDriver) { _inherits(WebDriverAgentDriver, _BaseDriver); function WebDriverAgentDriver() { var opts = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0]; var shouldValidateCaps = arguments.length <= 1 || arguments[1] === undefined ? true : arguments[1]; _classCallCheck(this, WebDriverAgentDriver); _get(Object.getPrototypeOf(WebDriverAgentDriver.prototype), 'constructor', this).call(this, opts, shouldValidateCaps); // TODO set up desired cap constraints //this.desiredCapConstraints = desiredCapConstraints; this.wda = null; this.sim = null; this.jwpProxyActive = false; this.proxyReqRes = null; this.jwpProxyAvoid = []; this._safariDriver = null; // lazy-loaded in getter } _createClass(WebDriverAgentDriver, [{ key: 'getSafariDriver', value: function getSafariDriver() { var safariDriver; return _regeneratorRuntime.async(function getSafariDriver$(context$2$0) { while (1) switch (context$2$0.prev = context$2$0.next) { case 0: if (!this._safariDriver) { context$2$0.next = 2; break; } return context$2$0.abrupt('return', this._safariDriver); case 2: _logger2['default'].info('Creating Safari Driver'); safariDriver = new _appiumSafariDriver.SafariDriver(); context$2$0.prev = 4; context$2$0.next = 7; return _regeneratorRuntime.awrap(safariDriver.createSession(this.opts)); case 7: context$2$0.next = 14; break; case 9: context$2$0.prev = 9; context$2$0.t0 = context$2$0['catch'](4); _logger2['default'].warn('Safari Driver could not start a session, no webviews found'); _logger2['default'].debug(context$2$0.t0, context$2$0.t0.message); return context$2$0.abrupt('return', null); case 14: _logger2['default'].info('Safari Driver connected'); this._safariDriver = safariDriver; return context$2$0.abrupt('return', safariDriver); case 17: case 'end': return context$2$0.stop(); } }, null, this, [[4, 9]]); } }, { key: 'createSession', value: function createSession(caps) { var sessionId, _ref, _ref2, _ref3, udid, realDevice; return _regeneratorRuntime.async(function createSession$(context$2$0) { while (1) switch (context$2$0.prev = context$2$0.next) { case 0: this.lifecycleData = {}; // this is used for keeping track of the state we start so when we delete the session we can put things back context$2$0.prev = 1; sessionId = undefined; context$2$0.next = 5; return _regeneratorRuntime.awrap(_get(Object.getPrototypeOf(WebDriverAgentDriver.prototype), 'createSession', this).call(this, caps)); case 5: _ref = context$2$0.sent; _ref2 = _slicedToArray(_ref, 1); sessionId = _ref2[0]; _logger2['default'].info('Determining device to run tests on'); context$2$0.next = 11; return _regeneratorRuntime.awrap(this.determineDevice()); case 11: _ref3 = context$2$0.sent; udid = _ref3.udid; realDevice = _ref3.realDevice; this.opts.udid = udid; this.opts.isRealDevice = realDevice; if (!this.opts.browserName) { context$2$0.next = 22; break; } _logger2['default'].info('safari test requested'); this.safari = true; this.opts.bundleId = SAFARI_BUNDLE_ID; if (!this.opts.isRealDevice) { context$2$0.next = 22; break; } throw new Error('real device safari support not yet implemented. Contact an appium dev'); case 22: if (!this.opts.app) { context$2$0.next = 25; break; } context$2$0.next = 25; return _regeneratorRuntime.awrap(this.checkAppPresent()); case 25: if (this.opts.bundleId) { context$2$0.next = 29; break; } context$2$0.next = 28; return _regeneratorRuntime.awrap((0, _iosAppUtils.extractBundleId)(this.opts.app)); case 28: this.opts.bundleId = context$2$0.sent; case 29: if (this.opts.isRealDevice) { context$2$0.next = 34; break; } _logger2['default'].info('setting up simulator'); context$2$0.next = 33; return _regeneratorRuntime.awrap(this.startSim()); case 33: this.sim = context$2$0.sent; case 34: if (!(this.isSafari() && this.isSimulator())) { context$2$0.next = 38; break; } context$2$0.next = 37; return _regeneratorRuntime.awrap((0, _simulatorManagementJs.launchSafariOnSim)(this.opts.safariInitialUrl, this.sim)); case 37: return context$2$0.abrupt('return', [sessionId, caps]); case 38: this.wda = new _webdriveragent2['default']({ sim: this.sim, //TODO can't be a sim if real device. find a different way to supply logs to webdriveragent platformVersion: this.opts.platformVersion, host: this.opts.host, agentPath: this.opts.agentPath }); context$2$0.next = 41; return _regeneratorRuntime.awrap(this.wda.launch(sessionId)); case 41: this.proxyReqRes = this.wda.proxyReqRes.bind(this.wda); this.jwpProxyActive = true; context$2$0.next = 45; return _regeneratorRuntime.awrap(this.startWdaSession(this.opts.app, this.opts.bundleId)); case 45: return context$2$0.abrupt('return', [sessionId, caps]); case 48: context$2$0.prev = 48; context$2$0.t0 = context$2$0['catch'](1); _logger2['default'].error(context$2$0.t0); context$2$0.next = 53; return _regeneratorRuntime.awrap(this.deleteSession()); case 53: throw context$2$0.t0; case 54: case 'end': return context$2$0.stop(); } }, null, this, [[1, 48]]); } }, { key: 'deleteSession', value: function deleteSession() { var noReset; return _regeneratorRuntime.async(function deleteSession$(context$2$0) { while (1) switch (context$2$0.prev = context$2$0.next) { case 0: this.jwpProxyActive = false; this.proxyReqRes = null; noReset = this.opts.noReset; if (!this.wda) { context$2$0.next = 6; break; } context$2$0.next = 6; return _regeneratorRuntime.awrap(this.wda.quit()); case 6: if (!(!noReset && this.sim)) { context$2$0.next = 13; break; } if (!this.lifecycleData.bootSim) { context$2$0.next = 10; break; } context$2$0.next = 10; return _regeneratorRuntime.awrap(this.sim.shutdown()); case 10: if (!this.lifecycleData.createSim) { context$2$0.next = 13; break; } context$2$0.next = 13; return _regeneratorRuntime.awrap(this.sim['delete']()); case 13: context$2$0.next = 15; return _regeneratorRuntime.awrap(_get(Object.getPrototypeOf(WebDriverAgentDriver.prototype), 'deleteSession', this).call(this)); case 15: case 'end': return context$2$0.stop(); } }, null, this); } }, { key: 'checkAppPresent', value: function checkAppPresent() { return _regeneratorRuntime.async(function checkAppPresent$(context$2$0) { while (1) switch (context$2$0.prev = context$2$0.next) { case 0: _logger2['default'].debug("Checking whether app is actually present"); context$2$0.next = 3; return _regeneratorRuntime.awrap(_appiumSupport.fs.exists(this.opts.app)); case 3: if (context$2$0.sent) { context$2$0.next = 5; break; } _logger2['default'].errorAndThrow('Could not find app at ' + this.opts.app); case 5: case 'end': return context$2$0.stop(); } }, null, this); } }, { key: 'determineDevice', value: function determineDevice() { var realDevice, udid, sim; return _regeneratorRuntime.async(function determineDevice$(context$2$0) { while (1) switch (context$2$0.prev = context$2$0.next) { case 0: realDevice = undefined, udid = undefined; if (!this.opts.udid) { context$2$0.next = 13; break; } context$2$0.next = 4; return _regeneratorRuntime.awrap((0, _appiumIosSimulator.simExists)(this.opts.udid)); case 4: if (!context$2$0.sent) { context$2$0.next = 10; break; } this.lifecycleData.createSim = false; udid = this.opts.udid; realDevice = false; context$2$0.next = 11; break; case 10: throw new _mobileJsonWireProtocol.errors.SessionNotCreatedError('no simulator found with udid ' + this.opts.udid); case 11: context$2$0.next = 19; break; case 13: // no udid: create a new sim _logger2['default'].info('udid not provided, using desired caps to create a new sim'); context$2$0.next = 16; return _regeneratorRuntime.awrap(this.createSim()); case 16: sim = context$2$0.sent; udid = sim.udid; realDevice = false; case 19: return context$2$0.abrupt('return', { udid: udid, realDevice: realDevice }); case 20: case 'end': return context$2$0.stop(); } }, null, this); } }, { key: 'startSim', value: function startSim() { var sim; return _regeneratorRuntime.async(function startSim$(context$2$0) { while (1) switch (context$2$0.prev = context$2$0.next) { case 0: sim = undefined; context$2$0.next = 3; return _regeneratorRuntime.awrap((0, _appiumIosSimulator.getSimulator)(this.opts.udid)); case 3: sim = context$2$0.sent; context$2$0.next = 6; return _regeneratorRuntime.awrap((0, _simulatorManagementJs.simBooted)(this.opts.udid)); case 6: if (context$2$0.sent) { context$2$0.next = 15; break; } _logger2['default'].info('simulator with udid ' + this.opts.udid + ' not booted. Booting up now'); context$2$0.next = 10; return _regeneratorRuntime.awrap((0, _appiumIosSimulator.killAllSimulators)()); case 10: context$2$0.next = 12; return _regeneratorRuntime.awrap(sim.run()); case 12: this.lifecycleData.bootSim = true; context$2$0.next = 17; break; case 15: _logger2['default'].info('simulator ' + this.opts.udid + ' already booted'); this.lifecycleData.bootSim = false; case 17: return context$2$0.abrupt('return', sim); case 18: case 'end': return context$2$0.stop(); } }, null, this); } }, { key: 'createSim', value: function createSim() { var sim; return _regeneratorRuntime.async(function createSim$(context$2$0) { while (1) switch (context$2$0.prev = context$2$0.next) { case 0: this.lifecycleData.createSim = true; // create sim for caps context$2$0.next = 3; return _regeneratorRuntime.awrap((0, _simulatorManagementJs.createSim)(this.caps, this.sessionId)); case 3: sim = context$2$0.sent; _logger2['default'].info('created simulator ' + sim.udid + '. Booting it up'); return context$2$0.abrupt('return', sim); case 6: case 'end': return context$2$0.stop(); } }, null, this); } }, { key: 'launchApp', value: function launchApp() { var APP_LAUNCH_TIMEOUT, checkStatus; return _regeneratorRuntime.async(function launchApp$(context$2$0) { var _this = this; while (1) switch (context$2$0.prev = context$2$0.next) { case 0: APP_LAUNCH_TIMEOUT = 20 * 1000; context$2$0.next = 3; return _regeneratorRuntime.awrap((0, _nodeSimctl.launch)(this.sim.udid, this.opts.bundleId)); case 3: checkStatus = function checkStatus() { var response, currentApp; return _regeneratorRuntime.async(function checkStatus$(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.wda.jwproxy.command('/status', 'GET')); case 2: response = context$3$0.sent; currentApp = response.currentApp.bundleID; if (!(currentApp !== this.opts.bundleId)) { context$3$0.next = 6; break; } throw new Error(this.opts.bundleId + ' not in foreground. ' + currentApp + ' is in foreground'); case 6: case 'end': return context$3$0.stop(); } }, null, _this); }; _logger2['default'].info('waiting for ' + this.opts.bundleId + ' to be in foreground'); context$2$0.next = 7; return _regeneratorRuntime.awrap((0, _asyncbox.retryInterval)(APP_LAUNCH_TIMEOUT / 200, 200, checkStatus)); case 7: _logger2['default'].info(this.opts.bundleId + ' is in foreground'); case 8: case 'end': return context$2$0.stop(); } }, null, this); } }, { key: 'startWdaSession', value: function startWdaSession(appPath, bundleId) { var desired; return _regeneratorRuntime.async(function startWdaSession$(context$2$0) { while (1) switch (context$2$0.prev = context$2$0.next) { case 0: desired = { desiredCapabilities: { app: appPath, bundleId: bundleId } }; context$2$0.next = 3; return _regeneratorRuntime.awrap(this.wda.jwproxy.command('/session', 'POST', desired)); case 3: case 'end': return context$2$0.stop(); } }, null, this); } }, { key: 'shouldProxyToSafari', value: function shouldProxyToSafari(command) { var commandsToNotProxyToSafari = ['getContexts', 'setContext', 'getCurrentContext', 'deleteSession']; return this.inWebview() && (0, _mobileJsonWireProtocol.isSessionCommand)(command) && !_lodash2['default'].contains(commandsToNotProxyToSafari, command); } }, { key: 'executeCommand', value: function executeCommand(cmd) { var safariDriver, args$2$0 = arguments; return _regeneratorRuntime.async(function executeCommand$(context$2$0) { while (1) switch (context$2$0.prev = context$2$0.next) { case 0: if (!this.shouldProxyToSafari(cmd)) { context$2$0.next = 7; break; } context$2$0.next = 3; return _regeneratorRuntime.awrap(this.getSafariDriver()); case 3: safariDriver = context$2$0.sent; context$2$0.next = 6; return _regeneratorRuntime.awrap(safariDriver.executeCommand.apply(safariDriver, args$2$0)); case 6: return context$2$0.abrupt('return', context$2$0.sent); case 7: context$2$0.next = 9; return _regeneratorRuntime.awrap(_get(Object.getPrototypeOf(WebDriverAgentDriver.prototype), 'executeCommand', this).apply(this, args$2$0)); case 9: return context$2$0.abrupt('return', context$2$0.sent); case 10: case 'end': return context$2$0.stop(); } }, null, this); } // Override Proxy methods from BaseDriver }, { key: 'proxyActive', value: function proxyActive() { return this.jwpProxyActive; } }, { key: 'getProxyAvoidList', value: function getProxyAvoidList() { return [['GET', /context/], ['POST', /context/]]; } }, { key: 'canProxy', value: function canProxy() { return true; } }, { key: 'isSafari', value: function isSafari() { return this.safari; } }, { key: 'isRealDevice', value: function isRealDevice() { return this.opts.realDevice; } }, { key: 'isSimulator', value: function isSimulator() { return !this.opts.realDevice; } }, { key: 'inWebview', value: function inWebview() { return this.isSafari() || this.webview; } }, { key: 'driverData', get: function get() { // TODO fill out resource info here return {}; } }]); return WebDriverAgentDriver; })(_appiumBaseDriver.BaseDriver); var _iteratorNormalCompletion = true; var _didIteratorError = false; var _iteratorError = undefined; try { for (var _iterator = _getIterator(_lodash2['default'].pairs(_commandsIndexJs2['default'])), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { var _step$value = _slicedToArray(_step.value, 2); var cmd = _step$value[0]; var fn = _step$value[1]; WebDriverAgentDriver.prototype[cmd] = fn; } } catch (err) { _didIteratorError = true; _iteratorError = err; } finally { try { if (!_iteratorNormalCompletion && _iterator['return']) { _iterator['return'](); } } finally { if (_didIteratorError) { throw _iteratorError; } } } exports.WebDriverAgentDriver = WebDriverAgentDriver; // TODO add validation on caps // TODO handle otherSessionData for multiple sessions // fail very early if the app doesn't actually exist // TODO: this kills all simulators, so needs to be changed if running multiple sims //TODO check for real device // TODO for now just kill all sims unless specified udid is booted. // if booted, use it. if not booted, start it up // if no udid, well lets see if we can start one up based on desired caps // if we support multiple sims we need to change this //# sourceMappingURL=data:application/json;base64,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NlExQixvQ0FBc0Isb0JBQUUsS0FBSyw4QkFBVSw0R0FBRTs7O1FBQS9CLEdBQUc7UUFBRSxFQUFFOztBQUNmLHdCQUFvQixDQUFDLFNBQVMsQ0FBQyxHQUFHLENBQUMsR0FBRyxFQUFFLENBQUM7R0FDMUM7Ozs7Ozs7Ozs7Ozs7Ozs7UUFFUSxvQkFBb0IsR0FBcEIsb0JBQW9CIiwiZmlsZSI6ImxpYi9kcml2ZXIuanMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBCYXNlRHJpdmVyIH0gZnJvbSAnYXBwaXVtLWJhc2UtZHJpdmVyJztcbmltcG9ydCB7IGZzIH0gZnJvbSAnYXBwaXVtLXN1cHBvcnQnO1xuaW1wb3J0IF8gZnJvbSAnbG9kYXNoJztcbmltcG9ydCB7IGxhdW5jaCB9IGZyb20gJ25vZGUtc2ltY3RsJztcbmltcG9ydCB7IGV4dHJhY3RCdW5kbGVJZCB9IGZyb20gJ2lvcy1hcHAtdXRpbHMnO1xuaW1wb3J0IFdlYkRyaXZlckFnZW50IGZyb20gJy4vd2ViZHJpdmVyYWdlbnQnO1xuaW1wb3J0IGxvZyBmcm9tICcuL2xvZ2dlcic7XG5pbXBvcnQgeyBzaW1Cb290ZWQsIGNyZWF0ZVNpbSwgbGF1bmNoU2FmYXJpT25TaW0gfSBmcm9tICcuL3NpbXVsYXRvck1hbmFnZW1lbnQuanMnO1xuaW1wb3J0IHsga2lsbEFsbFNpbXVsYXRvcnMsIGdldFNpbXVsYXRvciwgc2ltRXhpc3RzIH0gZnJvbSAnYXBwaXVtLWlvcy1zaW11bGF0b3InO1xuaW1wb3J0IHsgcmV0cnlJbnRlcnZhbCB9IGZyb20gJ2FzeW5jYm94JztcbmltcG9ydCB7IGVycm9ycywgaXNTZXNzaW9uQ29tbWFuZCB9IGZyb20gJ21vYmlsZS1qc29uLXdpcmUtcHJvdG9jb2wnO1xuaW1wb3J0IHsgU2FmYXJpRHJpdmVyIH0gZnJvbSAnYXBwaXVtLXNhZmFyaS1kcml2ZXInO1xuaW1wb3J0IGNvbW1hbmRzIGZyb20gJy4vY29tbWFuZHMvaW5kZXguanMnO1xuXG5jb25zdCBTQUZBUklfQlVORExFX0lEID0gJ2NvbS5hcHBsZS5tb2JpbGVzYWZhcmknO1xuXG5jbGFzcyBXZWJEcml2ZXJBZ2VudERyaXZlciBleHRlbmRzIEJhc2VEcml2ZXIge1xuICBjb25zdHJ1Y3RvciAob3B0cyA9IHt9LCBzaG91bGRWYWxpZGF0ZUNhcHMgPSB0cnVlKSB7XG4gICAgc3VwZXIob3B0cywgc2hvdWxkVmFsaWRhdGVDYXBzKTtcblxuICAgIC8vIFRPRE8gc2V0IHVwIGRlc2lyZWQgY2FwIGNvbnN0cmFpbnRzXG4gICAgLy90aGlzLmRlc2lyZWRDYXBDb25zdHJhaW50cyA9IGRlc2lyZWRDYXBDb25zdHJhaW50cztcbiAgICB0aGlzLndkYSA9IG51bGw7XG4gICAgdGhpcy5zaW0gPSBudWxsO1xuICAgIHRoaXMuandwUHJveHlBY3RpdmUgPSBmYWxzZTtcbiAgICB0aGlzLnByb3h5UmVxUmVzID0gbnVsbDtcbiAgICB0aGlzLmp3cFByb3h5QXZvaWQgPSBbXTtcbiAgICB0aGlzLl9zYWZhcmlEcml2ZXIgPSBudWxsOyAvLyBsYXp5LWxvYWRlZCBpbiBnZXR0ZXJcbiAgfVxuXG4gIGdldCBkcml2ZXJEYXRhICgpIHtcbiAgICAvLyBUT0RPIGZpbGwgb3V0IHJlc291cmNlIGluZm8gaGVyZVxuICAgIHJldHVybiB7fTtcbiAgfVxuXG4gIGFzeW5jIGdldFNhZmFyaURyaXZlciAoKSB7XG4gICAgaWYgKHRoaXMuX3NhZmFyaURyaXZlcikge1xuICAgICAgcmV0dXJuIHRoaXMuX3NhZmFyaURyaXZlcjtcbiAgICB9XG5cbiAgICBsb2cuaW5mbygnQ3JlYXRpbmcgU2FmYXJpIERyaXZlcicpO1xuICAgIGxldCBzYWZhcmlEcml2ZXIgPSBuZXcgU2FmYXJpRHJpdmVyKCk7XG5cbiAgICB0cnkge1xuICAgICAgYXdhaXQgc2FmYXJpRHJpdmVyLmNyZWF0ZVNlc3Npb24odGhpcy5vcHRzKTtcbiAgICB9IGNhdGNoIChlKSB7XG4gICAgICBsb2cud2FybignU2FmYXJpIERyaXZlciBjb3VsZCBub3Qgc3RhcnQgYSBzZXNzaW9uLCBubyB3ZWJ2aWV3cyBmb3VuZCcpO1xuICAgICAgbG9nLmRlYnVnKGUsIGUubWVzc2FnZSk7XG4gICAgICByZXR1cm4gbnVsbDtcbiAgICB9XG4gICAgbG9nLmluZm8oJ1NhZmFyaSBEcml2ZXIgY29ubmVjdGVkJyk7XG4gICAgdGhpcy5fc2FmYXJpRHJpdmVyID0gc2FmYXJpRHJpdmVyO1xuICAgIHJldHVybiBzYWZhcmlEcml2ZXI7XG4gIH1cblxuICBhc3luYyBjcmVhdGVTZXNzaW9uIChjYXBzKSB7XG4gICAgdGhpcy5saWZlY3ljbGVEYXRhID0ge307IC8vIHRoaXMgaXMgdXNlZCBmb3Iga2VlcGluZyB0cmFjayBvZiB0aGUgc3RhdGUgd2Ugc3RhcnQgc28gd2hlbiB3ZSBkZWxldGUgdGhlIHNlc3Npb24gd2UgY2FuIHB1dCB0aGluZ3MgYmFja1xuICAgIHRyeSB7XG4gICAgICAvLyBUT0RPIGFkZCB2YWxpZGF0aW9uIG9uIGNhcHNcbiAgICAgIC8vIFRPRE8gaGFuZGxlIG90aGVyU2Vzc2lvbkRhdGEgZm9yIG11bHRpcGxlIHNlc3Npb25zXG4gICAgICBsZXQgc2Vzc2lvbklkO1xuICAgICAgW3Nlc3Npb25JZF0gPSBhd2FpdCBzdXBlci5jcmVhdGVTZXNzaW9uKGNhcHMpO1xuXG4gICAgICBsb2cuaW5mbygnRGV0ZXJtaW5pbmcgZGV2aWNlIHRvIHJ1biB0ZXN0cyBvbicpO1xuICAgICAgbGV0IHsgdWRpZCwgcmVhbERldmljZSB9ID0gYXdhaXQgdGhpcy5kZXRlcm1pbmVEZXZpY2UoKTtcbiAgICAgIHRoaXMub3B0cy51ZGlkID0gdWRpZDtcbiAgICAgIHRoaXMub3B0cy5pc1JlYWxEZXZpY2UgPSByZWFsRGV2aWNlO1xuXG4gICAgICBpZiAodGhpcy5vcHRzLmJyb3dzZXJOYW1lKSB7XG4gICAgICAgIGxvZy5pbmZvKCdzYWZhcmkgdGVzdCByZXF1ZXN0ZWQnKTtcbiAgICAgICAgdGhpcy5zYWZhcmkgPSB0cnVlO1xuICAgICAgICB0aGlzLm9wdHMuYnVuZGxlSWQgPSBTQUZBUklfQlVORExFX0lEO1xuICAgICAgICBpZiAodGhpcy5vcHRzLmlzUmVhbERldmljZSkge1xuICAgICAgICAgIHRocm93IG5ldyBFcnJvcigncmVhbCBkZXZpY2Ugc2FmYXJpIHN1cHBvcnQgbm90IHlldCBpbXBsZW1lbnRlZC4gQ29udGFjdCBhbiBhcHBpdW0gZGV2Jyk7XG4gICAgICAgIH1cbiAgICAgIH1cblxuICAgICAgLy8gZmFpbCB2ZXJ5IGVhcmx5IGlmIHRoZSBhcHAgZG9lc24ndCBhY3R1YWxseSBleGlzdFxuICAgICAgaWYgKHRoaXMub3B0cy5hcHApIHtcbiAgICAgICAgYXdhaXQgdGhpcy5jaGVja0FwcFByZXNlbnQoKTtcbiAgICAgIH1cblxuICAgICAgaWYgKCF0aGlzLm9wdHMuYnVuZGxlSWQpIHtcbiAgICAgICAgdGhpcy5vcHRzLmJ1bmRsZUlkID0gYXdhaXQgZXh0cmFjdEJ1bmRsZUlkKHRoaXMub3B0cy5hcHApO1xuICAgICAgfVxuXG4gICAgICBpZiAoIXRoaXMub3B0cy5pc1JlYWxEZXZpY2UpIHtcbiAgICAgICAgbG9nLmluZm8oJ3NldHRpbmcgdXAgc2ltdWxhdG9yJyk7XG4gICAgICAgIHRoaXMuc2ltID0gYXdhaXQgdGhpcy5zdGFydFNpbSgpO1xuICAgICAgfVxuXG4gICAgICBpZiAodGhpcy5pc1NhZmFyaSgpICYmIHRoaXMuaXNTaW11bGF0b3IoKSkge1xuICAgICAgICBhd2FpdCBsYXVuY2hTYWZhcmlPblNpbSh0aGlzLm9wdHMuc2FmYXJpSW5pdGlhbFVybCwgdGhpcy5zaW0pO1xuICAgICAgICByZXR1cm4gW3Nlc3Npb25JZCwgY2Fwc107XG4gICAgICB9XG5cbiAgICAgIHRoaXMud2RhID0gbmV3IFdlYkRyaXZlckFnZW50KHtcbiAgICAgICAgc2ltOiB0aGlzLnNpbSwgLy9UT0RPIGNhbid0IGJlIGEgc2ltIGlmIHJlYWwgZGV2aWNlLiBmaW5kIGEgZGlmZmVyZW50IHdheSB0byBzdXBwbHkgbG9ncyB0byB3ZWJkcml2ZXJhZ2VudFxuICAgICAgICBwbGF0Zm9ybVZlcnNpb246IHRoaXMub3B0cy5wbGF0Zm9ybVZlcnNpb24sXG4gICAgICAgIGhvc3Q6IHRoaXMub3B0cy5ob3N0LFxuICAgICAgICBhZ2VudFBhdGg6IHRoaXMub3B0cy5hZ2VudFBhdGhcbiAgICAgIH0pO1xuXG4gICAgICBhd2FpdCB0aGlzLndkYS5sYXVuY2goc2Vzc2lvbklkKTtcblxuICAgICAgdGhpcy5wcm94eVJlcVJlcyA9IHRoaXMud2RhLnByb3h5UmVxUmVzLmJpbmQodGhpcy53ZGEpO1xuICAgICAgdGhpcy5qd3BQcm94eUFjdGl2ZSA9IHRydWU7XG5cbiAgICAgIGF3YWl0IHRoaXMuc3RhcnRXZGFTZXNzaW9uKHRoaXMub3B0cy5hcHAsIHRoaXMub3B0cy5idW5kbGVJZCk7XG5cbiAgICAgIHJldHVybiBbc2Vzc2lvbklkLCBjYXBzXTtcbiAgICB9IGNhdGNoIChlKSB7XG4gICAgICBsb2cuZXJyb3IoZSk7XG4gICAgICBhd2FpdCB0aGlzLmRlbGV0ZVNlc3Npb24oKTtcbiAgICAgIHRocm93IGU7XG4gICAgfVxuICB9XG5cbiAgYXN5bmMgZGVsZXRlU2Vzc2lvbiAoKSB7XG4gICAgdGhpcy5qd3BQcm94eUFjdGl2ZSA9IGZhbHNlO1xuICAgIHRoaXMucHJveHlSZXFSZXMgPSBudWxsO1xuXG4gICAgbGV0IG5vUmVzZXQgPSB0aGlzLm9wdHMubm9SZXNldDtcblxuICAgIGlmICh0aGlzLndkYSkge1xuICAgICAgYXdhaXQgdGhpcy53ZGEucXVpdCgpO1xuICAgIH1cbiAgICAvLyBUT0RPOiB0aGlzIGtpbGxzIGFsbCBzaW11bGF0b3JzLCBzbyBuZWVkcyB0byBiZSBjaGFuZ2VkIGlmIHJ1bm5pbmcgbXVsdGlwbGUgc2ltc1xuICAgIGlmICghbm9SZXNldCAmJiB0aGlzLnNpbSkge1xuICAgICAgaWYgKHRoaXMubGlmZWN5Y2xlRGF0YS5ib290U2ltKSB7XG4gICAgICAgIGF3YWl0IHRoaXMuc2ltLnNodXRkb3duKCk7XG4gICAgICB9XG4gICAgICBpZiAodGhpcy5saWZlY3ljbGVEYXRhLmNyZWF0ZVNpbSkge1xuICAgICAgICBhd2FpdCB0aGlzLnNpbS5kZWxldGUoKTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICBhd2FpdCBzdXBlci5kZWxldGVTZXNzaW9uKCk7XG4gIH1cblxuICBhc3luYyBjaGVja0FwcFByZXNlbnQgKCkge1xuICAgIGxvZy5kZWJ1ZyhcIkNoZWNraW5nIHdoZXRoZXIgYXBwIGlzIGFjdHVhbGx5IHByZXNlbnRcIik7XG4gICAgaWYgKCEoYXdhaXQgZnMuZXhpc3RzKHRoaXMub3B0cy5hcHApKSkge1xuICAgICAgbG9nLmVycm9yQW5kVGhyb3coYENvdWxkIG5vdCBmaW5kIGFwcCBhdCAke3RoaXMub3B0cy5hcHB9YCk7XG4gICAgfVxuICB9XG5cbiAgYXN5bmMgZGV0ZXJtaW5lRGV2aWNlKCkge1xuICAgIGxldCByZWFsRGV2aWNlLCB1ZGlkO1xuXG4gICAgaWYgKHRoaXMub3B0cy51ZGlkKSB7XG4gICAgICBpZiAoYXdhaXQgc2ltRXhpc3RzKHRoaXMub3B0cy51ZGlkKSkge1xuICAgICAgICB0aGlzLmxpZmVjeWNsZURhdGEuY3JlYXRlU2ltID0gZmFsc2U7XG4gICAgICAgIHVkaWQgPSB0aGlzLm9wdHMudWRpZDtcbiAgICAgICAgcmVhbERldmljZSA9IGZhbHNlO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAvL1RPRE8gY2hlY2sgZm9yIHJlYWwgZGV2aWNlXG4gICAgICAgICAgdGhyb3cgbmV3IGVycm9ycy5TZXNzaW9uTm90Q3JlYXRlZEVycm9yKGBubyBzaW11bGF0b3IgZm91bmQgd2l0aCB1ZGlkICR7dGhpcy5vcHRzLnVkaWR9YCk7XG4gICAgICB9XG4gICAgfSBlbHNlIHtcbiAgICAgIC8vIG5vIHVkaWQ6IGNyZWF0ZSBhIG5ldyBzaW1cbiAgICAgIGxvZy5pbmZvKGB1ZGlkIG5vdCBwcm92aWRlZCwgdXNpbmcgZGVzaXJlZCBjYXBzIHRvIGNyZWF0ZSBhIG5ldyBzaW1gKTtcbiAgICAgIGxldCBzaW0gPSBhd2FpdCB0aGlzLmNyZWF0ZVNpbSgpO1xuICAgICAgdWRpZCA9IHNpbS51ZGlkO1xuICAgICAgcmVhbERldmljZSA9IGZhbHNlO1xuICAgIH1cblxuICAgIHJldHVybiB7IHVkaWQsIHJlYWxEZXZpY2UgfTtcbiAgfVxuXG4gIGFzeW5jIHN0YXJ0U2ltICgpIHtcbiAgICBsZXQgc2ltO1xuICAgIC8vIFRPRE8gZm9yIG5vdyBqdXN0IGtpbGwgYWxsIHNpbXMgdW5sZXNzIHNwZWNpZmllZCB1ZGlkIGlzIGJvb3RlZC5cbiAgICAvLyBpZiBib290ZWQsIHVzZSBpdC4gaWYgbm90IGJvb3RlZCwgc3RhcnQgaXQgdXBcbiAgICAvLyBpZiBubyB1ZGlkLCB3ZWxsIGxldHMgc2VlIGlmIHdlIGNhbiBzdGFydCBvbmUgdXAgYmFzZWQgb24gZGVzaXJlZCBjYXBzXG4gICAgLy8gaWYgd2Ugc3VwcG9ydCBtdWx0aXBsZSBzaW1zIHdlIG5lZWQgdG8gY2hhbmdlIHRoaXNcblxuICAgIHNpbSA9IGF3YWl0IGdldFNpbXVsYXRvcih0aGlzLm9wdHMudWRpZCk7XG4gICAgaWYgKCFhd2FpdCBzaW1Cb290ZWQodGhpcy5vcHRzLnVkaWQpKSB7XG4gICAgICBsb2cuaW5mbyhgc2ltdWxhdG9yIHdpdGggdWRpZCAke3RoaXMub3B0cy51ZGlkfSBub3QgYm9vdGVkLiBCb290aW5nIHVwIG5vd2ApO1xuICAgICAgYXdhaXQga2lsbEFsbFNpbXVsYXRvcnMoKTtcbiAgICAgIGF3YWl0IHNpbS5ydW4oKTtcbiAgICAgIHRoaXMubGlmZWN5Y2xlRGF0YS5ib290U2ltID0gdHJ1ZTtcbiAgICB9IGVsc2Uge1xuICAgICAgbG9nLmluZm8oYHNpbXVsYXRvciAke3RoaXMub3B0cy51ZGlkfSBhbHJlYWR5IGJvb3RlZGApO1xuICAgICAgdGhpcy5saWZlY3ljbGVEYXRhLmJvb3RTaW0gPSBmYWxzZTtcbiAgICB9XG4gICAgcmV0dXJuIHNpbTtcbiAgfVxuXG4gIGFzeW5jIGNyZWF0ZVNpbSAoKSB7XG5cbiAgICB0aGlzLmxpZmVjeWNsZURhdGEuY3JlYXRlU2ltID0gdHJ1ZTtcblxuICAgIC8vIGNyZWF0ZSBzaW0gZm9yIGNhcHNcbiAgICBsZXQgc2ltID0gYXdhaXQgY3JlYXRlU2ltKHRoaXMuY2FwcywgdGhpcy5zZXNzaW9uSWQpO1xuICAgIGxvZy5pbmZvKGBjcmVhdGVkIHNpbXVsYXRvciAke3NpbS51ZGlkfS4gQm9vdGluZyBpdCB1cGApO1xuXG4gICAgcmV0dXJuIHNpbTtcbiAgfVxuXG4gIGFzeW5jIGxhdW5jaEFwcCAoKSB7XG4gICAgY29uc3QgQVBQX0xBVU5DSF9USU1FT1VUID0gMjAgKiAxMDAwO1xuXG4gICAgYXdhaXQgbGF1bmNoKHRoaXMuc2ltLnVkaWQsIHRoaXMub3B0cy5idW5kbGVJZCk7XG5cbiAgICBsZXQgY2hlY2tTdGF0dXMgPSBhc3luYyAoKSA9PiB7XG4gICAgICBsZXQgcmVzcG9uc2UgPSBhd2FpdCB0aGlzLndkYS5qd3Byb3h5LmNvbW1hbmQoJy9zdGF0dXMnLCAnR0VUJyk7XG4gICAgICBsZXQgY3VycmVudEFwcCA9IHJlc3BvbnNlLmN1cnJlbnRBcHAuYnVuZGxlSUQ7XG4gICAgICBpZiAoY3VycmVudEFwcCAhPT0gdGhpcy5vcHRzLmJ1bmRsZUlkKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihgJHt0aGlzLm9wdHMuYnVuZGxlSWR9IG5vdCBpbiBmb3JlZ3JvdW5kLiAke2N1cnJlbnRBcHB9IGlzIGluIGZvcmVncm91bmRgKTtcbiAgICAgIH1cbiAgICB9O1xuXG4gICAgbG9nLmluZm8oYHdhaXRpbmcgZm9yICR7dGhpcy5vcHRzLmJ1bmRsZUlkfSB0byBiZSBpbiBmb3JlZ3JvdW5kYCk7XG4gICAgYXdhaXQgcmV0cnlJbnRlcnZhbChBUFBfTEFVTkNIX1RJTUVPVVQgLyAyMDAsIDIwMCwgY2hlY2tTdGF0dXMpO1xuICAgIGxvZy5pbmZvKGAke3RoaXMub3B0cy5idW5kbGVJZH0gaXMgaW4gZm9yZWdyb3VuZGApO1xuICB9XG5cbiAgYXN5bmMgc3RhcnRXZGFTZXNzaW9uKGFwcFBhdGgsIGJ1bmRsZUlkKSB7XG4gICAgbGV0IGRlc2lyZWQgPSB7XG4gICAgICBkZXNpcmVkQ2FwYWJpbGl0aWVzOiB7XG4gICAgICAgIGFwcDogYXBwUGF0aCxcbiAgICAgICAgYnVuZGxlSWQ6IGJ1bmRsZUlkXG4gICAgICB9XG4gICAgfTtcbiAgICBhd2FpdCB0aGlzLndkYS5qd3Byb3h5LmNvbW1hbmQoJy9zZXNzaW9uJywgJ1BPU1QnLCBkZXNpcmVkKTtcbiAgfVxuXG4gIHNob3VsZFByb3h5VG9TYWZhcmkgKGNvbW1hbmQpIHtcbiAgICBsZXQgY29tbWFuZHNUb05vdFByb3h5VG9TYWZhcmkgPSBbXG4gICAgICAnZ2V0Q29udGV4dHMnLFxuICAgICAgJ3NldENvbnRleHQnLFxuICAgICAgJ2dldEN1cnJlbnRDb250ZXh0JyxcbiAgICAgICdkZWxldGVTZXNzaW9uJ1xuICAgIF07XG5cbiAgICByZXR1cm4gdGhpcy5pbldlYnZpZXcoKSAmJlxuICAgICAgICAgICBpc1Nlc3Npb25Db21tYW5kKGNvbW1hbmQpICYmXG4gICAgICAgICAgICFfLmNvbnRhaW5zKGNvbW1hbmRzVG9Ob3RQcm94eVRvU2FmYXJpLCBjb21tYW5kKTtcbiAgfVxuXG4gIGFzeW5jIGV4ZWN1dGVDb21tYW5kIChjbWQpIHtcbiAgICBpZiAodGhpcy5zaG91bGRQcm94eVRvU2FmYXJpKGNtZCkpIHtcbiAgICAgIGxldCBzYWZhcmlEcml2ZXIgPSBhd2FpdCB0aGlzLmdldFNhZmFyaURyaXZlcigpO1xuICAgICAgcmV0dXJuIGF3YWl0IHNhZmFyaURyaXZlci5leGVjdXRlQ29tbWFuZCguLi5hcmd1bWVudHMpO1xuICAgIH1cblxuICAgIHJldHVybiBhd2FpdCBzdXBlci5leGVjdXRlQ29tbWFuZCguLi5hcmd1bWVudHMpO1xuICB9XG5cbiAgLy8gT3ZlcnJpZGUgUHJveHkgbWV0aG9kcyBmcm9tIEJhc2VEcml2ZXJcbiAgcHJveHlBY3RpdmUgKCkge1xuICAgIHJldHVybiB0aGlzLmp3cFByb3h5QWN0aXZlO1xuICB9XG5cbiAgZ2V0UHJveHlBdm9pZExpc3QgKCkge1xuICAgIHJldHVybiBbXG4gICAgICBbJ0dFVCcsIC9jb250ZXh0L10sXG4gICAgICBbJ1BPU1QnLCAvY29udGV4dC9dXG4gICAgXTtcbiAgfVxuXG4gIGNhblByb3h5ICgpIHtcbiAgICByZXR1cm4gdHJ1ZTtcbiAgfVxuXG4gIGlzU2FmYXJpICgpIHtcbiAgICByZXR1cm4gdGhpcy5zYWZhcmk7XG4gIH1cblxuICBpc1JlYWxEZXZpY2UgKCkge1xuICAgIHJldHVybiB0aGlzLm9wdHMucmVhbERldmljZTtcbiAgfVxuXG4gIGlzU2ltdWxhdG9yICgpIHtcbiAgICByZXR1cm4gIXRoaXMub3B0cy5yZWFsRGV2aWNlO1xuICB9XG5cbiAgaW5XZWJ2aWV3ICgpIHtcbiAgICByZXR1cm4gdGhpcy5pc1NhZmFyaSgpIHx8IHRoaXMud2VidmlldztcbiAgfVxuXG59XG5cbmZvciAobGV0IFtjbWQsIGZuXSBvZiBfLnBhaXJzKGNvbW1hbmRzKSkge1xuICBXZWJEcml2ZXJBZ2VudERyaXZlci5wcm90b3R5cGVbY21kXSA9IGZuO1xufVxuXG5leHBvcnQgeyBXZWJEcml2ZXJBZ2VudERyaXZlciB9O1xuIl19