From 09b99e357ddc95b24569f1911223e9f132cd0985 Mon Sep 17 00:00:00 2001 From: rango Date: Mon, 3 Aug 2026 21:53:16 +0800 Subject: [PATCH] Add exit code phpunit tests --- phpunit/src/ExitCodeTest.php | 155 +++++++++++++++++++++++++++++++++++ 1 file changed, 155 insertions(+) create mode 100644 phpunit/src/ExitCodeTest.php diff --git a/phpunit/src/ExitCodeTest.php b/phpunit/src/ExitCodeTest.php new file mode 100644 index 00000000..cd54fec1 --- /dev/null +++ b/phpunit/src/ExitCodeTest.php @@ -0,0 +1,155 @@ + process exits with N + * - uncaught exception -> process exits with 255 + * - fatal error -> process exits with 255 + * - normal completion -> process exits with 0 + */ +class ExitCodeTest extends TestCase +{ + private string $workDir; + + protected function setUp(): void + { + parent::setUp(); + $this->workDir = sys_get_temp_dir() . '/typephp-exitcode-' . bin2hex(random_bytes(6)); + mkdir($this->workDir, 0777, true); + } + + protected function tearDown(): void + { + $this->removeDirectory($this->workDir); + parent::tearDown(); + } + + public function testExplicitExitCodeIsPreserved(): void + { + $exitCode = $this->buildAndRun(<<<'PHP' +assertSame(3, $exitCode); + } + + public function testUncaughtExceptionExitsWith255(): void + { + $exitCode = $this->buildAndRun(<<<'PHP' +assertSame(255, $exitCode); + } + + public function testFatalErrorExitsWith255(): void + { + $exitCode = $this->buildAndRun(<<<'PHP' +assertSame(255, $exitCode); + } + + public function testNormalCompletionExitsWithZero(): void + { + $exitCode = $this->buildAndRun(<<<'PHP' +assertSame(0, $exitCode); + } + + /** + * Compile a single-file bin project and return the exit code of the binary. + */ + private function buildAndRun(string $source): int + { + $srcDir = $this->workDir . '/src'; + $binDir = $this->workDir . '/bin'; + $buildDir = $this->workDir . '/build'; + mkdir($srcDir, 0777, true); + mkdir($binDir, 0777, true); + + file_put_contents($srcDir . '/main.php', $source); + file_put_contents($srcDir . '/project.yml', sprintf( + "name: exit-code-test\n" + . "version: 0.1.0\n" + . "mode: bin\n" + . "output: %s/exit_code_test\n" + . "build-dir: %s\n" + . "optimize: 0\n" + . "no-progress: true\n" + . "\n" + . "sources:\n" + . " - main.php\n", + $binDir, + $buildDir + )); + + $tpc = dirname(__DIR__, 2) . '/bin/tpc.php'; + $buildCommand = escapeshellarg(PHP_BINARY) . ' ' . escapeshellarg($tpc) + . ' ' . escapeshellarg($srcDir . '/project.yml') . ' -j 1'; + exec($buildCommand . ' 2>&1', $buildOutput, $buildExitCode); + $this->assertSame( + 0, + $buildExitCode, + 'AOT compilation failed: ' . implode("\n", $buildOutput) + ); + + $binary = $binDir . '/exit_code_test'; + exec(escapeshellarg($binary) . ' 2>&1', $runOutput, $exitCode); + + return $exitCode; + } + + private function removeDirectory(string $dir): void + { + if (!is_dir($dir)) { + return; + } + $items = scandir($dir); + foreach ($items ?: [] as $item) { + if ($item === '.' || $item === '..') { + continue; + } + $path = $dir . DIRECTORY_SEPARATOR . $item; + is_dir($path) ? $this->removeDirectory($path) : @unlink($path); + } + @rmdir($dir); + } +}