refactor(ide): Improve IDE detection discovery (#6765)

This commit is contained in:
Shreya Keshive
2025-08-25 11:39:57 -07:00
committed by GitHub
parent 0641b1c095
commit 776627c855
13 changed files with 373 additions and 152 deletions

View File

@@ -0,0 +1,90 @@
/**
* @license
* Copyright 2025 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import {
describe,
it,
expect,
vi,
afterEach,
beforeEach,
type Mock,
} from 'vitest';
import { getIdeProcessInfo } from './process-utils.js';
import os from 'node:os';
const mockedExec = vi.hoisted(() => vi.fn());
vi.mock('node:util', () => ({
promisify: vi.fn().mockReturnValue(mockedExec),
}));
vi.mock('node:os', () => ({
default: {
platform: vi.fn(),
},
}));
describe('getIdeProcessInfo', () => {
beforeEach(() => {
Object.defineProperty(process, 'pid', { value: 1000, configurable: true });
mockedExec.mockReset();
});
afterEach(() => {
vi.restoreAllMocks();
});
describe('on Unix', () => {
it('should traverse up to find the shell and return grandparent process info', async () => {
(os.platform as Mock).mockReturnValue('linux');
// process (1000) -> shell (800) -> IDE (700)
mockedExec
.mockResolvedValueOnce({ stdout: '800 /bin/bash' }) // pid 1000 -> ppid 800 (shell)
.mockResolvedValueOnce({ stdout: '700 /usr/lib/vscode/code' }) // pid 800 -> ppid 700 (IDE)
.mockResolvedValueOnce({ stdout: '700 /usr/lib/vscode/code' }); // get command for pid 700
const result = await getIdeProcessInfo();
expect(result).toEqual({ pid: 700, command: '/usr/lib/vscode/code' });
});
it('should return parent process info if grandparent lookup fails', async () => {
(os.platform as Mock).mockReturnValue('linux');
mockedExec
.mockResolvedValueOnce({ stdout: '800 /bin/bash' }) // pid 1000 -> ppid 800 (shell)
.mockRejectedValueOnce(new Error('ps failed')) // lookup for ppid of 800 fails
.mockResolvedValueOnce({ stdout: '800 /bin/bash' }); // get command for pid 800
const result = await getIdeProcessInfo();
expect(result).toEqual({ pid: 800, command: '/bin/bash' });
});
});
describe('on Windows', () => {
it('should traverse up and find the great-grandchild of the root process', async () => {
(os.platform as Mock).mockReturnValue('win32');
const processInfoMap = new Map([
[1000, { stdout: 'ParentProcessId=900\r\nCommandLine=node.exe\r\n' }],
[
900,
{ stdout: 'ParentProcessId=800\r\nCommandLine=powershell.exe\r\n' },
],
[800, { stdout: 'ParentProcessId=700\r\nCommandLine=code.exe\r\n' }],
[700, { stdout: 'ParentProcessId=0\r\nCommandLine=wininit.exe\r\n' }],
]);
mockedExec.mockImplementation((command: string) => {
const pidMatch = command.match(/ProcessId=(\d+)/);
if (pidMatch) {
const pid = parseInt(pidMatch[1], 10);
return Promise.resolve(processInfoMap.get(pid));
}
return Promise.reject(new Error('Invalid command for mock'));
});
const result = await getIdeProcessInfo();
expect(result).toEqual({ pid: 900, command: 'powershell.exe' });
});
});
});