mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-05-15 06:12:50 -07:00
Add script to deflake integration tests (#10666)
Co-authored-by: gemini-cli-robot <gemini-cli-robot@google.com> Co-authored-by: Jacob Richman <jacob314@gmail.com>
This commit is contained in:
@@ -20,6 +20,9 @@
|
|||||||
"start": "cross-env NODE_ENV=development node scripts/start.js",
|
"start": "cross-env NODE_ENV=development node scripts/start.js",
|
||||||
"start:a2a-server": "CODER_AGENT_PORT=41242 npm run start --workspace @google/gemini-cli-a2a-server",
|
"start:a2a-server": "CODER_AGENT_PORT=41242 npm run start --workspace @google/gemini-cli-a2a-server",
|
||||||
"debug": "cross-env DEBUG=1 node --inspect-brk scripts/start.js",
|
"debug": "cross-env DEBUG=1 node --inspect-brk scripts/start.js",
|
||||||
|
"deflake": "node scripts/deflake.js",
|
||||||
|
"deflake:test:integration:sandbox:none": "npm run deflake -- --command='npm run test:integration:sandbox:none'",
|
||||||
|
"deflake:test:integration:sandbox:docker": "npm run deflake -- --command='npm run test:integration:sandbox:docker'",
|
||||||
"auth:npm": "npx google-artifactregistry-auth",
|
"auth:npm": "npx google-artifactregistry-auth",
|
||||||
"auth:docker": "gcloud auth configure-docker us-west1-docker.pkg.dev",
|
"auth:docker": "gcloud auth configure-docker us-west1-docker.pkg.dev",
|
||||||
"auth": "npm run auth:npm && npm run auth:docker",
|
"auth": "npm run auth:npm && npm run auth:docker",
|
||||||
|
|||||||
@@ -0,0 +1,98 @@
|
|||||||
|
/**
|
||||||
|
* @license
|
||||||
|
* Copyright 2025 Google LLC
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { spawn } from 'node:child_process';
|
||||||
|
import yargs from 'yargs';
|
||||||
|
import { hideBin } from 'yargs/helpers';
|
||||||
|
|
||||||
|
// Script to deflake tests
|
||||||
|
// Ex. npm run deflake -- --command="npm run test:e2e -- --test-name-pattern 'extension'" --runs=3
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runs a command and streams its output to the console.
|
||||||
|
* @param {string} command The command string to execute (e.g., 'npm run test:e2e -- --watch').
|
||||||
|
* @returns {Promise<number>} A Promise that resolves with the exit code of the process.
|
||||||
|
*/
|
||||||
|
function runCommand(command) {
|
||||||
|
// Split the command string into the command name and its arguments.
|
||||||
|
// This handles quotes and spaces more robustly than a simple split(' ').
|
||||||
|
// For 'npm run test:e2e -- --test-name-pattern "replace"', it results in:
|
||||||
|
// cmd: 'npm', args: ['run', 'test:e2e', '--', '--test-name-pattern', 'replace']
|
||||||
|
const parts = command.match(/(?:[^\s"]+|"[^"]*")+/g) || [];
|
||||||
|
const cmd = parts[0];
|
||||||
|
const args = parts.slice(1).map((arg) => arg.replace(/"/g, '')); // Remove surrounding quotes
|
||||||
|
|
||||||
|
if (!cmd) {
|
||||||
|
return Promise.resolve(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const child = spawn(cmd, args, {
|
||||||
|
shell: true,
|
||||||
|
stdio: 'inherit',
|
||||||
|
});
|
||||||
|
|
||||||
|
child.on('close', (code) => {
|
||||||
|
resolve(code ?? 1); // code can be null if the process was killed
|
||||||
|
});
|
||||||
|
|
||||||
|
child.on('error', (err) => {
|
||||||
|
// An error occurred in spawning the process (e.g., command not found).
|
||||||
|
console.error(`Failed to start command: ${err.message}`);
|
||||||
|
reject(err);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// -------------------------------------------------------------------
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
const argv = await yargs(hideBin(process.argv))
|
||||||
|
.option('command', {
|
||||||
|
type: 'string',
|
||||||
|
demandOption: true,
|
||||||
|
description: 'The command to run',
|
||||||
|
})
|
||||||
|
.option('runs', {
|
||||||
|
type: 'number',
|
||||||
|
default: 50,
|
||||||
|
description: 'The number of runs to perform',
|
||||||
|
}).argv;
|
||||||
|
|
||||||
|
const NUM_RUNS = argv.runs;
|
||||||
|
const COMMAND = argv.command;
|
||||||
|
let failures = 0;
|
||||||
|
|
||||||
|
console.log(`--- Starting Deflake Run (${NUM_RUNS} iterations) ---`);
|
||||||
|
for (let i = 1; i <= NUM_RUNS; i++) {
|
||||||
|
console.log(`\n[RUN ${i}/${NUM_RUNS}]`);
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 3. Await the asynchronous command run
|
||||||
|
const exitCode = await runCommand(COMMAND);
|
||||||
|
|
||||||
|
if (exitCode === 0) {
|
||||||
|
console.log('✅ Run PASS');
|
||||||
|
} else {
|
||||||
|
console.log(`❌ Run FAIL (Exit Code: ${exitCode})`);
|
||||||
|
failures++;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('❌ Run FAIL (Execution Error)', error);
|
||||||
|
failures++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('\n--- FINAL DEFLAKE SUMMARY ---');
|
||||||
|
console.log(`Total Runs: ${NUM_RUNS}`);
|
||||||
|
console.log(`Total Failures: ${failures}`);
|
||||||
|
|
||||||
|
process.exit(failures > 0 ? 1 : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
main().catch((error) => {
|
||||||
|
console.error('Error in deflake:', error);
|
||||||
|
process.exit(1);
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user