Skip to content

Commit b802007

Browse files
committed
Cross platfor fixes
1 parent d174145 commit b802007

6 files changed

Lines changed: 56 additions & 34 deletions

File tree

src/Builder/ServerBuilder.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,9 +119,11 @@ public function withPluginTools()
119119
$pluginSrcPath = dirname(__DIR__);
120120
$pluginSrcPath = str_replace(ROOT, '', $pluginSrcPath);
121121
$pluginSrcPath = ltrim($pluginSrcPath, DIRECTORY_SEPARATOR);
122+
// Normalize to forward slashes for consistency across platforms
123+
$pluginSrcPath = str_replace(DIRECTORY_SEPARATOR, '/', $pluginSrcPath);
122124

123125
foreach ($scanDirs as $dir) {
124-
$path = $pluginSrcPath . DIRECTORY_SEPARATOR . $dir;
126+
$path = $pluginSrcPath . '/' . $dir;
125127
if (!in_array($path, $this->scanDirs, true)) {
126128
$this->scanDirs[] = $path;
127129
}

src/Command/ServerCommand.php

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -227,19 +227,23 @@ private function launchInspector(ConsoleIo $io): int
227227
*/
228228
private function findExecutable(string $name): ?string
229229
{
230-
// Try which command first (Unix/Linux/macOS)
231-
$which = trim((string)shell_exec(sprintf('which %s 2>/dev/null', escapeshellarg($name))));
232-
if ($which !== '' && is_executable($which)) {
233-
return $which;
234-
}
235-
236-
// Try where command (Windows)
237-
$where = trim((string)shell_exec(sprintf('where %s 2>nul', escapeshellarg($name))));
238-
if ($where !== '') {
239-
$paths = explode("\n", $where);
240-
$firstPath = trim($paths[0]);
241-
if ($firstPath !== '' && is_executable($firstPath)) {
242-
return $firstPath;
230+
$nullDevice = DIRECTORY_SEPARATOR === '\\' ? 'NUL' : '/dev/null';
231+
232+
if (DIRECTORY_SEPARATOR === '\\') {
233+
// Windows: use where command
234+
$where = trim((string)shell_exec(sprintf(sprintf('where %%s 2>%s', $nullDevice), escapeshellarg($name))));
235+
if ($where !== '') {
236+
$paths = explode("\n", $where);
237+
$firstPath = trim($paths[0]);
238+
if ($firstPath !== '' && is_executable($firstPath)) {
239+
return $firstPath;
240+
}
241+
}
242+
} else {
243+
// Unix/Linux/macOS: use which command
244+
$which = trim((string)shell_exec(sprintf(sprintf('which %%s 2>%s', $nullDevice), escapeshellarg($name))));
245+
if ($which !== '' && is_executable($which)) {
246+
return $which;
243247
}
244248
}
245249

src/Tools/TinkerTools.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,8 @@ public function getPhpBinary(): ?string
198198
}
199199

200200
// Try `which php` command (most reliable for finding the active PHP)
201-
$whichResult = shell_exec('which php 2>/dev/null');
201+
$nullDevice = DIRECTORY_SEPARATOR === '\\' ? 'NUL' : '/dev/null';
202+
$whichResult = shell_exec('which php 2>' . $nullDevice);
202203
if (is_string($whichResult) && trim($whichResult) !== '') {
203204
$which = trim($whichResult);
204205
if (is_executable($which)) {

tests/TestCase/Builder/ServerBuilderTest.php

Lines changed: 25 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -117,12 +117,15 @@ public function testWithPluginToolsAddsPluginDirectory(): void
117117
$this->assertGreaterThan(1, count($scanDirs));
118118

119119
// Plugin Tools, Prompts, and Resources paths should be in scan dirs
120-
$pluginSrcPath = dirname(dirname(dirname(__DIR__))) . '/src';
121-
$pluginSrcPath = ltrim($pluginSrcPath, DIRECTORY_SEPARATOR);
120+
$pluginSrcPath = dirname(dirname(dirname(__DIR__)));
121+
// Normalize dirname result to forward slashes first
122+
$pluginSrcPath = str_replace(DIRECTORY_SEPARATOR, '/', $pluginSrcPath);
123+
$pluginSrcPath .= '/src';
124+
$pluginSrcPath = ltrim($pluginSrcPath, '/');
122125

123-
$toolsPath = $pluginSrcPath . DIRECTORY_SEPARATOR . 'Tools';
124-
$promptsPath = $pluginSrcPath . DIRECTORY_SEPARATOR . 'Prompts';
125-
$resourcesPath = $pluginSrcPath . DIRECTORY_SEPARATOR . 'Resources';
126+
$toolsPath = $pluginSrcPath . '/Tools';
127+
$promptsPath = $pluginSrcPath . '/Prompts';
128+
$resourcesPath = $pluginSrcPath . '/Resources';
126129

127130
$this->assertContains($toolsPath, $scanDirs);
128131
$this->assertContains($promptsPath, $scanDirs);
@@ -140,12 +143,15 @@ public function testWithPluginToolsDoesNotDuplicate(): void
140143
$builder->withPluginTools();
141144

142145
$scanDirs = $builder->getScanDirs();
143-
$pluginSrcPath = dirname(dirname(dirname(__DIR__))) . '/src';
144-
$pluginSrcPath = ltrim($pluginSrcPath, DIRECTORY_SEPARATOR);
146+
$pluginSrcPath = dirname(dirname(dirname(__DIR__)));
147+
// Normalize dirname result to forward slashes first
148+
$pluginSrcPath = str_replace(DIRECTORY_SEPARATOR, '/', $pluginSrcPath);
149+
$pluginSrcPath .= '/src';
150+
$pluginSrcPath = ltrim($pluginSrcPath, '/');
145151

146-
$toolsPath = $pluginSrcPath . DIRECTORY_SEPARATOR . 'Tools';
147-
$promptsPath = $pluginSrcPath . DIRECTORY_SEPARATOR . 'Prompts';
148-
$resourcesPath = $pluginSrcPath . DIRECTORY_SEPARATOR . 'Resources';
152+
$toolsPath = $pluginSrcPath . '/Tools';
153+
$promptsPath = $pluginSrcPath . '/Prompts';
154+
$resourcesPath = $pluginSrcPath . '/Resources';
149155

150156
// Count occurrences of each path - should be 1 each
151157
$toolsCount = count(array_filter($scanDirs, fn(string $dir): bool => $dir === $toolsPath));
@@ -276,12 +282,15 @@ public function testBuildWithPluginTools(): void
276282
$this->assertInstanceOf(Server::class, $server);
277283

278284
// Verify Tools, Prompts, and Resources directories are in scan dirs
279-
$pluginSrcPath = dirname(dirname(dirname(__DIR__))) . '/src';
280-
$pluginSrcPath = ltrim($pluginSrcPath, DIRECTORY_SEPARATOR);
281-
282-
$toolsPath = $pluginSrcPath . DIRECTORY_SEPARATOR . 'Tools';
283-
$promptsPath = $pluginSrcPath . DIRECTORY_SEPARATOR . 'Prompts';
284-
$resourcesPath = $pluginSrcPath . DIRECTORY_SEPARATOR . 'Resources';
285+
$pluginSrcPath = dirname(dirname(dirname(__DIR__)));
286+
// Normalize dirname result to forward slashes first
287+
$pluginSrcPath = str_replace(DIRECTORY_SEPARATOR, '/', $pluginSrcPath);
288+
$pluginSrcPath .= '/src';
289+
$pluginSrcPath = ltrim($pluginSrcPath, '/');
290+
291+
$toolsPath = $pluginSrcPath . '/Tools';
292+
$promptsPath = $pluginSrcPath . '/Prompts';
293+
$resourcesPath = $pluginSrcPath . '/Resources';
285294
$this->assertContains($toolsPath, $builder->getScanDirs());
286295
$this->assertContains($promptsPath, $builder->getScanDirs());
287296
$this->assertContains($resourcesPath, $builder->getScanDirs());

tests/TestCase/Documentation/DocumentProcessorTest.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,8 @@ protected function readTestFile(string $filename): string
5050
$content = file_get_contents($this->docsPath . $filename);
5151
$this->assertNotFalse($content, sprintf('Test file %s not found', $filename));
5252

53-
return $content;
53+
// Normalize line endings for cross-platform consistency
54+
return str_replace("\r\n", "\n", $content);
5455
}
5556

5657
/**

tests/TestCase/Tools/TinkerToolsTest.php

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -409,11 +409,12 @@ public function testSubprocessFailsWithInvalidPhpBinary(): void
409409
$result = $this->tinkerTools->execute('return 1;');
410410

411411
$this->assertFalse($result['success']);
412-
// Error can be our message or shell's "No such file or directory"
412+
// Error can be our message or shell's error (varies by platform)
413413
$this->assertTrue(
414414
str_contains($result['error'], 'PHP binary') ||
415415
str_contains($result['error'], 'No such file or directory') ||
416-
str_contains($result['error'], 'not found'),
416+
str_contains($result['error'], 'not found') ||
417+
str_contains($result['error'], 'cannot find the path'),
417418
'Expected error about missing PHP binary, got: ' . $result['error'],
418419
);
419420
}
@@ -443,6 +444,10 @@ public function testDefaultTimeoutIsThirtySeconds(): void
443444
*/
444445
public function testExecuteTimeout(): void
445446
{
447+
if (DIRECTORY_SEPARATOR === '\\') {
448+
$this->markTestSkipped('Process timeout behavior differs on Windows');
449+
}
450+
446451
// Use a very short timeout with code that sleeps
447452
$result = $this->tinkerTools->execute('sleep(5); return "done";', 1);
448453

0 commit comments

Comments
 (0)