gst-atom-xcuitest-driver
Version:
ATOM driver for iOS using XCUITest for backend
450 lines (335 loc) • 50.8 kB
JavaScript
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.detectUdid = detectUdid;
exports.getAndCheckXcodeVersion = getAndCheckXcodeVersion;
exports.getAndCheckIosSdkVersion = getAndCheckIosSdkVersion;
exports.getGenericSimulatorForIosVersion = getGenericSimulatorForIosVersion;
exports.checkAppPresent = checkAppPresent;
exports.getDriverInfo = getDriverInfo;
exports.clearSystemFiles = clearSystemFiles;
exports.translateDeviceName = translateDeviceName;
exports.normalizeCommandTimeouts = normalizeCommandTimeouts;
exports.markSystemFilesForCleanup = markSystemFilesForCleanup;
exports.printUser = printUser;
exports.getPIDsListeningOnPort = getPIDsListeningOnPort;
exports.encodeBase64OrUpload = encodeBase64OrUpload;
exports.removeAllSessionWebSocketHandlers = removeAllSessionWebSocketHandlers;
exports.verifyApplicationPlatform = verifyApplicationPlatform;
exports.isLocalHost = isLocalHost;
exports.normalizePlatformVersion = normalizePlatformVersion;
exports.DEFAULT_TIMEOUT_KEY = void 0;
require("source-map-support/register");
var _bluebird = _interopRequireDefault(require("bluebird"));
var _gstAtomIosDevice = require("gst-atom-ios-device");
var _appiumSupport = require("appium-support");
var _path = _interopRequireDefault(require("path"));
var _gstAtomIosDriver = require("gst-atom-ios-driver");
var _teen_process = require("teen_process");
var _appiumXcode = _interopRequireDefault(require("appium-xcode"));
var _lodash = _interopRequireDefault(require("lodash"));
var _logger = _interopRequireDefault(require("./logger"));
var _iosGenericSimulators = _interopRequireDefault(require("./ios-generic-simulators"));
var _url = _interopRequireDefault(require("url"));
var _os = _interopRequireDefault(require("os"));
var _semver = _interopRequireDefault(require("semver"));
const DEFAULT_TIMEOUT_KEY = 'default';
exports.DEFAULT_TIMEOUT_KEY = DEFAULT_TIMEOUT_KEY;
const XCTEST_LOG_FILES_PATTERNS = [/^Session-WebDriverAgentRunner.*\.log$/i, /^StandardOutputAndStandardError\.txt$/i];
const XCTEST_LOGS_CACHE_FOLDER_PREFIX = 'com.apple.dt.XCTest';
async function detectUdid(usbmuxdRemoteHost, usbmuxdRemotePort) {
_logger.default.debug('Auto-detecting real device udid...');
var options = {
usbmuxdRemoteHost: usbmuxdRemoteHost,
usbmuxdRemotePort: usbmuxdRemotePort
};
const udids = await _gstAtomIosDevice.utilities.getConnectedDevices(null, options);
if (_lodash.default.isEmpty(udids)) {
throw new Error('No device is connected to the host');
}
const udid = _lodash.default.last(udids);
if (udids.length > 1) {
_logger.default.warn(`Multiple devices found: ${udids.join(', ')}`);
_logger.default.warn(`Choosing '${udid}'. If this is wrong, manually set with 'udid' desired capability`);
}
_logger.default.debug(`Detected real device udid: '${udid}'`);
return udid;
}
async function getAndCheckXcodeVersion() {
let version;
try {
version = await _appiumXcode.default.getVersion(true);
} catch (err) {
_logger.default.debug(err);
_logger.default.errorAndThrow(`Could not determine Xcode version: ${err.message}`);
}
if (version.versionFloat < 7.3) {
_logger.default.errorAndThrow(`Xcode version '${version.versionString}'. Support for ` + `Xcode ${version.versionString} is not supported. ` + `Please upgrade to version 7.3 or higher`);
}
return version;
}
async function getAndCheckIosSdkVersion() {
try {
return await _appiumXcode.default.getMaxIOSSDK();
} catch (err) {
_logger.default.errorAndThrow(`Could not determine iOS SDK version: ${err.message}`);
}
}
function getGenericSimulatorForIosVersion(platformVersion, deviceName) {
let genericSimulators = _iosGenericSimulators.default[deviceName];
if (genericSimulators) {
genericSimulators = genericSimulators.sort(([simOne], [simTwo]) => _appiumSupport.util.compareVersions(simOne, '<', simTwo) ? -1 : 1);
let genericIosSimulator;
for (const [platformVersionFromList, iosSimulator] of genericSimulators) {
if (_appiumSupport.util.compareVersions(platformVersionFromList, '>', platformVersion)) {
break;
}
genericIosSimulator = iosSimulator;
}
return genericIosSimulator;
}
}
function translateDeviceName(platformVersion, deviceName = '') {
const deviceNameTranslated = getGenericSimulatorForIosVersion(platformVersion, deviceName.toLowerCase().trim());
if (deviceNameTranslated) {
_logger.default.debug(`Changing deviceName from '${deviceName}' to '${deviceNameTranslated}'`);
return deviceNameTranslated;
}
return deviceName;
}
const derivedDataCleanupMarkers = new Map();
async function markSystemFilesForCleanup(wda) {
if (!wda || !(await wda.retrieveDerivedDataPath())) {
_logger.default.warn('No WebDriverAgent derived data available, so unable to mark system files for cleanup');
return;
}
const logsRoot = _path.default.resolve(await wda.retrieveDerivedDataPath(), 'Logs');
let markersCount = 0;
if (derivedDataCleanupMarkers.has(logsRoot)) {
markersCount = derivedDataCleanupMarkers.get(logsRoot);
}
derivedDataCleanupMarkers.set(logsRoot, ++markersCount);
}
async function clearSystemFiles(wda) {
if (!wda || !(await wda.retrieveDerivedDataPath())) {
_logger.default.warn('No WebDriverAgent derived data available, so unable to clear system files');
return;
}
const logsRoot = _path.default.resolve(await wda.retrieveDerivedDataPath(), 'Logs');
if (derivedDataCleanupMarkers.has(logsRoot)) {
let markersCount = derivedDataCleanupMarkers.get(logsRoot);
derivedDataCleanupMarkers.set(logsRoot, --markersCount);
if (markersCount > 0) {
_logger.default.info(`Not cleaning '${logsRoot}' folder, because the other session does not expect it to be cleaned`);
return;
}
}
derivedDataCleanupMarkers.set(logsRoot, 0);
const globPattern = `${_os.default.tmpdir()}/${XCTEST_LOGS_CACHE_FOLDER_PREFIX}*/`;
const dstFolders = await _appiumSupport.fs.glob(globPattern);
if (_lodash.default.isEmpty(dstFolders)) {
_logger.default.debug(`Did not find the temporary XCTest logs root at '${globPattern}'`);
} else {
for (const dstFolder of dstFolders) {
let scheduledFilesCount = 0;
_bluebird.default.resolve(_appiumSupport.fs.walkDir(dstFolder, true, (itemPath, isDir) => {
if (isDir) {
return;
}
const fileName = _path.default.basename(itemPath);
if (!XCTEST_LOG_FILES_PATTERNS.some(p => p.test(fileName))) {
return;
}
_appiumSupport.fs.unlink(itemPath).catch(e => {
_logger.default.info(e.message);
});
scheduledFilesCount++;
})).finally(() => {
if (scheduledFilesCount > 0) {
_logger.default.info(`Scheduled ${scheduledFilesCount} temporary XCTest log ` + `${_appiumSupport.util.pluralize('file', scheduledFilesCount)} for cleanup in '${dstFolder}'`);
}
}).catch(e => {
_logger.default.info(e.message);
});
}
_logger.default.debug(`Started background XCTest logs cleanup in '${dstFolders}'`);
}
if (await _appiumSupport.fs.exists(logsRoot)) {
_logger.default.info(`Cleaning test logs in '${logsRoot}' folder`);
await _gstAtomIosDriver.utils.clearLogs([logsRoot]);
return;
}
_logger.default.info(`There is no ${logsRoot} folder, so not cleaning files`);
}
async function checkAppPresent(app) {
_logger.default.debug(`Checking whether app '${app}' is actually present on file system`);
if (!(await _appiumSupport.fs.exists(app))) {
_logger.default.errorAndThrow(`Could not find app at '${app}'`);
}
_logger.default.debug('App is present');
}
async function getDriverInfo() {
const stat = await _appiumSupport.fs.stat(_path.default.resolve(__dirname, '..'));
const built = stat.mtime.getTime();
const pkg = require(__filename.includes('build/lib/utils') ? '../../package.json' : '../package.json');
const version = pkg.version;
return {
built,
version
};
}
function normalizeCommandTimeouts(value) {
if (typeof value !== 'string') {
return value;
}
let result = {};
if (!isNaN(value)) {
result[DEFAULT_TIMEOUT_KEY] = _lodash.default.toInteger(value);
return result;
}
try {
result = JSON.parse(value);
if (!_lodash.default.isPlainObject(result)) {
throw new Error();
}
} catch (err) {
_logger.default.errorAndThrow(`"commandTimeouts" capability should be a valid JSON object. "${value}" was given instead`);
}
for (let [cmd, timeout] of _lodash.default.toPairs(result)) {
if (!_lodash.default.isInteger(timeout) || timeout <= 0) {
_logger.default.errorAndThrow(`The timeout for "${cmd}" should be a valid natural number of milliseconds. "${timeout}" was given instead`);
}
}
return result;
}
async function printUser() {
try {
let {
stdout
} = await (0, _teen_process.exec)('whoami');
_logger.default.debug(`Current user: '${stdout.trim()}'`);
} catch (err) {
_logger.default.debug(`Unable to get username running server: ${err.message}`);
}
}
async function getPIDsListeningOnPort(port, filteringFunc = null) {
const result = [];
try {
const {
stdout
} = await (0, _teen_process.exec)('lsof', ['-ti', `tcp:${port}`]);
result.push(...stdout.trim().split(/\n+/));
} catch (e) {
return result;
}
if (!_lodash.default.isFunction(filteringFunc)) {
return result;
}
return await _bluebird.default.filter(result, async x => {
const {
stdout
} = await (0, _teen_process.exec)('ps', ['-p', x, '-o', 'command']);
return await filteringFunc(stdout);
});
}
async function encodeBase64OrUpload(localPath, remotePath = null, uploadOptions = {}) {
if (!(await _appiumSupport.fs.exists(localPath))) {
_logger.default.errorAndThrow(`The file at '${localPath}' does not exist or is not accessible`);
}
const {
size
} = await _appiumSupport.fs.stat(localPath);
_logger.default.debug(`The size of the file is ${_appiumSupport.util.toReadableSizeString(size)}`);
if (_lodash.default.isEmpty(remotePath)) {
return (await _appiumSupport.util.toInMemoryBase64(localPath)).toString();
}
const {
user,
pass,
method
} = uploadOptions;
const remoteUrl = _url.default.parse(remotePath);
let options = {};
if (remoteUrl.protocol.startsWith('http')) {
options = {
url: remoteUrl.href,
method: method || 'PUT',
multipart: [{
body: _appiumSupport.fs.createReadStream(localPath)
}]
};
if (user && pass) {
options.auth = {
user,
pass
};
}
} else if (remoteUrl.protocol === 'ftp:') {
options = {
host: remoteUrl.hostname,
port: remoteUrl.port || 21
};
if (user && pass) {
options.user = user;
options.pass = pass;
}
}
await _appiumSupport.net.uploadFile(localPath, remotePath, options);
return '';
}
async function removeAllSessionWebSocketHandlers(server, sessionId) {
if (!server || !_lodash.default.isFunction(server.getWebSocketHandlers)) {
return;
}
const activeHandlers = await server.getWebSocketHandlers(sessionId);
for (const pathname of _lodash.default.keys(activeHandlers)) {
await server.removeWebSocketHandler(pathname);
}
}
async function verifyApplicationPlatform(app, expectedPlatform) {
_logger.default.debug('Verifying application platform');
const infoPlist = _path.default.resolve(app, 'Info.plist');
if (!(await _appiumSupport.fs.exists(infoPlist))) {
_logger.default.debug(`'${infoPlist}' does not exist`);
return;
}
const {
CFBundleSupportedPlatforms
} = await _appiumSupport.plist.parsePlistFile(infoPlist);
_logger.default.debug(`CFBundleSupportedPlatforms: ${JSON.stringify(CFBundleSupportedPlatforms)}`);
if (!_lodash.default.isArray(CFBundleSupportedPlatforms)) {
_logger.default.debug(`CFBundleSupportedPlatforms key does not exist in '${infoPlist}'`);
return;
}
const {
isSimulator,
isTvOS
} = expectedPlatform;
const prefix = isTvOS ? 'AppleTV' : 'iPhone';
const suffix = isSimulator ? 'Simulator' : 'OS';
const dstPlatform = `${prefix}${suffix}`;
if (!CFBundleSupportedPlatforms.includes(dstPlatform)) {
throw new Error(`${isSimulator ? 'Simulator' : 'Real device'} architecture is unsupported by the '${app}' application. ` + `Make sure the correct deployment target has been selected for its compilation in Xcode.`);
}
}
function isLocalHost(urlString) {
try {
const {
hostname
} = _url.default.parse(urlString);
return ['localhost', '127.0.0.1', '::1', '::ffff:127.0.0.1'].includes(hostname);
} catch (ign) {
_logger.default.warn(`'${urlString}' cannot be parsed as a valid URL`);
}
return false;
}
function normalizePlatformVersion(originalVersion) {
const normalizedVersion = _semver.default.coerce(originalVersion);
if (!normalizedVersion) {
throw new Error(`The platform version '${originalVersion}' should be a valid version number`);
}
return `${normalizedVersion.major}.${normalizedVersion.minor}`;
}require('source-map-support').install();
//# sourceMappingURL=data:application/json;charset=utf8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImxpYi91dGlscy5qcyJdLCJuYW1lcyI6WyJERUZBVUxUX1RJTUVPVVRfS0VZIiwiWENURVNUX0xPR19GSUxFU19QQVRURVJOUyIsIlhDVEVTVF9MT0dTX0NBQ0hFX0ZPTERFUl9QUkVGSVgiLCJkZXRlY3RVZGlkIiwidXNibXV4ZFJlbW90ZUhvc3QiLCJ1c2JtdXhkUmVtb3RlUG9ydCIsImxvZyIsImRlYnVnIiwib3B0aW9ucyIsInVkaWRzIiwidXRpbGl0aWVzIiwiZ2V0Q29ubmVjdGVkRGV2aWNlcyIsIl8iLCJpc0VtcHR5IiwiRXJyb3IiLCJ1ZGlkIiwibGFzdCIsImxlbmd0aCIsIndhcm4iLCJqb2luIiwiZ2V0QW5kQ2hlY2tYY29kZVZlcnNpb24iLCJ2ZXJzaW9uIiwieGNvZGUiLCJnZXRWZXJzaW9uIiwiZXJyIiwiZXJyb3JBbmRUaHJvdyIsIm1lc3NhZ2UiLCJ2ZXJzaW9uRmxvYXQiLCJ2ZXJzaW9uU3RyaW5nIiwiZ2V0QW5kQ2hlY2tJb3NTZGtWZXJzaW9uIiwiZ2V0TWF4SU9TU0RLIiwiZ2V0R2VuZXJpY1NpbXVsYXRvckZvcklvc1ZlcnNpb24iLCJwbGF0Zm9ybVZlcnNpb24iLCJkZXZpY2VOYW1lIiwiZ2VuZXJpY1NpbXVsYXRvcnMiLCJpb3NHZW5lcmljU2ltdWxhdG9ycyIsInNvcnQiLCJzaW1PbmUiLCJzaW1Ud28iLCJ1dGlsIiwiY29tcGFyZVZlcnNpb25zIiwiZ2VuZXJpY0lvc1NpbXVsYXRvciIsInBsYXRmb3JtVmVyc2lvbkZyb21MaXN0IiwiaW9zU2ltdWxhdG9yIiwidHJhbnNsYXRlRGV2aWNlTmFtZSIsImRldmljZU5hbWVUcmFuc2xhdGVkIiwidG9Mb3dlckNhc2UiLCJ0cmltIiwiZGVyaXZlZERhdGFDbGVhbnVwTWFya2VycyIsIk1hcCIsIm1hcmtTeXN0ZW1GaWxlc0ZvckNsZWFudXAiLCJ3ZGEiLCJyZXRyaWV2ZURlcml2ZWREYXRhUGF0aCIsImxvZ3NSb290IiwicGF0aCIsInJlc29sdmUiLCJtYXJrZXJzQ291bnQiLCJoYXMiLCJnZXQiLCJzZXQiLCJjbGVhclN5c3RlbUZpbGVzIiwiaW5mbyIsImdsb2JQYXR0ZXJuIiwib3MiLCJ0bXBkaXIiLCJkc3RGb2xkZXJzIiwiZnMiLCJnbG9iIiwiZHN0Rm9sZGVyIiwic2NoZWR1bGVkRmlsZXNDb3VudCIsIkIiLCJ3YWxrRGlyIiwiaXRlbVBhdGgiLCJpc0RpciIsImZpbGVOYW1lIiwiYmFzZW5hbWUiLCJzb21lIiwicCIsInRlc3QiLCJ1bmxpbmsiLCJjYXRjaCIsImUiLCJmaW5hbGx5IiwicGx1cmFsaXplIiwiZXhpc3RzIiwiaW9zVXRpbHMiLCJjbGVhckxvZ3MiLCJjaGVja0FwcFByZXNlbnQiLCJhcHAiLCJnZXREcml2ZXJJbmZvIiwic3RhdCIsIl9fZGlybmFtZSIsImJ1aWx0IiwibXRpbWUiLCJnZXRUaW1lIiwicGtnIiwicmVxdWlyZSIsIl9fZmlsZW5hbWUiLCJpbmNsdWRlcyIsIm5vcm1hbGl6ZUNvbW1hbmRUaW1lb3V0cyIsInZhbHVlIiwicmVzdWx0IiwiaXNOYU4iLCJ0b0ludGVnZXIiLCJKU09OIiwicGFyc2UiLCJpc1BsYWluT2JqZWN0IiwiY21kIiwidGltZW91dCIsInRvUGFpcnMiLCJpc0ludGVnZXIiLCJwcmludFVzZXIiLCJzdGRvdXQiLCJnZXRQSURzTGlzdGVuaW5nT25Qb3J0IiwicG9ydCIsImZpbHRlcmluZ0Z1bmMiLCJwdXNoIiwic3BsaXQiLCJpc0Z1bmN0aW9uIiwiZmlsdGVyIiwieCIsImVuY29kZUJhc2U2NE9yVXBsb2FkIiwibG9jYWxQYXRoIiwicmVtb3RlUGF0aCIsInVwbG9hZE9wdGlvbnMiLCJzaXplIiwidG9SZWFkYWJsZVNpemVTdHJpbmciLCJ0b0luTWVtb3J5QmFzZTY0IiwidG9TdHJpbmciLCJ1c2VyIiwicGFzcyIsIm1ldGhvZCIsInJlbW90ZVVybCIsInVybCIsInByb3RvY29sIiwic3RhcnRzV2l0aCIsImhyZWYiLCJtdWx0aXBhcnQiLCJib2R5IiwiY3JlYXRlUmVhZFN0cmVhbSIsImF1dGgiLCJob3N0IiwiaG9zdG5hbWUiLCJuZXQiLCJ1cGxvYWRGaWxlIiwicmVtb3ZlQWxsU2Vzc2lvbldlYlNvY2tldEhhbmRsZXJzIiwic2VydmVyIiwic2Vzc2lvbklkIiwiZ2V0V2ViU29ja2V0SGFuZGxlcnMiLCJhY3RpdmVIYW5kbGVycyIsInBhdGhuYW1lIiwia2V5cyIsInJlbW92ZVdlYlNvY2tldEhhbmRsZXIiLCJ2ZXJpZnlBcHBsaWNhdGlvblBsYXRmb3JtIiwiZXhwZWN0ZWRQbGF0Zm9ybSIsImluZm9QbGlzdCIsIkNGQnVuZGxlU3VwcG9ydGVkUGxhdGZvcm1zIiwicGxpc3QiLCJwYXJzZVBsaXN0RmlsZSIsInN0cmluZ2lmeSIsImlzQXJyYXkiLCJpc1NpbXVsYXRvciIsImlzVHZPUyIsInByZWZpeCIsInN1ZmZpeCIsImRzdFBsYXRmb3JtIiwiaXNMb2NhbEhvc3QiLCJ1cmxTdHJpbmciLCJpZ24iLCJub3JtYWxpemVQbGF0Zm9ybVZlcnNpb24iLCJvcmlnaW5hbFZlcnNpb24iLCJub3JtYWxpemVkVmVyc2lvbiIsInNlbXZlciIsImNvZXJjZSIsIm1ham9yIiwibWlub3IiXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUFBQTs7QUFDQTs7QUFDQTs7QUFDQTs7QUFDQTs7QUFDQTs7QUFDQTs7QUFDQTs7QUFDQTs7QUFDQTs7QUFDQTs7QUFDQTs7QUFDQTs7QUFHQSxNQUFNQSxtQkFBbUIsR0FBRyxTQUE1Qjs7QUFDQSxNQUFNQyx5QkFBeUIsR0FBRyxDQUNoQyx3Q0FEZ0MsRUFFaEMsd0NBRmdDLENBQWxDO0FBSUEsTUFBTUMsK0JBQStCLEdBQUcscUJBQXhDOztBQUdBLGVBQWVDLFVBQWYsQ0FBMkJDLGlCQUEzQixFQUE4Q0MsaUJBQTlDLEVBQWlFO0FBQy9EQyxrQkFBSUMsS0FBSixDQUFVLG9DQUFWOztBQUNBLE1BQUlDLE9BQU8sR0FBRztBQUNaSixJQUFBQSxpQkFBaUIsRUFBRUEsaUJBRFA7QUFFWkMsSUFBQUEsaUJBQWlCLEVBQUVBO0FBRlAsR0FBZDtBQUlBLFFBQU1JLEtB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ekUsR0FBSSxpQkFBeEYsR0FDYix5RkFERyxDQUFOO0FBRUQ7QUFDRjs7QUFPRCxTQUFTOEUsV0FBVCxDQUFzQkMsU0FBdEIsRUFBaUM7QUFDL0IsTUFBSTtBQUNGLFVBQU07QUFBQ3pCLE1BQUFBO0FBQUQsUUFBYVQsYUFBSTVCLEtBQUosQ0FBVThELFNBQVYsQ0FBbkI7O0FBQ0EsV0FBTyxDQUFDLFdBQUQsRUFBYyxXQUFkLEVBQTJCLEtBQTNCLEVBQWtDLGtCQUFsQyxFQUFzRHJFLFFBQXRELENBQStENEMsUUFBL0QsQ0FBUDtBQUNELEdBSEQsQ0FHRSxPQUFPMEIsR0FBUCxFQUFZO0FBQ1psSyxvQkFBSVksSUFBSixDQUFVLElBQUdxSixTQUFVLG1DQUF2QjtBQUNEOztBQUNELFNBQU8sS0FBUDtBQUNEOztBQVNELFNBQVNFLHdCQUFULENBQW1DQyxlQUFuQyxFQUFvRDtBQUNsRCxRQUFNQyxpQkFBaUIsR0FBR0MsZ0JBQU9DLE1BQVAsQ0FBY0gsZUFBZCxDQUExQjs7QUFDQSxNQUFJLENBQUNDLGlCQUFMLEVBQXdCO0FBQ3RCLFVBQU0sSUFBSTdKLEtBQUosQ0FBVyx5QkFBd0I0SixlQUFnQixvQ0FBbkQsQ0FBTjtBQUNEOztBQUNELFNBQVEsR0FBRUMsaUJBQWlCLENBQUNHLEtBQU0sSUFBR0gsaUJBQWlCLENBQUNJLEtBQU0sRUFBN0Q7QUFDRCIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCBCIGZyb20gJ2JsdWViaXJkJztcbmltcG9ydCB7IHV0aWxpdGllcyB9IGZyb20gJ2dzdC1hdG9tLWlvcy1kZXZpY2UnO1xuaW1wb3J0IHsgZnMsIHV0aWwsIG5ldCwgcGxpc3QgfSBmcm9tICdhcHBpdW0tc3VwcG9ydCc7XG5pbXBvcnQgcGF0aCBmcm9tICdwYXRoJztcbmltcG9ydCB7IHV0aWxzIGFzIGlvc1V0aWxzIH0gZnJvbSAnZ3N0LWF0b20taW9zLWRyaXZlcic7XG5pbXBvcnQgeyBleGVjIH0gZnJvbSAndGVlbl9wcm9jZXNzJztcbmltcG9ydCB4Y29kZSBmcm9tICdhcHBpdW0teGNvZGUnO1xuaW1wb3J0IF8gZnJvbSAnbG9kYXNoJztcbmltcG9ydCBsb2cgZnJvbSAnLi9sb2dnZXInO1xuaW1wb3J0IGlvc0dlbmVyaWNTaW11bGF0b3JzIGZyb20gJy4vaW9zLWdlbmVyaWMtc2ltdWxhdG9ycyc7XG5pbXBvcnQgdXJsIGZyb20gJ3VybCc7XG5pbXBvcnQgb3MgZnJvbSAnb3MnO1xuaW1wb3J0IHNlbXZlciBmcm9tICdzZW12ZXInO1xuXG5cbmNvbnN0IERFRkFVTFRfVElNRU9VVF9LRVkgPSAnZGVmYXVsdCc7XG5jb25zdCBYQ1RFU1RfTE9HX0ZJTEVTX1BBVFRFUk5TID0gW1xuICAvXlNlc3Npb24tV2ViRHJpdmVyQWdlbnRSdW5uZXIuKlxcLmxvZyQvaSxcbiAgL15TdGFuZGFyZE91dHB1dEFuZFN0YW5kYXJkRXJyb3JcXC50eHQkL2ksXG5dO1xuY29uc3QgWENURVNUX0xPR1NfQ0FDSEVfRk9MREVSX1BSRUZJWCA9ICdjb20uYXBwbGUuZHQuWENUZXN0JztcblxuXG5hc3luYyBmdW5jdGlvbiBkZXRlY3RVZGlkICh1c2JtdXhkUmVtb3RlSG9zdCwgdXNibXV4ZFJlbW90ZVBvcnQpIHtcbiAgbG9nLmRlYnVnKCdBdXRvLWRldGVjdGluZyByZWFsIGRldmljZSB1ZGlkLi4uJyk7XG4gIHZhciBvcHRpb25zID0ge1xuICAgIHVzYm11eGRSZW1vdGVIb3N0OiB1c2JtdXhkUmVtb3RlSG9zdCxcbiAgICB1c2JtdXhkUmVtb3RlUG9ydDogdXNibXV4ZFJlbW90ZVBvcnRcbiAgfTtcbiAgY29uc3QgdWRpZHMgPSBhd2FpdCB1dGlsaXRpZXMuZ2V0Q29ubmVjdGVkRGV2aWNlcyhudWxsLCBvcHRpb25zKTtcbiAgaWYgKF8uaXNFbXB0eSh1ZGlkcykpIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoJ05vIGRldmljZSBpcyBjb25uZWN0ZWQgdG8gdGhlIGhvc3QnKTtcbiAgfVxuICBjb25zdCB1ZGlkID0gXy5sYXN0KHVkaWRzKTtcbiAgaWYgKHVkaWRzLmxlbmd0aCA+IDEpIHtcbiAgICBsb2cud2FybihgTXVsdGlwbGUgZGV2aWNlcyBmb3VuZDogJHt1ZGlkcy5qb2luKCcsICcpfWApO1xuICAgIGxvZy53YXJuKGBDaG9vc2luZyAnJHt1ZGlkfScuIElmIHRoaXMgaXMgd3JvbmcsIG1hbnVhbGx5IHNldCB3aXRoICd1ZGlkJyBkZXNpcmVkIGNhcGFiaWxpdHlgKTtcbiAgfVxuICBsb2cuZGVidWcoYERldGVjdGVkIHJlYWwgZGV2aWNlIHVkaWQ6ICcke3VkaWR9J2ApO1xuICByZXR1cm4gdWRpZDtcbn1cblxuYXN5bmMgZnVuY3Rpb24gZ2V0QW5kQ2hlY2tYY29kZVZlcnNpb24gKCkge1xuICBsZXQgdmVyc2lvbjtcbiAgdHJ5IHtcbiAgICB2ZXJzaW9uID0gYXdhaXQgeGNvZGUuZ2V0VmVyc2lvbih0cnVlKTtcbiAgfSBjYXRjaCAoZXJyKSB7XG4gICAgbG9nLmRlYnVnKGVycik7XG4gICAgbG9nLmVycm9yQW5kVGhyb3coYENvdWxkIG5vdCBkZXRlcm1pbmUgWGNvZGUgdmVyc2lvbjogJHtlcnIubWVzc2FnZX1gKTtcbiAgfVxuXG4gIC8vIHdlIGRvIG5vdCBzdXBwb3J0IFhjb2RlcyA8IDcuMyxcbiAgaWYgKHZlcnNpb24udmVyc2lvbkZsb2F0IDwgNy4zKSB7XG4gICAgbG9nLmVycm9yQW5kVGhyb3coYFhjb2RlIHZlcnNpb24gJyR7dmVyc2lvbi52ZXJzaW9uU3RyaW5nfScuIFN1cHBvcnQgZm9yIGAgK1xuICAgICAgICAgICAgICAgICAgICAgIGBYY29kZSAke3ZlcnNpb24udmVyc2lvblN0cmluZ30gaXMgbm90IHN1cHBvcnRlZC4gYCArXG4gICAgICAgICAgICAgICAgICAgICAgYFBsZWFzZSB1cGdyYWRlIHRvIHZlcnNpb24gNy4zIG9yIGhpZ2hlcmApO1xuICB9XG4gIHJldHVybiB2ZXJzaW9uO1xufVxuXG5hc3luYyBmdW5jdGlvbiBnZXRBbmRDaGVja0lvc1Nka1ZlcnNpb24gKCkge1xuICB0cnkge1xuICAgIHJldHVybiBhd2FpdCB4Y29kZS5nZXRNYXhJT1NTREsoKTtcbiAgfSBjYXRjaCAoZXJyKSB7XG4gICAgbG9nLmVycm9yQW5kVGhyb3coYENvdWxkIG5vdCBkZXRlcm1pbmUgaU9TIFNESyB2ZXJzaW9uOiAke2Vyci5tZXNzYWdlfWApO1xuICB9XG59XG5cbi8qKlxuICogR2V0IHRoZSBnZW5lcmljIHNpbXVsYXRvciBmb3IgYSBnaXZlbiBJT1MgdmVyc2lvbiBhbmQgZGV2aWNlIHR5cGUgKGlQaG9uZSwgaVBhZClcbiAqXG4gKiBAcGFyYW0ge3N0cmluZ3xudW1iZXJ9IHBsYXRmb3JtVmVyc2lvbiBJT1MgdmVyc2lvbi4gZS5nLikgMTMuMFxuICogQHBhcmFtIHtzdHJpbmd9IGRldmljZU5hbWUgVHlwZSBvZiBJT1MgZGV2aWNlLiBDYW4gYmUgaVBob25lLCBpUGFkIChwb3NzaWJseSBtb3JlIGluIHRoZSBmdXR1cmUpXG4gKlxuICogQHJldHVybnMge3N0cmluZ30gR2VuZXJpYyBpUGhvbmUgb3IgaVBhZCBzaW11bGF0b3IgKGlmIGFwcGxpY2FibGUpXG4gKi9cbmZ1bmN0aW9uIGdldEdlbmVyaWNTaW11bGF0b3JGb3JJb3NWZXJzaW9uIChwbGF0Zm9ybVZlcnNpb24sIGRldmljZU5hbWUpIHtcbiAgbGV0IGdlbmVyaWNTaW11bGF0b3JzID0gaW9zR2VuZXJpY1NpbXVsYXRvcnNbZGV2aWNlTmFtZV07XG5cbiAgaWYgKGdlbmVyaWNTaW11bGF0b3JzKSB7XG4gICAgZ2VuZXJpY1NpbXVsYXRvcnMgPSBnZW5lcmljU2ltdWxhdG9ycy5zb3J0KChbc2ltT25lXSwgW3NpbVR3b10pID0+IHV0aWwuY29tcGFyZVZlcnNpb25zKHNpbU9uZSwgJzwnLCBzaW1Ud28pID8gLTEgOiAxKTtcblxuICAgIC8vIEZpbmQgdGhlIGhpZ2hlc3QgaU9TIHZlcnNpb24gaW4gdGhlIGxpc3QgdGhhdCBpcyBiZWxvdyB0aGUgcHJvdmlkZWQgdmVyc2lvblxuICAgIGxldCBnZW5lcmljSW9zU2ltdWxhdG9yO1xuICAgIGZvciAoY29uc3QgW3BsYXRmb3JtVmVyc2lvbkZyb21MaXN0LCBpb3NTaW11bGF0b3JdIG9mIGdlbmVyaWNTaW11bGF0b3JzKSB7XG4gICAgICBpZiAodXRpbC5jb21wYXJlVmVyc2lvbnMocGxhdGZvcm1WZXJzaW9uRnJvbUxpc3QsICc+JywgcGxhdGZvcm1WZXJzaW9uKSkge1xuICAgICAgICBicmVhaztcbiAgICAgIH1cbiAgICAgIGdlbmVyaWNJb3NTaW11bGF0b3IgPSBpb3NTaW11bGF0b3I7XG4gICAgfVxuICAgIHJldHVybiBnZW5lcmljSW9zU2ltdWxhdG9yO1xuICB9XG59XG5cbmZ1bmN0aW9uIHRyYW5zbGF0ZURldmljZU5hbWUgKHBsYXRmb3JtVmVyc2lvbiwgZGV2aWNlTmFtZSA9ICcnKSB7XG4gIGNvbnN0IGRldmljZU5hbWVUcmFuc2xhdGVkID0gZ2V0R2VuZXJpY1NpbXVsYXRvckZvcklvc1ZlcnNpb24ocGxhdGZvcm1WZXJzaW9uLCBkZXZpY2VOYW1lLnRvTG93ZXJDYXNlKCkudHJpbSgpKTtcbiAgaWYgKGRldmljZU5hbWVUcmFuc2xhdGVkKSB7XG4gICAgbG9nLmRlYnVnKGBDaGFuZ2luZyBkZXZpY2VOYW1lIGZyb20gJyR7ZGV2aWNlTmFtZX0nIHRvICcke2RldmljZU5hbWVUcmFuc2xhdGVkfSdgKTtcbiAgICByZXR1cm4gZGV2aWNlTmFtZVRyYW5zbGF0ZWQ7XG4gIH1cbiAgcmV0dXJuIGRldmljZU5hbWU7XG59XG5cbi8vIFRoaXMgbWFwIGNvbnRhaW5zIGRlcml2ZWQgZGF0YSBsb2dzIGZvbGRlcnMgYXMga2V5c1xuLy8gYW5kIHZhbHVlcyBhcmUgdGhlIGNvdW50IG9mIHRpbWVzIHRoZSBwYXJ0aWN1bGFyXG4vLyBmb2xkZXIgaGFzIGJlZW4gc2NoZWR1bGVkIGZvciByZW1vdmFsXG5jb25zdCBkZXJpdmVkRGF0YUNsZWFudXBNYXJrZXJzID0gbmV3IE1hcCgpO1xuXG5hc3luYyBmdW5jdGlvbiBtYXJrU3lzdGVtRmlsZXNGb3JDbGVhbnVwICh3ZGEpIHtcbiAgaWYgKCF3ZGEgfHwgIWF3YWl0IHdkYS5yZXRyaWV2ZURlcml2ZWREYXRhUGF0aCgpKSB7XG4gICAgbG9nLndhcm4oJ05vIFdlYkRyaXZlckFnZW50IGRlcml2ZWQgZGF0YSBhdmFpbGFibGUsIHNvIHVuYWJsZSB0byBtYXJrIHN5c3RlbSBmaWxlcyBmb3IgY2xlYW51cCcpO1xuICAgIHJldHVybjtcbiAgfVxuXG4gIGNvbnN0IGxvZ3NSb290ID0gcGF0aC5yZXNvbHZlKGF3YWl0IHdkYS5yZXRyaWV2ZURlcml2ZWREYXRhUGF0aCgpLCAnTG9ncycpO1xuICBsZXQgbWFya2Vyc0NvdW50ID0gMDtcbiAgaWYgKGRlcml2ZWREYXRhQ2xlYW51cE1hcmtlcnMuaGFzKGxvZ3NSb290KSkge1xuICAgIG1hcmtlcnNDb3VudCA9IGRlcml2ZWREYXRhQ2xlYW51cE1hcmtlcnMuZ2V0KGxvZ3NSb290KTtcbiAgfVxuICBkZXJpdmVkRGF0YUNsZWFudXBNYXJrZXJzLnNldChsb2dzUm9vdCwgKyttYXJrZXJzQ291bnQpO1xufVxuXG5hc3luYyBmdW5jdGlvbiBjbGVhclN5c3RlbUZpbGVzICh3ZGEpIHtcbiAgLy8gb25seSB3YW50IHRvIGNsZWFyIHRoZSBzeXN0ZW0gZmlsZXMgZm9yIHRoZSBwYXJ0aWN1bGFyIFdEQSB4Y29kZSBydW5cbiAgaWYgKCF3ZGEgfHwgIWF3YWl0IHdkYS5yZXRyaWV2ZURlcml2ZWREYXRhUGF0aCgpKSB7XG4gICAgbG9nLndhcm4oJ05vIFdlYkRyaXZlckFnZW50IGRlcml2ZWQgZGF0YSBhdmFpbGFibGUsIHNvIHVuYWJsZSB0byBjbGVhciBzeXN0ZW0gZmlsZXMnKTtcbiAgICByZXR1cm47XG4gIH1cblxuICBjb25zdCBsb2dzUm9vdCA9IHBhdGgucmVzb2x2ZShhd2FpdCB3ZGEucmV0cmlldmVEZXJpdmVkRGF0YVBhdGgoKSwgJ0xvZ3MnKTtcbiAgaWYgKGRlcml2ZWREYXRhQ2xlYW51cE1hcmtlcnMuaGFzKGxvZ3NSb290KSkge1xuICAgIGxldCBtYXJrZXJzQ291bnQgPSBkZXJpdmVkRGF0YUNsZWFudXBNYXJrZXJzLmdldChsb2dzUm9vdCk7XG4gICAgZGVyaXZlZERhdGFDbGVhbnVwTWFya2Vycy5zZXQobG9nc1Jvb3QsIC0tbWFya2Vyc0NvdW50KTtcbiAgICBpZiAobWFya2Vyc0NvdW50ID4gMCkge1xuICAgICAgbG9nLmluZm8oYE5vdCBjbGVhbmluZyAnJHtsb2dzUm9vdH0nIGZvbGRlciwgYmVjYXVzZSB0aGUgb3RoZXIgc2Vzc2lvbiBkb2VzIG5vdCBleHBlY3QgaXQgdG8gYmUgY2xlYW5lZGApO1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgfVxuICBkZXJpdmVkRGF0YUNsZWFudXBNYXJrZXJzLnNldChsb2dzUm9vdCwgMCk7XG5cbiAgLy8gQ2xlYW5pbmcgdXAgYmlnIHRlbXBvcmFyeSBmaWxlcyBjcmVhdGVkIGJ5IFhDVGVzdDogaHR0cHM6Ly9naXRodWIuY29tL2FwcGl1bS9hcHBpdW0vaXNzdWVzLzk0MTBcbiAgY29uc3QgZ2xvYlBhdHRlcm4gPSBgJHtvcy50bXBkaXIoKX0vJHtYQ1RFU1RfTE9HU19DQUNIRV9GT0xERVJfUFJFRklYfSovYDtcbiAgY29uc3QgZHN0Rm9sZGVycyA9IGF3YWl0IGZzLmdsb2IoZ2xvYlBhdHRlcm4pO1xuICBpZiAoXy5pc0VtcHR5KGRzdEZvbGRlcnMpKSB7XG4gICAgbG9nLmRlYnVnKGBEaWQgbm90IGZpbmQgdGhlIHRlbXBvcmFyeSBYQ1Rlc3QgbG9ncyByb290IGF0ICcke2dsb2JQYXR0ZXJufSdgKTtcbiAgfSBlbHNlIHtcbiAgICAvLyBwZXJmb3JtIHRoZSBjbGVhbnVwIGFzeW5jaHJvbm91c2x5XG4gICAgZm9yIChjb25zdCBkc3RGb2xkZXIgb2YgZHN0Rm9sZGVycykge1xuICAgICAgbGV0IHNjaGVkdWxlZEZpbGVzQ291bnQgPSAwO1xuICAgICAgQi5yZXNvbHZlKGZzLndhbGtEaXIoZHN0Rm9sZGVyLCB0cnVlLCAoaXRlbVBhdGgsIGlzRGlyKSA9PiB7XG4gICAgICAgIGlmIChpc0Rpcikge1xuICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuICAgICAgICBjb25zdCBmaWxlTmFtZSA9IHBhdGguYmFzZW5hbWUoaXRlbVBhdGgpO1xuICAgICAgICBpZiAoIVhDVEVTVF9MT0dfRklMRVNfUEFUVEVSTlMuc29tZSgocCkgPT4gcC50ZXN0KGZpbGVOYW1lKSkpIHtcbiAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cblxuICAgICAgICAvLyBkZWxldGUgdGhlIGZpbGUgYXN5bmNocm9ub3VzbHlcbiAgICAgICAgZnMudW5saW5rKGl0ZW1QYXRoKS5jYXRjaCgoZSkgPT4ge1xuICAgICAgICAgIGxvZy5pbmZvKGUubWVzc2FnZSk7XG4gICAgICAgIH0pO1xuICAgICAgICBzY2hlZHVsZWRGaWxlc0NvdW50Kys7XG4gICAgICB9KSkuZmluYWxseSgoKSA9PiB7XG4gICAgICAgIGlmIChzY2hlZHVsZWRGaWxlc0NvdW50ID4gMCkge1xuICAgICAgICAgIGxvZy5pbmZvKGBTY2hlZHVsZWQgJHtzY2hlZHVsZWRGaWxlc0NvdW50fSB0ZW1wb3JhcnkgWENUZXN0IGxvZyBgICtcbiAgICAgICAgICAgIGAke3V0aWwucGx1cmFsaXplKCdmaWxlJywgc2NoZWR1bGVkRmlsZXNDb3VudCl9IGZvciBjbGVhbnVwIGluICcke2RzdEZvbGRlcn0nYCk7XG4gICAgICAgIH1cbiAgICAgIH0pLmNhdGNoKChlKSA9PiB7XG4gICAgICAgIGxvZy5pbmZvKGUubWVzc2FnZSk7XG4gICAgICB9KTtcbiAgICB9XG4gICAgbG9nLmRlYnVnKGBTdGFydGVkIGJhY2tncm91bmQgWENUZXN0IGxvZ3MgY2xlYW51cCBpbiAnJHtkc3RGb2xkZXJzfSdgKTtcbiAgfVxuXG4gIGlmIChhd2FpdCBmcy5leGlzdHMobG9nc1Jvb3QpKSB7XG4gICAgbG9nLmluZm8oYENsZWFuaW5nIHRlc3QgbG9ncyBpbiAnJHtsb2dzUm9vdH0nIGZvbGRlcmApO1xuICAgIGF3YWl0IGlvc1V0aWxzLmNsZWFyTG9ncyhbbG9nc1Jvb3RdKTtcbiAgICByZXR1cm47XG4gIH1cbiAgbG9nLmluZm8oYFRoZXJlIGlzIG5vICR7bG9nc1Jvb3R9IGZvbGRlciwgc28gbm90IGNsZWFuaW5nIGZpbGVzYCk7XG59XG5cbmFzeW5jIGZ1bmN0aW9uIGNoZWNrQXBwUHJlc2VudCAoYXBwKSB7XG4gIGxvZy5kZWJ1ZyhgQ2hlY2tpbmcgd2hldGhlciBhcHAgJyR7YXBwfScgaXMgYWN0dWFsbHkgcHJlc2VudCBvbiBmaWxlIHN5c3RlbWApO1xuICBpZiAoIShhd2FpdCBmcy5leGlzdHMoYXBwKSkpIHtcbiAgICBsb2cuZXJyb3JBbmRUaHJvdyhgQ291bGQgbm90IGZpbmQgYXBwIGF0ICcke2FwcH0nYCk7XG4gIH1cbiAgbG9nLmRlYnVnKCdBcHAgaXMgcHJlc2VudCcpO1xufVxuXG5hc3luYyBmdW5jdGlvbiBnZXREcml2ZXJJbmZvICgpIHtcbiAgY29uc3Qgc3RhdCA9IGF3YWl0IGZzLnN0YXQocGF0aC5yZXNvbHZlKF9fZGlybmFtZSwgJy4uJykpO1xuICBjb25zdCBidWlsdCA9IHN0YXQubXRpbWUuZ2V0VGltZSgpO1xuXG4gIC8vIGdldCB0aGUgcGFja2FnZS5qc29uIGFuZCB0aGUgdmVyc2lvbiBmcm9tIGl0XG4gIGNvbnN0IHBrZyA9IHJlcXVpcmUoX19maWxlbmFtZS5pbmNsdWRlcygnYnVpbGQvbGliL3V0aWxzJykgPyAnLi4vLi4vcGFja2FnZS5qc29uJyA6ICcuLi9wYWNrYWdlLmpzb24nKTtcbiAgY29uc3QgdmVyc2lvbiA9IHBrZy52ZXJzaW9uO1xuXG4gIHJldHVybiB7XG4gICAgYnVpbHQsXG4gICAgdmVyc2lvbixcbiAgfTtcbn1cblxuZnVuY3Rpb24gbm9ybWFsaXplQ29tbWFuZFRpbWVvdXRzICh2YWx1ZSkge1xuICAvLyBUaGUgdmFsdWUgaXMgbm9ybWFsaXplZCBhbHJlYWR5XG4gIGlmICh0eXBlb2YgdmFsdWUgIT09ICdzdHJpbmcnKSB7XG4gICAgcmV0dXJuIHZhbHVlO1xuICB9XG5cbiAgbGV0IHJlc3VsdCA9IHt9O1xuICAvLyBVc2UgYXMgZGVmYXVsdCB0aW1lb3V0IGZvciBhbGwgY29tbWFuZHMgaWYgYSBzaW5nbGUgaW50ZWdlciB2YWx1ZSBpcyBwcm92aWRlZFxuICBpZiAoIWlzTmFOKHZhbHVlKSkge1xuICAgIHJlc3VsdFtERUZBVUxUX1RJTUVPVVRfS0VZXSA9IF8udG9JbnRlZ2VyKHZhbHVlKTtcbiAgICByZXR1cm4gcmVzdWx0O1xuICB9XG5cbiAgLy8gSlNPTiBvYmplY3QgaGFzIGJlZW4gcHJvdmlkZWQuIExldCdzIHBhcnNlIGl0XG4gIHRyeSB7XG4gICAgcmVzdWx0ID0gSlNPTi5wYXJzZSh2YWx1ZSk7XG4gICAgaWYgKCFfLmlzUGxhaW5PYmplY3QocmVzdWx0KSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCk7XG4gICAgfVxuICB9IGNhdGNoIChlcnIpIHtcbiAgICBsb2cuZXJyb3JBbmRUaHJvdyhgXCJjb21tYW5kVGltZW91dHNcIiBjYXBhYmlsaXR5IHNob3VsZCBiZSBhIHZhbGlkIEpTT04gb2JqZWN0LiBcIiR7dmFsdWV9XCIgd2FzIGdpdmVuIGluc3RlYWRgKTtcbiAgfVxuICBmb3IgKGxldCBbY21kLCB0aW1lb3V0XSBvZiBfLnRvUGFpcnMocmVzdWx0KSkge1xuICAgIGlmICghXy5pc0ludGVnZXIodGltZW91dCkgfHwgdGltZW91dCA8PSAwKSB7XG4gICAgICBsb2cuZXJyb3JBbmRUaHJvdyhgVGhlIHRpbWVvdXQgZm9yIFwiJHtjbWR9XCIgc2hvdWxkIGJlIGEgdmFsaWQgbmF0dXJhbCBudW1iZXIgb2YgbWlsbGlzZWNvbmRzLiBcIiR7dGltZW91dH1cIiB3YXMgZ2l2ZW4gaW5zdGVhZGApO1xuICAgIH1cbiAgfVxuICByZXR1cm4gcmVzdWx0O1xufVxuXG5hc3luYyBmdW5jdGlvbiBwcmludFVzZXIgKCkge1xuICB0cnkge1xuICAgIGxldCB7c3Rkb3V0fSA9IGF3YWl0IGV4ZWMoJ3dob2FtaScpO1xuICAgIGxvZy5kZWJ1ZyhgQ3VycmVudCB1c2VyOiAnJHtzdGRvdXQudHJpbSgpfSdgKTtcbiAgfSBjYXRjaCAoZXJyKSB7XG4gICAgbG9nLmRlYnVnKGBVbmFibGUgdG8gZ2V0IHVzZXJuYW1lIHJ1bm5pbmcgc2VydmVyOiAke2Vyci5tZXNzYWdlfWApO1xuICB9XG59XG5cbi8qKlxuICogR2V0IHRoZSBJRHMgb2YgcHJvY2Vzc2VzIGxpc3RlbmluZyBvbiB0aGUgcGFydGljdWxhciBzeXN0ZW0gcG9ydC5cbiAqIEl0IGlzIGFsc28gcG9zc2libGUgdG8gYXBwbHkgYWRkaXRpb25hbCBmaWx0ZXJpbmcgYmFzZWQgb24gdGhlXG4gKiBwcm9jZXNzIGNvbW1hbmQgbGluZS5cbiAqXG4gKiBAcGFyYW0ge3N0cmluZ3xudW1iZXJ9IHBvcnQgLSBUaGUgcG9ydCBudW1iZXIuXG4gKiBAcGFyYW0gez9GdW5jdGlvbn0gZmlsdGVyaW5nRnVuYyAtIE9wdGlvbmFsIGxhbWJkYSBmdW5jdGlvbiwgd2hpY2hcbiAqICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVjZWl2ZXMgY29tbWFuZCBsaW5lIHN0cmluZyBvZiB0aGUgcGFydGljdWxhciBwcm9jZXNzXG4gKiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxpc3RlbmluZyBvbiBnaXZlbiBwb3J0LCBhbmQgaXMgZXhwZWN0ZWQgdG8gcmV0dXJuXG4gKiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVpdGhlciB0cnVlIG9yIGZhbHNlIHRvIGluY2x1ZGUvZXhjbHVkZSB0aGUgY29ycmVzcG9uZGluZyBQSURcbiAqICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZnJvbSB0aGUgcmVzdWx0aW5nIGFycmF5LlxuICogQHJldHVybnMge0FycmF5PHN0cmluZz59IC0gdGhlIGxpc3Qgb2YgbWF0Y2hlZCBwcm9jZXNzIGlkcy5cbiAqL1xuYXN5bmMgZnVuY3Rpb24gZ2V0UElEc0xpc3RlbmluZ09uUG9ydCAocG9ydCwgZmlsdGVyaW5nRnVuYyA9IG51bGwpIHtcbiAgY29uc3QgcmVzdWx0ID0gW107XG4gIHRyeSB7XG4gICAgLy8gVGhpcyBvbmx5IHdvcmtzIHNpbmNlIE1hYyBPUyBYIEVsIENhcGl0YW5cbiAgICBjb25zdCB7c3Rkb3V0fSA9IGF3YWl0IGV4ZWMoJ2xzb2YnLCBbJy10aScsIGB0Y3A6JHtwb3J0fWBdKTtcbiAgICByZXN1bHQucHVzaCguLi4oc3Rkb3V0LnRyaW0oKS5zcGxpdCgvXFxuKy8pKSk7XG4gIH0gY2F0Y2ggKGUpIHtcbiAgICByZXR1cm4gcmVzdWx0O1xuICB9XG5cbiAgaWYgKCFfLmlzRnVuY3Rpb24oZmlsdGVyaW5nRnVuYykpIHtcbiAgICByZXR1cm4gcmVzdWx0O1xuICB9XG4gIHJldHVybiBhd2FpdCBCLmZpbHRlcihyZXN1bHQsIGFzeW5jICh4KSA9PiB7XG4gICAgY29uc3Qge3N0ZG91dH0gPSBhd2FpdCBleGVjKCdwcycsIFsnLXAnLCB4LCAnLW8nLCAnY29tbWFuZCddKTtcbiAgICByZXR1cm4gYXdhaXQgZmlsdGVyaW5nRnVuYyhzdGRvdXQpO1xuICB9KTtcbn1cblxuLyoqXG4gKiBAdHlwZWRlZiB7T2JqZWN0fSBVcGxvYWRPcHRpb25zXG4gKlxuICogQHByb3BlcnR5IHs/c3RyaW5nfSB1c2VyIC0gVGhlIG5hbWUgb2YgdGhlIHVzZXIgZm9yIHRoZSByZW1vdGUgYXV0aGVudGljYXRpb24uIE9ubHkgd29ya3MgaWYgYHJlbW90ZVBhdGhgIGlzIHByb3ZpZGVkLlxuICogQHByb3BlcnR5IHs/c3RyaW5nfSBwYXNzIC0gVGhlIHBhc3N3b3JkIGZvciB0aGUgcmVtb3RlIGF1dGhlbnRpY2F0aW9uLiBPbmx5IHdvcmtzIGlmIGByZW1vdGVQYXRoYCBpcyBwcm92aWRlZC5cbiAqIEBwcm9wZXJ0eSB7P3N0cmluZ30gbWV0aG9kIC0gVGhlIGh0dHAgbXVsdGlwYXJ0IHVwbG9hZCBtZXRob2QgbmFtZS4gVGhlICdQVVQnIG9uZSBpcyB1c2VkIGJ5IGRlZmF1bHQuXG4gKiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIE9ubHkgd29ya3MgaWYgYHJlbW90ZVBhdGhgIGlzIHByb3ZpZGVkLlxuICovXG5cblxuLyoqXG4gKiBFbmNvZGVzIHRoZSBnaXZlbiBsb2NhbCBmaWxlIHRvIGJhc2U2NCBhbmQgcmV0dXJucyB0aGUgcmVzdWx0aW5nIHN0cmluZ1xuICogb3IgdXBsb2FkcyBpdCB0byBhIHJlbW90ZSBzZXJ2ZXIgdXNpbmcgaHR0cC9odHRwcyBvciBmdHAgcHJvdG9jb2xzXG4gKiBpZiBgcmVtb3RlUGF0aGAgaXMgc2V0XG4gKlxuICogQHBhcmFtIHtzdHJpbmd9IGxvY2FsUGF0aCAtIFRoZSBwYXRoIHRvIGFuIGV4aXN0aW5nIGxvY2FsIGZpbGVcbiAqIEBwYXJhbSB7P3N0cmluZ30gcmVtb3RlUGF0aCAtIFRoZSBwYXRoIHRvIHRoZSByZW1vdGUgbG9jYXRpb24sIHdoZXJlXG4gKiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aGlzIGZpbGUgc2hvdWxkIGJlIHVwbG9hZGVkXG4gKiBAcGFyYW0gez9VcGxvYWRPcHRpb25zfSB1cGxvYWRPcHRpb25zIC0gU2V0IG9mIHVwbG9hZCBvcHRpb25zXG4gKiBAcmV0dXJucyB7c3RyaW5nfSBFaXRoZXIgYW4gZW1wdHkgc3RyaW5nIGlmIHRoZSB1cGxvYWQgd2FzIHN1Y2Nlc3NmdWwgb3JcbiAqIGJhc2U2NC1lbmNvZGVkIGZpbGUgcmVwcmVzZW50YXRpb24gaWYgYHJlbW90ZVBhdGhgIGlzIGZhbHN5XG4gKi9cbmFzeW5jIGZ1bmN0aW9uIGVuY29kZUJhc2U2NE9yVXBsb2FkIChsb2NhbFBhdGgsIHJlbW90ZVBhdGggPSBudWxsLCB1cGxvYWRPcHRpb25zID0ge30pIHtcbiAgaWYgKCFhd2FpdCBmcy5leGlzdHMobG9jYWxQYXRoKSkge1xuICAgIGxvZy5lcnJvckFuZFRocm93KGBUaGUgZmlsZSBhdCAnJHtsb2NhbFBhdGh9JyBkb2VzIG5vdCBleGlzdCBvciBpcyBub3QgYWNjZXNzaWJsZWApO1xuICB9XG5cbiAgY29uc3Qge3NpemV9ID0gYXdhaXQgZnMuc3RhdChsb2NhbFBhdGgpO1xuICBsb2cuZGVidWcoYFRoZSBzaXplIG9mIHRoZSBmaWxlIGlzICR7dXRpbC50b1JlYWRhYmxlU2l6ZVN0cmluZyhzaXplKX1gKTtcbiAgaWYgKF8uaXNFbXB0eShyZW1vdGVQYXRoKSkge1xuICAgIHJldHVybiAoYXdhaXQgdXRpbC50b0luTWVtb3J5QmFzZTY0KGxvY2FsUGF0aCkpLnRvU3RyaW5nKCk7XG4gIH1cblxuICBjb25zdCB7XG4gICAgdXNlcixcbiAgICBwYXNzLFxuICAgIG1ldGhvZCxcbiAgfSA9IHVwbG9hZE9wdGlvbnM7XG4gIGNvbnN0IHJlbW90ZVVybCA9IHVybC5wYXJzZShyZW1vdGVQYXRoKTtcbiAgbGV0IG9wdGlvbnMgPSB7fTtcbiAgaWYgKHJlbW90ZVVybC5wcm90b2NvbC5zdGFydHNXaXRoKCdodHRwJykpIHtcbiAgICBvcHRpb25zID0ge1xuICAgICAgdXJsOiByZW1vdGVVcmwuaHJlZixcbiAgICAgIG1ldGhvZDogbWV0aG9kIHx8ICdQVVQnLFxuICAgICAgbXVsdGlwYXJ0OiBbeyBib2R5OiBmcy5jcmVhdGVSZWFkU3RyZWFtKGxvY2FsUGF0aCkgfV0sXG4gICAgfTtcbiAgICBpZiAodXNlciAmJiBwYXNzKSB7XG4gICAgICBvcHRpb25zLmF1dGggPSB7dXNlciwgcGFzc307XG4gICAgfVxuICB9IGVsc2UgaWYgKHJlbW90ZVVybC5wcm90b2NvbCA9PT0gJ2Z0cDonKSB7XG4gICAgb3B0aW9ucyA9IHtcbiAgICAgIGhvc3Q6IHJlbW90ZVVybC5ob3N0bmFtZSxcbiAgICAgIHBvcnQ6IHJlbW90ZVVybC5wb3J0IHx8IDIxLFxuICAgIH07XG4gICAgaWYgKHVzZXIgJiYgcGFzcykge1xuICAgICAgb3B0aW9ucy51c2VyID0gdXNlcjtcbiAgICAgIG9wdGlvbnMucGFzcyA9IHBhc3M7XG4gICAgfVxuICB9XG4gIGF3YWl0IG5ldC51cGxvYWRGaWxlKGxvY2FsUGF0aCwgcmVtb3RlUGF0aCwgb3B0aW9ucyk7XG4gIHJldHVybiAnJztcbn1cblxuLyoqXG4gKiBTdG9wcyBhbmQgcmVtb3ZlcyBhbGwgd2ViIHNvY2tldCBoYW5kbGVycyB0aGF0IGFyZSBsaXN0ZW5pbmdcbiAqIGluIHNjb3BlIG9mIHRoZSBjdXJyZWN0IHNlc3Npb24uXG4gKlxuICogQHBhcmFtIHtPYmplY3R9IHNlcnZlciAtIFRoZSBpbnN0YW5jZSBvZiBOb2RlSnMgSFRUUCBzZXJ2ZXIsXG4gKiB3aGljaCBob3N0cyBBcHBpdW1cbiAqIEBwYXJhbSB7c3RyaW5nfSBzZXNzaW9uSWQgLSBUaGUgaWQgb2YgdGhlIGN1cnJlbnQgc2Vzc2lvblxuICovXG5hc3luYyBmdW5jdGlvbiByZW1vdmVBbGxTZXNzaW9uV2ViU29ja2V0SGFuZGxlcnMgKHNlcnZlciwgc2Vzc2lvbklkKSB7XG4gIGlmICghc2VydmVyIHx8ICFfLmlzRnVuY3Rpb24oc2VydmVyLmdldFdlYlNvY2tldEhhbmRsZXJzKSkge1xuICAgIHJldHVybjtcbiAgfVxuXG4gIGNvbnN0IGFjdGl2ZUhhbmRsZXJzID0gYXdhaXQgc2VydmVyLmdldFdlYlNvY2tldEhhbmRsZXJzKHNlc3Npb25JZCk7XG4gIGZvciAoY29uc3QgcGF0aG5hbWUgb2YgXy5rZXlzKGFjdGl2ZUhhbmRsZXJzKSkge1xuICAgIGF3YWl0IHNlcnZlci5yZW1vdmVXZWJTb2NrZXRIYW5kbGVyKHBhdGhuYW1lKTtcbiAgfVxufVxuXG4vKipcbiAqIEB0eXBlZGVmIHtPYmplY3R9IFBsYXRmb3JtT3B0c1xuICpcbiAqIEBwcm9wZXJ0eSB7Ym9vbGVhbn0gaXNTaW11bGF0b3IgLSBXaGV0aGVyIHRoZSBkZXN0aW5hdGlvbiBwbGF0Zm9ybSBpcyBhIFNpbXVsYXRvclxuICogQHByb3BlcnR5IHtib29sZWFufSBpc1R2T1MgLSBXaGV0aGVyIHRoZSBkZXN0aW5hdGlvbiBwbGF0Zm9ybSBpcyBhIFNpbXVsYXRvclxuICovXG5cbi8qKlxuICogVmVyaWZ5IHdoZXRoZXIgdGhlIGdpdmVuIGFwcGxpY2F0aW9uIGlzIGNvbXBhdGlibGUgdG8gdGhlXG4gKiBwbGF0Zm9ybSB3aGVyZSBpdCBpcyBnb2luZyB0byBiZSBpbnN0YWxsZWQgYW5kIHRlc3RlZC5cbiAqXG4gKiBAcGFyYW0ge3N0cmluZ30gYXBwIC0gVGhlIGFjdHVhbCBwYXRoIHRvIHRoZSBhcHBsaWNhdGlvbiBidW5kbGVcbiAqIEBwYXJhbSB7UGxhdGZvcm1PcHRzfSBleHBlY3RlZFBsYXRmb3JtXG4gKiBAdGhyb3dzIHtFcnJvcn0gSWYgYnVuZGxlIGFyY2hpdGVjdHVyZSBkb2VzIG5vdCBtYXRjaCB0aGUgZXhwZWN0ZWQgZGV2aWNlIGFyY2hpdGVjdHVyZS5cbiAqL1xuYXN5bmMgZnVuY3Rpb24gdmVyaWZ5QXBwbGljYXRpb25QbGF0Zm9ybSAoYXBwLCBleHBlY3RlZFBsYXRmb3JtKSB7XG4gIGxvZy5kZWJ1ZygnVmVyaWZ5aW5nIGFwcGxpY2F0aW9uIHBsYXRmb3JtJyk7XG5cbiAgY29uc3QgaW5mb1BsaXN0ID0gcGF0aC5yZXNvbHZlKGFwcCwgJ0luZm8ucGxpc3QnKTtcbiAgaWYgKCFhd2FpdCBmcy5leGlzdHMoaW5mb1BsaXN0KSkge1xuICAgIGxvZy5kZWJ1ZyhgJyR7aW5mb1BsaXN0fScgZG9lcyBub3QgZXhpc3RgKTtcbiAgICByZXR1cm47XG4gIH1cblxuICBjb25zdCB7Q0ZCdW5kbGVTdXBwb3J0ZWRQbGF0Zm9ybXN9ID0gYXdhaXQgcGxpc3QucGFyc2VQbGlzdEZpbGUoaW5mb1BsaXN0KTtcbiAgbG9nLmRlYnVnKGBDRkJ1bmRsZVN1cHBvcnRlZFBsYXRmb3JtczogJHtKU09OLnN0cmluZ2lmeShDRkJ1bmRsZVN1cHBvcnRlZFBsYXRmb3Jtcyl9YCk7XG4gIGlmICghXy5pc0FycmF5KENGQnVuZGxlU3VwcG9ydGVkUGxhdGZvcm1zKSkge1xuICAgIGxvZy5kZWJ1ZyhgQ0ZCdW5kbGVTdXBwb3J0ZWRQbGF0Zm9ybXMga2V5IGRvZXMgbm90IGV4aXN0IGluICcke2luZm9QbGlzdH0nYCk7XG4gICAgcmV0dXJuO1xuICB9XG5cbiAgY29uc3Qge1xuICAgIGlzU2ltdWxhdG9yLFxuICAgIGlzVHZPUyxcbiAgfSA9IGV4cGVjdGVkUGxhdGZvcm07XG4gIGNvbnN0IHByZWZpeCA9IGlzVHZPUyA/ICdBcHBsZVRWJyA6ICdpUGhvbmUnO1xuICBjb25zdCBzdWZmaXggPSBpc1NpbXVsYXRvciA/ICdTaW11bGF0b3InIDogJ09TJztcbiAgY29uc3QgZHN0UGxhdGZvcm0gPSBgJHtwcmVmaXh9JHtzdWZmaXh9YDtcbiAgaWYgKCFDRkJ1bmRsZVN1cHBvcnRlZFBsYXRmb3Jtcy5pbmNsdWRlcyhkc3RQbGF0Zm9ybSkpIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoYCR7aXNTaW11bGF0b3IgPyAnU2ltdWxhdG9yJyA6ICdSZWFsIGRldmljZSd9IGFyY2hpdGVjdHVyZSBpcyB1bnN1cHBvcnRlZCBieSB0aGUgJyR7YXBwfScgYXBwbGljYXRpb24uIGAgK1xuICAgICAgYE1ha2Ugc3VyZSB0aGUgY29ycmVjdCBkZXBsb3ltZW50IHRhcmdldCBoYXMgYmVlbiBzZWxlY3RlZCBmb3IgaXRzIGNvbXBpbGF0aW9uIGluIFhjb2RlLmApO1xuICB9XG59XG5cbi8qKlxuICogUmV0dXJucyB0cnVlIGlmIHRoZSB1cmxTdHJpbmcgaXMgbG9jYWxob3N0XG4gKiBAcGFyYW0gez9zdHJpbmd9IHVybFN0cmluZ1xuICogQHJldHVybnMge2Jvb2xlYW59IFJldHVybiB0cnVlIGlmIHRoZSB1cmxTdHJpbmcgaXMgbG9jYWxob3N0XG4gKi9cbmZ1bmN0aW9uIGlzTG9jYWxIb3N0ICh1cmxTdHJpbmcpIHtcbiAgdHJ5IHtcbiAgICBjb25zdCB7aG9zdG5hbWV9ID0gdXJsLnBhcnNlKHVybFN0cmluZyk7XG4gICAgcmV0dXJuIFsnbG9jYWxob3N0JywgJzEyNy4wLjAuMScsICc6OjEnLCAnOjpmZmZmOjEyNy4wLjAuMSddLmluY2x1ZGVzKGhvc3RuYW1lKTtcbiAgfSBjYXRjaCAoaWduKSB7XG4gICAgbG9nLndhcm4oYCcke3VybFN0cmluZ30nIGNhbm5vdCBiZSBwYXJzZWQgYXMgYSB2YWxpZCBVUkxgKTtcbiAgfVxuICByZXR1cm4gZmFsc2U7XG59XG5cbi8qKlxuICogTm9ybWFsaXplcyBwbGF0Zm9ybVZlcnNpb24gdG8gYSB2YWxpZCBpT1MgdmVyc2lvbiBzdHJpbmdcbiAqXG4gKiBAcGFyYW0ge3N0cmluZ30gb3JpZ2luYWxWZXJzaW9uIC0gTG9vc2UgdmVyc2lvbiBudW1iZXIsIHRoYXQgY2FuIGJlIHBhcnNlZCBieSBzZW12ZXJcbiAqIEByZXR1cm4ge3N0cmluZ30gaU9TIHZlcnNpb24gbnVtYmVyIGluIDxtYWpvcj4uPG1pbm9yPiBmb3JtYXRcbiAqIEB0aHJvd3Mge0Vycm9yfSBpZiB0aGUgdmVyc2lvbiBudW1iZXIgY2Fubm90IGJlIHBhcnNlZFxuICovXG5mdW5jdGlvbiBub3JtYWxpemVQbGF0Zm9ybVZlcnNpb24gKG9yaWdpbmFsVmVyc2lvbikge1xuICBjb25zdCBub3JtYWxpemVkVmVyc2lvbiA9IHNlbXZlci5jb2VyY2Uob3JpZ2luYWxWZXJzaW9uKTtcbiAgaWYgKCFub3JtYWxpemVkVmVyc2lvbikge1xuICAgIHRocm93IG5ldyBFcnJvcihgVGhlIHBsYXRmb3JtIHZlcnNpb24gJyR7b3JpZ2luYWxWZXJzaW9ufScgc2