UNPKG

projen

Version:

CDK for software projects

368 lines • 50.3 kB
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.NodeNativeTestRunner = exports.Destination = exports.NodeTestUpdateSnapshot = void 0;
const JSII_RTTI_SYMBOL_1 = Symbol.for("jsii.rtti");
const path_1 = require("path");
const component_1 = require("../component");
const node_config_file_1 = require("./node-config-file");
const node_project_1 = require("./node-project");
const util_1 = require("../util");
const constructs_1 = require("../util/constructs");
const DEFAULT_COVERAGE_DIR = "coverage";
const DEFAULT_TEST_REPORTS_DIR = "test-reports";
const DEFAULT_NODE_TEST_MATCH_EXTENSIONS = "cjs,mjs,js,jsx,cts,mts,ts,tsx";
/**
 * Default glob patterns used to discover test files for Node's built-in test
 * runner, combining Node.js' own default test file discovery with common
 * Jest conventions, so that projects moving from Jest keep matching their
 * existing test files.
 *
 * Node's test runner glob matching follows `glob(7)` and does not support
 * extglobs (e.g. `@(...)`, `+(...)`, `?(...)`), so, unlike Jest's own
 * defaults, each alternative is spelled out explicitly instead of relying on
 * extended glob groups.
 *
 * @see https://nodejs.org/api/test.html#running-tests-from-the-command-line
 */
const DEFAULT_NODE_TEST_MATCH = [
    // Node.js' own defaults: files inside a "test" directory, or named/prefixed/suffixed "test"
    `**/test/**/*.{${DEFAULT_NODE_TEST_MATCH_EXTENSIONS}}`,
    `**/test.{${DEFAULT_NODE_TEST_MATCH_EXTENSIONS}}`,
    `**/test-*.{${DEFAULT_NODE_TEST_MATCH_EXTENSIONS}}`,
    `**/*.test.{${DEFAULT_NODE_TEST_MATCH_EXTENSIONS}}`,
    `**/*-test.{${DEFAULT_NODE_TEST_MATCH_EXTENSIONS}}`,
    `**/*_test.{${DEFAULT_NODE_TEST_MATCH_EXTENSIONS}}`,
    // Jest conventions: files inside a "__tests__" directory, or named/suffixed "spec"
    `**/__tests__/**/*.{${DEFAULT_NODE_TEST_MATCH_EXTENSIONS}}`,
    `**/spec.{${DEFAULT_NODE_TEST_MATCH_EXTENSIONS}}`,
    `**/*.spec.{${DEFAULT_NODE_TEST_MATCH_EXTENSIONS}}`,
];
/**
 * Whether to update snapshots in task "test" (which is executed in task
 * "build" and build workflows), or create a separate task "test:update" for
 * updating snapshots.
 */
var NodeTestUpdateSnapshot;
(function (NodeTestUpdateSnapshot) {
    /**
     * Always update snapshots in "test" task.
     */
    NodeTestUpdateSnapshot["ALWAYS"] = "always";
    /**
     * Never update snapshots in "test" task and create a separate "test:update" task.
     */
    NodeTestUpdateSnapshot["NEVER"] = "never";
})(NodeTestUpdateSnapshot || (exports.NodeTestUpdateSnapshot = NodeTestUpdateSnapshot = {}));
/**
 * Glob patterns matching the files that contain tests.
 */
class TestMatch {
    _patterns = new Set();
    _defaultValue;
    constructor(defaultValue) {
        this._defaultValue = defaultValue;
    }
    add(pattern) {
        this._patterns.add(pattern);
    }
    remove(pattern) {
        this._patterns.delete(pattern);
    }
    /**
     * Resolved lazily so patterns added or removed after construction are
     * still reflected.
     */
    deferred() {
        return this._patterns.size > 0
            ? Array.from(this._patterns)
            : this._defaultValue;
    }
}
/**
 * Where a reporter's output is written.
 *
 * @see https://nodejs.org/api/test.html#test-reporters
 */
class Destination {
    value;
    static [JSII_RTTI_SYMBOL_1] = { fqn: "projen.javascript.Destination", version: "0.104.8" };
    /**
     * Write to standard output.
     */
    static STDOUT = new Destination("stdout");
    /**
     * Write to standard error.
     */
    static STDERR = new Destination("stderr");
    /**
     * Write to a file at the given path.
     * @param path path of the file to write to
     */
    static file(path) {
        return new Destination(path);
    }
    constructor(
    /**
     * The underlying value: `"stdout"`, `"stderr"`, or a file path.
     */
    value) {
        this.value = value;
    }
}
exports.Destination = Destination;
/**
 * The set of reporters configured for a `NodeNativeTestRunner`.
 *
 * Provides the `test.testReporter` and `test.testReporterDestination` values
 * of the generated Node.js configuration file.
 */
class NodeReporters {
    project;
    _reporters = new Map();
    constructor(project) {
        this.project = project;
    }
    /**
     * Adds a reporter, or updates its destination if one with the same name
     * is already configured.
     * @param name The name/kind of the reporter, e.g. `spec`, `junit`, `lcov`.
     * @param destination Where the reporter's output is written.
     * @default Destination.STDOUT
     */
    add(name, destination = Destination.STDOUT) {
        this._reporters.set(name, destination);
        if (destination !== Destination.STDOUT &&
            destination !== Destination.STDERR) {
            this.ensureDestinationDirectory(destination);
        }
    }
    /**
     * Removes a reporter, if configured.
     * @param name The name/kind of the reporter to remove.
     */
    remove(name) {
        this._reporters.delete(name);
    }
    /**
     * Lists the configured reporters, in the order they were added.
     */
    list() {
        return Array.from(this._reporters, ([name, destination]) => ({
            name,
            destination,
        }));
    }
    /**
     * Ensures the directory of a reporter's file destination exists before the
     * test runner is invoked, and excludes it from the project's `.gitignore`
     * and package (e.g. `.npmignore`) files.
     */
    ensureDestinationDirectory(destination) {
        const dir = path_1.posix.dirname(destination.value);
        if (dir === ".") {
            return;
        }
        // the reporter does not create its output directory, so it must exist
        // before the test runner is invoked.
        this.project.testTask.prependExec(`mkdir -p ${dir}`);
        const dirPattern = path_1.posix.join("/", dir, "/");
        this.project.addGitIgnore(dirPattern);
        this.project.addPackageIgnore(dirPattern);
    }
}
/**
 * Configures Node's built-in test runner (`node --test`).
 *
 * Owns the generated Node.js configuration file, the "test", "test:update"
 * and "test:watch" tasks, and the reporters and test match patterns they use.
 */
class NodeNativeTestRunner extends component_1.Component {
    static [JSII_RTTI_SYMBOL_1] = { fqn: "projen.javascript.NodeNativeTestRunner", version: "0.104.8" };
    /**
     * Returns the singleton NodeNativeTestRunner component of a project or undefined
     * if there is none.
     */
    static of(project) {
        const isNodeNativeTestRunner = (c) => c instanceof NodeNativeTestRunner;
        return project.components.find(isNodeNativeTestRunner);
    }
    project;
    coverageDirectory;
    updateSnapshot;
    reporters;
    /**
     * The generated Node.js configuration file.
     */
    configFile;
    testReportsDir = DEFAULT_TEST_REPORTS_DIR;
    testMatch;
    constructor(scope, options = {}) {
        super(scope);
        this.project = (0, constructs_1.closestProjectMustBe)(scope, node_project_1.NodeProject, new.target.name);
        // Node's snapshot files are generated, not authored.
        this.project.root.annotateGenerated("*.snap");
        this.project.root.annotateGenerated("*.snapshot");
        this.updateSnapshot =
            options.updateSnapshot ?? NodeTestUpdateSnapshot.ALWAYS;
        this.reporters = new NodeReporters(this.project);
        const preserveDefaultReporters = options.preserveDefaultReporters ?? true;
        this.testMatch = new TestMatch(DEFAULT_NODE_TEST_MATCH);
        if (options.testMatch && options.testMatch.length > 0) {
            options.testMatch.forEach((pattern) => this.testMatch.add(pattern));
        }
        const collectCoverage = options.collectCoverage ?? true;
        const coverageDirectory = options.coverageDirectory ?? DEFAULT_COVERAGE_DIR;
        this.coverageDirectory = collectCoverage ? coverageDirectory : undefined;
        if (this.coverageDirectory) {
            const coverageDirectoryPath = path_1.posix.join("/", this.coverageDirectory, "/");
            this.project.addGitIgnore(coverageDirectoryPath);
            this.project.addPackageIgnore(coverageDirectoryPath);
        }
        if (preserveDefaultReporters && collectCoverage) {
            this.reporters.add("spec", Destination.STDOUT);
            this.reporters.add("lcov", Destination.file(`${coverageDirectory}/lcov.info`));
            this.reporters.add("junit", Destination.file(`${this.testReportsDir}/junit.xml`));
        }
        for (const { name, destination } of options.reporters ?? []) {
            this.reporters.add(name, destination);
        }
        const defaultTest = {
            test: true,
            testGlobalSetup: options.globalSetup,
            ...(collectCoverage
                ? {
                    experimentalTestCoverage: true,
                    testCoverageExclude: options.coveragePathIgnorePatterns ?? [
                        "**/test/**",
                        "**/__tests__/**",
                    ],
                }
                : {}),
            experimentalTestModuleMocks: options.moduleMocks,
            // lazily resolved at synthesis time, so that reporters added or
            // removed via `reporters` after construction are reflected in the
            // generated file.
            testReporter: (() => this.reporters.list().map((r) => r.name)),
            testReporterDestination: (() => this.reporters.list().map((r) => r.destination.value)),
        };
        const test = (0, util_1.deepMerge)([defaultTest, options.testConfig ?? {}], { destructive: true });
        let nodeOptions = { ...options.nodeOptions };
        const extraCliOptions = options.extraCliOptions ?? [];
        if (options.transformTypes ?? false) {
            this.project.addDevDeps("amaro");
            const imports = nodeOptions.import ?? [];
            if (!imports.includes("amaro/transform")) {
                imports.unshift("amaro/transform");
            }
            nodeOptions = {
                ...nodeOptions,
                enableSourceMaps: true,
                import: imports,
            };
        }
        this.configFile = new node_config_file_1.NodeConfigFile(this.project, {
            filePath: options.configFilePath,
            test,
            nodeOptions,
        });
        this.project.npmignore?.addPatterns(`/${this.configFile.file.path}`);
        // Test match patterns are positional arguments and must come after every
        // flag: once Node sees the first positional argument it stops parsing
        // flags, so any flag appended after them (e.g. `--watch`) is ignored.
        const command = [
            "node",
            `--experimental-config-file=${this.configFile.file.path}`,
            ...extraCliOptions,
        ];
        // `testMatch` is a live collection (`add`/`remove` can still be called
        // after construction), so its resolution is deferred with `lazyExecArgs`
        // rather than read eagerly here.
        const testMatchArgs = () => this.testMatch.deferred();
        if (this.updateSnapshot === NodeTestUpdateSnapshot.ALWAYS) {
            this.project.testTask.execArgs(lazyExecArgs(() => [
                ...command,
                "--test-update-snapshots",
                ...testMatchArgs(),
            ]), { receiveArgs: true });
        }
        else {
            const testUpdate = this.project.tasks.tryFind("test:update");
            if (!testUpdate) {
                this.project.addTask("test:update", {
                    description: "Update test snapshots",
                    execArgs: lazyExecArgs(() => [
                        ...command,
                        "--test-update-snapshots",
                        ...testMatchArgs(),
                    ]),
                    receiveArgs: true,
                });
            }
            this.project.testTask.execArgs(lazyExecArgs(() => [...command, ...testMatchArgs()]), { receiveArgs: true });
        }
        const testWatch = this.project.tasks.tryFind("test:watch");
        if (!testWatch) {
            this.project.addTask("test:watch", {
                description: "Run tests in watch mode",
                execArgs: lazyExecArgs(() => [
                    ...command,
                    "--watch",
                    ...testMatchArgs(),
                ]),
            });
        }
    }
    /**
     * Adds a test match pattern.
     * @param pattern glob pattern to match for tests
     */
    addTestMatch(pattern) {
        this.testMatch.add(pattern);
    }
    /**
     * Removes a test match pattern, if configured.
     * @param pattern glob pattern to remove
     */
    removeTestMatch(pattern) {
        this.testMatch.remove(pattern);
    }
    /**
     * Adds a reporter, or updates its destination if one with the same name
     * is already configured.
     * @param name The name/kind of the reporter, e.g. `spec`, `junit`, `lcov`.
     * @param destination Where the reporter's output is written.
     * @default Destination.STDOUT
     */
    addReporter(name, destination) {
        this.reporters.add(name, destination);
    }
    /**
     * Removes a reporter, if configured.
     * @param name The name/kind of the reporter to remove.
     */
    removeReporter(name) {
        this.reporters.remove(name);
    }
    /**
     * Lists the configured reporters, in the order they were added.
     */
    listReporters() {
        return this.reporters.list();
    }
}
exports.NodeNativeTestRunner = NodeNativeTestRunner;
/**
 * Defers resolution of a `TaskStep`'s `execArgs` to synthesis time, when the
 * tasks manifest is rendered.
 *
 * `TaskStep.execArgs` is typed as a plain `string[]`, but the manifest is
 * written through the generic `resolve()` walker (see
 * `ProjenTaskRunner.tasksManifest`) - the same mechanism `JsonFile`/`ObjectFile`
 * use for lazy values elsewhere (e.g. `NodeReporters.list`, `TestMatch`).
 * That walker calls any function it finds, so a callback deferred here runs
 * once, at that point, in place of a plain array.
 */
function lazyExecArgs(fn) {
    return fn;
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibm9kZS1uYXRpdmUtdGVzdC1ydW5uZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvamF2YXNjcmlwdC9ub2RlLW5hdGl2ZS10ZXN0LXJ1bm5lci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7O0FBQUEsK0JBQTZCO0FBRTdCLDRDQUF5QztBQU16Qyx5REFBb0Q7QUFDcEQsaURBQTZDO0FBQzdDLGtDQUFvQztBQUNwQyxtREFBMEQ7QUFFMUQsTUFBTSxvQkFBb0IsR0FBRyxVQUFVLENBQUM7QUFFeEMsTUFBTSx3QkFBd0IsR0FBRyxjQUFjLENBQUM7QUFFaEQsTUFBTSxrQ0FBa0MsR0FBRywrQkFBK0IsQ0FBQztBQUUzRTs7Ozs7Ozs7Ozs7O0dBWUc7QUFDSCxNQUFNLHVCQUF1QixHQUFhO0lBQ3hDLDRGQUE0RjtJQUM1RixpQkFBaUIsa0NBQWtDLEdBQUc7SUFDdEQsWUFBWSxrQ0FBa0MsR0FBRztJQUNqRCxjQUFjLGtDQUFrQyxHQUFHO0lBQ25ELGNBQWMsa0NBQWtDLEdBQUc7SUFDbkQsY0FBYyxrQ0FBa0MsR0FBRztJQUNuRCxjQUFjLGtDQUFrQyxHQUFHO0lBQ25ELG1GQUFtRjtJQUNuRixzQkFBc0Isa0NBQWtDLEdBQUc7SUFDM0QsWUFBWSxrQ0FBa0MsR0FBRztJQUNqRCxjQUFjLGtDQUFrQyxHQUFHO0NBQ3BELENBQUM7QUFFRjs7OztHQUlHO0FBQ0gsSUFBWSxzQkFVWDtBQVZELFdBQVksc0JBQXNCO0lBQ2hDOztPQUVHO0lBQ0gsMkNBQWlCLENBQUE7SUFFakI7O09BRUc7SUFDSCx5Q0FBZSxDQUFBO0FBQ2pCLENBQUMsRUFWVyxzQkFBc0Isc0NBQXRCLHNCQUFzQixRQVVqQztBQUVEOztHQUVHO0FBQ0gsTUFBTSxTQUFTO0lBQ0ksU0FBUyxHQUFHLElBQUksR0FBRyxFQUFVLENBQUM7SUFDOUIsYUFBYSxDQUFXO0lBRXpDLFlBQVksWUFBc0I7UUFDaEMsSUFBSSxDQUFDLGFBQWEsR0FBRyxZQUFZLENBQUM7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IHsgSUNvbnN0cnVjdCB9IGZyb20gXCJjb25zdHJ1Y3RzXCI7XG5pbXBvcnQgeyBDb21wb25lbnQgfSBmcm9tIFwiLi4vY29tcG9uZW50XCI7XG5pbXBvcnQgdHlwZSB7IFByb2plY3QgfSBmcm9tIFwiLi4vcHJvamVjdFwiO1xuaW1wb3J0IHR5cGUge1xuICBOb2RlQ29uZmlnU2NoZW1hTm9kZU9wdGlvbnMsXG4gIE5vZGVDb25maWdTY2hlbWFUZXN0LFxufSBmcm9tIFwiLi9ub2RlLWNvbmZpZ1wiO1xuaW1wb3J0IHsgTm9kZUNvbmZpZ0ZpbGUgfSBmcm9tIFwiLi9ub2RlLWNvbmZpZy1maWxlXCI7XG5pbXBvcnQgeyBOb2RlUHJvamVjdCB9IGZyb20gXCIuL25vZGUtcHJvamVjdFwiO1xuaW1wb3J0IHsgZGVlcE1lcmdlIH0gZnJvbSBcIi4uL3V0aWxcIjtcbmltcG9ydCB7IGNsb3Nlc3RQcm9qZWN0TXVzdEJlIH0gZnJvbSBcIi4uL3V0aWwvY29uc3RydWN0c1wiO1xuXG5jb25zdCBERUZBVUxUX0NPVkVSQUdFX0RJUiA9IFwiY292ZXJhZ2VcIjtcblxuY29uc3QgREVGQVVMVF9URVNUX1JFUE9SVFNfRElSID0gXCJ0ZXN0LXJlcG9ydHNcIjtcblxuY29uc3QgREVGQVVMVF9OT0RFX1RFU1RfTUFUQ0hfRVhURU5TSU9OUyA9IFwiY2pzLG1qcyxqcyxqc3gsY3RzLG10cyx0cyx0c3hcIjtcblxuLyoqXG4gKiBEZWZhdWx0IGdsb2IgcGF0dGVybnMgdXNlZCB0byBkaXNjb3ZlciB0ZXN0IGZpbGVzIGZvciBOb2RlJ3MgYnVpbHQtaW4gdGVzdFxuICogcnVubmVyLCBjb21iaW5pbmcgTm9kZS5qcycgb3duIGRlZmF1bHQgdGVzdCBmaWxlIGRpc2NvdmVyeSB3aXRoIGNvbW1vblxuICogSmVzdCBjb252ZW50aW9ucywgc28gdGhhdCBwcm9qZWN0cyBtb3ZpbmcgZnJvbSBKZXN0IGtlZXAgbWF0Y2hpbmcgdGhlaXJcbiAqIGV4aXN0aW5nIHRlc3QgZmlsZXMuXG4gKlxuICogTm9kZSdzIHRlc3QgcnVubmVyIGdsb2IgbWF0Y2hpbmcgZm9sbG93cyBgZ2xvYig3KWAgYW5kIGRvZXMgbm90IHN1cHBvcnRcbiAqIGV4dGdsb2JzIChlLmcuIGBAKC4uLilgLCBgKyguLi4pYCwgYD8oLi4uKWApLCBzbywgdW5saWtlIEplc3QncyBvd25cbiAqIGRlZmF1bHRzLCBlYWNoIGFsdGVybmF0aXZlIGlzIHNwZWxsZWQgb3V0IGV4cGxpY2l0bHkgaW5zdGVhZCBvZiByZWx5aW5nIG9uXG4gKiBleHRlbmRlZCBnbG9iIGdyb3Vwcy5cbiAqXG4gKiBAc2VlIGh0dHBzOi8vbm9kZWpzLm9yZy9hcGkvdGVzdC5odG1sI3J1bm5pbmctdGVzdHMtZnJvbS10aGUtY29tbWFuZC1saW5lXG4gKi9cbmNvbnN0IERFRkFVTFRfTk9ERV9URVNUX01BVENIOiBzdHJpbmdbXSA9IFtcbiAgLy8gTm9kZS5qcycgb3duIGRlZmF1bHRzOiBmaWxlcyBpbnNpZGUgYSBcInRlc3RcIiBkaXJlY3RvcnksIG9yIG5hbWVkL3ByZWZpeGVkL3N1ZmZpeGVkIFwidGVzdFwiXG4gIGAqKi90ZXN0LyoqLyoueyR7REVGQVVMVF9OT0RFX1RFU1RfTUFUQ0hfRVhURU5TSU9OU319YCxcbiAgYCoqL3Rlc3QueyR7REVGQVVMVF9OT0RFX1RFU1RfTUFUQ0hfRVhURU5TSU9OU319YCxcbiAgYCoqL3Rlc3QtKi57JHtERUZBVUxUX05PREVfVEVTVF9NQVRDSF9FWFRFTlNJT05TfX1gLFxuICBgKiovKi50ZXN0Lnske0RFRkFVTFRfTk9ERV9URVNUX01BVENIX0VYVEVOU0lPTlN9fWAsXG4gIGAqKi8qLXRlc3QueyR7REVGQVVMVF9OT0RFX1RFU1RfTUFUQ0hfRVhURU5TSU9OU319YCxcbiAgYCoqLypfdGVzdC57JHtERUZBVUxUX05PREVfVEVTVF9NQVRDSF9FWFRFTlNJT05TfX1gLFxuICAvLyBKZXN0IGNvbnZlbnRpb25zOiBmaWxlcyBpbnNpZGUgYSBcIl9fdGVzdHNfX1wiIGRpcmVjdG9yeSwgb3IgbmFtZWQvc3VmZml4ZWQgXCJzcGVjXCJcbiAgYCoqL19fdGVzdHNfXy8qKi8qLnske0RFRkFVTFRfTk9ERV9URVNUX01BVENIX0VYVEVOU0lPTlN9fWAsXG4gIGAqKi9zcGVjLnske0RFRkFVTFRfTk9ERV9URVNUX01BVENIX0VYVEVOU0lPTlN9fWAsXG4gIGAqKi8qLnNwZWMueyR7REVGQVVMVF9OT0RFX1RFU1RfTUFUQ0hfRVhURU5TSU9OU319YCxcbl07XG5cbi8qKlxuICogV2hldGhlciB0byB1cGRhdGUgc25hcHNob3RzIGluIHRhc2sgXCJ0ZXN0XCIgKHdoaWNoIGlzIGV4ZWN1dGVkIGluIHRhc2tcbiAqIFwiYnVpbGRcIiBhbmQgYnVpbGQgd29ya2Zsb3dzKSwgb3IgY3JlYXRlIGEgc2VwYXJhdGUgdGFzayBcInRlc3Q6dXBkYXRlXCIgZm9yXG4gKiB1cGRhdGluZyBzbmFwc2hvdHMuXG4gKi9cbmV4cG9ydCBlbnVtIE5vZGVUZXN0VXBkYXRlU25hcHNob3Qge1xuICAvKipcbiAgICogQWx3YXlzIHVwZGF0ZSBzbmFwc2hvdHMgaW4gXCJ0ZXN0XCIgdGFzay5cbiAgICovXG4gIEFMV0FZUyA9IFwiYWx3YXlzXCIsXG5cbiAgLyoqXG4gICAqIE5ldmVyIHVwZGF0ZSBzbmFwc2hvdHMgaW4gXCJ0ZXN0XCIgdGFzayBhbmQgY3JlYXRlIGEgc2VwYXJhdGUgXCJ0ZXN0OnVwZGF0ZVwiIHRhc2suXG4gICAqL1xuICBORVZFUiA9IFwibmV2ZXJcIixcbn1cblxuLyoqXG4gKiBHbG9iIHBhdHRlcm5zIG1hdGNoaW5nIHRoZSBmaWxlcyB0aGF0IGNvbnRhaW4gdGVzdHMuXG4gKi9cbmNsYXNzIFRlc3RNYXRjaCB7XG4gIHByaXZhdGUgcmVhZG9ubHkgX3BhdHRlcm5zID0gbmV3IFNldDxzdHJpbmc+KCk7XG4gIHByaXZhdGUgcmVhZG9ubHkgX2RlZmF1bHRWYWx1ZTogc3RyaW5nW107XG5cbiAgY29uc3RydWN0b3IoZGVmYXVsdFZhbHVlOiBzdHJpbmdbXSkge1xuICAgIHRoaXMuX2RlZmF1bHRWYWx1ZSA9IGRlZmF1bHRWYWx1ZTtcbiAgfVxuXG4gIHB1YmxpYyBhZGQocGF0dGVybjogc3RyaW5nKSB7XG4gICAgdGhpcy5fcGF0dGVybnMuYWRkKHBhdHRlcm4pO1xuICB9XG5cbiAgcHVibGljIHJlbW92ZShwYXR0ZXJuOiBzdHJpbmcpIHtcbiAgICB0aGlzLl9wYXR0ZXJucy5kZWxldGUocGF0dGVybik7XG4gIH1cblxuICAvKipcbiAgICogUmVzb2x2ZWQgbGF6aWx5IHNvIHBhdHRlcm5zIGFkZGVkIG9yIHJlbW92ZWQgYWZ0ZXIgY29uc3RydWN0aW9uIGFyZVxuICAgKiBzdGlsbCByZWZsZWN0ZWQuXG4gICAqL1xuICBwdWJsaWMgZGVmZXJyZWQoKTogc3RyaW5nW10ge1xuICAgIHJldHVybiB0aGlzLl9wYXR0ZXJucy5zaXplID4gMFxuICAgICAgPyBBcnJheS5mcm9tKHRoaXMuX3BhdHRlcm5zKVxuICAgICAgOiB0aGlzLl9kZWZhdWx0VmFsdWU7XG4gIH1cbn1cblxuZXhwb3J0IHR5cGUgUmVwb3J0ZXJLaW5kID0gXCJkb3RcIiB8IFwianVuaXRcIiB8IFwibGNvdlwiIHwgXCJzcGVjXCIgfCBcInRhcFwiO1xuXG4vKipcbiAqIFdoZXJlIGEgcmVwb3J0ZXIncyBvdXRwdXQgaXMgd3JpdHRlbi5cbiAqXG4gKiBAc2VlIGh0dHBzOi8vbm9kZWpzLm9yZy9hcGkvdGVzdC5odG1sI3Rlc3QtcmVwb3J0ZXJzXG4gKi9cbmV4cG9ydCBjbGFzcyBEZXN0aW5hdGlvbiB7XG4gIC8qKlxuICAgKiBXcml0ZSB0byBzdGFuZGFyZCBvdXRwdXQuXG4gICAqL1xuICBwdWJsaWMgc3RhdGljIHJlYWRvbmx5IFNURE9VVCA9IG5ldyBEZXN0aW5hdGlvbihcInN0ZG91dFwiKTtcblxuICAvKipcbiAgICogV3JpdGUgdG8gc3RhbmRhcmQgZXJyb3IuXG4gICAqL1xuICBwdWJsaWMgc3RhdGljIHJlYWRvbmx5IFNUREVSUiA9IG5ldyBEZXN0aW5hdGlvbihcInN0ZGVyclwiKTtcblxuICAvKipcbiAgICogV3JpdGUgdG8gYSBmaWxlIGF0IHRoZSBnaXZlbiBwYXRoLlxuICAgKiBAcGFyYW0gcGF0aCBwYXRoIG9mIHRoZSBmaWxlIHRvIHdyaXRlIHRvXG4gICAqL1xuICBwdWJsaWMgc3RhdGljIGZpbGUocGF0aDogc3RyaW5nKTogRGVzdGluYXRpb24ge1xuICAgIHJldHVybiBuZXcgRGVzdGluYXRpb24ocGF0aCk7XG4gIH1cblxuICBwcml2YXRlIGNvbnN0cnVjdG9yKFxuICAgIC8qKlxuICAgICAqIFRoZSB1bmRlcmx5aW5nIHZhbHVlOiBgXCJzdGRvdXRcImAsIGBcInN0ZGVyclwiYCwgb3IgYSBmaWxlIHBhdGguXG4gICAgICovXG4gICAgcHVibGljIHJlYWRvbmx5IHZhbHVlOiBzdHJpbmcsXG4gICkge31cbn1cblxuLyoqXG4gKiBBIHNpbmdsZSByZXBvcnRlci9kZXN0aW5hdGlvbiBwYWlyIGZvciB0aGUgTm9kZS5qcyBuYXRpdmUgdGVzdCBydW5uZXIuXG4gKlxuICogQHNlZSBodHRwczovL25vZGVqcy5vcmcvYXBpL3Rlc3QuaHRtbCN0ZXN0LXJlcG9ydGVyc1xuICovXG5leHBvcnQgaW50ZXJmYWNlIE5vZGVSZXBvcnRlciB7XG4gIC8qKlxuICAgKiBUaGUgbmFtZS9raW5kIG9mIHRoZSByZXBvcnRlci5cbiAgICovXG4gIHJlYWRvbmx5IG5hbWU6IFJlcG9ydGVyS2luZDtcbiAgLyoqXG4gICAqIFdoZXJlIHRoZSByZXBvcnRlcidzIG91dHB1dCBpcyB3cml0dGVuLlxuICAgKlxuICAgKiBAc2VlIGh0dHBzOi8vZ2l0aHViLmNvbS9ub2RlanMvbm9kZS9ibG9iLzQyMTVjYzM1ZTI1YzQ0ZjlmNGZlYTVhNDU0MWFmYzg2MmRiN2VmMGEvdGVzdC9wYXJhbGxlbC90ZXN0LXJ1bm5lci1yZXBvcnRlcnMuanMjTDQ2LUw3N1xuICAgKiBAZGVmYXVsdCBEZXN0aW5hdGlvbi5TVERPVVRcbiAgICovXG4gIHJlYWRvbmx5IGRlc3RpbmF0aW9uOiBEZXN0aW5hdGlvbjtcbn1cblxuLyoqXG4gKiBUaGUgc2V0IG9mIHJlcG9ydGVycyBjb25maWd1cmVkIGZvciBhIGBOb2RlTmF0aXZlVGVzdFJ1bm5lcmAuXG4gKlxuICogUHJvdmlkZXMgdGhlIGB0ZXN0LnRlc3RSZXBvcnRlcmAgYW5kIGB0ZXN0LnRlc3RSZXBvcnRlckRlc3RpbmF0aW9uYCB2YWx1ZXNcbiAqIG9mIHRoZSBnZW5lcmF0ZWQgTm9kZS5qcyBjb25maWd1cmF0aW9uIGZpbGUuXG4gKi9cbmNsYXNzIE5vZGVSZXBvcnRlcnMge1xuICBwcml2YXRlIHJlYWRvbmx5IF9yZXBvcnRlcnM6IE1hcDxSZXBvcnRlcktpbmQsIERlc3RpbmF0aW9uPiA9IG5ldyBNYXAoKTtcblxuICBjb25zdHJ1Y3Rvcihwcml2YXRlIHJlYWRvbmx5IHByb2plY3Q6IE5vZGVQcm9qZWN0KSB7fVxuXG4gIC8qKlxuICAgKiBBZGRzIGEgcmVwb3J0ZXIsIG9yIHVwZGF0ZXMgaXRzIGRlc3RpbmF0aW9uIGlmIG9uZSB3aXRoIHRoZSBzYW1lIG5hbWVcbiAgICogaXMgYWxyZWFkeSBjb25maWd1cmVkLlxuICAgKiBAcGFyYW0gbmFtZSBUaGUgbmFtZS9raW5kIG9mIHRoZSByZXBvcnRlciwgZS5nLiBgc3BlY2AsIGBqdW5pdGAsIGBsY292YC5cbiAgICogQHBhcmFtIGRlc3RpbmF0aW9uIFdoZXJlIHRoZSByZXBvcnRlcidzIG91dHB1dCBpcyB3cml0dGVuLlxuICAgKiBAZGVmYXVsdCBEZXN0aW5hdGlvbi5TVERPVVRcbiAgICovXG4gIHB1YmxpYyBhZGQoXG4gICAgbmFtZTogUmVwb3J0ZXJLaW5kLFxuICAgIGRlc3RpbmF0aW9uOiBEZXN0aW5hdGlvbiA9IERlc3RpbmF0aW9uLlNURE9VVCxcbiAgKTogdm9pZCB7XG4gICAgdGhpcy5fcmVwb3J0ZXJzLnNldChuYW1lLCBkZXN0aW5hdGlvbik7XG5cbiAgICBpZiAoXG4gICAgICBkZXN0aW5hdGlvbiAhPT0gRGVzdGluYXRpb24uU1RET1VUICYmXG4gICAgICBkZXN0aW5hdGlvbiAhPT0gRGVzdGluYXRpb24uU1RERVJSXG4gICAgKSB7XG4gICAgICB0aGlzLmVuc3VyZURlc3RpbmF0aW9uRGlyZWN0b3J5KGRlc3RpbmF0aW9uKTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogUmVtb3ZlcyBhIHJlcG9ydGVyLCBpZiBjb25maWd1cmVkLlxuICAgKiBAcGFyYW0gbmFtZSBUaGUgbmFtZS9raW5kIG9mIHRoZSByZXBvcnRlciB0byByZW1vdmUuXG4gICAqL1xuICBwdWJsaWMgcmVtb3ZlKG5hbWU6IFJlcG9ydGVyS2luZCk6IHZvaWQge1xuICAgIHRoaXMuX3JlcG9ydGVycy5kZWxldGUobmFtZSk7XG4gIH1cblxuICAvKipcbiAgICogTGlzdHMgdGhlIGNvbmZpZ3VyZWQgcmVwb3J0ZXJzLCBpbiB0aGUgb3JkZXIgdGhleSB3ZXJlIGFkZGVkLlxuICAgKi9cbiAgcHVibGljIGxpc3QoKTogTm9kZVJlcG9ydGVyW10ge1xuICAgIHJldHVybiBBcnJheS5mcm9tKHRoaXMuX3JlcG9ydGVycywgKFtuYW1lLCBkZXN0aW5hdGlvbl0pID0+ICh7XG4gICAgICBuYW1lLFxuICAgICAgZGVzdGluYXRpb24sXG4gICAgfSkpO1xuICB9XG5cbiAgLyoqXG4gICAqIEVuc3VyZXMgdGhlIGRpcmVjdG9yeSBvZiBhIHJlcG9ydGVyJ3MgZmlsZSBkZXN0aW5hdGlvbiBleGlzdHMgYmVmb3JlIHRoZVxuICAgKiB0ZXN0IHJ1bm5lciBpcyBpbnZva2VkLCBhbmQgZXhjbHVkZXMgaXQgZnJvbSB0aGUgcHJvamVjdCdzIGAuZ2l0aWdub3JlYFxuICAgKiBhbmQgcGFja2FnZSAoZS5nLiBgLm5wbWlnbm9yZWApIGZpbGVzLlxuICAgKi9cbiAgcHJpdmF0ZSBlbnN1cmVEZXN0aW5hdGlvbkRpcmVjdG9yeShkZXN0aW5hdGlvbjogRGVzdGluYXRpb24pOiB2b2lkIHtcbiAgICBjb25zdCBkaXIgPSBwb3NpeC5kaXJuYW1lKGRlc3RpbmF0aW9uLnZhbHVlKTtcbiAgICBpZiAoZGlyID09PSBcIi5cIikge1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICAvLyB0aGUgcmVwb3J0ZXIgZG9lcyBub3QgY3JlYXRlIGl0cyBvdXRwdXQgZGlyZWN0b3J5LCBzbyBpdCBtdXN0IGV4aXN0XG4gICAgLy8gYmVmb3JlIHRoZSB0ZXN0IHJ1bm5lciBpcyBpbnZva2VkLlxuICAgIHRoaXMucHJvamVjdC50ZXN0VGFzay5wcmVwZW5kRXhlYyhgbWtkaXIgLXAgJHtkaXJ9YCk7XG5cbiAgICBjb25zdCBkaXJQYXR0ZXJuID0gcG9zaXguam9pbihcIi9cIiwgZGlyLCBcIi9cIik7XG4gICAgdGhpcy5wcm9qZWN0LmFkZEdpdElnbm9yZShkaXJQYXR0ZXJuKTtcbiAgICB0aGlzLnByb2plY3QuYWRkUGFja2FnZUlnbm9yZShkaXJQYXR0ZXJuKTtcbiAgfVxufVxuXG4vKipcbiAqIE9wdGlvbnMgZm9yIE5vZGUuanMnIGJ1aWx0LWluIHRlc3QgcnVubmVyIChgbm9kZSAtLXRlc3RgKS5cbiAqL1xuZXhwb3J0IGludGVyZmFjZSBOb2RlTmF0aXZlVGVzdFJ1bm5lck9wdGlvbnMge1xuICAvKipcbiAgICogVGhlIGRpcmVjdG9yeSB3aGVyZSBjb3ZlcmFnZSBmaWxlcyBhcmUgb3V0cHV0LCBpZiBjb3ZlcmFnZSBjb2xsZWN0aW9uXG4gICAqIGlzIGVuYWJsZWQuXG4gICAqXG4gICAqIEBkZWZhdWx0IFwiY292ZXJhZ2VcIlxuICAgKi9cbiAgcmVhZG9ubHkgY292ZXJhZ2VEaXJlY3Rvcnk/OiBzdHJpbmc7XG5cbiAgLyoqXG4gICAqIFdoZXRoZXIgdG8gdXBkYXRlIHNuYXBzaG90cyBpbiB0YXNrIFwidGVzdFwiICh3aGljaCBpcyBleGVjdXRlZCBpbiB0YXNrXG4gICAqIFwiYnVpbGRcIiBhbmQgYnVpbGQgd29ya2Zsb3dzKSwgb3IgY3JlYXRlIGEgc2VwYXJhdGUgdGFzayBcInRlc3Q6dXBkYXRlXCJcbiAgICogZm9yIHVwZGF0aW5nIHNuYXBzaG90cy5cbiAgICpcbiAgICogQGRlZmF1bHQgLSBBTFdBWVNcbiAgICovXG4gIHJlYWRvbmx5IHVwZGF0ZVNuYXBzaG90PzogTm9kZVRlc3RVcGRhdGVTbmFwc2hvdDtcblxuICAvKipcbiAgICogQWRkaXRpb25hbCBvcHRpb25zIHRvIHBhc3MgdG8gdGhlIGBub2RlIC0tdGVzdGAgQ0xJIGludm9jYXRpb24uXG4gICAqXG4gICAqIEVhY2ggZWxlbWVudCBpcyBwYXNzZWQgYXMgYSBzaW5nbGUgYXJndW1lbnQsIGV4YWN0bHkgYXMgZ2l2ZW46IG5vIHNoZWxsXG4gICAqIHBhcnNlcyB0aGVzZSwgc28gYSBmbGFnIGFuZCBpdHMgdmFsdWUgbmVlZCBzZXBhcmF0ZSBlbGVtZW50c1xuICAgKiAoYFtcIi0tZm9vXCIsIFwiYmFyXCJdYCwgbm90IGBbXCItLWZvbyBiYXJcIl1gKS5cbiAgICpcbiAgICogQGRlZmF1bHQgLSBubyBleHRyYSBvcHRpb25zXG4gICAqL1xuICByZWFkb25seSBleHRyYUNsaU9wdGlvbnM/OiBzdHJpbmdbXTtcblxuICAvKipcbiAgICogUHJlc2VydmUgdGhlIGRlZmF1bHQgcmVwb3J0ZXJzIChgc3BlY2AsIGBsY292YCwgYGp1bml0YCkgd2hlbiBhZGRpdGlvbmFsXG4gICAqIHJlcG9ydGVycyBhcmUgYWRkZWQuXG4gICAqXG4gICAqIEBkZWZhdWx0IHRydWVcbiAgICovXG4gIHJlYWRvbmx5IHByZXNlcnZlRGVmYXVsdFJlcG9ydGVycz86IGJvb2xlYW47XG5cbiAgLyoqXG4gICAqIEFkZGl0aW9uYWwgcmVwb3J0ZXJzIHRvIGNvbmZpZ3VyZSAoZS5nLiBgeyBuYW1lOiBcInRhcFwiLCBkZXN0aW5hdGlvbjpcbiAgICogRGVzdGluYXRpb24uZmlsZShcInRlc3QtcmVwb3J0cy90YXAudHh0XCIpIH1gKS5cbiAgICpcbiAgICogVGhlc2UgYXJlIGFkZGVkIG9uIHRvcCBvZiB0aGUgZGVmYXVsdCByZXBvcnRlcnMgKGBzcGVjYCwgYGxjb3ZgLCBganVuaXRgKSxcbiAgICogd2hpY2ggYXJlIGNvbnRyb2xsZWQgdmlhIGBjb2xsZWN0Q292ZXJhZ2VgLiBVc2VcbiAgICogYE5vZGVOYXRpdmVUZXN0UnVubmVyLmFkZFJlcG9ydGVyYC9gcmVtb3ZlUmVwb3J0ZXJgL2BsaXN0UmVwb3J0ZXJzYCB0b1xuICAgKiBtYW5hZ2UgcmVwb3J0ZXJzIGFmdGVyIGNvbnN0cnVjdGlvbi5cbiAgICpcbiAgICogQGRlZmF1bHQgLSBubyBhZGRpdGlvbmFsIHJlcG9ydGVyc1xuICAgKi9cbiAgcmVhZG9ubHkgcmVwb3J0ZXJzPzogTm9kZVJlcG9ydGVyW107XG5cbiAgLyoqXG4gICAqIFBhdGggdG8gdGhlIEpTT04gY29uZmlndXJhdGlvbiBmaWxlIGZvciB0aGUgdGVzdCBydW5uZXIuXG4gICAqXG4gICAqIEBkZWZhdWx0IFwibm9kZS5jb25maWcuanNvblwiXG4gICAqL1xuICByZWFkb25seSBjb25maWdGaWxlUGF0aD86IHN0cmluZztcblxuICAvKipcbiAgICogR2xvYiBwYXR0ZXJucyBtYXRjaGluZyB0aGUgZmlsZXMgdGhhdCBjb250YWluIHRlc3RzLiBCeSBkZWZhdWx0IGl0XG4gICAqIGNvbWJpbmVzIE5vZGUuanMnIG93biBkZWZhdWx0IHRlc3QgZmlsZSBkaXNjb3Zlcnkgd2l0aCBKZXN0IGNvbnZlbnRpb25zLlxuICAgKlxuICAgKiBAZGVmYXVsdCAtIGNvbWJpbmVzIE5vZGUuanMnIG93biBkZWZhdWx0IHRlc3QgZmlsZSBkaXNjb3Zlcnkgd2l0aCBKZXN0IGNvbnZlbnRpb25zXG4gICAqL1xuICByZWFkb25seSB0ZXN0TWF0Y2g/OiBzdHJpbmdbXTtcblxuICAvKipcbiAgICogSW5kaWNhdGVzIHdoZXRoZXIgdGhlIGNvdmVyYWdlIGluZm9ybWF0aW9uIHNob3VsZCBiZSBjb2xsZWN0ZWQgd2hpbGVcbiAgICogZXhlY3V0aW5nIHRoZSB0ZXN0LCB2aWEgYC0tZXhwZXJpbWVudGFsLXRlc3QtY292ZXJhZ2VgLlxuICAgKlxuICAgKiBAZGVmYXVsdCB0cnVlXG4gICAqL1xuICByZWFkb25seSBjb2xsZWN0Q292ZXJhZ2U/OiBib29sZWFuO1xuXG4gIC8qKlxuICAgKiBBbiBhcnJheSBvZiBnbG9iIHBhdHRlcm5zIHRoYXQgYXJlIG1hdGNoZWQgYWdhaW5zdCBhbGwgZmlsZSBwYXRocyBiZWZvcmVcbiAgICogZXhlY3V0aW5nIGNvdmVyYWdlIGNvbGxlY3Rpb24uIElmIGEgZmlsZSBwYXRoIG1hdGNoZXMgYW55IG9mIHRoZVxuICAgKiBwYXR0ZXJucywgY292ZXJhZ2UgaW5mb3JtYXRpb24gd2lsbCBiZSBza2lwcGVkIGZvciBpdC5cbiAgICpcbiAgICogQGRlZmF1bHQgW1wiKipcXC90ZXN0LyoqXCIsIFwiKipcXC9fX3Rlc3RzX18vKipcIl1cbiAgICovXG4gIHJlYWRvbmx5IGNvdmVyYWdlUGF0aElnbm9yZVBhdHRlcm5zPzogc3RyaW5nW107XG5cbiAgLyoqXG4gICAqIFRoaXMgb3B0aW9uIGFsbG93cyB0aGUgdXNlIG9mIGEgY3VzdG9tIGdsb2JhbCBzZXR1cCBtb2R1bGUgd2hpY2hcbiAgICogZXhwb3J0cyBhIGZ1bmN0aW9uIHRoYXQgaXMgdHJpZ2dlcmVkIG9uY2UgYmVmb3JlIGFsbCB0ZXN0IHN1aXRlcy5cbiAgICogV3JpdHRlbiBhcyBgdGVzdC1nbG9iYWwtc2V0dXBgIGluIHRoZSBnZW5lcmF0ZWQgTm9kZS5qcyBjb25maWd1cmF0aW9uXG4gICAqIGZpbGUuXG4gICAqXG4gICAqIEBkZWZhdWx0IC0gdW5kZWZpbmVkXG4gICAqL1xuICByZWFkb25seSBnbG9iYWxTZXR1cD86IHN0cmluZztcblxuICAvKipcbiAgICogRW5hYmxlIG1vZHVsZSBtb2NraW5nIHN1cHBvcnQgdmlhIGAtLWV4cGVyaW1lbnRhbC10ZXN0LW1vZHVsZS1tb2Nrc2AuXG4gICAqXG4gICAqIEBkZWZhdWx0IGZhbHNlXG4gICAqL1xuICByZWFkb25seSBtb2R1bGVNb2Nrcz86IGJvb2xlYW47XG5cbiAgLyoqXG4gICAqIFdoZXRoZXIgdG8gZW5hYmxlIHRyYW5zZm9ybWF0aW9uIG9mIFR5cGVTY3JpcHQtb25seSBzeW50YXggKGUuZy4gZW51bXMsIG5hbWVzcGFjZXMpLlxuICAgKlxuICAgKiBVc2VzIGBhbWFyb2AgKHRoZSBUeXBlU2NyaXB0IHRyYW5zZm9ybWVyIHVzZWQgaW50ZXJuYWxseSBieSBOb2RlLmpzKSBhcyBhblxuICAgKiBleHRlcm5hbCBsb2FkZXIgdmlhIGAtLWltcG9ydD1hbWFyby90cmFuc2Zvcm1gLiBBZGRzIGEgZGVwZW5kZW5jeSBvbiB0aGVcbiAgICogYGFtYXJvYCBwYWNrYWdlIGFuZCBlbmFibGVzIGAtLWVuYWJsZS1zb3VyY2UtbWFwc2AgdG8gcHJlc2VydmUgYWNjdXJhdGVcbiAgICogc3RhY2sgdHJhY2VzLlxuICAgKlxuICAgKiBAc2VlIGh0dHBzOi8vZ2l0aHViLmNvbS9ub2RlanMvYW1hcm9cbiAgICogQGRlZmF1bHQgZmFsc2VcbiAgICovXG4gIHJlYWRvbmx5IHRyYW5zZm9ybVR5cGVzPzogYm9vbGVhbjtcblxuICAvKipcbiAgICogQWRkaXRpb25hbCBlbnRyaWVzIGZvciB0aGUgYG5vZGVPcHRpb25zYCBzZWN0aW9uIG9mIHRoZSBnZW5lcmF0ZWRcbiAgICogY29uZmlndXJhdGlvbiBmaWxlIChlLmcuIGBlbmFibGVTb3VyY2VNYXBzYCwgYGRpc2FibGVXYXJuaW5nYCkuXG4gICAqXG4gICAqIEBkZWZhdWx0IC0gbm8gYWRkaXRpb25hbCBub2RlIG9wdGlvbnNcbiAgICovXG4gIHJlYWRvbmx5IG5vZGVPcHRpb25zPzogTm9kZUNvbmZpZ1NjaGVtYU5vZGVPcHRpb25zO1xuXG4gIC8qKlxuICAgKiBBZGRpdGlvbmFsIGVudHJpZXMgZm9yIHRoZSBgdGVzdGAgc2VjdGlvbiBvZiB0aGUgZ2VuZXJhdGVkXG4gICAqIGNvbmZpZ3VyYXRpb24gZmlsZSAoZS5nLiBgdGVzdENvbmN1cnJlbmN5YCwgYHRlc3RUaW1lb3V0YCkuXG4gICAqXG4gICAqIEBkZWZhdWx0IC0gbm8gYWRkaXRpb25hbCBvcHRpb25zXG4gICAqL1xuICByZWFkb25seSB0ZXN0Q29uZmlnPzogTm9kZUNvbmZpZ1NjaGVtYVRlc3Q7XG59XG5cbi8qKlxuICogQ29uZmlndXJlcyBOb2RlJ3MgYnVpbHQtaW4gdGVzdCBydW5uZXIgKGBub2RlIC0tdGVzdGApLlxuICpcbiAqIE93bnMgdGhlIGdlbmVyYXRlZCBOb2RlLmpzIGNvbmZpZ3VyYXRpb24gZmlsZSwgdGhlIFwidGVzdFwiLCBcInRlc3Q6dXBkYXRlXCJcbiAqIGFuZCBcInRlc3Q6d2F0Y2hcIiB0YXNrcywgYW5kIHRoZSByZXBvcnRlcnMgYW5kIHRlc3QgbWF0Y2ggcGF0dGVybnMgdGhleSB1c2UuXG4gKi9cbmV4cG9ydCBjbGFzcyBOb2RlTmF0aXZlVGVzdFJ1bm5lciBleHRlbmRzIENvbXBvbmVudCB7XG4gIC8qKlxuICAgKiBSZXR1cm5zIHRoZSBzaW5nbGV0b24gTm9kZU5hdGl2ZVRlc3RSdW5uZXIgY29tcG9uZW50IG9mIGEgcHJvamVjdCBvciB1bmRlZmluZWRcbiAgICogaWYgdGhlcmUgaXMgbm9uZS5cbiAgICovXG4gIHB1YmxpYyBzdGF0aWMgb2YocHJvamVjdDogUHJvamVjdCk6IE5vZGVOYXRpdmVUZXN0UnVubmVyIHwgdW5kZWZpbmVkIHtcbiAgICBjb25zdCBpc05vZGVOYXRpdmVUZXN0UnVubmVyID0gKGM6IENvbXBvbmVudCk6IGMgaXMgTm9kZU5hdGl2ZVRlc3RSdW5uZXIgPT5cbiAgICAgIGMgaW5zdGFuY2VvZiBOb2RlTmF0aXZlVGVzdFJ1bm5lcjtcbiAgICByZXR1cm4gcHJvamVjdC5jb21wb25lbnRzLmZpbmQoaXNOb2RlTmF0aXZlVGVzdFJ1bm5lcik7XG4gIH1cblxuICBwdWJsaWMgcmVhZG9ubHkgcHJvamVjdDogTm9kZVByb2plY3Q7XG5cbiAgcHJpdmF0ZSByZWFkb25seSBjb3ZlcmFnZURpcmVjdG9yeT86IHN0cmluZztcblxuICBwcml2YXRlIHJlYWRvbmx5IHVwZGF0ZVNuYXBzaG90OiBOb2RlVGVzdFVwZGF0ZVNuYXBzaG90O1xuXG4gIHByaXZhdGUgcmVhZG9ubHkgcmVwb3J0ZXJzOiBOb2RlUmVwb3J0ZXJzO1xuXG4gIC8qKlxuICAgKiBUaGUgZ2VuZXJhdGVkIE5vZGUuanMgY29uZmlndXJhdGlvbiBmaWxlLlxuICAgKi9cbiAgcHVibGljIHJlYWRvbmx5IGNvbmZpZ0ZpbGU6IE5vZGVDb25maWdGaWxlO1xuXG4gIHByaXZhdGUgcmVhZG9ubHkgdGVzdFJlcG9ydHNEaXI6IHN0cmluZyA9IERFRkFVTFRfVEVTVF9SRVBPUlRTX0RJUjtcblxuICBwcml2YXRlIHJlYWRvbmx5IHRlc3RNYXRjaDogVGVzdE1hdGNoO1xuXG4gIGNvbnN0cnVjdG9yKHNjb3BlOiBJQ29uc3RydWN0LCBvcHRpb25zOiBOb2RlTmF0aXZlVGVzdFJ1bm5lck9wdGlvbnMgPSB7fSkge1xuICAgIHN1cGVyKHNjb3BlKTtcbiAgICB0aGlzLnByb2plY3QgPSBjbG9zZXN0UHJvamVjdE11c3RCZShzY29wZSwgTm9kZVByb2plY3QsIG5ldy50YXJnZXQubmFtZSk7XG5cbiAgICAvLyBOb2RlJ3Mgc25hcHNob3QgZmlsZXMgYXJlIGdlbmVyYXRlZCwgbm90IGF1dGhvcmVkLlxuICAgIHRoaXMucHJvamVjdC5yb290LmFubm90YXRlR2VuZXJhdGVkKFwiKi5zbmFwXCIpO1xuICAgIHRoaXMucHJvamVjdC5yb290LmFubm90YXRlR2VuZXJhdGVkKFwiKi5zbmFwc2hvdFwiKTtcblxuICAgIHRoaXMudXBkYXRlU25hcHNob3QgPVxuICAgICAgb3B0aW9ucy51cGRhdGVTbmFwc2hvdCA/PyBOb2RlVGVzdFVwZGF0ZVNuYXBzaG90LkFMV0FZUztcblxuICAgIHRoaXMucmVwb3J0ZXJzID0gbmV3IE5vZGVSZXBvcnRlcnModGhpcy5wcm9qZWN0KTtcbiAgICBjb25zdCBwcmVzZXJ2ZURlZmF1bHRSZXBvcnRlcnMgPSBvcHRpb25zLnByZXNlcnZlRGVmYXVsdFJlcG9ydGVycyA/PyB0cnVlO1xuXG4gICAgdGhpcy50ZXN0TWF0Y2ggPSBuZXcgVGVzdE1hdGNoKERFRkFVTFRfTk9ERV9URVNUX01BVENIKTtcblxuICAgIGlmIChvcHRpb25zLnRlc3RNYXRjaCAmJiBvcHRpb25zLnRlc3RNYXRjaC5sZW5ndGggPiAwKSB7XG4gICAgICBvcHRpb25zLnRlc3RNYXRjaC5mb3JFYWNoKChwYXR0ZXJuKSA9PiB0aGlzLnRlc3RNYXRjaC5hZGQocGF0dGVybikpO1xuICAgIH1cblxuICAgIGNvbnN0IGNvbGxlY3RDb3ZlcmFnZSA9IG9wdGlvbnMuY29sbGVjdENvdmVyYWdlID8/IHRydWU7XG4gICAgY29uc3QgY292ZXJhZ2VEaXJlY3RvcnkgPSBvcHRpb25zLmNvdmVyYWdlRGlyZWN0b3J5ID8/IERFRkFVTFRfQ09WRVJBR0VfRElSO1xuICAgIHRoaXMuY292ZXJhZ2VEaXJlY3RvcnkgPSBjb2xsZWN0Q292ZXJhZ2UgPyBjb3ZlcmFnZURpcmVjdG9yeSA6IHVuZGVmaW5lZDtcblxuICAgIGlmICh0aGlzLmNvdmVyYWdlRGlyZWN0b3J5KSB7XG4gICAgICBjb25zdCBjb3ZlcmFnZURpcmVjdG9yeVBhdGggPSBwb3NpeC5qb2luKFxuICAgICAgICBcIi9cIixcbiAgICAgICAgdGhpcy5jb3ZlcmFnZURpcmVjdG9yeSxcbiAgICAgICAgXCIvXCIsXG4gICAgICApO1xuICAgICAgdGhpcy5wcm9qZWN0LmFkZEdpdElnbm9yZShjb3ZlcmFnZURpcmVjdG9yeVBhdGgpO1xuICAgICAgdGhpcy5wcm9qZWN0LmFkZFBhY2thZ2VJZ25vcmUoY292ZXJhZ2VEaXJlY3RvcnlQYXRoKTtcbiAgICB9XG5cbiAgICBpZiAocHJlc2VydmVEZWZhdWx0UmVwb3J0ZXJzICYmIGNvbGxlY3RDb3ZlcmFnZSkge1xuICAgICAgdGhpcy5yZXBvcnRlcnMuYWRkKFwic3BlY1wiLCBEZXN0aW5hdGlvbi5TVERPVVQpO1xuICAgICAgdGhpcy5yZXBvcnRlcnMuYWRkKFxuICAgICAgICBcImxjb3ZcIixcbiAgICAgICAgRGVzdGluYXRpb24uZmlsZShgJHtjb3ZlcmFnZURpcmVjdG9yeX0vbGNvdi5pbmZvYCksXG4gICAgICApO1xuICAgICAgdGhpcy5yZXBvcnRlcnMuYWRkKFxuICAgICAgICBcImp1bml0XCIsXG4gICAgICAgIERlc3RpbmF0aW9uLmZpbGUoYCR7dGhpcy50ZXN0UmVwb3J0c0Rpcn0vanVuaXQueG1sYCksXG4gICAgICApO1xuICAgIH1cblxuICAgIGZvciAoY29uc3QgeyBuYW1lLCBkZXN0aW5hdGlvbiB9IG9mIG9wdGlvbnMucmVwb3J0ZXJzID8/IFtdKSB7XG4gICAgICB0aGlzLnJlcG9ydGVycy5hZGQobmFtZSwgZGVzdGluYXRpb24pO1xuICAgIH1cblxuICAgIGNvbnN0IGRlZmF1bHRUZXN0OiBOb2RlQ29uZmlnU2NoZW1hVGVzdCA9IHtcbiAgICAgIHRlc3Q6IHRydWUsXG4gICAgICB0ZXN0R2xvYmFsU2V0dXA6IG9wdGlvbnMuZ2xvYmFsU2V0dXAsXG4gICAgICAuLi4oY29sbGVjdENvdmVyYWdlXG4gICAgICAgID8ge1xuICAgICAgICAgICAgZXhwZXJpbWVudGFsVGVzdENvdmVyYWdlOiB0cnVlLFxuICAgICAgICAgICAgdGVzdENvdmVyYWdlRXhjbHVkZTogb3B0aW9ucy5jb3ZlcmFnZVBhdGhJZ25vcmVQYXR0ZXJucyA/PyBbXG4gICAgICAgICAgICAgIFwiKiovdGVzdC8qKlwiLFxuICAgICAgICAgICAgICBcIioqL19fdGVzdHNfXy8qKlwiLFxuICAgICAgICAgICAgXSxcbiAgICAgICAgICB9XG4gICAgICAgIDoge30pLFxuICAgICAgZXhwZXJpbWVudGFsVGVzdE1vZHVsZU1vY2tzOiBvcHRpb25zLm1vZHVsZU1vY2tzLFxuICAgICAgLy8gbGF6aWx5IHJlc29sdmVkIGF0IHN5bnRoZXNpcyB0aW1lLCBzbyB0aGF0IHJlcG9ydGVycyBhZGRlZCBvclxuICAgICAgLy8gcmVtb3ZlZCB2aWEgYHJlcG9ydGVyc2AgYWZ0ZXIgY29uc3RydWN0aW9uIGFyZSByZWZsZWN0ZWQgaW4gdGhlXG4gICAgICAvLyBnZW5lcmF0ZWQgZmlsZS5cbiAgICAgIHRlc3RSZXBvcnRlcjogKCgpID0+IHRoaXMucmVwb3J0ZXJzLmxpc3QoKS5tYXAoKHIpID0+IHIubmFtZSkpIGFzIGFueSxcbiAgICAgIHRlc3RSZXBvcnRlckRlc3RpbmF0aW9uOiAoKCkgPT5cbiAgICAgICAgdGhpcy5yZXBvcnRlcnMubGlzdCgpLm1hcCgocikgPT4gci5kZXN0aW5hdGlvbi52YWx1ZSkpIGFzIGFueSxcbiAgICB9O1xuICAgIGNvbnN0IHRlc3Q6IE5vZGVDb25maWdTY2hlbWFUZXN0ID0gZGVlcE1lcmdlKFxuICAgICAgW2RlZmF1bHRUZXN0LCBvcHRpb25zLnRlc3RDb25maWcgPz8ge31dLFxuICAgICAgeyBkZXN0cnVjdGl2ZTogdHJ1ZSB9LFxuICAgICk7XG5cbiAgICBsZXQgbm9kZU9wdGlvbnM6IE5vZGVDb25maWdTY2hlbWFOb2RlT3B0aW9ucyA9IHsgLi4ub3B0aW9ucy5ub2RlT3B0aW9ucyB9O1xuICAgIGNvbnN0IGV4dHJhQ2xpT3B0aW9ucyA9IG9wdGlvbnMuZXh0cmFDbGlPcHRpb25zID8/IFtdO1xuXG4gICAgaWYgKG9wdGlvbnMudHJhbnNmb3JtVHlwZXMgPz8gZmFsc2UpIHtcbiAgICAgIHRoaXMucHJvamVjdC5hZGREZXZEZXBzKFwiYW1hcm9cIik7XG4gICAgICBjb25zdCBpbXBvcnRzID0gbm9kZU9wdGlvbnMuaW1wb3J0ID8/IFtdO1xuICAgICAgaWYgKCFpbXBvcnRzLmluY2x1ZGVzKFwiYW1hcm8vdHJhbnNmb3JtXCIpKSB7XG4gICAgICAgIGltcG9ydHMudW5zaGlmdChcImFtYXJvL3RyYW5zZm9ybVwiKTtcbiAgICAgIH1cbiAgICAgIG5vZGVPcHRpb25zID0ge1xuICAgICAgICAuLi5ub2RlT3B0aW9ucyxcbiAgICAgICAgZW5hYmxlU291cmNlTWFwczogdHJ1ZSxcbiAgICAgICAgaW1wb3J0OiBpbXBvcnRzLFxuICAgICAgfTtcbiAgICB9XG5cbiAgICB0aGlzLmNvbmZpZ0ZpbGUgPSBuZXcgTm9kZUNvbmZpZ0ZpbGUodGhpcy5wcm9qZWN0LCB7XG4gICAgICBmaWxlUGF0aDogb3B0aW9ucy5jb25maWdGaWxlUGF0aCxcbiAgICAgIHRlc3QsXG4gICAgICBub2RlT3B0aW9ucyxcbiAgICB9KTtcbiAgICB0aGlzLnByb2plY3QubnBtaWdub3JlPy5hZGRQYXR0ZXJucyhgLyR7dGhpcy5jb25maWdGaWxlLmZpbGUucGF0aH1gKTtcblxuICAgIC8vIFRlc3QgbWF0Y2ggcGF0dGVybnMgYXJlIHBvc2l0aW9uYWwgYXJndW1lbnRzIGFuZCBtdXN0IGNvbWUgYWZ0ZXIgZXZlcnlcbiAgICAvLyBmbGFnOiBvbmNlIE5vZGUgc2VlcyB0aGUgZmlyc3QgcG9zaXRpb25hbCBhcmd1bWVudCBpdCBzdG9wcyBwYXJzaW5nXG4gICAgLy8gZmxhZ3MsIHNvIGFueSBmbGFnIGFwcGVuZGVkIGFmdGVyIHRoZW0gKGUuZy4gYC0td2F0Y2hgKSBpcyBpZ25vcmVkLlxuICAgIGNvbnN0IGNvbW1hbmQ6IHN0cmluZ1tdID0gW1xuICAgICAgXCJub2RlXCIsXG4gICAgICBgLS1leHBlcmltZW50YWwtY29uZmlnLWZpbGU9JHt0aGlzLmNvbmZpZ0ZpbGUuZmlsZS5wYXRofWAsXG4gICAgICAuLi5leHRyYUNsaU9wdGlvbnMsXG4gICAgXTtcblxuICAgIC8vIGB0ZXN0TWF0Y2hgIGlzIGEgbGl2ZSBjb2xsZWN0aW9uIChgYWRkYC9gcmVtb3ZlYCBjYW4gc3RpbGwgYmUgY2FsbGVkXG4gICAgLy8gYWZ0ZXIgY29uc3RydWN0aW9uKSwgc28gaXRzIHJlc29sdXRpb24gaXMgZGVmZXJyZWQgd2l0aCBgbGF6eUV4ZWNBcmdzYFxuICAgIC8vIHJhdGhlciB0aGFuIHJlYWQgZWFnZXJseSBoZXJlLlxuICAgIGNvbnN0IHRlc3RNYXRjaEFyZ3MgPSAoKSA9PiB0aGlzLnRlc3RNYXRjaC5kZWZlcnJlZCgpO1xuXG4gICAgaWYgKHRoaXMudXBkYXRlU25hcHNob3QgPT09IE5vZGVUZXN0VXBkYXRlU25hcHNob3QuQUxXQVlTKSB7XG4gICAgICB0aGlzLnByb2plY3QudGVzdFRhc2suZXhlY0FyZ3MoXG4gICAgICAgIGxhenlFeGVjQXJncygoKSA9PiBbXG4gICAgICAgICAgLi4uY29tbWFuZCxcbiAgICAgICAgICBcIi0tdGVzdC11cGRhdGUtc25hcHNob3RzXCIsXG4gICAgICAgICAgLi4udGVzdE1hdGNoQXJncygpLFxuICAgICAgICBdKSxcbiAgICAgICAgeyByZWNlaXZlQXJnczogdHJ1ZSB9LFxuICAgICAgKTtcbiAgICB9IGVsc2Uge1xuICAgICAgY29uc3QgdGVzdFVwZGF0ZSA9IHRoaXMucHJvamVjdC50YXNrcy50cnlGaW5kKFwidGVzdDp1cGRhdGVcIik7XG4gICAgICBpZiAoIXRlc3RVcGRhdGUpIHtcbiAgICAgICAgdGhpcy5wcm9qZWN0LmFkZFRhc2soXCJ0ZXN0OnVwZGF0ZVwiLCB7XG4gICAgICAgICAgZGVzY3JpcHRpb246IFwiVXBkYXRlIHRlc3Qgc25hcHNob3RzXCIsXG4gICAgICAgICAgZXhlY0FyZ3M6IGxhenlFeGVjQXJncygoKSA9PiBbXG4gICAgICAgICAgICAuLi5jb21tYW5kLFxuICAgICAgICAgICAgXCItLXRlc3QtdXBkYXRlLXNuYXBzaG90c1wiLFxuICAgICAgICAgICAgLi4udGVzdE1hdGNoQXJncygpLFxuICAgICAgICAgIF0pLFxuICAgICAgICAgIHJlY2VpdmVBcmdzOiB0cnVlLFxuICAgICAgICB9KTtcbiAgICAgIH1cblxuICAgICAgdGhpcy5wcm9qZWN0LnRlc3RUYXNrLmV4ZWNBcmdzKFxuICAgICAgICBsYXp5RXhlY0FyZ3MoKCkgPT4gWy4uLmNvbW1hbmQsIC4uLnRlc3RNYXRjaEFyZ3MoKV0pLFxuICAgICAgICB7IHJlY2VpdmVBcmdzOiB0cnVlIH0sXG4gICAgICApO1xuICAgIH1cblxuICAgIGNvbnN0IHRlc3RXYXRjaCA9IHRoaXMucHJvamVjdC50YXNrcy50cnlGaW5kKFwidGVzdDp3YXRjaFwiKTtcbiAgICBpZiAoIXRlc3RXYXRjaCkge1xuICAgICAgdGhpcy5wcm9qZWN0LmFkZFRhc2soXCJ0ZXN0OndhdGNoXCIsIHtcbiAgICAgICAgZGVzY3JpcHRpb246IFwiUnVuIHRlc3RzIGluIHdhdGNoIG1vZGVcIixcbiAgICAgICAgZXhlY0FyZ3M6IGxhenlFeGVjQXJncygoKSA9PiBbXG4gICAgICAgICAgLi4uY29tbWFuZCxcbiAgICAgICAgICBcIi0td2F0Y2hcIixcbiAgICAgICAgICAuLi50ZXN0TWF0Y2hBcmdzKCksXG4gICAgICAgIF0pLFxuICAgICAgfSk7XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIEFkZHMgYSB0ZXN0IG1hdGNoIHBhdHRlcm4uXG4gICAqIEBwYXJhbSBwYXR0ZXJuIGdsb2IgcGF0dGVybiB0byBtYXRjaCBmb3IgdGVzdHNcbiAgICovXG4gIHB1YmxpYyBhZGRUZXN0TWF0Y2gocGF0dGVybjogc3RyaW5nKSB7XG4gICAgdGhpcy50ZXN0TWF0Y2guYWRkKHBhdHRlcm4pO1xuICB9XG5cbiAgLyoqXG4gICAqIFJlbW92ZXMgYSB0ZXN0IG1hdGNoIHBhdHRlcm4sIGlmIGNvbmZpZ3VyZWQuXG4gICAqIEBwYXJhbSBwYXR0ZXJuIGdsb2IgcGF0dGVybiB0byByZW1vdmVcbiAgICovXG4gIHB1YmxpYyByZW1vdmVUZXN0TWF0Y2gocGF0dGVybjogc3RyaW5nKSB7XG4gICAgdGhpcy50ZXN0TWF0Y2gucmVtb3ZlKHBhdHRlcm4pO1xuICB9XG5cbiAgLyoqXG4gICAqIEFkZHMgYSByZXBvcnRlciwgb3IgdXBkYXRlcyBpdHMgZGVzdGluYXRpb24gaWYgb25lIHdpdGggdGhlIHNhbWUgbmFtZVxuICAgKiBpcyBhbHJlYWR5IGNvbmZpZ3VyZWQuXG4gICAqIEBwYXJhbSBuYW1lIFRoZSBuYW1lL2tpbmQgb2YgdGhlIHJlcG9ydGVyLCBlLmcuIGBzcGVjYCwgYGp1bml0YCwgYGxjb3ZgLlxuICAgKiBAcGFyYW0gZGVzdGluYXRpb24gV2hlcmUgdGhlIHJlcG9ydGVyJ3Mgb3V0cHV0IGlzIHdyaXR0ZW4uXG4gICAqIEBkZWZhdWx0IERlc3RpbmF0aW9uLlNURE9VVFxuICAgKi9cbiAgcHVibGljIGFkZFJlcG9ydGVyKG5hbWU6IFJlcG9ydGVyS2luZCwgZGVzdGluYXRpb24/OiBEZXN0aW5hdGlvbikge1xuICAgIHRoaXMucmVwb3J0ZXJzLmFkZChuYW1lLCBkZXN0aW5hdGlvbik7XG4gIH1cblxuICAvKipcbiAgICogUmVtb3ZlcyBhIHJlcG9ydGVyLCBpZiBjb25maWd1cmVkLlxuICAgKiBAcGFyYW0gbmFtZSBUaGUgbmFtZS9raW5kIG9mIHRoZSByZXBvcnRlciB0byByZW1vdmUuXG4gICAqL1xuICBwdWJsaWMgcmVtb3ZlUmVwb3J0ZXIobmFtZTogUmVwb3J0ZXJLaW5kKSB7XG4gICAgdGhpcy5yZXBvcnRlcnMucmVtb3ZlKG5hbWUpO1xuICB9XG5cbiAgLyoqXG4gICAqIExpc3RzIHRoZSBjb25maWd1cmVkIHJlcG9ydGVycywgaW4gdGhlIG9yZGVyIHRoZXkgd2VyZSBhZGRlZC5cbiAgICovXG4gIHB1YmxpYyBsaXN0UmVwb3J0ZXJzKCk6IE5vZGVSZXBvcnRlcltdIHtcbiAgICByZXR1cm4gdGhpcy5yZXBvcnRlcnMubGlzdCgpO1xuICB9XG59XG5cbi8qKlxuICogRGVmZXJzIHJlc29sdXRpb24gb2YgYSBgVGFza1N0ZXBgJ3MgYGV4ZWNBcmdzYCB0byBzeW50aGVzaXMgdGltZSwgd2hlbiB0aGVcbiAqIHRhc2tzIG1hbmlmZXN0IGlzIHJlbmRlcmVkLlxuICpcbiAqIGBUYXNrU3RlcC5leGVjQXJnc2AgaXMgdHlwZWQgYXMgYSBwbGFpbiBgc3RyaW5nW11gLCBidXQgdGhlIG1hbmlmZXN0IGlzXG4gKiB3cml0dGVuIHRocm91Z2ggdGhlIGdlbmVyaWMgYHJlc29sdmUoKWAgd2Fsa2VyIChzZWVcbiAqIGBQcm9qZW5UYXNrUnVubmVyLnRhc2tzTWFuaWZlc3RgKSAtIHRoZSBzYW1lIG1lY2hhbmlzbSBgSnNvbkZpbGVgL2BPYmplY3RGaWxlYFxuICogdXNlIGZvciBsYXp5IHZhbHVlcyBlbHNld2hlcmUgKGUuZy4gYE5vZGVSZXBvcnRlcnMubGlzdGAsIGBUZXN0TWF0Y2hgKS5cbiAqIFRoYXQgd2Fsa2VyIGNhbGxzIGFueSBmdW5jdGlvbiBpdCBmaW5kcywgc28gYSBjYWxsYmFjayBkZWZlcnJlZCBoZXJlIHJ1bnNcbiAqIG9uY2UsIGF0IHRoYXQgcG9pbnQsIGluIHBsYWNlIG9mIGEgcGxhaW4gYXJyYXkuXG4gKi9cbmZ1bmN0aW9uIGxhenlFeGVjQXJncyhmbjogKCkgPT4gc3RyaW5nW10pOiBzdHJpbmdbXSB7XG4gIHJldHVybiBmbiBhcyBhbnk7XG59XG4iXX0=