@aws-cdk/integ-runner
Version:
CDK Integration Testing Tool
274 lines • 43.9 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 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: [] })
.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: [] })
.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 })
.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: false, desc: 'If this is "true" then the stack update workflow will be disabled' })
.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: [] })
.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');
}
const requestedTests = fromFile
? (fs.readFileSync(fromFile, { encoding: 'utf8' })).split('\n').filter(x => x)
: (tests.length > 0 ? tests : undefined); // 'undefined' means no request
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'],
disableUpdateWorkflow: argv['disable-update-workflow'],
language: arrayFromYargs(argv.language),
watch: argv.watch,
};
}
async function main(args) {
const options = parseCliArgs(args);
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.disableUpdateWorkflow,
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.disableUpdateWorkflow || args.force || args.dryRun) {
logger.warning('args `--update-on-failed`, `--disable-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(err => {
logger.error(err);
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 {};
}
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xpLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiY2xpLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7O0FBaUJBLG9DQWlGQztBQUVELG9CQXVHQztBQTRFRCxrQkFLQztBQTVSRCxnRkFBZ0Y7QUFDaEYseUJBQXlCO0FBQ3pCLDZCQUE2QjtBQUM3QiwrQkFBK0I7QUFDL0IseUNBQXlDO0FBQ3pDLG1DQUFtQztBQUVuQyxrRUFBOEQ7QUFFOUQsdUNBQWtFO0FBQ2xFLHFFQUFvRTtBQUVwRSw2Q0FBNkM7QUFDN0MsK0NBQStDO0FBQy9DLGlFQUFpRTtBQUNqRSxNQUFNLEtBQUssR0FBRyxPQUFPLENBQUMsT0FBTyxDQUFDLENBQUM7QUFFL0IsU0FBZ0IsWUFBWSxDQUFDLE9BQWlCLEVBQUU7SUFDOUMsTUFBTSxJQUFJLEdBQUcsS0FBSztTQUNmLEtBQUssQ0FBQywrQkFBK0IsQ0FBQztTQUN0QyxNQUFNLENBQUMsUUFBUSxFQUFFO1FBQ2hCLE1BQU0sRUFBRSxJQUFJO1FBQ1osWUFBWSxFQUFFLGNBQWM7UUFDNUIsT0FBTyxFQUFFLG1CQUFtQjtRQUM1QixJQUFJLEVBQUUseUZBQXlGO0tBQ2hHLENBQUM7U0FDRCxNQUFNLENBQUMsT0FBTyxFQUFFLEVBQUUsSUFBSSxFQUFFLFNBQVMsRUFBRSxPQUFPLEVBQUUsS0FBSyxFQUFFLElBQUksRUFBRSxtQ0FBbUMsRUFBRSxDQUFDO1NBQy9GLE1BQU0sQ0FBQyxNQUFNLEVBQUUsRUFBRSxJQUFJLEVBQUUsU0FBUyxFQUFFLE9BQU8sRUFBRSxLQUFLLEVBQUUsSUFBSSxFQUFFLG9DQUFvQyxFQUFFLENBQUM7U0FDL0YsTUFBTSxDQUFDLE9BQU8sRUFBRSxFQUFFLElBQUksRUFBRSxTQUFTLEVBQUUsT0FBTyxFQUFFLElBQUksRUFBRSxJQUFJLEVBQUUsOEVBQThFLEVBQUUsQ0FBQztTQUN6SSxNQUFNLENBQUMsU0FBUyxFQUFFLEVBQUUsSUFBSSxFQUFFLFNBQVMsRUFBRSxPQUFPLEVBQUUsS0FBSyxF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RUFBRSxDQUFDLFlBQVksQ0FBQyxRQUFRLEVBQUUsRUFBRSxRQUFRLEVBQUUsT0FBTyxFQUFFLENBQUMsQ0FBQyxDQUFDO0lBQ3RFLENBQUM7SUFBQyxNQUFNLENBQUM7UUFDUCxPQUFPLEVBQUUsQ0FBQztJQUNaLENBQUM7QUFDSCxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLy8gRXhlcmNpc2UgYWxsIGludGVnIHN0YWNrcyBhbmQgaWYgdGhleSBkZXBsb3ksIHVwZGF0ZSB0aGUgZXhwZWN0ZWQgc3ludGggZmlsZXNcbmltcG9ydCAqIGFzIGZzIGZyb20gJ2ZzJztcbmltcG9ydCAqIGFzIHBhdGggZnJvbSAncGF0aCc7XG5pbXBvcnQgKiBhcyBjaGFsayBmcm9tICdjaGFsayc7XG5pbXBvcnQgKiBhcyB3b3JrZXJwb29sIGZyb20gJ3dvcmtlcnBvb2wnO1xuaW1wb3J0ICogYXMgbG9nZ2VyIGZyb20gJy4vbG9nZ2VyJztcbmltcG9ydCB0eXBlIHsgSW50ZWdUZXN0LCBJbnRlZ1Rlc3RJbmZvIH0gZnJvbSAnLi9ydW5uZXIvaW50ZWdyYXRpb24tdGVzdHMnO1xuaW1wb3J0IHsgSW50ZWdyYXRpb25UZXN0cyB9IGZyb20gJy4vcnVubmVyL2ludGVncmF0aW9uLXRlc3RzJztcbmltcG9ydCB0eXBlIHsgSW50ZWdSdW5uZXJNZXRyaWNzLCBJbnRlZ1Rlc3RXb3JrZXJDb25maWcsIERlc3RydWN0aXZlQ2hhbmdlIH0gZnJvbSAnLi93b3JrZXJzJztcbmltcG9ydCB7IHJ1blNuYXBzaG90VGVzdHMsIHJ1bkludGVncmF0aW9uVGVzdHMgfSBmcm9tICcuL3dvcmtlcnMnO1xuaW1wb3J0IHsgd2F0Y2hJbnRlZ3JhdGlvblRlc3QgfSBmcm9tICcuL3dvcmtlcnMvaW50ZWctd2F0Y2gtd29ya2VyJztcblxuLy8gaHR0cHM6Ly9naXRodWIuY29tL3lhcmdzL3lhcmdzL2lzc3Vlcy8xOTI5XG4vLyBodHRwczovL2dpdGh1Yi5jb20vZXZhbncvZXNidWlsZC9pc3N1ZXMvMTQ5MlxuLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIEB0eXBlc2NyaXB0LWVzbGludC9uby1yZXF1aXJlLWltcG9ydHNcbmNvbnN0IHlhcmdzID0gcmVxdWlyZSgneWFyZ3MnKTtcblxuZXhwb3J0IGZ1bmN0aW9uIHBhcnNlQ2xpQXJncyhhcmdzOiBzdHJpbmdbXSA9IFtdKSB7XG4gIGNvbnN0IGFyZ3YgPSB5YXJnc1xuICAgIC51c2FnZSgnVXNhZ2U6IGludGVnLXJ1bm5lciBbVEVTVC4uLl0nKVxuICAgIC5vcHRpb24oJ2NvbmZpZycsIHtcbiAgICAgIGNvbmZpZzogdHJ1ZSxcbiAgICAgIGNvbmZpZ1BhcnNlcjogY29uZmlnRnJvbUZpbGUsXG4gICAgICBkZWZhdWx0OiAnaW50ZWcuY29uZmlnLmpzb24nLFxuICAgICAgZGVzYzogJ0xvYWQgb3B0aW9ucyBmcm9tIGEgSlNPTiBjb25maWcgZmlsZS4gT3B0aW9ucyBwcm92aWRlZCBhcyBDTEkgYXJndW1lbnRzIHRha2UgcHJlY2VkZW50LicsXG4gICAgfSlcbiAgICAub3B0aW9uKCd3YXRjaCcsIHsgdHlwZTogJ2Jvb2xlYW4nLCBkZWZhdWx0OiBmYWxzZSwgZGVzYzogJ1BlcmZvcm0gaW50ZWcgdGVzdHMgaW4gd2F0Y2ggbW9kZScgfSlcbiAgICAub3B0aW9uKCdsaXN0JywgeyB0eXBlOiAnYm9vbGVhbicsIGRlZmF1bHQ6IGZhbHNlLCBkZXNjOiAnTGlzdCB0ZXN0cyBpbnN0ZWFkIG9mIHJ1bm5pbmcgdGhlbScgfSlcbiAgICAub3B0aW9uKCdjbGVhbicsIHsgdHlwZTogJ2Jvb2xlYW4nLCBkZWZhdWx0OiB0cnVlLCBkZXNjOiAnQ2xlYW4gdXAgYW5kIGRlbGV0ZSBzdGFjayBhZnRlciB0ZXN0IGlzIGNvbXBsZXRlZCAodXNlIC0tbm8tY2xlYW4gdG8gbmVnYXRlKScgfSlcbiAgICAub3B0aW9uKCd2ZXJib3NlJywgeyB0eXBlOiAnYm9vbGVhbicsIGRlZmF1bHQ6IGZhbHNlLCBhbGlhczogJ3YnLCBjb3VudDogdHJ1ZSwgZGVzYzogJ1ZlcmJvc2UgbG9ncyBhbmQgbWV0cmljcyBvbiBpbnRlZ3JhdGlvbiB0ZXN0cyBkdXJhdGlvbnMgKHNwZWNpZnkgbXVsdGlwbGUgdGltZXMgdG8gaW5jcmVhc2UgdmVyYm9zaXR5KScgfSlcbiAgICAub3B0aW9uKCdkcnktcnVuJywgeyB0eXBlOiAnYm9vbGVhbicsIGRlZmF1bHQ6IGZhbHNlLCBkZXNjOiAnZG8gbm90IGFjdHVhbGx5IGRlcGxveSB0aGUgc3RhY2suIGp1c3QgdXBkYXRlIHRoZSBzbmFwc2hvdCAobm90IHJlY29tbWVuZGVkISknIH0pXG4gICAgLm9wdGlvbigndXBkYXRlLW9uLWZhaWxlZCcsIHsgdHlwZTogJ2Jvb2xlYW4nLCBkZWZhdWx0OiBmYWxzZSwgZGVzYzogJ3JlcnVuIGludGVncmF0aW9uIHRlc3RzIGFuZCB1cGRhdGUgc25hcHNob3RzIGZvciBmYWlsZWQgdGVzdHMuJyB9KVxuICAgIC5vcHRpb24oJ2ZvcmNlJywgeyB0eXBlOiAnYm9vbGVhbicsIGRlZmF1bHQ6IGZhbHNlLCBkZXNjOiAnUmVydW4gYWxsIGludGVncmF0aW9uIHRlc3RzIGV2ZW4gaWYgdGVzdHMgYXJlIHBhc3NpbmcnIH0pXG4gICAgLm9wdGlvbigncGFyYWxsZWwtcmVnaW9ucycsIHsgdHlwZTogJ2FycmF5JywgZGVzYzogJ1Rlc3RzIGFyZSBydW4gaW4gcGFyYWxsZWwgYWNyb3NzIHRoZXNlIHJlZ2lvbnMuIFRvIHByZXZlbnQgdGVzdHMgZnJvbSBydW5uaW5nIGluIHBhcmFsbGVsLCBwcm92aWRlIG9ubHkgYSBzaW5nbGUgcmVnaW9uJywgZGVmYXVsdDogW10gfSlcbiAgICAub3B0aW9ucygnZGlyZWN0b3J5JywgeyB0eXBlOiAnc3RyaW5nJywgZGVmYXVsdDogJ3Rlc3QnLCBkZXNjOiAnc3RhcnRpbmcgZGlyZWN0b3J5IHRvIGRpc2NvdmVyIGludGVncmF0aW9uIHRlc3RzLiBUZXN0cyB3aWxsIGJlIGRpc2NvdmVyZWQgcmVjdXJzaXZlbHkgZnJvbSB0aGlzIGRpcmVjdG9yeScgfSlcbiAgICAub3B0aW9ucygncHJvZmlsZXMnLCB7IHR5cGU6ICdhcnJheScsIGRlc2M6ICdsaXN0IG9mIEFXUyBwcm9maWxlcyB0byB1c2UuIFRlc3RzIHdpbGwgYmUgcnVuIGluIHBhcmFsbGVsIGFjcm9zcyBlYWNoIHByb2ZpbGUrcmVnaW9ucycsIGRlZmF1bHQ6IFtdIH0pXG4gICAgLm9wdGlvbnMoJ21heC13b3JrZXJzJywgeyB0eXBlOiAnbnVtYmVyJywgZGVzYzogJ1RoZSBtYXggbnVtYmVyIG9mIHdvcmtlcnBvb2wgd29ya2VycyB0byB1c2Ugd2hlbiBydW5uaW5nIGludGVncmF0aW9uIHRlc3RzIGluIHBhcmFsbGVsJywgZGVmYXVsdDogMTYgfSlcbiAgICAub3B0aW9ucygnZXhjbHVkZScsIHsgdHlwZTogJ2Jvb2xlYW4nLCBkZXNjOiAnUnVuIGFsbCB0ZXN0cyBpbiB0aGUgZGlyZWN0b3J5LCBleGNlcHQgdGhlIHNwZWNpZmllZCBURVNUcycsIGRlZmF1bHQ6IGZhbHNlIH0pXG4gICAgLm9wdGlvbnMoJ2Zyb20tZmlsZScsIHsgdHlwZTogJ3N0cmluZycsIGRlc2M6ICdSZWFkIFRFU1QgbmFtZXMgZnJvbSBhIGZpbGUgKG9uZSBURVNUIHBlciBsaW5lKScgfSlcbiAgICAub3B0aW9uKCdpbnNwZWN0LWZhaWx1cmVzJywgeyB0eXBlOiAnYm9vbGVhbicsIGRlc2M6ICdLZWVwIHRoZSBpbnRlZyB0ZXN0IGNsb3VkIGFzc2VtYmx5IGlmIGEgZmFpbHVyZSBvY2N1cnMgZm9yIGluc3BlY3Rpb24nLCBkZWZhdWx0OiBmYWxzZSB9KVxuICAgIC5vcHRpb24oJ2Rpc2FibGUtdXBkYXRlLXdvcmtmbG93JywgeyB0eXBlOiAnYm9vbGVhbicsIGRlZmF1bHQ6IGZhbHNlLCBkZXNjOiAnSWYgdGhpcyBpcyBcInRydWVcIiB0aGVuIHRoZSBzdGFjayB1cGRhdGUgd29ya2Zsb3cgd2lsbCBiZSBkaXNhYmxlZCcgfSlcbiAgICAub3B0aW9uKCdsYW5ndWFnZScsIHtcbiAgICAgIGFsaWFzOiAnbCcsXG4gICAgICBkZWZhdWx0OiBbJ2phdmFzY3JpcHQnLCAndHlwZXNjcmlwdCcsICdweXRob24nLCAnZ28nXSxcbiAgICAgIGNob2ljZXM6IFsnamF2YXNjcmlwdCcsICd0eXBlc2NyaXB0JywgJ3B5dGhvbicsICdnbyddLFxuICAgICAgdHlwZTogJ2FycmF5JyxcbiAgICAgIG5hcmdzOiAxLFxuICAgICAgZGVzYzogJ1VzZSB0aGVzZSBwcmVzZXRzIHRvIHJ1biBpbnRlZ3JhdGlvbiB0ZXN0cyBmb3IgdGhlIHNlbGVjdGVkIGxhbmd1YWdlcycsXG4gICAgfSlcbiAgICAub3B0aW9uKCdhcHAnLCB7IHR5cGU6ICdzdHJpbmcnLCBkZWZhdWx0OiB1bmRlZmluZWQsIGRlc2M6ICdUaGUgY3VzdG9tIENMSSBjb21tYW5kIHRoYXQgd2lsbCBiZSB1c2VkIHRvIHJ1biB0aGUgdGVzdCBmaWxlcy4gWW91IGNhbiBpbmNsdWRlIHtmaWxlUGF0aH0gdG8gc3BlY2lmeSB3aGVyZSBpbiB0aGUgY29tbWFuZCB0aGUgdGVzdCBmaWxlIHBhdGggc2hvdWxkIGJlIGluc2VydGVkLiBFeGFtcGxlOiAtLWFwcD1cInB5dGhvbjMuOCB7ZmlsZVBhdGh9XCIuJyB9KVxuICAgIC5vcHRpb24oJ3Rlc3QtcmVnZXgnLCB7IHR5cGU6ICdhcnJheScsIGRlc2M6ICdEZXRlY3QgaW50ZWdyYXRpb24gdGVzdCBmaWxlcyBtYXRjaGluZyB0aGlzIEphdmFTY3JpcHQgcmVnZXggcGF0dGVybi4gSWYgdXNlZCBtdWx0aXBsZSB0aW1lcywgYWxsIGZpbGVzIG1hdGNoaW5nIGFueSBvbmUgb2YgdGhlIHBhdHRlcm5zIGFyZSBkZXRlY3RlZC4nLCBkZWZhdWx0OiBbXSB9KVxuICAgIC5zdHJpY3QoKVxuICAgIC5wYXJzZShhcmdzKTtcblxuICBjb25zdCB0ZXN0czogc3RyaW5nW10gPSBhcmd2Ll87XG4gIGNvbnN0IHBhcmFsbGVsUmVnaW9ucyA9IGFycmF5RnJvbVlhcmdzKGFyZ3ZbJ3BhcmFsbGVsLXJlZ2lvbnMnXSk7XG4gIGNvbnN0IHRlc3RSZWdpb25zOiBzdHJpbmdbXSA9IHBhcmFsbGVsUmVnaW9ucyA/PyBbJ3VzLWVhc3QtMScsICd1cy1lYXN0LTInLCAndXMtd2VzdC0yJ107XG4gIGNvbnN0IHByb2ZpbGVzID0gYXJyYXlGcm9tWWFyZ3MoYXJndi5wcm9maWxlcyk7XG4gIGNvbnN0IGZyb21GaWxlOiBzdHJpbmcgfCB1bmRlZmluZWQgPSBhcmd2Wydmcm9tLWZpbGUnXTtcbiAgY29uc3QgbWF4V29ya2VyczogbnVtYmVyID0gYXJndlsnbWF4LXdvcmtlcnMnXTtcbiAgY29uc3QgdmVyYm9zaXR5OiBudW1iZXIgPSBhcmd2LnZlcmJvc2U7XG4gIGNvbnN0IHZlcmJvc2U6IGJvb2xlYW4gPSB2ZXJib3NpdHkgPj0gMTtcblxuICBjb25zdCBudW1UZXN0cyA9IHRlc3RSZWdpb25zLmxlbmd0aCAqIChwcm9maWxlcyA/PyBbMV0pLmxlbmd0aDtcbiAgaWYgKG1heFdvcmtlcnMgPCBudW1UZXN0cykge1xuICAgIGxvZ2dlci53YXJuaW5nKCdZb3UgYXJlIGF0dGVtcHRpbmcgdG8gcnVuICVzIHRlc3RzIGluIHBhcmFsbGVsLCBidXQgb25seSBoYXZlICVzIHdvcmtlcnMuIE5vdCBhbGwgb2YgeW91ciBwcm9maWxlcytyZWdpb25zIHdpbGwgYmUgdXRpbGl6ZWQnLCBudW1UZXN0cywgbWF4V29ya2Vycyk7XG4gIH1cblxuICBpZiAodGVzdHMubGVuZ3RoID4gMCAmJiBmcm9tRmlsZSkge1xuICAgIHRocm93IG5ldyBFcnJvcignQSBsaXN0IG9mIHRlc3RzIGNhbm5vdCBiZSBwcm92aWRlZCBpZiBcIi0tZnJvbS1maWxlXCIgaXMgcHJvdmlkZWQnKTtcbiAgfVxuICBjb25zdCByZXF1ZXN0ZWRUZXN0cyA9IGZyb21GaWxlXG4gICAgPyAoZnMucmVhZEZpbGVTeW5jKGZyb21GaWxlLCB7IGVuY29kaW5nOiAndXRmOCcgfSkpLnNwbGl0KCdcXG4nKS5maWx0ZXIoeCA9PiB4KVxuICAgIDogKHRlc3RzLmxlbmd0aCA+IDAgPyB0ZXN0cyA6IHVuZGVmaW5lZCk7IC8vICd1bmRlZmluZWQnIG1lYW5zIG5vIHJlcXVlc3RcblxuICByZXR1cm4ge1xuICAgIHRlc3RzOiByZXF1ZXN0ZWRUZXN0cyxcbiAgICBhcHA6IGFyZ3YuYXBwIGFzIChzdHJpbmcgfCB1bmRlZmluZWQpLFxuICAgIHRlc3RSZWdleDogYXJyYXlGcm9tWWFyZ3MoYXJndlsndGVzdC1yZWdleCddKSxcbiAgICB0ZXN0UmVnaW9ucyxcbiAgICBvcmlnaW5hbFJlZ2lvbnM6IHBhcmFsbGVsUmVnaW9ucyxcbiAgICBwcm9maWxlcyxcbiAgICBydW5VcGRhdGVPbkZhaWxlZDogKGFyZ3ZbJ3VwZGF0ZS1vbi1mYWlsZWQnXSA/PyBmYWxzZSkgYXMgYm9vbGVhbixcbiAgICBmcm9tRmlsZSxcbiAgICBleGNsdWRlOiBhcmd2LmV4Y2x1ZGUgYXMgYm9vbGVhbixcbiAgICBtYXhXb3JrZXJzLFxuICAgIGxpc3Q6IGFyZ3YubGlzdCBhcyBib29sZWFuLFxuICAgIGRpcmVjdG9yeTogYXJndi5kaXJlY3RvcnkgYXMgc3RyaW5nLFxuICAgIGluc3BlY3RGYWlsdXJlczogYXJndlsnaW5zcGVjdC1mYWlsdXJlcyddIGFzIGJvb2xlYW4sXG4gICAgdmVyYm9zaXR5LFxuICAgIHZlcmJvc2UsXG4gICAgY2xlYW46IGFyZ3YuY2xlYW4gYXMgYm9vbGVhbixcbiAgICBmb3JjZTogYXJndi5mb3JjZSBhcyBib29sZWFuLFxuICAgIGRyeVJ1bjogYXJndlsnZHJ5LXJ1biddIGFzIGJvb2xlYW4sXG4gICAgZGlzYWJsZVVwZGF0ZVdvcmtmbG93OiBhcmd2WydkaXNhYmxlLXVwZGF0ZS13b3JrZmxvdyddIGFzIGJvb2xlYW4sXG4gICAgbGFuZ3VhZ2U6IGFycmF5RnJvbVlhcmdzKGFyZ3YubGFuZ3VhZ2UpLFxuICAgIHdhdGNoOiBhcmd2LndhdGNoIGFzIGJvb2xlYW4sXG4gIH07XG59XG5cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBtYWluKGFyZ3M6IHN0cmluZ1tdKSB7XG4gIGNvbnN0IG9wdGlvbnMgPSBwYXJzZUNsaUFyZ3MoYXJncyk7XG5cbiAgY29uc3QgdGVzdHNGcm9tQXJncyA9IGF3YWl0IG5ldyBJbnRlZ3JhdGlvblRlc3RzKHBhdGgucmVzb2x2ZShvcHRpb25zLmRpcmVjdG9yeSkpLmZyb21DbGlPcHRpb25zKG9wdGlvbnMpO1xuXG4gIC8vIExpc3Qgb25seSBwcmludHMgdGhlIGRpc2NvdmVyZWQgdGVzdHNcbiAgaWYgKG9wdGlvbnMubGlzdCkge1xuICAgIHByb2Nlc3Muc3Rkb3V0LndyaXRlKHRlc3RzRnJvbUFyZ3MubWFwKHQgPT4gdC5kaXNjb3ZlcnlSZWxhdGl2ZUZpbGVOYW1lKS5qb2luKCdcXG4nKSArICdcXG4nKTtcbiAgICByZXR1cm47XG4gIH1cblxuICBjb25zdCBwb29sID0gd29ya2VycG9vbC5wb29sKHBhdGguam9pbihfX2Rpcm5hbWUsICcuLicsICdsaWInLCAnd29ya2VycycsICdleHRyYWN0JywgJ2luZGV4LmpzJyksIHtcbiAgICBtYXhXb3JrZXJzOiBvcHRpb25zLndhdGNoID8gMSA6IG9wdGlvbnMubWF4V29ya2VycyxcbiAgfSk7XG5cbiAgY29uc3QgdGVzdHNUb1J1bjogSW50ZWdUZXN0V29ya2VyQ29uZmlnW10gPSBbXTtcbiAgbGV0IGRlc3RydWN0aXZlQ2hhbmdlczogYm9vbGVhbiA9IGZhbHNlO1xuICBsZXQgZmFpbGVkU25hcHNob3RzOiBJbnRlZ1Rlc3RXb3JrZXJDb25maWdbXSA9IFtdO1xuICBsZXQgdGVzdHNTdWNjZWVkZWQgPSBmYWxzZTtcbiAgdmFsaWRhdGVXYXRjaEFyZ3Moe1xuICAgIC4uLm9wdGlvbnMsXG4gICAgdGVzdFJlZ2lvbnM6IG9wdGlvbnMub3JpZ2luYWxSZWdpb25zLFxuICAgIHRlc3RzOiB0ZXN0c0Zyb21BcmdzLFxuICB9KTtcblxuICB0cnkge1xuICAgIGlmICghb3B0aW9ucy53YXRjaCkge1xuICAgICAgLy8gYWx3YXlzIHJ1biBzbmFwc2hvdCB0ZXN0cywgYnV0IGlmICctLWZvcmNlJyBpcyBwYXNzZWQgdGhlblxuICAgICAgLy8gcnVuIGludGVncmF0aW9uIHRlc3RzIG9uIGFsbCBmYWlsZWQgdGVzdHMsIG5vdCBqdXN0IHRob3NlIHRoYXRcbiAgICAgIC8vIGZhaWxlZCBzbmFwc2hvdCB0ZXN0c1xuICAgICAgZmFpbGVkU25hcHNob3RzID0gYXdhaXQgcnVuU25hcHNob3RUZXN0cyhwb29sLCB0ZXN0c0Zyb21BcmdzLCB7XG4gICAgICAgIHJldGFpbjogb3B0aW9ucy5pbnNwZWN0RmFpbHVyZXMsXG4gICAgICAgIHZlcmJvc2U6IG9wdGlvbnMudmVyYm9zZSxcbiAgICAgIH0pO1xuICAgICAgZm9yIChjb25zdCBmYWlsdXJlIG9mIGZhaWxlZFNuYXBzaG90cykge1xuICAgICAgICBsb2dnZXIud2FybmluZyhgRmFpbGVkOiAke2ZhaWx1cmUuZmlsZU5hbWV9YCk7XG4gICAgICAgIGlmIChmYWlsdXJlLmRlc3RydWN0aXZlQ2hhbmdlcyAmJiBmYWlsdXJlLmRlc3RydWN0aXZlQ2hhbmdlcy5sZW5ndGggPiAwKSB7XG4gICAgICAgICAgcHJpbnREZXN0cnVjdGl2ZUNoYW5nZXMoZmFpbHVyZS5kZXN0cnVjdGl2ZUNoYW5nZXMpO1xuICAgICAgICAgIGRlc3RydWN0aXZlQ2hhbmdlcyA9IHRydWU7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIGlmICghb3B0aW9ucy5mb3JjZSkge1xuICAgICAgICB0ZXN0c1RvUnVuLnB1c2goLi4uZmFpbGVkU25hcHNob3RzKTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIC8vIGlmIGFueSBvZiB0aGUgdGVzdCBmYWlsZWQgc25hcHNob3QgdGVzdHMsIGtlZXAgdGhvc2UgcmVzdWx0c1xuICAgICAgICAvLyBhbmQgbWVyZ2Ugd2l0aCB0aGUgcmVzdCBvZiB0aGUgdGVzdHMgZnJvbSBhcmdzXG4gICAgICAgIHRlc3RzVG9SdW4ucHVzaCguLi5tZXJnZVRlc3RzKHRlc3RzRnJvbUFyZ3MubWFwKHQgPT4gdC5pbmZvKSwgZmFpbGVkU25hcHNob3RzKSk7XG4gICAgICB9XG4gICAgfSBlbHNlIHtcbiAgICAgIHRlc3RzVG9SdW4ucHVzaCguLi50ZXN0c0Zyb21BcmdzLm1hcCh0ID0+IHQuaW5mbykpO1xuICAgIH1cblxuICAgIC8vIHJ1biBpbnRlZ3JhdGlvbiB0ZXN0cyBpZiBgLS11cGRhdGUtb24tZmFpbGVkYCBPUiBgLS1mb3JjZWAgaXMgdXNlZFxuICAgIGlmIChvcHRpb25zLnJ1blVwZGF0ZU9uRmFpbGVkIHx8IG9wdGlvbnMuZm9yY2UpIHtcbiAgICAgIGNvbnN0IHsgc3VjY2VzcywgbWV0cmljcyB9ID0gYXdhaXQgcnVuSW50ZWdyYXRpb25UZXN0cyh7XG4gICAgICAgIHBvb2wsXG4gICAgICAgIHRlc3RzOiB0ZXN0c1RvUnVuLFxuICAgICAgICByZWdpb25zOiBvcHRpb25zLnRlc3RSZWdpb25zLFxuICAgICAgICBwcm9maWxlczogb3B0aW9ucy5wcm9maWxlcyxcbiAgICAgICAgY2xlYW46IG9wdGlvbnMuY2xlYW4sXG4gICAgICAgIGRyeVJ1bjogb3B0aW9ucy5kcnlSdW4sXG4gICAgICAgIHZlcmJvc2l0eTogb3B0aW9ucy52ZXJib3NpdHksXG4gICAgICAgIHVwZGF0ZVdvcmtmbG93OiAhb3B0aW9ucy5kaXNhYmxlVXBkYXRlV29ya2Zsb3csXG4gICAgICAgIHdhdGNoOiBvcHRpb25zLndhdGNoLFxuICAgICAgfSk7XG4gICAgICB0ZXN0c1N1Y2NlZWRlZCA9IHN1Y2Nlc3M7XG5cbiAgICAgIGlmIChvcHRpb25zLmNsZWFuID09PSBmYWxzZSkge1xuICAgICAgICBsb2dnZXIud2FybmluZygnTm90IGNsZWFuaW5nIHVwIHN0YWNrcyBzaW5jZSBcIi0tbm8tY2xlYW5cIiB3YXMgdXNlZCcpO1xuICAgICAgfVxuXG4gICAgICBpZiAoQm9vbGVhbihvcHRpb25zLnZlcmJvc2UpKSB7XG4gICAgICAgIHByaW50TWV0cmljcyhtZXRyaWNzKTtcbiAgICAgIH1cblxuICAgICAgaWYgKCFzdWNjZXNzKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcignU29tZSBpbnRlZ3JhdGlvbiB0ZXN0cyBmYWlsZWQhJyk7XG4gICAgICB9XG4gICAgfSBlbHNlIGlmIChvcHRpb25zLndhdGNoKSB7XG4gICAgICBhd2FpdCB3YXRjaEludGVncmF0aW9uVGVzdChwb29sLCB7XG4gICAgICAgIHdhdGNoOiB0cnVlLFxuICAgICAgICB2ZXJib3NpdHk6IG9wdGlvbnMudmVyYm9zaXR5LFxuICAgICAgICAuLi50ZXN0c1RvUnVuWzBdLFxuICAgICAgICBwcm9maWxlOiBvcHRpb25zLnByb2ZpbGVzID8gb3B0aW9ucy5wcm9maWxlc1swXSA6IHVuZGVmaW5lZCxcbiAgICAgICAgcmVnaW9uOiBvcHRpb25zLnRlc3RSZWdpb25zWzBdLFxuICAgICAgfSk7XG4gICAgfVxuICB9IGZpbmFsbHkge1xuICAgIHZvaWQgcG9vbC50ZXJtaW5hdGUoKTtcbiAgfVxuXG4gIGlmIChkZXN0cnVjdGl2ZUNoYW5nZXMpIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoJ1NvbWUgY2hhbmdlcyB3ZXJlIGRlc3RydWN0aXZlIScpO1xuICB9XG4gIGlmIChmYWlsZWRTbmFwc2hvdHMubGVuZ3RoID4gMCkge1xuICAgIGxldCBtZXNzYWdlID0gJyc7XG4gICAgaWYgKCFvcHRpb25zLnJ1blVwZGF0ZU9uRmFpbGVkKSB7XG4gICAgICBtZXNzYWdlID0gJ1RvIHJlLXJ1biBmYWlsZWQgdGVzdHMgcnVuOiBpbnRlZy1ydW5uZXIgLS11cGRhdGUtb24tZmFpbGVkJztcbiAgICB9XG4gICAgaWYgKCF0ZXN0c1N1Y2NlZWRlZCkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKGBTb21lIHRlc3RzIGZhaWxlZCFcXG4ke21lc3NhZ2V9YCk7XG4gICAgfVxuICB9XG59XG5cbmZ1bmN0aW9uIHZhbGlkYXRlV2F0Y2hBcmdzKGFyZ3M6IHtcbiAgdGVzdHM6IEludGVnVGVzdFtdO1xuICB0ZXN0UmVnaW9ucz86IHN0cmluZ1tdO1xuICBwcm9maWxlcz86IHN0cmluZ1tdO1xuICBtYXhXb3JrZXJzOiBudW1iZXI7XG4gIGZvcmNlOiBib29sZWFuO1xuICBkcnlSdW46IGJvb2xlYW47XG4gIGRpc2FibGVVcGRhdGVXb3JrZmxvdzogYm9vbGVhbjtcbiAgcnVuVXBkYXRlT25GYWlsZWQ6IGJvb2xlYW47XG4gIHdhdGNoOiBib29sZWFuO1xufSkge1xuICBpZiAoYXJncy53YXRjaCkge1xuICAgIGlmIChcbiAgICAgIChhcmdzLnRlc3RSZWdpb25zICYmIGFyZ3MudGVzdFJlZ2lvbnMubGVuZ3RoID4gMSlcbiAgICAgICAgfHwgKGFyZ3MucHJvZmlsZXMgJiYgYXJncy5wcm9maWxlcy5sZW5ndGggPiAxKVxuICAgICAgICB8fCBhcmdzLnRlc3RzLmxlbmd0aCA+IDEpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcignUnVubmluZyB3aXRoIHdhdGNoIG9ubHkgc3VwcG9ydHMgYSBzaW5nbGUgdGVzdC4gT25seSBwcm92aWRlIGEgc2luZ2xlIG9wdGlvbicrXG4gICAgICAgICd0byBgLS1wcm9maWxlc2AgYC0tcGFyYWxsZWwtcmVnaW9uc2AgYC0tbWF4LXdvcmtlcnMnKTtcbiAgICB9XG5cbiAgICBpZiAoYXJncy5ydW5VcGRhdGVPbkZhaWxlZCB8fCBhcmdzLmRpc2FibGVVcGRhdGVXb3JrZmxvdyB8fCBhcmdzLmZvcmNlIHx8IGFyZ3MuZHJ5UnVuKSB7XG4gICAgICBsb2dnZXIud2FybmluZygnYXJncyBgLS11cGRhdGUtb24tZmFpbGVkYCwgYC0tZGlzYWJsZS11cGRhdGUtd29ya2Zsb3dgLCBgLS1mb3JjZWAsIGAtLWRyeS1ydW5gIGhhdmUgbm8gZWZmZWN0IHdoZW4gcnVubmluZyB3aXRoIGAtLXdhdGNoYCcpO1xuICAgIH1cbiAgfVxufVxuXG5mdW5jdGlvbiBwcmludERlc3RydWN0aXZlQ2hhbmdlcyhjaGFuZ2VzOiBEZXN0cnVjdGl2ZUNoYW5nZVtdKTogdm9pZCB7XG4gIGlmIChjaGFuZ2VzLmxlbmd0aCA+IDApIHtcbiAgICBsb2dnZXIud2FybmluZygnISEhIFRoaXMgdGVzdCBjb250YWlucyAlcyAhISEnLCBjaGFsay5ib2xkKCdkZXN0cnVjdGl2ZSBjaGFuZ2VzJykpO1xuICAgIGNoYW5nZXMuZm9yRWFjaChjaGFuZ2UgPT4ge1xuICAgICAgbG9nZ2VyLndhcm5pbmcoJyAgICBTdGFjazogJXMgLSBSZXNvdXJjZTogJXMgLSBJbXBhY3Q6ICVzJywgY2hhbmdlLnN0YWNrTmFtZSwgY2hhbmdlLmxvZ2ljYWxJZCwgY2hhbmdlLmltcGFjdCk7XG4gICAgfSk7XG4gICAgbG9nZ2VyLndhcm5pbmcoJyEhISBJZiB0aGVzZSBkZXN0cnVjdGl2ZSBjaGFuZ2VzIGFyZSBuZWNlc3NhcnksIHBsZWFzZSBpbmRpY2F0ZSB0aGlzIG9uIHRoZSBQUiAhISEnKTtcbiAgfVxufVxuXG5mdW5jdGlvbiBwcmludE1ldHJpY3MobWV0cmljczogSW50ZWdSdW5uZXJNZXRyaWNzW10pOiB2b2lkIHtcbiAgbG9nZ2VyLmhpZ2hsaWdodCgnICAgLS0tIEludGVncmF0aW9uIHRlc3QgbWV0cmljcyAtLS0nKTtcbiAgY29uc3Qgc29ydGVkTWV0cmljcyA9IG1ldHJpY3Muc29ydCgoYSwgYikgPT4gYS5kdXJhdGlvbiAtIGIuZHVyYXRpb24pO1xuICBzb3J0ZWRNZXRyaWNzLmZvckVhY2gobWV0cmljID0+IHtcbiAgICBsb2dnZXIucHJpbnQoJ1Byb2ZpbGUgJXMgKyBSZWdpb24gJXMgdG90YWwgdGltZTogJXMnLCBtZXRyaWMucHJvZmlsZSwgbWV0cmljLnJlZ2lvbiwgbWV0cmljLmR1cmF0aW9uKTtcbiAgICBjb25zdCBzb3J0ZWRUZXN0cyA9IE9iamVjdC5lbnRyaWVzKG1ldHJpYy50ZXN0cykuc29ydCgoYSwgYikgPT4gYVsxXSAtIGJbMV0pO1xuICAgIHNvcnRlZFRlc3RzLmZvckVhY2godGVzdCA9PiBsb2dnZXIucHJpbnQoJyAgJXM6ICVzJywgdGVzdFswXSwgdGVzdFsxXSkpO1xuICB9KTtcbn1cblxuLyoqXG4gKiBUcmFuc2xhdGUgYSBZYXJncyBpbnB1dCBhcnJheSB0byBzb21ldGhpbmcgdGhhdCBtYWtlcyBtb3JlIHNlbnNlIGluIGEgcHJvZ3JhbW1pbmcgbGFuZ3VhZ2VcbiAqIG1vZGVsICh0ZWxsaW5nIHRoZSBkaWZmZXJlbmNlIGJldHdlZW4gYWJzZW5jZSBhbmQgYW4gZW1wdHkgYXJyYXkpXG4gKlxuICogLSBBbiBlbXB0eSBhcnJheSBpcyB0aGUgZGVmYXVsdCBjYXNlLCBtZWFuaW5nIHRoZSB1c2VyIGRpZG4ndCBwYXNzIGFueSBhcmd1bWVudHMuIFdlIHJldHVyblxuICogICB1bmRlZmluZWQuXG4gKiAtIElmIHRoZSB1c2VyIHBhc3NlZCBhIHNpbmdsZSBlbXB0eSBzdHJpbmcsIHRoZXkgZGlkIHNvbWV0aGluZyBsaWtlIGAtLWFycmF5PWAsIHdoaWNoIHdlJ2xsXG4gKiAgIHRha2UgdG8gbWVhbiB0aGV5IHBhc3NlZCBhbiBlbXB0eSBhcnJheS5cbiAqL1xuZnVuY3Rpb24gYXJyYXlGcm9tWWFyZ3MoeHM6IHN0cmluZ1tdKTogc3RyaW5nW10gfCB1bmRlZmluZWQge1xuICBpZiAoeHMubGVuZ3RoID09PSAwKSB7XG4gICAgcmV0dXJuIHVuZGVmaW5lZDtcbiAgfVxuICByZXR1cm4geHMuZmlsdGVyKHggPT4geCAhPT0gJycpO1xufVxuXG4vKipcbiAqIE1lcmdlIHRoZSB0ZXN0cyB3ZSByZWNlaXZlZCBmcm9tIGNvbW1hbmQgbGluZSBhcmd1bWVudHMgd2l0aFxuICogdGVzdHMgdGhhdCBmYWlsZWQgc25hcHNob3QgdGVzdHMuIFRoZSBmYWlsZWQgc25hcHNob3QgdGVzdHMgaGF2ZSBhZGRpdGlvbmFsXG4gKiBpbmZvcm1hdGlvbiB0aGF0IHdlIHdhbnQgdG8ga2VlcCBzbyB0aGlzIHNob3VsZCBvdmVycmlkZSBhbnkgdGVzdCBmcm9tIGFyZ3NcbiAqL1xuZnVuY3Rpb24gbWVyZ2VUZXN0cyh0ZXN0RnJvbUFyZ3M6IEludGVnVGVzdEluZm9bXSwgZmFpbGVkU25hcHNob3RUZXN0czogSW50ZWdUZXN0V29ya2VyQ29uZmlnW10pOiBJbnRlZ1Rlc3RXb3JrZXJDb25maWdbXSB7XG4gIGNvbnN0IGZhaWxlZFRlc3ROYW1lcyA9IG5ldyBTZXQoZmFpbGVkU25hcHNob3RUZXN0cy5tYXAodGVzdCA9PiB0ZXN0LmZpbGVOYW1lKSk7XG4gIGNvbnN0IGZpbmFsOiBJbnRlZ1Rlc3RXb3JrZXJDb25maWdbXSA9IGZhaWxlZFNuYXBzaG90VGVzdHM7XG4gIGZpbmFsLnB1c2goLi4udGVzdEZyb21BcmdzLmZpbHRlcih0ZXN0ID0+ICFmYWlsZWRUZXN0TmFtZXMuaGFzKHRlc3QuZmlsZU5hbWUpKSk7XG4gIHJldHVybiBmaW5hbDtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGNsaShhcmdzOiBzdHJpbmdbXSA9IHByb2Nlc3MuYXJndi5zbGljZSgyKSkge1xuICBtYWluKGFyZ3MpLnRoZW4oKS5jYXRjaChlcnIgPT4ge1xuICAgIGxvZ2dlci5lcnJvcihlcnIpO1xuICAgIHByb2Nlc3MuZXhpdENvZGUgPSAxO1xuICB9KTtcbn1cblxuLyoqXG4gKiBSZWFkIENMSSBvcHRpb25zIGZyb20gYSBjb25maWcgZmlsZSBpZiBwcm92aWRlZC5cbiAqXG4gKiBAcmV0dXJucyBwYXJzZWQgQ0xJIGNvbmZpZyBvcHRpb25zXG4gKi9cbmZ1bmN0aW9uIGNvbmZpZ0Zyb21GaWxlKGZpbGVOYW1lPzogc3RyaW5nKTogUmVjb3JkPHN0cmluZywgYW55PiB7XG4gIGlmICghZmlsZU5hbWUpIHtcbiAgICByZXR1cm4ge307XG4gIH1cblxuICB0cnkge1xuICAgIHJldHVybiBKU09OLnBhcnNlKGZzLnJlYWRGaWxlU3luYyhmaWxlTmFtZSwgeyBlbmNvZGluZzogJ3V0Zi04JyB9KSk7XG4gIH0gY2F0Y2gge1xuICAgIHJldHVybiB7fTtcbiAgfVxufVxuIl19