diff --git a/packages/@aws-cdk/integ-runner/README.md b/packages/@aws-cdk/integ-runner/README.md index 308d6da190f6f..185611c779c20 100644 --- a/packages/@aws-cdk/integ-runner/README.md +++ b/packages/@aws-cdk/integ-runner/README.md @@ -53,7 +53,7 @@ to be a self contained CDK app. The runner will execute the following for each f - `--verbose` (default=`false`) verbose logging, including integration test metrics (specify multiple times to increase verbosity) -- `--parallel-regions` (default=`us-east-1`,`us-east-2`, `us-west-2`) +- `--parallel-regions` (default=`us-east-1`,`us-east-2`,`us-west-2`) List of regions to run tests in. If this is provided then all tests will be run in parallel across these regions - `--directory` (default=`test`) @@ -68,6 +68,8 @@ to be a self contained CDK app. The runner will execute the following for each f Read the list of tests from this file - `--disable-update-workflow` (default=`false`) If this is set to `true` then the [update workflow](#update-workflow) will be disabled +- `--language [javascript|typescript|python|java|csharp|fsharp|go]` (default=`javascript`) + Use a preset to run integration tests for the selected language - `--app` The custom CLI command that will be used to run the test files. You can include {filePath} to specify where in the command the test file path should be inserted. Example: --app="python3.8 {filePath}". - `--test-regex` diff --git a/packages/@aws-cdk/integ-runner/lib/cli.ts b/packages/@aws-cdk/integ-runner/lib/cli.ts index c3a2256c0813b..b4c7b73de47ae 100644 --- a/packages/@aws-cdk/integ-runner/lib/cli.ts +++ b/packages/@aws-cdk/integ-runner/lib/cli.ts @@ -4,7 +4,7 @@ import * as path from 'path'; import * as chalk from 'chalk'; import * as workerpool from 'workerpool'; import * as logger from './logger'; -import { IntegrationTests, IntegTestInfo, IntegTest } from './runner/integration-tests'; +import { IntegrationTests, IntegTestInfo, IntegTest, IntegrationTestsDiscoveryOptions, IntegrationTestsDiscovery } from './runner/integration-tests'; import { runSnapshotTests, runIntegrationTests, IntegRunnerMetrics, IntegTestWorkerConfig, DestructiveChange } from './workers'; // https://github.com/yargs/yargs/issues/1929 @@ -30,6 +30,13 @@ export async function main(args: string[]) { .options('from-file', { type: 'string', desc: 'Read TEST names from a file (one TEST per line)' }) .option('inspect-failures', { type: 'boolean', desc: 'Keep the integ test cloud assembly if a failure occurs for inspection', default: false }) .option('disable-update-workflow', { type: 'boolean', default: false, desc: 'If this is "true" then the stack update workflow will be disabled' }) + .option('language', { + alias: 'l', + default: 'javascript', + choices: ['javascript', 'typescript', 'python', 'java', 'csharp', 'fsharp', 'go'], + type: 'array', + desc: 'Use this preset to run integration tests for the selected language', + }) .option('app', { type: 'string', default: undefined, desc: 'The custom CLI command that will be used to run the test files. You can include {filePath} to specify where in the command the test file path should be inserted. Example: --app="python3.8 {filePath}".' }) .option('test-regex', { type: 'array', desc: 'Detect integration test files matching this JavaScript regex pattern. If used multiple times, all files matching any one of the patterns are detected.', default: [] }) .strict() @@ -51,6 +58,11 @@ export async function main(args: string[]) { const fromFile: string | undefined = argv['from-file']; const exclude: boolean = argv.exclude; const app: string | undefined = argv.app; + const discoveryOptions: IntegrationTestsDiscoveryOptions = { + ...IntegrationTestsDiscovery.fromLanguagePreset(argv.language), + app, + testRegex, + }; let failedSnapshots: IntegTestWorkerConfig[] = []; if (argv['max-workers'] < testRegions.length * (profiles ?? [1]).length) { @@ -60,7 +72,7 @@ export async function main(args: string[]) { let testsSucceeded = false; try { if (argv.list) { - const tests = await new IntegrationTests(argv.directory).fromCliArgs({ testRegex, app }); + const tests = await new IntegrationTests(argv.directory).fromCliArgs(discoveryOptions); process.stdout.write(tests.map(t => t.discoveryRelativeFileName).join('\n') + '\n'); return; } @@ -73,8 +85,7 @@ export async function main(args: string[]) { : (argv._.length > 0 ? argv._ : undefined); // 'undefined' means no request testsFromArgs.push(...(await new IntegrationTests(path.resolve(argv.directory)).fromCliArgs({ - app, - testRegex, + ...discoveryOptions, tests: requestedTests, exclude, }))); diff --git a/packages/@aws-cdk/integ-runner/lib/runner/integration-tests.ts b/packages/@aws-cdk/integ-runner/lib/runner/integration-tests.ts index d7559ea911c10..d07886648b5ce 100644 --- a/packages/@aws-cdk/integ-runner/lib/runner/integration-tests.ts +++ b/packages/@aws-cdk/integ-runner/lib/runner/integration-tests.ts @@ -139,6 +139,42 @@ export class IntegTest { } } +/** + * Returns the name of the Python executable for this OS + */ +function pythonExecutable() { + let python = 'python3'; + if (process.platform === 'win32') { + python = 'python'; + } + return python; +} + +class IntegrationTestPreset { + public constructor(readonly app: string, readonly testRegex: string[]) {} +} + +const PRESETS = { + javascript: new IntegrationTestPreset('node {filePath}', ['^integ\..*\.js$/']), + typescript: new IntegrationTestPreset('node -r ts-node/register {filePath}', ['^integ\.(?!.*\.d\.ts$).*\.ts$']), + python: new IntegrationTestPreset(`${pythonExecutable()} {filePath}`, ['^integ_.*\.py$']), + csharp: new IntegrationTestPreset('dotnet run --project {filePath}', ['^Integ.*\.csproj$']), + fsharp: new IntegrationTestPreset('dotnet run --project {filePath}', ['^Integ.*\.fsproj$']), + // these are still unconfirmed and need testing + go: new IntegrationTestPreset('go mod download && go run {filePath}', ['^integ_.*\.go$']), + java: new IntegrationTestPreset('mvn -e -q compile exec:java', ['^Integ.*\.java$']), +}; + +export enum Language { + JAVASCRIPT = 'javascript', + TYPESCRIPT = 'typescript', + PYTHON = 'python', + GO = 'go', + JAVA = 'java', + CSHARP = 'csharp', + FSHARP = 'fsharp' +} + /** * Configuration options how integration test files are discovered */ @@ -174,6 +210,20 @@ export interface IntegrationTestsDiscoveryOptions { readonly app?: string; } +export class IntegrationTestsDiscovery { + /** + * Get integration tests discovery options from a language preset. + */ + public static fromLanguagePreset(language?: Language): IntegrationTestsDiscoveryOptions { + if (language && PRESETS[language]) { + return PRESETS[language]; + } + return {}; + } + + private constructor() {} +} + /** * The list of tests to run can be provided in a file @@ -181,9 +231,9 @@ export interface IntegrationTestsDiscoveryOptions { */ export interface IntegrationTestFileConfig extends IntegrationTestsDiscoveryOptions { /** - * List of tests to include (or exclude if `exclude=true`) + * Language preset used for integration test */ - readonly tests: string[]; + readonly language?: Language; } /** @@ -200,7 +250,10 @@ export class IntegrationTests { public async fromFile(fileName: string): Promise { const file: IntegrationTestFileConfig = JSON.parse(fs.readFileSync(fileName, { encoding: 'utf-8' })); - return this.discover(file); + return this.discover({ + ...IntegrationTestsDiscovery.fromLanguagePreset(file.language), + ...file, + }); } /**