assertTrue(new Command('true')->status()->success()); } /** * Test that the stdin, stdout, and stderr handles are captured. */ public function testOutput(): void { $output = new Command('true') ->stdin(Stdio::null()) ->stdout(Stdio::null()) ->stderr(Stdio::null()) ->output(); $this->assertTrue($output->status->success()); } public function testChildOnlyAcceptsProcesses(): void { $this->expectException(InvalidArgumentException::class); $handle = fopen('php://memory', 'w'); $this->assertNotFalse($handle); new Child($handle, []); } /** * Test that the stdin, stdout, and stderr handles are captured. */ public function testChild(): void { $child = new Command('/usr/bin/sleep') ->stdin(Stdio::null()) ->stdout(Stdio::null()) ->stderr(Stdio::null()) ->arg('1000') ->spawn(); $this->assertGreaterThan(1, $child->id()); $child->kill(); $status = $child->wait(); $this->assertEquals(null, $status->code()); $this->assertEquals(9, $status->signal()); $this->assertEquals(null, $status->stoppedSignal()); // Killing and waiting after the child exited does nothing. $child->kill(); $this->assertSame($child->wait(), $status); $output = $child->waitWithOutput(); $this->assertSame($output->status, $status); $this->assertSame($child->waitWithOutput(), $output); } public function testChildDestructor(): void { $process = proc_open( ['true'], [ 0 => ['pipe', 'w'], 1 => ['pipe', 'r'], 2 => ['pipe', 'r'], ], $pipes, ); $this->assertNotFalse($process); $child = new Child($process, $pipes); $this->assertEquals('process', get_resource_type($process)); $this->assertEquals('stream', get_resource_type($pipes[0])); $this->assertEquals('stream', get_resource_type($pipes[1])); $this->assertEquals('stream', get_resource_type($pipes[2])); unset($child); gc_collect_cycles(); $this->assertEquals('Unknown', get_resource_type($process)); $this->assertEquals('Unknown', get_resource_type($pipes[0])); $this->assertEquals('Unknown', get_resource_type($pipes[1])); $this->assertEquals('Unknown', get_resource_type($pipes[2])); } public function testStdio(): void { if (PHP_OS_FAMILY === 'Windows') { $this->markTestSkipped(); } $cat = new Command('cat') ->stdin(Stdio::piped()) ->stdout(Stdio::piped()) ->stderr(Stdio::piped()) ->spawn(); $cat->stdin?->write('Hello, World!'); $cat->stdin?->close(); $this->assertNotNull($cat->stdout); $output = new Command('cat') ->stdin($cat->stdout) ->output(); $this->assertTrue($cat->wait()->success()); $this->assertTrue($output->status->success()); $this->assertEquals('Hello, World!', $output->stdout); $this->assertEquals('', $output->stderr); } public function testThrowsOnNotExecutable(): void { $this->expectException(ChildException::class); new Command(uniqid())->spawn(); } public function testEnv(): void { if (PHP_OS_FAMILY === 'Windows') { $this->markTestSkipped(); } $output = new Command('env') ->envClear() ->envs([ 'FOO' => 'foo', 'BAR' => 'bar', 'BAZ' => 'baz', ]) ->envRemove('FOO') ->output(); $this->assertTrue($output->status->success()); $this->assertEquals("BAR=bar\nBAZ=baz\n", $output->stdout); } public function testEnvInherit(): void { if (PHP_OS_FAMILY === 'Windows') { $this->markTestSkipped(); } $home = getenv('HOME'); $this->assertIsString($home, 'Congratulations! You have a weird system'); $child = new Command('env') ->stdout(Stdio::piped()) ->env('FOO', 'foo') ->spawn(); $this->assertNotNull($child->stdout); $envs = []; foreach (explode(PHP_EOL, $child->stdout->getContents()) as $line) { [$var, $val] = explode('=', $line); $envs[$var] = $val; } $this->assertEquals($home, $envs['HOME']); $this->assertEquals('foo', $envs['FOO']); } }