UNPKG

@aws-cdk/integ-runner

Version:

CDK Integration Testing Tool

382 lines 48.5 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.DEFAULT_SYNTH_OPTIONS = exports.IntegRunner = void 0; exports.currentlyRecommendedAwsCdkLibFlags = currentlyRecommendedAwsCdkLibFlags; /* eslint-disable @cdklabs/no-literal-partition */ const path = require("path"); const cloud_assembly_api_1 = require("@aws-cdk/cloud-assembly-api"); const cx_api_1 = require("@aws-cdk/cx-api"); const fs = require("fs-extra"); const integ_test_suite_1 = require("./integ-test-suite"); const recommendedFlagsFile = require("../recommended-feature-flags.json"); const utils_1 = require("../utils"); const engine_1 = require("./engine"); const logger = require("../logger"); const cloud_assembly_1 = require("./private/cloud-assembly"); const integ_manifest_1 = require("./private/integ-manifest"); const DESTRUCTIVE_CHANGES = '!!DESTRUCTIVE_CHANGES:'; /** * The different components of a test name */ /** * Represents an Integration test runner */ class IntegRunner { /** * The directory where the snapshot will be stored */ snapshotDir; /** * An instance of the CDK CLI */ cdk; /** * Pretty name of the test */ testName; /** * The value used in the '--app' CLI parameter * * Path to the integ test source file, relative to `this.directory`. */ cdkApp; /** * The path where the `cdk.context.json` file * will be created */ cdkContextPath; /** * The working directory that the integration tests will be * executed from */ directory; /** * The test to run */ test; /** * Default options to pass to the CDK CLI */ defaultArgs = { pathMetadata: false, assetMetadata: false, versionReporting: false, }; /** * The directory where the CDK will be synthed to * * Relative to cwd. */ cdkOutDir; /** * The profile to use for the CDK CLI calls */ profile; /** * Show output from the integ test run. */ showOutput; _destructiveChanges; legacyContext; _expectedTestSuite; _actualTestSuite; constructor(options) { this.test = options.test; this.directory = this.test.directory; this.testName = this.test.testName; this.snapshotDir = this.test.snapshotDir; this.cdkContextPath = path.join(this.directory, 'cdk.context.json'); this.profile = options.profile; this.showOutput = options.showOutput ?? false; this.cdk = options.cdk ?? (0, engine_1.makeEngine)(options); this.cdkOutDir = options.integOutDir ?? this.test.temporaryOutputDir; const testRunCommand = this.test.appCommand; this.cdkApp = testRunCommand.replace('{filePath}', path.relative(this.directory, this.test.fileName)); } /** * Return the list of expected (i.e. existing) test cases for this integration test */ async expectedTests() { return (await this.expectedTestSuite())?.testSuite; } /** * Return the list of actual (i.e. new) test cases for this integration test */ async actualTests() { return (await this.actualTestSuite()).testSuite; } /** * Generate a new "actual" snapshot which will be compared to the * existing "expected" snapshot * This will synth and then load the integration test manifest */ async generateActualSnapshot() { await this.cdk.synth({ app: this.cdkApp, // we don't know the "actual" context yet (this method is what generates it) so just // use the "expected" context. This is only run in order to read the manifest context: this.getContext((await this.expectedTestSuite())?.synthContext), env: exports.DEFAULT_SYNTH_OPTIONS.env, output: path.relative(this.directory, this.cdkOutDir), }); const manifest = await this.loadManifest(this.cdkOutDir); // after we load the manifest remove the tmp snapshot // so that it doesn't mess up the real snapshot created later this.cleanup(); return manifest; } /** * Returns true if a snapshot already exists for this test */ hasSnapshot() { return fs.existsSync(this.snapshotDir); } /** * The test suite from the existing snapshot */ async expectedTestSuite() { if (!this._expectedTestSuite && this.hasSnapshot()) { this._expectedTestSuite = await this.loadManifest(); } return this._expectedTestSuite; } /** * The test suite from the new "actual" snapshot */ async actualTestSuite() { if (!this._actualTestSuite) { this._actualTestSuite = await this.generateActualSnapshot(); } return this._actualTestSuite; } /** * Load the integ manifest which contains information * on how to execute the tests * First we try and load the manifest from the integ manifest (i.e. integ.json) * from the cloud assembly. If it doesn't exist, then we fallback to the * "legacy mode" and create a manifest from pragma */ async loadManifest(dir) { const manifest = dir ?? this.snapshotDir; try { const testSuite = integ_test_suite_1.IntegTestSuite.fromPath(manifest); return testSuite; } catch (modernError) { // Only attempt legacy test case if the integ test manifest was not found // For any other errors, e.g. when parsing the manifest fails, we abort. if (!(modernError instanceof integ_manifest_1.NoManifestError)) { throw modernError; } if (this.showOutput) { logger.trace("Failed to load integ test manifest for '%s'. Attempting as deprecated legacy test instead. Error was: %s", manifest, modernError.message ?? String(modernError)); } const testCases = await integ_test_suite_1.LegacyIntegTestSuite.fromLegacy({ cdk: this.cdk, testName: this.test.normalizedTestName, integSourceFilePath: this.test.fileName, listOptions: { ...this.defaultArgs, all: true, app: this.cdkApp, profile: this.profile, output: path.relative(this.directory, this.cdkOutDir), }, }); this.legacyContext = integ_test_suite_1.LegacyIntegTestSuite.getPragmaContext(this.test.fileName); return testCases; } } cleanup() { const cdkOutPath = this.cdkOutDir; if (fs.existsSync(cdkOutPath)) { fs.removeSync(cdkOutPath); } } /** * If there are any destructive changes to a stack then this will record * those in the manifest.json file */ renderTraceData() { const traceData = new Map(); const destructiveChanges = this._destructiveChanges ?? []; destructiveChanges.forEach(change => { const trace = traceData.get(change.stackName); if (trace) { trace.set(change.logicalId, `${DESTRUCTIVE_CHANGES} ${change.impact}`); } else { traceData.set(change.stackName, new Map([ [change.logicalId, `${DESTRUCTIVE_CHANGES} ${change.impact}`], ])); } }); return traceData; } /** * In cases where we do not want to retain the assets, * for example, if the assets are very large. * * Since it is possible to disable the update workflow for individual test * cases, this needs to first get a list of stacks that have the update workflow * disabled and then delete assets that relate to that stack. It does that * by reading the asset manifest for the stack and deleting the asset source */ async removeAssetsFromSnapshot() { const stacks = (await this.actualTestSuite()).getStacksWithoutUpdateWorkflow() ?? []; const manifest = cloud_assembly_1.AssemblyManifestReader.fromPath(this.snapshotDir); const assets = (0, utils_1.flatten)(stacks.map(stack => { return manifest.getAssetLocationsForStack(stack) ?? []; })); assets.forEach(asset => { const fileName = path.join(this.snapshotDir, asset); if (fs.existsSync(fileName)) { if (fs.lstatSync(fileName).isDirectory()) { fs.removeSync(fileName); } else { fs.unlinkSync(fileName); } } }); } /** * Remove the asset cache (.cache/) files from the snapshot. * These are a cache of the asset zips, but we are fine with * re-zipping on deploy */ removeAssetsCacheFromSnapshot() { const files = fs.readdirSync(this.snapshotDir); files.forEach(file => { const fileName = path.join(this.snapshotDir, file); if (fs.lstatSync(fileName).isDirectory() && file === '.cache') { fs.emptyDirSync(fileName); fs.rmdirSync(fileName); } }); } /** * Create the new snapshot. * * If lookups are enabled, then we need create the snapshot by synth'ing again * with the dummy context so that each time the test is run on different machines * (and with different context/env) the diff will not change. * * If lookups are disabled (which means the stack is env agnostic) then just copy * the assembly that was output by the deployment */ async createSnapshot() { if (fs.existsSync(this.snapshotDir)) { fs.removeSync(this.snapshotDir); } const actualTestSuite = await this.actualTestSuite(); // if lookups are enabled then we need to synth again // using dummy context and save that as the snapshot await this.cdk.synth({ app: this.cdkApp, context: this.getContext(actualTestSuite.enableLookups ? exports.DEFAULT_SYNTH_OPTIONS.context : {}), env: exports.DEFAULT_SYNTH_OPTIONS.env, output: path.relative(this.directory, this.snapshotDir), }); await this.cleanupSnapshot(); } /** * Perform some cleanup steps after the snapshot is created * Anytime the snapshot needs to be modified after creation * the logic should live here. */ async cleanupSnapshot() { if (fs.existsSync(this.snapshotDir)) { await this.removeAssetsFromSnapshot(); this.removeAssetsCacheFromSnapshot(); const assembly = cloud_assembly_1.AssemblyManifestReader.fromPath(this.snapshotDir); assembly.cleanManifest(); assembly.recordTrace(this.renderTraceData()); } // if this is a legacy test then create an integ manifest // in the snapshot directory which can be used for the // update workflow. Save any legacyContext as well so that it can be read // the next time const actualTestSuite = await this.actualTestSuite(); if (actualTestSuite.type === 'legacy-test-suite') { actualTestSuite.saveManifest(this.snapshotDir, this.legacyContext); } } getContext(additionalContext) { return { ...currentlyRecommendedAwsCdkLibFlags(), ...this.legacyContext, ...additionalContext, // We originally had PLANNED to set this to ['aws', 'aws-cn'], but due to a programming mistake // it was set to everything. In this PR, set it to everything to not mess up all the snapshots. [cx_api_1.TARGET_PARTITIONS]: undefined, /* ---------------- THE FUTURE LIVES BELOW---------------------------- // Restricting to these target partitions makes most service principals synthesize to // `service.${URL_SUFFIX}`, which is technically *incorrect* (it's only `amazonaws.com` // or `amazonaws.com.cn`, never UrlSuffix for any of the restricted regions) but it's what // most existing integ tests contain, and we want to disturb as few as possible. // [TARGET_PARTITIONS]: ['aws', 'aws-cn'], /* ---------------- END OF THE FUTURE ------------------------------- */ }; } } exports.IntegRunner = IntegRunner; // Default context we run all integ tests with, so they don't depend on the // account of the exercising user. exports.DEFAULT_SYNTH_OPTIONS = { context: { [cloud_assembly_api_1.AVAILABILITY_ZONE_FALLBACK_CONTEXT_KEY]: ['test-region-1a', 'test-region-1b', 'test-region-1c'], 'availability-zones:account=12345678:region=test-region': ['test-region-1a', 'test-region-1b', 'test-region-1c'], 'ssm:account=12345678:parameterName=/aws/service/ami-amazon-linux-latest/amzn-ami-hvm-x86_64-gp2:region=test-region': 'ami-1234', 'ssm:account=12345678:parameterName=/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2:region=test-region': 'ami-1234', 'ssm:account=12345678:parameterName=/aws/service/ecs/optimized-ami/amazon-linux/recommended:region=test-region': '{"image_id": "ami-1234"}', // eslint-disable-next-line @stylistic/max-len 'ami:account=12345678:filters.image-type.0=machine:filters.name.0=amzn-ami-vpc-nat-*:filters.state.0=available:owners.0=amazon:region=test-region': 'ami-1234', 'vpc-provider:account=12345678:filter.isDefault=true:region=test-region:returnAsymmetricSubnets=true': { vpcId: 'vpc-60900905', subnetGroups: [ { type: 'Public', name: 'Public', subnets: [ { subnetId: 'subnet-e19455ca', availabilityZone: 'us-east-1a', routeTableId: 'rtb-e19455ca', }, { subnetId: 'subnet-e0c24797', availabilityZone: 'us-east-1b', routeTableId: 'rtb-e0c24797', }, { subnetId: 'subnet-ccd77395', availabilityZone: 'us-east-1c', routeTableId: 'rtb-ccd77395', }, ], }, ], }, }, env: { CDK_INTEG_ACCOUNT: '12345678', CDK_INTEG_REGION: 'test-region', CDK_INTEG_HOSTED_ZONE_ID: 'Z23ABC4XYZL05B', CDK_INTEG_HOSTED_ZONE_NAME: 'example.com', CDK_INTEG_DOMAIN_NAME: '*.example.com', CDK_INTEG_CERT_ARN: 'arn:aws:acm:test-region:12345678:certificate/86468209-a272-595d-b831-0efb6421265z', CDK_INTEG_SUBNET_ID: 'subnet-0dff1a399d8f6f92c', }, }; /** * Return the currently recommended flags for `aws-cdk-lib`. * * These have been built into the CLI at build time. If this ever gets changed * back to a dynamic load, remember that this source file may be bundled into * a JavaScript bundle, and `__dirname` might not point where you think it does. */ function currentlyRecommendedAwsCdkLibFlags() { return recommendedFlagsFile; } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicnVubmVyLWJhc2UuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJydW5uZXItYmFzZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUF1ZEEsZ0ZBRUM7QUF6ZEQsa0RBQWtEO0FBQ2xELDZCQUE2QjtBQUM3QixvRUFBcUY7QUFFckYsNENBQW9EO0FBQ3BELCtCQUErQjtBQUMvQix5REFBMEU7QUFFMUUsMEVBQTBFO0FBQzFFLG9DQUFtQztBQUNuQyxxQ0FBc0M7QUFFdEMsb0NBQW9DO0FBRXBDLDZEQUFrRTtBQUVsRSw2REFBMkQ7QUFFM0QsTUFBTSxtQkFBbUIsR0FBRyx3QkFBd0IsQ0FBQztBQXFEckQ7O0dBRUc7QUFDSDs7R0FFRztBQUNILE1BQXNCLFdBQVc7SUFDL0I7O09BRUc7SUFDYSxXQUFXLENBQVM7SUFFcEM7O09BRUc7SUFDYSxHQUFHLENBQU87SUFFMUI7O09BRUc7SUFDYSxRQUFRLENBQVM7SUFFakM7Ozs7T0FJRztJQUNnQixNQUFNLENBQVM7SUFFbEM7OztPQUdHO0lBQ2dCLGNBQWMsQ0FBUztJQUUxQzs7O09BR0c7SUFDZ0IsU0FBUyxDQUFTO0lBRXJDOztPQUVHO0lBQ2dCLElBQUksQ0FBWTtJQUVuQzs7T0FFRztJQUNnQixXQUFXLEdBQXNCO1FBQ2xELFlBQVksRUFBRSxLQUFLO1FBQ25CLGFBQWEsRUFBRSxLQUFLO1FBQ3BCLGdCQUFnQixFQUFFLEtBQUs7S0FDeEIsQ0FBQztJQUVGOzs7O09BSUc7SUFDZ0IsU0FBUyxDQUFTO0lBRXJDOztPQUVHO0lBQ2dCLE9BQU8sQ0FBVTtJQUVwQzs7T0FFRztJQUNnQixVQUFVLENBQVU7SUFFN0IsbUJBQW1CLENBQXVCO0lBQzVDLGFBQWEsQ0FBdUI7SUFDcEMsa0JBQWtCLENBQXlDO0lBQzNELGdCQUFnQixDQUF5QztJQUVqRSxZQUFZLE9BQTJCO1FBQ3JDLElBQUksQ0FBQyxJQUFJLEdBQUcsT0FBTyxDQUFDLElBQUksQ0FBQztRQUN6QixJQUFJLENBQUMsU0FBUyxHQUFH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QUFDO1FBQ2hHLHdEQUF3RCxFQUFFLENBQUMsZ0JBQWdCLEVBQUUsZ0JBQWdCLEVBQUUsZ0JBQWdCLENBQUM7UUFDaEgsb0hBQW9ILEVBQUUsVUFBVTtRQUNoSSxxSEFBcUgsRUFBRSxVQUFVO1FBQ2pJLCtHQUErRyxFQUFFLDBCQUEwQjtRQUMzSSw4Q0FBOEM7UUFDOUMsa0pBQWtKLEVBQUUsVUFBVTtRQUM5SixxR0FBcUcsRUFBRTtZQUNyRyxLQUFLLEVBQUUsY0FBYztZQUNyQixZQUFZLEVBQUU7Z0JBQ1o7b0JBQ0UsSUFBSSxFQUFFLFFBQVE7b0JBQ2QsSUFBSSxFQUFFLFFBQVE7b0JBQ2QsT0FBTyxFQUFFO3dCQUNQOzRCQUNFLFFBQVEsRUFBRSxpQkFBaUI7NEJBQzNCLGdCQUFnQixFQUFFLFlBQVk7NEJBQzlCLFlBQVksRUFBRSxjQUFjO3lCQUM3Qjt3QkFDRDs0QkFDRSxRQUFRLEVBQUUsaUJBQWlCOzRCQUMzQixnQkFBZ0IsRUFBRSxZQUFZOzRCQUM5QixZQUFZLEVBQUUsY0FBYzt5QkFDN0I7d0JBQ0Q7NEJBQ0UsUUFBUSxFQUFFLGlCQUFpQjs0QkFDM0IsZ0JBQWdCLEVBQUUsWUFBWTs0QkFDOUIsWUFBWSxFQUFFLGNBQWM7eUJBQzdCO3FCQUNGO2lCQUNGO2FBQ0Y7U0FDRjtLQUNGO0lBQ0QsR0FBRyxFQUFFO1FBQ0gsaUJBQWlCLEVBQUUsVUFBVTtRQUM3QixnQkFBZ0IsRUFBRSxhQUFhO1FBQy9CLHdCQUF3QixFQUFFLGdCQUFnQjtRQUMxQywwQkFBMEIsRUFBRSxhQUFhO1FBQ3pDLHFCQUFxQixFQUFFLGVBQWU7UUFDdEMsa0JBQWtCLEVBQUUsbUZBQW1GO1FBQ3ZHLG1CQUFtQixFQUFFLDBCQUEwQjtLQUNoRDtDQUNGLENBQUM7QUFFRjs7Ozs7O0dBTUc7QUFDSCxTQUFnQixrQ0FBa0M7SUFDaEQsT0FBTyxvQkFBb0IsQ0FBQztBQUM5QixDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLyogZXNsaW50LWRpc2FibGUgQGNka2xhYnMvbm8tbGl0ZXJhbC1wYXJ0aXRpb24gKi9cbmltcG9ydCAqIGFzIHBhdGggZnJvbSAncGF0aCc7XG5pbXBvcnQgeyBBVkFJTEFCSUxJVFlfWk9ORV9GQUxMQkFDS19DT05URVhUX0tFWSB9IGZyb20gJ0Bhd3MtY2RrL2Nsb3VkLWFzc2VtYmx5LWFwaSc7XG5pbXBvcnQgdHlwZSB7IFRlc3RDYXNlLCBEZWZhdWx0Q2RrT3B0aW9ucyB9IGZyb20gJ0Bhd3MtY2RrL2Nsb3VkLWFzc2VtYmx5LXNjaGVtYSc7XG5pbXBvcnQgeyBUQVJHRVRfUEFSVElUSU9OUyB9IGZyb20gJ0Bhd3MtY2RrL2N4LWFwaSc7XG5pbXBvcnQgKiBhcyBmcyBmcm9tICdmcy1leHRyYSc7XG5pbXBvcnQgeyBJbnRlZ1Rlc3RTdWl0ZSwgTGVnYWN5SW50ZWdUZXN0U3VpdGUgfSBmcm9tICcuL2ludGVnLXRlc3Qtc3VpdGUnO1xuaW1wb3J0IHR5cGUgeyBJbnRlZ1Rlc3QgfSBmcm9tICcuL2ludGVncmF0aW9uLXRlc3RzJztcbmltcG9ydCAqIGFzIHJlY29tbWVuZGVkRmxhZ3NGaWxlIGZyb20gJy4uL3JlY29tbWVuZGVkLWZlYXR1cmUtZmxhZ3MuanNvbic7XG5pbXBvcnQgeyBmbGF0dGVuIH0gZnJvbSAnLi4vdXRpbHMnO1xuaW1wb3J0IHsgbWFrZUVuZ2luZSB9IGZyb20gJy4vZW5naW5lJztcbmltcG9ydCB0eXBlIHsgSUNkayB9IGZyb20gJy4uL2VuZ2luZXMvY2RrLWludGVyZmFjZSc7XG5pbXBvcnQgKiBhcyBsb2dnZXIgZnJvbSAnLi4vbG9nZ2VyJztcbmltcG9ydCB0eXBlIHsgTWFuaWZlc3RUcmFjZSB9IGZyb20gJy4vcHJpdmF0ZS9jbG91ZC1hc3NlbWJseSc7XG5pbXBvcnQgeyBBc3NlbWJseU1hbmlmZXN0UmVhZGVyIH0gZnJvbSAnLi9wcml2YXRlL2Nsb3VkLWFzc2VtYmx5JztcbmltcG9ydCB0eXBlIHsgRGVzdHJ1Y3RpdmVDaGFuZ2UgfSBmcm9tICcuLi93b3JrZXJzL2NvbW1vbic7XG5pbXBvcnQgeyBOb01hbmlmZXN0RXJyb3IgfSBmcm9tICcuL3ByaXZhdGUvaW50ZWctbWFuaWZlc3QnO1xuXG5jb25zdCBERVNUUlVDVElWRV9DSEFOR0VTID0gJyEhREVTVFJVQ1RJVkVfQ0hBTkdFUzonO1xuXG4vKipcbiAqIE9wdGlvbnMgZm9yIGNyZWF0aW5nIGFuIGludGVncmF0aW9uIHRlc3QgcnVubmVyXG4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgSW50ZWdSdW5uZXJPcHRpb25zIHtcbiAgLyoqXG4gICAqIEluZm9ybWF0aW9uIGFib3V0IHRoZSB0ZXN0IHRvIHJ1blxuICAgKi9cbiAgcmVhZG9ubHkgdGVzdDogSW50ZWdUZXN0O1xuXG4gIC8qKlxuICAgKiBUaGUgcmVnaW9uIHdoZXJlIHRoZSB0ZXN0IHNob3VsZCBiZSBkZXBsb3llZFxuICAgKi9cbiAgcmVhZG9ubHkgcmVnaW9uOiBzdHJpbmc7XG5cbiAgLyoqXG4gICAqIFRoZSBBV1MgcHJvZmlsZSB0byB1c2Ugd2hlbiBpbnZva2luZyB0aGUgQ0RLIENMSVxuICAgKlxuICAgKiBAZGVmYXVsdCAtIG5vIHByb2ZpbGUgaXMgcGFzc2VkLCB0aGUgZGVmYXVsdCBwcm9maWxlIGlzIHVzZWRcbiAgICovXG4gIHJlYWRvbmx5IHByb2ZpbGU/OiBzdHJpbmc7XG5cbiAgLyoqXG4gICAqIEFkZGl0aW9uYWwgZW52aXJvbm1lbnQgdmFyaWFibGVzIHRoYXQgd2lsbCBiZSBhdmFpbGFibGVcbiAgICogdG8gdGhlIENESyBDTElcbiAgICpcbiAgICogQGRlZmF1bHQgLSBubyBhZGRpdGlvbmFsIGVudmlyb25tZW50IHZhcmlhYmxlc1xuICAgKi9cbiAgcmVhZG9ubHkgZW52PzogeyBbbmFtZTogc3RyaW5nXTogc3RyaW5nIH07XG5cbiAgLyoqXG4gICAqIHRtcCBjZGsub3V0IGRpcmVjdG9yeVxuICAgKlxuICAgKiBAZGVmYXVsdCAtIGRpcmVjdG9yeSB3aWxsIGJlIGBjZGstaW50ZWcub3V0LiR7dGVzdE5hbWV9YFxuICAgKi9cbiAgcmVhZG9ubHkgaW50ZWdPdXREaXI/OiBzdHJpbmc7XG5cbiAgLyoqXG4gICAqIEluc3RhbmNlIG9mIHRoZSBDREsgVG9vbGtpdCBFbmdpbmUgdG8gdXNlXG4gICAqXG4gICAqIEBkZWZhdWx0IC0gYmFzZWQgb24gYGVuZ2luZWAgb3B0aW9uXG4gICAqL1xuICByZWFkb25seSBjZGs/OiBJQ2RrO1xuXG4gIC8qKlxuICAgKiBTaG93IG91dHB1dCBmcm9tIHJ1bm5pbmcgaW50ZWdyYXRpb24gdGVzdHNcbiAgICpcbiAgICogQGRlZmF1bHQgZmFsc2VcbiAgICovXG4gIHJlYWRvbmx5IHNob3dPdXRwdXQ/OiBib29sZWFuO1xufVxuXG4vKipcbiAqIFRoZSBkaWZmZXJlbnQgY29tcG9uZW50cyBvZiBhIHRlc3QgbmFtZVxuICovXG4vKipcbiAqIFJlcHJlc2VudHMgYW4gSW50ZWdyYXRpb24gdGVzdCBydW5uZXJcbiAqL1xuZXhwb3J0IGFic3RyYWN0IGNsYXNzIEludGVnUnVubmVyIHtcbiAgLyoqXG4gICAqIFRoZSBkaXJlY3Rvcnkgd2hlcmUgdGhlIHNuYXBzaG90IHdpbGwgYmUgc3RvcmVkXG4gICAqL1xuICBwdWJsaWMgcmVhZG9ubHkgc25hcHNob3REaXI6IHN0cmluZztcblxuICAvKipcbiAgICogQW4gaW5zdGFuY2Ugb2YgdGhlIENESyAgQ0xJXG4gICAqL1xuICBwdWJsaWMgcmVhZG9ubHkgY2RrOiBJQ2RrO1xuXG4gIC8qKlxuICAgKiBQcmV0dHkgbmFtZSBvZiB0aGUgdGVzdFxuICAgKi9cbiAgcHVibGljIHJlYWRvbmx5IHRlc3ROYW1lOiBzdHJpbmc7XG5cbiAgLyoqXG4gICAqIFRoZSB2YWx1ZSB1c2VkIGluIHRoZSAnLS1hcHAnIENMSSBwYXJhbWV0ZXJcbiAgICpcbiAgICogUGF0aCB0byB0aGUgaW50ZWcgdGVzdCBzb3VyY2UgZmlsZSwgcmVsYXRpdmUgdG8gYHRoaXMuZGlyZWN0b3J5YC5cbiAgICovXG4gIHByb3RlY3RlZCByZWFkb25seSBjZGtBcHA6IHN0cmluZztcblxuICAvKipcbiAgICogVGhlIHBhdGggd2hlcmUgdGhlIGBjZGsuY29udGV4dC5qc29uYCBmaWxlXG4gICAqIHdpbGwgYmUgY3JlYXRlZFxuICAgKi9cbiAgcHJvdGVjdGVkIHJlYWRvbmx5IGNka0NvbnRleHRQYXRoOiBzdHJpbmc7XG5cbiAgLyoqXG4gICAqIFRoZSB3b3JraW5nIGRpcmVjdG9yeSB0aGF0IHRoZSBpbnRlZ3JhdGlvbiB0ZXN0cyB3aWxsIGJlXG4gICAqIGV4ZWN1dGVkIGZyb21cbiAgICovXG4gIHByb3RlY3RlZCByZWFkb25seSBkaXJlY3Rvcnk6IHN0cmluZztcblxuICAvKipcbiAgICogVGhlIHRlc3QgdG8gcnVuXG4gICAqL1xuICBwcm90ZWN0ZWQgcmVhZG9ubHkgdGVzdDogSW50ZWdUZXN0O1xuXG4gIC8qKlxuICAgKiBEZWZhdWx0IG9wdGlvbnMgdG8gcGFzcyB0byB0aGUgQ0RLIENMSVxuICAgKi9cbiAgcHJvdGVjdGVkIHJlYWRvbmx5IGRlZmF1bHRBcmdzOiBEZWZhdWx0Q2RrT3B0aW9ucyA9IHtcbiAgICBwYXRoTWV0YWRhdGE6IGZhbHNlLFxuICAgIGFzc2V0TWV0YWRhdGE6IGZhbHNlLFxuICAgIHZlcnNpb25SZXBvcnRpbmc6IGZhbHNlLFxuICB9O1xuXG4gIC8qKlxuICAgKiBUaGUgZGlyZWN0b3J5IHdoZXJlIHRoZSBDREsgd2lsbCBiZSBzeW50aGVkIHRvXG4gICAqXG4gICAqIFJlbGF0aXZlIHRvIGN3ZC5cbiAgICovXG4gIHByb3RlY3RlZCByZWFkb25seSBjZGtPdXREaXI6IHN0cmluZztcblxuICAvKipcbiAgICogVGhlIHByb2ZpbGUgdG8gdXNlIGZvciB0aGUgQ0RLIENMSSBjYWxsc1xuICAgKi9cbiAgcHJvdGVjdGVkIHJlYWRvbmx5IHByb2ZpbGU/OiBzdHJpbmc7XG5cbiAgLyoqXG4gICAqIFNob3cgb3V0cHV0IGZyb20gdGhlIGludGVnIHRlc3QgcnVuLlxuICAgKi9cbiAgcHJvdGVjdGVkIHJlYWRvbmx5IHNob3dPdXRwdXQ6IGJvb2xlYW47XG5cbiAgcHJvdGVjdGVkIF9kZXN0cnVjdGl2ZUNoYW5nZXM/OiBEZXN0cnVjdGl2ZUNoYW5nZVtdO1xuICBwcml2YXRlIGxlZ2FjeUNvbnRleHQ/OiBSZWNvcmQ8c3RyaW5nLCBhbnk+O1xuICBwcml2YXRlIF9leHBlY3RlZFRlc3RTdWl0ZT86IEludGVnVGVzdFN1aXRlIHwgTGVnYWN5SW50ZWdUZXN0U3VpdGU7XG4gIHByaXZhdGUgX2FjdHVhbFRlc3RTdWl0ZT86IEludGVnVGVzdFN1aXRlIHwgTGVnYWN5SW50ZWdUZXN0U3VpdGU7XG5cbiAgY29uc3RydWN0b3Iob3B0aW9uczogSW50ZWdSdW5uZXJPcHRpb25zKSB7XG4gICAgdGhpcy50ZXN0ID0gb3B0aW9ucy50ZXN0O1xuICAgIHRoaXMuZGlyZWN0b3J5ID0gdGhpcy50ZXN0LmRpcmVjdG9yeTtcbiAgICB0aGlzLnRlc3ROYW1lID0gdGhpcy50ZXN0LnRlc3ROYW1lO1xuICAgIHRoaXMuc25hcHNob3REaXIgPSB0aGlzLnRlc3Quc25hcHNob3REaXI7XG4gICAgdGhpcy5jZGtDb250ZXh0UGF0aCA9IHBhdGguam9pbih0aGlzLmRpcmVjdG9yeSwgJ2Nkay5jb250ZXh0Lmpzb24nKTtcbiAgICB0aGlzLnByb2ZpbGUgPSBvcHRpb25zLnByb2ZpbGU7XG4gICAgdGhpcy5zaG93T3V0cHV0ID0gb3B0aW9ucy5zaG93T3V0cHV0ID8/IGZhbHNlO1xuXG4gICAgdGhpcy5jZGsgPSBvcHRpb25zLmNkayA/PyBtYWtlRW5naW5lKG9wdGlvbnMpO1xuICAgIHRoaXMuY2RrT3V0RGlyID0gb3B0aW9ucy5pbnRlZ091dERpciA/PyB0aGlzLnRlc3QudGVtcG9yYXJ5T3V0cHV0RGlyO1xuXG4gICAgY29uc3QgdGVzdFJ1bkNvbW1hbmQgPSB0aGlzLnRlc3QuYXBwQ29tbWFuZDtcbiAgICB0aGlzLmNka0FwcCA9IHRlc3RSdW5Db21tYW5kLnJlcGxhY2UoJ3tmaWxlUGF0aH0nLCBwYXRoLnJlbGF0aXZlKHRoaXMuZGlyZWN0b3J5LCB0aGlzLnRlc3QuZmlsZU5hbWUpKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBSZXR1cm4gdGhlIGxpc3Qgb2YgZXhwZWN0ZWQgKGkuZS4gZXhpc3RpbmcpIHRlc3QgY2FzZXMgZm9yIHRoaXMgaW50ZWdyYXRpb24gdGVzdFxuICAgKi9cbiAgcHVibGljIGFzeW5jIGV4cGVjdGVkVGVzdHMoKTogUHJvbWlzZTx7IFt0ZXN0TmFtZTogc3RyaW5nXTogVGVzdENhc2UgfSB8IHVuZGVmaW5lZD4ge1xuICAgIHJldHVybiAoYXdhaXQgdGhpcy5leHBlY3RlZFRlc3RTdWl0ZSgpKT8udGVzdFN1aXRlO1xuICB9XG5cbiAgLyoqXG4gICAqIFJldHVybiB0aGUgbGlzdCBvZiBhY3R1YWwgKGkuZS4gbmV3KSB0ZXN0IGNhc2VzIGZvciB0aGlzIGludGVncmF0aW9uIHRlc3RcbiAgICovXG4gIHB1YmxpYyBhc3luYyBhY3R1YWxUZXN0cygpOiBQcm9taXNlPHsgW3Rlc3ROYW1lOiBzdHJpbmddOiBUZXN0Q2FzZSB9IHwgdW5kZWZpbmVkPiB7XG4gICAgcmV0dXJuIChhd2FpdCB0aGlzLmFjdHVhbFRlc3RTdWl0ZSgpKS50ZXN0U3VpdGU7XG4gIH1cblxuICAvKipcbiAgICogR2VuZXJhdGUgYSBuZXcgXCJhY3R1YWxcIiBzbmFwc2hvdCB3aGljaCB3aWxsIGJlIGNvbXBhcmVkIHRvIHRoZVxuICAgKiBleGlzdGluZyBcImV4cGVjdGVkXCIgc25hcHNob3RcbiAgICogVGhpcyB3aWxsIHN5bnRoIGFuZCB0aGVuIGxvYWQgdGhlIGludGVncmF0aW9uIHRlc3QgbWFuaWZlc3RcbiAgICovXG4gIHB1YmxpYyBhc3luYyBnZW5lcmF0ZUFjdHVhbFNuYXBzaG90KCk6IFByb21pc2U8SW50ZWdUZXN0U3VpdGUgfCBMZWdhY3lJbnRlZ1Rlc3RTdWl0ZT4ge1xuICAgIGF3YWl0IHRoaXMuY2RrLnN5bnRoKHtcbiAgICAgIGFwcDogdGhpcy5jZGtBcHAsXG4gICAgICAvLyB3ZSBkb24ndCBrbm93IHRoZSBcImFjdHVhbFwiIGNvbnRleHQgeWV0ICh0aGlzIG1ldGhvZCBpcyB3aGF0IGdlbmVyYXRlcyBpdCkgc28ganVzdFxuICAgICAgLy8gdXNlIHRoZSBcImV4cGVjdGVkXCIgY29udGV4dC4gVGhpcyBpcyBvbmx5IHJ1biBpbiBvcmRlciB0byByZWFkIHRoZSBtYW5pZmVzdFxuICAgICAgY29udGV4dDogdGhpcy5nZXRDb250ZXh0KChhd2FpdCB0aGlzLmV4cGVjdGVkVGVzdFN1aXRlKCkpPy5zeW50aENvbnRleHQpLFxuICAgICAgZW52OiBERUZBVUxUX1NZTlRIX09QVElPTlMuZW52LFxuICAgICAgb3V0cHV0OiBwYXRoLnJlbGF0aXZlKHRoaXMuZGlyZWN0b3J5LCB0aGlzLmNka091dERpciksXG4gICAgfSk7XG4gICAgY29uc3QgbWFuaWZlc3QgPSBhd2FpdCB0aGlzLmxvYWRNYW5pZmVzdCh0aGlzLmNka091dERpcik7XG4gICAgLy8gYWZ0ZXIgd2UgbG9hZCB0aGUgbWFuaWZlc3QgcmVtb3ZlIHRoZSB0bXAgc25hcHNob3RcbiAgICAvLyBzbyB0aGF0IGl0IGRvZXNuJ3QgbWVzcyB1cCB0aGUgcmVhbCBzbmFwc2hvdCBjcmVhdGVkIGxhdGVyXG4gICAgdGhpcy5jbGVhbnVwKCk7XG4gICAgcmV0dXJuIG1hbmlmZXN0O1xuICB9XG5cbiAgLyoqXG4gICAqIFJldHVybnMgdHJ1ZSBpZiBhIHNuYXBzaG90IGFscmVhZHkgZXhpc3RzIGZvciB0aGlzIHRlc3RcbiAgICovXG4gIHB1YmxpYyBoYXNTbmFwc2hvdCgpOiBib29sZWFuIHtcbiAgICByZXR1cm4gZnMuZXhpc3RzU3luYyh0aGlzLnNuYXBzaG90RGlyKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBUaGUgdGVzdCBzdWl0ZSBmcm9tIHRoZSBleGlzdGluZyBzbmFwc2hvdFxuICAgKi9cbiAgcHJvdGVjdGVkIGFzeW5jIGV4cGVjdGVkVGVzdFN1aXRlKCk6IFByb21pc2U8SW50ZWdUZXN0U3VpdGUgfCBMZWdhY3lJbnRlZ1Rlc3RTdWl0ZSB8IHVuZGVmaW5lZD4ge1xuICAgIGlmICghdGhpcy5fZXhwZWN0ZWRUZXN0U3VpdGUgJiYgdGhpcy5oYXNTbmFwc2hvdCgpKSB7XG4gICAgICB0aGlzLl9leHBlY3RlZFRlc3RTdWl0ZSA9IGF3YWl0IHRoaXMubG9hZE1hbmlmZXN0KCk7XG4gICAgfVxuICAgIHJldHVybiB0aGlzLl9leHBlY3RlZFRlc3RTdWl0ZTtcbiAgfVxuXG4gIC8qKlxuICAgKiBUaGUgdGVzdCBzdWl0ZSBmcm9tIHRoZSBuZXcgXCJhY3R1YWxcIiBzbmFwc2hvdFxuICAgKi9cbiAgcHJvdGVjdGVkIGFzeW5jIGFjdHVhbFRlc3RTdWl0ZSgpOiBQcm9taXNlPEludGVnVGVzdFN1aXRlIHwgTGVnYWN5SW50ZWdUZXN0U3VpdGU+IHtcbiAgICBpZiAoIXRoaXMuX2FjdHVhbFRlc3RTdWl0ZSkge1xuICAgICAgdGhpcy5fYWN0dWFsVGVzdFN1aXRlID0gYXdhaXQgdGhpcy5nZW5lcmF0ZUFjdHVhbFNuYXBzaG90KCk7XG4gICAgfVxuICAgIHJldHVybiB0aGlzLl9hY3R1YWxUZXN0U3VpdGU7XG4gIH1cblxuICAvKipcbiAgICogTG9hZCB0aGUgaW50ZWcgbWFuaWZlc3Qgd2hpY2ggY29udGFpbnMgaW5mb3JtYXRpb25cbiAgICogb24gaG93IHRvIGV4ZWN1dGUgdGhlIHRlc3RzXG4gICAqIEZpcnN0IHdlIHRyeSBhbmQgbG9hZCB0aGUgbWFuaWZlc3QgZnJvbSB0aGUgaW50ZWcgbWFuaWZlc3QgKGkuZS4gaW50ZWcuanNvbilcbiAgICogZnJvbSB0aGUgY2xvdWQgYXNzZW1ibHkuIElmIGl0IGRvZXNuJ3QgZXhpc3QsIHRoZW4gd2UgZmFsbGJhY2sgdG8gdGhlXG4gICAqIFwibGVnYWN5IG1vZGVcIiBhbmQgY3JlYXRlIGEgbWFuaWZlc3QgZnJvbSBwcmFnbWFcbiAgICovXG4gIHByb3RlY3RlZCBhc3luYyBsb2FkTWFuaWZlc3QoZGlyPzogc3RyaW5nKTogUHJvbWlzZTxJbnRlZ1Rlc3RTdWl0ZSB8IExlZ2FjeUludGVnVGVzdFN1aXRlPiB7XG4gICAgY29uc3QgbWFuaWZlc3QgPSBkaXIgPz8gdGhpcy5zbmFwc2hvdERpcjtcbiAgICB0cnkge1xuICAgICAgY29uc3QgdGVzdFN1aXRlID0gSW50ZWdUZXN0U3VpdGUuZnJvbVBhdGgobWFuaWZlc3QpO1xuICAgICAgcmV0dXJuIHRlc3RTdWl0ZTtcbiAgICB9IGNhdGNoIChtb2Rlcm5FcnJvcjogYW55KSB7XG4gICAgICAvLyBPbmx5IGF0dGVtcHQgbGVnYWN5IHRlc3QgY2FzZSBpZiB0aGUgaW50ZWcgdGVzdCBtYW5pZmVzdCB3YXMgbm90IGZvdW5kXG4gICAgICAvLyBGb3IgYW55IG90aGVyIGVycm9ycywgZS5nLiB3aGVuIHBhcnNpbmcgdGhlIG1hbmlmZXN0IGZhaWxzLCB3ZSBhYm9ydC5cbiAgICAgIGlmICghKG1vZGVybkVycm9yIGluc3RhbmNlb2YgTm9NYW5pZmVzdEVycm9yKSkge1xuICAgICAgICB0aHJvdyBtb2Rlcm5FcnJvcjtcbiAgICAgIH1cblxuICAgICAgaWYgKHRoaXMuc2hvd091dHB1dCkge1xuICAgICAgICBsb2dnZXIudHJhY2UoXG4gICAgICAgICAgXCJGYWlsZWQgdG8gbG9hZCBpbnRlZyB0ZXN0IG1hbmlmZXN0IGZvciAnJXMnLiBBdHRlbXB0aW5nIGFzIGRlcHJlY2F0ZWQgbGVnYWN5IHRlc3QgaW5zdGVhZC4gRXJyb3Igd2FzOiAlc1wiLFxuICAgICAgICAgIG1hbmlmZXN0LFxuICAgICAgICAgIG1vZGVybkVycm9yLm1lc3NhZ2UgPz8gU3RyaW5nKG1vZGVybkVycm9yKSxcbiAgICAgICAgKTtcbiAgICAgIH1cblxuICAgICAgY29uc3QgdGVzdENhc2VzID0gYXdhaXQgTGVnYWN5SW50ZWdUZXN0U3VpdGUuZnJvbUxlZ2FjeSh7XG4gICAgICAgIGNkazogdGhpcy5jZGssXG4gICAgICAgIHRlc3ROYW1lOiB0aGlzLnRlc3Qubm9ybWFsaXplZFRlc3ROYW1lLFxuICAgICAgICBpbnRlZ1NvdXJjZUZpbGVQYXRoOiB0aGlzLnRlc3QuZmlsZU5hbWUsXG4gICAgICAgIGxpc3RPcHRpb25zOiB7XG4gICAgICAgICAgLi4udGhpcy5kZWZhdWx0QXJncyxcbiAgICAgICAgICBhbGw6IHRydWUsXG4gICAgICAgICAgYXBwOiB0aGlzLmNka0FwcCxcbiAgICAgICAgICBwcm9maWxlOiB0aGlzLnByb2ZpbGUsXG4gICAgICAgICAgb3V0cHV0OiBwYXRoLnJlbGF0aXZlKHRoaXMuZGlyZWN0b3J5LCB0aGlzLmNka091dERpciksXG4gICAgICAgIH0sXG4gICAgICB9KTtcbiAgICAgIHRoaXMubGVnYWN5Q29udGV4dCA9IExlZ2FjeUludGVnVGVzdFN1aXRlLmdldFByYWdtYUNvbnRleHQodGhpcy50ZXN0LmZpbGVOYW1lKTtcbiAgICAgIHJldHVybiB0ZXN0Q2FzZXM7XG4gICAgfVxuICB9XG5cbiAgcHJvdGVjdGVkIGNsZWFudXAoKTogdm9pZCB7XG4gICAgY29uc3QgY2RrT3V0UGF0aCA9IHRoaXMuY2RrT3V0RGlyO1xuICAgIGlmIChmcy5leGlzdHNTeW5jKGNka091dFBhdGgpKSB7XG4gICAgICBmcy5yZW1vdmVTeW5jKGNka091dFBhdGgpO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBJZiB0aGVyZSBhcmUgYW55IGRlc3RydWN0aXZlIGNoYW5nZXMgdG8gYSBzdGFjayB0aGVuIHRoaXMgd2lsbCByZWNvcmRcbiAgICogdGhvc2UgaW4gdGhlIG1hbmlmZXN0Lmpzb24gZmlsZVxuICAgKi9cbiAgcHJpdmF0ZSByZW5kZXJUcmFjZURhdGEoKTogTWFuaWZlc3RUcmFjZSB7XG4gICAgY29uc3QgdHJhY2VEYXRhOiBNYW5pZmVzdFRyYWNlID0gbmV3IE1hcCgpO1xuICAgIGNvbnN0IGRlc3RydWN0aXZlQ2hhbmdlcyA9IHRoaXMuX2Rlc3RydWN0aXZlQ2hhbmdlcyA/PyBbXTtcbiAgICBkZXN0cnVjdGl2ZUNoYW5nZXMuZm9yRWFjaChjaGFuZ2UgPT4ge1xuICAgICAgY29uc3QgdHJhY2UgPSB0cmFjZURhdGEuZ2V0KGNoYW5nZS5zdGFja05hbWUpO1xuICAgICAgaWYgKHRyYWNlKSB7XG4gICAgICAgIHRyYWNlLnNldChjaGFuZ2UubG9naWNhbElkLCBgJHtERVNUUlVDVElWRV9DSEFOR0VTfSAke2NoYW5nZS5pbXBhY3R9YCk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICB0cmFjZURhdGEuc2V0KGNoYW5nZS5zdGFja05hbWUsIG5ldyBNYXAoW1xuICAgICAgICAgIFtjaGFuZ2UubG9naWNhbElkLCBgJHtERVNUUlVDVElWRV9DSEFOR0VTfSAke2NoYW5nZS5pbXBhY3R9YF0sXG4gICAgICAgIF0pKTtcbiAgICAgIH1cbiAgICB9KTtcbiAgICByZXR1cm4gdHJhY2VEYXRhO1xuICB9XG5cbiAgLyoqXG4gICAqIEluIGNhc2VzIHdoZXJlIHdlIGRvIG5vdCB3YW50IHRvIHJldGFpbiB0aGUgYXNzZXRzLFxuICAgKiBmb3IgZXhhbXBsZSwgaWYgdGhlIGFzc2V0cyBhcmUgdmVyeSBsYXJnZS5cbiAgICpcbiAgICogU2luY2UgaXQgaXMgcG9zc2libGUgdG8gZGlzYWJsZSB0aGUgdXBkYXRlIHdvcmtmbG93IGZvciBpbmRpdmlkdWFsIHRlc3RcbiAgICogY2FzZXMsIHRoaXMgbmVlZHMgdG8gZmlyc3QgZ2V0IGEgbGlzdCBvZiBzdGFja3MgdGhhdCBoYXZlIHRoZSB1cGRhdGUgd29ya2Zsb3dcbiAgICogZGlzYWJsZWQgYW5kIHRoZW4gZGVsZXRlIGFzc2V0cyB0aGF0IHJlbGF0ZSB0byB0aGF0IHN0YWNrLiBJdCBkb2VzIHRoYXRcbiAgICogYnkgcmVhZGluZyB0aGUgYXNzZXQgbWFuaWZlc3QgZm9yIHRoZSBzdGFjayBhbmQgZGVsZXRpbmcgdGhlIGFzc2V0IHNvdXJjZVxuICAgKi9cbiAgcHJvdGVjdGVkIGFzeW5jIHJlbW92ZUFzc2V0c0Zyb21TbmFwc2hvdCgpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBjb25zdCBzdGFja3MgPSAoYXdhaXQgdGhpcy5hY3R1YWxUZXN0U3VpdGUoKSkuZ2V0U3RhY2tzV2l0aG91dFVwZGF0ZVdvcmtmbG93KCkgPz8gW107XG4gICAgY29uc3QgbWFuaWZlc3QgPSBBc3NlbWJseU1hbmlmZXN0UmVhZGVyLmZyb21QYXRoKHRoaXMuc25hcHNob3REaXIpO1xuICAgIGNvbnN0IGFzc2V0cyA9IGZsYXR0ZW4oc3RhY2tzLm1hcChzdGFjayA9PiB7XG4gICAgICByZXR1cm4gbWFuaWZlc3QuZ2V0QXNzZXRMb2NhdGlvbnNGb3JTdGFjayhzdGFjaykgPz8gW107XG4gICAgfSkpO1xuXG4gICAgYXNzZXRzLmZvckVhY2goYXNzZXQgPT4ge1xuICAgICAgY29uc3QgZmlsZU5hbWUgPSBwYXRoLmpvaW4odGhpcy5zbmFwc2hvdERpciwgYXNzZXQpO1xuICAgICAgaWYgKGZzLmV4aXN0c1N5bmMoZmlsZU5hbWUpKSB7XG4gICAgICAgIGlmIChmcy5sc3RhdFN5bmMoZmlsZU5hbWUpLmlzRGlyZWN0b3J5KCkpIHtcbiAgICAgICAgICBmcy5yZW1vdmVTeW5jKGZpbGVOYW1lKTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBmcy51bmxpbmtTeW5jKGZpbGVOYW1lKTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH0pO1xuICB9XG5cbiAgLyoqXG4gICAqIFJlbW92ZSB0aGUgYXNzZXQgY2FjaGUgKC5jYWNoZS8pIGZpbGVzIGZyb20gdGhlIHNuYXBzaG90LlxuICAgKiBUaGVzZSBhcmUgYSBjYWNoZSBvZiB0aGUgYXNzZXQgemlwcywgYnV0IHdlIGFyZSBmaW5lIHdpdGhcbiAgICogcmUtemlwcGluZyBvbiBkZXBsb3lcbiAgICovXG4gIHByb3RlY3RlZCByZW1vdmVBc3NldHNDYWNoZUZyb21TbmFwc2hvdCgpOiB2b2lkIHtcbiAgICBjb25zdCBmaWxlcyA9IGZzLnJlYWRkaXJTeW5jKHRoaXMuc25hcHNob3REaXIpO1xuICAgIGZpbGVzLmZvckVhY2goZmlsZSA9PiB7XG4gICAgICBjb25zdCBmaWxlTmFtZSA9IHBhdGguam9pbih0aGlzLnNuYXBzaG90RGlyLCBmaWxlKTtcbiAgICAgIGlmIChmcy5sc3RhdFN5bmMoZmlsZU5hbWUpLmlzRGlyZWN0b3J5KCkgJiYgZmlsZSA9PT0gJy5jYWNoZScpIHtcbiAgICAgICAgZnMuZW1wdHlEaXJTeW5jKGZpbGVOYW1lKTtcbiAgICAgICAgZnMucm1kaXJTeW5jKGZpbGVOYW1lKTtcbiAgICAgIH1cbiAgICB9KTtcbiAgfVxuXG4gIC8qKlxuICAgKiBDcmVhdGUgdGhlIG5ldyBzbmFwc2hvdC5cbiAgICpcbiAgICogSWYgbG9va3VwcyBhcmUgZW5hYmxlZCwgdGhlbiB3ZSBuZWVkIGNyZWF0ZSB0aGUgc25hcHNob3QgYnkgc3ludGgnaW5nIGFnYWluXG4gICAqIHdpdGggdGhlIGR1bW15IGNvbnRleHQgc28gdGhhdCBlYWNoIHRpbWUgdGhlIHRlc3QgaXMgcnVuIG9uIGRpZmZlcmVudCBtYWNoaW5lc1xuICAgKiAoYW5kIHdpdGggZGlmZmVyZW50IGNvbnRleHQvZW52KSB0aGUgZGlmZiB3aWxsIG5vdCBjaGFuZ2UuXG4gICAqXG4gICAqIElmIGxvb2t1cHMgYXJlIGRpc2FibGVkICh3aGljaCBtZWFucyB0aGUgc3RhY2sgaXMgZW52IGFnbm9zdGljKSB0aGVuIGp1c3QgY29weVxuICAgKiB0aGUgYXNzZW1ibHkgdGhhdCB3YXMgb3V0cHV0IGJ5IHRoZSBkZXBsb3ltZW50XG4gICAqL1xuICBwcm90ZWN0ZWQgYXN5bmMgY3JlYXRlU25hcHNob3QoKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgaWYgKGZzLmV4aXN0c1N5bmModGhpcy5zbmFwc2hvdERpcikpIHtcbiAgICAgIGZzLnJlbW92ZVN5bmModGhpcy5zbmFwc2hvdERpcik7XG4gICAgfVxuXG4gICAgY29uc3QgYWN0dWFsVGVzdFN1aXRlID0gYXdhaXQgdGhpcy5hY3R1YWxUZXN0U3VpdGUoKTtcblxuICAgIC8vIGlmIGxvb2t1cHMgYXJlIGVuYWJsZWQgdGhlbiB3ZSBuZWVkIHRvIHN5bnRoIGFnYWluXG4gICAgLy8gdXNpbmcgZHVtbXkgY29udGV4dCBhbmQgc2F2ZSB0aGF0IGFzIHRoZSBzbmFwc2hvdFxuICAgIGF3YWl0IHRoaXMuY2RrLnN5bnRoKHtcbiAgICAgIGFwcDogdGhpcy5jZGtBcHAsXG4gICAgICBjb250ZXh0OiB0aGlzLmdldENvbnRleHQoYWN0dWFsVGVzdFN1aXRlLmVuYWJsZUxvb2t1cHMgPyBERUZBVUxUX1NZTlRIX09QVElPTlMuY29udGV4dCA6IHt9KSxcbiAgICAgIGVudjogREVGQVVMVF9TWU5USF9PUFRJT05TLmVudixcbiAgICAgIG91dHB1dDogcGF0aC5yZWxhdGl2ZSh0aGlzLmRpcmVjdG9yeSwgdGhpcy5zbmFwc2hvdERpciksXG4gICAgfSk7XG5cbiAgICBhd2FpdCB0aGlzLmNsZWFudXBTbmFwc2hvdCgpO1xuICB9XG5cbiAgLyoqXG4gICAqIFBlcmZvcm0gc29tZSBjbGVhbnVwIHN0ZXBzIGFmdGVyIHRoZSBzbmFwc2hvdCBpcyBjcmVhdGVkXG4gICAqIEFueXRpbWUgdGhlIHNuYXBzaG90IG5lZWRzIHRvIGJlIG1vZGlmaWVkIGFmdGVyIGNyZWF0aW9uXG4gICAqIHRoZSBsb2dpYyBzaG91bGQgbGl2ZSBoZXJlLlxuICAgKi9cbiAgcHJpdmF0ZSBhc3luYyBjbGVhbnVwU25hcHNob3QoKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgaWYgKGZzLmV4aXN0c1N5bmModGhpcy5zbmFwc2hvdERpcikpIHtcbiAgICAgIGF3YWl0IHRoaXMucmVtb3ZlQXNzZXRzRnJvbVNuYXBzaG90KCk7XG4gICAgICB0aGlzLnJlbW92ZUFzc2V0c0NhY2hlRnJvbVNuYXBzaG90KCk7XG4gICAgICBjb25zdCBhc3NlbWJseSA9IEFzc2VtYmx5TWFuaWZlc3RSZWFkZXIuZnJvbVBhdGgodGhpcy5zbmFwc2hvdERpcik7XG4gICAgICBhc3NlbWJseS5jbGVhbk1hbmlmZXN0KCk7XG4gICAgICBhc3NlbWJseS5yZWNvcmRUcmFjZSh0aGlzLnJlbmRlclRyYWNlRGF0YSgpKTtcbiAgICB9XG5cbiAgICAvLyBpZiB0aGlzIGlzIGEgbGVnYWN5IHRlc3QgdGhlbiBjcmVhdGUgYW4gaW50ZWcgbWFuaWZlc3RcbiAgICAvLyBpbiB0aGUgc25hcHNob3QgZGlyZWN0b3J5IHdoaWNoIGNhbiBiZSB1c2VkIGZvciB0aGVcbiAgICAvLyB1cGRhdGUgd29ya2Zsb3cuIFNhdmUgYW55IGxlZ2FjeUNvbnRleHQgYXMgd2VsbCBzbyB0aGF0IGl0IGNhbiBiZSByZWFkXG4gICAgLy8gdGhlIG5leHQgdGltZVxuICAgIGNvbnN0IGFjdHVhbFRlc3RTdWl0ZSA9IGF3YWl0IHRoaXMuYWN0dWFsVGVzdFN1aXRlKCk7XG4gICAgaWYgKGFjdHVhbFRlc3RTdWl0ZS50eXBlID09PSAnbGVnYWN5LXRlc3Qtc3VpdGUnKSB7XG4gICAgICAoYWN0dWFsVGVzdFN1aXRlIGFzIExlZ2FjeUludGVnVGVzdFN1aXRlKS5zYXZlTWFuaWZlc3QodGhpcy5zbmFwc2hvdERpciwgdGhpcy5sZWdhY3lDb250ZXh0KTtcbiAgICB9XG4gIH1cblxuICBwcm90ZWN0ZWQgZ2V0Q29udGV4dChhZGRpdGlvbmFsQ29udGV4dD86IFJlY29yZDxzdHJpbmcsIGFueT4pOiBSZWNvcmQ8c3RyaW5nLCBhbnk+IHtcbiAgICByZXR1cm4ge1xuICAgICAgLi4uY3VycmVudGx5UmVjb21tZW5kZWRBd3NDZGtMaWJGbGFncygpLFxuICAgICAgLi4udGhpcy5sZWdhY3lDb250ZXh0LFxuICAgICAgLi4uYWRkaXRpb25hbENvbnRleHQsXG5cbiAgICAgIC8vIFdlIG9yaWdpbmFsbHkgaGFkIFBMQU5ORUQgdG8gc2V0IHRoaXMgdG8gWydhd3MnLCAnYXdzLWNuJ10sIGJ1dCBkdWUgdG8gYSBwcm9ncmFtbWluZyBtaXN0YWtlXG4gICAgICAvLyBpdCB3YXMgc2V0IHRvIGV2ZXJ5dGhpbmcuIEluIHRoaXMgUFIsIHNldCBpdCB0byBldmVyeXRoaW5nIHRvIG5vdCBtZXNzIHVwIGFsbCB0aGUgc25hcHNob3RzLlxuICAgICAgW1RBUkdFVF9QQVJUSVRJT05TXTogdW5kZWZpbmVkLFxuXG4gICAgICAvKiAtLS0tLS0tLS0tLS0tLS0tIFRIRSBGVVRVUkUgTElWRVMgQkVMT1ctLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gICAgICAvLyBSZXN0cmljdGluZyB0byB0aGVzZSB0YXJnZXQgcGFydGl0aW9ucyBtYWtlcyBtb3N0IHNlcnZpY2UgcHJpbmNpcGFscyBzeW50aGVzaXplIHRvXG4gICAgICAvLyBgc2VydmljZS4ke1VSTF9TVUZGSVh9YCwgd2hpY2ggaXMgdGVjaG5pY2FsbHkgKmluY29ycmVjdCogKGl0J3Mgb25seSBgYW1hem9uYXdzLmNvbWBcbiAgICAgIC8vIG9yIGBhbWF6b25hd3MuY29tLmNuYCwgbmV2ZXIgVXJsU3VmZml4IGZvciBhbnkgb2YgdGhlIHJlc3RyaWN0ZWQgcmVnaW9ucykgYnV0IGl0J3Mgd2hhdFxuICAgICAgLy8gbW9zdCBleGlzdGluZyBpbnRlZyB0ZXN0cyBjb250YWluLCBhbmQgd2Ugd2FudCB0byBkaXN0dXJiIGFzIGZldyBhcyBwb3NzaWJsZS5cbiAgICAgIC8vIFtUQVJHRVRfUEFSVElUSU9OU106IFsnYXdzJywgJ2F3cy1jbiddLFxuICAgICAgLyogLS0tLS0tLS0tLS0tLS0tLSBFTkQgT0YgVEhFIEZVVFVSRSAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tICovXG4gICAgfTtcbiAgfVxufVxuXG4vLyBEZWZhdWx0IGNvbnRleHQgd2UgcnVuIGFsbCBpbnRlZyB0ZXN0cyB3aXRoLCBzbyB0aGV5IGRvbid0IGRlcGVuZCBvbiB0aGVcbi8vIGFjY291bnQgb2YgdGhlIGV4ZXJjaXNpbmcgdXNlci5cbmV4cG9ydCBjb25zdCBERUZBVUxUX1NZTlRIX09QVElPTlMgPSB7XG4gIGNvbnRleHQ6IHtcbiAgICBbQVZBSUxBQklMSVRZX1pPTkVfRkFMTEJBQ0tfQ09OVEVYVF9LRVldOiBbJ3Rlc3QtcmVnaW9uLTFhJywgJ3Rlc3QtcmVnaW9uLTFiJywgJ3Rlc3QtcmVnaW9uLTFjJ10sXG4gICAgJ2F2YWlsYWJpbGl0eS16b25lczphY2NvdW50PTEyMzQ1Njc4OnJlZ2lvbj10ZXN0LXJlZ2lvbic6IFsndGVzdC1yZWdpb24tMWEnLCAndGVzdC1yZWdpb24tMWInLCAndGVzdC1yZWdpb24tMWMnXSxcbiAgICAnc3NtOmFjY291bnQ9MTIzNDU2Nzg6cGFyYW1ldGVyTmFtZT0vYXdzL3NlcnZpY2UvYW1pLWFtYXpvbi1saW51eC1sYXRlc3QvYW16bi1hbWktaHZtLXg4Nl82NC1ncDI6cmVnaW9uPXRlc3QtcmVnaW9uJzogJ2FtaS0xMjM0JyxcbiAgICAnc3NtOmFjY291bnQ9MTIzNDU2Nzg6cGFyYW1ldGVyTmFtZT0vYXdzL3NlcnZpY2UvYW1pLWFtYXpvbi1saW51eC1sYXRlc3QvYW16bjItYW1pLWh2bS14ODZfNjQtZ3AyOnJlZ2lvbj10ZXN0LXJlZ2lvbic6ICdhbWktMTIzNCcsXG4gICAgJ3NzbTphY2NvdW50PTEyMzQ1Njc4OnBhcmFtZXRlck5hbWU9L2F3cy9zZXJ2aWNlL2Vjcy9vcHRpbWl6ZWQtYW1pL2FtYXpvbi1saW51eC9yZWNvbW1lbmRlZDpyZWdpb249dGVzdC1yZWdpb24nOiAne1wiaW1hZ2VfaWRcIjogXCJhbWktMTIzNFwifScsXG4gICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIEBzdHlsaXN0aWMvbWF4LWxlblxuICAgICdhbWk6YWNjb3VudD0xMjM0NTY3ODpmaWx0ZXJzLmltYWdlLXR5cGUuMD1tYWNoaW5lOmZpbHRlcnMubmFtZS4wPWFtem4tYW1pLXZwYy1uYXQtKjpmaWx0ZXJzLnN0YXRlLjA9YXZhaWxhYmxlOm93bmVycy4wPWFtYXpvbjpyZWdpb249dGVzdC1yZWdpb24nOiAnYW1pLTEyMzQnLFxuICAgICd2cGMtcHJvdmlkZXI6YWNjb3VudD0xMjM0NTY3ODpmaWx0ZXIuaXNEZWZhdWx0PXRydWU6cmVnaW9uPXRlc3QtcmVnaW9uOnJldHVybkFzeW1tZXRyaWNTdWJuZXRzPXRydWUnOiB7XG4gICAgICB2cGNJZDogJ3ZwYy02MDkwMDkwNScsXG4gICAgICBzdWJuZXRHcm91cHM6IFtcbiAgICAgICAge1xuICAgICAgICAgIHR5cGU6ICdQdWJsaWMnLFxuICAgICAgICAgIG5hbWU6ICdQdWJsaWMnLFxuICAgICAgICAgIHN1Ym5ldHM6IFtcbiAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgc3VibmV0SWQ6ICdzdWJuZXQtZTE5NDU1Y2EnLFxuICAgICAgICAgICAgICBhdmFpbGFiaWxpdHlab25lOiAndXMtZWFzdC0xYScsXG4gICAgICAgICAgICAgIHJvdXRlVGFibGVJZDogJ3J0Yi1lMTk0NTVjYScsXG4gICAgICAgICAgICB9LFxuICAgICAgICAgICAge1xuICAgICAgICAgICAgICBzdWJuZXRJZDogJ3N1Ym5ldC1lMGMyNDc5NycsXG4gICAgICAgICAgICAgIGF2YWlsYWJpbGl0eVpvbmU6ICd1cy1lYXN0LTFiJyxcbiAgICAgICAgICAgICAgcm91dGVUYWJsZUlkOiAncnRiLWUwYzI0Nzk3JyxcbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgICB7XG4gICAgICAgICAgICAgIHN1Ym5ldElkOiAnc3VibmV0LWNjZDc3Mzk1JyxcbiAgICAgICAgICAgICAgYXZhaWxhYmlsaXR5Wm9uZTogJ3VzLWVhc3QtMWMnLFxuICAgICAgICAgICAgICByb3V0ZVRhYmxlSWQ6ICdydGItY2NkNzczOTUnLFxuICAgICAgICAgICAgfSxcbiAgICAgICAgICBdLFxuICAgICAgICB9LFxuICAgICAgXSxcbiAgICB9LFxuICB9LFxuICBlbnY6IHtcbiAgICBDREtfSU5URUdfQUNDT1VOVDogJzEyMzQ1Njc4JyxcbiAgICBDREtfSU5URUdfUkVHSU9OOiAndGVzdC1yZWdpb24nLFxuICAgIENES19JTlRFR19IT1NURURfWk9ORV9JRDogJ1oyM0FCQzRYWVpMMDVCJyxcbiAgICBDREtfSU5URUdfSE9TVEVEX1pPTkVfTkFNRTogJ2V4YW1wbGUuY29tJyxcbiAgICBDREtfSU5URUdfRE9NQUlOX05BTUU6ICcqLmV4YW1wbGUuY29tJyxcbiAgICBDREtfSU5URUdfQ0VSVF9BUk46ICdhcm46YXdzOmFjbTp0ZXN0LXJlZ2lvbjoxMjM0NTY3ODpjZXJ0aWZpY2F0ZS84NjQ2ODIwOS1hMjcyLTU5NWQtYjgzMS0wZWZiNjQyMTI2NXonLFxuICAgIENES19JTlRFR19TVUJORVRfSUQ6ICdzdWJuZXQtMGRmZjFhMzk5ZDhmNmY5MmMnLFxuICB9LFxufTtcblxuLyoqXG4gKiBSZXR1cm4gdGhlIGN1cnJlbnRseSByZWNvbW1lbmRlZCBmbGFncyBmb3IgYGF3cy1jZGstbGliYC5cbiAqXG4gKiBUaGVzZSBoYXZlIGJlZW4gYnVpbHQgaW50byB0aGUgQ0xJIGF0IGJ1aWxkIHRpbWUuIElmIHRoaXMgZXZlciBnZXRzIGNoYW5nZWRcbiAqIGJhY2sgdG8gYSBkeW5hbWljIGxvYWQsIHJlbWVtYmVyIHRoYXQgdGhpcyBzb3VyY2UgZmlsZSBtYXkgYmUgYnVuZGxlZCBpbnRvXG4gKiBhIEphdmFTY3JpcHQgYnVuZGxlLCBhbmQgYF9fZGlybmFtZWAgbWlnaHQgbm90IHBvaW50IHdoZXJlIHlvdSB0aGluayBpdCBkb2VzLlxuICovXG5leHBvcnQgZnVuY3Rpb24gY3VycmVudGx5UmVjb21tZW5kZWRBd3NDZGtMaWJGbGFncygpIHtcbiAgcmV0dXJuIHJlY29tbWVuZGVkRmxhZ3NGaWxlO1xufVxuIl19