@aws-cdk/integ-runner
Version:
CDK Integration Testing Tool
307 lines • 50 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.parseCliArgs = parseCliArgs;
exports.main = main;
exports.cli = cli;
// Exercise all integ stacks and if they deploy, update the expected synth files
const fs = require("fs");
const path = require("path");
const chalk = require("chalk");
const workerpool = require("workerpool");
const logger = require("./logger");
const integration_tests_1 = require("./runner/integration-tests");
const unstable_features_1 = require("./unstable-features");
const workers_1 = require("./workers");
const integ_watch_worker_1 = require("./workers/integ-watch-worker");
// https://github.com/yargs/yargs/issues/1929
// https://github.com/evanw/esbuild/issues/1492
// eslint-disable-next-line @typescript-eslint/no-require-imports
const yargs = require('yargs');
function parseCliArgs(args = []) {
const argv = yargs
.usage('Usage: integ-runner [TEST...]')
.option('config', {
config: true,
configParser: configFromFile,
default: 'integ.config.json',
desc: 'Load options from a JSON config file. Options provided as CLI arguments take precedent.',
})
.option('watch', { type: 'boolean', default: false, desc: 'Perform integ tests in watch mode' })
.option('list', { type: 'boolean', default: false, desc: 'List tests instead of running them' })
.option('clean', { type: 'boolean', default: true, desc: 'Clean up and delete stack after test is completed (use --no-clean to negate)' })
.option('verbose', { type: 'boolean', default: false, alias: 'v', count: true, desc: 'Verbose logs and metrics on integration tests durations (specify multiple times to increase verbosity)' })
.option('dry-run', { type: 'boolean', default: false, desc: 'do not actually deploy the stack. just update the snapshot (not recommended!)' })
.option('update-on-failed', { type: 'boolean', default: false, desc: 'rerun integration tests and update snapshots for failed tests.' })
.option('force', { type: 'boolean', default: false, desc: 'Rerun all integration tests even if tests are passing' })
.option('parallel-regions', { type: 'array', desc: 'Tests are run in parallel across these regions. To prevent tests from running in parallel, provide only a single region', default: [], coerce: splitByComma })
.options('directory', { type: 'string', default: 'test', desc: 'starting directory to discover integration tests. Tests will be discovered recursively from this directory' })
.options('profiles', { type: 'array', desc: 'list of AWS profiles to use. Tests will be run in parallel across each profile+regions', default: [], coerce: splitByComma })
.options('max-workers', { type: 'number', desc: 'The max number of workerpool workers to use when running integration tests in parallel', default: 16 })
.options('exclude', { type: 'boolean', desc: 'Run all tests in the directory, except the specified TESTs', default: false })
.option('strict', { type: 'boolean', default: false, desc: 'Fail if any specified tests are not found' })
.options('from-file', { type: 'string', desc: 'Read TEST names from a file (one TEST per line)' })
.option('inspect-failures', { type: 'boolean', desc: 'Keep the integ test cloud assembly if a failure occurs for inspection', default: false })
.option('disable-update-workflow', { type: 'boolean', default: undefined, desc: 'DEPRECATED, use --[no]-update-workflow instead' })
.option('update-workflow', { type: 'boolean', default: undefined, desc: 'Deploys the committed snapshot before the updated application. Only works if snapshots are region-agnostic.' })
.option('language', {
alias: 'l',
default: ['javascript', 'typescript', 'python', 'go'],
choices: ['javascript', 'typescript', 'python', 'go'],
type: 'array',
nargs: 1,
desc: 'Use these presets to run integration tests for the selected languages',
})
.option('app', { type: 'string', default: undefined, desc: 'The custom CLI command that will be used to run the test files. You can include {filePath} to specify where in the command the test file path should be inserted. Example: --app="python3.8 {filePath}".' })
.option('test-regex', { type: 'array', desc: 'Detect integration test files matching this JavaScript regex pattern. If used multiple times, all files matching any one of the patterns are detected.', default: [] })
.option('unstable', { type: 'array', desc: `Opt-in to using unstable features. By using these flags you acknowledge that scope and API of unstable features may change without notice. Specify multiple times for each unstable feature you want to opt-in to. ${(0, unstable_features_1.availableFeaturesDescription)()}`, nargs: 1, default: [] })
.strict()
.parse(args);
const tests = argv._;
const parallelRegions = arrayFromYargs(argv['parallel-regions']);
const testRegions = parallelRegions ?? ['us-east-1', 'us-east-2', 'us-west-2'];
const profiles = arrayFromYargs(argv.profiles);
const fromFile = argv['from-file'];
const maxWorkers = argv['max-workers'];
const verbosity = argv.verbose;
const verbose = verbosity >= 1;
const numTests = testRegions.length * (profiles ?? [1]).length;
if (maxWorkers < numTests) {
logger.warning('You are attempting to run %s tests in parallel, but only have %s workers. Not all of your profiles+regions will be utilized', numTests, maxWorkers);
}
if (tests.length > 0 && fromFile) {
throw new Error('A list of tests cannot be provided if "--from-file" is provided');
}
if (argv.strict && argv.exclude) {
throw new Error('Cannot use --strict with --exclude');
}
const requestedTests = fromFile
? (fs.readFileSync(fromFile, { encoding: 'utf8' }))
.split('\n')
.filter(x => x)
.filter(x => !x.startsWith('#'))
: (tests.length > 0 ? tests : undefined); // 'undefined' means no request
if (argv['disable-update-workflow'] !== undefined && argv['update-workflow'] !== undefined) {
throw new Error('--disable-update-workflow and --[no-]update-workflow cannot be used together');
}
let updateWorkflow = argv['update-workflow'] !== undefined ? !!argv['update-workflow'] : !argv['disable-update-workflow'];
return {
tests: requestedTests,
app: argv.app,
testRegex: arrayFromYargs(argv['test-regex']),
testRegions,
originalRegions: parallelRegions,
profiles,
runUpdateOnFailed: (argv['update-on-failed'] ?? false),
fromFile,
exclude: argv.exclude,
maxWorkers,
list: argv.list,
directory: argv.directory,
inspectFailures: argv['inspect-failures'],
verbosity,
verbose,
clean: argv.clean,
force: argv.force,
dryRun: argv['dry-run'],
updateWorkflow,
language: arrayFromYargs(argv.language),
watch: argv.watch,
strict: argv.strict,
unstable: arrayFromYargs(argv.unstable) ?? [],
};
}
async function main(args) {
try {
const options = parseCliArgs(args);
// Process unstable features and emit appropriate warnings
options.unstable = (0, unstable_features_1.processUnstableFeatures)(options.unstable);
await run(options);
}
catch (err) {
logger.error(err);
throw err;
}
}
async function run(options) {
const testsFromArgs = await new integration_tests_1.IntegrationTests(path.resolve(options.directory)).fromCliOptions(options);
// List only prints the discovered tests
if (options.list) {
process.stdout.write(testsFromArgs.map(t => t.discoveryRelativeFileName).join('\n') + '\n');
return;
}
const pool = workerpool.pool(path.join(__dirname, '..', 'lib', 'workers', 'extract', 'index.js'), {
maxWorkers: options.watch ? 1 : options.maxWorkers,
});
const testsToRun = [];
let destructiveChanges = false;
let failedSnapshots = [];
let testsSucceeded = false;
validateWatchArgs({
...options,
testRegions: options.originalRegions,
tests: testsFromArgs,
});
try {
if (!options.watch) {
// always run snapshot tests, but if '--force' is passed then
// run integration tests on all failed tests, not just those that
// failed snapshot tests
failedSnapshots = await (0, workers_1.runSnapshotTests)(pool, testsFromArgs, {
retain: options.inspectFailures,
verbose: options.verbose,
});
for (const failure of failedSnapshots) {
logger.warning(`Failed: ${failure.fileName}`);
if (failure.destructiveChanges && failure.destructiveChanges.length > 0) {
printDestructiveChanges(failure.destructiveChanges);
destructiveChanges = true;
}
}
if (!options.force) {
testsToRun.push(...failedSnapshots);
}
else {
// if any of the test failed snapshot tests, keep those results
// and merge with the rest of the tests from args
testsToRun.push(...mergeTests(testsFromArgs.map(t => t.info), failedSnapshots));
}
}
else {
testsToRun.push(...testsFromArgs.map(t => t.info));
}
// run integration tests if `--update-on-failed` OR `--force` is used
if (options.runUpdateOnFailed || options.force) {
const { success, metrics } = await (0, workers_1.runIntegrationTests)({
pool,
tests: testsToRun,
regions: options.testRegions,
profiles: options.profiles,
clean: options.clean,
dryRun: options.dryRun,
verbosity: options.verbosity,
updateWorkflow: options.updateWorkflow,
watch: options.watch,
});
testsSucceeded = success;
if (options.clean === false) {
logger.warning('Not cleaning up stacks since "--no-clean" was used');
}
if (Boolean(options.verbose)) {
printMetrics(metrics);
}
if (!success) {
throw new Error('Some integration tests failed!');
}
}
else if (options.watch) {
await (0, integ_watch_worker_1.watchIntegrationTest)(pool, {
watch: true,
verbosity: options.verbosity,
...testsToRun[0],
profile: options.profiles ? options.profiles[0] : undefined,
region: options.testRegions[0],
});
}
}
finally {
void pool.terminate();
}
if (destructiveChanges) {
throw new Error('Some changes were destructive!');
}
if (failedSnapshots.length > 0) {
let message = '';
if (!options.runUpdateOnFailed) {
message = 'To re-run failed tests run: integ-runner --update-on-failed';
}
if (!testsSucceeded) {
throw new Error(`Some tests failed!\n${message}`);
}
}
}
function validateWatchArgs(args) {
if (args.watch) {
if ((args.testRegions && args.testRegions.length > 1)
|| (args.profiles && args.profiles.length > 1)
|| args.tests.length > 1) {
throw new Error('Running with watch only supports a single test. Only provide a single option' +
'to `--profiles` `--parallel-regions` `--max-workers');
}
if (args.runUpdateOnFailed || args.updateWorkflow || args.force || args.dryRun) {
logger.warning('args `--update-on-failed`, `--update-workflow`, `--force`, `--dry-run` have no effect when running with `--watch`');
}
}
}
function printDestructiveChanges(changes) {
if (changes.length > 0) {
logger.warning('!!! This test contains %s !!!', chalk.bold('destructive changes'));
changes.forEach(change => {
logger.warning(' Stack: %s - Resource: %s - Impact: %s', change.stackName, change.logicalId, change.impact);
});
logger.warning('!!! If these destructive changes are necessary, please indicate this on the PR !!!');
}
}
function printMetrics(metrics) {
logger.highlight(' --- Integration test metrics ---');
const sortedMetrics = metrics.sort((a, b) => a.duration - b.duration);
sortedMetrics.forEach(metric => {
logger.print('Profile %s + Region %s total time: %s', metric.profile, metric.region, metric.duration);
const sortedTests = Object.entries(metric.tests).sort((a, b) => a[1] - b[1]);
sortedTests.forEach(test => logger.print(' %s: %s', test[0], test[1]));
});
}
/**
* Translate a Yargs input array to something that makes more sense in a programming language
* model (telling the difference between absence and an empty array)
*
* - An empty array is the default case, meaning the user didn't pass any arguments. We return
* undefined.
* - If the user passed a single empty string, they did something like `--array=`, which we'll
* take to mean they passed an empty array.
*/
function arrayFromYargs(xs) {
if (xs.length === 0) {
return undefined;
}
return xs.filter(x => x !== '');
}
/**
* Merge the tests we received from command line arguments with
* tests that failed snapshot tests. The failed snapshot tests have additional
* information that we want to keep so this should override any test from args
*/
function mergeTests(testFromArgs, failedSnapshotTests) {
const failedTestNames = new Set(failedSnapshotTests.map(test => test.fileName));
const final = failedSnapshotTests;
final.push(...testFromArgs.filter(test => !failedTestNames.has(test.fileName)));
return final;
}
function cli(args = process.argv.slice(2)) {
main(args).then().catch(() => {
process.exitCode = 1;
});
}
/**
* Read CLI options from a config file if provided.
*
* @returns parsed CLI config options
*/
function configFromFile(fileName) {
if (!fileName) {
return {};
}
try {
return JSON.parse(fs.readFileSync(fileName, { encoding: 'utf-8' }));
}
catch {
return {};
}
}
/**
* Coerce function for yargs array options to support comma-separated values.
* Yargs doesn't natively split `--option=a,b,c` into ['a', 'b', 'c'].
*/
function splitByComma(xs) {
return xs.flatMap(x => x.split(',')).map(x => x.trim());
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xpLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiY2xpLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7O0FBa0JBLG9DQW9HQztBQUVELG9CQVlDO0FBbUxELGtCQUlDO0FBM1RELGdGQUFnRjtBQUNoRix5QkFBeUI7QUFDekIsNkJBQTZCO0FBQzdCLCtCQUErQjtBQUMvQix5Q0FBeUM7QUFDekMsbUNBQW1DO0FBRW5DLGtFQUE4RDtBQUM5RCwyREFBNEY7QUFFNUYsdUNBQWtFO0FBQ2xFLHFFQUFvRTtBQUVwRSw2Q0FBNkM7QUFDN0MsK0NBQStDO0FBQy9DLGlFQUFpRTtBQUNqRSxNQUFNLEtBQUssR0FBRyxPQUFPLENBQUMsT0FBTyxDQUFDLENBQUM7QUFFL0IsU0FBZ0IsWUFBWSxDQUFDLE9BQWlCLEVBQUU7SUFDOUMsTUFBTSxJQUFJLEdBQUcsS0FBSztTQUNmLEtBQUssQ0FBQywrQkFBK0IsQ0FBQztTQUN0QyxNQUFNLENBQUMsUUFBUSxFQUFFO1FBQ2hCLE1BQU0sRUFBRSxJQUFJO1FBQ1osWUFBWSxFQUFFLGNBQWM7UUFDNUIsT0FBTyxFQUFFLG1CQUFtQjtRQUM1QixJQUFJLEVBQUUseUZBQXlGO0tBQ2hHLENBQUM7U0FDRCxNQUFNLENBQUMsT0FBTyxFQUFFLEVBQUUsSUFBSSxFQUFFLFNBQVMsRUFBRSxPQUFPLEVBQUUsS0FBSyxFQUFFLElBQUksRUFBRSxtQ0FBbUMsRUFBRSxDQUFDO1NBQy9GLE1BQU0sQ0FBQyxNQUFNLEVBQUUsRUFBRSxJQUFJLEVBQUUsU0FBUyxFQUFFLE9BQU8sRUFBRSxLQUFLLEVBQUUsSUFBSSxFQUFFLG9DQUFvQyxFQUFFLENBQUM7U0FDL0YsTUFBTSxDQUFDLE9BQU8sRUFBRSxFQUFFLElBQUksRUFBRSxTQUFTLEVBQUUsT0FBTyxFQUFFLElBQUksRUFBRSxJQUFJLEVBQUUsOEVBQThFLEVBQUUsQ0FBQztTQUN6SSxNQUFNLENBQUMsU0FBUyxFQUFFLEVBQUUsSUFBSSxFQUFFLFNBQVMsRUFBRSxPQUFP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QyxJQUFJLEVBQUUsQ0FBQyxLQUFLLENBQUMsR0FBRyxFQUFFO1FBQzNCLE9BQU8sQ0FBQyxRQUFRLEdBQUcsQ0FBQyxDQUFDO0lBQ3ZCLENBQUMsQ0FBQyxDQUFDO0FBQ0wsQ0FBQztBQUVEOzs7O0dBSUc7QUFDSCxTQUFTLGNBQWMsQ0FBQyxRQUFpQjtJQUN2QyxJQUFJLENBQUMsUUFBUSxFQUFFLENBQUM7UUFDZCxPQUFPLEVBQUUsQ0FBQztJQUNaLENBQUM7SUFFRCxJQUFJLENBQUM7UUFDSCxPQUFPLElBQUksQ0FBQyxLQUFLLENBQUMsRUFBRSxDQUFDLFlBQVksQ0FBQyxRQUFRLEVBQUUsRUFBRSxRQUFRLEVBQUUsT0FBTyxFQUFFLENBQUMsQ0FBQyxDQUFDO0lBQ3RFLENBQUM7SUFBQyxNQUFNLENBQUM7UUFDUCxPQUFPLEVBQUUsQ0FBQztJQUNaLENBQUM7QUFDSCxDQUFDO0FBRUQ7OztHQUdHO0FBQ0gsU0FBUyxZQUFZLENBQUMsRUFBWTtJQUNoQyxPQUFPLEVBQUUsQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUMsS0FBSyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDLElBQUksRUFBRSxDQUFDLENBQUM7QUFDMUQsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8vIEV4ZXJjaXNlIGFsbCBpbnRlZyBzdGFja3MgYW5kIGlmIHRoZXkgZGVwbG95LCB1cGRhdGUgdGhlIGV4cGVjdGVkIHN5bnRoIGZpbGVzXG5pbXBvcnQgKiBhcyBmcyBmcm9tICdmcyc7XG5pbXBvcnQgKiBhcyBwYXRoIGZyb20gJ3BhdGgnO1xuaW1wb3J0ICogYXMgY2hhbGsgZnJvbSAnY2hhbGsnO1xuaW1wb3J0ICogYXMgd29ya2VycG9vbCBmcm9tICd3b3JrZXJwb29sJztcbmltcG9ydCAqIGFzIGxvZ2dlciBmcm9tICcuL2xvZ2dlcic7XG5pbXBvcnQgdHlwZSB7IEludGVnVGVzdCwgSW50ZWdUZXN0SW5mbyB9IGZyb20gJy4vcnVubmVyL2ludGVncmF0aW9uLXRlc3RzJztcbmltcG9ydCB7IEludGVncmF0aW9uVGVzdHMgfSBmcm9tICcuL3J1bm5lci9pbnRlZ3JhdGlvbi10ZXN0cyc7XG5pbXBvcnQgeyBwcm9jZXNzVW5zdGFibGVGZWF0dXJlcywgYXZhaWxhYmxlRmVhdHVyZXNEZXNjcmlwdGlvbiB9IGZyb20gJy4vdW5zdGFibGUtZmVhdHVyZXMnO1xuaW1wb3J0IHR5cGUgeyBJbnRlZ1J1bm5lck1ldHJpY3MsIEludGVnVGVzdFdvcmtlckNvbmZpZywgRGVzdHJ1Y3RpdmVDaGFuZ2UgfSBmcm9tICcuL3dvcmtlcnMnO1xuaW1wb3J0IHsgcnVuU25hcHNob3RUZXN0cywgcnVuSW50ZWdyYXRpb25UZXN0cyB9IGZyb20gJy4vd29ya2Vycyc7XG5pbXBvcnQgeyB3YXRjaEludGVncmF0aW9uVGVzdCB9IGZyb20gJy4vd29ya2Vycy9pbnRlZy13YXRjaC13b3JrZXInO1xuXG4vLyBodHRwczovL2dpdGh1Yi5jb20veWFyZ3MveWFyZ3MvaXNzdWVzLzE5Mjlcbi8vIGh0dHBzOi8vZ2l0aHViLmNvbS9ldmFudy9lc2J1aWxkL2lzc3Vlcy8xNDkyXG4vLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgQHR5cGVzY3JpcHQtZXNsaW50L25vLXJlcXVpcmUtaW1wb3J0c1xuY29uc3QgeWFyZ3MgPSByZXF1aXJlKCd5YXJncycpO1xuXG5leHBvcnQgZnVuY3Rpb24gcGFyc2VDbGlBcmdzKGFyZ3M6IHN0cmluZ1tdID0gW10pIHtcbiAgY29uc3QgYXJndiA9IHlhcmdzXG4gICAgLnVzYWdlKCdVc2FnZTogaW50ZWctcnVubmVyIFtURVNULi4uXScpXG4gICAgLm9wdGlvbignY29uZmlnJywge1xuICAgICAgY29uZmlnOiB0cnVlLFxuICAgICAgY29uZmlnUGFyc2VyOiBjb25maWdGcm9tRmlsZSxcbiAgICAgIGRlZmF1bHQ6ICdpbnRlZy5jb25maWcuanNvbicsXG4gICAgICBkZXNjOiAnTG9hZCBvcHRpb25zIGZyb20gYSBKU09OIGNvbmZpZyBmaWxlLiBPcHRpb25zIHByb3ZpZGVkIGFzIENMSSBhcmd1bWVudHMgdGFrZSBwcmVjZWRlbnQuJyxcbiAgICB9KVxuICAgIC5vcHRpb24oJ3dhdGNoJywgeyB0eXBlOiAnYm9vbGVhbicsIGRlZmF1bHQ6IGZhbHNlLCBkZXNjOiAnUGVyZm9ybSBpbnRlZyB0ZXN0cyBpbiB3YXRjaCBtb2RlJyB9KVxuICAgIC5vcHRpb24oJ2xpc3QnLCB7IHR5cGU6ICdib29sZWFuJywgZGVmYXVsdDogZmFsc2UsIGRlc2M6ICdMaXN0IHRlc3RzIGluc3RlYWQgb2YgcnVubmluZyB0aGVtJyB9KVxuICAgIC5vcHRpb24oJ2NsZWFuJywgeyB0eXBlOiAnYm9vbGVhbicsIGRlZmF1bHQ6IHRydWUsIGRlc2M6ICdDbGVhbiB1cCBhbmQgZGVsZXRlIHN0YWNrIGFmdGVyIHRlc3QgaXMgY29tcGxldGVkICh1c2UgLS1uby1jbGVhbiB0byBuZWdhdGUpJyB9KVxuICAgIC5vcHRpb24oJ3ZlcmJvc2UnLCB7IHR5cGU6ICdib29sZWFuJywgZGVmYXVsdDogZmFsc2UsIGFsaWFzOiAndicsIGNvdW50OiB0cnVlLCBkZXNjOiAnVmVyYm9zZSBsb2dzIGFuZCBtZXRyaWNzIG9uIGludGVncmF0aW9uIHRlc3RzIGR1cmF0aW9ucyAoc3BlY2lmeSBtdWx0aXBsZSB0aW1lcyB0byBpbmNyZWFzZSB2ZXJib3NpdHkpJyB9KVxuICAgIC5vcHRpb24oJ2RyeS1ydW4nLCB7IHR5cGU6ICdib29sZWFuJywgZGVmYXVsdDogZmFsc2UsIGRlc2M6ICdkbyBub3QgYWN0dWFsbHkgZGVwbG95IHRoZSBzdGFjay4ganVzdCB1cGRhdGUgdGhlIHNuYXBzaG90IChub3QgcmVjb21tZW5kZWQhKScgfSlcbiAgICAub3B0aW9uKCd1cGRhdGUtb24tZmFpbGVkJywgeyB0eXBlOiAnYm9vbGVhbicsIGRlZmF1bHQ6IGZhbHNlLCBkZXNjOiAncmVydW4gaW50ZWdyYXRpb24gdGVzdHMgYW5kIHVwZGF0ZSBzbmFwc2hvdHMgZm9yIGZhaWxlZCB0ZXN0cy4nIH0pXG4gICAgLm9wdGlvbignZm9yY2UnLCB7IHR5cGU6ICdib29sZWFuJywgZGVmYXVsdDogZmFsc2UsIGRlc2M6ICdSZXJ1biBhbGwgaW50ZWdyYXRpb24gdGVzdHMgZXZlbiBpZiB0ZXN0cyBhcmUgcGFzc2luZycgfSlcbiAgICAub3B0aW9uKCdwYXJhbGxlbC1yZWdpb25zJywgeyB0eXBlOiAnYXJyYXknLCBkZXNjOiAnVGVzdHMgYXJlIHJ1biBpbiBwYXJhbGxlbCBhY3Jvc3MgdGhlc2UgcmVnaW9ucy4gVG8gcHJldmVudCB0ZXN0cyBmcm9tIHJ1bm5pbmcgaW4gcGFyYWxsZWwsIHByb3ZpZGUgb25seSBhIHNpbmdsZSByZWdpb24nLCBkZWZhdWx0OiBbXSwgY29lcmNlOiBzcGxpdEJ5Q29tbWEgfSlcbiAgICAub3B0aW9ucygnZGlyZWN0b3J5JywgeyB0eXBlOiAnc3RyaW5nJywgZGVmYXVsdDogJ3Rlc3QnLCBkZXNjOiAnc3RhcnRpbmcgZGlyZWN0b3J5IHRvIGRpc2NvdmVyIGludGVncmF0aW9uIHRlc3RzLiBUZXN0cyB3aWxsIGJlIGRpc2NvdmVyZWQgcmVjdXJzaXZlbHkgZnJvbSB0aGlzIGRpcmVjdG9yeScgfSlcbiAgICAub3B0aW9ucygncHJvZmlsZXMnLCB7IHR5cGU6ICdhcnJheScsIGRlc2M6ICdsaXN0IG9mIEFXUyBwcm9maWxlcyB0byB1c2UuIFRlc3RzIHdpbGwgYmUgcnVuIGluIHBhcmFsbGVsIGFjcm9zcyBlYWNoIHByb2ZpbGUrcmVnaW9ucycsIGRlZmF1bHQ6IFtdLCBjb2VyY2U6IHNwbGl0QnlDb21tYSB9KVxuICAgIC5vcHRpb25zKCdtYXgtd29ya2VycycsIHsgdHlwZTogJ251bWJlcicsIGRlc2M6ICdUaGUgbWF4IG51bWJlciBvZiB3b3JrZXJwb29sIHdvcmtlcnMgdG8gdXNlIHdoZW4gcnVubmluZyBpbnRlZ3JhdGlvbiB0ZXN0cyBpbiBwYXJhbGxlbCcsIGRlZmF1bHQ6IDE2IH0pXG4gICAgLm9wdGlvbnMoJ2V4Y2x1ZGUnLCB7IHR5cGU6ICdib29sZWFuJywgZGVzYzogJ1J1biBhbGwgdGVzdHMgaW4gdGhlIGRpcmVjdG9yeSwgZXhjZXB0IHRoZSBzcGVjaWZpZWQgVEVTVHMnLCBkZWZhdWx0OiBmYWxzZSB9KVxuICAgIC5vcHRpb24oJ3N0cmljdCcsIHsgdHlwZTogJ2Jvb2xlYW4nLCBkZWZhdWx0OiBmYWxzZSwgZGVzYzogJ0ZhaWwgaWYgYW55IHNwZWNpZmllZCB0ZXN0cyBhcmUgbm90IGZvdW5kJyB9KVxuICAgIC5vcHRpb25zKCdmcm9tLWZpbGUnLCB7IHR5cGU6ICdzdHJpbmcnLCBkZXNjOiAnUmVhZCBURVNUIG5hbWVzIGZyb20gYSBmaWxlIChvbmUgVEVTVCBwZXIgbGluZSknIH0pXG4gICAgLm9wdGlvbignaW5zcGVjdC1mYWlsdXJlcycsIHsgdHlwZTogJ2Jvb2xlYW4nLCBkZXNjOiAnS2VlcCB0aGUgaW50ZWcgdGVzdCBjbG91ZCBhc3NlbWJseSBpZiBhIGZhaWx1cmUgb2NjdXJzIGZvciBpbnNwZWN0aW9uJywgZGVmYXVsdDogZmFsc2UgfSlcbiAgICAub3B0aW9uKCdkaXNhYmxlLXVwZGF0ZS13b3JrZmxvdycsIHsgdHlwZTogJ2Jvb2xlYW4nLCBkZWZhdWx0OiB1bmRlZmluZWQsIGRlc2M6ICdERVBSRUNBVEVELCB1c2UgLS1bbm9dLXVwZGF0ZS13b3JrZmxvdyBpbnN0ZWFkJyB9KVxuICAgIC5vcHRpb24oJ3VwZGF0ZS13b3JrZmxvdycsIHsgdHlwZTogJ2Jvb2xlYW4nLCBkZWZhdWx0OiB1bmRlZmluZWQsIGRlc2M6ICdEZXBsb3lzIHRoZSBjb21taXR0ZWQgc25hcHNob3QgYmVmb3JlIHRoZSB1cGRhdGVkIGFwcGxpY2F0aW9uLiBPbmx5IHdvcmtzIGlmIHNuYXBzaG90cyBhcmUgcmVnaW9uLWFnbm9zdGljLicgfSlcbiAgICAub3B0aW9uKCdsYW5ndWFnZScsIHtcbiAgICAgIGFsaWFzOiAnbCcsXG4gICAgICBkZWZhdWx0OiBbJ2phdmFzY3JpcHQnLCAndHlwZXNjcmlwdCcsICdweXRob24nLCAnZ28nXSxcbiAgICAgIGNob2ljZXM6IFsnamF2YXNjcmlwdCcsICd0eXBlc2NyaXB0JywgJ3B5dGhvbicsICdnbyddLFxuICAgICAgdHlwZTogJ2FycmF5JyxcbiAgICAgIG5hcmdzOiAxLFxuICAgICAgZGVzYzogJ1VzZSB0aGVzZSBwcmVzZXRzIHRvIHJ1biBpbnRlZ3JhdGlvbiB0ZXN0cyBmb3IgdGhlIHNlbGVjdGVkIGxhbmd1YWdlcycsXG4gICAgfSlcbiAgICAub3B0aW9uKCdhcHAnLCB7IHR5cGU6ICdzdHJpbmcnLCBkZWZhdWx0OiB1bmRlZmluZWQsIGRlc2M6ICdUaGUgY3VzdG9tIENMSSBjb21tYW5kIHRoYXQgd2lsbCBiZSB1c2VkIHRvIHJ1biB0aGUgdGVzdCBmaWxlcy4gWW91IGNhbiBpbmNsdWRlIHtmaWxlUGF0aH0gdG8gc3BlY2lmeSB3aGVyZSBpbiB0aGUgY29tbWFuZCB0aGUgdGVzdCBmaWxlIHBhdGggc2hvdWxkIGJlIGluc2VydGVkLiBFeGFtcGxlOiAtLWFwcD1cInB5dGhvbjMuOCB7ZmlsZVBhdGh9XCIuJyB9KVxuICAgIC5vcHRpb24oJ3Rlc3QtcmVnZXgnLCB7IHR5cGU6ICdhcnJheScsIGRlc2M6ICdEZXRlY3QgaW50ZWdyYXRpb24gdGVzdCBmaWxlcyBtYXRjaGluZyB0aGlzIEphdmFTY3JpcHQgcmVnZXggcGF0dGVybi4gSWYgdXNlZCBtdWx0aXBsZSB0aW1lcywgYWxsIGZpbGVzIG1hdGNoaW5nIGFueSBvbmUgb2YgdGhlIHBhdHRlcm5zIGFyZSBkZXRlY3RlZC4nLCBkZWZhdWx0OiBbXSB9KVxuICAgIC5vcHRpb24oJ3Vuc3RhYmxlJywgeyB0eXBlOiAnYXJyYXknLCBkZXNjOiBgT3B0LWluIHRvIHVzaW5nIHVuc3RhYmxlIGZlYXR1cmVzLiBCeSB1c2luZyB0aGVzZSBmbGFncyB5b3UgYWNrbm93bGVkZ2UgdGhhdCBzY29wZSBhbmQgQVBJIG9mIHVuc3RhYmxlIGZlYXR1cmVzIG1heSBjaGFuZ2Ugd2l0aG91dCBub3RpY2UuIFNwZWNpZnkgbXVsdGlwbGUgdGltZXMgZm9yIGVhY2ggdW5zdGFibGUgZmVhdHVyZSB5b3Ugd2FudCB0byBvcHQtaW4gdG8uICR7YXZhaWxhYmxlRmVhdHVyZXNEZXNjcmlwdGlvbigpfWAsIG5hcmdzOiAxLCBkZWZhdWx0OiBbXSB9KVxuICAgIC5zdHJpY3QoKVxuICAgIC5wYXJzZShhcmdzKTtcblxuICBjb25zdCB0ZXN0czogc3RyaW5nW10gPSBhcmd2Ll87XG4gIGNvbnN0IHBhcmFsbGVsUmVnaW9ucyA9IGFycmF5RnJvbVlhcmdzKGFyZ3ZbJ3BhcmFsbGVsLXJlZ2lvbnMnXSk7XG4gIGNvbnN0IHRlc3RSZWdpb25zOiBzdHJpbmdbXSA9IHBhcmFsbGVsUmVnaW9ucyA/PyBbJ3VzLWVhc3QtMScsICd1cy1lYXN0LTInLCAndXMtd2VzdC0yJ107XG4gIGNvbnN0IHByb2ZpbGVzID0gYXJyYXlGcm9tWWFyZ3MoYXJndi5wcm9maWxlcyk7XG4gIGNvbnN0IGZyb21GaWxlOiBzdHJpbmcgfCB1bmRlZmluZWQgPSBhcmd2Wydmcm9tLWZpbGUnXTtcbiAgY29uc3QgbWF4V29ya2VyczogbnVtYmVyID0gYXJndlsnbWF4LXdvcmtlcnMnXTtcbiAgY29uc3QgdmVyYm9zaXR5OiBudW1iZXIgPSBhcmd2LnZlcmJvc2U7XG4gIGNvbnN0IHZlcmJvc2U6IGJvb2xlYW4gPSB2ZXJib3NpdHkgPj0gMTtcblxuICBjb25zdCBudW1UZXN0cyA9IHRlc3RSZWdpb25zLmxlbmd0aCAqIChwcm9maWxlcyA/PyBbMV0pLmxlbmd0aDtcbiAgaWYgKG1heFdvcmtlcnMgPCBudW1UZXN0cykge1xuICAgIGxvZ2dlci53YXJuaW5nKCdZb3UgYXJlIGF0dGVtcHRpbmcgdG8gcnVuICVzIHRlc3RzIGluIHBhcmFsbGVsLCBidXQgb25seSBoYXZlICVzIHdvcmtlcnMuIE5vdCBhbGwgb2YgeW91ciBwcm9maWxlcytyZWdpb25zIHdpbGwgYmUgdXRpbGl6ZWQnLCBudW1UZXN0cywgbWF4V29ya2Vycyk7XG4gIH1cblxuICBpZiAodGVzdHMubGVuZ3RoID4gMCAmJiBmcm9tRmlsZSkge1xuICAgIHRocm93IG5ldyBFcnJvcignQSBsaXN0IG9mIHRlc3RzIGNhbm5vdCBiZSBwcm92aWRlZCBpZiBcIi0tZnJvbS1maWxlXCIgaXMgcHJvdmlkZWQnKTtcbiAgfVxuXG4gIGlmIChhcmd2LnN0cmljdCAmJiBhcmd2LmV4Y2x1ZGUpIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoJ0Nhbm5vdCB1c2UgLS1zdHJpY3Qgd2l0aCAtLWV4Y2x1ZGUnKTtcbiAgfVxuXG4gIGNvbnN0IHJlcXVlc3RlZFRlc3RzID0gZnJvbUZpbGVcbiAgICA/IChmcy5yZWFkRmlsZVN5bmMoZnJvbUZpbGUsIHsgZW5jb2Rpbmc6ICd1dGY4JyB9KSlcbiAgICAgIC5zcGxpdCgnXFxuJylcbiAgICAgIC5maWx0ZXIoeCA9PiB4KVxuICAgICAgLmZpbHRlcih4ID0+ICF4LnN0YXJ0c1dpdGgoJyMnKSlcbiAgICA6ICh0ZXN0cy5sZW5ndGggPiAwID8gdGVzdHMgOiB1bmRlZmluZWQpOyAvLyAndW5kZWZpbmVkJyBtZWFucyBubyByZXF1ZXN0XG5cbiAgaWYgKGFyZ3ZbJ2Rpc2FibGUtdXBkYXRlLXdvcmtmbG93J10gIT09IHVuZGVmaW5lZCAmJiBhcmd2Wyd1cGRhdGUtd29ya2Zsb3cnXSAhPT0gdW5kZWZpbmVkKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKCctLWRpc2FibGUtdXBkYXRlLXdvcmtmbG93IGFuZCAtLVtuby1ddXBkYXRlLXdvcmtmbG93IGNhbm5vdCBiZSB1c2VkIHRvZ2V0aGVyJyk7XG4gIH1cblxuICBsZXQgdXBkYXRlV29ya2Zsb3cgPSBhcmd2Wyd1cGRhdGUtd29ya2Zsb3cnXSAhPT0gdW5kZWZpbmVkID8gISFhcmd2Wyd1cGRhdGUtd29ya2Zsb3cnXSA6ICFhcmd2WydkaXNhYmxlLXVwZGF0ZS13b3JrZmxvdyddO1xuXG4gIHJldHVybiB7XG4gICAgdGVzdHM6IHJlcXVlc3RlZFRlc3RzLFxuICAgIGFwcDogYXJndi5hcHAgYXMgKHN0cmluZyB8IHVuZGVmaW5lZCksXG4gICAgdGVzdFJlZ2V4OiBhcnJheUZyb21ZYXJncyhhcmd2Wyd0ZXN0LXJlZ2V4J10pLFxuICAgIHRlc3RSZWdpb25zLFxuICAgIG9yaWdpbmFsUmVnaW9uczogcGFyYWxsZWxSZWdpb25zLFxuICAgIHByb2ZpbGVzLFxuICAgIHJ1blVwZGF0ZU9uRmFpbGVkOiAoYXJndlsndXBkYXRlLW9uLWZhaWxlZCddID8/IGZhbHNlKSBhcyBib29sZWFuLFxuICAgIGZyb21GaWxlLFxuICAgIGV4Y2x1ZGU6IGFyZ3YuZXhjbHVkZSBhcyBib29sZWFuLFxuICAgIG1heFdvcmtlcnMsXG4gICAgbGlzdDogYXJndi5saXN0IGFzIGJvb2xlYW4sXG4gICAgZGlyZWN0b3J5OiBhcmd2LmRpcmVjdG9yeSBhcyBzdHJpbmcsXG4gICAgaW5zcGVjdEZhaWx1cmVzOiBhcmd2WydpbnNwZWN0LWZhaWx1cmVzJ10gYXMgYm9vbGVhbixcbiAgICB2ZXJib3NpdHksXG4gICAgdmVyYm9zZSxcbiAgICBjbGVhbjogYXJndi5jbGVhbiBhcyBib29sZWFuLFxuICAgIGZvcmNlOiBhcmd2LmZvcmNlIGFzIGJvb2xlYW4sXG4gICAgZHJ5UnVuOiBhcmd2WydkcnktcnVuJ10gYXMgYm9vbGVhbixcbiAgICB1cGRhdGVXb3JrZmxvdyxcbiAgICBsYW5ndWFnZTogYXJyYXlGcm9tWWFyZ3MoYXJndi5sYW5ndWFnZSksXG4gICAgd2F0Y2g6IGFyZ3Yud2F0Y2ggYXMgYm9vbGVhbixcbiAgICBzdHJpY3Q6IGFyZ3Yuc3RyaWN0IGFzIGJvb2xlYW4sXG4gICAgdW5zdGFibGU6IGFycmF5RnJvbVlhcmdzKGFyZ3YudW5zdGFibGUpID8/IFtdLFxuICB9O1xufVxuXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gbWFpbihhcmdzOiBzdHJpbmdbXSkge1xuICB0cnkge1xuICAgIGNvbnN0IG9wdGlvbnMgPSBwYXJzZUNsaUFyZ3MoYXJncyk7XG5cbiAgICAvLyBQcm9jZXNzIHVuc3RhYmxlIGZlYXR1cmVzIGFuZCBlbWl0IGFwcHJvcHJpYXRlIHdhcm5pbmdzXG4gICAgb3B0aW9ucy51bnN0YWJsZSA9IHByb2Nlc3NVbnN0YWJsZUZlYXR1cmVzKG9wdGlvbnMudW5zdGFibGUpO1xuXG4gICAgYXdhaXQgcnVuKG9wdGlvbnMpO1xuICB9IGNhdGNoIChlcnI6IGFueSkge1xuICAgIGxvZ2dlci5lcnJvcihlcnIpO1xuICAgIHRocm93IGVycjtcbiAgfVxufVxuXG5hc3luYyBmdW5jdGlvbiBydW4ob3B0aW9uczogUmV0dXJuVHlwZTx0eXBlb2YgcGFyc2VDbGlBcmdzPikge1xuICBjb25zdCB0ZXN0c0Zyb21BcmdzID0gYXdhaXQgbmV3IEludGVncmF0aW9uVGVzdHMocGF0aC5yZXNvbHZlKG9wdGlvbnMuZGlyZWN0b3J5KSkuZnJvbUNsaU9wdGlvbnMob3B0aW9ucyk7XG5cbiAgLy8gTGlzdCBvbmx5IHByaW50cyB0aGUgZGlzY292ZXJlZCB0ZXN0c1xuICBpZiAob3B0aW9ucy5saXN0KSB7XG4gICAgcHJvY2Vzcy5zdGRvdXQud3JpdGUodGVzdHNGcm9tQXJncy5tYXAodCA9PiB0LmRpc2NvdmVyeVJlbGF0aXZlRmlsZU5hbWUpLmpvaW4oJ1xcbicpICsgJ1xcbicpO1xuICAgIHJldHVybjtcbiAgfVxuXG4gIGNvbnN0IHBvb2wgPSB3b3JrZXJwb29sLnBvb2wocGF0aC5qb2luKF9fZGlybmFtZSwgJy4uJywgJ2xpYicsICd3b3JrZXJzJywgJ2V4dHJhY3QnLCAnaW5kZXguanMnKSwge1xuICAgIG1heFdvcmtlcnM6IG9wdGlvbnMud2F0Y2ggPyAxIDogb3B0aW9ucy5tYXhXb3JrZXJzLFxuICB9KTtcblxuICBjb25zdCB0ZXN0c1RvUnVuOiBJbnRlZ1Rlc3RXb3JrZXJDb25maWdbXSA9IFtdO1xuICBsZXQgZGVzdHJ1Y3RpdmVDaGFuZ2VzOiBib29sZWFuID0gZmFsc2U7XG4gIGxldCBmYWlsZWRTbmFwc2hvdHM6IEludGVnVGVzdFdvcmtlckNvbmZpZ1tdID0gW107XG4gIGxldCB0ZXN0c1N1Y2NlZWRlZCA9IGZhbHNlO1xuICB2YWxpZGF0ZVdhdGNoQXJncyh7XG4gICAgLi4ub3B0aW9ucyxcbiAgICB0ZXN0UmVnaW9uczogb3B0aW9ucy5vcmlnaW5hbFJlZ2lvbnMsXG4gICAgdGVzdHM6IHRlc3RzRnJvbUFyZ3MsXG4gIH0pO1xuXG4gIHRyeSB7XG4gICAgaWYgKCFvcHRpb25zLndhdGNoKSB7XG4gICAgICAvLyBhbHdheXMgcnVuIHNuYXBzaG90IHRlc3RzLCBidXQgaWYgJy0tZm9yY2UnIGlzIHBhc3NlZCB0aGVuXG4gICAgICAvLyBydW4gaW50ZWdyYXRpb24gdGVzdHMgb24gYWxsIGZhaWxlZCB0ZXN0cywgbm90IGp1c3QgdGhvc2UgdGhhdFxuICAgICAgLy8gZmFpbGVkIHNuYXBzaG90IHRlc3RzXG4gICAgICBmYWlsZWRTbmFwc2hvdHMgPSBhd2FpdCBydW5TbmFwc2hvdFRlc3RzKHBvb2wsIHRlc3RzRnJvbUFyZ3MsIHtcbiAgICAgICAgcmV0YWluOiBvcHRpb25zLmluc3BlY3RGYWlsdXJlcyxcbiAgICAgICAgdmVyYm9zZTogb3B0aW9ucy52ZXJib3NlLFxuICAgICAgfSk7XG4gICAgICBmb3IgKGNvbnN0IGZhaWx1cmUgb2YgZmFpbGVkU25hcHNob3RzKSB7XG4gICAgICAgIGxvZ2dlci53YXJuaW5nKGBGYWlsZWQ6ICR7ZmFpbHVyZS5maWxlTmFtZX1gKTtcbiAgICAgICAgaWYgKGZhaWx1cmUuZGVzdHJ1Y3RpdmVDaGFuZ2VzICYmIGZhaWx1cmUuZGVzdHJ1Y3RpdmVDaGFuZ2VzLmxlbmd0aCA+IDApIHtcbiAgICAgICAgICBwcmludERlc3RydWN0aXZlQ2hhbmdlcyhmYWlsdXJlLmRlc3RydWN0aXZlQ2hhbmdlcyk7XG4gICAgICAgICAgZGVzdHJ1Y3RpdmVDaGFuZ2VzID0gdHJ1ZTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgICAgaWYgKCFvcHRpb25zLmZvcmNlKSB7XG4gICAgICAgIHRlc3RzVG9SdW4ucHVzaCguLi5mYWlsZWRTbmFwc2hvdHMpO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgLy8gaWYgYW55IG9mIHRoZSB0ZXN0IGZhaWxlZCBzbmFwc2hvdCB0ZXN0cywga2VlcCB0aG9zZSByZXN1bHRzXG4gICAgICAgIC8vIGFuZCBtZXJnZSB3aXRoIHRoZSByZXN0IG9mIHRoZSB0ZXN0cyBmcm9tIGFyZ3NcbiAgICAgICAgdGVzdHNUb1J1bi5wdXNoKC4uLm1lcmdlVGVzdHModGVzdHNGcm9tQXJncy5tYXAodCA9PiB0LmluZm8pLCBmYWlsZWRTbmFwc2hvdHMpKTtcbiAgICAgIH1cbiAgICB9IGVsc2Uge1xuICAgICAgdGVzdHNUb1J1bi5wdXNoKC4uLnRlc3RzRnJvbUFyZ3MubWFwKHQgPT4gdC5pbmZvKSk7XG4gICAgfVxuXG4gICAgLy8gcnVuIGludGVncmF0aW9uIHRlc3RzIGlmIGAtLXVwZGF0ZS1vbi1mYWlsZWRgIE9SIGAtLWZvcmNlYCBpcyB1c2VkXG4gICAgaWYgKG9wdGlvbnMucnVuVXBkYXRlT25GYWlsZWQgfHwgb3B0aW9ucy5mb3JjZSkge1xuICAgICAgY29uc3QgeyBzdWNjZXNzLCBtZXRyaWNzIH0gPSBhd2FpdCBydW5JbnRlZ3JhdGlvblRlc3RzKHtcbiAgICAgICAgcG9vbCxcbiAgICAgICAgdGVzdHM6IHRlc3RzVG9SdW4sXG4gICAgICAgIHJlZ2lvbnM6IG9wdGlvbnMudGVzdFJlZ2lvbnMsXG4gICAgICAgIHByb2ZpbGVzOiBvcHRpb25zLnByb2ZpbGVzLFxuICAgICAgICBjbGVhbjogb3B0aW9ucy5jbGVhbixcbiAgICAgICAgZHJ5UnVuOiBvcHRpb25zLmRyeVJ1bixcbiAgICAgICAgdmVyYm9zaXR5OiBvcHRpb25zLnZlcmJvc2l0eSxcbiAgICAgICAgdXBkYXRlV29ya2Zsb3c6IG9wdGlvbnMudXBkYXRlV29ya2Zsb3csXG4gICAgICAgIHdhdGNoOiBvcHRpb25zLndhdGNoLFxuICAgICAgfSk7XG4gICAgICB0ZXN0c1N1Y2NlZWRlZCA9IHN1Y2Nlc3M7XG5cbiAgICAgIGlmIChvcHRpb25zLmNsZWFuID09PSBmYWxzZSkge1xuICAgICAgICBsb2dnZXIud2FybmluZygnTm90IGNsZWFuaW5nIHVwIHN0YWNrcyBzaW5jZSBcIi0tbm8tY2xlYW5cIiB3YXMgdXNlZCcpO1xuICAgICAgfVxuXG4gICAgICBpZiAoQm9vbGVhbihvcHRpb25zLnZlcmJvc2UpKSB7XG4gICAgICAgIHByaW50TWV0cmljcyhtZXRyaWNzKTtcbiAgICAgIH1cblxuICAgICAgaWYgKCFzdWNjZXNzKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcignU29tZSBpbnRlZ3JhdGlvbiB0ZXN0cyBmYWlsZWQhJyk7XG4gICAgICB9XG4gICAgfSBlbHNlIGlmIChvcHRpb25zLndhdGNoKSB7XG4gICAgICBhd2FpdCB3YXRjaEludGVncmF0aW9uVGVzdChwb29sLCB7XG4gICAgICAgIHdhdGNoOiB0cnVlLFxuICAgICAgICB2ZXJib3NpdHk6IG9wdGlvbnMudmVyYm9zaXR5LFxuICAgICAgICAuLi50ZXN0c1RvUnVuWzBdLFxuICAgICAgICBwcm9maWxlOiBvcHRpb25zLnByb2ZpbGVzID8gb3B0aW9ucy5wcm9maWxlc1swXSA6IHVuZGVmaW5lZCxcbiAgICAgICAgcmVnaW9uOiBvcHRpb25zLnRlc3RSZWdpb25zWzBdLFxuICAgICAgfSk7XG4gICAgfVxuICB9IGZpbmFsbHkge1xuICAgIHZvaWQgcG9vbC50ZXJtaW5hdGUoKTtcbiAgfVxuXG4gIGlmIChkZXN0cnVjdGl2ZUNoYW5nZXMpIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoJ1NvbWUgY2hhbmdlcyB3ZXJlIGRlc3RydWN0aXZlIScpO1xuICB9XG4gIGlmIChmYWlsZWRTbmFwc2hvdHMubGVuZ3RoID4gMCkge1xuICAgIGxldCBtZXNzYWdlID0gJyc7XG4gICAgaWYgKCFvcHRpb25zLnJ1blVwZGF0ZU9uRmFpbGVkKSB7XG4gICAgICBtZXNzYWdlID0gJ1RvIHJlLXJ1biBmYWlsZWQgdGVzdHMgcnVuOiBpbnRlZy1ydW5uZXIgLS11cGRhdGUtb24tZmFpbGVkJztcbiAgICB9XG4gICAgaWYgKCF0ZXN0c1N1Y2NlZWRlZCkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKGBTb21lIHRlc3RzIGZhaWxlZCFcXG4ke21lc3NhZ2V9YCk7XG4gICAgfVxuICB9XG59XG5cbmZ1bmN0aW9uIHZhbGlkYXRlV2F0Y2hBcmdzKGFyZ3M6IHtcbiAgdGVzdHM6IEludGVnVGVzdFtdO1xuICB0ZXN0UmVnaW9ucz86IHN0cmluZ1tdO1xuICBwcm9maWxlcz86IHN0cmluZ1tdO1xuICBtYXhXb3JrZXJzOiBudW1iZXI7XG4gIGZvcmNlOiBib29sZWFuO1xuICBkcnlSdW46IGJvb2xlYW47XG4gIHVwZGF0ZVdvcmtmbG93OiBib29sZWFuO1xuICBydW5VcGRhdGVPbkZhaWxlZDogYm9vbGVhbjtcbiAgd2F0Y2g6IGJvb2xlYW47XG59KSB7XG4gIGlmIChhcmdzLndhdGNoKSB7XG4gICAgaWYgKFxuICAgICAgKGFyZ3MudGVzdFJlZ2lvbnMgJiYgYXJncy50ZXN0UmVnaW9ucy5sZW5ndGggPiAxKVxuICAgICAgICB8fCAoYXJncy5wcm9maWxlcyAmJiBhcmdzLnByb2ZpbGVzLmxlbmd0aCA+IDEpXG4gICAgICAgIHx8IGFyZ3MudGVzdHMubGVuZ3RoID4gMSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdSdW5uaW5nIHdpdGggd2F0Y2ggb25seSBzdXBwb3J0cyBhIHNpbmdsZSB0ZXN0LiBPbmx5IHByb3ZpZGUgYSBzaW5nbGUgb3B0aW9uJytcbiAgICAgICAgJ3RvIGAtLXByb2ZpbGVzYCBgLS1wYXJhbGxlbC1yZWdpb25zYCBgLS1tYXgtd29ya2VycycpO1xuICAgIH1cblxuICAgIGlmIChhcmdzLnJ1blVwZGF0ZU9uRmFpbGVkIHx8IGFyZ3MudXBkYXRlV29ya2Zsb3cgfHwgYXJncy5mb3JjZSB8fCBhcmdzLmRyeVJ1bikge1xuICAgICAgbG9nZ2VyLndhcm5pbmcoJ2FyZ3MgYC0tdXBkYXRlLW9uLWZhaWxlZGAsIGAtLXVwZGF0ZS13b3JrZmxvd2AsIGAtLWZvcmNlYCwgYC0tZHJ5LXJ1bmAgaGF2ZSBubyBlZmZlY3Qgd2hlbiBydW5uaW5nIHdpdGggYC0td2F0Y2hgJyk7XG4gICAgfVxuICB9XG59XG5cbmZ1bmN0aW9uIHByaW50RGVzdHJ1Y3RpdmVDaGFuZ2VzKGNoYW5nZXM6IERlc3RydWN0aXZlQ2hhbmdlW10pOiB2b2lkIHtcbiAgaWYgKGNoYW5nZXMubGVuZ3RoID4gMCkge1xuICAgIGxvZ2dlci53YXJuaW5nKCchISEgVGhpcyB0ZXN0IGNvbnRhaW5zICVzICEhIScsIGNoYWxrLmJvbGQoJ2Rlc3RydWN0aXZlIGNoYW5nZXMnKSk7XG4gICAgY2hhbmdlcy5mb3JFYWNoKGNoYW5nZSA9PiB7XG4gICAgICBsb2dnZXIud2FybmluZygnICAgIFN0YWNrOiAlcyAtIFJlc291cmNlOiAlcyAtIEltcGFjdDogJXMnLCBjaGFuZ2Uuc3RhY2tOYW1lLCBjaGFuZ2UubG9naWNhbElkLCBjaGFuZ2UuaW1wYWN0KTtcbiAgICB9KTtcbiAgICBsb2dnZXIud2FybmluZygnISEhIElmIHRoZXNlIGRlc3RydWN0aXZlIGNoYW5nZXMgYXJlIG5lY2Vzc2FyeSwgcGxlYXNlIGluZGljYXRlIHRoaXMgb24gdGhlIFBSICEhIScpO1xuICB9XG59XG5cbmZ1bmN0aW9uIHByaW50TWV0cmljcyhtZXRyaWNzOiBJbnRlZ1J1bm5lck1ldHJpY3NbXSk6IHZvaWQge1xuICBsb2dnZXIuaGlnaGxpZ2h0KCcgICAtLS0gSW50ZWdyYXRpb24gdGVzdCBtZXRyaWNzIC0tLScpO1xuICBjb25zdCBzb3J0ZWRNZXRyaWNzID0gbWV0cmljcy5zb3J0KChhLCBiKSA9PiBhLmR1cmF0aW9uIC0gYi5kdXJhdGlvbik7XG4gIHNvcnRlZE1ldHJpY3MuZm9yRWFjaChtZXRyaWMgPT4ge1xuICAgIGxvZ2dlci5wcmludCgnUHJvZmlsZSAlcyArIFJlZ2lvbiAlcyB0b3RhbCB0aW1lOiAlcycsIG1ldHJpYy5wcm9maWxlLCBtZXRyaWMucmVnaW9uLCBtZXRyaWMuZHVyYXRpb24pO1xuICAgIGNvbnN0IHNvcnRlZFRlc3RzID0gT2JqZWN0LmVudHJpZXMobWV0cmljLnRlc3RzKS5zb3J0KChhLCBiKSA9PiBhWzFdIC0gYlsxXSk7XG4gICAgc29ydGVkVGVzdHMuZm9yRWFjaCh0ZXN0ID0+IGxvZ2dlci5wcmludCgnICAlczogJXMnLCB0ZXN0WzBdLCB0ZXN0WzFdKSk7XG4gIH0pO1xufVxuXG4vKipcbiAqIFRyYW5zbGF0ZSBhIFlhcmdzIGlucHV0IGFycmF5IHRvIHNvbWV0aGluZyB0aGF0IG1ha2VzIG1vcmUgc2Vuc2UgaW4gYSBwcm9ncmFtbWluZyBsYW5ndWFnZVxuICogbW9kZWwgKHRlbGxpbmcgdGhlIGRpZmZlcmVuY2UgYmV0d2VlbiBhYnNlbmNlIGFuZCBhbiBlbXB0eSBhcnJheSlcbiAqXG4gKiAtIEFuIGVtcHR5IGFycmF5IGlzIHRoZSBkZWZhdWx0IGNhc2UsIG1lYW5pbmcgdGhlIHVzZXIgZGlkbid0IHBhc3MgYW55IGFyZ3VtZW50cy4gV2UgcmV0dXJuXG4gKiAgIHVuZGVmaW5lZC5cbiAqIC0gSWYgdGhlIHVzZXIgcGFzc2VkIGEgc2luZ2xlIGVtcHR5IHN0cmluZywgdGhleSBkaWQgc29tZXRoaW5nIGxpa2UgYC0tYXJyYXk9YCwgd2hpY2ggd2UnbGxcbiAqICAgdGFrZSB0byBtZWFuIHRoZXkgcGFzc2VkIGFuIGVtcHR5IGFycmF5LlxuICovXG5mdW5jdGlvbiBhcnJheUZyb21ZYXJncyh4czogc3RyaW5nW10pOiBzdHJpbmdbXSB8IHVuZGVmaW5lZCB7XG4gIGlmICh4cy5sZW5ndGggPT09IDApIHtcbiAgICByZXR1cm4gdW5kZWZpbmVkO1xuICB9XG4gIHJldHVybiB4cy5maWx0ZXIoeCA9PiB4ICE9PSAnJyk7XG59XG5cbi8qKlxuICogTWVyZ2UgdGhlIHRlc3RzIHdlIHJlY2VpdmVkIGZyb20gY29tbWFuZCBsaW5lIGFyZ3VtZW50cyB3aXRoXG4gKiB0ZXN0cyB0aGF0IGZhaWxlZCBzbmFwc2hvdCB0ZXN0cy4gVGhlIGZhaWxlZCBzbmFwc2hvdCB0ZXN0cyBoYXZlIGFkZGl0aW9uYWxcbiAqIGluZm9ybWF0aW9uIHRoYXQgd2Ugd2FudCB0byBrZWVwIHNvIHRoaXMgc2hvdWxkIG92ZXJyaWRlIGFueSB0ZXN0IGZyb20gYXJnc1xuICovXG5mdW5jdGlvbiBtZXJnZVRlc3RzKHRlc3RGcm9tQXJnczogSW50ZWdUZXN0SW5mb1tdLCBmYWlsZWRTbmFwc2hvdFRlc3RzOiBJbnRlZ1Rlc3RXb3JrZXJDb25maWdbXSk6IEludGVnVGVzdFdvcmtlckNvbmZpZ1tdIHtcbiAgY29uc3QgZmFpbGVkVGVzdE5hbWVzID0gbmV3IFNldChmYWlsZWRTbmFwc2hvdFRlc3RzLm1hcCh0ZXN0ID0+IHRlc3QuZmlsZU5hbWUpKTtcbiAgY29uc3QgZmluYWw6IEludGVnVGVzdFdvcmtlckNvbmZpZ1tdID0gZmFpbGVkU25hcHNob3RUZXN0cztcbiAgZmluYWwucHVzaCguLi50ZXN0RnJvbUFyZ3MuZmlsdGVyKHRlc3QgPT4gIWZhaWxlZFRlc3ROYW1lcy5oYXModGVzdC5maWxlTmFtZSkpKTtcbiAgcmV0dXJuIGZpbmFsO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gY2xpKGFyZ3M6IHN0cmluZ1tdID0gcHJvY2Vzcy5hcmd2LnNsaWNlKDIpKSB7XG4gIG1haW4oYXJncykudGhlbigpLmNhdGNoKCgpID0+IHtcbiAgICBwcm9jZXNzLmV4aXRDb2RlID0gMTtcbiAgfSk7XG59XG5cbi8qKlxuICogUmVhZCBDTEkgb3B0aW9ucyBmcm9tIGEgY29uZmlnIGZpbGUgaWYgcHJvdmlkZWQuXG4gKlxuICogQHJldHVybnMgcGFyc2VkIENMSSBjb25maWcgb3B0aW9uc1xuICovXG5mdW5jdGlvbiBjb25maWdGcm9tRmlsZShmaWxlTmFtZT86IHN0cmluZyk6IFJlY29yZDxzdHJpbmcsIGFueT4ge1xuICBpZiAoIWZpbGVOYW1lKSB7XG4gICAgcmV0dXJuIHt9O1xuICB9XG5cbiAgdHJ5IHtcbiAgICByZXR1cm4gSlNPTi5wYXJzZShmcy5yZWFkRmlsZVN5bmMoZmlsZU5hbWUsIHsgZW5jb2Rpbmc6ICd1dGYtOCcgfSkpO1xuICB9IGNhdGNoIHtcbiAgICByZXR1cm4ge307XG4gIH1cbn1cblxuLyoqXG4gKiBDb2VyY2UgZnVuY3Rpb24gZm9yIHlhcmdzIGFycmF5IG9wdGlvbnMgdG8gc3VwcG9ydCBjb21tYS1zZXBhcmF0ZWQgdmFsdWVzLlxuICogWWFyZ3MgZG9lc24ndCBuYXRpdmVseSBzcGxpdCBgLS1vcHRpb249YSxiLGNgIGludG8gWydhJywgJ2InLCAnYyddLlxuICovXG5mdW5jdGlvbiBzcGxpdEJ5Q29tbWEoeHM6IHN0cmluZ1tdKTogc3RyaW5nW10ge1xuICByZXR1cm4geHMuZmxhdE1hcCh4ID0+IHguc3BsaXQoJywnKSkubWFwKHggPT4geC50cmltKCkpO1xufVxuIl19