mirror of
https://github.com/QwenLM/qwen-code.git
synced 2025-12-23 10:17:50 +00:00
Compare commits
2 Commits
cb59b5a9dc
...
c2b59038ae
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c2b59038ae | ||
|
|
27bf42b4f5 |
@@ -50,7 +50,7 @@ describe('getIdeProcessInfo', () => {
|
||||
expect(result).toEqual({ pid: 700, command: '/usr/lib/vscode/code' });
|
||||
});
|
||||
|
||||
it('should return parent process info if grandparent lookup fails', async () => {
|
||||
it('should return shell process info if grandparent lookup fails', async () => {
|
||||
(os.platform as Mock).mockReturnValue('linux');
|
||||
mockedExec
|
||||
.mockResolvedValueOnce({ stdout: '800 /bin/bash' }) // pid 1000 -> ppid 800 (shell)
|
||||
@@ -63,7 +63,7 @@ describe('getIdeProcessInfo', () => {
|
||||
});
|
||||
|
||||
describe('on Windows', () => {
|
||||
it('should traverse up and find the great-grandchild of the root process', async () => {
|
||||
it('should find known IDE process in the chain', async () => {
|
||||
(os.platform as Mock).mockReturnValue('win32');
|
||||
|
||||
const processes = [
|
||||
@@ -97,38 +97,92 @@ describe('getIdeProcessInfo', () => {
|
||||
if (file === 'powershell') {
|
||||
return Promise.resolve({ stdout: JSON.stringify(processes) });
|
||||
}
|
||||
// Fallback for getProcessInfo calls if any (should not happen in new logic for Windows traversal)
|
||||
return Promise.resolve({ stdout: '' });
|
||||
});
|
||||
|
||||
const result = await getIdeProcessInfo();
|
||||
// 1000 -> 900 -> 800 -> 700 (root child)
|
||||
// Great-grandchild of root (700) is 900.
|
||||
// Wait, logic is:
|
||||
// 700 (root child) -> 800 (grandchild) -> 900 (great-grandchild) -> 1000 (current)
|
||||
// The code looks for the grandchild of the root.
|
||||
// Root is 0 (conceptually). Child of root is 700. Grandchild is 800.
|
||||
// The code says:
|
||||
// "We've found the grandchild of the root (`currentPid`). The IDE process is its child, which we've stored in `previousPid`."
|
||||
// Strategy 1: Should find code.exe (known IDE process) in the chain
|
||||
// Process chain: 1000 (node.exe) -> 900 (powershell.exe) -> 800 (code.exe) -> 700 (wininit.exe)
|
||||
// The function should identify code.exe as the IDE process
|
||||
expect(result).toEqual({ pid: 800, command: 'code.exe' });
|
||||
});
|
||||
|
||||
// Let's trace the loop in `getIdeProcessInfoForWindows`:
|
||||
// currentPid = 1000, previousPid = 1000
|
||||
// Loop 1:
|
||||
// proc = 1000. parentPid = 900.
|
||||
// parentProc = 900. parentProc.parentPid = 800 != 0.
|
||||
// previousPid = 1000. currentPid = 900.
|
||||
// Loop 2:
|
||||
// proc = 900. parentPid = 800.
|
||||
// parentProc = 800. parentProc.parentPid = 700 != 0.
|
||||
// previousPid = 900. currentPid = 800.
|
||||
// Loop 3:
|
||||
// proc = 800. parentPid = 700.
|
||||
// parentProc = 700. parentProc.parentPid = 0.
|
||||
// MATCH!
|
||||
// ideProc = processMap.get(previousPid) = processMap.get(900) = powershell.exe
|
||||
// return { pid: 900, command: 'powershell.exe' }
|
||||
it('should find shell parent when shell exists in chain', async () => {
|
||||
(os.platform as Mock).mockReturnValue('win32');
|
||||
|
||||
expect(result).toEqual({ pid: 900, command: 'powershell.exe' });
|
||||
const processes = [
|
||||
{
|
||||
ProcessId: 1000,
|
||||
ParentProcessId: 900,
|
||||
Name: 'node.exe',
|
||||
CommandLine: 'node.exe',
|
||||
},
|
||||
{
|
||||
ProcessId: 900,
|
||||
ParentProcessId: 800,
|
||||
Name: 'cmd.exe',
|
||||
CommandLine: 'cmd.exe',
|
||||
},
|
||||
{
|
||||
ProcessId: 800,
|
||||
ParentProcessId: 700,
|
||||
Name: 'explorer.exe',
|
||||
CommandLine: 'explorer.exe',
|
||||
},
|
||||
{
|
||||
ProcessId: 700,
|
||||
ParentProcessId: 0,
|
||||
Name: 'wininit.exe',
|
||||
CommandLine: 'wininit.exe',
|
||||
},
|
||||
];
|
||||
|
||||
mockedExec.mockImplementation((file: string, _args: string[]) => {
|
||||
if (file === 'powershell') {
|
||||
return Promise.resolve({ stdout: JSON.stringify(processes) });
|
||||
}
|
||||
return Promise.resolve({ stdout: '' });
|
||||
});
|
||||
|
||||
const result = await getIdeProcessInfo();
|
||||
// Strategy 3: Should find cmd.exe and return its parent (explorer.exe)
|
||||
expect(result).toEqual({ pid: 800, command: 'explorer.exe' });
|
||||
});
|
||||
|
||||
it('should handle Git Bash with missing parent by finding IDE in process table', async () => {
|
||||
(os.platform as Mock).mockReturnValue('win32');
|
||||
|
||||
const processes = [
|
||||
{
|
||||
ProcessId: 1000,
|
||||
ParentProcessId: 900,
|
||||
Name: 'node.exe',
|
||||
CommandLine: 'node.exe',
|
||||
},
|
||||
{
|
||||
ProcessId: 900,
|
||||
ParentProcessId: 12345, // Parent doesn't exist in process table
|
||||
Name: 'bash.exe',
|
||||
CommandLine: 'bash.exe',
|
||||
},
|
||||
{
|
||||
ProcessId: 800,
|
||||
ParentProcessId: 0,
|
||||
Name: 'code.exe',
|
||||
CommandLine: 'code.exe',
|
||||
},
|
||||
];
|
||||
|
||||
mockedExec.mockImplementation((file: string, _args: string[]) => {
|
||||
if (file === 'powershell') {
|
||||
return Promise.resolve({ stdout: JSON.stringify(processes) });
|
||||
}
|
||||
return Promise.resolve({ stdout: '' });
|
||||
});
|
||||
|
||||
const result = await getIdeProcessInfo();
|
||||
// Strategy 2: Git Bash with missing parent should find code.exe in process table
|
||||
expect(result).toEqual({ pid: 800, command: 'code.exe' });
|
||||
});
|
||||
|
||||
it('should handle empty process list gracefully', async () => {
|
||||
@@ -142,13 +196,7 @@ describe('getIdeProcessInfo', () => {
|
||||
|
||||
it('should handle malformed JSON output gracefully', async () => {
|
||||
(os.platform as Mock).mockReturnValue('win32');
|
||||
mockedExec.mockResolvedValue({ stdout: '{"invalid":json}' }); // Malformed JSON will throw in JSON.parse
|
||||
|
||||
// If JSON.parse fails, getProcessTreeForWindows returns empty map.
|
||||
// Then getIdeProcessInfoForWindows returns current pid.
|
||||
|
||||
// Wait, mockedExec throws? No, JSON.parse throws.
|
||||
// getProcessTreeForWindows catches error and returns empty map.
|
||||
mockedExec.mockResolvedValue({ stdout: '{"invalid":json}' });
|
||||
|
||||
const result = await getIdeProcessInfo();
|
||||
expect(result).toEqual({ pid: 1000, command: '' });
|
||||
|
||||
@@ -14,6 +14,18 @@ const execFileAsync = promisify(execFile);
|
||||
|
||||
const MAX_TRAVERSAL_DEPTH = 32;
|
||||
|
||||
/**
|
||||
* Escapes a string for safe use inside PowerShell double-quoted strings.
|
||||
* Must escape backslashes first, then double quotes.
|
||||
*
|
||||
* @param str The string to escape.
|
||||
* @returns The escaped string safe for PowerShell double-quoted context.
|
||||
*/
|
||||
function escapeForPowerShellDoubleQuotes(str: string): string {
|
||||
// Order matters: escape backslashes first, then double quotes
|
||||
return str.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the parent process ID, name, and command for a given process ID.
|
||||
*
|
||||
@@ -39,7 +51,7 @@ async function getProcessInfo(pid: number): Promise<{
|
||||
].join(' ');
|
||||
|
||||
const { stdout } = await execAsync(
|
||||
`powershell -NoProfile -NonInteractive -Command "${powershellCommand.replace(/"/g, '\\"')}"`,
|
||||
`powershell -NoProfile -NonInteractive -Command "${escapeForPowerShellDoubleQuotes(powershellCommand)}"`,
|
||||
);
|
||||
const output = stdout.trim();
|
||||
if (!output) return { parentPid: 0, name: '', command: '' };
|
||||
|
||||
Reference in New Issue
Block a user