An AperiTestCase decorator. Test shell scripts, including interactive scripts. Read from sdout/stderr and write to stdin.
runScript(
$command, - shell $command
$cwd = null, - optionally pass $cwd (absolute path)
$config = array()) - Optionally override default settings.
(see InteractiveScript::_defaultConfig)enter($input) - enter some input at a script prompt
assertStdout($text) - Assert the last chunk of text read from
script STDOUT.
In a non-interactive script the "last
chunk" is the full stdout output. In
an interactive script this is one of:
(a) the initial stdout output up to
and including the first prompt
after launching the script
(b) all stdout text after a stdin input
up to and including the next promptassertStderr($text) - stderr since last prompt or
start of scriptassertStdoutHas($pattern) - assert a perl regex pattern in the
last stdout chunkassertStderrHas($pattern) - assert a perl regex pattern in the
last stderr chunkassertConversationHas($pattern) - assert a perl regex pattern in the
full conversation ie all stdin
inputs and all stdout / stderr
chunksassertExitCode($i) - the script's exit code
dumpStdout() - print the last chunk of text read from
script stdoutdumpStderr() - print the last chunk of text read from
script stderrdumpConversation() - print all inputs, all stderr chunks &
all stdout chunks (in the sequence in
which they were read by the script
wrapper: this may differ from the
sequence in which they were written
by the script).dumpStatus() - proc_get_status() call: command, pid,
running etc. From the manual: "The
exit code returned by the process (which
is only meaningful if running is FALSE).
Only the first call of this function
returns the real value, the next call
returns -1".A test might look something like this. First, the script to test, greet.php:
<?php
fwrite("hello world! hello! hello!\n");
exit(0);
Decorate the standard test case with ShellTestTools:
<?php
class YourTestCase extends AperiUnitTest {
function beforeInvoking() {
$this->mixin('ShellTestTools');
}
Test the script:
<?php
class YourTestCase extends AperiUnitTest {
function beforeInvoking() {
...
}
function test() {
$this->runScript('php greet.php');
$this->assertStdoutHas('/hello world/i');
}
Often it's better to assert a few, significant parts of the output rather than an identical string comparison of the entire text.
The ~Has() methods accept a variable number of aguments. You can optionally specify the number of matches:
<?php
class YourTestCase extends AperiUnitTest {
...
...
function test() {
$this->runScript('php greet.php');
$this->assertStdoutHas(3, "/hello/");
}
Use $this->enter($input) for script inputs. This has the same effect as typing in the terminal and hitting enter.
For convenience, the $input arg doesn't have to end in a newline. This will be added automatically if it doesn't exist.
Suppose you want to test the script, petshop.php:
<?php
fwrite(STDOUT, "what's your pet?\n");
$reply = trim(fgets(STDIN));
if('exit' == $reply) {
exit(0);
}
if('Norwegian Blue' == $reply) {
fwrite(STDERR, "he doesn't look at all well\n");
exit(1);
} else {
fwrite(STDOUT, "that's a nice $reply\n");
exit(0);
}
<?php
class YourTestCase extends AperiUnitTest {
function beforeInvoking() {
$this->mixin('ShellTestTools');
}
function test() {
$this->runScript('php petshop.php');
$this->assertStdoutHas("/what's your pet\?/i");
$this->enter('gannet');
$this->assertStdoutHas("/that's a nice gannet/i");
}
To check the stderr output you'd do a similar test with "Norwegian Blue" as the input.
Note that, in an interactive script, assertStdout() does not match against the full stdout output, just the chunks read between prompts. As soon as you enter($input), buffers are filled with the next stdout chunk. The methods: assertStdout, assertStdoutHas, assertStderr, and assertStderrHas all work in this way. Only assertConversationHas looks at the full "conversation" ie the combined stdin, stderr and stdout text in the sequence in which they were read by the wrapper class (note that this may differ slightly from the order in which the writes were made by the wrapped script).
Thus, you can test all the logic pathways in a complex script which contains a series of prompts and inputs. Enter an input, assert the script's stdout/stderr reponse to the input, and repeat until done.
If an interactive test is failing, but you're not sure why, try calling $this->dumpConversation() to see the big picture.