Skip to content

Commit

Permalink
Merge branch 'develop'
Browse files Browse the repository at this point in the history
* develop:
  allow to call output() and wait() on the same foreground process
  • Loading branch information
Baptouuuu committed May 1, 2022
2 parents 6aff73e + 6248aa5 commit ab6d4a8
Show file tree
Hide file tree
Showing 4 changed files with 111 additions and 29 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Changelog

## 4.1.0 - 2022-05-01

### Changed

- It is now allowed to call `output()` and `wait()` on the same foreground process

## 4.0.1 - 2022-02-26

### Fixed
Expand Down
30 changes: 25 additions & 5 deletions src/Server/Process/Foreground.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,23 +9,33 @@
Str,
Maybe,
Either,
SideEffect,
};

final class Foreground implements Process
{
private Started $process;
private Output $output;
/** @var ?Either<Failed|Signaled|TimedOut, SideEffect> */
private ?Either $status = null;

public function __construct(Started $process, bool $streamOutput = false)
{
$this->process = $process;
$yieldOutput = function() use ($process): \Generator {
$output = $process->output();

foreach ($output as $chunk) {
yield $chunk;
}

$this->status = $output->getReturn();
};

if ($streamOutput) {
$output = Sequence::lazy(static function() use ($process) {
yield from $process->output();
});
$output = Sequence::lazy($yieldOutput);
} else {
$output = Sequence::defer($process->output());
$output = Sequence::defer($yieldOutput());
}

$this->output = new Output\Output($output);
Expand All @@ -43,6 +53,16 @@ public function output(): Output

public function wait(): Either
{
return $this->process->wait();
if (\is_null($this->status)) {
// we iterate over the output here to keep it in memory (when not
// streamed) so it is available when calling output() after wait()
// the exit status will be set on this process when done iterating
// over the output (@see __construct)
$_ = $this->output->foreach(static fn() => null);
}

// the status should always be set here because we iterated over the
// output above but we stil coalesce to the wait() call to please psalm
return $this->status ??= $this->process->wait();
}
}
42 changes: 18 additions & 24 deletions src/Server/Process/Started.php
Original file line number Diff line number Diff line change
Expand Up @@ -122,31 +122,11 @@ public function wait(): Either
// do nothing with the output
}

$status = $output->getReturn();

if ($status instanceof TimedOut) {
/** @var Either<Failed|Signaled|TimedOut, SideEffect> */
return Either::left($status);
}

if ($status['signaled'] || $status['stopped']) {
/** @var Either<Failed|Signaled|TimedOut, SideEffect> */
return Either::left(new Signaled);
}

$exitCode = new ExitCode($status['exitcode']);

if (!$exitCode->successful()) {
/** @var Either<Failed|Signaled|TimedOut, SideEffect> */
return Either::left(new Failed($exitCode));
}

/** @var Either<Failed|Signaled|TimedOut, SideEffect> */
return Either::right(new SideEffect);
return $output->getReturn();
}

/**
* @return \Generator<int, array{0: Str, 1: Type}, mixed, Status|TimedOut>
* @return \Generator<int, array{0: Str, 1: Type}, mixed, Either<Failed|Signaled|TimedOut, SideEffect>>
*/
public function output(bool $keepOutputWhileWriting = true): \Generator
{
Expand Down Expand Up @@ -176,7 +156,8 @@ public function output(bool $keepOutputWhileWriting = true): \Generator
);

if ($timedOut) {
return $this->abort();
/** @var Either<Failed|Signaled|TimedOut, SideEffect> */
return Either::left($this->abort());
}

$status = $this->status();
Expand Down Expand Up @@ -207,7 +188,20 @@ public function output(bool $keepOutputWhileWriting = true): \Generator

$this->close();

return $status;
if ($status['signaled'] || $status['stopped']) {
/** @var Either<Failed|Signaled|TimedOut, SideEffect> */
return Either::left(new Signaled);
}

$exitCode = new ExitCode($status['exitcode']);

if (!$exitCode->successful()) {
/** @var Either<Failed|Signaled|TimedOut, SideEffect> */
return Either::left(new Failed($exitCode));
}

/** @var Either<Failed|Signaled|TimedOut, SideEffect> */
return Either::right(new SideEffect);
}

/**
Expand Down
62 changes: 62 additions & 0 deletions tests/Server/Process/ForegroundTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -144,4 +144,66 @@ public function testWait()
),
);
}

public function testExitStatusIsKeptInMemory()
{
$slow = new Unix(
new Clock,
Select::timeoutAfter(new ElapsedPeriod(0)),
new Usleep,
new Second(1),
Command::foreground('php fixtures/slow.php'),
);
$process = new Foreground($slow());

$this->assertSame(
$process->wait(),
$process->wait(),
);
}

public function testExitStatusIsAvailableAfterIteratingOverTheOutput()
{
$slow = new Unix(
new Clock,
Select::timeoutAfter(new ElapsedPeriod(0)),
new Usleep,
new Second(1),
Command::foreground('php fixtures/slow.php'),
);
$process = new Foreground($slow());
$process->output()->toString();

$this->assertInstanceOf(
SideEffect::class,
$process
->wait()
->match(
static fn($sideEffect) => $sideEffect,
static fn() => null,
),
);
}

public function testOutputIsAvailableAfterWaitingForExitStatus()
{
$slow = new Unix(
new Clock,
Select::timeoutAfter(new ElapsedPeriod(0)),
new Usleep,
new Second(1),
Command::foreground('php fixtures/slow.php'),
);
$process = new Foreground($slow());
$this->assertInstanceOf(
SideEffect::class,
$process
->wait()
->match(
static fn($sideEffect) => $sideEffect,
static fn() => null,
),
);
$this->assertSame("0\n1\n2\n3\n4\n5\n", $process->output()->toString());
}
}

0 comments on commit ab6d4a8

Please sign in to comment.