@angular/cdk
Version:
Angular Material Component Development Kit
219 lines • 35.9 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
*/
import { __awaiter } from "tslib";
import { parallel } from './change-detection';
import { ComponentHarness, HarnessPredicate, } from './component-harness';
/**
* Base harness environment class that can be extended to allow `ComponentHarness`es to be used in
* different test environments (e.g. testbed, protractor, etc.). This class implements the
* functionality of both a `HarnessLoader` and `LocatorFactory`. This class is generic on the raw
* element type, `E`, used by the particular test environment.
*/
export class HarnessEnvironment {
constructor(rawRootElement) {
this.rawRootElement = rawRootElement;
this.rootElement = this.createTestElement(rawRootElement);
}
// Implemented as part of the `LocatorFactory` interface.
documentRootLocatorFactory() {
return this.createEnvironment(this.getDocumentRoot());
}
// Implemented as part of the `LocatorFactory` interface.
locatorFor(...queries) {
return () => _assertResultFound(this._getAllHarnessesAndTestElements(queries), _getDescriptionForLocatorForQueries(queries));
}
// Implemented as part of the `LocatorFactory` interface.
locatorForOptional(...queries) {
return () => __awaiter(this, void 0, void 0, function* () { return (yield this._getAllHarnessesAndTestElements(queries))[0] || null; });
}
// Implemented as part of the `LocatorFactory` interface.
locatorForAll(...queries) {
return () => this._getAllHarnessesAndTestElements(queries);
}
// Implemented as part of the `LocatorFactory` interface.
rootHarnessLoader() {
return __awaiter(this, void 0, void 0, function* () {
return this;
});
}
// Implemented as part of the `LocatorFactory` interface.
harnessLoaderFor(selector) {
return __awaiter(this, void 0, void 0, function* () {
return this.createEnvironment(yield _assertResultFound(this.getAllRawElements(selector), [_getDescriptionForHarnessLoaderQuery(selector)]));
});
}
// Implemented as part of the `LocatorFactory` interface.
harnessLoaderForOptional(selector) {
return __awaiter(this, void 0, void 0, function* () {
const elements = yield this.getAllRawElements(selector);
return elements[0] ? this.createEnvironment(elements[0]) : null;
});
}
// Implemented as part of the `LocatorFactory` interface.
harnessLoaderForAll(selector) {
return __awaiter(this, void 0, void 0, function* () {
const elements = yield this.getAllRawElements(selector);
return elements.map(element => this.createEnvironment(element));
});
}
// Implemented as part of the `HarnessLoader` interface.
getHarness(query) {
return this.locatorFor(query)();
}
// Implemented as part of the `HarnessLoader` interface.
getAllHarnesses(query) {
return this.locatorForAll(query)();
}
// Implemented as part of the `HarnessLoader` interface.
getChildLoader(selector) {
return __awaiter(this, void 0, void 0, function* () {
return this.createEnvironment(yield _assertResultFound(this.getAllRawElements(selector), [_getDescriptionForHarnessLoaderQuery(selector)]));
});
}
// Implemented as part of the `HarnessLoader` interface.
getAllChildLoaders(selector) {
return __awaiter(this, void 0, void 0, function* () {
return (yield this.getAllRawElements(selector)).map(e => this.createEnvironment(e));
});
}
/** Creates a `ComponentHarness` for the given harness type with the given raw host element. */
createComponentHarness(harnessType, element) {
return new harnessType(this.createEnvironment(element));
}
/**
* Matches the given raw elements with the given list of element and harness queries to produce a
* list of matched harnesses and test elements.
*/
_getAllHarnessesAndTestElements(queries) {
return __awaiter(this, void 0, void 0, function* () {
const { allQueries, harnessQueries, elementQueries, harnessTypes } = _parseQueries(queries);
// Combine all of the queries into one large comma-delimited selector and use it to get all raw
// elements matching any of the individual queries.
const rawElements = yield this.getAllRawElements([...elementQueries, ...harnessQueries.map(predicate => predicate.getSelector())].join(','));
// If every query is searching for the same harness subclass, we know every result corresponds
// to an instance of that subclass. Likewise, if every query is for a `TestElement`, we know
// every result corresponds to a `TestElement`. Otherwise we need to verify which result was
// found by which selector so it can be matched to the appropriate instance.
const skipSelectorCheck = (elementQueries.length === 0 && harnessTypes.size === 1) ||
harnessQueries.length === 0;
const perElementMatches = yield parallel(() => rawElements.map((rawElement) => __awaiter(this, void 0, void 0, function* () {
const testElement = this.createTestElement(rawElement);
const allResultsForElement = yield Promise.all(
// For each query, get `null` if it doesn't match, or a `TestElement` or
// `ComponentHarness` as appropriate if it does match. This gives us everything that
// matches the current raw element, but it may contain duplicate entries (e.g.
// multiple `TestElement` or multiple `ComponentHarness` of the same type).
allQueries.map(query => this._getQueryResultForElement(query, rawElement, testElement, skipSelectorCheck)));
return _removeDuplicateQueryResults(allResultsForElement);
})));
return [].concat(...perElementMatches);
});
}
/**
* Check whether the given query matches the given element, if it does return the matched
* `TestElement` or `ComponentHarness`, if it does not, return null. In cases where the caller
* knows for sure that the query matches the element's selector, `skipSelectorCheck` can be used
* to skip verification and optimize performance.
*/
_getQueryResultForElement(query, rawElement, testElement, skipSelectorCheck = false) {
return __awaiter(this, void 0, void 0, function* () {
if (typeof query === 'string') {
return ((skipSelectorCheck || (yield testElement.matchesSelector(query))) ? testElement : null);
}
if (skipSelectorCheck || (yield testElement.matchesSelector(query.getSelector()))) {
const harness = this.createComponentHarness(query.harnessType, rawElement);
return (yield query.evaluate(harness)) ? harness : null;
}
return null;
});
}
}
/**
* Parses a list of queries in the format accepted by the `locatorFor*` methods into an easier to
* work with format.
*/
function _parseQueries(queries) {
const allQueries = [];
const harnessQueries = [];
const elementQueries = [];
const harnessTypes = new Set();
for (const query of queries) {
if (typeof query === 'string') {
allQueries.push(query);
elementQueries.push(query);
}
else {
const predicate = query instanceof HarnessPredicate ? query : new HarnessPredicate(query, {});
allQueries.push(predicate);
harnessQueries.push(predicate);
harnessTypes.add(predicate.harnessType);
}
}
return { allQueries, harnessQueries, elementQueries, harnessTypes };
}
/**
* Removes duplicate query results for a particular element. (e.g. multiple `TestElement`
* instances or multiple instances of the same `ComponentHarness` class.
*/
function _removeDuplicateQueryResults(results) {
return __awaiter(this, void 0, void 0, function* () {
let testElementMatched = false;
let matchedHarnessTypes = new Set();
const dedupedMatches = [];
for (const result of results) {
if (!result) {
continue;
}
if (result instanceof ComponentHarness) {
if (!matchedHarnessTypes.has(result.constructor)) {
matchedHarnessTypes.add(result.constructor);
dedupedMatches.push(result);
}
}
else if (!testElementMatched) {
testElementMatched = true;
dedupedMatches.push(result);
}
}
return dedupedMatches;
});
}
/** Verifies that there is at least one result in an array. */
function _assertResultFound(results, queryDescriptions) {
return __awaiter(this, void 0, void 0, function* () {
const result = (yield results)[0];
if (result == undefined) {
throw Error(`Failed to find element matching one of the following queries:\n` +
queryDescriptions.map(desc => `(${desc})`).join(',\n'));
}
return result;
});
}
/** Gets a list of description strings from a list of queries. */
function _getDescriptionForLocatorForQueries(queries) {
return queries.map(query => typeof query === 'string' ?
_getDescriptionForTestElementQuery(query) : _getDescriptionForComponentHarnessQuery(query));
}
/** Gets a description string for a `ComponentHarness` query. */
function _getDescriptionForComponentHarnessQuery(query) {
const harnessPredicate = query instanceof HarnessPredicate ? query : new HarnessPredicate(query, {});
const { name, hostSelector } = harnessPredicate.harnessType;
const description = `${name} with host element matching selector: "${hostSelector}"`;
const constraints = harnessPredicate.getDescription();
return description + (constraints ?
` satisfying the constraints: ${harnessPredicate.getDescription()}` : '');
}
/** Gets a description string for a `TestElement` query. */
function _getDescriptionForTestElementQuery(selector) {
return `TestElement for element matching selector: "${selector}"`;
}
/** Gets a description string for a `HarnessLoader` query. */
function _getDescriptionForHarnessLoaderQuery(selector) {
return `HarnessLoader for element matching selector: "${selector}"`;
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaGFybmVzcy1lbnZpcm9ubWVudC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uLy4uL3NyYy9jZGsvdGVzdGluZy9oYXJuZXNzLWVudmlyb25tZW50LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7Ozs7R0FNRzs7QUFFSCxPQUFPLEVBQUMsUUFBUSxFQUFDLE1BQU0sb0JBQW9CLENBQUM7QUFDNUMsT0FBTyxFQUVMLGdCQUFnQixFQUdoQixnQkFBZ0IsR0FJakIsTUFBTSxxQkFBcUIsQ0FBQztBQXFCN0I7Ozs7O0dBS0c7QUFDSCxNQUFNLE9BQWdCLGtCQUFrQjtJQUl0QyxZQUFnQyxjQUFpQjtRQUFqQixtQkFBYyxHQUFkLGNBQWMsQ0FBRztRQUMvQyxJQUFJLENBQUMsV0FBVyxHQUFHLElBQUksQ0FBQyxpQkFBaUIsQ0FBQyxjQUFjLENBQUMsQ0FBQztJQUM1RCxDQUFDO0lBRUQseURBQXlEO0lBQ3pELDBCQUEwQjtRQUN4QixPQUFPLElBQUksQ0FBQyxpQkFBaUIsQ0FBQyxJQUFJLENBQUMsZUFBZSxFQUFFLENBQUMsQ0FBQztJQUN4RCxDQUFDO0lBRUQseURBQXlEO0lBQ3pELFVBQVUsQ0FBMkMsR0FBRyxPQUFVO1FBRWhFLE9BQU8sR0FBRyxFQUFFLENBQUMsa0JBQWtCLENBQzNCLElBQUksQ0FBQywrQkFBK0IsQ0FBQyxPQUFPLENBQUMsRUFDN0MsbUNBQW1DLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQztJQUNwRCxDQUFDO0lBRUQseURBQXlEO0lBQ3pELGtCQUFrQixDQUEyQyxHQUFHLE9BQVU7UUFFeEUsT0FBTyxHQUFTLEVBQUUsZ0RBQUMsT0FBQSxDQUFDLE1BQU0sSUFBSSxDQUFDLCtCQUErQixDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLElBQUksSUFBSSxDQUFBLEdBQUEsQ0FBQztJQUN0RixDQUFDO0lBRUQseURBQXlEO0lBQ3pELGFBQWEsQ0FBMkMsR0FBRyxPQUFVO1FBRW5FLE9BQU8sR0FBRyxFQUFFLENBQUMsSUFBSSxDQUFDLCtCQUErQixDQUFDLE9BQU8sQ0FBQyxDQUFDO0lBQzdELENBQUM7SUFFRCx5REFBeUQ7SUFDbkQsaUJBQWlCOztZQUNyQixPQUFPLElBQUksQ0FBQztRQUNkLENBQUM7S0FBQTtJQUVELHlEQUF5RDtJQUNuRCxnQkFBZ0IsQ0FBQyxRQUFnQjs7WUFDckMsT0FBTyxJQUFJLENBQUMsaUJBQWlCLENBQUMsTUFBTSxrQkFBa0IsQ0FBQyxJQUFJLENBQUMsaUJBQWlCLENBQUMsUUFBUSxDQUFDLEVBQ25GLENBQUMsb0NBQW9DLENBQUMsUUFBUSxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUM7UUFDekQsQ0FBQztLQUFBO0lBRUQseURBQXlEO0lBQ25ELHdCQUF3QixDQUFDLFFBQWdCOztZQUM3QyxNQUFNLFFBQVEsR0FBRyxNQUFNLElBQUksQ0FBQyxpQkFBaUIsQ0FBQyxRQUFRLENBQUMsQ0FBQztZQUN4RCxPQUFPLFFBQVEsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLGlCQUFpQixDQUFDLFFBQVEsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUM7UUFDbEUsQ0FBQztLQUFBO0lBRUQseURBQXlEO0lBQ25ELG1CQUFtQixDQUFDLFFBQWdCOztZQUN4QyxNQUFNLFFBQVEsR0FBRyxNQUFNLElBQUksQ0FBQyxpQkFBaUIsQ0FBQyxRQUFRLENBQUMsQ0FBQztZQUN4RCxPQUFPLFFBQVEsQ0FBQyxHQUFHLENBQUMsT0FBTyxDQUFDLEVBQUUsQ0FBQyxJQUFJLENBQUMsaUJBQWlCLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQztRQUNsRSxDQUFDO0tBQUE7SUFFRCx3REFBd0Q7SUFDeEQsVUFBVSxDQUE2QixLQUFzQjtRQUMzRCxPQUFPLElBQUksQ0FBQyxVQUFVLENBQUMsS0FBSyxDQUFDLEVBQUUsQ0FBQztJQUNsQyxDQUFDO0lBRUQsd0RBQXdEO0lBQ3hELGVBQWUsQ0FBNkIsS0FBc0I7UUFDaEUsT0FBTyxJQUFJLENBQUMsYUFBYSxDQUFDLEtBQUssQ0FBQyxFQUFFLENBQUM7SUFDckMsQ0FBQztJQUVELHdEQUF3RDtJQUNsRCxjQUFjLENBQUMsUUFBZ0I7O1lBQ25DLE9BQU8sSUFBSSxDQUFDLGlCQUFpQixDQUFDLE1BQU0sa0JBQWtCLENBQUMsSUFBSSxDQUFDLGlCQUFpQixDQUFDLFFBQVEsQ0FBQyxFQUNuRixDQUFDLG9DQUFvQyxDQUFDLFFBQVEsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDO1FBQ3pELENBQUM7S0FBQTtJQUVELHdEQUF3RDtJQUNsRCxrQkFBa0IsQ0FBQyxRQUFnQjs7WUFDdkMsT0FBTyxDQUFDLE1BQU0sSUFBSSxDQUFDLGlCQUFpQixDQUFDLFFBQVEsQ0FBQyxDQUFDLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsSUFBSSxDQUFDLGlCQUFpQixDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUM7UUFDdEYsQ0FBQztLQUFBO0lBRUQsK0ZBQStGO0lBQ3JGLHNCQUFzQixDQUM1QixXQUEyQyxFQUFFLE9BQVU7UUFDekQsT0FBTyxJQUFJLFdBQVcsQ0FBQyxJQUFJLENBQUMsaUJBQWlCLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQztJQUMxRCxDQUFDO0lBc0JEOzs7T0FHRztJQUNXLCtCQUErQixDQUN6QyxPQUFVOztZQUNaLE1BQU0sRUFBQyxVQUFVLEVBQUUsY0FBYyxFQUFFLGNBQWMsRUFBRSxZQUFZLEVBQUMsR0FBRyxhQUFhLENBQUMsT0FBTyxDQUFDLENBQUM7WUFFMUYsK0ZBQStGO1lBQy9GLG1EQUFtRDtZQUNuRCxNQUFNLFdBQVcsR0FBRyxNQUFNLElBQUksQ0FBQyxpQkFBaUIsQ0FDNUMsQ0FBQyxHQUFHLGNBQWMsRUFBRSxHQUFHLGNBQWMsQ0FBQyxHQUFHLENBQUMsU0FBUyxDQUFDLEVBQUUsQ0FBQyxTQUFTLENBQUMsV0FBVyxFQUFFLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDO1lBRWhHLDhGQUE4RjtZQUM5Riw0RkFBNEY7WUFDNUYsNEZBQTRGO1lBQzVGLDRFQUE0RTtZQUM1RSxNQUFNLGlCQUFpQixHQUFHLENBQUMsY0FBYyxDQUFDLE1BQU0sS0FBSyxDQUFDLElBQUksWUFBWSxDQUFDLElBQUksS0FBSyxDQUFDLENBQUM7Z0JBQzlFLGNBQWMsQ0FBQyxNQUFNLEtBQUssQ0FBQyxDQUFDO1lBRWhDLE1BQU0saUJBQWlCLEdBQUcsTUFBTSxRQUFRLENBQUMsR0FBRyxFQUFFLENBQUMsV0FBVyxDQUFDLEdBQUcsQ0FBQyxDQUFNLFVBQVUsRUFBQyxFQUFFO2dCQUNoRixNQUFNLFdBQVcsR0FBRyxJQUFJLENBQUMsaUJBQWlCLENBQUMsVUFBVSxDQUFDLENBQUM7Z0JBQ3ZELE1BQU0sb0JBQW9CLEdBQUcsTUFBTSxPQUFPLENBQUMsR0FBRztnQkFDMUMsd0VBQXdFO2dCQUN4RSxvRkFBb0Y7Z0JBQ3BGLDhFQUE4RTtnQkFDOUUsMkVBQTJFO2dCQUMzRSxVQUFVLENBQUMsR0FBRyxDQUFDLEtBQUssQ0FBQyxFQUFFLENBQUMsSUFBSSxDQUFDLHlCQUF5QixDQUNsRCxLQUFLLEVBQUUsVUFBVSxFQUFFLFdBQVcsRUFBRSxpQkFBaUIsQ0FBQyxDQUFDLENBQUMsQ0FBQztnQkFDN0QsT0FBTyw0QkFBNEIsQ0FBQyxvQkFBb0IsQ0FBQyxDQUFDO1lBQzVELENBQUMsQ0FBQSxDQUFDLENBQUMsQ0FBQztZQUNKLE9BQVEsRUFBVSxDQUFDLE1BQU0sQ0FBQyxHQUFHLGlCQUFpQixDQUFDLENBQUM7UUFDbEQsQ0FBQztLQUFBO0lBRUQ7Ozs7O09BS0c7SUFDVyx5QkFBeUIsQ0FDbkMsS0FBbUMsRUFBRSxVQUFhLEVBQUUsV0FBd0IsRUFDNUUsb0JBQTZCLEtBQUs7O1lBQ3BDLElBQUksT0FBTyxLQUFLLEtBQUssUUFBUSxFQUFFO2dCQUM3QixPQUFPLENBQUMsQ0FBQyxpQkFBaUIsS0FBSSxNQUFNLFdBQVcsQ0FBQyxlQUFlLENBQUMsS0FBSyxDQUFDLENBQUEsQ0FBQyxDQUFDLENBQUMsQ0FBQyxXQUFXLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxDQUFDO2FBQy9GO1lBQ0QsSUFBSSxpQkFBaUIsS0FBSSxNQUFNLFdBQVcsQ0FBQyxlQUFlLENBQUMsS0FBSyxDQUFDLFdBQVcsRUFBRSxDQUFDLENBQUEsRUFBRTtnQkFDL0UsTUFBTSxPQUFPLEdBQUcsSUFBSSxDQUFDLHNCQUFzQixDQUFDLEtBQUssQ0FBQyxXQUFXLEVBQUUsVUFBVSxDQUFDLENBQUM7Z0JBQzNFLE9BQU8sQ0FBQyxNQUFNLEtBQUssQ0FBQyxRQUFRLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUM7YUFDekQ7WUFDRCxPQUFPLElBQUksQ0FBQztRQUNkLENBQUM7S0FBQTtDQUNGO0FBRUQ7OztHQUdHO0FBQ0gsU0FBUyxhQUFhLENBQTJDLE9BQVU7SUFFekUsTUFBTSxVQUFVLEdBQUcsRUFBRSxDQUFDO0lBQ3RCLE1BQU0sY0FBYyxHQUFHLEVBQUUsQ0FBQztJQUMxQixNQUFNLGNBQWMsR0FBRyxFQUFFLENBQUM7SUFDMUIsTUFBTSxZQUFZLEdBQ2QsSUFBSSxHQUFHLEVBQXNFLENBQUM7SUFFbEYsS0FBSyxNQUFNLEtBQUssSUFBSSxPQUFPLEVBQUU7UUFDM0IsSUFBSSxPQUFPLEtBQUssS0FBSyxRQUFRLEVBQUU7WUFDN0IsVUFBVSxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsQ0FBQztZQUN2QixjQUFjLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxDQUFDO1NBQzVCO2FBQU07WUFDTCxNQUFNLFNBQVMsR0FBRyxLQUFLLFlBQVksZ0JBQWdCLENBQUMsQ0FBQyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsSUFBSSxnQkFBZ0IsQ0FBQyxLQUFLLEVBQUUsRUFBRSxDQUFDLENBQUM7WUFDOUYsVUFBVSxDQUFDLElBQUksQ0FBQyxTQUFTLENBQUMsQ0FBQztZQUMzQixjQUFjLENBQUMsSUFBSSxDQUFDLFNBQVMsQ0FBQyxDQUFDO1lBQy9CLFlBQVksQ0FBQyxHQUFHLENBQUMsU0FBUyxDQUFDLFdBQVcsQ0FBQyxDQUFDO1NBQ3pDO0tBQ0Y7SUFFRCxPQUFPLEVBQUMsVUFBVSxFQUFFLGNBQWMsRUFBRSxjQUFjLEVBQUUsWUFBWSxFQUFDLENBQUM7QUFDcEUsQ0FBQztBQUVEOzs7R0FHRztBQUNILFNBQWUsNEJBQTRCLENBQ3ZDLE9BQVU7O1FBQ1osSUFBSSxrQkFBa0IsR0FBRyxLQUFLLENBQUM7UUFDL0IsSUFBSSxtQkFBbUIsR0FBRyxJQUFJLEdBQUcsRUFBRSxDQUFDO1FBQ3BDLE1BQU0sY0FBYyxHQUFHLEVBQUUsQ0FBQztRQUMxQixLQUFLLE1BQU0sTUFBTSxJQUFJLE9BQU8sRUFBRTtZQUM1QixJQUFJLENBQUMsTUFBTSxFQUFFO2dCQUNYLFNBQVM7YUFDVjtZQUNELElBQUksTUFBTSxZQUFZLGdCQUFnQixFQUFFO2dCQUN0QyxJQUFJLENBQUMsbUJBQW1CLENBQUMsR0FBRyxDQUFDLE1BQU0sQ0FBQyxXQUFXLENBQUMsRUFBRTtvQkFDaEQsbUJBQW1CLENBQUMsR0FBRyxDQUFDLE1BQU0sQ0FBQyxXQUFXLENBQUMsQ0FBQztvQkFDNUMsY0FBYyxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsQ0FBQztpQkFDN0I7YUFDRjtpQkFBTSxJQUFJLENBQUMsa0JBQWtCLEVBQUU7Z0JBQzlCLGtCQUFrQixHQUFHLElBQUksQ0FBQztnQkFDMUIsY0FBYyxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsQ0FBQzthQUM3QjtTQUNGO1FBQ0QsT0FBTyxjQUFtQixDQUFDO0lBQzdCLENBQUM7Q0FBQTtBQUVELDhEQUE4RDtBQUM5RCxTQUFlLGtCQUFrQixDQUFJLE9BQXFCLEVBQUUsaUJBQTJCOztRQUVyRixNQUFNLE1BQU0sR0FBRyxDQUFDLE1BQU0sT0FBTyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUM7UUFDbEMsSUFBSSxNQUFNLElBQUksU0FBUyxFQUFFO1lBQ3ZCLE1BQU0sS0FBSyxDQUFDLGlFQUFpRTtnQkFDekUsaUJBQWlCLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUMsSUFBSSxJQUFJLEdBQUcsQ0FBQyxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDO1NBQzdEO1FBQ0QsT0FBTyxNQUFNLENBQUM7SUFDaEIsQ0FBQztDQUFBO0FBRUQsaUVBQWlFO0FBQ2pFLFNBQVMsbUNBQW1DLENBQUMsT0FBdUM7SUFDbEYsT0FBTyxPQUFPLENBQUMsR0FBRyxDQUFDLEtBQUssQ0FBQyxFQUFFLENBQUMsT0FBTyxLQUFLLEtBQUssUUFBUSxDQUFDLENBQUM7UUFDbkQsa0NBQWtDLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDLHVDQUF1QyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUM7QUFDbEcsQ0FBQztBQUVELGdFQUFnRTtBQUNoRSxTQUFTLHVDQUF1QyxDQUFDLEtBQXdCO0lBQ3ZFLE1BQU0sZ0JBQWdCLEdBQ2xCLEtBQUssWUFBWSxnQkFBZ0IsQ0FBQyxDQUFDLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxJQUFJLGdCQUFnQixDQUFDLEtBQUssRUFBRSxFQUFFLENBQUMsQ0FBQztJQUNoRixNQUFNLEVBQUMsSUFBSSxFQUFFLFlBQVksRUFBQyxHQUFHLGdCQUFnQixDQUFDLFdBQVcsQ0FBQztJQUMxRCxNQUFNLFdBQVcsR0FBRyxHQUFHLElBQUksMENBQTBDLFlBQVksR0FBRyxDQUFDO0lBQ3JGLE1BQU0sV0FBVyxHQUFHLGdCQUFnQixDQUFDLGNBQWMsRUFBRSxDQUFDO0lBQ3RELE9BQU8sV0FBVyxHQUFHLENBQUMsV0FBVyxDQUFDLENBQUM7UUFDL0IsZ0NBQWdDLGdCQUFnQixDQUFDLGNBQWMsRUFBRSxFQUFFLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDO0FBQ2hGLENBQUM7QUFFRCwyREFBMkQ7QUFDM0QsU0FBUyxrQ0FBa0MsQ0FBQyxRQUFnQjtJQUMxRCxPQUFPLCtDQUErQyxRQUFRLEdBQUcsQ0FBQztBQUNwRSxDQUFDO0FBRUQsNkRBQTZEO0FBQzdELFNBQVMsb0NBQW9DLENBQUMsUUFBZ0I7SUFDNUQsT0FBTyxpREFBaUQsUUFBUSxHQUFHLENBQUM7QUFDdEUsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogQGxpY2Vuc2VcbiAqIENvcHlyaWdodCBHb29nbGUgTExDIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG4gKlxuICogVXNlIG9mIHRoaXMgc291cmNlIGNvZGUgaXMgZ292ZXJuZWQgYnkgYW4gTUlULXN0eWxlIGxpY2Vuc2UgdGhhdCBjYW4gYmVcbiAqIGZvdW5kIGluIHRoZSBMSUNFTlNFIGZpbGUgYXQgaHR0cHM6Ly9hbmd1bGFyLmlvL2xpY2Vuc2VcbiAqL1xuXG5pbXBvcnQge3BhcmFsbGVsfSBmcm9tICcuL2NoYW5nZS1kZXRlY3Rpb24nO1xuaW1wb3J0IHtcbiAgQXN5bmNGYWN0b3J5Rm4sXG4gIENvbXBvbmVudEhhcm5lc3MsXG4gIENvbXBvbmVudEhhcm5lc3NDb25zdHJ1Y3RvcixcbiAgSGFybmVzc0xvYWRlcixcbiAgSGFybmVzc1ByZWRpY2F0ZSxcbiAgSGFybmVzc1F1ZXJ5LFxuICBMb2NhdG9yRmFjdG9yeSxcbiAgTG9jYXRvckZuUmVzdWx0LFxufSBmcm9tICcuL2NvbXBvbmVudC1oYXJuZXNzJztcbmltcG9ydCB7VGVzdEVsZW1lbnR9IGZyb20gJy4vdGVzdC1lbGVtZW50JztcblxuLyoqIFBhcnNlZCBmb3JtIG9mIHRoZSBxdWVyaWVzIHBhc3NlZCB0byB0aGUgYGxvY2F0b3JGb3IqYCBtZXRob2RzLiAqL1xudHlwZSBQYXJzZWRRdWVyaWVzPFQgZXh0ZW5kcyBDb21wb25lbnRIYXJuZXNzPiA9IHtcbiAgLyoqIFRoZSBmdWxsIGxpc3Qgb2YgcXVlcmllcywgaW4gdGhlaXIgb3JpZ2luYWwgb3JkZXIuICovXG4gIGFsbFF1ZXJpZXM6IChzdHJpbmcgfCBIYXJuZXNzUHJlZGljYXRlPFQ+KVtdLFxuICAvKipcbiAgICogQSBmaWx0ZXJlZCB2aWV3IG9mIGBhbGxRdWVyaWVzYCBjb250YWluaW5nIG9ubHkgdGhlIHF1ZXJpZXMgdGhhdCBhcmUgbG9va2luZyBmb3IgYVxuICAgKiBgQ29tcG9uZW50SGFybmVzc2BcbiAgICovXG4gIGhhcm5lc3NRdWVyaWVzOiBIYXJuZXNzUHJlZGljYXRlPFQ+W10sXG4gIC8qKlxuICAgKiBBIGZpbHRlcmVkIHZpZXcgb2YgYGFsbFF1ZXJpZXNgIGNvbnRhaW5pbmcgb25seSB0aGUgcXVlcmllcyB0aGF0IGFyZSBsb29raW5nIGZvciBhXG4gICAqIGBUZXN0RWxlbWVudGBcbiAgICovXG4gIGVsZW1lbnRRdWVyaWVzOiBzdHJpbmdbXSxcbiAgLyoqIFRoZSBzZXQgb2YgYWxsIGBDb21wb25lbnRIYXJuZXNzYCBzdWJjbGFzc2VzIHJlcHJlc2VudGVkIGluIHRoZSBvcmlnaW5hbCBxdWVyeSBsaXN0LiAqL1xuICBoYXJuZXNzVHlwZXM6IFNldDxDb21wb25lbnRIYXJuZXNzQ29uc3RydWN0b3I8VD4+LFxufTtcblxuLyoqXG4gKiBCYXNlIGhhcm5lc3MgZW52aXJvbm1lbnQgY2xhc3MgdGhhdCBjYW4gYmUgZXh0ZW5kZWQgdG8gYWxsb3cgYENvbXBvbmVudEhhcm5lc3NgZXMgdG8gYmUgdXNlZCBpblxuICogZGlmZmVyZW50IHRlc3QgZW52aXJvbm1lbnRzIChlLmcuIHRlc3RiZWQsIHByb3RyYWN0b3IsIGV0Yy4pLiBUaGlzIGNsYXNzIGltcGxlbWVudHMgdGhlXG4gKiBmdW5jdGlvbmFsaXR5IG9mIGJvdGggYSBgSGFybmVzc0xvYWRlcmAgYW5kIGBMb2NhdG9yRmFjdG9yeWAuIFRoaXMgY2xhc3MgaXMgZ2VuZXJpYyBvbiB0aGUgcmF3XG4gKiBlbGVtZW50IHR5cGUsIGBFYCwgdXNlZCBieSB0aGUgcGFydGljdWxhciB0ZXN0IGVudmlyb25tZW50LlxuICovXG5leHBvcnQgYWJzdHJhY3QgY2xhc3MgSGFybmVzc0Vudmlyb25tZW50PEU+IGltcGxlbWVudHMgSGFybmVzc0xvYWRlciwgTG9jYXRvckZhY3Rvcnkge1xuICAvLyBJbXBsZW1lbnRlZCBhcyBwYXJ0IG9mIHRoZSBgTG9jYXRvckZhY3RvcnlgIGludGVyZmFjZS5cbiAgcm9vdEVsZW1lbnQ6IFRlc3RFbGVtZW50O1xuXG4gIHByb3RlY3RlZCBjb25zdHJ1Y3Rvcihwcm90ZWN0ZWQgcmF3Um9vdEVsZW1lbnQ6IEUpIHtcbiAgICB0aGlzLnJvb3RFbGVtZW50ID0gdGhpcy5jcmVhdGVUZXN0RWxlbWVudChyYXdSb290RWxlbWVudCk7XG4gIH1cblxuICAvLyBJbXBsZW1lbnRlZCBhcyBwYXJ0IG9mIHRoZSBgTG9jYXRvckZhY3RvcnlgIGludGVyZmFjZS5cbiAgZG9jdW1lbnRSb290TG9jYXRvckZhY3RvcnkoKTogTG9jYXRvckZhY3Rvcnkge1xuICAgIHJldHVybiB0aGlzLmNyZWF0ZUVudmlyb25tZW50KHRoaXMuZ2V0RG9jdW1lbnRSb290KCkpO1xuICB9XG5cbiAgLy8gSW1wbGVtZW50ZWQgYXMgcGFydCBvZiB0aGUgYExvY2F0b3JGYWN0b3J5YCBpbnRlcmZhY2UuXG4gIGxvY2F0b3JGb3I8VCBleHRlbmRzIChIYXJuZXNzUXVlcnk8YW55PiB8IHN0cmluZylbXT4oLi4ucXVlcmllczogVCk6XG4gICAgICBBc3luY0ZhY3RvcnlGbjxMb2NhdG9yRm5SZXN1bHQ8VD4+IHtcbiAgICByZXR1cm4gKCkgPT4gX2Fzc2VydFJlc3VsdEZvdW5kKFxuICAgICAgICB0aGlzLl9nZXRBbGxIYXJuZXNzZXNBbmRUZXN0RWxlbWVudHMocXVlcmllcyksXG4gICAgICAgIF9nZXREZXNjcmlwdGlvbkZvckxvY2F0b3JGb3JRdWVyaWVzKHF1ZXJpZXMpKTtcbiAgfVxuXG4gIC8vIEltcGxlbWVudGVkIGFzIHBhcnQgb2YgdGhlIGBMb2NhdG9yRmFjdG9yeWAgaW50ZXJmYWNlLlxuICBsb2NhdG9yRm9yT3B0aW9uYWw8VCBleHRlbmRzIChIYXJuZXNzUXVlcnk8YW55PiB8IHN0cmluZylbXT4oLi4ucXVlcmllczogVCk6XG4gICAgICBBc3luY0ZhY3RvcnlGbjxMb2NhdG9yRm5SZXN1bHQ8VD4gfCBudWxsPiB7XG4gICAgcmV0dXJuIGFzeW5jICgpID0+IChhd2FpdCB0aGlzLl9nZXRBbGxIYXJuZXNzZXNBbmRUZXN0RWxlbWVudHMocXVlcmllcykpWzBdIHx8IG51bGw7XG4gIH1cblxuICAvLyBJbXBsZW1lbnRlZCBhcyBwYXJ0IG9mIHRoZSBgTG9jYXRvckZhY3RvcnlgIGludGVyZmFjZS5cbiAgbG9jYXRvckZvckFsbDxUIGV4dGVuZHMgKEhhcm5lc3NRdWVyeTxhbnk+IHwgc3RyaW5nKVtdPiguLi5xdWVyaWVzOiBUKTpcbiAgICAgIEFzeW5jRmFjdG9yeUZuPExvY2F0b3JGblJlc3VsdDxUPltdPiB7XG4gICAgcmV0dXJuICgpID0+IHRoaXMuX2dldEFsbEhhcm5lc3Nlc0FuZFRlc3RFbGVtZW50cyhxdWVyaWVzKTtcbiAgfVxuXG4gIC8vIEltcGxlbWVudGVkIGFzIHBhcnQgb2YgdGhlIGBMb2NhdG9yRmFjdG9yeWAgaW50ZXJmYWNlLlxuICBhc3luYyByb290SGFybmVzc0xvYWRlcigpOiBQcm9taXNlPEhhcm5lc3NMb2FkZXI+IHtcbiAgICByZXR1cm4gdGhpcztcbiAgfVxuXG4gIC8vIEltcGxlbWVudGVkIGFzIHBhcnQgb2YgdGhlIGBMb2NhdG9yRmFjdG9yeWAgaW50ZXJmYWNlLlxuICBhc3luYyBoYXJuZXNzTG9hZGVyRm9yKHNlbGVjdG9yOiBzdHJpbmcpOiBQcm9taXNlPEhhcm5lc3NMb2FkZXI+IHtcbiAgICByZXR1cm4gdGhpcy5jcmVhdGVFbnZpcm9ubWVudChhd2FpdCBfYXNzZXJ0UmVzdWx0Rm91bmQodGhpcy5nZXRBbGxSYXdFbGVtZW50cyhzZWxlY3RvciksXG4gICAgICAgIFtfZ2V0RGVzY3JpcHRpb25Gb3JIYXJuZXNzTG9hZGVyUXVlcnkoc2VsZWN0b3IpXSkpO1xuICB9XG5cbiAgLy8gSW1wbGVtZW50ZWQgYXMgcGFydCBvZiB0aGUgYExvY2F0b3JGYWN0b3J5YCBpbnRlcmZhY2UuXG4gIGFzeW5jIGhhcm5lc3NMb2FkZXJGb3JPcHRpb25hbChzZWxlY3Rvcjogc3RyaW5nKTogUHJvbWlzZTxIYXJuZXNzTG9hZGVyIHwgbnVsbD4ge1xuICAgIGNvbnN0IGVsZW1lbnRzID0gYXdhaXQgdGhpcy5nZXRBbGxSYXdFbGVtZW50cyhzZWxlY3Rvcik7XG4gICAgcmV0dXJuIGVsZW1lbnRzWzBdID8gdGhpcy5jcmVhdGVFbnZpcm9ubWVudChlbGVtZW50c1swXSkgOiBudWxsO1xuICB9XG5cbiAgLy8gSW1wbGVtZW50ZWQgYXMgcGFydCBvZiB0aGUgYExvY2F0b3JGYWN0b3J5YCBpbnRlcmZhY2UuXG4gIGFzeW5jIGhhcm5lc3NMb2FkZXJGb3JBbGwoc2VsZWN0b3I6IHN0cmluZyk6IFByb21pc2U8SGFybmVzc0xvYWRlcltdPiB7XG4gICAgY29uc3QgZWxlbWVudHMgPSBhd2FpdCB0aGlzLmdldEFsbFJhd0VsZW1lbnRzKHNlbGVjdG9yKTtcbiAgICByZXR1cm4gZWxlbWVudHMubWFwKGVsZW1lbnQgPT4gdGhpcy5jcmVhdGVFbnZpcm9ubWVudChlbGVtZW50KSk7XG4gIH1cblxuICAvLyBJbXBsZW1lbnRlZCBhcyBwYXJ0IG9mIHRoZSBgSGFybmVzc0xvYWRlcmAgaW50ZXJmYWNlLlxuICBnZXRIYXJuZXNzPFQgZXh0ZW5kcyBDb21wb25lbnRIYXJuZXNzPihxdWVyeTogSGFybmVzc1F1ZXJ5PFQ+KTogUHJvbWlzZTxUPiB7XG4gICAgcmV0dXJuIHRoaXMubG9jYXRvckZvcihxdWVyeSkoKTtcbiAgfVxuXG4gIC8vIEltcGxlbWVudGVkIGFzIHBhcnQgb2YgdGhlIGBIYXJuZXNzTG9hZGVyYCBpbnRlcmZhY2UuXG4gIGdldEFsbEhhcm5lc3NlczxUIGV4dGVuZHMgQ29tcG9uZW50SGFybmVzcz4ocXVlcnk6IEhhcm5lc3NRdWVyeTxUPik6IFByb21pc2U8VFtdPiB7XG4gICAgcmV0dXJuIHRoaXMubG9jYXRvckZvckFsbChxdWVyeSkoKTtcbiAgfVxuXG4gIC8vIEltcGxlbWVudGVkIGFzIHBhcnQgb2YgdGhlIGBIYXJuZXNzTG9hZGVyYCBpbnRlcmZhY2UuXG4gIGFzeW5jIGdldENoaWxkTG9hZGVyKHNlbGVjdG9yOiBzdHJpbmcpOiBQcm9taXNlPEhhcm5lc3NMb2FkZXI+IHtcbiAgICByZXR1cm4gdGhpcy5jcmVhdGVFbnZpcm9ubWVudChhd2FpdCBfYXNzZXJ0UmVzdWx0Rm91bmQodGhpcy5nZXRBbGxSYXdFbGVtZW50cyhzZWxlY3RvciksXG4gICAgICAgIFtfZ2V0RGVzY3JpcHRpb25Gb3JIYXJuZXNzTG9hZGVyUXVlcnkoc2VsZWN0b3IpXSkpO1xuICB9XG5cbiAgLy8gSW1wbGVtZW50ZWQgYXMgcGFydCBvZiB0aGUgYEhhcm5lc3NMb2FkZXJgIGludGVyZmFjZS5cbiAgYXN5bmMgZ2V0QWxsQ2hpbGRMb2FkZXJzKHNlbGVjdG9yOiBzdHJpbmcpOiBQcm9taXNlPEhhcm5lc3NMb2FkZXJbXT4ge1xuICAgIHJldHVybiAoYXdhaXQgdGhpcy5nZXRBbGxSYXdFbGVtZW50cyhzZWxlY3RvcikpLm1hcChlID0+IHRoaXMuY3JlYXRlRW52aXJvbm1lbnQoZSkpO1xuICB9XG5cbiAgLyoqIENyZWF0ZXMgYSBgQ29tcG9uZW50SGFybmVzc2AgZm9yIHRoZSBnaXZlbiBoYXJuZXNzIHR5cGUgd2l0aCB0aGUgZ2l2ZW4gcmF3IGhvc3QgZWxlbWVudC4gKi9cbiAgcHJvdGVjdGVkIGNyZWF0ZUNvbXBvbmVudEhhcm5lc3M8VCBleHRlbmRzIENvbXBvbmVudEhhcm5lc3M+KFxuICAgICAgaGFybmVzc1R5cGU6IENvbXBvbmVudEhhcm5lc3NDb25zdHJ1Y3RvcjxUPiwgZWxlbWVudDogRSk6IFQge1xuICAgIHJldHVybiBuZXcgaGFybmVzc1R5cGUodGhpcy5jcmVhdGVFbnZpcm9ubWVudChlbGVtZW50KSk7XG4gIH1cblxuICAvLyBQYXJ0IG9mIExvY2F0b3JGYWN0b3J5IGludGVyZmFjZSwgc3ViY2xhc3NlcyB3aWxsIGltcGxlbWVudC5cbiAgYWJzdHJhY3QgZm9yY2VTdGFiaWxpemUoKTogUHJvbWlzZTx2b2lkPjtcblxuICAvLyBQYXJ0IG9mIExvY2F0b3JGYWN0b3J5IGludGVyZmFjZSwgc3ViY2xhc3NlcyB3aWxsIGltcGxlbWVudC5cbiAgYWJzdHJhY3Qgd2FpdEZvclRhc2tzT3V0c2lkZUFuZ3VsYXIoKTogUHJvbWlzZTx2b2lkPjtcblxuICAvKiogR2V0cyB0aGUgcm9vdCBlbGVtZW50IGZvciB0aGUgZG9jdW1lbnQuICovXG4gIHByb3RlY3RlZCBhYnN0cmFjdCBnZXREb2N1bWVudFJvb3QoKTogRTtcblxuICAvKiogQ3JlYXRlcyBhIGBUZXN0RWxlbWVudGAgZnJvbSBhIHJhdyBlbGVtZW50LiAqL1xuICBwcm90ZWN0ZWQgYWJzdHJhY3QgY3JlYXRlVGVzdEVsZW1lbnQoZWxlbWVudDogRSk6IFRlc3RFbGVtZW50O1xuXG4gIC8qKiBDcmVhdGVzIGEgYEhhcm5lc3NMb2FkZXJgIHJvb3RlZCBhdCB0aGUgZ2l2ZW4gcmF3IGVsZW1lbnQuICovXG4gIHByb3RlY3RlZCBhYnN0cmFjdCBjcmVhdGVFbnZpcm9ubWVudChlbGVtZW50OiBFKTogSGFybmVzc0Vudmlyb25tZW50PEU+O1xuXG4gIC8qKlxuICAgKiBHZXRzIGEgbGlzdCBvZiBhbGwgZWxlbWVudHMgbWF0Y2hpbmcgdGhlIGdpdmVuIHNlbGVjdG9yIHVuZGVyIHRoaXMgZW52aXJvbm1lbnQncyByb290IGVsZW1lbnQuXG4gICAqL1xuICBwcm90ZWN0ZWQgYWJzdHJhY3QgZ2V0QWxsUmF3RWxlbWVudHMoc2VsZWN0b3I6IHN0cmluZyk6IFByb21pc2U8RVtdPjtcblxuICAvKipcbiAgICogTWF0Y2hlcyB0aGUgZ2l2ZW4gcmF3IGVsZW1lbnRzIHdpdGggdGhlIGdpdmVuIGxpc3Qgb2YgZWxlbWVudCBhbmQgaGFybmVzcyBxdWVyaWVzIHRvIHByb2R1Y2UgYVxuICAgKiBsaXN0IG9mIG1hdGNoZWQgaGFybmVzc2VzIGFuZCB0ZXN0IGVsZW1lbnRzLlxuICAgKi9cbiAgcHJpdmF0ZSBhc3luYyBfZ2V0QWxsSGFybmVzc2VzQW5kVGVzdEVsZW1lbnRzPFQgZXh0ZW5kcyAoSGFybmVzc1F1ZXJ5PGFueT4gfCBzdHJpbmcpW10+KFxuICAgICAgcXVlcmllczogVCk6IFByb21pc2U8TG9jYXRvckZuUmVzdWx0PFQ+W10+IHtcbiAgICBjb25zdCB7YWxsUXVlcmllcywgaGFybmVzc1F1ZXJpZXMsIGVsZW1lbnRRdWVyaWVzLCBoYXJuZXNzVHlwZXN9ID0gX3BhcnNlUXVlcmllcyhxdWVyaWVzKTtcblxuICAgIC8vIENvbWJpbmUgYWxsIG9mIHRoZSBxdWVyaWVzIGludG8gb25lIGxhcmdlIGNvbW1hLWRlbGltaXRlZCBzZWxlY3RvciBhbmQgdXNlIGl0IHRvIGdldCBhbGwgcmF3XG4gICAgLy8gZWxlbWVudHMgbWF0Y2hpbmcgYW55IG9mIHRoZSBpbmRpdmlkdWFsIHF1ZXJpZXMuXG4gICAgY29uc3QgcmF3RWxlbWVudHMgPSBhd2FpdCB0aGlzLmdldEFsbFJhd0VsZW1lbnRzKFxuICAgICAgICBbLi4uZWxlbWVudFF1ZXJpZXMsIC4uLmhhcm5lc3NRdWVyaWVzLm1hcChwcmVkaWNhdGUgPT4gcHJlZGljYXRlLmdldFNlbGVjdG9yKCkpXS5qb2luKCcsJykpO1xuXG4gICAgLy8gSWYgZXZlcnkgcXVlcnkgaXMgc2VhcmNoaW5nIGZvciB0aGUgc2FtZSBoYXJuZXNzIHN1YmNsYXNzLCB3ZSBrbm93IGV2ZXJ5IHJlc3VsdCBjb3JyZXNwb25kc1xuICAgIC8vIHRvIGFuIGluc3RhbmNlIG9mIHRoYXQgc3ViY2xhc3MuIExpa2V3aXNlLCBpZiBldmVyeSBxdWVyeSBpcyBmb3IgYSBgVGVzdEVsZW1lbnRgLCB3ZSBrbm93XG4gICAgLy8gZXZlcnkgcmVzdWx0IGNvcnJlc3BvbmRzIHRvIGEgYFRlc3RFbGVtZW50YC4gT3RoZXJ3aXNlIHdlIG5lZWQgdG8gdmVyaWZ5IHdoaWNoIHJlc3VsdCB3YXNcbiAgICAvLyBmb3VuZCBieSB3aGljaCBzZWxlY3RvciBzbyBpdCBjYW4gYmUgbWF0Y2hlZCB0byB0aGUgYXBwcm9wcmlhdGUgaW5zdGFuY2UuXG4gICAgY29uc3Qgc2tpcFNlbGVjdG9yQ2hlY2sgPSAoZWxlbWVudFF1ZXJpZXMubGVuZ3RoID09PSAwICYmIGhhcm5lc3NUeXBlcy5zaXplID09PSAxKSB8fFxuICAgICAgICBoYXJuZXNzUXVlcmllcy5sZW5ndGggPT09IDA7XG5cbiAgICBjb25zdCBwZXJFbGVtZW50TWF0Y2hlcyA9IGF3YWl0IHBhcmFsbGVsKCgpID0+IHJhd0VsZW1lbnRzLm1hcChhc3luYyByYXdFbGVtZW50ID0+IHtcbiAgICAgIGNvbnN0IHRlc3RFbGVtZW50ID0gdGhpcy5jcmVhdGVUZXN0RWxlbWVudChyYXdFbGVtZW50KTtcbiAgICAgIGNvbnN0IGFsbFJlc3VsdHNGb3JFbGVtZW50ID0gYXdhaXQgUHJvbWlzZS5hbGwoXG4gICAgICAgICAgLy8gRm9yIGVhY2ggcXVlcnksIGdldCBgbnVsbGAgaWYgaXQgZG9lc24ndCBtYXRjaCwgb3IgYSBgVGVzdEVsZW1lbnRgIG9yXG4gICAgICAgICAgLy8gYENvbXBvbmVudEhhcm5lc3NgIGFzIGFwcHJvcHJpYXRlIGlmIGl0IGRvZXMgbWF0Y2guIFRoaXMgZ2l2ZXMgdXMgZXZlcnl0aGluZyB0aGF0XG4gICAgICAgICAgLy8gbWF0Y2hlcyB0aGUgY3VycmVudCByYXcgZWxlbWVudCwgYnV0IGl0IG1heSBjb250YWluIGR1cGxpY2F0ZSBlbnRyaWVzIChlLmcuXG4gICAgICAgICAgLy8gbXVsdGlwbGUgYFRlc3RFbGVtZW50YCBvciBtdWx0aXBsZSBgQ29tcG9uZW50SGFybmVzc2Agb2YgdGhlIHNhbWUgdHlwZSkuXG4gICAgICAgICAgYWxsUXVlcmllcy5tYXAocXVlcnkgPT4gdGhpcy5fZ2V0UXVlcnlSZXN1bHRGb3JFbGVtZW50KFxuICAgICAgICAgICAgICBxdWVyeSwgcmF3RWxlbWVudCwgdGVzdEVsZW1lbnQsIHNraXBTZWxlY3RvckNoZWNrKSkpO1xuICAgICAgcmV0dXJuIF9yZW1vdmVEdXBsaWNhdGVRdWVyeVJlc3VsdHMoYWxsUmVzdWx0c0ZvckVsZW1lbnQpO1xuICAgIH0pKTtcbiAgICByZXR1cm4gKFtdIGFzIGFueSkuY29uY2F0KC4uLnBlckVsZW1lbnRNYXRjaGVzKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBDaGVjayB3aGV0aGVyIHRoZSBnaXZlbiBxdWVyeSBtYXRjaGVzIHRoZSBnaXZlbiBlbGVtZW50LCBpZiBpdCBkb2VzIHJldHVybiB0aGUgbWF0Y2hlZFxuICAgKiBgVGVzdEVsZW1lbnRgIG9yIGBDb21wb25lbnRIYXJuZXNzYCwgaWYgaXQgZG9lcyBub3QsIHJldHVybiBudWxsLiBJbiBjYXNlcyB3aGVyZSB0aGUgY2FsbGVyXG4gICAqIGtub3dzIGZvciBzdXJlIHRoYXQgdGhlIHF1ZXJ5IG1hdGNoZXMgdGhlIGVsZW1lbnQncyBzZWxlY3RvciwgYHNraXBTZWxlY3RvckNoZWNrYCBjYW4gYmUgdXNlZFxuICAgKiB0byBza2lwIHZlcmlmaWNhdGlvbiBhbmQgb3B0aW1pemUgcGVyZm9ybWFuY2UuXG4gICAqL1xuICBwcml2YXRlIGFzeW5jIF9nZXRRdWVyeVJlc3VsdEZvckVsZW1lbnQ8VCBleHRlbmRzIENvbXBvbmVudEhhcm5lc3M+KFxuICAgICAgcXVlcnk6IHN0cmluZyB8IEhhcm5lc3NQcmVkaWNhdGU8VD4sIHJhd0VsZW1lbnQ6IEUsIHRlc3RFbGVtZW50OiBUZXN0RWxlbWVudCxcbiAgICAgIHNraXBTZWxlY3RvckNoZWNrOiBib29sZWFuID0gZmFsc2UpOiBQcm9taXNlPFQgfCBUZXN0RWxlbWVudCB8IG51bGw+IHtcbiAgICBpZiAodHlwZW9mIHF1ZXJ5ID09PSAnc3RyaW5nJykge1xuICAgICAgcmV0dXJuICgoc2tpcFNlbGVjdG9yQ2hlY2sgfHwgYXdhaXQgdGVzdEVsZW1lbnQubWF0Y2hlc1NlbGVjdG9yKHF1ZXJ5KSkgPyB0ZXN0RWxlbWVudCA6IG51bGwpO1xuICAgIH1cbiAgICBpZiAoc2tpcFNlbGVjdG9yQ2hlY2sgfHwgYXdhaXQgdGVzdEVsZW1lbnQubWF0Y2hlc1NlbGVjdG9yKHF1ZXJ5LmdldFNlbGVjdG9yKCkpKSB7XG4gICAgICBjb25zdCBoYXJuZXNzID0gdGhpcy5jcmVhdGVDb21wb25lbnRIYXJuZXNzKHF1ZXJ5Lmhhcm5lc3NUeXBlLCByYXdFbGVtZW50KTtcbiAgICAgIHJldHVybiAoYXdhaXQgcXVlcnkuZXZhbHVhdGUoaGFybmVzcykpID8gaGFybmVzcyA6IG51bGw7XG4gICAgfVxuICAgIHJldHVybiBudWxsO1xuICB9XG59XG5cbi8qKlxuICogUGFyc2VzIGEgbGlzdCBvZiBxdWVyaWVzIGluIHRoZSBmb3JtYXQgYWNjZXB0ZWQgYnkgdGhlIGBsb2NhdG9yRm9yKmAgbWV0aG9kcyBpbnRvIGFuIGVhc2llciB0b1xuICogd29yayB3aXRoIGZvcm1hdC5cbiAqL1xuZnVuY3Rpb24gX3BhcnNlUXVlcmllczxUIGV4dGVuZHMgKEhhcm5lc3NRdWVyeTxhbnk+IHwgc3RyaW5nKVtdPihxdWVyaWVzOiBUKTpcbiAgICBQYXJzZWRRdWVyaWVzPExvY2F0b3JGblJlc3VsdDxUPiAmIENvbXBvbmVudEhhcm5lc3M+IHtcbiAgY29uc3QgYWxsUXVlcmllcyA9IFtdO1xuICBjb25zdCBoYXJuZXNzUXVlcmllcyA9IFtdO1xuICBjb25zdCBlbGVtZW50UXVlcmllcyA9IFtdO1xuICBjb25zdCBoYXJuZXNzVHlwZXMgPVxuICAgICAgbmV3IFNldDxDb21wb25lbnRIYXJuZXNzQ29uc3RydWN0b3I8TG9jYXRvckZuUmVzdWx0PFQ+ICYgQ29tcG9uZW50SGFybmVzcz4+KCk7XG5cbiAgZm9yIChjb25zdCBxdWVyeSBvZiBxdWVyaWVzKSB7XG4gICAgaWYgKHR5cGVvZiBxdWVyeSA9PT0gJ3N0cmluZycpIHtcbiAgICAgIGFsbFF1ZXJpZXMucHVzaChxdWVyeSk7XG4gICAgICBlbGVtZW50UXVlcmllcy5wdXNoKHF1ZXJ5KTtcbiAgICB9IGVsc2Uge1xuICAgICAgY29uc3QgcHJlZGljYXRlID0gcXVlcnkgaW5zdGFuY2VvZiBIYXJuZXNzUHJlZGljYXRlID8gcXVlcnkgOiBuZXcgSGFybmVzc1ByZWRpY2F0ZShxdWVyeSwge30pO1xuICAgICAgYWxsUXVlcmllcy5wdXNoKHByZWRpY2F0ZSk7XG4gICAgICBoYXJuZXNzUXVlcmllcy5wdXNoKHByZWRpY2F0ZSk7XG4gICAgICBoYXJuZXNzVHlwZXMuYWRkKHByZWRpY2F0ZS5oYXJuZXNzVHlwZSk7XG4gICAgfVxuICB9XG5cbiAgcmV0dXJuIHthbGxRdWVyaWVzLCBoYXJuZXNzUXVlcmllcywgZWxlbWVudFF1ZXJpZXMsIGhhcm5lc3NUeXBlc307XG59XG5cbi8qKlxuICogUmVtb3ZlcyBkdXBsaWNhdGUgcXVlcnkgcmVzdWx0cyBmb3IgYSBwYXJ0aWN1bGFyIGVsZW1lbnQuIChlLmcuIG11bHRpcGxlIGBUZXN0RWxlbWVudGBcbiAqIGluc3RhbmNlcyBvciBtdWx0aXBsZSBpbnN0YW5jZXMgb2YgdGhlIHNhbWUgYENvbXBvbmVudEhhcm5lc3NgIGNsYXNzLlxuICovXG5hc3luYyBmdW5jdGlvbiBfcmVtb3ZlRHVwbGljYXRlUXVlcnlSZXN1bHRzPFQgZXh0ZW5kcyAoQ29tcG9uZW50SGFybmVzcyB8IFRlc3RFbGVtZW50IHwgbnVsbClbXT4oXG4gICAgcmVzdWx0czogVCk6IFByb21pc2U8VD4ge1xuICBsZXQgdGVzdEVsZW1lbnRNYXRjaGVkID0gZmFsc2U7XG4gIGxldCBtYXRjaGVkSGFybmVzc1R5cGVzID0gbmV3IFNldCgpO1xuICBjb25zdCBkZWR1cGVkTWF0Y2hlcyA9IFtdO1xuICBmb3IgKGNvbnN0IHJlc3VsdCBvZiByZXN1bHRzKSB7XG4gICAgaWYgKCFyZXN1bHQpIHtcbiAgICAgIGNvbnRpbnVlO1xuICAgIH1cbiAgICBpZiAocmVzdWx0IGluc3RhbmNlb2YgQ29tcG9uZW50SGFybmVzcykge1xuICAgICAgaWYgKCFtYXRjaGVkSGFybmVzc1R5cGVzLmhhcyhyZXN1bHQuY29uc3RydWN0b3IpKSB7XG4gICAgICAgIG1hdGNoZWRIYXJuZXNzVHlwZXMuYWRkKHJlc3VsdC5jb25zdHJ1Y3Rvcik7XG4gICAgICAgIGRlZHVwZWRNYXRjaGVzLnB1c2gocmVzdWx0KTtcbiAgICAgIH1cbiAgICB9IGVsc2UgaWYgKCF0ZXN0RWxlbWVudE1hdGNoZWQpIHtcbiAgICAgIHRlc3RFbGVtZW50TWF0Y2hlZCA9IHRydWU7XG4gICAgICBkZWR1cGVkTWF0Y2hlcy5wdXNoKHJlc3VsdCk7XG4gICAgfVxuICB9XG4gIHJldHVybiBkZWR1cGVkTWF0Y2hlcyBhcyBUO1xufVxuXG4vKiogVmVyaWZpZXMgdGhhdCB0aGVyZSBpcyBhdCBsZWFzdCBvbmUgcmVzdWx0IGluIGFuIGFycmF5LiAqL1xuYXN5bmMgZnVuY3Rpb24gX2Fzc2VydFJlc3VsdEZvdW5kPFQ+KHJlc3VsdHM6IFByb21pc2U8VFtdPiwgcXVlcnlEZXNjcmlwdGlvbnM6IHN0cmluZ1tdKTpcbiAgICBQcm9taXNlPFQ+IHtcbiAgY29uc3QgcmVzdWx0ID0gKGF3YWl0IHJlc3VsdHMpWzBdO1xuICBpZiAocmVzdWx0ID09IHVuZGVmaW5lZCkge1xuICAgIHRocm93IEVycm9yKGBGYWlsZWQgdG8gZmluZCBlbGVtZW50IG1hdGNoaW5nIG9uZSBvZiB0aGUgZm9sbG93aW5nIHF1ZXJpZXM6XFxuYCArXG4gICAgICAgIHF1ZXJ5RGVzY3JpcHRpb25zLm1hcChkZXNjID0+IGAoJHtkZXNjfSlgKS5qb2luKCcsXFxuJykpO1xuICB9XG4gIHJldHVybiByZXN1bHQ7XG59XG5cbi8qKiBHZXRzIGEgbGlzdCBvZiBkZXNjcmlwdGlvbiBzdHJpbmdzIGZyb20gYSBsaXN0IG9mIHF1ZXJpZXMuICovXG5mdW5jdGlvbiBfZ2V0RGVzY3JpcHRpb25Gb3JMb2NhdG9yRm9yUXVlcmllcyhxdWVyaWVzOiAoc3RyaW5nIHwgSGFybmVzc1F1ZXJ5PGFueT4pW10pIHtcbiAgcmV0dXJuIHF1ZXJpZXMubWFwKHF1ZXJ5ID0+IHR5cGVvZiBxdWVyeSA9PT0gJ3N0cmluZycgP1xuICAgICAgX2dldERlc2NyaXB0aW9uRm9yVGVzdEVsZW1lbnRRdWVyeShxdWVyeSkgOiBfZ2V0RGVzY3JpcHRpb25Gb3JDb21wb25lbnRIYXJuZXNzUXVlcnkocXVlcnkpKTtcbn1cblxuLyoqIEdldHMgYSBkZXNjcmlwdGlvbiBzdHJpbmcgZm9yIGEgYENvbXBvbmVudEhhcm5lc3NgIHF1ZXJ5LiAqL1xuZnVuY3Rpb24gX2dldERlc2NyaXB0aW9uRm9yQ29tcG9uZW50SGFybmVzc1F1ZXJ5KHF1ZXJ5OiBIYXJuZXNzUXVlcnk8YW55Pikge1xuICBjb25zdCBoYXJuZXNzUHJlZGljYXRlID1cbiAgICAgIHF1ZXJ5IGluc3RhbmNlb2YgSGFybmVzc1ByZWRpY2F0ZSA/IHF1ZXJ5IDogbmV3IEhhcm5lc3NQcmVkaWNhdGUocXVlcnksIHt9KTtcbiAgY29uc3Qge25hbWUsIGhvc3RTZWxlY3Rvcn0gPSBoYXJuZXNzUHJlZGljYXRlLmhhcm5lc3NUeXBlO1xuICBjb25zdCBkZXNjcmlwdGlvbiA9IGAke25hbWV9IHdpdGggaG9zdCBlbGVtZW50IG1hdGNoaW5nIHNlbGVjdG9yOiBcIiR7aG9zdFNlbGVjdG9yfVwiYDtcbiAgY29uc3QgY29uc3RyYWludHMgPSBoYXJuZXNzUHJlZGljYXRlLmdldERlc2NyaXB0aW9uKCk7XG4gIHJldHVybiBkZXNjcmlwdGlvbiArIChjb25zdHJhaW50cyA/XG4gICAgICBgIHNhdGlzZnlpbmcgdGhlIGNvbnN0cmFpbnRzOiAke2hhcm5lc3NQcmVkaWNhdGUuZ2V0RGVzY3JpcHRpb24oKX1gIDogJycpO1xufVxuXG4vKiogR2V0cyBhIGRlc2NyaXB0aW9uIHN0cmluZyBmb3IgYSBgVGVzdEVsZW1lbnRgIHF1ZXJ5LiAqL1xuZnVuY3Rpb24gX2dldERlc2NyaXB0aW9uRm9yVGVzdEVsZW1lbnRRdWVyeShzZWxlY3Rvcjogc3RyaW5nKSB7XG4gIHJldHVybiBgVGVzdEVsZW1lbnQgZm9yIGVsZW1lbnQgbWF0Y2hpbmcgc2VsZWN0b3I6IFwiJHtzZWxlY3Rvcn1cImA7XG59XG5cbi8qKiBHZXRzIGEgZGVzY3JpcHRpb24gc3RyaW5nIGZvciBhIGBIYXJuZXNzTG9hZGVyYCBxdWVyeS4gKi9cbmZ1bmN0aW9uIF9nZXREZXNjcmlwdGlvbkZvckhhcm5lc3NMb2FkZXJRdWVyeShzZWxlY3Rvcjogc3RyaW5nKSB7XG4gIHJldHVybiBgSGFybmVzc0xvYWRlciBmb3IgZWxlbWVudCBtYXRjaGluZyBzZWxlY3RvcjogXCIke3NlbGVjdG9yfVwiYDtcbn1cbiJdfQ==