diff --git a/code/core/src/common/utils/process-ancestry.test.ts b/code/core/src/common/utils/process-ancestry.test.ts index faa6a7f48624..3aaaab8f33ed 100644 --- a/code/core/src/common/utils/process-ancestry.test.ts +++ b/code/core/src/common/utils/process-ancestry.test.ts @@ -90,14 +90,24 @@ describe('getProcessAncestry', () => { expect(getProcessAncestry(9999)).toEqual([]); }); - it('breaks on cycles in the process tree', () => { + it('stops without warning on a self-referencing process on posix', () => { platformMock.mockReturnValue('linux'); - const warn = vi.spyOn(console, 'warn').mockImplementation(() => {}); execSyncMock.mockReturnValue(' 5 5 looping-process \n'); - getProcessAncestry(5); + expect(getProcessAncestry(5)).toEqual([{ pid: 5, ppid: 5, command: 'looping-process' }]); + }); - expect(warn).toHaveBeenCalledWith('Detected cycle in process tree at PID 5'); - warn.mockRestore(); + it('stops without warning on cycles in the process tree', () => { + platformMock.mockReturnValue('win32'); + execSyncMock.mockImplementation((command: string) => + command.includes('ProcessId=10)') + ? 'Node,CommandLine,ParentProcessId,ProcessId\r\nHOST,node child.js,20,10\r\n' + : 'Node,CommandLine,ParentProcessId,ProcessId\r\nHOST,cmd.exe,10,20\r\n' + ); + + expect(getProcessAncestry(10)).toEqual([ + { pid: 10, ppid: 20, command: 'node child.js' }, + { pid: 20, ppid: 10, command: 'cmd.exe' }, + ]); }); }); diff --git a/code/core/src/common/utils/process-ancestry.ts b/code/core/src/common/utils/process-ancestry.ts index 0f63137e2f53..9a30223eaf2e 100644 --- a/code/core/src/common/utils/process-ancestry.ts +++ b/code/core/src/common/utils/process-ancestry.ts @@ -119,8 +119,8 @@ function walkAncestry( let maxDepth = MAX_DEPTH; while (currentPid && maxDepth > 0) { + // Windows reuses PIDs without reparenting orphans, so a cycle here is expected. if (visited.has(currentPid)) { - console.warn(`Detected cycle in process tree at PID ${currentPid}`); break; } visited.add(currentPid); diff --git a/code/lib/create-storybook/src/commands/PreflightCheckCommand.test.ts b/code/lib/create-storybook/src/commands/PreflightCheckCommand.test.ts index 15b318ac36d1..c21b7bbb1167 100644 --- a/code/lib/create-storybook/src/commands/PreflightCheckCommand.test.ts +++ b/code/lib/create-storybook/src/commands/PreflightCheckCommand.test.ts @@ -5,6 +5,7 @@ import { PackageManagerName, resolveStorybookVersionSpecifier, invalidateProjectRootCache, + getProcessAncestry, } from 'storybook/internal/common'; import { logger, once } from 'storybook/internal/node-logger'; import { MinimumReleaseAgeHandledError } from 'storybook/internal/server-errors'; @@ -55,6 +56,7 @@ describe('PreflightCheckCommand', () => { vi.clearAllMocks(); once.clear(); vi.mocked(resolveStorybookVersionSpecifier).mockReturnValue(undefined); + vi.mocked(getProcessAncestry).mockReturnValue([]); }); afterAll(() => {