@angular/core
Version:
Angular - the core framework
188 lines • 29.7 kB
JavaScript
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
(function (factory) {
if (typeof module === "object" && typeof module.exports === "object") {
var v = factory(require, exports);
if (v !== undefined) module.exports = v;
}
else if (typeof define === "function" && define.amd) {
define("@angular/core/schematics/migrations/testbed-teardown/util", ["require", "exports", "typescript", "@angular/core/schematics/utils/typescript/imports", "@angular/core/schematics/utils/typescript/symbol"], factory);
}
})(function (require, exports) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.migrateTestModuleMetadataLiteral = exports.getInitTestEnvironmentLiteralReplacement = exports.findTestModuleMetadataNodes = exports.findInitTestEnvironmentCalls = void 0;
const typescript_1 = __importDefault(require("typescript"));
const imports_1 = require("@angular/core/schematics/utils/typescript/imports");
const symbol_1 = require("@angular/core/schematics/utils/typescript/symbol");
/** Finds the `initTestEnvironment` calls that need to be migrated. */
function findInitTestEnvironmentCalls(typeChecker, allSourceFiles) {
const callsToMigrate = new Set();
let totalCalls = 0;
allSourceFiles.forEach(sourceFile => {
sourceFile.forEachChild(function walk(node) {
if (typescript_1.default.isCallExpression(node) && typescript_1.default.isPropertyAccessExpression(node.expression) &&
typescript_1.default.isIdentifier(node.expression.name) &&
node.expression.name.text === 'initTestEnvironment' &&
isTestBedAccess(typeChecker, node.expression)) {
totalCalls++;
if (shouldMigrateInitTestEnvironment(node)) {
callsToMigrate.add(node);
}
}
node.forEachChild(walk);
});
});
return {
// Sort the nodes so that they will be migrated in reverse source order (nodes at the end of
// the file are migrated first). This avoids issues where a migrated node will offset the
// bounds of all nodes that come after it. Note that the nodes here are from all of the
// passed in source files, but that doesn't matter since the later nodes will still appear
// after the earlier ones.
callsToMigrate: sortInReverseSourceOrder(Array.from(callsToMigrate)),
totalCalls
};
}
exports.findInitTestEnvironmentCalls = findInitTestEnvironmentCalls;
/** Finds the `configureTestingModule` and `withModule` calls that need to be migrated. */
function findTestModuleMetadataNodes(typeChecker, sourceFile) {
const testModuleMetadataLiterals = new Set();
const withModuleImport = (0, imports_1.getImportSpecifier)(sourceFile, '@angular/core/testing', 'withModule');
sourceFile.forEachChild(function walk(node) {
if (typescript_1.default.isCallExpression(node)) {
const isConfigureTestingModuleCall = typescript_1.default.isPropertyAccessExpression(node.expression) &&
typescript_1.default.isIdentifier(node.expression.name) &&
node.expression.name.text === 'configureTestingModule' &&
isTestBedAccess(typeChecker, node.expression) && shouldMigrateModuleConfigCall(node);
const isWithModuleCall = withModuleImport && typescript_1.default.isIdentifier(node.expression) &&
(0, symbol_1.isReferenceToImport)(typeChecker, node.expression, withModuleImport) &&
shouldMigrateModuleConfigCall(node);
if (isConfigureTestingModuleCall || isWithModuleCall) {
testModuleMetadataLiterals.add(node.arguments[0]);
}
}
node.forEachChild(walk);
});
// Sort the nodes so that they will be migrated in reverse source order (nodes at the end of
// the file are migrated first). This avoids issues where a migrated node will offset the
// bounds of all nodes that come after it.
return sortInReverseSourceOrder(Array.from(testModuleMetadataLiterals));
}
exports.findTestModuleMetadataNodes = findTestModuleMetadataNodes;
/**
* Gets data that can be used to migrate a call to `TestBed.initTestEnvironment`.
* The returned `span` is used to mark the text that should be replaced while the `text`
* is the code that should be inserted instead.
*/
function getInitTestEnvironmentLiteralReplacement(node, printer) {
const literalProperties = [];
const lastArg = node.arguments[node.arguments.length - 1];
let span;
let prefix;
if (node.arguments.length > 2) {
if (isFunction(lastArg)) {
// If the last argument is a function, add the function as the `aotSummaries` property.
literalProperties.push(typescript_1.default.createPropertyAssignment('aotSummaries', lastArg));
}
else if (typescript_1.default.isObjectLiteralExpression(lastArg)) {
// If the property is an object literal, copy over all the properties.
literalProperties.push(...lastArg.properties);
}
prefix = '';
span = { start: lastArg.getStart(), end: lastArg.getEnd(), length: lastArg.getWidth() };
}
else {
const start = lastArg.getEnd();
prefix = ', ';
span = { start, end: start, length: 0 };
}
// Finally push the teardown object so that it appears last.
literalProperties.push(createTeardownAssignment());
return {
span,
text: prefix +
printer.printNode(typescript_1.default.EmitHint.Unspecified, typescript_1.default.createObjectLiteral(literalProperties, true), node.getSourceFile())
};
}
exports.getInitTestEnvironmentLiteralReplacement = getInitTestEnvironmentLiteralReplacement;
/** Migrates an object literal that is passed into `configureTestingModule` or `withModule`. */
function migrateTestModuleMetadataLiteral(node) {
return typescript_1.default.createObjectLiteral([...node.properties, createTeardownAssignment()], node.properties.length > 0);
}
exports.migrateTestModuleMetadataLiteral = migrateTestModuleMetadataLiteral;
/** Returns whether a property access points to `TestBed`. */
function isTestBedAccess(typeChecker, node) {
var _a, _b;
const symbolName = (_b = (_a = typeChecker.getTypeAtLocation(node.expression)) === null || _a === void 0 ? void 0 : _a.getSymbol()) === null || _b === void 0 ? void 0 : _b.getName();
return symbolName === 'TestBed' || symbolName === 'TestBedStatic';
}
/** Whether a call to `initTestEnvironment` should be migrated. */
function shouldMigrateInitTestEnvironment(node) {
// If there is no third argument, we definitely have to migrate it.
if (node.arguments.length === 2) {
return true;
}
// This is technically a type error so we shouldn't mess with it.
if (node.arguments.length < 2) {
return false;
}
// Otherwise we need to figure out if the `teardown` flag is set on the last argument.
const lastArg = node.arguments[2];
// Note: the checks below will identify something like `initTestEnvironment(..., ..., {})`,
// but they'll ignore a variable being passed in as the last argument like `const config = {};
// initTestEnvironment(..., ..., config)`. While we can resolve the variable to its declaration
// using `typeChecker.getTypeAtLocation(lastArg).getSymbol()?.valueDeclaration`, we deliberately
// don't, because it introduces some complexity and we may end up breaking user code. E.g.
// the `config` from the example above may be passed in to other functions or the `teardown`
// flag could be added later on by a function call.
// If the argument is an object literal and there are no
// properties called `teardown`, we have to migrate it.
if (isObjectLiteralWithoutTeardown(lastArg)) {
return true;
}
// If the last argument is an `aotSummaries` function, we also have to migrate.
if (isFunction(lastArg)) {
return true;
}
// Otherwise don't migrate if we couldn't identify the last argument.
return false;
}
/**
* Whether a call to a module configuration function should be migrated. This covers
* `TestBed.configureTestingModule` and `withModule` since they both accept `TestModuleMetadata`
* as their first argument.
*/
function shouldMigrateModuleConfigCall(node) {
return node.arguments.length > 0 && isObjectLiteralWithoutTeardown(node.arguments[0]);
}
/** Returns whether a node is a function literal. */
function isFunction(node) {
return typescript_1.default.isArrowFunction(node) || typescript_1.default.isFunctionExpression(node) ||
typescript_1.default.isFunctionDeclaration(node);
}
/** Checks whether a node is an object literal that doesn't contain a property called `teardown`. */
function isObjectLiteralWithoutTeardown(node) {
return typescript_1.default.isObjectLiteralExpression(node) && !node.properties.find(prop => {
var _a;
return ((_a = prop.name) === null || _a === void 0 ? void 0 : _a.getText()) === 'teardown';
});
}
/** Creates a teardown configuration property assignment. */
function createTeardownAssignment() {
// `teardown: {destroyAfterEach: false}`
return typescript_1.default.createPropertyAssignment('teardown', typescript_1.default.createObjectLiteral([typescript_1.default.createPropertyAssignment('destroyAfterEach', typescript_1.default.createFalse())]));
}
/** Sorts an array of AST nodes in reverse source order. */
function sortInReverseSourceOrder(nodes) {
return nodes.sort((a, b) => b.getEnd() - a.getEnd());
}
});
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidXRpbC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uLy4uLy4uLy4uL3BhY2thZ2VzL2NvcmUvc2NoZW1hdGljcy9taWdyYXRpb25zL3Rlc3RiZWQtdGVhcmRvd24vdXRpbC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7Ozs7O0dBTUc7Ozs7Ozs7Ozs7Ozs7Ozs7SUFFSCw0REFBNEI7SUFFNUIsK0VBQWtFO0lBQ2xFLDZFQUFrRTtJQVVsRSxzRUFBc0U7SUFDdEUsU0FBZ0IsNEJBQTRCLENBQ3hDLFdBQTJCLEVBQUUsY0FBK0I7UUFDOUQsTUFBTSxjQUFjLEdBQUcsSUFBSSxHQUFHLEVBQXFCLENBQUM7UUFDcEQsSUFBSSxVQUFVLEdBQUcsQ0FBQyxDQUFDO1FBRW5CLGNBQWMsQ0FBQyxPQUFPLENBQUMsVUFBVSxDQUFDLEVBQUU7WUFDbEMsVUFBVSxDQUFDLFlBQVksQ0FBQyxTQUFTLElBQUksQ0FBQyxJQUFhO2dCQUNqRCxJQUFJLG9CQUFFLENBQUMsZ0JBQWdCLENBQUMsSUFBSSxDQUFDLElBQUksb0JBQUUsQ0FBQywwQkFBMEIsQ0FBQyxJQUFJLENBQUMsVUFBVSxDQUFDO29CQUMzRSxvQkFBRSxDQUFDLFlBQVksQ0FBQyxJQUFJLENBQUMsVUFBVSxDQUFDLElBQUksQ0FBQztvQkFDckMsSUFBSSxDQUFDLFVBQVUsQ0FBQyxJQUFJLENBQUMsSUFBSSxLQUFLLHFCQUFxQjtvQkFDbkQsZUFBZSxDQUFDLFdBQVcsRUFBRSxJQUFJLENBQUMsVUFBVSxDQUFDLEVBQUU7b0JBQ2pELFVBQVUsRUFBRSxDQUFDO29CQUNiLElBQUksZ0NBQWdDLENBQUMsSUFBSSxDQUFDLEVBQUU7d0JBQzFDLGNBQWMsQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLENBQUM7cUJBQzFCO2lCQUNGO2dCQUVELElBQUksQ0FBQyxZQUFZLENBQUMsSUFBSSxDQUFDLENBQUM7WUFDMUIsQ0FBQyxDQUFDLENBQUM7UUFDTCxDQUFDLENBQUMsQ0FBQztRQUVILE9BQU87WUFDTCw0RkFBNEY7WUFDNUYseUZBQXlGO1lBQ3pGLHVGQUF1RjtZQUN2RiwwRkFBMEY7WUFDMUYsMEJBQTBCO1lBQzFCLGNBQWMsRUFBRSx3QkFBd0IsQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLGNBQWMsQ0FBQyxDQUFDO1lBQ3BFLFVBQVU7U0FDWCxDQUFDO0lBQ0osQ0FBQztJQTlCRCxvRUE4QkM7SUFFRCwwRkFBMEY7SUFDMUYsU0FBZ0IsMkJBQTJCLENBQ3ZDLFdBQTJCLEVBQUUsVUFBeUI7UUFDeEQsTUFBTSwwQkFBMEIsR0FBRyxJQUFJLEdBQUcsRUFBOEIsQ0FBQztRQUN6RSxNQUFNLGdCQUFnQixHQUFHLElBQUEsNEJBQWtCLEVBQUMsVUFBVSxFQUFFLHVCQUF1QixFQUFFLFlBQVksQ0FBQyxDQUFDO1FBRS9GLFVBQVUsQ0FBQyxZQUFZLENBQUMsU0FBUyxJQUFJLENBQUMsSUFBYTtZQUNqRCxJQUFJLG9CQUFFLENBQUMsZ0JBQWdCLENBQUMsSUFBSSxDQUFDLEVBQUU7Z0JBQzdCLE1BQU0sNEJBQTRCLEdBQUcsb0JBQUUsQ0FBQywwQkFBMEIsQ0FBQyxJQUFJLENBQUMsVUFBVSxDQUFDO29CQUMvRSxvQkFBRSxDQUFDLFlBQVksQ0FBQyxJQUFJLENBQUMsVUFBVSxDQUFDLElBQUksQ0FBQztvQkFDckMsSUFBSSxDQUFDLFVBQVUsQ0FBQyxJQUFJLENBQUMsSUFBSSxLQUFLLHdCQUF3QjtvQkFDdEQsZUFBZSxDQUFDLFdBQVcsRUFBRSxJQUFJLENBQUMsVUFBVSxDQUFDLElBQUksNkJBQTZCLENBQUMsSUFBSSxDQUFDLENBQUM7Z0JBQ3pGLE1BQU0sZ0JBQWdCLEdBQUcsZ0JBQWdCLElBQUksb0JBQUUsQ0FBQyxZQUFZLENBQUMsSUFBSSxDQUFDLFVBQVUsQ0FBQztvQkFDekUsSUFBQSw0QkFBbUIsRUFBQyxXQUFXLEVBQUUsSUFBSSxDQUFDLFVBQVUsRUFBRSxnQkFBZ0IsQ0FBQztvQkFDbkUsNkJBQTZCLENBQUMsSUFBSSxDQUFDLENBQUM7Z0JBRXhDLElBQUksNEJBQTRCLElBQUksZ0JBQWdCLEVBQUU7b0JBQ3BELDBCQUEwQixDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsU0FBUyxDQUFDLENBQUMsQ0FBK0IsQ0FBQyxDQUFDO2lCQUNqRjthQUNGO1lBRUQsSUFBSSxDQUFDLFlBQVksQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUMxQixDQUFDLENBQUMsQ0FBQztRQUVILDRGQUE0RjtRQUM1Rix5RkFBeUY7UUFDekYsMENBQTBDO1FBQzFDLE9BQU8sd0JBQXdCLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQywwQkFBMEIsQ0FBQyxDQUFDLENBQUM7SUFDMUUsQ0FBQztJQTNCRCxrRUEyQkM7SUFFRDs7OztPQUlHO0lBQ0gsU0FBZ0Isd0NBQXdDLENBQ3BELElBQXVCLEVBQUUsT0FBbUI7UUFDOUMsTUFBTSxpQkFBaUIsR0FBa0MsRUFBRSxDQUFDO1FBQzVELE1BQU0sT0FBTyxHQUFHLElBQUksQ0FBQyxTQUFTLENBQUMsSUFBSSxDQUFDLFNBQVMsQ0FBQyxNQUFNLEdBQUcsQ0FBQyxDQUFDLENBQUM7UUFDMUQsSUFBSSxJQUFrRCxDQUFDO1FBQ3ZELElBQUksTUFBYyxDQUFDO1FBRW5CLElBQUksSUFBSSxDQUFDLFNBQVMsQ0FBQyxNQUFNLEdBQUcsQ0FBQyxFQUFFO1lBQzdCLElBQUksVUFBVSxDQUFDLE9BQU8sQ0FBQyxFQUFFO2dCQUN2Qix1RkFBdUY7Z0JBQ3ZGLGlCQUFpQixDQUFDLElBQUksQ0FBQyxvQkFBRSxDQUFDLHdCQUF3QixDQUFDLGNBQWMsRUFBRSxPQUFPLENBQUMsQ0FBQyxDQUFDO2FBQzlFO2lCQUFNLElBQUksb0JBQUUsQ0FBQyx5QkFBeUIsQ0FBQyxPQUFPLENBQUMsRUFBRTtnQkFDaEQsc0VBQXNFO2dCQUN0RSxpQkFBaUIsQ0FBQyxJQUFJLENBQUMsR0FBRyxPQUFPLENBQUMsVUFBVSxDQUFDLENBQUM7YUFDL0M7WUFFRCxNQUFNLEdBQUcsRUFBRSxDQUFDO1lBQ1osSUFBSSxHQUFHLEVBQUMsS0FBSyxFQUFFLE9BQU8sQ0FBQyxRQUFRLEVBQUUsRUFBRSxHQUFHLEVBQUUsT0FBTyxDQUFDLE1BQU0sRUFBRSxFQUFFLE1BQU0sRUFBRSxPQUFPLENBQUMsUUFBUSxFQUFFLEVBQUMsQ0FBQztTQUN2RjthQUFNO1lBQ0wsTUFBTSxLQUFLLEdBQUcsT0FBTyxDQUFDLE1BQU0sRUFBRSxDQUFDO1lBQy9CLE1BQU0sR0FBRyxJQUFJLENBQUM7WUFDZCxJQUFJLEdBQUcsRUFBQyxLQUFLLEVBQUUsR0FBRyxFQUFFLEtBQUssRUFBRSxNQUFNLEVBQUUsQ0FBQyxFQUFDLENBQUM7U0FDdkM7UUFFRCw0REFBNEQ7UUFDNUQsaUJBQWlCLENBQUMsSUFBSSxDQUFDLHdCQUF3QixFQUFFLENBQUMsQ0FBQztRQUVuRCxPQUFPO1lBQ0wsSUFBSTtZQUNKLElBQUksRUFBRSxNQUFNO2dCQUNSLE9BQU8sQ0FBQyxTQUFTLENBQ2Isb0JBQUUsQ0FBQyxRQUFRLENBQUMsV0FBVyxFQUFFLG9CQUFFLENBQUMsbUJBQW1CLENBQUMsaUJBQWlCLEVBQUUsSUFBSSxDQUFDLEVBQ3hFLElBQUksQ0FBQyxhQUFhLEVBQUUsQ0FBQztTQUM5QixDQUFDO0lBQ0osQ0FBQztJQWxDRCw0RkFrQ0M7SUFFRCwrRkFBK0Y7SUFDL0YsU0FBZ0IsZ0NBQWdDLENBQUMsSUFBZ0M7UUFFL0UsT0FBTyxvQkFBRSxDQUFDLG1CQUFtQixDQUN6QixDQUFDLEdBQUcsSUFBSSxDQUFDLFVBQVUsRUFBRSx3QkFBd0IsRUFBRSxDQUFDLEVBQUUsSUFBSSxDQUFDLFVBQVUsQ0FBQyxNQUFNLEdBQUcsQ0FBQyxDQUFDLENBQUM7SUFDcEYsQ0FBQztJQUpELDRFQUlDO0lBRUQsNkRBQTZEO0lBQzdELFNBQVMsZUFBZSxDQUFDLFdBQTJCLEVBQUUsSUFBaUM7O1FBQ3JGLE1BQU0sVUFBVSxHQUFHLE1BQUEsTUFBQSxXQUFXLENBQUMsaUJBQWlCLENBQUMsSUFBSSxDQUFDLFVBQVUsQ0FBQywwQ0FBRSxTQUFTLEVBQUUsMENBQUUsT0FBTyxFQUFFLENBQUM7UUFDMUYsT0FBTyxVQUFVLEtBQUssU0FBUyxJQUFJLFVBQVUsS0FBSyxlQUFlLENBQUM7SUFDcEUsQ0FBQztJQUVELGtFQUFrRTtJQUNsRSxTQUFTLGdDQUFnQyxDQUFDLElBQXVCO1FBQy9ELG1FQUFtRTtRQUNuRSxJQUFJLElBQUksQ0FBQyxTQUFTLENBQUMsTUFBTSxLQUFLLENBQUMsRUFBRTtZQUMvQixPQUFPLElBQUksQ0FBQztTQUNiO1FBRUQsaUVBQWlFO1FBQ2pFLElBQUksSUFBSSxDQUFDLFNBQVMsQ0FBQyxNQUFNLEdBQUcsQ0FBQyxFQUFFO1lBQzdCLE9BQU8sS0FBSyxDQUFDO1NBQ2Q7UUFFRCxzRkFBc0Y7UUFDdEYsTUFBTSxPQUFPLEdBQUcsSUFBSSxDQUFDLFNBQVMsQ0FBQyxDQUFDLENBQUMsQ0FBQztRQUVsQywyRkFBMkY7UUFDM0YsOEZBQThGO1FBQzlGLCtGQUErRjtRQUMvRixnR0FBZ0c7UUFDaEcsMEZBQTBGO1FBQzFGLDRGQUE0RjtRQUM1RixtREFBbUQ7UUFFbkQsd0RBQXdEO1FBQ3hELHVEQUF1RDtRQUN2RCxJQUFJLDhCQUE4QixDQUFDLE9BQU8sQ0FBQyxFQUFFO1lBQzNDLE9BQU8sSUFBSSxDQUFDO1NBQ2I7UUFFRCwrRUFBK0U7UUFDL0UsSUFBSSxVQUFVLENBQUMsT0FBTyxDQUFDLEVBQUU7WUFDdkIsT0FBTyxJQUFJLENBQUM7U0FDYjtRQUVELHFFQUFxRTtRQUNyRSxPQUFPLEtBQUssQ0FBQztJQUNmLENBQUM7SUFFRDs7OztPQUlHO0lBQ0gsU0FBUyw2QkFBNkIsQ0FBQyxJQUF1QjtRQUU1RCxPQUFPLElBQUksQ0FBQyxTQUFTLENBQUMsTUFBTSxHQUFHLENBQUMsSUFBSSw4QkFBOEIsQ0FBQyxJQUFJLENBQUMsU0FBUyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUM7SUFDeEYsQ0FBQztJQUVELG9EQUFvRDtJQUNwRCxTQUFTLFVBQVUsQ0FBQyxJQUFhO1FBRS9CLE9BQU8sb0JBQUUsQ0FBQyxlQUFlLENBQUMsSUFBSSxDQUFDLElBQUksb0JBQUUsQ0FBQyxvQkFBb0IsQ0FBQyxJQUFJLENBQUM7WUFDNUQsb0JBQUUsQ0FBQyxxQkFBcUIsQ0FBQyxJQUFJLENBQUMsQ0FBQztJQUNyQyxDQUFDO0lBRUQsb0dBQW9HO0lBQ3BHLFNBQVMsOEJBQThCLENBQUMsSUFBYTtRQUNuRCxPQUFPLG9CQUFFLENBQUMseUJBQXlCLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsVUFBVSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsRUFBRTs7WUFDeEUsT0FBTyxDQUFBLE1BQUEsSUFBSSxDQUFDLElBQUksMENBQUUsT0FBTyxFQUFFLE1BQUssVUFBVSxDQUFDO1FBQzdDLENBQUMsQ0FBQyxDQUFDO0lBQ0wsQ0FBQztJQUVELDREQUE0RDtJQUM1RCxTQUFTLHdCQUF3QjtRQUMvQix3Q0FBd0M7UUFDeEMsT0FBTyxvQkFBRSxDQUFDLHdCQUF3QixDQUM5QixVQUFVLEVBQ1Ysb0JBQUUsQ0FBQyxtQkFBbUIsQ0FBQyxDQUFDLG9CQUFFLENBQUMsd0JBQXdCLENBQUMsa0JBQWtCLEVBQUUsb0JBQUUsQ0FBQyxXQUFXLEVBQUUsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDO0lBQ25HLENBQUM7SUFFRCwyREFBMkQ7SUFDM0QsU0FBUyx3QkFBd0IsQ0FBb0IsS0FBVTtRQUM3RCxPQUFPLEtBQUssQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxDQUFDLENBQUMsTUFBTSxFQUFFLEdBQUcsQ0FBQyxDQUFDLE1BQU0sRUFBRSxDQUFDLENBQUM7SUFDdkQsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogQGxpY2Vuc2VcbiAqIENvcHlyaWdodCBHb29nbGUgTExDIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG4gKlxuICogVXNlIG9mIHRoaXMgc291cmNlIGNvZGUgaXMgZ292ZXJuZWQgYnkgYW4gTUlULXN0eWxlIGxpY2Vuc2UgdGhhdCBjYW4gYmVcbiAqIGZvdW5kIGluIHRoZSBMSUNFTlNFIGZpbGUgYXQgaHR0cHM6Ly9hbmd1bGFyLmlvL2xpY2Vuc2VcbiAqL1xuXG5pbXBvcnQgdHMgZnJvbSAndHlwZXNjcmlwdCc7XG5cbmltcG9ydCB7Z2V0SW1wb3J0U3BlY2lmaWVyfSBmcm9tICcuLi8uLi91dGlscy90eXBlc2NyaXB0L2ltcG9ydHMnO1xuaW1wb3J0IHtpc1JlZmVyZW5jZVRvSW1wb3J0fSBmcm9tICcuLi8uLi91dGlscy90eXBlc2NyaXB0L3N5bWJvbCc7XG5cbi8qKiBSZXN1bHQgb2YgYSBmdWxsLXByb2dyYW0gYW5hbHlzaXMgbG9va2luZyBmb3IgYGluaXRUZXN0RW52aXJvbm1lbnRgIGNhbGxzLiAqL1xuZXhwb3J0IGludGVyZmFjZSBJbml0VGVzdEVudmlyb25tZW50QW5hbHlzaXMge1xuICAvKiogVG90YWwgbnVtYmVyIG9mIGNhbGxzIHRoYXQgd2VyZSBmb3VuZC4gKi9cbiAgdG90YWxDYWxsczogbnVtYmVyO1xuICAvKiogQ2FsbHMgdGhhdCBuZWVkIHRvIGJlIG1pZ3JhdGVkLiAqL1xuICBjYWxsc1RvTWlncmF0ZTogdHMuQ2FsbEV4cHJlc3Npb25bXTtcbn1cblxuLyoqIEZpbmRzIHRoZSBgaW5pdFRlc3RFbnZpcm9ubWVudGAgY2FsbHMgdGhhdCBuZWVkIHRvIGJlIG1pZ3JhdGVkLiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGZpbmRJbml0VGVzdEVudmlyb25tZW50Q2FsbHMoXG4gICAgdHlwZUNoZWNrZXI6IHRzLlR5cGVDaGVja2VyLCBhbGxTb3VyY2VGaWxlczogdHMuU291cmNlRmlsZVtdKTogSW5pdFRlc3RFbnZpcm9ubWVudEFuYWx5c2lzIHtcbiAgY29uc3QgY2FsbHNUb01pZ3JhdGUgPSBuZXcgU2V0PHRzLkNhbGxFeHByZXNzaW9uPigpO1xuICBsZXQgdG90YWxDYWxscyA9IDA7XG5cbiAgYWxsU291cmNlRmlsZXMuZm9yRWFjaChzb3VyY2VGaWxlID0+IHtcbiAgICBzb3VyY2VGaWxlLmZvckVhY2hDaGlsZChmdW5jdGlvbiB3YWxrKG5vZGU6IHRzLk5vZGUpIHtcbiAgICAgIGlmICh0cy5pc0NhbGxFeHByZXNzaW9uKG5vZGUpICYmIHRzLmlzUHJvcGVydHlBY2Nlc3NFeHByZXNzaW9uKG5vZGUuZXhwcmVzc2lvbikgJiZcbiAgICAgICAgICB0cy5pc0lkZW50aWZpZXIobm9kZS5leHByZXNzaW9uLm5hbWUpICYmXG4gICAgICAgICAgbm9kZS5leHByZXNzaW9uLm5hbWUudGV4dCA9PT0gJ2luaXRUZXN0RW52aXJvbm1lbnQnICYmXG4gICAgICAgICAgaXNUZXN0QmVkQWNjZXNzKHR5cGVDaGVja2VyLCBub2RlLmV4cHJlc3Npb24pKSB7XG4gICAgICAgIHRvdGFsQ2FsbHMrKztcbiAgICAgICAgaWYgKHNob3VsZE1pZ3JhdGVJbml0VGVzdEVudmlyb25tZW50KG5vZGUpKSB7XG4gICAgICAgICAgY2FsbHNUb01pZ3JhdGUuYWRkKG5vZGUpO1xuICAgICAgICB9XG4gICAgICB9XG5cbiAgICAgIG5vZGUuZm9yRWFjaENoaWxkKHdhbGspO1xuICAgIH0pO1xuICB9KTtcblxuICByZXR1cm4ge1xuICAgIC8vIFNvcnQgdGhlIG5vZGVzIHNvIHRoYXQgdGhleSB3aWxsIGJlIG1pZ3JhdGVkIGluIHJldmVyc2Ugc291cmNlIG9yZGVyIChub2RlcyBhdCB0aGUgZW5kIG9mXG4gICAgLy8gdGhlIGZpbGUgYXJlIG1pZ3JhdGVkIGZpcnN0KS4gVGhpcyBhdm9pZHMgaXNzdWVzIHdoZXJlIGEgbWlncmF0ZWQgbm9kZSB3aWxsIG9mZnNldCB0aGVcbiAgICAvLyBib3VuZHMgb2YgYWxsIG5vZGVzIHRoYXQgY29tZSBhZnRlciBpdC4gTm90ZSB0aGF0IHRoZSBub2RlcyBoZXJlIGFyZSBmcm9tIGFsbCBvZiB0aGVcbiAgICAvLyBwYXNzZWQgaW4gc291cmNlIGZpbGVzLCBidXQgdGhhdCBkb2Vzbid0IG1hdHRlciBzaW5jZSB0aGUgbGF0ZXIgbm9kZXMgd2lsbCBzdGlsbCBhcHBlYXJcbiAgICAvLyBhZnRlciB0aGUgZWFybGllciBvbmVzLlxuICAgIGNhbGxzVG9NaWdyYXRlOiBzb3J0SW5SZXZlcnNlU291cmNlT3JkZXIoQXJyYXkuZnJvbShjYWxsc1RvTWlncmF0ZSkpLFxuICAgIHRvdGFsQ2FsbHNcbiAgfTtcbn1cblxuLyoqIEZpbmRzIHRoZSBgY29uZmlndXJlVGVzdGluZ01vZHVsZWAgYW5kIGB3aXRoTW9kdWxlYCBjYWxscyB0aGF0IG5lZWQgdG8gYmUgbWlncmF0ZWQuICovXG5leHBvcnQgZnVuY3Rpb24gZmluZFRlc3RNb2R1bGVNZXRhZGF0YU5vZGVzKFxuICAgIHR5cGVDaGVja2VyOiB0cy5UeXBlQ2hlY2tlciwgc291cmNlRmlsZTogdHMuU291cmNlRmlsZSkge1xuICBjb25zdCB0ZXN0TW9kdWxlTWV0YWRhdGFMaXRlcmFscyA9IG5ldyBTZXQ8dHMuT2JqZWN0TGl0ZXJhbEV4cHJlc3Npb24+KCk7XG4gIGNvbnN0IHdpdGhNb2R1bGVJbXBvcnQgPSBnZXRJbXBvcnRTcGVjaWZpZXIoc291cmNlRmlsZSwgJ0Bhbmd1bGFyL2NvcmUvdGVzdGluZycsICd3aXRoTW9kdWxlJyk7XG5cbiAgc291cmNlRmlsZS5mb3JFYWNoQ2hpbGQoZnVuY3Rpb24gd2Fsayhub2RlOiB0cy5Ob2RlKSB7XG4gICAgaWYgKHRzLmlzQ2FsbEV4cHJlc3Npb24obm9kZSkpIHtcbiAgICAgIGNvbnN0IGlzQ29uZmlndXJlVGVzdGluZ01vZHVsZUNhbGwgPSB0cy5pc1Byb3BlcnR5QWNjZXNzRXhwcmVzc2lvbihub2RlLmV4cHJlc3Npb24pICYmXG4gICAgICAgICAgdHMuaXNJZGVudGlmaWVyKG5vZGUuZXhwcmVzc2lvbi5uYW1lKSAmJlxuICAgICAgICAgIG5vZGUuZXhwcmVzc2lvbi5uYW1lLnRleHQgPT09ICdjb25maWd1cmVUZXN0aW5nTW9kdWxlJyAmJlxuICAgICAgICAgIGlzVGVzdEJlZEFjY2Vzcyh0eXBlQ2hlY2tlciwgbm9kZS5leHByZXNzaW9uKSAmJiBzaG91bGRNaWdyYXRlTW9kdWxlQ29uZmlnQ2FsbChub2RlKTtcbiAgICAgIGNvbnN0IGlzV2l0aE1vZHVsZUNhbGwgPSB3aXRoTW9kdWxlSW1wb3J0ICYmIHRzLmlzSWRlbnRpZmllcihub2RlLmV4cHJlc3Npb24pICYmXG4gICAgICAgICAgaXNSZWZlcmVuY2VUb0ltcG9ydCh0eXBlQ2hlY2tlciwgbm9kZS5leHByZXNzaW9uLCB3aXRoTW9kdWxlSW1wb3J0KSAmJlxuICAgICAgICAgIHNob3VsZE1pZ3JhdGVNb2R1bGVDb25maWdDYWxsKG5vZGUpO1xuXG4gICAgICBpZiAoaXNDb25maWd1cmVUZXN0aW5nTW9kdWxlQ2FsbCB8fCBpc1dpdGhNb2R1bGVDYWxsKSB7XG4gICAgICAgIHRlc3RNb2R1bGVNZXRhZGF0YUxpdGVyYWxzLmFkZChub2RlLmFyZ3VtZW50c1swXSBhcyB0cy5PYmplY3RMaXRlcmFsRXhwcmVzc2lvbik7XG4gICAgICB9XG4gICAgfVxuXG4gICAgbm9kZS5mb3JFYWNoQ2hpbGQod2Fsayk7XG4gIH0pO1xuXG4gIC8vIFNvcnQgdGhlIG5vZGVzIHNvIHRoYXQgdGhleSB3aWxsIGJlIG1pZ3JhdGVkIGluIHJldmVyc2Ugc291cmNlIG9yZGVyIChub2RlcyBhdCB0aGUgZW5kIG9mXG4gIC8vIHRoZSBmaWxlIGFyZSBtaWdyYXRlZCBmaXJzdCkuIFRoaXMgYXZvaWRzIGlzc3VlcyB3aGVyZSBhIG1pZ3JhdGVkIG5vZGUgd2lsbCBvZmZzZXQgdGhlXG4gIC8vIGJvdW5kcyBvZiBhbGwgbm9kZXMgdGhhdCBjb21lIGFmdGVyIGl0LlxuICByZXR1cm4gc29ydEluUmV2ZXJzZVNvdXJjZU9yZGVyKEFycmF5LmZyb20odGVzdE1vZHVsZU1ldGFkYXRhTGl0ZXJhbHMpKTtcbn1cblxuLyoqXG4gKiBHZXRzIGRhdGEgdGhhdCBjYW4gYmUgdXNlZCB0byBtaWdyYXRlIGEgY2FsbCB0byBgVGVzdEJlZC5pbml0VGVzdEVudmlyb25tZW50YC5cbiAqIFRoZSByZXR1cm5lZCBgc3BhbmAgaXMgdXNlZCB0byBtYXJrIHRoZSB0ZXh0IHRoYXQgc2hvdWxkIGJlIHJlcGxhY2VkIHdoaWxlIHRoZSBgdGV4dGBcbiAqIGlzIHRoZSBjb2RlIHRoYXQgc2hvdWxkIGJlIGluc2VydGVkIGluc3RlYWQuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBnZXRJbml0VGVzdEVudmlyb25tZW50TGl0ZXJhbFJlcGxhY2VtZW50KFxuICAgIG5vZGU6IHRzLkNhbGxFeHByZXNzaW9uLCBwcmludGVyOiB0cy5QcmludGVyKSB7XG4gIGNvbnN0IGxpdGVyYWxQcm9wZXJ0aWVzOiB0cy5PYmplY3RMaXRlcmFsRWxlbWVudExpa2VbXSA9IFtdO1xuICBjb25zdCBsYXN0QXJnID0gbm9kZS5hcmd1bWVudHNbbm9kZS5hcmd1bWVudHMubGVuZ3RoIC0gMV07XG4gIGxldCBzcGFuOiB7c3RhcnQ6IG51bWJlciwgZW5kOiBudW1iZXIsIGxlbmd0aDogbnVtYmVyfTtcbiAgbGV0IHByZWZpeDogc3RyaW5nO1xuXG4gIGlmIChub2RlLmFyZ3VtZW50cy5sZW5ndGggPiAyKSB7XG4gICAgaWYgKGlzRnVuY3Rpb24obGFzdEFyZykpIHtcbiAgICAgIC8vIElmIHRoZSBsYXN0IGFyZ3VtZW50IGlzIGEgZnVuY3Rpb24sIGFkZCB0aGUgZnVuY3Rpb24gYXMgdGhlIGBhb3RTdW1tYXJpZXNgIHByb3BlcnR5LlxuICAgICAgbGl0ZXJhbFByb3BlcnRpZXMucHVzaCh0cy5jcmVhdGVQcm9wZXJ0eUFzc2lnbm1lbnQoJ2FvdFN1bW1hcmllcycsIGxhc3RBcmcpKTtcbiAgICB9IGVsc2UgaWYgKHRzLmlzT2JqZWN0TGl0ZXJhbEV4cHJlc3Npb24obGFzdEFyZykpIHtcbiAgICAgIC8vIElmIHRoZSBwcm9wZXJ0eSBpcyBhbiBvYmplY3QgbGl0ZXJhbCwgY29weSBvdmVyIGFsbCB0aGUgcHJvcGVydGllcy5cbiAgICAgIGxpdGVyYWxQcm9wZXJ0aWVzLnB1c2goLi4ubGFzdEFyZy5wcm9wZXJ0aWVzKTtcbiAgICB9XG5cbiAgICBwcmVmaXggPSAnJztcbiAgICBzcGFuID0ge3N0YXJ0OiBsYXN0QXJnLmdldFN0YXJ0KCksIGVuZDogbGFzdEFyZy5nZXRFbmQoKSwgbGVuZ3RoOiBsYXN0QXJnLmdldFdpZHRoKCl9O1xuICB9IGVsc2Uge1xuICAgIGNvbnN0IHN0YXJ0ID0gbGFzdEFyZy5nZXRFbmQoKTtcbiAgICBwcmVmaXggPSAnLCAnO1xuICAgIHNwYW4gPSB7c3RhcnQsIGVuZDogc3RhcnQsIGxlbmd0aDogMH07XG4gIH1cblxuICAvLyBGaW5hbGx5IHB1c2ggdGhlIHRlYXJkb3duIG9iamVjdCBzbyB0aGF0IGl0IGFwcGVhcnMgbGFzdC5cbiAgbGl0ZXJhbFByb3BlcnRpZXMucHVzaChjcmVhdGVUZWFyZG93bkFzc2lnbm1lbnQoKSk7XG5cbiAgcmV0dXJuIHtcbiAgICBzcGFuLFxuICAgIHRleHQ6IHByZWZpeCArXG4gICAgICAgIHByaW50ZXIucHJpbnROb2RlKFxuICAgICAgICAgICAgdHMuRW1pdEhpbnQuVW5zcGVjaWZpZWQsIHRzLmNyZWF0ZU9iamVjdExpdGVyYWwobGl0ZXJhbFByb3BlcnRpZXMsIHRydWUpLFxuICAgICAgICAgICAgbm9kZS5nZXRTb3VyY2VGaWxlKCkpXG4gIH07XG59XG5cbi8qKiBNaWdyYXRlcyBhbiBvYmplY3QgbGl0ZXJhbCB0aGF0IGlzIHBhc3NlZCBpbnRvIGBjb25maWd1cmVUZXN0aW5nTW9kdWxlYCBvciBgd2l0aE1vZHVsZWAuICovXG5leHBvcnQgZnVuY3Rpb24gbWlncmF0ZVRlc3RNb2R1bGVNZXRhZGF0YUxpdGVyYWwobm9kZTogdHMuT2JqZWN0TGl0ZXJhbEV4cHJlc3Npb24pOlxuICAgIHRzLk9iamVjdExpdGVyYWxFeHByZXNzaW9uIHtcbiAgcmV0dXJuIHRzLmNyZWF0ZU9iamVjdExpdGVyYWwoXG4gICAgICBbLi4ubm9kZS5wcm9wZXJ0aWVzLCBjcmVhdGVUZWFyZG93bkFzc2lnbm1lbnQoKV0sIG5vZGUucHJvcGVydGllcy5sZW5ndGggPiAwKTtcbn1cblxuLyoqIFJldHVybnMgd2hldGhlciBhIHByb3BlcnR5IGFjY2VzcyBwb2ludHMgdG8gYFRlc3RCZWRgLiAqL1xuZnVuY3Rpb24gaXNUZXN0QmVkQWNjZXNzKHR5cGVDaGVja2VyOiB0cy5UeXBlQ2hlY2tlciwgbm9kZTogdHMuUHJvcGVydHlBY2Nlc3NFeHByZXNzaW9uKTogYm9vbGVhbiB7XG4gIGNvbnN0IHN5bWJvbE5hbWUgPSB0eXBlQ2hlY2tlci5nZXRUeXBlQXRMb2NhdGlvbihub2RlLmV4cHJlc3Npb24pPy5nZXRTeW1ib2woKT8uZ2V0TmFtZSgpO1xuICByZXR1cm4gc3ltYm9sTmFtZSA9PT0gJ1Rlc3RCZWQnIHx8IHN5bWJvbE5hbWUgPT09ICdUZXN0QmVkU3RhdGljJztcbn1cblxuLyoqIFdoZXRoZXIgYSBjYWxsIHRvIGBpbml0VGVzdEVudmlyb25tZW50YCBzaG91bGQgYmUgbWlncmF0ZWQuICovXG5mdW5jdGlvbiBzaG91bGRNaWdyYXRlSW5pdFRlc3RFbnZpcm9ubWVudChub2RlOiB0cy5DYWxsRXhwcmVzc2lvbik6IGJvb2xlYW4ge1xuICAvLyBJZiB0aGVyZSBpcyBubyB0aGlyZCBhcmd1bWVudCwgd2UgZGVmaW5pdGVseSBoYXZlIHRvIG1pZ3JhdGUgaXQuXG4gIGlmIChub2RlLmFyZ3VtZW50cy5sZW5ndGggPT09IDIpIHtcbiAgICByZXR1cm4gdHJ1ZTtcbiAgfVxuXG4gIC8vIFRoaXMgaXMgdGVjaG5pY2FsbHkgYSB0eXBlIGVycm9yIHNvIHdlIHNob3VsZG4ndCBtZXNzIHdpdGggaXQuXG4gIGlmIChub2RlLmFyZ3VtZW50cy5sZW5ndGggPCAyKSB7XG4gICAgcmV0dXJuIGZhbHNlO1xuICB9XG5cbiAgLy8gT3RoZXJ3aXNlIHdlIG5lZWQgdG8gZmlndXJlIG91dCBpZiB0aGUgYHRlYXJkb3duYCBmbGFnIGlzIHNldCBvbiB0aGUgbGFzdCBhcmd1bWVudC5cbiAgY29uc3QgbGFzdEFyZyA9IG5vZGUuYXJndW1lbnRzWzJdO1xuXG4gIC8vIE5vdGU6IHRoZSBjaGVja3MgYmVsb3cgd2lsbCBpZGVudGlmeSBzb21ldGhpbmcgbGlrZSBgaW5pdFRlc3RFbnZpcm9ubWVudCguLi4sIC4uLiwge30pYCxcbiAgLy8gYnV0IHRoZXknbGwgaWdub3JlIGEgdmFyaWFibGUgYmVpbmcgcGFzc2VkIGluIGFzIHRoZSBsYXN0IGFyZ3VtZW50IGxpa2UgYGNvbnN0IGNvbmZpZyA9IHt9O1xuICAvLyBpbml0VGVzdEVudmlyb25tZW50KC4uLiwgLi4uLCBjb25maWcpYC4gV2hpbGUgd2UgY2FuIHJlc29sdmUgdGhlIHZhcmlhYmxlIHRvIGl0cyBkZWNsYXJhdGlvblxuICAvLyB1c2luZyBgdHlwZUNoZWNrZXIuZ2V0VHlwZUF0TG9jYXRpb24obGFzdEFyZykuZ2V0U3ltYm9sKCk/LnZhbHVlRGVjbGFyYXRpb25gLCB3ZSBkZWxpYmVyYXRlbHlcbiAgLy8gZG9uJ3QsIGJlY2F1c2UgaXQgaW50cm9kdWNlcyBzb21lIGNvbXBsZXhpdHkgYW5kIHdlIG1heSBlbmQgdXAgYnJlYWtpbmcgdXNlciBjb2RlLiBFLmcuXG4gIC8vIHRoZSBgY29uZmlnYCBmcm9tIHRoZSBleGFtcGxlIGFib3ZlIG1heSBiZSBwYXNzZWQgaW4gdG8gb3RoZXIgZnVuY3Rpb25zIG9yIHRoZSBgdGVhcmRvd25gXG4gIC8vIGZsYWcgY291bGQgYmUgYWRkZWQgbGF0ZXIgb24gYnkgYSBmdW5jdGlvbiBjYWxsLlxuXG4gIC8vIElmIHRoZSBhcmd1bWVudCBpcyBhbiBvYmplY3QgbGl0ZXJhbCBhbmQgdGhlcmUgYXJlIG5vXG4gIC8vIHByb3BlcnRpZXMgY2FsbGVkIGB0ZWFyZG93bmAsIHdlIGhhdmUgdG8gbWlncmF0ZSBpdC5cbiAgaWYgKGlzT2JqZWN0TGl0ZXJhbFdpdGhvdXRUZWFyZG93bihsYXN0QXJnKSkge1xuICAgIHJldHVybiB0cnVlO1xuICB9XG5cbiAgLy8gSWYgdGhlIGxhc3QgYXJndW1lbnQgaXMgYW4gYGFvdFN1bW1hcmllc2AgZnVuY3Rpb24sIHdlIGFsc28gaGF2ZSB0byBtaWdyYXRlLlxuICBpZiAoaXNGdW5jdGlvbihsYXN0QXJnKSkge1xuICAgIHJldHVybiB0cnVlO1xuICB9XG5cbiAgLy8gT3RoZXJ3aXNlIGRvbid0IG1pZ3JhdGUgaWYgd2UgY291bGRuJ3QgaWRlbnRpZnkgdGhlIGxhc3QgYXJndW1lbnQuXG4gIHJldHVybiBmYWxzZTtcbn1cblxuLyoqXG4gKiBXaGV0aGVyIGEgY2FsbCB0byBhIG1vZHVsZSBjb25maWd1cmF0aW9uIGZ1bmN0aW9uIHNob3VsZCBiZSBtaWdyYXRlZC4gVGhpcyBjb3ZlcnNcbiAqIGBUZXN0QmVkLmNvbmZpZ3VyZVRlc3RpbmdNb2R1bGVgIGFuZCBgd2l0aE1vZHVsZWAgc2luY2UgdGhleSBib3RoIGFjY2VwdCBgVGVzdE1vZHVsZU1ldGFkYXRhYFxuICogYXMgdGhlaXIgZmlyc3QgYXJndW1lbnQuXG4gKi9cbmZ1bmN0aW9uIHNob3VsZE1pZ3JhdGVNb2R1bGVDb25maWdDYWxsKG5vZGU6IHRzLkNhbGxFeHByZXNzaW9uKTogbm9kZSBpcyB0cy5DYWxsRXhwcmVzc2lvbiZcbiAgICB7YXJndW1lbnRzOiBbdHMuT2JqZWN0TGl0ZXJhbEV4cHJlc3Npb24sIC4uLnRzLkV4cHJlc3Npb25bXV19IHtcbiAgcmV0dXJuIG5vZGUuYXJndW1lbnRzLmxlbmd0aCA+IDAgJiYgaXNPYmplY3RMaXRlcmFsV2l0aG91dFRlYXJkb3duKG5vZGUuYXJndW1lbnRzWzBdKTtcbn1cblxuLyoqIFJldHVybnMgd2hldGhlciBhIG5vZGUgaXMgYSBmdW5jdGlvbiBsaXRlcmFsLiAqL1xuZnVuY3Rpb24gaXNGdW5jdGlvbihub2RlOiB0cy5Ob2RlKTogbm9kZSBpcyB0cy5BcnJvd0Z1bmN0aW9ufHRzLkZ1bmN0aW9uRXhwcmVzc2lvbnxcbiAgICB0cy5GdW5jdGlvbkRlY2xhcmF0aW9uIHtcbiAgcmV0dXJuIHRzLmlzQXJyb3dGdW5jdGlvbihub2RlKSB8fCB0cy5pc0Z1bmN0aW9uRXhwcmVzc2lvbihub2RlKSB8fFxuICAgICAgdHMuaXNGdW5jdGlvbkRlY2xhcmF0aW9uKG5vZGUpO1xufVxuXG4vKiogQ2hlY2tzIHdoZXRoZXIgYSBub2RlIGlzIGFuIG9iamVjdCBsaXRlcmFsIHRoYXQgZG9lc24ndCBjb250YWluIGEgcHJvcGVydHkgY2FsbGVkIGB0ZWFyZG93bmAuICovXG5mdW5jdGlvbiBpc09iamVjdExpdGVyYWxXaXRob3V0VGVhcmRvd24obm9kZTogdHMuTm9kZSk6IG5vZGUgaXMgdHMuT2JqZWN0TGl0ZXJhbEV4cHJlc3Npb24ge1xuICByZXR1cm4gdHMuaXNPYmplY3RMaXRlcmFsRXhwcmVzc2lvbihub2RlKSAmJiAhbm9kZS5wcm9wZXJ0aWVzLmZpbmQocHJvcCA9PiB7XG4gICAgcmV0dXJuIHByb3AubmFtZT8uZ2V0VGV4dCgpID09PSAndGVhcmRvd24nO1xuICB9KTtcbn1cblxuLyoqIENyZWF0ZXMgYSB0ZWFyZG93biBjb25maWd1cmF0aW9uIHByb3BlcnR5IGFzc2lnbm1lbnQuICovXG5mdW5jdGlvbiBjcmVhdGVUZWFyZG93bkFzc2lnbm1lbnQoKTogdHMuUHJvcGVydHlBc3NpZ25tZW50IHtcbiAgLy8gYHRlYXJkb3duOiB7ZGVzdHJveUFmdGVyRWFjaDogZmFsc2V9YFxuICByZXR1cm4gdHMuY3JlYXRlUHJvcGVydHlBc3NpZ25tZW50KFxuICAgICAgJ3RlYXJkb3duJyxcbiAgICAgIHRzLmNyZWF0ZU9iamVjdExpdGVyYWwoW3RzLmNyZWF0ZVByb3BlcnR5QXNzaWdubWVudCgnZGVzdHJveUFmdGVyRWFjaCcsIHRzLmNyZWF0ZUZhbHNlKCkpXSkpO1xufVxuXG4vKiogU29ydHMgYW4gYXJyYXkgb2YgQVNUIG5vZGVzIGluIHJldmVyc2Ugc291cmNlIG9yZGVyLiAqL1xuZnVuY3Rpb24gc29ydEluUmV2ZXJzZVNvdXJjZU9yZGVyPFQgZXh0ZW5kcyB0cy5Ob2RlPihub2RlczogVFtdKTogVFtdIHtcbiAgcmV0dXJuIG5vZGVzLnNvcnQoKGEsIGIpID0+IGIuZ2V0RW5kKCkgLSBhLmdldEVuZCgpKTtcbn1cbiJdfQ==