UNPKG

babel-plugin-transform-adana

Version:
559 lines (479 loc) 61.6 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.skip = skip; exports.key = key; exports.default = instrumenter; var types = _interopRequireWildcard(require("@babel/types")); var _micromatch = _interopRequireDefault(require("micromatch")); var _path = require("path"); var _prelude = _interopRequireDefault(require("./prelude")); var _meta = _interopRequireDefault(require("./meta")); var _tags = require("./tags"); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); } function skip(_ref, file) { var ignore = _ref.ignore, only = _ref.only; if (only) { return (0, _micromatch.default)([file], Array.isArray(only) ? only : [only], { nocase: true }).length <= 0; } if (ignore) { return (0, _micromatch.default)([file], Array.isArray(ignore) ? ignore : [ignore], { nocase: true }).length > 0; } return false; } /** * Create an opaque, unique key for a given node. Useful for tagging the node * in separate places. * @param {Object} path Babel path to derive key from. * @returns {String} String key. */ function key(path) { var node = path.node; if (node.loc) { var location = node.loc.start; return `${location.line}:${location.column}`; } throw new TypeError('Path must have valid location.'); } /** * Some nodes need to marked as non-instrumentable; since babel will apply * our plugin to nodes we create, we have to be careful to not put ourselves * into an infinite loop. * @param {Object} node Babel AST node. * @returns {Object} Babel AST node that won't be instrumented. */ function X(node) { node.__adana = true; return node; } function ignore(path) { return !path.node || !path.node.loc || path.node.__adana; } function standardize(listener) { return function (path, state) { return ignore(path) ? undefined : listener(path, state); }; } /** * Create the transform-adana babel plugin. * @returns {Object} `babel` plugin object. */ function instrumenter() { /** * Create a chunk of code that marks the specified node as having * been executed. * @param {Object} state `babel` state for the path that's being walked. * @param {Object} options Configure how the marker behaves. * @returns {Object} AST node for marking coverage. */ function createMarker(state, options) { var tags = options.tags, loc = options.loc, name = options.name, group = options.group; var coverage = (0, _meta.default)(state); var id = coverage.entries.length; coverage.entries.push({ id, loc, tags, name, group, count: 0 }); // Maker is simply a statement incrementing a coverage variable. return X(types.updateExpression('++', types.memberExpression(types.memberExpression(coverage.variable, types.numericLiteral(id), true), types.stringLiteral('count'), true), true)); } /** * [isInstrumentableStatement description] * @param {[type]} path [description] * @returns {Boolean} [description] */ function isInstrumentableStatement(path) { var parent = path.parentPath; return !parent.isReturnStatement() && !parent.isVariableDeclaration() && !parent.isExportDeclaration() && !parent.isFunctionDeclaration() && !parent.isIfStatement(); } /** * Inject a marker that measures whether the node for the given path has * been run or not. * @param {Object} path [description] * @param {Object} state [description] * @param {Object} options [description] * @returns {void} */ function instrument(path, state, options) { // This function is here because isInstrumentableStatement() is being // called; we can't create the marker without knowing the result of that, // otherwise dead markers will be created. function marker() { return createMarker(state, _extends({ loc: path.node.loc }, options)); } if (path.isBlockStatement()) { path.unshiftContainer('body', X(types.expressionStatement(marker()))); } else if (path.isExpression()) { path.replaceWith(X(types.sequenceExpression([marker(), path.node]))); } else if (path.isStatement()) { if (isInstrumentableStatement(path)) { path.insertBefore(X(types.expressionStatement(marker()))); } } } /** * [visitStatement description] * @param {[type]} path [description] * @param {[type]} state [description] * @returns {void} */ function visitStatement(path, state) { instrument(path, state, { tags: ['statement', 'line'], loc: path.node.loc }); } /** * The function visitor is mainly to track the definitions of functions; * being able ensure how many of your functions have actually been invoked. * @param {[type]} path [description] * @param {[type]} state [description] * @returns {void} */ function visitFunction(path, state) { instrument(path.get('body'), state, { tags: ['function'], name: path.node.id ? path.node.id.name : `@${key(path)}`, loc: path.node.loc }); } /** * Multiple branches based on the result of `case _` and `default`. If you * do not provide a `default` one will be intelligently added for you, * forcing you to cover that case. * @param {[type]} path [description] * @param {[type]} state [description] * @returns {void} */ function visitSwitchStatement(path, state) { var hasDefault = false; path.get('cases').forEach(function (entry) { if (entry.node.test) { (0, _tags.addRules)(state, entry.node.loc, entry.node.test.trailingComments); } if (entry.node.consequent.length > 1) { (0, _tags.addRules)(state, entry.node.loc, entry.node.consequent[0].leadingComments); } if (entry.node.test === null) { hasDefault = true; } entry.unshiftContainer('consequent', createMarker(state, { tags: ['branch', 'switch'], loc: entry.node.loc, group: key(path) })); }); // Default is technically a branch, just like if statements without // else's are also technically a branch. if (!hasDefault) { // Add an extra break to the end of the last case in case some idiot // forgot to add it. var cases = path.get('cases'); if (cases.length > 0) { cases[cases.length - 1].pushContainer('consequent', types.breakStatement()); } // Finally add the default case. path.pushContainer('cases', types.switchCase(null, [types.expressionStatement(createMarker(state, { tags: ['branch', 'switch'], loc: { start: path.node.loc.end, end: path.node.loc.end }, group: key(path) })), types.breakStatement()])); } } /** * [visitVariableDeclaration description] * @param {[type]} path [description] * @param {[type]} state [description] * @returns {void} */ function visitVariableDeclaration(path, state) { path.get('declarations').forEach(function (decl) { if (decl.has('init')) { instrument(decl.get('init'), state, { tags: ['statement', 'variable', 'line'] }); } }); } /** * Includes both while and do-while loops. They contain a single branch which * tests the loop condition. * @param {[type]} path [description] * @param {[type]} state [description] * @returns {void} */ function visitWhileLoop(path, state) { var test = path.get('test'); var group = key(path); // This is a particularly clever use of the fact JS operators are short- // circuiting. To instrument a loop one _cannot_ add a marker on the outside // of the loop body due to weird cases of things where loops are in non- // block if statements. So instead, create the following mechanism: // ((condition && A) || !B) where A and B are markers. Since markers are // postfix, they're always true. Ergo, A is only incremented when condition // is true, B only when it's false and the truth value of the whole // statement is preserved. Neato. test.replaceWith(types.logicalExpression('||', types.logicalExpression('&&', X(test.node), createMarker(state, { tags: ['branch', 'line', 'statement', 'loop', 'while'], loc: test.node.loc, group })), types.unaryExpression('!', createMarker(state, { tags: ['branch', 'line', 'loop', 'while'], loc: test.node.loc, group })))); } /** * The try block can either fully succeed (no error) or it can throw. Both * cases are accounted for. * @param {[type]} path [description] * @param {[type]} state [description] * @returns {void} */ function visitTryStatement(path, state) { var group = key(path); var body = path.get('block'); var trigger = path.scope.generateDeclaredUidIdentifier('_exception'); (0, _tags.addRules)(state, body.node.loc, body.node.leadingComments); path.get('block').unshiftContainer('body', types.expressionStatement(types.assignmentExpression('=', trigger, types.booleanLiteral(true)))); var handlerExpression = types.expressionStatement(types.assignmentExpression('=', trigger, types.booleanLiteral(false))); var handlerLoc; if (path.has('handler')) { var handler = path.get('handler').node; handlerLoc = handler.loc; (0, _tags.addRules)(state, handler.loc, handler.body.leadingComments); path.get('handler.body').unshiftContainer('body', handlerExpression); } else { var loc = path.get('block').node.loc.end; handlerLoc = { start: loc, end: loc }; path.get('handler').replaceWith(types.catchClause(types.identifier('err'), types.blockStatement([handlerExpression, types.throwStatement(types.identifier('err'))]))); } var guard = types.ifStatement(trigger, types.expressionStatement(createMarker(state, { tags: ['branch', 'line', 'exception'], loc: path.get('block').node.loc, group })), types.expressionStatement(createMarker(state, { tags: ['branch', 'line', 'exception'], loc: handlerLoc, group }))); if (path.has('finalizer')) { path.get('finalizer').unshiftContainer('body', guard); } else { path.get('finalizer').replaceWith(types.blockStatement([guard])); } } /** * Return statements are instrumented by marking the next block they return. * This helps ensure multi-line expressions for return statements are * accurately captured. * @param {[type]} path [description] * @param {[type]} state [description] * @returns {[type]} [description] */ function visitReturnStatement(path, state) { if (!path.has('argument')) { path.get('argument').replaceWith(types.sequenceExpression([createMarker(state, { loc: path.node.loc, tags: ['line', 'statement'] }), types.identifier('undefined')])); } else { instrument(path.get('argument'), state, { tags: ['line', 'statement'] }); } } /** * For multi-line reporting (and objects do tend to span multiple lines) this * is required to know which parts of the object where actually executed. * Ignore shorthand property that look like `{ this }`. * @param {[type]} path [description] * @param {[type]} state [description] * @returns {[type]} [description] */ function visitObjectProperty(path, state) { if (!path.node.shorthand && !path.parentPath.isPattern()) { var _key = path.get('key'); var value = path.get('value'); if (path.node.computed) { instrument(_key, state, { tags: ['line'] }); } instrument(value, state, { tags: ['line'] }); } } /** * For multi-line reporting (and arrays do tend to span multiple lines) this * is required to know which parts of the array where actually executed. * This does _not_ include destructed arrays. * @param {[type]} path [description] * @param {[type]} state [description] * @returns {[type]} [description] */ function visitArrayExpression(path, state) { if (!path.parentPath.isPattern()) { path.get('elements').forEach(function (element) { instrument(element, state, { tags: ['line'] }); }); } } /** * Logical expressions are those using logic operators like `&&` and `||`. * Since logic expressions short-circuit in JS they are effectively branches * and will be treated as such here. * @param {[type]} path [description] * @param {[type]} state [description] * @returns {void} */ function visitLogicalExpression(path, state) { var group = key(path); var left = path.get('left').node; var right = path.get('right').node; path.replaceWith(X(types.logicalExpression(path.node.operator, types.sequenceExpression([createMarker(state, { tags: ['branch', 'logic'], loc: left.loc, group }), left]), types.sequenceExpression([createMarker(state, { tags: ['branch', 'logic'], loc: right.loc, group }), right])))); } /** * Conditionals are either if/else statements or tenaiary expressions. They * have a test case and two choices (based on the test result). Both cases * are always accounted for, even if the code does not exist for the alternate * case. * @param {[type]} path [description] * @param {[type]} state [description] * @returns {void} */ function visitConditional(path, state) { // Branches can be grouped together so that each of the possible branch // destinations is accounted for under one group. For if statements, this // refers to all the blocks that fall under a single if.. else if.. else.. // grouping. var root = path.findParent(function (search) { return search.node.type === path.node.type && !ignore(search) && (!search.parentPath || search.parentPath.node.type !== path.node.type); }) || path; // Create the group name based on the root `if` statement. var group = key(root); function tagBranch(path) { (0, _tags.addRules)(state, path.node.loc, path.node.leadingComments); if (path.isBlockStatement() && path.node.body.length > 0) { (0, _tags.addRules)(state, path.node.loc, path.node.body[0].leadingComments); } } tagBranch(path.get('consequent')); if (path.has('alternate')) { tagBranch(path.get('alternate')); } instrument(path.get('consequent'), state, { tags: ['branch', 'line', 'if'], loc: path.node.consequent.loc, group }); if (path.has('alternate') && !path.get('alternate').isIfStatement()) { instrument(path.get('alternate'), state, { tags: ['branch', 'line', 'if'], loc: path.node.alternate.loc, group }); } else if (!path.has('alternate')) { path.get('alternate').replaceWith(types.expressionStatement(createMarker(state, { tags: ['branch', 'if'], loc: { start: path.node.loc.end, end: path.node.loc.end }, group }))); } } function noInstrument(path) { if (path.node && path.node.leadingComments && path.node.leadingComments.some(function (comment) { return /^\s*adana-no-instrument\s*$/.exec(comment.value); })) { path.skip(); return; } } var visitor = { // Expressions ArrowFunctionExpression: visitFunction, FunctionExpression: visitFunction, ObjectMethod: visitFunction, ClassMethod: visitFunction, LogicalExpression: visitLogicalExpression, ConditionalExpression: visitConditional, ObjectProperty: visitObjectProperty, ArrayExpression: visitArrayExpression, // Declarations FunctionDeclaration: visitFunction, VariableDeclaration: visitVariableDeclaration, // Statements ContinueStatement: visitStatement, BreakStatement: visitStatement, ExpressionStatement: visitStatement, ThrowStatement: visitStatement, ReturnStatement: visitReturnStatement, TryStatement: visitTryStatement, WhileStatement: visitWhileLoop, DoWhileStatement: visitWhileLoop, IfStatement: visitConditional, SwitchStatement: visitSwitchStatement, // Generics enter: noInstrument }; Object.keys(visitor).forEach(function (key) { visitor[key] = standardize(visitor[key]); }); // Create the actual babel plugin object. return { visitor: { Program(path, state) { var opts = state.opts, filename = state.filename, cwd = state.file.opts.cwd; // Check if file should be instrumented or not. var name = filename ? (0, _path.relative)(cwd, filename) : '<source>'; if (filename && skip(opts, name)) { return; } (0, _meta.default)(state, { source: state.file.code, entries: [], rules: [], tags: {}, variable: path.scope.generateUidIdentifier('coverage'), name }); path.traverse(visitor, state); (0, _tags.applyRules)(state); path.unshiftContainer('body', (0, _prelude.default)(state)); } } }; } //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uL3NyYy9pbnN0cnVtZW50ZXIuanMiXSwibmFtZXMiOlsic2tpcCIsImZpbGUiLCJpZ25vcmUiLCJvbmx5IiwiQXJyYXkiLCJpc0FycmF5Iiwibm9jYXNlIiwibGVuZ3RoIiwia2V5IiwicGF0aCIsIm5vZGUiLCJsb2MiLCJsb2NhdGlvbiIsInN0YXJ0IiwibGluZSIsImNvbHVtbiIsIlR5cGVFcnJvciIsIlgiLCJfX2FkYW5hIiwic3RhbmRhcmRpemUiLCJsaXN0ZW5lciIsInN0YXRlIiwidW5kZWZpbmVkIiwiaW5zdHJ1bWVudGVyIiwiY3JlYXRlTWFya2VyIiwib3B0aW9ucyIsInRhZ3MiLCJuYW1lIiwiZ3JvdXAiLCJjb3ZlcmFnZSIsImlkIiwiZW50cmllcyIsInB1c2giLCJjb3VudCIsInR5cGVzIiwidXBkYXRlRXhwcmVzc2lvbiIsIm1lbWJlckV4cHJlc3Npb24iLCJ2YXJpYWJsZSIsIm51bWVyaWNMaXRlcmFsIiwic3RyaW5nTGl0ZXJhbCIsImlzSW5zdHJ1bWVudGFibGVTdGF0ZW1lbnQiLCJwYXJlbnQiLCJwYXJlbnRQYXRoIiwiaXNSZXR1cm5TdGF0ZW1lbnQiLCJpc1ZhcmlhYmxlRGVjbGFyYXRpb24iLCJpc0V4cG9ydERlY2xhcmF0aW9uIiwiaXNGdW5jdGlvbkRlY2xhcmF0aW9uIiwiaXNJZlN0YXRlbWVudCIsImluc3RydW1lbnQiLCJtYXJrZXIiLCJpc0Jsb2NrU3RhdGVtZW50IiwidW5zaGlmdENvbnRhaW5lciIsImV4cHJlc3Npb25TdGF0ZW1lbnQiLCJpc0V4cHJlc3Npb24iLCJyZXBsYWNlV2l0aCIsInNlcXVlbmNlRXhwcmVzc2lvbiIsImlzU3RhdGVtZW50IiwiaW5zZXJ0QmVmb3JlIiwidmlzaXRTdGF0ZW1lbnQiLCJ2aXNpdEZ1bmN0aW9uIiwiZ2V0IiwidmlzaXRTd2l0Y2hTdGF0ZW1lbnQiLCJoYXNEZWZhdWx0IiwiZm9yRWFjaCIsImVudHJ5IiwidGVzdCIsInRyYWlsaW5nQ29tbWVudHMiLCJjb25zZXF1ZW50IiwibGVhZGluZ0NvbW1lbnRzIiwiY2FzZXMiLCJwdXNoQ29udGFpbmVyIiwiYnJlYWtTdGF0ZW1lbnQiLCJzd2l0Y2hDYXNlIiwiZW5kIiwidmlzaXRWYXJpYWJsZURlY2xhcmF0aW9uIiwiZGVjbCIsImhhcyIsInZpc2l0V2hpbGVMb29wIiwibG9naWNhbEV4cHJlc3Npb24iLCJ1bmFyeUV4cHJlc3Npb24iLCJ2aXNpdFRyeVN0YXRlbWVudCIsImJvZHkiLCJ0cmlnZ2VyIiwic2NvcGUiLCJnZW5lcmF0ZURlY2xhcmVkVWlkSWRlbnRpZmllciIsImFzc2lnbm1lbnRFeHByZXNzaW9uIiwiYm9vbGVhbkxpdGVyYWwiLCJoYW5kbGVyRXhwcmVzc2lvbiIsImhhbmRsZXJMb2MiLCJoYW5kbGVyIiwiY2F0Y2hDbGF1c2UiLCJpZGVudGlmaWVyIiwiYmxvY2tTdGF0ZW1lbnQiLCJ0aHJvd1N0YXRlbWVudCIsImd1YXJkIiwiaWZTdGF0ZW1lbnQiLCJ2aXNpdFJldHVyblN0YXRlbWVudCIsInZpc2l0T2JqZWN0UHJvcGVydHkiLCJzaG9ydGhhbmQiLCJpc1BhdHRlcm4iLCJ2YWx1ZSIsImNvbXB1dGVkIiwidmlzaXRBcnJheUV4cHJlc3Npb24iLCJlbGVtZW50IiwidmlzaXRMb2dpY2FsRXhwcmVzc2lvbiIsImxlZnQiLCJyaWdodCIsIm9wZXJhdG9yIiwidmlzaXRDb25kaXRpb25hbCIsInJvb3QiLCJmaW5kUGFyZW50Iiwic2VhcmNoIiwidHlwZSIsInRhZ0JyYW5jaCIsImFsdGVybmF0ZSIsIm5vSW5zdHJ1bWVudCIsInNvbWUiLCJjb21tZW50IiwiZXhlYyIsInZpc2l0b3IiLCJBcnJvd0Z1bmN0aW9uRXhwcmVzc2lvbiIsIkZ1bmN0aW9uRXhwcmVzc2lvbiIsIk9iamVjdE1ldGhvZCIsIkNsYXNzTWV0aG9kIiwiTG9naWNhbEV4cHJlc3Npb24iLCJDb25kaXRpb25hbEV4cHJlc3Npb24iLCJPYmplY3RQcm9wZXJ0eSIsIkFycmF5RXhwcmVzc2lvbiIsIkZ1bmN0aW9uRGVjbGFyYXRpb24iLCJWYXJpYWJsZURlY2xhcmF0aW9uIiwiQ29udGludWVTdGF0ZW1lbnQiLCJCcmVha1N0YXRlbWVudCIsIkV4cHJlc3Npb25TdGF0ZW1lbnQiLCJUaHJvd1N0YXRlbWVudCIsIlJldHVyblN0YXRlbWVudCIsIlRyeVN0YXRlbWVudCIsIldoaWxlU3RhdGVtZW50IiwiRG9XaGlsZVN0YXRlbWVudCIsIklmU3RhdGVtZW50IiwiU3dpdGNoU3RhdGVtZW50IiwiZW50ZXIiLCJPYmplY3QiLCJrZXlzIiwiUHJvZ3JhbSIsIm9wdHMiLCJmaWxlbmFtZSIsImN3ZCIsInNvdXJjZSIsImNvZGUiLCJydWxlcyIsImdlbmVyYXRlVWlkSWRlbnRpZmllciIsInRyYXZlcnNlIl0sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7QUFBQTs7QUFDQTs7QUFDQTs7QUFFQTs7QUFDQTs7QUFDQTs7Ozs7Ozs7QUFFTyxTQUFTQSxJQUFULE9BQThCQyxJQUE5QixFQUFvQztBQUFBLE1BQXJCQyxNQUFxQixRQUFyQkEsTUFBcUI7QUFBQSxNQUFiQyxJQUFhLFFBQWJBLElBQWE7O0FBQ3pDLE1BQUlBLElBQUosRUFBVTtBQUNSLFdBQU8seUJBQ0wsQ0FBQ0YsSUFBRCxDQURLLEVBRUxHLE1BQU1DLE9BQU4sQ0FBY0YsSUFBZCxJQUFzQkEsSUFBdEIsR0FBNkIsQ0FBQ0EsSUFBRCxDQUZ4QixFQUdMO0FBQUNHLGNBQVE7QUFBVCxLQUhLLEVBSUxDLE1BSkssSUFJSyxDQUpaO0FBS0Q7O0FBQ0QsTUFBSUwsTUFBSixFQUFZO0FBQ1YsV0FBTyx5QkFDTCxDQUFDRCxJQUFELENBREssRUFFTEcsTUFBTUMsT0FBTixDQUFjSCxNQUFkLElBQXdCQSxNQUF4QixHQUFpQyxDQUFDQSxNQUFELENBRjVCLEVBR0w7QUFBQ0ksY0FBUTtBQUFULEtBSEssRUFJTEMsTUFKSyxHQUlJLENBSlg7QUFLRDs7QUFDRCxTQUFPLEtBQVA7QUFDRDtBQUVEOzs7Ozs7OztBQU1PLFNBQVNDLEdBQVQsQ0FBYUMsSUFBYixFQUFtQjtBQUN4QixNQUFNQyxPQUFPRCxLQUFLQyxJQUFs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akMsQ0FMQTtBQU1WM0g7QUFOVSxTQUFaO0FBUUFsQixhQUFLOEksUUFBTCxDQUFjaEMsT0FBZCxFQUF1QmxHLEtBQXZCO0FBQ0EsOEJBQVdBLEtBQVg7QUFDQVosYUFBSzBDLGdCQUFMLENBQXNCLE1BQXRCLEVBQThCLHNCQUFROUIsS0FBUixDQUE5QjtBQUNEOztBQW5CTTtBQURKLEdBQVA7QUF1QkQiLCJmaWxlIjoiaW5zdHJ1bWVudGVyLmpzIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0ICogYXMgdHlwZXMgZnJvbSAnQGJhYmVsL3R5cGVzJztcbmltcG9ydCBtaWNyb21hdGNoIGZyb20gJ21pY3JvbWF0Y2gnO1xuaW1wb3J0IHtyZWxhdGl2ZX0gZnJvbSAncGF0aCc7XG5cbmltcG9ydCBwcmVsdWRlIGZyb20gJy4vcHJlbHVkZSc7XG5pbXBvcnQgbWV0YSBmcm9tICcuL21ldGEnO1xuaW1wb3J0IHthcHBseVJ1bGVzLCBhZGRSdWxlc30gZnJvbSAnLi90YWdzJztcblxuZXhwb3J0IGZ1bmN0aW9uIHNraXAoe2lnbm9yZSwgb25seX0sIGZpbGUpIHtcbiAgaWYgKG9ubHkpIHtcbiAgICByZXR1cm4gbWljcm9tYXRjaChcbiAgICAgIFtmaWxlXSxcbiAgICAgIEFycmF5LmlzQXJyYXkob25seSkgPyBvbmx5IDogW29ubHldLFxuICAgICAge25vY2FzZTogdHJ1ZX1cbiAgICApLmxlbmd0aCA8PSAwO1xuICB9XG4gIGlmIChpZ25vcmUpIHtcbiAgICByZXR1cm4gbWljcm9tYXRjaChcbiAgICAgIFtmaWxlXSxcbiAgICAgIEFycmF5LmlzQXJyYXkoaWdub3JlKSA/IGlnbm9yZSA6IFtpZ25vcmVdLFxuICAgICAge25vY2FzZTogdHJ1ZX1cbiAgICApLmxlbmd0aCA+IDA7XG4gIH1cbiAgcmV0dXJuIGZhbHNlO1xufVxuXG4vKipcbiAqIENyZWF0ZSBhbiBvcGFxdWUsIHVuaXF1ZSBrZXkgZm9yIGEgZ2l2ZW4gbm9kZS4gVXNlZnVsIGZvciB0YWdnaW5nIHRoZSBub2RlXG4gKiBpbiBzZXBhcmF0ZSBwbGFjZXMuXG4gKiBAcGFyYW0ge09iamVjdH0gcGF0aCBCYWJlbCBwYXRoIHRvIGRlcml2ZSBrZXkgZnJvbS5cbiAqIEByZXR1cm5zIHtTdHJpbmd9IFN0cmluZyBrZXkuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBrZXkocGF0aCkge1xuICBjb25zdCBub2RlID0gcGF0aC5ub2RlO1xuICBpZiAobm9kZS5sb2MpIHtcbiAgICBjb25zdCBsb2NhdGlvbiA9IG5vZGUubG9jLnN0YXJ0O1xuICAgIHJldHVybiBgJHtsb2NhdGlvbi5saW5lfToke2xvY2F0aW9uLmNvbHVtbn1gO1xuICB9XG4gIHRocm93IG5ldyBUeXBlRXJyb3IoJ1BhdGggbXVzdCBoYXZlIHZhbGlkIGxvY2F0aW9uLicpO1xufVxuXG4vKipcbiAqIFNvbWUgbm9kZXMgbmVlZCB0byBtYXJrZWQgYXMgbm9uLWluc3RydW1lbnRhYmxlOyBzaW5jZSBiYWJlbCB3aWxsIGFwcGx5XG4gKiBvdXIgcGx1Z2luIHRvIG5vZGVzIHdlIGNyZWF0ZSwgd2UgaGF2ZSB0byBiZSBjYXJlZnVsIHRvIG5vdCBwdXQgb3Vyc2VsdmVzXG4gKiBpbnRvIGFuIGluZmluaXRlIGxvb3AuXG4gKiBAcGFyYW0ge09iamVjdH0gbm9kZSBCYWJlbCBBU1Qgbm9kZS5cbiAqIEByZXR1cm5zIHtPYmplY3R9IEJhYmVsIEFTVCBub2RlIHRoYXQgd29uJ3QgYmUgaW5zdHJ1bWVudGVkLlxuICovXG5mdW5jdGlvbiBYKG5vZGUpIHtcbiAgbm9kZS5fX2FkYW5hID0gdHJ1ZTtcbiAgcmV0dXJuIG5vZGU7XG59XG5cbmZ1bmN0aW9uIGlnbm9yZShwYXRoKSB7XG4gIHJldHVybiAoIXBhdGgubm9kZSB8fCAhcGF0aC5ub2RlLmxvYyB8fCBwYXRoLm5vZGUuX19hZGFuYSk7XG59XG5cbmZ1bmN0aW9uIHN0YW5kYXJkaXplKGxpc3RlbmVyKSB7XG4gIHJldHVybiAocGF0aCwgc3RhdGUpID0+IGlnbm9yZShwYXRoKSA/IHVuZGVmaW5lZCA6IGxpc3RlbmVyKHBhdGgsIHN0YXRlKTtcbn1cblxuLyoqXG4gKiBDcmVhdGUgdGhlIHRyYW5zZm9ybS1hZGFuYSBiYWJlbCBwbHVnaW4uXG4gKiBAcmV0dXJucyB7T2JqZWN0fSBgYmFiZWxgIHBsdWdpbiBvYmplY3QuXG4gKi9cbmV4cG9ydCBkZWZhdWx0IGZ1bmN0aW9uIGluc3RydW1lbnRlcigpIHtcbiAgLyoqXG4gICAqIENyZWF0ZSBhIGNodW5rIG9mIGNvZGUgdGhhdCBtYXJrcyB0aGUgc3BlY2lmaWVkIG5vZGUgYXMgaGF2aW5nXG4gICAqIGJlZW4gZXhlY3V0ZWQuXG4gICAqIEBwYXJhbSB7T2JqZWN0fSBzdGF0ZSBgYmFiZWxgIHN0YXRlIGZvciB0aGUgcGF0aCB0aGF0J3MgYmVpbmcgd2Fsa2VkLlxuICAgKiBAcGFyYW0ge09iamVjdH0gb3B0aW9ucyBDb25maWd1cmUgaG93IHRoZSBtYXJrZXIgYmVoYXZlcy5cbiAgICogQHJldHVybnMge09iamVjdH0gQVNUIG5vZGUgZm9yIG1hcmtpbmcgY292ZXJhZ2UuXG4gICAqL1xuICBmdW5jdGlvbiBjcmVhdGVNYXJrZXIoc3RhdGUsIG9wdGlvbnMpIHtcbiAgICBjb25zdCB7dGFncywgbG9jLCBuYW1lLCBncm91cH0gPSBvcHRpb25zO1xuICAgIGNvbnN0IGNvdmVyYWdlID0gbWV0YShzdGF0ZSk7XG4gICAgY29uc3QgaWQgPSBjb3ZlcmFnZS5lbnRyaWVzLmxlbmd0aDtcblxuICAgIGNvdmVyYWdlLmVudHJpZXMucHVzaCh7XG4gICAgICBpZCxcbiAgICAgIGxvYyxcbiAgICAgIHRhZ3MsXG4gICAgICBuYW1lLFxuICAgICAgZ3JvdXAsXG4gICAgICBjb3VudDogMCxcbiAgICB9KTtcblxuICAgIC8vIE1ha2VyIGlzIHNpbXBseSBhIHN0YXRlbWVudCBpbmNyZW1lbnRpbmcgYSBjb3ZlcmFnZSB2YXJpYWJsZS5cbiAgICByZXR1cm4gWCh0eXBlcy51cGRhdGVFeHByZXNzaW9uKCcrKycsIHR5cGVzLm1lbWJlckV4cHJlc3Npb24oXG4gICAgICB0eXBlcy5tZW1iZXJFeHByZXNzaW9uKFxuICAgICAgICBjb3ZlcmFnZS52YXJpYWJsZSxcbiAgICAgICAgdHlwZXMubnVtZXJpY0xpdGVyYWwoaWQpLFxuICAgICAgICB0cnVlXG4gICAgICApLFxuICAgICAgdHlwZXMuc3RyaW5nTGl0ZXJhbCgnY291bnQnKSxcbiAgICAgIHRydWVcbiAgICApLCB0cnVlKSk7XG4gIH1cblxuICAvKipcbiAgICogW2lzSW5zdHJ1bWVudGFibGVTdGF0ZW1lbnQgZGVzY3JpcHRpb25dXG4gICAqIEBwYXJhbSAgIHtbdHlwZV19ICBwYXRoIFtkZXNjcmlwdGlvbl1cbiAgICogQHJldHVybnMge0Jvb2xlYW59ICAgICAgW2Rlc2NyaXB0aW9uXVxuICAgKi9cbiAgZnVuY3Rpb24gaXNJbnN0cnVtZW50YWJsZVN0YXRlbWVudChwYXRoKSB7XG4gICAgY29uc3QgcGFyZW50ID0gcGF0aC5wYXJlbnRQYXRoO1xuICAgIHJldHVybiAhcGFyZW50LmlzUmV0dXJuU3RhdGVtZW50KCkgJiZcbiAgICAgICFwYXJlbnQuaXNWYXJpYWJsZURlY2xhcmF0aW9uKCkgJiZcbiAgICAgICFwYXJlbnQuaXNFeHBvcnREZWNsYXJhdGlvbigpICYmXG4gICAgICAhcGFyZW50LmlzRnVuY3Rpb25EZWNsYXJhdGlvbigpICYmXG4gICAgICAhcGFyZW50LmlzSWZTdGF0ZW1lbnQoKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBJbmplY3QgYSBtYXJrZXIgdGhhdCBtZWFzdXJlcyB3aGV0aGVyIHRoZSBub2RlIGZvciB0aGUgZ2l2ZW4gcGF0aCBoYXNcbiAgICogYmVlbiBydW4gb3Igbm90LlxuICAgKiBAcGFyYW0ge09iamVjdH0gcGF0aCAgICBbZGVzY3JpcHRpb25dXG4gICAqIEBwYXJhbSB7T2JqZWN0fSBzdGF0ZSAgIFtkZXNjcmlwdGlvbl1cbiAgICogQHBhcmFtIHtPYmplY3R9IG9wdGlvbnMgW2Rlc2NyaXB0aW9uXVxuICAgKiBAcmV0dXJucyB7dm9pZH1cbiAgICovXG4gIGZ1bmN0aW9uIGluc3RydW1lbnQocGF0aCwgc3RhdGUsIG9wdGlvbnMpIHtcbiAgICAvLyBUaGlzIGZ1bmN0aW9uIGlzIGhlcmUgYmVjYXVzZSBpc0luc3RydW1lbnRhYmxlU3RhdGVtZW50KCkgaXMgYmVpbmdcbiAgICAvLyBjYWxsZWQ7IHdlIGNhbid0IGNyZWF0ZSB0aGUgbWFya2VyIHdpdGhvdXQga25vd2luZyB0aGUgcmVzdWx0IG9mIHRoYXQsXG4gICAgLy8gb3RoZXJ3aXNlIGRlYWQgbWFya2VycyB3aWxsIGJlIGNyZWF0ZWQuXG4gICAgZnVuY3Rpb24gbWFya2VyKCkge1xuICAgICAgcmV0dXJuIGNyZWF0ZU1hcmtlcihzdGF0ZSwge1xuICAgICAgICBsb2M6IHBhdGgubm9kZS5sb2MsXG4gICAgICAgIC4uLm9wdGlvbnMsXG4gICAgICB9KTtcbiAgICB9XG5cbiAgICBpZiAocGF0aC5pc0Jsb2NrU3RhdGVtZW50KCkpIHtcbiAgICAgIHBhdGgudW5zaGlmdENvbnRhaW5lcignYm9keScsIFgodHlwZXMuZXhwcmVzc2lvblN0YXRlbWVudChtYXJrZXIoKSkpKTtcbiAgICB9IGVsc2UgaWYgKHBhdGguaXNFeHByZXNzaW9uKCkpIHtcbiAgICAgIHBhdGgucmVwbGFjZVdpdGgoWCh0eXBlcy5zZXF1ZW5jZUV4cHJlc3Npb24oW21hcmtlcigpLCBwYXRoLm5vZGVdKSkpO1xuICAgIH0gZWxzZSBpZiAocGF0aC5pc1N0YXRlbWVudCgpKSB7XG4gICAgICBpZiAoaXNJbnN0cnVtZW50YWJsZVN0YXRlbWVudChwYXRoKSkge1xuICAgICAgICBwYXRoLmluc2VydEJlZm9yZShYKHR5cGVzLmV4cHJlc3Npb25TdGF0ZW1lbnQobWFya2VyKCkpKSk7XG4gICAgICB9XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIFt2aXNpdFN0YXRlbWVudCBkZXNjcmlwdGlvbl1cbiAgICogQHBhcmFtIHtbdHlwZV19IHBhdGggIFtkZXNjcmlwdGlvbl1cbiAgICogQHBhcmFtIHtbdHlwZV19IHN0YXRlIFtkZXNjcmlwdGlvbl1cbiAgICogQHJldHVybnMge3ZvaWR9XG4gICAqL1xuICBmdW5jdGlvbiB2aXNpdFN0YXRlbWVudChwYXRoLCBzdGF0ZSkge1xuICAgIGluc3RydW1lbnQocGF0aCwgc3RhdGUsIHtcbiAgICAgIHRhZ3M6IFsnc3RhdGVtZW50JywgJ2xpbmUnXSxcbiAgICAgIGxvYzogcGF0aC5ub2RlLmxvYyxcbiAgICB9KTtcbiAgfVxuXG4gIC8qKlxuICAgKiBUaGUgZnVuY3Rpb24gdmlzaXRvciBpcyBtYWlubHkgdG8gdHJhY2sgdGhlIGRlZmluaXRpb25zIG9mIGZ1bmN0aW9ucztcbiAgICogYmVpbmcgYWJsZSBlbnN1cmUgaG93IG1hbnkgb2YgeW91ciBmdW5jdGlvbnMgaGF2ZSBhY3R1YWxseSBiZWVuIGludm9rZWQuXG4gICAqIEBwYXJhbSB7W3R5cGVdfSBwYXRoICBbZGVzY3JpcHRpb25dXG4gICAqIEBwYXJhbSB7W3R5cGVdfSBzdGF0ZSBbZGVzY3JpcHRpb25dXG4gICAqIEByZXR1cm5zIHt2b2lkfVxuICAgKi9cbiAgZnVuY3Rpb24gdmlzaXRGdW5jdGlvbihwYXRoLCBzdGF0ZSkge1xuICAgIGluc3RydW1lbnQocGF0aC5nZXQoJ2JvZHknKSwgc3RhdGUsIHtcbiAgICAgIHRhZ3M6IFsnZnVuY3Rpb24nXSxcbiAgICAgIG5hbWU6IHBhdGgubm9kZS5pZCA/IHBhdGgubm9kZS5pZC5uYW1lIDogYEAke2tleShwYXRoKX1gLFxuICAgICAgbG9jOiBwYXRoLm5vZGUubG9jLFxuICAgIH0pO1xuICB9XG5cbiAgLyoqXG4gICAqIE11bHRpcGxlIGJyYW5jaGVzIGJhc2VkIG9uIHRoZSByZXN1bHQgb2YgYGNhc2UgX2AgYW5kIGBkZWZhdWx0YC4gSWYgeW91XG4gICAqIGRvIG5vdCBwcm92aWRlIGEgYGRlZmF1bHRgIG9uZSB3aWxsIGJlIGludGVsbGlnZW50bHkgYWRkZWQgZm9yIHlvdSxcbiAgICogZm9yY2luZyB5b3UgdG8gY292ZXIgdGhhdCBjYXNlLlxuICAgKiBAcGFyYW0ge1t0eXBlXX0gcGF0aCAgW2Rlc2NyaXB0aW9uXVxuICAgKiBAcGFyYW0ge1t0eXBlXX0gc3RhdGUgW2Rlc2NyaXB0aW9uXVxuICAgKiBAcmV0dXJucyB7dm9pZH1cbiAgICovXG4gIGZ1bmN0aW9uIHZpc2l0U3dpdGNoU3RhdGVtZW50KHBhdGgsIHN0YXRlKSB7XG4gICAgbGV0IGhhc0RlZmF1bHQgPSBmYWxzZTtcbiAgICBwYXRoLmdldCgnY2FzZXMnKS5mb3JFYWNoKChlbnRyeSkgPT4ge1xuICAgICAgaWYgKGVudHJ5Lm5vZGUudGVzdCkge1xuICAgICAgICBhZGRSdWxlcyhzdGF0ZSwgZW50cnkubm9kZS5sb2MsIGVudHJ5Lm5vZGUudGVzdC50cmFpbGluZ0NvbW1lbnRzKTtcbiAgICAgIH1cbiAgICAgIGlmIChlbnRyeS5ub2RlLmNvbnNlcXVlbnQubGVuZ3RoID4gMSkge1xuICAgICAgICBhZGRSdWxlcyhcbiAgICAgICAgICBzdGF0ZSxcbiAgICAgICAgICBlbnRyeS5ub2RlLmxvYyxcbiAgICAgICAgICBlbnRyeS5ub2RlLmNvbnNlcXVlbnRbMF0ubGVhZGluZ0NvbW1lbnRzXG4gICAgICAgICk7XG4gICAgICB9XG5cbiAgICAgIGlmIChlbnRyeS5ub2RlLnRlc3QgPT09IG51bGwpIHtcbiAgICAgICAgaGFzRGVmYXVsdCA9IHRydWU7XG4gICAgICB9XG4gICAgICBlbnRyeS51bnNoaWZ0Q29udGFpbmVyKCdjb25zZXF1ZW50JywgY3JlYXRlTWFya2VyKHN0YXRlLCB7XG4gICAgICAgIHRhZ3M6IFsnYnJhbmNoJywgJ3N3aXRjaCddLFxuICAgICAgICBsb2M6IGVudHJ5Lm5vZGUubG9jLFxuICAgICAgICBncm91cDoga2V5KHBhdGgpLFxuICAgICAgfSkpO1xuICAgIH0pO1xuXG4gICAgLy8gRGVmYXVsdCBpcyB0ZWNobmljYWxseSBhIGJyYW5jaCwganVzdCBsaWtlIGlmIHN0YXRlbWVudHMgd2l0aG91dFxuICAgIC8vIGVsc2UncyBhcmUgYWxzbyB0ZWNobmljYWxseSBhIGJyYW5jaC5cbiAgICBpZiAoIWhhc0RlZmF1bHQpIHtcbiAgICAgIC8vIEFkZCBhbiBleHRyYSBicmVhayB0byB0aGUgZW5kIG9mIHRoZSBsYXN0IGNhc2UgaW4gY2FzZSBzb21lIGlkaW90XG4gICAgICAvLyBmb3Jnb3QgdG8gYWRkIGl0LlxuICAgICAgY29uc3QgY2FzZXMgPSBwYXRoLmdldCgnY2FzZXMnKTtcbiAgICAgIGlmIChjYXNlcy5sZW5ndGggPiAwKSB7XG4gICAgICAgIGNhc2VzW2Nhc2VzLmxlbmd0aCAtIDFdLnB1c2hDb250YWluZXIoXG4gICAgICAgICAgJ2NvbnNlcXVlbnQnLFxuICAgICAgICAgIHR5cGVzLmJyZWFrU3RhdGVtZW50KClcbiAgICAgICAgKTtcbiAgICAgIH1cbiAgICAgIC8vIEZpbmFsbHkgYWRkIHRoZSBkZWZhdWx0IGNhc2UuXG4gICAgICBwYXRoLnB1c2hDb250YWluZXIoJ2Nhc2VzJywgdHlwZXMuc3dpdGNoQ2FzZShudWxsLCBbXG4gICAgICAgIHR5cGVzLmV4cHJlc3Npb25TdGF0ZW1lbnQoY3JlYXRlTWFya2VyKHN0YXRlLCB7XG4gICAgICAgICAgdGFnczogWydicmFuY2gnLCAnc3dpdGNoJ10sXG4gICAgICAgICAgbG9jOiB7XG4gICAgICAgICAgICBzdGFydDogcGF0aC5ub2RlLmxvYy5lbmQsXG4gICAgICAgICAgICBlbmQ6IHBhdGgubm9kZS5sb2MuZW5kLFxuICAgICAgICAgIH0sXG4gICAgICAgICAgZ3JvdXA6IGtleShwYXRoKSxcbiAgICAgICAgfSkpLFxuICAgICAgICB0eXBlcy5icmVha1N0YXRlbWVudCgpLFxuICAgICAgXSkpO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBbdmlzaXRWYXJpYWJsZURlY2xhcmF0aW9uIGRlc2NyaXB0aW9uXVxuICAgKiBAcGFyYW0ge1t0eXBlXX0gcGF0aCAgW2Rlc2NyaXB0aW9uXVxuICAgKiBAcGFyYW0ge1t0eXBlXX0gc3RhdGUgW2Rlc2NyaXB0aW9uXVxuICAgKiBAcmV0dXJucyB7dm9pZH1cbiAgICovXG4gIGZ1bmN0aW9uIHZpc2l0VmFyaWFibGVEZWNsYXJhdGlvbihwYXRoLCBzdGF0ZSkge1xuICAgIHBhdGguZ2V0KCdkZWNsYXJhdGlvbnMnKS5mb3JFYWNoKChkZWNsKSA9PiB7XG4gICAgICBpZiAoZGVjbC5oYXMoJ2luaXQnKSkge1xuICAgICAgICBpbnN0cnVtZW50KGRlY2wuZ2V0KCdpbml0JyksIHN0YXRlLCB7XG4gICAgICAgICAgdGFnczogWydzdGF0ZW1lbnQnLCAndmFyaWFibGUnLCAnbGluZSddLFxuICAgICAgICB9KTtcbiAgICAgIH1cbiAgICB9KTtcbiAgfVxuXG4gIC8qKlxuICAgKiBJbmNsdWRlcyBib3RoIHdoaWxlIGFuZCBkby13aGlsZSBsb29wcy4gVGhleSBjb250YWluIGEgc2luZ2xlIGJyYW5jaCB3aGljaFxuICAgKiB0ZXN0cyB0aGUgbG9vcCBjb25kaXRpb24uXG4gICAqIEBwYXJhbSB7W3R5cGVdfSBwYXRoICBbZGVzY3JpcHRpb25dXG4gICAqIEBwYXJhbSB7W3R5cGVdfSBzdGF0ZSBbZGVzY3JpcHRpb25dXG4gICAqIEByZXR1cm5zIHt2b2lkfVxuICAgKi9cbiAgZnVuY3Rpb24gdmlzaXRXaGlsZUxvb3AocGF0aCwgc3RhdGUpIHtcbiAgICBjb25zdCB0ZXN0ID0gcGF0aC5nZXQoJ3Rlc3QnKTtcbiAgICBjb25zdCBncm91cCA9IGtleShwYXRoKTtcbiAgICAvLyBUaGlzIGlzIGEgcGFydGljdWxhcmx5IGNsZXZlciB1c2Ugb2YgdGhlIGZhY3QgSlMgb3BlcmF0b3JzIGFyZSBzaG9ydC1cbiAgICAvLyBjaXJjdWl0aW5nLiBUbyBpbnN0cnVtZW50IGEgbG9vcCBvbmUgX2Nhbm5vdF8gYWRkIGEgbWFya2VyIG9uIHRoZSBvdXRzaWRlXG4gICAgLy8gb2YgdGhlIGxvb3AgYm9keSBkdWUgdG8gd2VpcmQgY2FzZXMgb2YgdGhpbmdzIHdoZXJlIGxvb3BzIGFyZSBpbiBub24tXG4gICAgLy8gYmxvY2sgaWYgc3RhdGVtZW50cy4gU28gaW5zdGVhZCwgY3JlYXRlIHRoZSBmb2xsb3dpbmcgbWVjaGFuaXNtOlxuICAgIC8vICgoY29uZGl0aW9uICYmIEEpIHx8ICFCKSB3aGVyZSBBIGFuZCBCIGFyZSBtYXJrZXJzLiBTaW5jZSBtYXJrZXJzIGFyZVxuICAgIC8vIHBvc3RmaXgsIHRoZXkncmUgYWx3YXlzIHRydWUuIEVyZ28sIEEgaXMgb25seSBpbmNyZW1lbnRlZCB3aGVuIGNvbmRpdGlvblxuICAgIC8vIGlzIHRydWUsIEIgb25seSB3aGVuIGl0J3MgZmFsc2UgYW5kIHRoZSB0cnV0aCB2YWx1ZSBvZiB0aGUgd2hvbGVcbiAgICAvLyBzdGF0ZW1lbnQgaXMgcHJlc2VydmVkLiBOZWF0by5cbiAgICB0ZXN0LnJlcGxhY2VXaXRoKHR5cGVzLmxvZ2ljYWxFeHByZXNzaW9uKFxuICAgICAgJ3x8JyxcbiAgICAgIHR5cGVzLmxvZ2ljYWxFeHByZXNzaW9uKFxuICAgICAgICAnJiYnLFxuICAgICAgICBYKHRlc3Qubm9kZSksXG4gICAgICAgIGNyZWF0ZU1hcmtlcihzdGF0ZSwge1xuICAgICAgICAgIHRhZ3M6IFsnYnJhbmNoJywgJ2xpbmUnLCAnc3RhdGVtZW50JywgJ2xvb3AnLCAnd2hpbGUnXSxcbiAgICAgICAgICBsb2M6IHRlc3Qubm9kZS5sb2MsXG4gICAgICAgICAgZ3JvdXAsXG4gICAgICAgIH0pXG4gICAgICApLFxuICAgICAgdHlwZXMudW5hcnlFeHByZXNzaW9uKFxuICAgICAgICAnIScsXG4gICAgICAgIGNyZWF0ZU1hcmtlcihzdGF0ZSwge1xuICAgICAgICAgIHRhZ3M6IFsnYnJhbmNoJywgJ2xpbmUnLCAnbG9vcCcsICd3aGlsZSddLFxuICAgICAgICAgIGxvYzogdGVzdC5ub2RlLmxvYyxcbiAgICAgICAgICBncm91cCxcbiAgICAgICAgfSlcbiAgICAgIClcbiAgICApKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBUaGUgdHJ5IGJsb2NrIGNhbiBlaXRoZXIgZnVsbHkgc3VjY2VlZCAobm8gZXJyb3IpIG9yIGl0IGNhbiB0aHJvdy4gQm90aFxuICAgKiBjYXNlcyBhcmUgYWNjb3VudGVkIGZvci5cbiAgICogQHBhcmFtIHtbdHlwZV19IHBhdGggIFtkZXNjcmlwdGlvbl1cbiAgICogQHBhcmFtIHtbdHlwZV19IHN0YXRlIFtkZXNjcmlwdGlvbl1cbiAgICogQHJldHVybnMge3ZvaWR9XG4gICAqL1xuICBmdW5jdGlvbiB2aXNpdFRyeVN0YXRlbWVudChwYXRoLCBzdGF0ZSkge1xuICAgIGNvbnN0IGdyb3VwID0ga2V5KHBhdGgpO1xuICAgIGNvbnN0IGJvZHkgPSBwYXRoLmdldCgnYmxvY2snKTtcbiAgICBjb25zdCB0cmlnZ2VyID0gcGF0aC5zY29wZS5nZW5lcmF0ZURlY2xhcmVkVWlkSWRlbnRpZmllcignX2V4Y2VwdGlvbicpO1xuICAgIGFkZFJ1bGVzKHN0YXRlLCBib2R5Lm5vZGUubG9jLCBib2R5Lm5vZGUubGVhZGluZ0NvbW1lbnRzKTtcbiAgICBwYXRoLmdldCgnYmxvY2snKS51bnNoaWZ0Q29udGFpbmVyKCdib2R5JywgdHlwZXMuZXhwcmVzc2lvblN0YXRlbWVudChcbiAgICAgIHR5cGVzLmFzc2lnbm1lbnRFeHByZXNzaW9uKCc9JywgdHJp