UNPKG

@aws-cdk-testing/cli-integ

Version:

Integration tests for the AWS CDK CLI

244 lines 38.4 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.SamIntegrationTestFixture = void 0; exports.withSamIntegrationCdkApp = withSamIntegrationCdkApp; exports.withSamIntegrationFixture = withSamIntegrationFixture; exports.randomInteger = randomInteger; exports.shellWithAction = shellWithAction; const child_process = require("child_process"); const os = require("os"); const path = require("path"); const resources_1 = require("./resources"); const shell_1 = require("./shell"); const with_aws_1 = require("./with-aws"); const with_cdk_app_1 = require("./with-cdk-app"); const with_timeout_1 = require("./with-timeout"); /** * Higher order function to execute a block with a SAM Integration CDK app fixture */ function withSamIntegrationCdkApp(block) { return async (context) => { const randy = context.randomString; const stackNamePrefix = `cdktest-${randy}`; const integTestDir = path.join(os.tmpdir(), `cdk-integ-${randy}`); context.log(` Stack prefix: ${stackNamePrefix}\n`); context.log(` Test directory: ${integTestDir}\n`); context.log(` Region: ${context.aws.region}\n`); await (0, with_cdk_app_1.cloneDirectory)(path.join(resources_1.RESOURCES_DIR, 'cdk-apps', 'sam_cdk_integ_app'), integTestDir, context.output); const fixture = new SamIntegrationTestFixture(integTestDir, stackNamePrefix, context.output, context.aws, context.randomString); await fixture.ecrPublicLogin(); let success = true; try { const installationVersion = fixture.library.requestedVersion(); const alphaInstallationVersion = fixture.library.requestedAlphaVersion(); await (0, with_cdk_app_1.installNpmPackages)(fixture, { 'aws-cdk-lib': installationVersion, '@aws-cdk/aws-lambda-go-alpha': alphaInstallationVersion, '@aws-cdk/aws-lambda-python-alpha': alphaInstallationVersion, 'constructs': '^10', }); await block(fixture); } catch (e) { // We survive certain cases involving gopkg.in if (errorCausedByGoPkg(e.message)) { return; } success = false; throw e; } finally { if (process.env.INTEG_NO_CLEAN) { context.log(`Left test directory in '${integTestDir}' ($INTEG_NO_CLEAN)\n`); } else { await fixture.dispose(success); } } }; } /** * Return whether or not the error is being caused by gopkg.in being down * * Our Go build depends on https://gopkg.in/, which has errors pretty often * (every couple of days). It is run by a single volunteer. */ function errorCausedByGoPkg(error) { // The error is different depending on what request fails. Messages recognized: //////////////////////////////////////////////////////////////////// // go: github.com/aws/aws-lambda-go@v1.28.0 requires // gopkg.in/yaml.v3@v3.0.0-20200615113413-eeeca48fe776: invalid version: git ls-remote -q origin in /go/pkg/mod/cache/vcs/0901dc1ef67fcce1c9b3ae51078740de4a0e2dc673e720584ac302973af82f36: exit status 128: // remote: Cannot obtain refs from GitHub: cannot talk to GitHub: Get https://github.com/go-yaml/yaml.git/info/refs?service=git-upload-pack: net/http: request canceled (Client.Timeout exceeded while awaiting headers) // fatal: unable to access 'https://gopkg.in/yaml.v3/': The requested URL returned error: 502 //////////////////////////////////////////////////////////////////// // go: downloading github.com/aws/aws-lambda-go v1.28.0 // go: github.com/aws/aws-lambda-go@v1.28.0 requires // gopkg.in/yaml.v3@v3.0.0-20200615113413-eeeca48fe776: unrecognized import path "gopkg.in/yaml.v3": reading https://gopkg.in/yaml.v3?go-get=1: 502 Bad Gateway // server response: Cannot obtain refs from GitHub: cannot talk to GitHub: Get https://github.com/go-yaml/yaml.git/info/refs?service=git-upload-pack: net/http: request canceled (Client.Timeout exceeded while awaiting headers) //////////////////////////////////////////////////////////////////// // go: github.com/aws/aws-lambda-go@v1.28.0 requires // gopkg.in/yaml.v3@v3.0.0-20200615113413-eeeca48fe776: invalid version: git fetch -f origin refs/heads/*:refs/heads/* refs/tags/*:refs/tags/* in /go/pkg/mod/cache/vcs/0901dc1ef67fcce1c9b3ae51078740de4a0e2dc673e720584ac302973af82f36: exit status 128: // error: RPC failed; HTTP 502 curl 22 The requested URL returned error: 502 // fatal: the remote end hung up unexpectedly //////////////////////////////////////////////////////////////////// return (error.includes('gopkg\.in.*invalid version.*exit status 128') || error.match(/unrecognized import path[^\n]gopkg\.in/)); } /** * SAM Integration test fixture for CDK - SAM integration test cases */ function withSamIntegrationFixture(block) { return (0, with_aws_1.withAws)((0, with_timeout_1.withTimeout)(with_cdk_app_1.EXTENDED_TEST_TIMEOUT_S, withSamIntegrationCdkApp(block))); } class SamIntegrationTestFixture extends with_cdk_app_1.TestFixture { async samShell(command, filter, action, options = {}) { return shellWithAction(command, filter, action, { outputs: [this.output], cwd: path.join(this.integTestDir, 'cdk.out').toString(), ...options, }); } async samBuild(stackName) { const fullStackName = this.fullStackName(stackName); const templatePath = path.join(this.integTestDir, 'cdk.out', `${fullStackName}.template.json`); const args = ['--template', templatePath.toString()]; return this.samShell(['sam', 'build', ...args]); } async samLocalStartApi(stackName, isBuilt, port, apiPath) { const fullStackName = this.fullStackName(stackName); const templatePath = path.join(this.integTestDir, 'cdk.out', `${fullStackName}.template.json`); const args = isBuilt ? [] : ['--template', templatePath.toString()]; args.push('--port'); args.push(port.toString()); // https://github.com/aws/aws-sam-cli/pull/7892 args.push('--no-memory-limit'); // "Press Ctrl+C to quit" looks to be printed by a Flask server built into SAM CLI. return this.samShell(['sam', 'local', 'start-api', ...args], 'Press CTRL+C to quit', () => { return fetch(`http://127.0.0.1:${port}${apiPath}`).then(resp => resp.json()).catch(error => { throw new Error(`Failed to invoke api path ${apiPath} on port ${port} with error ${error}`); }); }); } /** * Cleanup leftover stacks and buckets */ async dispose(success) { // If the tests completed successfully, happily delete the fixture // (otherwise leave it for humans to inspect) if (success) { const cleaned = (0, shell_1.rimraf)(this.integTestDir); if (!cleaned) { // eslint-disable-next-line no-console console.error(`Failed to clean up ${this.integTestDir} due to permissions issues (Docker running as root?)`); } } } } exports.SamIntegrationTestFixture = SamIntegrationTestFixture; function randomInteger(min, max) { return Math.floor(Math.random() * (max - min) + min); } /** * A shell command that does what you want * * Is platform-aware, handles errors nicely. */ async function shellWithAction(command, filter, action, options = {}, actionTimeoutSeconds = 600) { if (options.modEnv && options.env) { throw new Error('Use either env or modEnv but not both'); } const writeToOutputs = (x) => { for (const output of options.outputs ?? []) { output.write(x); } }; writeToOutputs(`💻 ${command.join(' ')}\n`); const env = options.env ?? (options.modEnv ? { ...process.env, ...options.modEnv } : undefined); const child = child_process.spawn(command.join(' '), [], { ...options, env, // Need this for Windows where we want .cmd and .bat to be found as well. shell: true, stdio: ['ignore', 'pipe', 'pipe'], }); return new Promise((resolve, reject) => { const out = new Array(); const stdout = new Array(); const stderr = new Array(); let actionSucceeded = false; let actionOutput; let actionExecuted = false; async function maybeExecuteAction(chunk) { out.push(Buffer.from(chunk)); if (!actionExecuted && typeof filter === 'string' && Buffer.concat(out).toString('utf-8').includes(filter) && typeof action === 'function') { actionExecuted = true; writeToOutputs('before executing action\n'); try { const output = await action(); writeToOutputs(`action output is ${JSON.stringify(output)}\n`); actionOutput = output; actionSucceeded = true; } catch (error) { writeToOutputs(`action error is ${error}\n`); actionSucceeded = false; actionOutput = error; } finally { writeToOutputs('terminate sam sub process\n'); killSubProcess(child, command.join(' ')); } } } if (typeof filter === 'string' && typeof action === 'function') { // Reject with an error if an action is configured, but the filter failed // to show up in the output before the timeout occurred. setTimeout(() => { if (!actionExecuted) { reject(new Error(`Timed out waiting for filter ${JSON.stringify(filter)} to appear in command output after ${actionTimeoutSeconds} seconds\nOutput so far:\n${Buffer.concat(out).toString('utf-8')}`)); killSubProcess(child, command.join(' ')); } }, actionTimeoutSeconds * 1_000).unref(); } child.stdout.on('data', chunk => { writeToOutputs(chunk); stdout.push(chunk); void maybeExecuteAction(chunk); }); child.stderr.on('data', chunk => { writeToOutputs(chunk); if (options.captureStderr ?? true) { stderr.push(chunk); } void maybeExecuteAction(chunk); }); child.once('error', reject); // Wait for 'exit' instead of close, don't care about reading the streams all the way to the end child.once('exit', (code, signal) => { writeToOutputs(`Subprocess has exited with code ${code}, signal ${signal}\n`); const output = (Buffer.concat(stdout).toString('utf-8') + Buffer.concat(stderr).toString('utf-8')).trim(); if (code == null || code === 0 || options.allowErrExit) { let result = new Array(); result.push(actionOutput); result.push(output); resolve({ actionSucceeded: actionSucceeded, actionOutput: actionOutput, shellOutput: output, }); } else { reject(new Error(`'${command.join(' ')}' exited with error code ${code}. Output: \n${output}`)); } }); }); } function killSubProcess(child, command) { /** * Check if the sub process is running in container, so child_process.spawn will * create multiple processes, and to kill all of them we need to run different logic */ child.kill('SIGINT'); child_process.exec(`for pid in $(ps -ef | grep "${command}" | awk '{print $2}'); do kill -2 $pid; done`); } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoid2l0aC1zYW0uanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJ3aXRoLXNhbS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUEwQkEsNERBK0NDO0FBa0NELDhEQUVDO0FBb0RELHNDQUVDO0FBT0QsMENBd0dDO0FBbFJELCtDQUErQztBQUMvQyx5QkFBeUI7QUFDekIsNkJBQTZCO0FBRTdCLDJDQUE0QztBQUU1QyxtQ0FBaUM7QUFFakMseUNBQXFDO0FBQ3JDLGlEQUt3QjtBQUN4QixpREFBNkM7QUFRN0M7O0dBRUc7QUFDSCxTQUFnQix3QkFBd0IsQ0FBcUMsS0FBNEQ7SUFDdkksT0FBTyxLQUFLLEVBQUUsT0FBVSxFQUFFLEVBQUU7UUFDMUIsTUFBTSxLQUFLLEdBQUcsT0FBTyxDQUFDLFlBQVksQ0FBQztRQUNuQyxNQUFNLGVBQWUsR0FBRyxXQUFXLEtBQUssRUFBRSxDQUFDO1FBQzNDLE1BQU0sWUFBWSxHQUFHLElBQUksQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDLE1BQU0sRUFBRSxFQUFFLGFBQWEsS0FBSyxFQUFFLENBQUMsQ0FBQztRQUVsRSxPQUFPLENBQUMsR0FBRyxDQUFDLG9CQUFvQixlQUFlLElBQUksQ0FBQyxDQUFDO1FBQ3JELE9BQU8sQ0FBQyxHQUFHLENBQUMsb0JBQW9CLFlBQVksSUFBSSxDQUFDLENBQUM7UUFDbEQsT0FBTyxDQUFDLEdBQUcsQ0FBQyxvQkFBb0IsT0FBTyxDQUFDLEdBQUcsQ0FBQyxNQUFNLElBQUksQ0FBQyxDQUFDO1FBRXhELE1BQU0sSUFBQSw2QkFBYyxFQUFDLElBQUksQ0FBQyxJQUFJLENBQUMseUJBQWEsRUFBRSxVQUFVLEVBQUUsbUJBQW1CLENBQUMsRUFBRSxZQUFZLEVBQUUsT0FBTyxDQUFDLE1BQU0sQ0FBQyxDQUFDO1FBQzlHLE1BQU0sT0FBTyxHQUFHLElBQUkseUJBQXlCLENBQzNDLFlBQVksRUFDWixlQUFlLEVBQ2YsT0FBTyxDQUFDLE1BQU0sRUFDZCxPQUFPLENBQUMsR0FBRyxFQUNYLE9BQU8sQ0FBQyxZQUFZLENBQUMsQ0FBQztRQUN4QixNQUFNLE9BQU8sQ0FBQyxjQUFjLEVBQUUsQ0FBQztRQUUvQixJQUFJLE9BQU8sR0FBRyxJQUFJLENBQUM7UUFDbkIsSUFBSSxDQUFDO1lBQ0gsTUFBTSxtQkFBbUIsR0FBRyxPQUFPLENBQUMsT0FBTyxDQUFDLGdCQUFnQixFQUFFLENBQUM7WUFFL0QsTUFBTSx3QkFBd0IsR0FBRyxPQUFPLENBQUMsT0FBTyxDQUFDLHFCQUFxQixFQUFFLENBQUM7WUFDekUsTUFBTSxJQUFBLGlDQUFrQixFQUFDLE9BQU8sRUFBRTtnQkFDaEMsYUFBYSxFQUFFLG1CQUFtQjtnQkFDbEMsOEJBQThCLEVBQUUsd0JBQXdCO2dCQUN4RCxrQ0FBa0MsRUFBRSx3QkFBd0I7Z0JBQzVELFlBQVksRUFBRSxLQUFLO2FBQ3BCLENBQUMsQ0FBQztZQUVILE1BQU0sS0FBSyxDQUFDLE9BQU8sQ0FBQyxDQUFDO1FBQ3ZCLENBQUM7UUFBQyxPQUFPLENBQU0sRUFBRSxDQUFDO1lBQ2hCLDhDQUE4QztZQUM5QyxJQUFJLGtCQUFrQixDQUFDLENBQUMsQ0FBQyxPQUFPLENBQUMsRUFBRSxDQUFDO2dCQUNsQyxPQUFPO1lBQ1QsQ0FBQztZQUNELE9BQU8sR0FBRyxLQUFLLENBQUM7WUFDaEIsTUFBTSxDQUFDLENBQUM7UUFDVixDQUFDO2dCQUFTLENBQUM7WUFDVCxJQUFJLE9BQU8sQ0FBQyxHQUFHLENBQUMsY0FBYyxFQUFFLENBQUM7Z0JBQy9CLE9BQU8sQ0FBQyxHQUFHLENBQUMsMkJBQTJCLFlBQVksdUJBQXVCLENBQUMsQ0FBQztZQUM5RSxDQUFDO2lCQUFNLENBQUM7Z0JBQ04sTUFBTSxPQUFPLENBQUMsT0FBTyxDQUFDLE9BQU8sQ0FBQyxDQUFDO1lBQ2pDLENBQUM7UUFDSCxDQUFDO0lBQ0gsQ0FBQyxDQUFDO0FBQ0osQ0FBQztBQUVEOzs7OztHQUtHO0FBQ0gsU0FBUyxrQkFBa0IsQ0FBQyxLQUFhO0lBQ3ZDLCtFQUErRTtJQUMvRSxvRUFBb0U7SUFDcEUsdURBQXVEO0lBQ3ZELG1OQUFtTjtJQUNuTiwrTkFBK047SUFDL04sb0dBQW9HO0lBQ3BHLG9FQUFvRTtJQUNwRSwwREFBMEQ7SUFDMUQsdURBQXVEO0lBQ3ZELHNLQUFzSztJQUN0Syx3T0FBd087SUFDeE8sb0VBQW9FO0lBQ3BFLHVEQUF1RDtJQUN2RCxpUUFBaVE7SUFDalEsbUZBQW1GO0lBQ25GLG9EQUFvRDtJQUNwRCxvRUFBb0U7SUFFcEUsT0FBTyxDQUFDLEtBQUssQ0FBQyxRQUFRLENBQUMsNkNBQTZDLENBQUM7V0FDaEUsS0FBSyxDQUFDLEtBQUssQ0FBQyx3Q0FBd0MsQ0FBQyxDQUFDLENBQUM7QUFDOUQsQ0FBQztBQUVEOztHQUVHO0FBQ0gsU0FBZ0IseUJBQXlCLENBQUMsS0FBNEQ7SUFDcEcsT0FBTyxJQUFBLGtCQUFPLEVBQUMsSUFBQSwwQkFBVyxFQUFDLHNDQUF1QixFQUFFLHdCQUF3QixDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsQ0FBQztBQUN4RixDQUFDO0FBRUQsTUFBYSx5QkFBMEIsU0FBUSwwQkFBVztJQUNqRCxLQUFLLENBQUMsUUFBUSxDQUFDLE9BQWlCLEVBQUUsTUFBZSxFQUFFLE1BQWtCLEVBQUUsVUFBZ0QsRUFBRTtRQUM5SCxPQUFPLGVBQWUsQ0FBQyxPQUFPLEVBQUUsTUFBTSxFQUFFLE1BQU0sRUFBRTtZQUM5QyxPQUFPLEVBQUUsQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDO1lBQ3RCLEdBQUcsRUFBRSxJQUFJLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxZQUFZLEVBQUUsU0FBUyxDQUFDLENBQUMsUUFBUSxFQUFFO1lBQ3ZELEdBQUcsT0FBTztTQUNYLENBQUMsQ0FBQztJQUNMLENBQUM7SUFFTSxLQUFLLENBQUMsUUFBUSxDQUFDLFNBQWlCO1FBQ3JDLE1BQU0sYUFBYSxHQUFHLElBQUksQ0FBQyxhQUFhLENBQUMsU0FBUyxDQUFDLENBQUM7UUFDcEQsTUFBTSxZQUFZLEdBQUcsSUFBSSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsWUFBWSxFQUFFLFNBQVMsRUFBRSxHQUFHLGFBQWEsZ0JBQWdCLENBQUMsQ0FBQztRQUMvRixNQUFNLElBQUksR0FBRyxDQUFDLFlBQVksRUFBRSxZQUFZLENBQUMsUUFBUSxFQUFFLENBQUMsQ0FBQztRQUNyRCxPQUFPLElBQUksQ0FBQyxRQUFRLENBQUMsQ0FBQyxLQUFLLEVBQUUsT0FBTyxFQUFFLEdBQUcsSUFBSSxDQUFDLENBQUMsQ0FBQztJQUNsRCxDQUFDO0lBRU0sS0FBSyxDQUFDLGdCQUFnQixDQUFDLFNBQWlCLEVBQUUsT0FBZ0IsRUFBRSxJQUFZLEVBQUUsT0FBZTtRQUM5RixNQUFNLGFBQWEsR0FBRyxJQUFJLENBQUMsYUFBYSxDQUFDLFNBQVMsQ0FBQyxDQUFDO1FBQ3BELE1BQU0sWUFBWSxHQUFHLElBQUksQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLFlBQVksRUFBRSxTQUFTLEVBQUUsR0FBRyxhQUFhLGdCQUFnQixDQUFDLENBQUM7UUFDL0YsTUFBTSxJQUFJLEdBQUcsT0FBTyxDQUFBLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDLENBQUMsWUFBWSxFQUFFLFlBQVksQ0FBQyxRQUFRLEVBQUUsQ0FBQyxDQUFDO1FBQ25FLElBQUksQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLENBQUM7UUFDcEIsSUFBSSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsUUFBUSxFQUFFLENBQUMsQ0FBQztRQUUzQiwrQ0FBK0M7UUFDL0MsSUFBSSxDQUFDLElBQUksQ0FBQyxtQkFBbUIsQ0FBQyxDQUFDO1FBRS9CLG1GQUFtRjtRQUNuRixPQUFPLElBQUksQ0FBQyxRQUFRLENBQUMsQ0FBQyxLQUFLLEVBQUUsT0FBTyxFQUFFLFdBQVcsRUFBRSxHQUFHLElBQUksQ0FBQyxFQUFFLHNCQUFzQixFQUFFLEdBQUUsRUFBRTtZQUN2RixPQUFPLEtBQUssQ0FBQyxvQkFBb0IsSUFBSSxHQUFHLE9BQU8sRUFBRSxDQUFDLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUMsSUFBSSxDQUFDLElBQUksRUFBRSxDQUFDLENBQUMsS0FBSyxDQUFDLEtBQUssQ0FBQyxFQUFFO2dCQUN6RixNQUFNLElBQUksS0FBSyxDQUFDLDZCQUE2QixPQUFPLFlBQVksSUFBSSxlQUFlLEtBQUssRUFBRSxDQUFDLENBQUM7WUFDOUYsQ0FBQyxDQUFDLENBQUM7UUFDTCxDQUFDLENBQUMsQ0FBQztJQUNMLENBQUM7SUFFRDs7T0FFRztJQUNJLEtBQUssQ0FBQyxPQUFPLENBQUMsT0FBZ0I7UUFDbkMsa0VBQWtFO1FBQ2xFLDZDQUE2QztRQUM3QyxJQUFJLE9BQU8sRUFBRSxDQUFDO1lBQ1osTUFBTSxPQUFPLEdBQUcsSUFBQSxjQUFNLEVBQUMsSUFBSSxDQUFDLFlBQVksQ0FBQyxDQUFDO1lBQzFDLElBQUksQ0FBQyxPQUFPLEVBQUUsQ0FBQztnQkFDYixzQ0FBc0M7Z0JBQ3RDLE9BQU8sQ0FBQyxLQUFLLENBQUMsc0JBQXNCLElBQUksQ0FBQyxZQUFZLHNEQUFzRCxDQUFDLENBQUM7WUFDL0csQ0FBQztRQUNILENBQUM7SUFDSCxDQUFDO0NBQ0Y7QUFoREQsOERBZ0RDO0FBRUQsU0FBZ0IsYUFBYSxDQUFDLEdBQVcsRUFBRSxHQUFXO0lBQ3BELE9BQU8sSUFBSSxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsTUFBTSxFQUFFLEdBQUcsQ0FBQyxHQUFHLEdBQUcsR0FBRyxDQUFDLEdBQUcsR0FBRyxDQUFDLENBQUM7QUFDdkQsQ0FBQztBQUVEOzs7O0dBSUc7QUFDSSxLQUFLLFVBQVUsZUFBZSxDQUNuQyxPQUFpQixFQUNqQixNQUFlLEVBQ2YsTUFBMkIsRUFDM0IsVUFBd0IsRUFBRSxFQUMxQix1QkFBK0IsR0FBRztJQUVsQyxJQUFJLE9BQU8sQ0FBQyxNQUFNLElBQUksT0FBTyxDQUFDLEdBQUcsRUFBRSxDQUFDO1FBQ2xDLE1BQU0sSUFBSSxLQUFLLENBQUMsdUNBQXVDLENBQUMsQ0FBQztJQUMzRCxDQUFDO0lBRUQsTUFBTSxjQUFjLEdBQUcsQ0FBQyxDQUFTLEVBQUUsRUFBRTtRQUNuQyxLQUFLLE1BQU0sTUFBTSxJQUFJLE9BQU8sQ0FBQyxPQUFPLElBQUksRUFBRSxFQUFFLENBQUM7WUFDM0MsTUFBTSxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsQ0FBQztRQUNsQixDQUFDO0lBQ0gsQ0FBQyxDQUFDO0lBQ0YsY0FBYyxDQUFDLE1BQU0sT0FBTyxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLENBQUM7SUFFNUMsTUFBTSxHQUFHLEdBQUcsT0FBTyxDQUFDLEdBQUcsSUFBSSxDQUFDLE9BQU8sQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLEVBQUUsR0FBRyxPQUFPLENBQUMsR0FBRyxFQUFFLEdBQUcsT0FBTyxDQUFDLE1BQU0sRUFBRSxDQUFDLENBQUMsQ0FBQyxTQUFTLENBQUMsQ0FBQztJQUVoRyxNQUFNLEtBQUssR0FBRyxhQUFhLENBQUMsS0FBSyxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLEVBQUUsRUFBRSxFQUFFO1FBQ3ZELEdBQUcsT0FBTztRQUNWLEdBQUc7UUFDSCx5RUFBeUU7UUFDekUsS0FBSyxFQUFFLElBQUk7UUFDWCxLQUFLLEVBQUUsQ0FBQyxRQUFRLEVBQUUsTUFBTSxFQUFFLE1BQU0sQ0FBQztLQUNsQyxDQUFDLENBQUM7SUFFSCxPQUFPLElBQUksT0FBTyxDQUFlLENBQUMsT0FBTyxFQUFFLE1BQU0sRUFBRSxFQUFFO1FBQ25ELE1BQU0sR0FBRyxHQUFHLElBQUksS0FBSyxFQUFVLENBQUM7UUFDaEMsTUFBTSxNQUFNLEdBQUcsSUFBSSxLQUFLLEVBQVUsQ0FBQztRQUNuQyxNQUFNLE1BQU0sR0FBRyxJQUFJLEtBQUssRUFBVSxDQUFDO1FBQ25DLElBQUksZUFBZSxHQUFHLEtBQUssQ0FBQztRQUM1QixJQUFJLFlBQWlCLENBQUM7UUFDdEIsSUFBSSxjQUFjLEdBQUcsS0FBSyxDQUFDO1FBRTNCLEtBQUssVUFBVSxrQkFBa0IsQ0FBQyxLQUFVO1lBQzFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDO1lBQzdCLElBQUksQ0FBQyxjQUFjLElBQUksT0FBTyxNQUFNLEtBQUssUUFBUSxJQUFJLE1BQU0sQ0FBQyxNQUFNLENBQUMsR0FBRyxDQUFDLENBQUMsUUFBUSxDQUFDLE9BQU8sQ0FBQyxDQUFDLFFBQVEsQ0FBQyxNQUFNLENBQUMsSUFBSSxPQUFPLE1BQU0sS0FBSyxVQUFVLEVBQUUsQ0FBQztnQkFDM0ksY0FBYyxHQUFHLElBQUksQ0FBQztnQkFDdEIsY0FBYyxDQUFDLDJCQUEyQixDQUFDLENBQUM7Z0JBQzVDLElBQUksQ0FBQztvQkFDSCxNQUFNLE1BQU0sR0FBRyxNQUFNLE1BQU0sRUFBRSxDQUFDO29CQUM5QixjQUFjLENBQUMsb0JBQW9CLElBQUksQ0FBQyxTQUFTLENBQUMsTUFBTSxDQUFDLElBQUksQ0FBQyxDQUFDO29CQUMvRCxZQUFZLEdBQUcsTUFBTSxDQUFDO29CQUN0QixlQUFlLEdBQUcsSUFBSSxDQUFDO2dCQUN6QixDQUFDO2dCQUFDLE9BQU8sS0FBVSxFQUFFLENBQUM7b0JBQ3BCLGNBQWMsQ0FBQyxtQkFBbUIsS0FBSyxJQUFJLENBQUMsQ0FBQztvQkFDN0MsZUFBZSxHQUFHLEtBQUssQ0FBQztvQkFDeEIsWUFBWSxHQUFHLEtBQUssQ0FBQztnQkFDdkIsQ0FBQzt3QkFBUyxDQUFDO29CQUNULGNBQWMsQ0FBQyw2QkFBNkIsQ0FBQyxDQUFDO29CQUM5QyxjQUFjLENBQUMsS0FBSyxFQUFFLE9BQU8sQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQztnQkFDM0MsQ0FBQztZQUNILENBQUM7UUFDSCxDQUFDO1FBRUQsSUFBSSxPQUFPLE1BQU0sS0FBSyxRQUFRLElBQUksT0FBTyxNQUFNLEtBQUssVUFBVSxFQUFFLENBQUM7WUFDL0QseUVBQXlFO1lBQ3pFLHdEQUF3RDtZQUN4RCxVQUFVLENBQ1IsR0FBRyxFQUFFO2dCQUNILElBQUksQ0FBQyxjQUFjLEVBQUUsQ0FBQztvQkFDcEIsTUFBTSxDQUFDLElBQUksS0FBSyxDQUFDLGdDQUFnQyxJQUFJLENBQUMsU0FBUyxDQUFDLE1BQU0sQ0FBQyxzQ0FBc0Msb0JBQW9CLDZCQUE2QixNQUFNLENBQUMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxDQUFDLFFBQVEsQ0FBQyxPQUFPLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQztvQkFDdk0sY0FBYyxDQUFDLEtBQUssRUFBRSxPQUFPLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUM7Z0JBQzNDLENBQUM7WUFDSCxDQUFDLEVBQUUsb0JBQW9CLEdBQUcsS0FBSyxDQUNoQyxDQUFDLEtBQUssRUFBRSxDQUFDO1FBQ1osQ0FBQztRQUVELEtBQUssQ0FBQyxNQUFPLENBQUMsRUFBRSxDQUFDLE1BQU0sRUFBRSxLQUFLLENBQUMsRUFBRTtZQUMvQixjQUFjLENBQUMsS0FBSyxDQUFDLENBQUM7WUFDdEIsTUFBTSxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsQ0FBQztZQUNuQixLQUFLLGtCQUFrQixDQUFDLEtBQUssQ0FBQyxDQUFDO1FBQ2pDLENBQUMsQ0FBQyxDQUFDO1FBRUgsS0FBSyxDQUFDLE1BQU8sQ0FBQyxFQUFFLENBQUMsTUFBTSxFQUFFLEtBQUssQ0FBQyxFQUFFO1lBQy9CLGNBQWMsQ0FBQyxLQUFLLENBQUMsQ0FBQztZQUN0QixJQUFJLE9BQU8sQ0FBQyxhQUFhLElBQUksSUFBSSxFQUFFLENBQUM7Z0JBQ2xDLE1BQU0sQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLENBQUM7WUFDckIsQ0FBQztZQUNELEtBQUssa0JBQWtCLENBQUMsS0FBSyxDQUFDLENBQUM7UUFDakMsQ0FBQyxDQUFDLENBQUM7UUFFSCxLQUFLLENBQUMsSUFBSSxDQUFDLE9BQU8sRUFBRSxNQUFNLENBQUMsQ0FBQztRQUU1QixnR0FBZ0c7UUFDaEcsS0FBSyxDQUFDLElBQUksQ0FBQyxNQUFNLEVBQUUsQ0FBQyxJQUFJLEVBQUUsTUFBTSxFQUFFLEVBQUU7WUFDbEMsY0FBYyxDQUFDLG1DQUFtQyxJQUFJLFlBQVksTUFBTSxJQUFJLENBQUMsQ0FBQztZQUM5RSxNQUFNLE1BQU0sR0FBRyxDQUFDLE1BQU0sQ0FBQyxNQUFNLENBQUMsTUFBTSxDQUFDLENBQUMsUUFBUSxDQUFDLE9BQU8sQ0FBQyxHQUFHLE1BQU0sQ0FBQyxNQUFNLENBQUMsTUFBTSxDQUFDLENBQUMsUUFBUSxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUMsSUFBSSxFQUFFLENBQUM7WUFDMUcsSUFBSSxJQUFJLElBQUksSUFBSSxJQUFJLElBQUksS0FBSyxDQUFDLElBQUksT0FBTyxDQUFDLFlBQVksRUFBRSxDQUFDO2dCQUN2RCxJQUFJLE1BQU0sR0FBRyxJQUFJLEtBQUssRUFBVSxDQUFDO2dCQUNqQyxNQUFNLENBQUMsSUFBSSxDQUFDLFlBQVksQ0FBQyxDQUFDO2dCQUMxQixNQUFNLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxDQUFDO2dCQUNwQixPQUFPLENBQUM7b0JBQ04sZUFBZSxFQUFFLGVBQWU7b0JBQ2hDLFlBQVksRUFBRSxZQUFZO29CQUMxQixXQUFXLEVBQUUsTUFBTTtpQkFDcEIsQ0FBQyxDQUFDO1lBQ0wsQ0FBQztpQkFBTSxDQUFDO2dCQUNOLE1BQU0sQ0FBQyxJQUFJLEtBQUssQ0FBQyxJQUFJLE9BQU8sQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLDRCQUE0QixJQUFJLGVBQWUsTUFBTSxFQUFFLENBQUMsQ0FBQyxDQUFDO1lBQ2xHLENBQUM7UUFDSCxDQUFDLENBQUMsQ0FBQztJQUNMLENBQUMsQ0FBQyxDQUFDO0FBQ0wsQ0FBQztBQUVELFNBQVMsY0FBYyxDQUFDLEtBQWlDLEVBQUUsT0FBZTtJQUN4RTs7O09BR0c7SUFDSCxLQUFLLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBQyxDQUFDO0lBQ3JCLGFBQWEsQ0FBQyxJQUFJLENBQUMsK0JBQStCLE9BQU8sOENBQThDLENBQUMsQ0FBQztBQUMzRyxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0ICogYXMgY2hpbGRfcHJvY2VzcyBmcm9tICdjaGlsZF9wcm9jZXNzJztcbmltcG9ydCAqIGFzIG9zIGZyb20gJ29zJztcbmltcG9ydCAqIGFzIHBhdGggZnJvbSAncGF0aCc7XG5pbXBvcnQgdHlwZSB7IFRlc3RDb250ZXh0IH0gZnJvbSAnLi9pbnRlZy10ZXN0JztcbmltcG9ydCB7IFJFU09VUkNFU19ESVIgfSBmcm9tICcuL3Jlc291cmNlcyc7XG5pbXBvcnQgdHlwZSB7IFNoZWxsT3B0aW9ucyB9IGZyb20gJy4vc2hlbGwnO1xuaW1wb3J0IHsgcmltcmFmIH0gZnJvbSAnLi9zaGVsbCc7XG5pbXBvcnQgdHlwZSB7IEF3c0NvbnRleHQgfSBmcm9tICcuL3dpdGgtYXdzJztcbmltcG9ydCB7IHdpdGhBd3MgfSBmcm9tICcuL3dpdGgtYXdzJztcbmltcG9ydCB7XG4gIGNsb25lRGlyZWN0b3J5LFxuICBpbnN0YWxsTnBtUGFja2FnZXMsXG4gIFRlc3RGaXh0dXJlLFxuICBFWFRFTkRFRF9URVNUX1RJTUVPVVRfUyxcbn0gZnJvbSAnLi93aXRoLWNkay1hcHAnO1xuaW1wb3J0IHsgd2l0aFRpbWVvdXQgfSBmcm9tICcuL3dpdGgtdGltZW91dCc7XG5cbmV4cG9ydCBpbnRlcmZhY2UgQWN0aW9uT3V0cHV0IHtcbiAgYWN0aW9uU3VjY2VlZGVkPzogYm9vbGVhbjtcbiAgYWN0aW9uT3V0cHV0PzogYW55O1xuICBzaGVsbE91dHB1dD86IHN0cmluZztcbn1cblxuLyoqXG4gKiBIaWdoZXIgb3JkZXIgZnVuY3Rpb24gdG8gZXhlY3V0ZSBhIGJsb2NrIHdpdGggYSBTQU0gSW50ZWdyYXRpb24gQ0RLIGFwcCBmaXh0dXJlXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiB3aXRoU2FtSW50ZWdyYXRpb25DZGtBcHA8QSBleHRlbmRzIFRlc3RDb250ZXh0ICYgQXdzQ29udGV4dD4oYmxvY2s6IChjb250ZXh0OiBTYW1JbnRlZ3JhdGlvblRlc3RGaXh0dXJlKSA9PiBQcm9taXNlPHZvaWQ+KSB7XG4gIHJldHVybiBhc3luYyAoY29udGV4dDogQSkgPT4ge1xuICAgIGNvbnN0IHJhbmR5ID0gY29udGV4dC5yYW5kb21TdHJpbmc7XG4gICAgY29uc3Qgc3RhY2tOYW1lUHJlZml4ID0gYGNka3Rlc3QtJHtyYW5keX1gO1xuICAgIGNvbnN0IGludGVnVGVzdERpciA9IHBhdGguam9pbihvcy50bXBkaXIoKSwgYGNkay1pbnRlZy0ke3JhbmR5fWApO1xuXG4gICAgY29udGV4dC5sb2coYCBTdGFjayBwcmVmaXg6ICAgJHtzdGFja05hbWVQcmVmaXh9XFxuYCk7XG4gICAgY29udGV4dC5sb2coYCBUZXN0IGRpcmVjdG9yeTogJHtpbnRlZ1Rlc3REaXJ9XFxuYCk7XG4gICAgY29udGV4dC5sb2coYCBSZWdpb246ICAgICAgICAgJHtjb250ZXh0LmF3cy5yZWdpb259XFxuYCk7XG5cbiAgICBhd2FpdCBjbG9uZURpcmVjdG9yeShwYXRoLmpvaW4oUkVTT1VSQ0VTX0RJUiwgJ2Nkay1hcHBzJywgJ3NhbV9jZGtfaW50ZWdfYXBwJyksIGludGVnVGVzdERpciwgY29udGV4dC5vdXRwdXQpO1xuICAgIGNvbnN0IGZpeHR1cmUgPSBuZXcgU2FtSW50ZWdyYXRpb25UZXN0Rml4dHVyZShcbiAgICAgIGludGVnVGVzdERpcixcbiAgICAgIHN0YWNrTmFtZVByZWZpeCxcbiAgICAgIGNvbnRleHQub3V0cHV0LFxuICAgICAgY29udGV4dC5hd3MsXG4gICAgICBjb250ZXh0LnJhbmRvbVN0cmluZyk7XG4gICAgYXdhaXQgZml4dHVyZS5lY3JQdWJsaWNMb2dpbigpO1xuXG4gICAgbGV0IHN1Y2Nlc3MgPSB0cnVlO1xuICAgIHRyeSB7XG4gICAgICBjb25zdCBpbnN0YWxsYXRpb25WZXJzaW9uID0gZml4dHVyZS5saWJyYXJ5LnJlcXVlc3RlZFZlcnNpb24oKTtcblxuICAgICAgY29uc3QgYWxwaGFJbnN0YWxsYXRpb25WZXJzaW9uID0gZml4dHVyZS5saWJyYXJ5LnJlcXVlc3RlZEFscGhhVmVyc2lvbigpO1xuICAgICAgYXdhaXQgaW5zdGFsbE5wbVBhY2thZ2VzKGZpeHR1cmUsIHtcbiAgICAgICAgJ2F3cy1jZGstbGliJzogaW5zdGFsbGF0aW9uVmVyc2lvbixcbiAgICAgICAgJ0Bhd3MtY2RrL2F3cy1sYW1iZGEtZ28tYWxwaGEnOiBhbHBoYUluc3RhbGxhdGlvblZlcnNpb24sXG4gICAgICAgICdAYXdzLWNkay9hd3MtbGFtYmRhLXB5dGhvbi1hbHBoYSc6IGFscGhhSW5zdGFsbGF0aW9uVmVyc2lvbixcbiAgICAgICAgJ2NvbnN0cnVjdHMnOiAnXjEwJyxcbiAgICAgIH0pO1xuXG4gICAgICBhd2FpdCBibG9jayhmaXh0dXJlKTtcbiAgICB9IGNhdGNoIChlOiBhbnkpIHtcbiAgICAgIC8vIFdlIHN1cnZpdmUgY2VydGFpbiBjYXNlcyBpbnZvbHZpbmcgZ29wa2cuaW5cbiAgICAgIGlmIChlcnJvckNhdXNlZEJ5R29Qa2coZS5tZXNzYWdlKSkge1xuICAgICAgICByZXR1cm47XG4gICAgICB9XG4gICAgICBzdWNjZXNzID0gZmFsc2U7XG4gICAgICB0aHJvdyBlO1xuICAgIH0gZmluYWxseSB7XG4gICAgICBpZiAocHJvY2Vzcy5lbnYuSU5URUdfTk9fQ0xFQU4pIHtcbiAgICAgICAgY29udGV4dC5sb2coYExlZnQgdGVzdCBkaXJlY3RvcnkgaW4gJyR7aW50ZWdUZXN0RGlyfScgKCRJTlRFR19OT19DTEVBTilcXG5gKTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIGF3YWl0IGZpeHR1cmUuZGlzcG9zZShzdWNjZXNzKTtcbiAgICAgIH1cbiAgICB9XG4gIH07XG59XG5cbi8qKlxuICogUmV0dXJuIHdoZXRoZXIgb3Igbm90IHRoZSBlcnJvciBpcyBiZWluZyBjYXVzZWQgYnkgZ29wa2cuaW4gYmVpbmcgZG93blxuICpcbiAqIE91ciBHbyBidWlsZCBkZXBlbmRzIG9uIGh0dHBzOi8vZ29wa2cuaW4vLCB3aGljaCBoYXMgZXJyb3JzIHByZXR0eSBvZnRlblxuICogKGV2ZXJ5IGNvdXBsZSBvZiBkYXlzKS4gSXQgaXMgcnVuIGJ5IGEgc2luZ2xlIHZvbHVudGVlci5cbiAqL1xuZnVuY3Rpb24gZXJyb3JDYXVzZWRCeUdvUGtnKGVycm9yOiBzdHJpbmcpIHtcbiAgLy8gVGhlIGVycm9yIGlzIGRpZmZlcmVudCBkZXBlbmRpbmcgb24gd2hhdCByZXF1ZXN0IGZhaWxzLiBNZXNzYWdlcyByZWNvZ25pemVkOlxuICAvLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vL1xuICAvLyAgICBnbzogZ2l0aHViLmNvbS9hd3MvYXdzLWxhbWJkYS1nb0B2MS4yOC4wIHJlcXVpcmVzXG4gIC8vICAgICAgICBnb3BrZy5pbi95YW1sLnYzQHYzLjAuMC0yMDIwMDYxNTExMzQxMy1lZWVjYTQ4ZmU3NzY6IGludmFsaWQgdmVyc2lvbjogZ2l0IGxzLXJlbW90ZSAtcSBvcmlnaW4gaW4gL2dvL3BrZy9tb2QvY2FjaGUvdmNzLzA5MDFkYzFlZjY3ZmNjZTFjOWIzYWU1MTA3ODc0MGRlNGEwZTJkYzY3M2U3MjA1ODRhYzMwMjk3M2FmODJmMzY6IGV4aXQgc3RhdHVzIDEyODpcbiAgLy8gICAgICAgIHJlbW90ZTogQ2Fubm90IG9idGFpbiByZWZzIGZyb20gR2l0SHViOiBjYW5ub3QgdGFsayB0byBHaXRIdWI6IEdldCBodHRwczovL2dpdGh1Yi5jb20vZ28teWFtbC95YW1sLmdpdC9pbmZvL3JlZnM/c2VydmljZT1naXQtdXBsb2FkLXBhY2s6IG5ldC9odHRwOiByZXF1ZXN0IGNhbmNlbGVkIChDbGllbnQuVGltZW91dCBleGNlZWRlZCB3aGlsZSBhd2FpdGluZyBoZWFkZXJzKVxuICAvLyAgICAgICAgZmF0YWw6IHVuYWJsZSB0byBhY2Nlc3MgJ2h0dHBzOi8vZ29wa2cuaW4veWFtbC52My8nOiBUaGUgcmVxdWVzdGVkIFVSTCByZXR1cm5lZCBlcnJvcjogNTAyXG4gIC8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vXG4gIC8vICAgIGdvOiBkb3dubG9hZGluZyBnaXRodWIuY29tL2F3cy9hd3MtbGFtYmRhLWdvIHYxLjI4LjBcbiAgLy8gICAgZ286IGdpdGh1Yi5jb20vYXdzL2F3cy1sYW1iZGEtZ29AdjEuMjguMCByZXF1aXJlc1xuICAvLyAgICAgICAgZ29wa2cuaW4veWFtbC52M0B2My4wLjAtMjAyMDA2MTUxMTM0MTMtZWVlY2E0OGZlNzc2OiB1bnJlY29nbml6ZWQgaW1wb3J0IHBhdGggXCJnb3BrZy5pbi95YW1sLnYzXCI6IHJlYWRpbmcgaHR0cHM6Ly9nb3BrZy5pbi95YW1sLnYzP2dvLWdldD0xOiA1MDIgQmFkIEdhdGV3YXlcbiAgLy8gICAgICAgIHNlcnZlciByZXNwb25zZTogQ2Fubm90IG9idGFpbiByZWZzIGZyb20gR2l0SHViOiBjYW5ub3QgdGFsayB0byBHaXRIdWI6IEdldCBodHRwczovL2dpdGh1Yi5jb20vZ28teWFtbC95YW1sLmdpdC9pbmZvL3JlZnM/c2VydmljZT1naXQtdXBsb2FkLXBhY2s6IG5ldC9odHRwOiByZXF1ZXN0IGNhbmNlbGVkIChDbGllbnQuVGltZW91dCBleGNlZWRlZCB3aGlsZSBhd2FpdGluZyBoZWFkZXJzKVxuICAvLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vL1xuICAvLyAgICBnbzogZ2l0aHViLmNvbS9hd3MvYXdzLWxhbWJkYS1nb0B2MS4yOC4wIHJlcXVpcmVzXG4gIC8vICAgICAgICBnb3BrZy5pbi95YW1sLnYzQHYzLjAuMC0yMDIwMDYxNTExMzQxMy1lZWVjYTQ4ZmU3NzY6IGludmFsaWQgdmVyc2lvbjogZ2l0IGZldGNoIC1mIG9yaWdpbiByZWZzL2hlYWRzLyo6cmVmcy9oZWFkcy8qIHJlZnMvdGFncy8qOnJlZnMvdGFncy8qIGluIC9nby9wa2cvbW9kL2NhY2hlL3Zjcy8wOTAxZGMxZWY2N2ZjY2UxYzliM2FlNTEwNzg3NDBkZTRhMGUyZGM2NzNlNzIwNTg0YWMzMDI5NzNhZjgyZjM2OiBleGl0IHN0YXR1cyAxMjg6XG4gIC8vICAgICAgICBlcnJvcjogUlBDIGZhaWxlZDsgSFRUUCA1MDIgY3VybCAyMiBUaGUgcmVxdWVzdGVkIFVSTCByZXR1cm5lZCBlcnJvcjogNTAyXG4gIC8vICAgICAgICBmYXRhbDogdGhlIHJlbW90ZSBlbmQgaHVuZyB1cCB1bmV4cGVjdGVkbHlcbiAgLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy9cblxuICByZXR1cm4gKGVycm9yLmluY2x1ZGVzKCdnb3BrZ1xcLmluLippbnZhbGlkIHZlcnNpb24uKmV4aXQgc3RhdHVzIDEyOCcpXG4gICAgfHwgZXJyb3IubWF0Y2goL3VucmVjb2duaXplZCBpbXBvcnQgcGF0aFteXFxuXWdvcGtnXFwuaW4vKSk7XG59XG5cbi8qKlxuICogU0FNIEludGVncmF0aW9uIHRlc3QgZml4dHVyZSBmb3IgQ0RLIC0gU0FNIGludGVncmF0aW9uIHRlc3QgY2FzZXNcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHdpdGhTYW1JbnRlZ3JhdGlvbkZpeHR1cmUoYmxvY2s6IChjb250ZXh0OiBTYW1JbnRlZ3JhdGlvblRlc3RGaXh0dXJlKSA9PiBQcm9taXNlPHZvaWQ+KSB7XG4gIHJldHVybiB3aXRoQXdzKHdpdGhUaW1lb3V0KEVYVEVOREVEX1RFU1RfVElNRU9VVF9TLCB3aXRoU2FtSW50ZWdyYXRpb25DZGtBcHAoYmxvY2spKSk7XG59XG5cbmV4cG9ydCBjbGFzcyBTYW1JbnRlZ3JhdGlvblRlc3RGaXh0dXJlIGV4dGVuZHMgVGVzdEZpeHR1cmUge1xuICBwdWJsaWMgYXN5bmMgc2FtU2hlbGwoY29tbWFuZDogc3RyaW5nW10sIGZpbHRlcj86IHN0cmluZywgYWN0aW9uPzogKCkgPT4gYW55LCBvcHRpb25zOiBPbWl0PFNoZWxsT3B0aW9ucywgJ2N3ZCcgfCAnb3V0cHV0Jz4gPSB7fSk6IFByb21pc2U8QWN0aW9uT3V0cHV0PiB7XG4gICAgcmV0dXJuIHNoZWxsV2l0aEFjdGlvbihjb21tYW5kLCBmaWx0ZXIsIGFjdGlvbiwge1xuICAgICAgb3V0cHV0czogW3RoaXMub3V0cHV0XSxcbiAgICAgIGN3ZDogcGF0aC5qb2luKHRoaXMuaW50ZWdUZXN0RGlyLCAnY2RrLm91dCcpLnRvU3RyaW5nKCksXG4gICAgICAuLi5vcHRpb25zLFxuICAgIH0pO1xuICB9XG5cbiAgcHVibGljIGFzeW5jIHNhbUJ1aWxkKHN0YWNrTmFtZTogc3RyaW5nKSB7XG4gICAgY29uc3QgZnVsbFN0YWNrTmFtZSA9IHRoaXMuZnVsbFN0YWNrTmFtZShzdGFja05hbWUpO1xuICAgIGNvbnN0IHRlbXBsYXRlUGF0aCA9IHBhdGguam9pbih0aGlzLmludGVnVGVzdERpciwgJ2Nkay5vdXQnLCBgJHtmdWxsU3RhY2tOYW1lfS50ZW1wbGF0ZS5qc29uYCk7XG4gICAgY29uc3QgYXJncyA9IFsnLS10ZW1wbGF0ZScsIHRlbXBsYXRlUGF0aC50b1N0cmluZygpXTtcbiAgICByZXR1cm4gdGhpcy5zYW1TaGVsbChbJ3NhbScsICdidWlsZCcsIC4uLmFyZ3NdKTtcbiAgfVxuXG4gIHB1YmxpYyBhc3luYyBzYW1Mb2NhbFN0YXJ0QXBpKHN0YWNrTmFtZTogc3RyaW5nLCBpc0J1aWx0OiBib29sZWFuLCBwb3J0OiBudW1iZXIsIGFwaVBhdGg6IHN0cmluZyk6IFByb21pc2U8QWN0aW9uT3V0cHV0PiB7XG4gICAgY29uc3QgZnVsbFN0YWNrTmFtZSA9IHRoaXMuZnVsbFN0YWNrTmFtZShzdGFja05hbWUpO1xuICAgIGNvbnN0IHRlbXBsYXRlUGF0aCA9IHBhdGguam9pbih0aGlzLmludGVnVGVzdERpciwgJ2Nkay5vdXQnLCBgJHtmdWxsU3RhY2tOYW1lfS50ZW1wbGF0ZS5qc29uYCk7XG4gICAgY29uc3QgYXJncyA9IGlzQnVpbHQ/IFtdIDogWyctLXRlbXBsYXRlJywgdGVtcGxhdGVQYXRoLnRvU3RyaW5nKCldO1xuICAgIGFyZ3MucHVzaCgnLS1wb3J0Jyk7XG4gICAgYXJncy5wdXNoKHBvcnQudG9TdHJpbmcoKSk7XG5cbiAgICAvLyBodHRwczovL2dpdGh1Yi5jb20vYXdzL2F3cy1zYW0tY2xpL3B1bGwvNzg5MlxuICAgIGFyZ3MucHVzaCgnLS1uby1tZW1vcnktbGltaXQnKTtcblxuICAgIC8vIFwiUHJlc3MgQ3RybCtDIHRvIHF1aXRcIiBsb29rcyB0byBiZSBwcmludGVkIGJ5IGEgRmxhc2sgc2VydmVyIGJ1aWx0IGludG8gU0FNIENMSS5cbiAgICByZXR1cm4gdGhpcy5zYW1TaGVsbChbJ3NhbScsICdsb2NhbCcsICdzdGFydC1hcGknLCAuLi5hcmdzXSwgJ1ByZXNzIENUUkwrQyB0byBxdWl0JywgKCk9PntcbiAgICAgIHJldHVybiBmZXRjaChgaHR0cDovLzEyNy4wLjAuMToke3BvcnR9JHthcGlQYXRofWApLnRoZW4ocmVzcCA9PiByZXNwLmpzb24oKSkuY2F0Y2goZXJyb3IgPT4ge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYEZhaWxlZCB0byBpbnZva2UgYXBpIHBhdGggJHthcGlQYXRofSBvbiBwb3J0ICR7cG9ydH0gd2l0aCBlcnJvciAke2Vycm9yfWApO1xuICAgICAgfSk7XG4gICAgfSk7XG4gIH1cblxuICAvKipcbiAgICogQ2xlYW51cCBsZWZ0b3ZlciBzdGFja3MgYW5kIGJ1Y2tldHNcbiAgICovXG4gIHB1YmxpYyBhc3luYyBkaXNwb3NlKHN1Y2Nlc3M6IGJvb2xlYW4pIHtcbiAgICAvLyBJZiB0aGUgdGVzdHMgY29tcGxldGVkIHN1Y2Nlc3NmdWxseSwgaGFwcGlseSBkZWxldGUgdGhlIGZpeHR1cmVcbiAgICAvLyAob3RoZXJ3aXNlIGxlYXZlIGl0IGZvciBodW1hbnMgdG8gaW5zcGVjdClcbiAgICBpZiAoc3VjY2Vzcykge1xuICAgICAgY29uc3QgY2xlYW5lZCA9IHJpbXJhZih0aGlzLmludGVnVGVzdERpcik7XG4gICAgICBpZiAoIWNsZWFuZWQpIHtcbiAgICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLWNvbnNvbGVcbiAgICAgICAgY29uc29sZS5lcnJvcihgRmFpbGVkIHRvIGNsZWFuIHVwICR7dGhpcy5pbnRlZ1Rlc3REaXJ9IGR1ZSB0byBwZXJtaXNzaW9ucyBpc3N1ZXMgKERvY2tlciBydW5uaW5nIGFzIHJvb3Q/KWApO1xuICAgICAgfVxuICAgIH1cbiAgfVxufVxuXG5leHBvcnQgZnVuY3Rpb24gcmFuZG9tSW50ZWdlcihtaW46IG51bWJlciwgbWF4OiBudW1iZXIpIHtcbiAgcmV0dXJuIE1hdGguZmxvb3IoTWF0aC5yYW5kb20oKSAqIChtYXggLSBtaW4pICsgbWluKTtcbn1cblxuLyoqXG4gKiBBIHNoZWxsIGNvbW1hbmQgdGhhdCBkb2VzIHdoYXQgeW91IHdhbnRcbiAqXG4gKiBJcyBwbGF0Zm9ybS1hd2FyZSwgaGFuZGxlcyBlcnJvcnMgbmljZWx5LlxuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gc2hlbGxXaXRoQWN0aW9uKFxuICBjb21tYW5kOiBzdHJpbmdbXSxcbiAgZmlsdGVyPzogc3RyaW5nLFxuICBhY3Rpb24/OiAoKSA9PiBQcm9taXNlPGFueT4sXG4gIG9wdGlvbnM6IFNoZWxsT3B0aW9ucyA9IHt9LFxuICBhY3Rpb25UaW1lb3V0U2Vjb25kczogbnVtYmVyID0gNjAwLFxuKTogUHJvbWlzZTxBY3Rpb25PdXRwdXQ+IHtcbiAgaWYgKG9wdGlvbnMubW9kRW52ICYmIG9wdGlvbnMuZW52KSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKCdVc2UgZWl0aGVyIGVudiBvciBtb2RFbnYgYnV0IG5vdCBib3RoJyk7XG4gIH1cblxuICBjb25zdCB3cml0ZVRvT3V0cHV0cyA9ICh4OiBzdHJpbmcpID0+IHtcbiAgICBmb3IgKGNvbnN0IG91dHB1dCBvZiBvcHRpb25zLm91dHB1dHMgPz8gW10pIHtcbiAgICAgIG91dHB1dC53cml0ZSh4KTtcbiAgICB9XG4gIH07XG4gIHdyaXRlVG9PdXRwdXRzKGDwn5K7ICR7Y29tbWFuZC5qb2luKCcgJyl9XFxuYCk7XG5cbiAgY29uc3QgZW52ID0gb3B0aW9ucy5lbnYgPz8gKG9wdGlvbnMubW9kRW52ID8geyAuLi5wcm9jZXNzLmVudiwgLi4ub3B0aW9ucy5tb2RFbnYgfSA6IHVuZGVmaW5lZCk7XG5cbiAgY29uc3QgY2hpbGQgPSBjaGlsZF9wcm9jZXNzLnNwYXduKGNvbW1hbmQuam9pbignICcpLCBbXSwge1xuICAgIC4uLm9wdGlvbnMsXG4gICAgZW52LFxuICAgIC8vIE5lZWQgdGhpcyBmb3IgV2luZG93cyB3aGVyZSB3ZSB3YW50IC5jbWQgYW5kIC5iYXQgdG8gYmUgZm91bmQgYXMgd2VsbC5cbiAgICBzaGVsbDogdHJ1ZSxcbiAgICBzdGRpbzogWydpZ25vcmUnLCAncGlwZScsICdwaXBlJ10sXG4gIH0pO1xuXG4gIHJldHVybiBuZXcgUHJvbWlzZTxBY3Rpb25PdXRwdXQ+KChyZXNvbHZlLCByZWplY3QpID0+IHtcbiAgICBjb25zdCBvdXQgPSBuZXcgQXJyYXk8QnVmZmVyPigpO1xuICAgIGNvbnN0IHN0ZG91dCA9IG5ldyBBcnJheTxCdWZmZXI+KCk7XG4gICAgY29uc3Qgc3RkZXJyID0gbmV3IEFycmF5PEJ1ZmZlcj4oKTtcbiAgICBsZXQgYWN0aW9uU3VjY2VlZGVkID0gZmFsc2U7XG4gICAgbGV0IGFjdGlvbk91dHB1dDogYW55O1xuICAgIGxldCBhY3Rpb25FeGVjdXRlZCA9IGZhbHNlO1xuXG4gICAgYXN5bmMgZnVuY3Rpb24gbWF5YmVFeGVjdXRlQWN0aW9uKGNodW5rOiBhbnkpIHtcbiAgICAgIG91dC5wdXNoKEJ1ZmZlci5mcm9tKGNodW5rKSk7XG4gICAgICBpZiAoIWFjdGlvbkV4ZWN1dGVkICYmIHR5cGVvZiBmaWx0ZXIgPT09ICdzdHJpbmcnICYmIEJ1ZmZlci5jb25jYXQob3V0KS50b1N0cmluZygndXRmLTgnKS5pbmNsdWRlcyhmaWx0ZXIpICYmIHR5cGVvZiBhY3Rpb24gPT09ICdmdW5jdGlvbicpIHtcbiAgICAgICAgYWN0aW9uRXhlY3V0ZWQgPSB0cnVlO1xuICAgICAgICB3cml0ZVRvT3V0cHV0cygnYmVmb3JlIGV4ZWN1dGluZyBhY3Rpb25cXG4nKTtcbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICBjb25zdCBvdXRwdXQgPSBhd2FpdCBhY3Rpb24oKTtcbiAgICAgICAgICB3cml0ZVRvT3V0cHV0cyhgYWN0aW9uIG91dHB1dCBpcyAke0pTT04uc3RyaW5naWZ5KG91dHB1dCl9XFxuYCk7XG4gICAgICAgICAgYWN0aW9uT3V0cHV0ID0gb3V0cHV0O1xuICAgICAgICAgIGFjdGlvblN1Y2NlZWRlZCA9IHRydWU7XG4gICAgICAgIH0gY2F0Y2ggKGVycm9yOiBhbnkpIHtcbiAgICAgICAgICB3cml0ZVRvT3V0cHV0cyhgYWN0aW9uIGVycm9yIGlzICR7ZXJyb3J9XFxuYCk7XG4gICAgICAgICAgYWN0aW9uU3VjY2VlZGVkID0gZmFsc2U7XG4gICAgICAgICAgYWN0aW9uT3V0cHV0ID0gZXJyb3I7XG4gICAgICAgIH0gZmluYWxseSB7XG4gICAgICAgICAgd3JpdGVUb091dHB1dHMoJ3Rlcm1pbmF0ZSBzYW0gc3ViIHByb2Nlc3NcXG4nKTtcbiAgICAgICAgICBraWxsU3ViUHJvY2VzcyhjaGlsZCwgY29tbWFuZC5qb2luKCcgJykpO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuXG4gICAgaWYgKHR5cGVvZiBmaWx0ZXIgPT09ICdzdHJpbmcnICYmIHR5cGVvZiBhY3Rpb24gPT09ICdmdW5jdGlvbicpIHtcbiAgICAgIC8vIFJlamVjdCB3aXRoIGFuIGVycm9yIGlmIGFuIGFjdGlvbiBpcyBjb25maWd1cmVkLCBidXQgdGhlIGZpbHRlciBmYWlsZWRcbiAgICAgIC8vIHRvIHNob3cgdXAgaW4gdGhlIG91dHB1dCBiZWZvcmUgdGhlIHRpbWVvdXQgb2NjdXJyZWQuXG4gICAgICBzZXRUaW1lb3V0KFxuICAgICAgICAoKSA9PiB7XG4gICAgICAgICAgaWYgKCFhY3Rpb25FeGVjdXRlZCkge1xuICAgICAgICAgICAgcmVqZWN0KG5ldyBFcnJvcihgVGltZWQgb3V0IHdhaXRpbmcgZm9yIGZpbHRlciAke0pTT04uc3RyaW5naWZ5KGZpbHRlcil9IHRvIGFwcGVhciBpbiBjb21tYW5kIG91dHB1dCBhZnRlciAke2FjdGlvblRpbWVvdXRTZWNvbmRzfSBzZWNvbmRzXFxuT3V0cHV0IHNvIGZhcjpcXG4ke0J1ZmZlci5jb25jYXQob3V0KS50b1N0cmluZygndXRmLTgnKX1gKSk7XG4gICAgICAgICAgICBraWxsU3ViUHJvY2VzcyhjaGlsZCwgY29tbWFuZC5qb2luKCcgJykpO1xuICAgICAgICAgIH1cbiAgICAgICAgfSwgYWN0aW9uVGltZW91dFNlY29uZHMgKiAxXzAwMCxcbiAgICAgICkudW5yZWYoKTtcbiAgICB9XG5cbiAgICBjaGlsZC5zdGRvdXQhLm9uKCdkYXRhJywgY2h1bmsgPT4ge1xuICAgICAgd3JpdGVUb091dHB1dHMoY2h1bmspO1xuICAgICAgc3Rkb3V0LnB1c2goY2h1bmspO1xuICAgICAgdm9pZCBtYXliZUV4ZWN1dGVBY3Rpb24oY2h1bmspO1xuICAgIH0pO1xuXG4gICAgY2hpbGQuc3RkZXJyIS5vbignZGF0YScsIGNodW5rID0+IHtcbiAgICAgIHdyaXRlVG9PdXRwdXRzKGNodW5rKTtcbiAgICAgIGlmIChvcHRpb25zLmNhcHR1cmVTdGRlcnIgPz8gdHJ1ZSkge1xuICAgICAgICBzdGRlcnIucHVzaChjaHVuayk7XG4gICAgICB9XG4gICAgICB2b2lkIG1heWJlRXhlY3V0ZUFjdGlvbihjaHVuayk7XG4gICAgfSk7XG5cbiAgICBjaGlsZC5vbmNlKCdlcnJvcicsIHJlamVjdCk7XG5cbiAgICAvLyBXYWl0IGZvciAnZXhpdCcgaW5zdGVhZCBvZiBjbG9zZSwgZG9uJ3QgY2FyZSBhYm91dCByZWFkaW5nIHRoZSBzdHJlYW1zIGFsbCB0aGUgd2F5IHRvIHRoZSBlbmRcbiAgICBjaGlsZC5vbmNlKCdleGl0JywgKGNvZGUsIHNpZ25hbCkgPT4ge1xuICAgICAgd3JpdGVUb091dHB1dHMoYFN1YnByb2Nlc3MgaGFzIGV4aXRlZCB3aXRoIGNvZGUgJHtjb2RlfSwgc2lnbmFsICR7c2lnbmFsfVxcbmApO1xuICAgICAgY29uc3Qgb3V0cHV0ID0gKEJ1ZmZlci5jb25jYXQoc3Rkb3V0KS50b1N0cmluZygndXRmLTgnKSArIEJ1ZmZlci5jb25jYXQoc3RkZXJyKS50b1N0cmluZygndXRmLTgnKSkudHJpbSgpO1xuICAgICAgaWYgKGNvZGUgPT0gbnVsbCB8fCBjb2RlID09PSAwIHx8IG9wdGlvbnMuYWxsb3dFcnJFeGl0KSB7XG4gICAgICAgIGxldCByZXN1bHQgPSBuZXcgQXJyYXk8c3RyaW5nPigpO1xuICAgICAgICByZXN1bHQucHVzaChhY3Rpb25PdXRwdXQpO1xuICAgICAgICByZXN1bHQucHVzaChvdXRwdXQpO1xuICAgICAgICByZXNvbHZlKHtcbiAgICAgICAgICBhY3Rpb25TdWNjZWVkZWQ6IGFjdGlvblN1Y2NlZWRlZCxcbiAgICAgICAgICBhY3Rpb25PdXRwdXQ6IGFjdGlvbk91dHB1dCxcbiAgICAgICAgICBzaGVsbE91dHB1dDogb3V0cHV0LFxuICAgICAgICB9KTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHJlamVjdChuZXcgRXJyb3IoYCcke2NvbW1hbmQuam9pbignICcpfScgZXhpdGVkIHdpdGggZXJyb3IgY29kZSAke2NvZGV9LiBPdXRwdXQ6IFxcbiR7b3V0cHV0fWApKTtcbiAgICAgIH1cbiAgICB9KTtcbiAgfSk7XG59XG5cbmZ1bmN0aW9uIGtpbGxTdWJQcm9jZXNzKGNoaWxkOiBjaGlsZF9wcm9jZXNzLkNoaWxkUHJvY2VzcywgY29tbWFuZDogc3RyaW5nKSB7XG4gIC8qKlxuICAgKiBDaGVjayBpZiB0aGUgc3ViIHByb2Nlc3MgaXMgcnVubmluZyBpbiBjb250YWluZXIsIHNvIGNoaWxkX3Byb2Nlc3Muc3Bhd24gd2lsbFxuICAgKiBjcmVhdGUgbXVsdGlwbGUgcHJvY2Vzc2VzLCBhbmQgdG8ga2lsbCBhbGwgb2YgdGhlbSB3ZSBuZWVkIHRvIHJ1biBkaWZmZXJlbnQgbG9naWNcbiAgICovXG4gIGNoaWxkLmtpbGwoJ1NJR0lOVCcpO1xuICBjaGlsZF9wcm9jZXNzLmV4ZWMoYGZvciBwaWQgaW4gJChwcyAtZWYgfCBncmVwIFwiJHtjb21tYW5kfVwiIHwgYXdrICd7cHJpbnQgJDJ9Jyk7IGRvIGtpbGwgLTIgJHBpZDsgZG9uZWApO1xufVxuIl19