Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions src/Illuminate/Contracts/Process/InvokedProcess.php
Original file line number Diff line number Diff line change
Expand Up @@ -61,4 +61,12 @@ public function latestErrorOutput();
* @return \Illuminate\Process\ProcessResult
*/
public function wait(?callable $output = null);

/**
* Wait until the given callback returns true.
*
* @param callable|null $output
* @return \Illuminate\Process\ProcessResult
*/
public function waitUntil(?callable $output = null);
Comment on lines +64 to +71
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be removed from here. this wasn't included here for a reason i guess.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What can be removed from where?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We didn’t have ‎waitUntil in the contract before but implemented in the real class. If there’s a specific reason it was left out, we can just remove it.

}
31 changes: 31 additions & 0 deletions src/Illuminate/Process/FakeInvokedProcess.php
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,37 @@ public function wait(?callable $output = null)
return $this->process->toProcessResult($this->command);
}

/**
* Wait until the given callback returns true.
*
* @param callable|null $output
* @return \Illuminate\Contracts\Process\ProcessResult
*/
public function waitUntil(?callable $output = null)
{
$shouldStop = false;

$this->outputHandler = $output
? function ($type, $buffer) use ($output, &$shouldStop) {
$shouldStop = call_user_func($output, $type, $buffer);
}
: $this->outputHandler;

if (! $this->outputHandler) {
$this->remainingRunIterations = 0;

return $this->predictProcessResult();
}

while ($this->running() && ! $shouldStop) {
//
}

$this->remainingRunIterations = 0;

return $this->process->toProcessResult($this->command);
}

/**
* Get the ultimate process result that will be returned by this "process".
*
Expand Down
255 changes: 255 additions & 0 deletions tests/Process/ProcessTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -765,6 +765,261 @@ public function testFakeInvokedProcessOutputWithLatestOutput()
$this->assertEquals("ONE\nTWO\nTHREE\n", $output[2]);
}

public function testFakeInvokedProcessWaitUntil()
{
$factory = new Factory;

$factory->fake(function () use ($factory) {
return $factory->describe()
->output('WAITING')
->output('READY')
->output('DONE')
->runsFor(iterations: 3);
});

$process = $factory->start('echo "WAITING"; sleep 1; echo "READY"; sleep 1; echo "DONE";');

$callbackInvoked = [];

$result = $process->waitUntil(function ($type, $buffer) use (&$callbackInvoked) {
$callbackInvoked[] = $buffer;

return str_contains($buffer, 'READY');
});

$this->assertInstanceOf(ProcessResult::class, $result);
$this->assertTrue($result->successful());
$this->assertContains("WAITING\n", $callbackInvoked);
$this->assertContains("READY\n", $callbackInvoked);
}

public function testFakeInvokedProcessWaitUntilWithNoCallback()
{
$factory = new Factory;

$factory->fake(function () use ($factory) {
return $factory->describe()
->output('OUTPUT');
});

$process = $factory->start('echo "OUTPUT"');

$result = $process->waitUntil();

$this->assertInstanceOf(ProcessResult::class, $result);
$this->assertTrue($result->successful());
$this->assertEquals("OUTPUT\n", $result->output());
}

public function testFakeInvokedProcessWaitUntilWithErrorOutput()
{
$factory = new Factory;

$factory->fake(function () use ($factory) {
return $factory->describe()
->output('STDOUT')
->errorOutput('ERROR1')
->errorOutput('TARGET_ERROR')
->output('MORE_STDOUT')
->runsFor(iterations: 4);
});

$process = $factory->start('echo "STDOUT"; echo "ERROR1" >&2; echo "TARGET_ERROR" >&2; echo "MORE_STDOUT";');

$callbackInvoked = [];

$result = $process->waitUntil(function ($type, $buffer) use (&$callbackInvoked) {
$callbackInvoked[] = [$type, $buffer];

return str_contains($buffer, 'TARGET_ERROR');
});

$this->assertInstanceOf(ProcessResult::class, $result);
$this->assertTrue($result->successful());
$this->assertContains(['out', "STDOUT\n"], $callbackInvoked);
$this->assertContains(['err', "ERROR1\n"], $callbackInvoked);
$this->assertContains(['err', "TARGET_ERROR\n"], $callbackInvoked);
}

public function testFakeInvokedProcessWaitUntilCalledTwice()
{
$factory = new Factory;

$factory->fake(function () use ($factory) {
return $factory->describe()
->output('FIRST')
->output('SECOND')
->output('THIRD')
->output('FOURTH')
->runsFor(iterations: 4);
});

$process = $factory->start('echo "FIRST"; echo "SECOND"; echo "THIRD"; echo "FOURTH";');

$firstCallbackInvoked = [];
$secondCallbackInvoked = [];

$firstResult = $process->waitUntil(function ($type, $buffer) use (&$firstCallbackInvoked) {
$firstCallbackInvoked[] = $buffer;

return str_contains($buffer, 'SECOND');
});

$this->assertInstanceOf(ProcessResult::class, $firstResult);
$this->assertTrue($firstResult->successful());
$this->assertContains("FIRST\n", $firstCallbackInvoked);
$this->assertContains("SECOND\n", $firstCallbackInvoked);
$this->assertCount(2, $firstCallbackInvoked);

$secondResult = $process->waitUntil(function ($type, $buffer) use (&$secondCallbackInvoked) {
$secondCallbackInvoked[] = $buffer;

return str_contains($buffer, 'FOURTH');
});

$this->assertInstanceOf(ProcessResult::class, $secondResult);
$this->assertTrue($secondResult->successful());
$this->assertContains("THIRD\n", $secondCallbackInvoked);
$this->assertContains("FOURTH\n", $secondCallbackInvoked);
$this->assertCount(2, $secondCallbackInvoked);
}

public function testFakeInvokedProcessWaitUntilThatNeverMatches()
{
$factory = new Factory;

$factory->fake(function () use ($factory) {
return $factory->describe()
->output('LINE1')
->output('LINE2')
->output('LINE3')
->runsFor(iterations: 3);
});

$process = $factory->start('echo "LINE1"; echo "LINE2"; echo "LINE3";');

$callbackInvoked = [];

$result = $process->waitUntil(function ($type, $buffer) use (&$callbackInvoked) {
$callbackInvoked[] = $buffer;

return str_contains($buffer, 'NEVER_MATCHES');
});

$this->assertInstanceOf(ProcessResult::class, $result);
$this->assertTrue($result->successful());
$this->assertCount(3, $callbackInvoked);
$this->assertContains("LINE1\n", $callbackInvoked);
$this->assertContains("LINE2\n", $callbackInvoked);
$this->assertContains("LINE3\n", $callbackInvoked);
}

public function testFakeInvokedProcessWaitUntilFollowedByWait()
{
$factory = new Factory;

$factory->fake(function () use ($factory) {
return $factory->describe()
->output('FIRST')
->output('SECOND')
->output('THIRD')
->runsFor(iterations: 3);
});

$process = $factory->start('echo "FIRST"; echo "SECOND"; echo "THIRD";');

$waitUntilCallbacks = [];
$waitCallbacks = [];

$process->waitUntil(function ($type, $buffer) use (&$waitUntilCallbacks) {
$waitUntilCallbacks[] = $buffer;

return str_contains($buffer, 'FIRST');
});

$result = $process->wait(function ($type, $buffer) use (&$waitCallbacks) {
$waitCallbacks[] = $buffer;
});

$this->assertInstanceOf(ProcessResult::class, $result);
$this->assertTrue($result->successful());
$this->assertCount(1, $waitUntilCallbacks);
$this->assertEquals("FIRST\n", $waitUntilCallbacks[0]);
$this->assertCount(2, $waitCallbacks);
$this->assertContains("SECOND\n", $waitCallbacks);
$this->assertContains("THIRD\n", $waitCallbacks);
}

public function testFakeInvokedProcessWaitCalledTwice()
{
$factory = new Factory;

$factory->fake(function () use ($factory) {
return $factory->describe()
->output('FIRST')
->output('SECOND')
->output('THIRD')
->runsFor(iterations: 3);
});

$process = $factory->start('echo "FIRST"; echo "SECOND"; echo "THIRD";');

$firstCallbackInvoked = [];
$secondCallbackInvoked = [];

$firstResult = $process->wait(function ($type, $buffer) use (&$firstCallbackInvoked) {
$firstCallbackInvoked[] = $buffer;
});

$this->assertInstanceOf(ProcessResult::class, $firstResult);
$this->assertTrue($firstResult->successful());
$this->assertCount(3, $firstCallbackInvoked);
$this->assertContains("FIRST\n", $firstCallbackInvoked);
$this->assertContains("SECOND\n", $firstCallbackInvoked);
$this->assertContains("THIRD\n", $firstCallbackInvoked);

$secondResult = $process->wait(function ($type, $buffer) use (&$secondCallbackInvoked) {
$secondCallbackInvoked[] = $buffer;
});

$this->assertInstanceOf(ProcessResult::class, $secondResult);
$this->assertTrue($secondResult->successful());
$this->assertEmpty($secondCallbackInvoked);
}

public function testFakeInvokedProcessWaitFollowedByWaitUntil()
{
$factory = new Factory;

$factory->fake(function () use ($factory) {
return $factory->describe()
->output('FIRST')
->output('SECOND')
->output('THIRD')
->runsFor(iterations: 3);
});

$process = $factory->start('echo "FIRST"; echo "SECOND"; echo "THIRD";');

$waitCallbacks = [];
$waitUntilCallbacks = [];

$process->wait(function ($type, $buffer) use (&$waitCallbacks) {
$waitCallbacks[] = $buffer;
});

$result = $process->waitUntil(function ($type, $buffer) use (&$waitUntilCallbacks) {
$waitUntilCallbacks[] = $buffer;

return str_contains($buffer, 'THIRD');
});

$this->assertInstanceOf(ProcessResult::class, $result);
$this->assertTrue($result->successful());
$this->assertCount(3, $waitCallbacks);
$this->assertEmpty($waitUntilCallbacks);
}

public function testBasicFakeAssertions()
{
$factory = new Factory;
Expand Down