UNPKG

@aws-cdk/integ-runner

Version:

CDK Integration Testing Tool

215 lines 33.8 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.IntegrationTests = exports.IntegTest = void 0; const path = require("path"); const fs = require("fs-extra"); const CDK_OUTDIR_PREFIX = 'cdk-integ.out'; /** * Derived information for IntegTests */ class IntegTest { constructor(info) { this.info = info; this.appCommand = info.appCommand ?? 'node {filePath}'; // for consistency, always run the CDK apps under test from the CWD // this is especially important for languages that use the CWD to discover assets // @see https://github.com/aws/aws-cdk-cli/issues/638 this.directory = process.cwd(); this.absoluteFileName = path.resolve(info.fileName); this.fileName = path.relative(this.directory, info.fileName); this.discoveryRelativeFileName = path.relative(info.discoveryRoot, info.fileName); // We treat the discovery root as the base for display names // Looks either like `integ.mytest` or `package/test/integ.mytest` const parsed = path.parse(this.fileName); this.testName = path.join(path.relative(this.info.discoveryRoot, parsed.dir), parsed.name); this.normalizedTestName = parsed.name; this.snapshotDir = path.join(parsed.dir, `${parsed.base}.snapshot`); this.temporaryOutputDir = path.join(parsed.dir, `${CDK_OUTDIR_PREFIX}.${parsed.base}.snapshot`); } /** * Whether this test matches the user-given name * * We are very lenient here. A name matches if it matches: * * - The CWD-relative filename * - The discovery root-relative filename * - The suite name * - The absolute filename */ matches(name) { return [ this.fileName, this.discoveryRelativeFileName, this.testName, this.absoluteFileName, ].includes(name); } } exports.IntegTest = IntegTest; /** * Returns the name of the Python executable for the current OS */ function pythonExecutable() { let python = 'python3'; if (process.platform === 'win32') { python = 'python'; } return python; } /** * Discover integration tests */ class IntegrationTests { constructor(directory) { this.directory = directory; } /** * Get integration tests discovery options from CLI options */ async fromCliOptions(options) { const baseOptions = { tests: options.tests, exclude: options.exclude, strict: options.strict, }; // Explicitly set both, app and test-regex if (options.app && options.testRegex) { return this.discover({ testCases: { [options.app]: options.testRegex, }, ...baseOptions, }); } // Use the selected presets if (!options.app && !options.testRegex) { // Only case with multiple languages, i.e. the only time we need to check the special case const ignoreUncompiledTypeScript = options.language?.includes('javascript') && options.language?.includes('typescript'); return this.discover({ testCases: this.getLanguagePresets(options.language), ...baseOptions, }, ignoreUncompiledTypeScript); } // Only one of app or test-regex is set, with a single preset selected // => override either app or test-regex if (options.language?.length === 1) { const [presetApp, presetTestRegex] = this.getLanguagePreset(options.language[0]); return this.discover({ testCases: { [options.app ?? presetApp]: options.testRegex ?? presetTestRegex, }, ...baseOptions, }); } // Only one of app or test-regex is set, with multiple presets // => impossible to resolve const option = options.app ? '--app' : '--test-regex'; throw new Error(`Only a single "--language" can be used with "${option}". Alternatively provide both "--app" and "--test-regex" to fully customize the configuration.`); } /** * Get the default configuration for a language */ getLanguagePreset(language) { const languagePresets = { javascript: ['node {filePath}', ['^integ\\..*\\.js$']], typescript: ['node -r ts-node/register {filePath}', ['^integ\\.(?!.*\\.d\\.ts$).*\\.ts$']], python: [`${pythonExecutable()} {filePath}`, ['^integ_.*\\.py$']], go: ['go run {filePath}', ['^integ_.*\\.go$']], }; return languagePresets[language]; } /** * Get the config for all selected languages */ getLanguagePresets(languages = []) { return Object.fromEntries(languages .map(language => this.getLanguagePreset(language)) .filter(Boolean)); } /** * If the user provides a list of tests, these can either be a list of tests to include or a list of tests to exclude. * * - If it is a list of tests to include then we discover all available tests and check whether they have provided valid tests. * If they have provided a test name that we don't find, then we write out that error message. * - If it is a list of tests to exclude, then we discover all available tests and filter out the tests that were provided by the user. */ filterTests(discoveredTests, requestedTests, exclude, strict) { if (!requestedTests) { return discoveredTests; } const allTests = discoveredTests.filter(t => { const matches = requestedTests.some(pattern => t.matches(pattern)); return matches !== !!exclude; // Looks weird but is equal to (matches && !exclude) || (!matches && exclude) }); // If not excluding, all patterns must have matched at least one test if (!exclude) { const unmatchedPatterns = requestedTests.filter(pattern => !discoveredTests.some(t => t.matches(pattern))); for (const unmatched of unmatchedPatterns) { process.stderr.write(`No such integ test: ${unmatched}\n`); } if (unmatchedPatterns.length > 0) { process.stderr.write(`Available tests: ${discoveredTests.map(t => t.discoveryRelativeFileName).join(' ')}\n`); if (strict) { throw new Error(`Strict mode: ${unmatchedPatterns.length} test(s) not found: ${unmatchedPatterns.join(', ')}`); } return []; } } return allTests; } /** * Takes an optional list of tests to look for, otherwise * it will look for all tests from the directory * * @param tests - Tests to include or exclude, undefined means include all tests. * @param exclude - Whether the 'tests' list is inclusive or exclusive (inclusive by default). */ async discover(options, ignoreUncompiledTypeScript = false) { const files = await this.readTree(); const testCases = Object.entries(options.testCases) .flatMap(([appCommand, patterns]) => files .filter(fileName => patterns.some((pattern) => { const regex = new RegExp(pattern); return regex.test(fileName) || regex.test(path.basename(fileName)); })) .map(fileName => new IntegTest({ discoveryRoot: this.directory, fileName, appCommand, }))); const discoveredTests = ignoreUncompiledTypeScript ? this.filterUncompiledTypeScript(testCases) : testCases; return this.filterTests(discoveredTests, options.tests, options.exclude, options.strict); } filterUncompiledTypeScript(testCases) { const jsTestCases = testCases.filter(t => t.fileName.endsWith('.js')); return testCases // Remove all TypeScript test cases (ending in .ts) // for which a compiled version is present (same name, ending in .js) .filter((tsCandidate) => { if (!tsCandidate.fileName.endsWith('.ts')) { return true; } return jsTestCases.findIndex(jsTest => jsTest.testName === tsCandidate.testName) === -1; }); } async readTree() { const ret = new Array(); async function recurse(dir) { const files = await fs.readdir(dir); for (const file of files) { const fullPath = path.join(dir, file); const statf = await fs.stat(fullPath); if (statf.isFile()) { ret.push(fullPath); } if (statf.isDirectory()) { await recurse(fullPath); } } } await recurse(this.directory); return ret; } } exports.IntegrationTests = IntegrationTests; //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW50ZWdyYXRpb24tdGVzdHMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJpbnRlZ3JhdGlvbi10ZXN0cy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFBQSw2QkFBNkI7QUFDN0IsK0JBQStCO0FBRS9CLE1BQU0saUJBQWlCLEdBQUcsZUFBZSxDQUFDO0FBdUMxQzs7R0FFRztBQUNILE1BQWEsU0FBUztJQTJEcEIsWUFBNEIsSUFBbUI7UUFBbkIsU0FBSSxHQUFKLElBQUksQ0FBZTtRQUM3QyxJQUFJLENBQUMsVUFBVSxHQUFHLElBQUksQ0FBQyxVQUFVLElBQUksaUJBQWlCLENBQUM7UUFFdkQsbUVBQW1FO1FBQ25FLGlGQUFpRjtRQUNqRixxREFBcUQ7UUFDckQsSUFBSSxDQUFDLFNBQVMsR0FBRyxPQUFPLENBQUMsR0FBRyxFQUFFLENBQUM7UUFDL0IsSUFBSSxDQUFDLGdCQUFnQixHQUFHLElBQUksQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBQyxDQUFDO1FBQ3BELElBQUksQ0FBQyxRQUFRLEdBQUcsSUFBSSxDQUFDLFFBQVEsQ0FBQyxJQUFJLENBQUMsU0FBUyxFQUFFLElBQUksQ0FBQyxRQUFRLENBQUMsQ0FBQztRQUM3RCxJQUFJLENBQUMseUJBQXlCLEdBQUcsSUFBSSxDQUFDLFFBQVEsQ0FBQyxJQUFJLENBQUMsYUFBYSxFQUFFLElBQUksQ0FBQyxRQUFRLENBQUMsQ0FBQztRQUVsRiw0REFBNEQ7UUFDNUQsa0VBQWtFO1FBQ2xFLE1BQU0sTUFBTSxHQUFHLElBQUksQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBQyxDQUFDO1FBQ3pDLElBQUksQ0FBQyxRQUFRLEdBQUcsSUFBSSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsYUFBYSxFQUFFLE1BQU0sQ0FBQyxHQUFHLENBQUMsRUFBRSxNQUFNLENBQUMsSUFBSSxDQUFDLENBQUM7UUFDM0Ys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WUFDSCxDQUFDO1FBQ0gsQ0FBQztRQUVELE1BQU0sT0FBTyxDQUFDLElBQUksQ0FBQyxTQUFTLENBQUMsQ0FBQztRQUM5QixPQUFPLEdBQUcsQ0FBQztJQUNiLENBQUM7Q0FDRjtBQXhMRCw0Q0F3TEMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgKiBhcyBwYXRoIGZyb20gJ3BhdGgnO1xuaW1wb3J0ICogYXMgZnMgZnJvbSAnZnMtZXh0cmEnO1xuXG5jb25zdCBDREtfT1VURElSX1BSRUZJWCA9ICdjZGstaW50ZWcub3V0JztcblxuLyoqXG4gKiBSZXByZXNlbnRzIGEgc2luZ2xlIGludGVncmF0aW9uIHRlc3RcbiAqXG4gKiBUaGlzIHR5cGUgaXMgYSBkYXRhLW9ubHkgc3RydWN0dXJlLCBzbyBpdCBjYW4gdHJpdmlhbGx5IGJlIHBhc3NlZCB0byB3b3JrZXJzLlxuICogRGVyaXZlZCBhdHRyaWJ1dGVzIGFyZSBjYWxjdWxhdGVkIHVzaW5nIHRoZSBgSW50ZWdUZXN0YCBjbGFzcy5cbiAqL1xuZXhwb3J0IGludGVyZmFjZSBJbnRlZ1Rlc3RJbmZvIHtcbiAgLyoqXG4gICAqIFBhdGggdG8gdGhlIGZpbGUgdG8gcnVuXG4gICAqXG4gICAqIFBhdGggaXMgcmVsYXRpdmUgdG8gdGhlIGN1cnJlbnQgd29ya2luZyBkaXJlY3RvcnkuXG4gICAqL1xuICByZWFkb25seSBmaWxlTmFtZTogc3RyaW5nO1xuXG4gIC8qKlxuICAgKiBUaGUgcm9vdCBkaXJlY3Rvcnkgd2UgZGlzY292ZXJlZCB0aGlzIHRlc3QgZnJvbVxuICAgKlxuICAgKiBQYXRoIGlzIHJlbGF0aXZlIHRvIHRoZSBjdXJyZW50IHdvcmtpbmcgZGlyZWN0b3J5LlxuICAgKi9cbiAgcmVhZG9ubHkgZGlzY292ZXJ5Um9vdDogc3RyaW5nO1xuXG4gIC8qKlxuICAgKiBUaGUgQ0xJIGNvbW1hbmQgdXNlZCB0byBydW4gdGhpcyB0ZXN0LlxuICAgKiBJZiBpdCBjb250YWlucyB7ZmlsZVBhdGh9LCB0aGUgdGVzdCBmaWxlIG5hbWVzIHdpbGwgYmUgc3Vic3RpdHV0ZWQgYXQgdGhhdCBwbGFjZSBpbiB0aGUgY29tbWFuZCBmb3IgZWFjaCBydW4uXG4gICAqXG4gICAqIEBkZWZhdWx0IC0gdGVzdCBydW4gY29tbWFuZCB3aWxsIGJlIGBub2RlIHtmaWxlUGF0aH1gXG4gICAqL1xuICByZWFkb25seSBhcHBDb21tYW5kPzogc3RyaW5nO1xuXG4gIC8qKlxuICAgKiB0cnVlIGlmIHRoaXMgdGVzdCBpcyBydW5uaW5nIGluIHdhdGNoIG1vZGVcbiAgICpcbiAgICogQGRlZmF1bHQgZmFsc2VcbiAgICovXG4gIHJlYWRvbmx5IHdhdGNoPzogYm9vbGVhbjtcbn1cblxuLyoqXG4gKiBEZXJpdmVkIGluZm9ybWF0aW9uIGZvciBJbnRlZ1Rlc3RzXG4gKi9cbmV4cG9ydCBjbGFzcyBJbnRlZ1Rlc3Qge1xuICAvKipcbiAgICogVGhlIG5hbWUgb2YgdGhlIGZpbGUgdG8gcnVuXG4gICAqXG4gICAqIFBhdGggaXMgcmVsYXRpdmUgdG8gdGhlIGN1cnJlbnQgd29ya2luZyBkaXJlY3RvcnkuXG4gICAqL1xuICBwdWJsaWMgcmVhZG9ubHkgZmlsZU5hbWU6IHN0cmluZztcblxuICAvKipcbiAgICogUmVsYXRpdmUgcGF0aCB0byB0aGUgZmlsZSB0byBydW5cbiAgICpcbiAgICogUmVsYXRpdmUgZnJvbSB0aGUgXCJkaXNjb3Zlcnkgcm9vdFwiLlxuICAgKi9cbiAgcHVibGljIHJlYWRvbmx5IGRpc2NvdmVyeVJlbGF0aXZlRmlsZU5hbWU6IHN0cmluZztcblxuICAvKipcbiAgICogVGhlIGFic29sdXRlIHBhdGggdG8gdGhlIGZpbGVcbiAgICovXG4gIHB1YmxpYyByZWFkb25seSBhYnNvbHV0ZUZpbGVOYW1lOiBzdHJpbmc7XG5cbiAgLyoqXG4gICAqIFRoZSBub3JtYWxpemVkIG5hbWUgb2YgdGhlIHRlc3QuIFRoaXMgbmFtZVxuICAgKiB3aWxsIGJlIHRoZSBzYW1lIHJlZ2FyZGxlc3Mgb2Ygd2hhdCBkaXJlY3RvcnkgdGhlIHRvb2xcbiAgICogaXMgcnVuIGZyb20uXG4gICAqL1xuICBwdWJsaWMgcmVhZG9ubHkgbm9ybWFsaXplZFRlc3ROYW1lOiBzdHJpbmc7XG5cbiAgLyoqXG4gICAqIERpcmVjdG9yeSB0aGUgdGVzdCBpcyBpblxuICAgKi9cbiAgcHVibGljIHJlYWRvbmx5IGRpcmVjdG9yeTogc3RyaW5nO1xuXG4gIC8qKlxuICAgKiBEaXNwbGF5IG5hbWUgZm9yIHRoZSB0ZXN0XG4gICAqXG4gICAqIERlcGVuZHMgb24gdGhlIGRpc2NvdmVyeSBkaXJlY3RvcnkuXG4gICAqXG4gICAqIExvb2tzIGxpa2UgYGludGVnLm15dGVzdGAgb3IgYHBhY2thZ2UvdGVzdC9pbnRlZy5teXRlc3RgLlxuICAgKi9cbiAgcHVibGljIHJlYWRvbmx5IHRlc3ROYW1lOiBzdHJpbmc7XG5cbiAgLyoqXG4gICAqIFBhdGggb2YgdGhlIHNuYXBzaG90IGRpcmVjdG9yeSBmb3IgdGhpcyB0ZXN0XG4gICAqL1xuICBwdWJsaWMgcmVhZG9ubHkgc25hcHNob3REaXI6IHN0cmluZztcblxuICAvKipcbiAgICogUGF0aCB0byB0aGUgdGVtcG9yYXJ5IG91dHB1dCBkaXJlY3RvcnkgZm9yIHRoaXMgdGVzdFxuICAgKi9cbiAgcHVibGljIHJlYWRvbmx5IHRlbXBvcmFyeU91dHB1dERpcjogc3RyaW5nO1xuXG4gIC8qKlxuICAgKiBUaGUgQ0xJIGNvbW1hbmQgdXNlZCB0byBydW4gdGhpcyB0ZXN0LlxuICAgKiBJZiBpdCBjb250YWlucyB7ZmlsZVBhdGh9LCB0aGUgdGVzdCBmaWxlIG5hbWVzIHdpbGwgYmUgc3Vic3RpdHV0ZWQgYXQgdGhhdCBwbGFjZSBpbiB0aGUgY29tbWFuZCBmb3IgZWFjaCBydW4uXG4gICAqXG4gICAqIEBkZWZhdWx0IC0gdGVzdCBydW4gY29tbWFuZCB3aWxsIGJlIGBub2RlIHtmaWxlUGF0aH1gXG4gICAqL1xuICByZWFkb25seSBhcHBDb21tYW5kOiBzdHJpbmc7XG5cbiAgY29uc3RydWN0b3IocHVibGljIHJlYWRvbmx5IGluZm86IEludGVnVGVzdEluZm8pIHtcbiAgICB0aGlzLmFwcENvbW1hbmQgPSBpbmZvLmFwcENvbW1hbmQgPz8gJ25vZGUge2ZpbGVQYXRofSc7XG5cbiAgICAvLyBmb3IgY29uc2lzdGVuY3ksIGFsd2F5cyBydW4gdGhlIENESyBhcHBzIHVuZGVyIHRlc3QgZnJvbSB0aGUgQ1dEXG4gICAgLy8gdGhpcyBpcyBlc3BlY2lhbGx5IGltcG9ydGFudCBmb3IgbGFuZ3VhZ2VzIHRoYXQgdXNlIHRoZSBDV0QgdG8gZGlzY292ZXIgYXNzZXRzXG4gICAgLy8gQHNlZSBodHRwczovL2dpdGh1Yi5jb20vYXdzL2F3cy1jZGstY2xpL2lzc3Vlcy82MzhcbiAgICB0aGlzLmRpcmVjdG9yeSA9IHByb2Nlc3MuY3dkKCk7XG4gICAgdGhpcy5hYnNvbHV0ZUZpbGVOYW1lID0gcGF0aC5yZXNvbHZlKGluZm8uZmlsZU5hbWUpO1xuICAgIHRoaXMuZmlsZU5hbWUgPSBwYXRoLnJlbGF0aXZlKHRoaXMuZGlyZWN0b3J5LCBpbmZvLmZpbGVOYW1lKTtcbiAgICB0aGlzLmRpc2NvdmVyeVJlbGF0aXZlRmlsZU5hbWUgPSBwYXRoLnJlbGF0aXZlKGluZm8uZGlzY292ZXJ5Um9vdCwgaW5mby5maWxlTmFtZSk7XG5cbiAgICAvLyBXZSB0cmVhdCB0aGUgZGlzY292ZXJ5IHJvb3QgYXMgdGhlIGJhc2UgZm9yIGRpc3BsYXkgbmFtZXNcbiAgICAvLyBMb29rcyBlaXRoZXIgbGlrZSBgaW50ZWcubXl0ZXN0YCBvciBgcGFja2FnZS90ZXN0L2ludGVnLm15dGVzdGBcbiAgICBjb25zdCBwYXJzZWQgPSBwYXRoLnBhcnNlKHRoaXMuZmlsZU5hbWUpO1xuICAgIHRoaXMudGVzdE5hbWUgPSBwYXRoLmpvaW4ocGF0aC5yZWxhdGl2ZSh0aGlzLmluZm8uZGlzY292ZXJ5Um9vdCwgcGFyc2VkLmRpciksIHBhcnNlZC5uYW1lKTtcbiAgICB0aGlzLm5vcm1hbGl6ZWRUZXN0TmFtZSA9IHBhcnNlZC5uYW1lO1xuICAgIHRoaXMuc25hcHNob3REaXIgPSBwYXRoLmpvaW4ocGFyc2VkLmRpciwgYCR7cGFyc2VkLmJhc2V9LnNuYXBzaG90YCk7XG4gICAgdGhpcy50ZW1wb3JhcnlPdXRwdXREaXIgPSBwYXRoLmpvaW4ocGFyc2VkLmRpciwgYCR7Q0RLX09VVERJUl9QUkVGSVh9LiR7cGFyc2VkLmJhc2V9LnNuYXBzaG90YCk7XG4gIH1cblxuICAvKipcbiAgICogV2hldGhlciB0aGlzIHRlc3QgbWF0Y2hlcyB0aGUgdXNlci1naXZlbiBuYW1lXG4gICAqXG4gICAqIFdlIGFyZSB2ZXJ5IGxlbmllbnQgaGVyZS4gQSBuYW1lIG1hdGNoZXMgaWYgaXQgbWF0Y2hlczpcbiAgICpcbiAgICogLSBUaGUgQ1dELXJlbGF0aXZlIGZpbGVuYW1lXG4gICAqIC0gVGhlIGRpc2NvdmVyeSByb290LXJlbGF0aXZlIGZpbGVuYW1lXG4gICAqIC0gVGhlIHN1aXRlIG5hbWVcbiAgICogLSBUaGUgYWJzb2x1dGUgZmlsZW5hbWVcbiAgICovXG4gIHB1YmxpYyBtYXRjaGVzKG5hbWU6IHN0cmluZykge1xuICAgIHJldHVybiBbXG4gICAgICB0aGlzLmZpbGVOYW1lLFxuICAgICAgdGhpcy5kaXNjb3ZlcnlSZWxhdGl2ZUZpbGVOYW1lLFxuICAgICAgdGhpcy50ZXN0TmFtZSxcbiAgICAgIHRoaXMuYWJzb2x1dGVGaWxlTmFtZSxcbiAgICBdLmluY2x1ZGVzKG5hbWUpO1xuICB9XG59XG5cbi8qKlxuICogQ29uZmlndXJhdGlvbiBvcHRpb25zIGhvdyBpbnRlZ3JhdGlvbiB0ZXN0IGZpbGVzIGFyZSBkaXNjb3ZlcmVkXG4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgSW50ZWdyYXRpb25UZXN0c0Rpc2NvdmVyeU9wdGlvbnMge1xuICAvKipcbiAgICogSWYgdGhpcyBpcyBzZXQgdG8gdHJ1ZSB0aGVuIHRoZSBsaXN0IG9mIHRlc3RzXG4gICAqIHByb3ZpZGVkIHdpbGwgYmUgZXhjbHVkZWRcbiAgICpcbiAgICogQGRlZmF1bHQgZmFsc2VcbiAgICovXG4gIHJlYWRvbmx5IGV4Y2x1ZGU/OiBib29sZWFuO1xuXG4gIC8qKlxuICAgKiBJZiB0aGlzIGlzIHNldCB0byB0cnVlLCB0aHJvdyBhbiBlcnJvciBpZiBhbnkgc3BlY2lmaWVkIHRlc3RzIGFyZSBub3QgZm91bmRcbiAgICpcbiAgICogQGRlZmF1bHQgZmFsc2VcbiAgICovXG4gIHJlYWRvbmx5IHN0cmljdD86IGJvb2xlYW47XG5cbiAgLyoqXG4gICAqIExpc3Qgb2YgdGVzdHMgdG8gaW5jbHVkZSAob3IgZXhjbHVkZSBpZiBgZXhjbHVkZT10cnVlYClcbiAgICpcbiAgICogQGRlZmF1bHQgLSBhbGwgbWF0Y2hlZCBmaWxlc1xuICAgKi9cbiAgcmVhZG9ubHkgdGVzdHM/OiBzdHJpbmdbXTtcblxuICAvKipcbiAgICogQSBtYXAgb2Ygb2YgdGhlIGFwcCBjb21tYW5kcyB0byBydW4gaW50ZWdyYXRpb24gdGVzdHMgd2l0aCxcbiAgICogYW5kIHRoZSByZWdleCBwYXR0ZXJucyBtYXRjaGluZyB0aGUgaW50ZWdyYXRpb24gdGVzdCBmaWxlcyBlYWNoIGFwcCBjb21tYW5kLlxuICAgKlxuICAgKiBJZiB0aGUgYXBwIGNvbW1hbmQgY29udGFpbnMge2ZpbGVQYXRofSwgdGhlIHRlc3QgZmlsZSBuYW1lcyB3aWxsIGJlIHN1YnN0aXR1dGVkIGF0IHRoYXQgcGxhY2UgaW4gdGhlIGNvbW1hbmQgZm9yIGVhY2ggcnVuLlxuICAgKi9cbiAgcmVhZG9ubHkgdGVzdENhc2VzOiB7XG4gICAgW2FwcDogc3RyaW5nXTogc3RyaW5nW107XG4gIH07XG59XG5cbi8qKlxuICogUmV0dXJucyB0aGUgbmFtZSBvZiB0aGUgUHl0aG9uIGV4ZWN1dGFibGUgZm9yIHRoZSBjdXJyZW50IE9TXG4gKi9cbmZ1bmN0aW9uIHB5dGhvbkV4ZWN1dGFibGUoKSB7XG4gIGxldCBweXRob24gPSAncHl0aG9uMyc7XG4gIGlmIChwcm9jZXNzLnBsYXRmb3JtID09PSAnd2luMzInKSB7XG4gICAgcHl0aG9uID0gJ3B5dGhvbic7XG4gIH1cbiAgcmV0dXJuIHB5dGhvbjtcbn1cblxuLyoqXG4gKiBEaXNjb3ZlciBpbnRlZ3JhdGlvbiB0ZXN0c1xuICovXG5leHBvcnQgY2xhc3MgSW50ZWdyYXRpb25UZXN0cyB7XG4gIGNvbnN0cnVjdG9yKHByaXZhdGUgcmVhZG9ubHkgZGlyZWN0b3J5OiBzdHJpbmcpIHtcbiAgfVxuXG4gIC8qKlxuICAgKiBHZXQgaW50ZWdyYXRpb24gdGVzdHMgZGlzY292ZXJ5IG9wdGlvbnMgZnJvbSBDTEkgb3B0aW9uc1xuICAgKi9cbiAgcHVibGljIGFzeW5jIGZyb21DbGlPcHRpb25zKG9wdGlvbnM6IHtcbiAgICBhcHA/OiBzdHJpbmc7XG4gICAgZXhjbHVkZT86IGJvb2xlYW47XG4gICAgbGFuZ3VhZ2U/OiBzdHJpbmdbXTtcbiAgICB0ZXN0UmVnZXg/OiBzdHJpbmdbXTtcbiAgICB0ZXN0cz86IHN0cmluZ1tdO1xuICAgIHN0cmljdD86IGJvb2xlYW47XG4gIH0pOiBQcm9taXNlPEludGVnVGVzdFtdPiB7XG4gICAgY29uc3QgYmFzZU9wdGlvbnMgPSB7XG4gICAgICB0ZXN0czogb3B0aW9ucy50ZXN0cyxcbiAgICAgIGV4Y2x1ZGU6IG9wdGlvbnMuZXhjbHVkZSxcbiAgICAgIHN0cmljdDogb3B0aW9ucy5zdHJpY3QsXG4gICAgfTtcblxuICAgIC8vIEV4cGxpY2l0bHkgc2V0IGJvdGgsIGFwcCBhbmQgdGVzdC1yZWdleFxuICAgIGlmIChvcHRpb25zLmFwcCAmJiBvcHRpb25zLnRlc3RSZWdleCkge1xuICAgICAgcmV0dXJuIHRoaXMuZGlzY292ZXIoe1xuICAgICAgICB0ZXN0Q2FzZXM6IHtcbiAgICAgICAgICBbb3B0aW9ucy5hcHBdOiBvcHRpb25zLnRlc3RSZWdleCxcbiAgICAgICAgfSxcbiAgICAgICAgLi4uYmFzZU9wdGlvbnMsXG4gICAgICB9KTtcbiAgICB9XG5cbiAgICAvLyBVc2UgdGhlIHNlbGVjdGVkIHByZXNldHNcbiAgICBpZiAoIW9wdGlvbnMuYXBwICYmICFvcHRpb25zLnRlc3RSZWdleCkge1xuICAgICAgLy8gT25seSBjYXNlIHdpdGggbXVsdGlwbGUgbGFuZ3VhZ2VzLCBpLmUuIHRoZSBvbmx5IHRpbWUgd2UgbmVlZCB0byBjaGVjayB0aGUgc3BlY2lhbCBjYXNlXG4gICAgICBjb25zdCBpZ25vcmVVbmNvbXBpbGVkVHlwZVNjcmlwdCA9IG9wdGlvbnMubGFuZ3VhZ2U/LmluY2x1ZGVzKCdqYXZhc2NyaXB0JykgJiYgb3B0aW9ucy5sYW5ndWFnZT8uaW5jbHVkZXMoJ3R5cGVzY3JpcHQnKTtcblxuICAgICAgcmV0dXJuIHRoaXMuZGlzY292ZXIoe1xuICAgICAgICB0ZXN0Q2FzZXM6IHRoaXMuZ2V0TGFuZ3VhZ2VQcmVzZXRzKG9wdGlvbnMubGFuZ3VhZ2UpLFxuICAgICAgICAuLi5iYXNlT3B0aW9ucyxcbiAgICAgIH0sIGlnbm9yZVVuY29tcGlsZWRUeXBlU2NyaXB0KTtcbiAgICB9XG5cbiAgICAvLyBPbmx5IG9uZSBvZiBhcHAgb3IgdGVzdC1yZWdleCBpcyBzZXQsIHdpdGggYSBzaW5nbGUgcHJlc2V0IHNlbGVjdGVkXG4gICAgLy8gPT4gb3ZlcnJpZGUgZWl0aGVyIGFwcCBvciB0ZXN0LXJlZ2V4XG4gICAgaWYgKG9wdGlvbnMubGFuZ3VhZ2U/Lmxlbmd0aCA9PT0gMSkge1xuICAgICAgY29uc3QgW3ByZXNldEFwcCwgcHJlc2V0VGVzdFJlZ2V4XSA9IHRoaXMuZ2V0TGFuZ3VhZ2VQcmVzZXQob3B0aW9ucy5sYW5ndWFnZVswXSk7XG4gICAgICByZXR1cm4gdGhpcy5kaXNjb3Zlcih7XG4gICAgICAgIHRlc3RDYXNlczoge1xuICAgICAgICAgIFtvcHRpb25zLmFwcCA/PyBwcmVzZXRBcHBdOiBvcHRpb25zLnRlc3RSZWdleCA/PyBwcmVzZXRUZXN0UmVnZXgsXG4gICAgICAgIH0sXG4gICAgICAgIC4uLmJhc2VPcHRpb25zLFxuICAgICAgfSk7XG4gICAgfVxuXG4gICAgLy8gT25seSBvbmUgb2YgYXBwIG9yIHRlc3QtcmVnZXggaXMgc2V0LCB3aXRoIG11bHRpcGxlIHByZXNldHNcbiAgICAvLyA9PiBpbXBvc3NpYmxlIHRvIHJlc29sdmVcbiAgICBjb25zdCBvcHRpb24gPSBvcHRpb25zLmFwcCA/ICctLWFwcCcgOiAnLS10ZXN0LXJlZ2V4JztcbiAgICB0aHJvdyBuZXcgRXJyb3IoYE9ubHkgYSBzaW5nbGUgXCItLWxhbmd1YWdlXCIgY2FuIGJlIHVzZWQgd2l0aCBcIiR7b3B0aW9ufVwiLiBBbHRlcm5hdGl2ZWx5IHByb3ZpZGUgYm90aCBcIi0tYXBwXCIgYW5kIFwiLS10ZXN0LXJlZ2V4XCIgdG8gZnVsbHkgY3VzdG9taXplIHRoZSBjb25maWd1cmF0aW9uLmApO1xuICB9XG5cbiAgLyoqXG4gICAqIEdldCB0aGUgZGVmYXVsdCBjb25maWd1cmF0aW9uIGZvciBhIGxhbmd1YWdlXG4gICAqL1xuICBwcml2YXRlIGdldExhbmd1YWdlUHJlc2V0KGxhbmd1YWdlOiBzdHJpbmcpIHtcbiAgICBjb25zdCBsYW5ndWFnZVByZXNldHM6IHtcbiAgICAgIFtsYW5ndWFnZTogc3RyaW5nXTogW3N0cmluZywgc3RyaW5nW11dO1xuICAgIH0gPSB7XG4gICAgICBqYXZhc2NyaXB0OiBbJ25vZGUge2ZpbGVQYXRofScsIFsnXmludGVnXFxcXC4uKlxcXFwuanMkJ11dLFxuICAgICAgdHlwZXNjcmlwdDogWydub2RlIC1yIHRzLW5vZGUvcmVnaXN0ZXIge2ZpbGVQYXRofScsIFsnXmludGVnXFxcXC4oPyEuKlxcXFwuZFxcXFwudHMkKS4qXFxcXC50cyQnXV0sXG4gICAgICBweXRob246IFtgJHtweXRob25FeGVjdXRhYmxlKCl9IHtmaWxlUGF0aH1gLCBbJ15pbnRlZ18uKlxcXFwucHkkJ11dLFxuICAgICAgZ286IFsnZ28gcnVuIHtmaWxlUGF0aH0nLCBbJ15pbnRlZ18uKlxcXFwuZ28kJ11dLFxuICAgIH07XG5cbiAgICByZXR1cm4gbGFuZ3VhZ2VQcmVzZXRzW2xhbmd1YWdlXTtcbiAgfVxuXG4gIC8qKlxuICAgKiBHZXQgdGhlIGNvbmZpZyBmb3IgYWxsIHNlbGVjdGVkIGxhbmd1YWdlc1xuICAgKi9cbiAgcHJpdmF0ZSBnZXRMYW5ndWFnZVByZXNldHMobGFuZ3VhZ2VzOiBzdHJpbmdbXSA9IFtdKSB7XG4gICAgcmV0dXJuIE9iamVjdC5mcm9tRW50cmllcyhcbiAgICAgIGxhbmd1YWdlc1xuICAgICAgICAubWFwKGxhbmd1YWdlID0+IHRoaXMuZ2V0TGFuZ3VhZ2VQcmVzZXQobGFuZ3VhZ2UpKVxuICAgICAgICAuZmlsdGVyKEJvb2xlYW4pLFxuICAgICk7XG4gIH1cblxuICAvKipcbiAgICogSWYgdGhlIHVzZXIgcHJvdmlkZXMgYSBsaXN0IG9mIHRlc3RzLCB0aGVzZSBjYW4gZWl0aGVyIGJlIGEgbGlzdCBvZiB0ZXN0cyB0byBpbmNsdWRlIG9yIGEgbGlzdCBvZiB0ZXN0cyB0byBleGNsdWRlLlxuICAgKlxuICAgKiAtIElmIGl0IGlzIGEgbGlzdCBvZiB0ZXN0cyB0byBpbmNsdWRlIHRoZW4gd2UgZGlzY292ZXIgYWxsIGF2YWlsYWJsZSB0ZXN0cyBhbmQgY2hlY2sgd2hldGhlciB0aGV5IGhhdmUgcHJvdmlkZWQgdmFsaWQgdGVzdHMuXG4gICAqICAgSWYgdGhleSBoYXZlIHByb3ZpZGVkIGEgdGVzdCBuYW1lIHRoYXQgd2UgZG9uJ3QgZmluZCwgdGhlbiB3ZSB3cml0ZSBvdXQgdGhhdCBlcnJvciBtZXNzYWdlLlxuICAgKiAtIElmIGl0IGlzIGEgbGlzdCBvZiB0ZXN0cyB0byBleGNsdWRlLCB0aGVuIHdlIGRpc2NvdmVyIGFsbCBhdmFpbGFibGUgdGVzdHMgYW5kIGZpbHRlciBvdXQgdGhlIHRlc3RzIHRoYXQgd2VyZSBwcm92aWRlZCBieSB0aGUgdXNlci5cbiAgICovXG4gIHByaXZhdGUgZmlsdGVyVGVzdHMoZGlzY292ZXJlZFRlc3RzOiBJbnRlZ1Rlc3RbXSwgcmVxdWVzdGVkVGVzdHM/OiBzdHJpbmdbXSwgZXhjbHVkZT86IGJvb2xlYW4sIHN0cmljdD86IGJvb2xlYW4pOiBJbnRlZ1Rlc3RbXSB7XG4gICAgaWYgKCFyZXF1ZXN0ZWRUZXN0cykge1xuICAgICAgcmV0dXJuIGRpc2NvdmVyZWRUZXN0cztcbiAgICB9XG5cbiAgICBjb25zdCBhbGxUZXN0cyA9IGRpc2NvdmVyZWRUZXN0cy5maWx0ZXIodCA9PiB7XG4gICAgICBjb25zdCBtYXRjaGVzID0gcmVxdWVzdGVkVGVzdHMuc29tZShwYXR0ZXJuID0+IHQubWF0Y2hlcyhwYXR0ZXJuKSk7XG4gICAgICByZXR1cm4gbWF0Y2hlcyAhPT0gISFleGNsdWRlOyAvLyBMb29rcyB3ZWlyZCBidXQgaXMgZXF1YWwgdG8gKG1hdGNoZXMgJiYgIWV4Y2x1ZGUpIHx8ICghbWF0Y2hlcyAmJiBleGNsdWRlKVxuICAgIH0pO1xuXG4gICAgLy8gSWYgbm90IGV4Y2x1ZGluZywgYWxsIHBhdHRlcm5zIG11c3QgaGF2ZSBtYXRjaGVkIGF0IGxlYXN0IG9uZSB0ZXN0XG4gICAgaWYgKCFleGNsdWRlKSB7XG4gICAgICBjb25zdCB1bm1hdGNoZWRQYXR0ZXJucyA9IHJlcXVlc3RlZFRlc3RzLmZpbHRlcihwYXR0ZXJuID0+ICFkaXNjb3ZlcmVkVGVzdHMuc29tZSh0ID0+IHQubWF0Y2hlcyhwYXR0ZXJuKSkpO1xuICAgICAgZm9yIChjb25zdCB1bm1hdGNoZWQgb2YgdW5tYXRjaGVkUGF0dGVybnMpIHtcbiAgICAgICAgcHJvY2Vzcy5zdGRlcnIud3JpdGUoYE5vIHN1Y2ggaW50ZWcgdGVzdDogJHt1bm1hdGNoZWR9XFxuYCk7XG4gICAgICB9XG4gICAgICBpZiAodW5tYXRjaGVkUGF0dGVybnMubGVuZ3RoID4gMCkge1xuICAgICAgICBwcm9jZXNzLnN0ZGVyci53cml0ZShgQXZhaWxhYmxlIHRlc3RzOiAke2Rpc2NvdmVyZWRUZXN0cy5tYXAodCA9PiB0LmRpc2NvdmVyeVJlbGF0aXZlRmlsZU5hbWUpLmpvaW4oJyAnKX1cXG5gKTtcbiAgICAgICAgaWYgKHN0cmljdCkge1xuICAgICAgICAgIHRocm93IG5ldyBFcnJvcihgU3RyaWN0IG1vZGU6ICR7dW5tYXRjaGVkUGF0dGVybnMubGVuZ3RofSB0ZXN0KHMpIG5vdCBmb3VuZDogJHt1bm1hdGNoZWRQYXR0ZXJucy5qb2luKCcsICcpfWApO1xuICAgICAgICB9XG4gICAgICAgIHJldHVybiBbXTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICByZXR1cm4gYWxsVGVzdHM7XG4gIH1cblxuICAvKipcbiAgICogVGFrZXMgYW4gb3B0aW9uYWwgbGlzdCBvZiB0ZXN0cyB0byBsb29rIGZvciwgb3RoZXJ3aXNlXG4gICAqIGl0IHdpbGwgbG9vayBmb3IgYWxsIHRlc3RzIGZyb20gdGhlIGRpcmVjdG9yeVxuICAgKlxuICAgKiBAcGFyYW0gdGVzdHMgLSBUZXN0cyB0byBpbmNsdWRlIG9yIGV4Y2x1ZGUsIHVuZGVmaW5lZCBtZWFucyBpbmNsdWRlIGFsbCB0ZXN0cy5cbiAgICogQHBhcmFtIGV4Y2x1ZGUgLSBXaGV0aGVyIHRoZSAndGVzdHMnIGxpc3QgaXMgaW5jbHVzaXZlIG9yIGV4Y2x1c2l2ZSAoaW5jbHVzaXZlIGJ5IGRlZmF1bHQpLlxuICAgKi9cbiAgcHJpdmF0ZSBhc3luYyBkaXNjb3ZlcihvcHRpb25zOiBJbnRlZ3JhdGlvblRlc3RzRGlzY292ZXJ5T3B0aW9ucywgaWdub3JlVW5jb21waWxlZFR5cGVTY3JpcHQ6IGJvb2xlYW4gPSBmYWxzZSk6IFByb21pc2U8SW50ZWdUZXN0W10+IHtcbiAgICBjb25zdCBmaWxlcyA9IGF3YWl0IHRoaXMucmVhZFRyZWUoKTtcblxuICAgIGNvbnN0IHRlc3RDYXNlcyA9IE9iamVjdC5lbnRyaWVzKG9wdGlvbnMudGVzdENhc2VzKVxuICAgICAgLmZsYXRNYXAoKFthcHBDb21tYW5kLCBwYXR0ZXJuc10pID0+IGZpbGVzXG4gICAgICAgIC5maWx0ZXIoZmlsZU5hbWUgPT4gcGF0dGVybnMuc29tZSgocGF0dGVybikgPT4ge1xuICAgICAgICAgIGNvbnN0IHJlZ2V4ID0gbmV3IFJlZ0V4cChwYXR0ZXJuKTtcbiAgICAgICAgICByZXR1cm4gcmVnZXgudGVzdChmaWxlTmFtZSkgfHwgcmVnZXgudGVzdChwYXRoLmJhc2VuYW1lKGZpbGVOYW1lKSk7XG4gICAgICAgIH0pKVxuICAgICAgICAubWFwKGZpbGVOYW1lID0+IG5ldyBJbnRlZ1Rlc3Qoe1xuICAgICAgICAgIGRpc2NvdmVyeVJvb3Q6IHRoaXMuZGlyZWN0b3J5LFxuICAgICAgICAgIGZpbGVOYW1lLFxuICAgICAgICAgIGFwcENvbW1hbmQsXG4gICAgICAgIH0pKSxcbiAgICAgICk7XG5cbiAgICBjb25zdCBkaXNjb3ZlcmVkVGVzdHMgPSBpZ25vcmVVbmNvbXBpbGVkVHlwZVNjcmlwdCA/IHRoaXMuZmlsdGVyVW5jb21waWxlZFR5cGVTY3JpcHQodGVzdENhc2VzKSA6IHRlc3RDYXNlcztcblxuICAgIHJldHVybiB0aGlzLmZpbHRlclRlc3RzKGRpc2NvdmVyZWRUZXN0cywgb3B0aW9ucy50ZXN0cywgb3B0aW9ucy5leGNsdWRlLCBvcHRpb25zLnN0cmljdCk7XG4gIH1cblxuICBwcml2YXRlIGZpbHRlclVuY29tcGlsZWRUeXBlU2NyaXB0KHRlc3RDYXNlczogSW50ZWdUZXN0W10pOiBJbnRlZ1Rlc3RbXSB7XG4gICAgY29uc3QganNUZXN0Q2FzZXMgPSB0ZXN0Q2FzZXMuZmlsdGVyKHQgPT4gdC5maWxlTmFtZS5lbmRzV2l0aCgnLmpzJykpO1xuXG4gICAgcmV0dXJuIHRlc3RDYXNlc1xuICAgICAgLy8gUmVtb3ZlIGFsbCBUeXBlU2NyaXB0IHRlc3QgY2FzZXMgKGVuZGluZyBpbiAudHMpXG4gICAgICAvLyBmb3Igd2hpY2ggYSBjb21waWxlZCB2ZXJzaW9uIGlzIHByZXNlbnQgKHNhbWUgbmFtZSwgZW5kaW5nIGluIC5qcylcbiAgICAgIC5maWx0ZXIoKHRzQ2FuZGlkYXRlKSA9PiB7XG4gICAgICAgIGlmICghdHNDYW5kaWRhdGUuZmlsZU5hbWUuZW5kc1dpdGgoJy50cycpKSB7XG4gICAgICAgICAgcmV0dXJuIHRydWU7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIGpzVGVzdENhc2VzLmZpbmRJbmRleChqc1Rlc3QgPT4ganNUZXN0LnRlc3ROYW1lID09PSB0c0NhbmRpZGF0ZS50ZXN0TmFtZSkgPT09IC0xO1xuICAgICAgfSk7XG4gIH1cblxuICBwcml2YXRlIGFzeW5jIHJlYWRUcmVlKCk6IFByb21pc2U8c3RyaW5nW10+IHtcbiAgICBjb25zdCByZXQgPSBuZXcgQXJyYXk8c3RyaW5nPigpO1xuXG4gICAgYXN5bmMgZnVuY3Rpb24gcmVjdXJzZShkaXI6IHN0cmluZykge1xuICAgICAgY29uc3QgZmlsZXMgPSBhd2FpdCBmcy5yZWFkZGlyKGRpcik7XG4gICAgICBmb3IgKGNvbnN0IGZpbGUgb2YgZmlsZXMpIHtcbiAgICAgICAgY29uc3QgZnVsbFBhdGggPSBwYXRoLmpvaW4oZGlyLCBmaWxlKTtcbiAgICAgICAgY29uc3Qgc3RhdGYgPSBhd2FpdCBmcy5zdGF0KGZ1bGxQYXRoKTtcbiAgICAgICAgaWYgKHN0YXRmLmlzRmlsZSgpKSB7XG4gICAgICAgICAgcmV0LnB1c2goZnVsbFBhdGgpO1xuICAgICAgICB9XG4gICAgICAgIGlmIChzdGF0Zi5pc0RpcmVjdG9yeSgpKSB7XG4gICAgICAgICAgYXdhaXQgcmVjdXJzZShmdWxsUGF0aCk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG5cbiAgICBhd2FpdCByZWN1cnNlKHRoaXMuZGlyZWN0b3J5KTtcbiAgICByZXR1cm4gcmV0O1xuICB9XG59XG4iXX0=