Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 2 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -517,12 +517,9 @@
"test": "npm run compile && node ./out/test/index.js",
"lint": "eslint -c .eslintrc.js --ext .ts ./src",
"build-plugin": "node scripts/buildJdtlsExt.js",
"vscode:prepublish": "webpack --mode production"
"vscode:prepublish": "webpack --mode production",
"extension": "vsce package"
},
"extensionDependencies": [
"redhat.java",
"vscjava.vscode-java-debug"
],
"devDependencies": {
"@types/fs-extra": "^9.0.13",
"@types/glob": "^7.2.0",
Expand Down
5 changes: 5 additions & 0 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { enableTests } from './commands/testDependenciesCommands';
import { testRunnerService } from './controller/testRunnerService';
import { TestRunner } from './java-test-runner.api';
import { parsePartsFromTestId, parseTestIdFromParts } from './utils/testItemUtils';
import { waitForExtensionDependencies } from './extensionDependencyValidator';

export let extensionContext: ExtensionContext;
let componentsRegistered: boolean = false;
Expand All @@ -32,6 +33,10 @@ export async function activate(context: ExtensionContext): Promise<any> {
replacementString: 'Path must include project and resource name: /<REDACT>',
}]});
await initExpService(context);
const extensionDependenciesResolved: boolean = await waitForExtensionDependencies();
if (!extensionDependenciesResolved) {
return;
}
await instrumentOperation('activation', doActivate)(context);
return {
registerTestProfile: (name: string, kind: TestRunProfileKind, runner: TestRunner) => {
Expand Down
87 changes: 87 additions & 0 deletions src/extensionDependencyValidator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
/**
* HackerRank-Specific Logic
* Dynamically waits for a list of extension dependencies to become available and active,
* replacing static extensionDependencies in package.json.
* - Waits up to 2 minutes total (not per extension),
* - Checks availability and attempts activation in parallel,
* - Fails only if any required extension fails to activate within the total timeout.
*/

import { extensions, Extension, OutputChannel, window } from 'vscode';

// Create an OutputChannel for logging
const logger: OutputChannel = window.createOutputChannel('Java Test Runner');

const WAIT_TIME_MS: number = 4000; // 3 seconds
const MAX_ATTEMPTS: number = 45; // 40 attempts * 3 seconds = 120 seconds (2 minutes)


/**
* Waits for a single extension to activate within a timeout window.
*/
async function waitForExtension(depId: string): Promise<boolean> {
for (let attempt: number = 0; attempt < MAX_ATTEMPTS; attempt++) {
const ext: Extension<any> | undefined = extensions.getExtension(depId);
const timeElapsedSec: number = ((attempt + 1) * WAIT_TIME_MS) / 1000;
if (ext) {
try {
if (!ext.isActive) {
logger.appendLine(
`[waitDeps] [${depId}][${timeElapsedSec}s] Found but not active. Activating...`
);
await ext.activate();
}

if (ext.isActive) {
logger.appendLine(`[waitDeps] [${depId}] Activated successfully.`);
return true;
}
} catch (e) {
logger.appendLine(
`[waitDeps] [${depId}][${timeElapsedSec}s] Activation failed: ${e}`
);
}
} else {
logger.appendLine(
`[waitDeps] [${depId}][${timeElapsedSec}s] Not found. Retrying...`
);
}
await new Promise<void>((resolve: (value: void) => void) =>
setTimeout(resolve, WAIT_TIME_MS)
);
}
logger.appendLine(`[waitDeps] [${depId}] Failed to activate within 2 minutes.`);
return false;
}

/**
* Waits for all required extensions in parallel.
* Returns true if all are activated, otherwise false.
*/
export async function waitForExtensionDependencies(): Promise<boolean> {
logger.appendLine('[waitDeps] Checking for required extension dependencies...');
const extensionDependencies: string[] = [
'redhat.java',
'vscjava.vscode-java-debug'
];
const results: Record<string, boolean> = {};
await Promise.all(
extensionDependencies.map(async (depId: string) => {
logger.appendLine(`[waitDeps] Waiting for ${depId}...`);
results[depId] = await waitForExtension(depId);
})
);
const failed: string[] = Object.entries(results)
.filter(([, success]: [string, boolean]) => !success)
.map(([depId]: [string, boolean]) => depId);
const failedDepsString: string = `[${failed.join(', ')}]`;
if (failed.length > 0) {
logger.appendLine(`[waitDeps] Failed dependencies: ${failedDepsString}`);
window.showErrorMessage(
`Activation failed: Java Test Runner requires the ${failedDepsString} extension(s), but they did not activate in time.`
);
return false;
}
logger.appendLine('[waitDeps] All dependencies activated successfully.');
return true;
}