run-phpt - execute PHPT tests
Introduction
The run-phpt
command is used to execute tests in the
PHPT
format. If the xdebug
extension
is present, the command can also be used to generate code coverage. This
coverage can then be used to construct a coverage report and even to
intelligently detect both modified tests and tests that are affected by
changes to the source code in between test runs.
The command takes as arguments a list of paths containing tests to execute,
unless the --modified
option is specified, then it takes
as arguments the path to the tests directory and the path to the source
directory. If the --modified
option is not specified,
and no arguments are passed, the command searches for tests in the current
working directory.
--modified
The --modified
or -m
option, if present,
implies both the --recursive
and --coverage
options, and is used to generate a coverage database and to use that database
to detect modifications in the tests and the source. These modified tests are
then executed.
The command places a file named pear2coverage.db
in the
tests directory, which is an Sqlite3 database containing the coverage information.
The coverage can be viewed as a web-based report by taking the
pear2coverage.phar.php
file installed with the developer
tools and placing it in a web server directory, and then browsing to it.
The web server must have the phar
and sqlite3
extensions enabled in order to function properly.
To illustrate how powerful this option is, imagine a hypothetical directory structure as follows:
src/ File1.php File2.php File3.php tests/ test1.phpt test2.phpt test3.phpt test4.phpt
Here are the source files:
File1.php:
<?php
class File1
{
var $a = 1;
function __construct($a = 1)
{
$this->a = $a;
}
function setInternalThing($thing)
{
$this->internal = $thing;
$this->internal->initialize($this);
}
}
?>
File2.php:
<?php
class File2
{
function initialize(File1 $parent)
{
$parent->a = 2;
}
}
?>
File3.php:
<?php
class File3 extends File2
{
function initialize(File1 $parent)
{
$parent->a = 3;
}
}
?>
test1.phpt:
--TEST--
test 1
--FILE--
<?php
function __autoload($class) { include __DIR__ . '/../src/' . $class . '.php'; }
$test = new File1(6);
if ($test->a != 6) {
echo '$a is not 6, it is ' . $test->a, "\n";
}
?>
===DONE===
--EXPECT--
===DONE===
test2.phpt:
--TEST--
test 2
--FILE--
<?php
function __autoload($class) { include __DIR__ . '/../src/' . $class . '.php'; }
$test2 = new File2;
$test = new File1;
$test->setInternalThing($test2);
if ($test->a != 2) {
echo '$a is not 2, it is ' . $test->a, "\n";
}
?>
===DONE===
--EXPECT--
===DONE===
test3.phpt:
--TEST--
test 3
--FILE--
<?php
function __autoload($class) { include __DIR__ . '/../src/' . $class . '.php'; }
$test2 = new File2;
$test = new stdClass;
$test2->initialize($test);
if ($test->a != 2) {
echo '$a is not 2, it is ' . $test->a, "\n";
}
?>
===DONE===
--EXPECT--
===DONE===
test4.phpt:
--TEST--
test 4
--FILE--
<?php
function __autoload($class) { include __DIR__ . '/../src/' . $class . '.php'; }
$test3 = new File3;
$test = new File1;
$test->setInternalThing($test3);
if ($test->a != 3) {
echo '$a is not 3, it is ' . $test->a, "\n";
}
?>
===DONE===
--EXPECT--
===DONE===
If a modification is made to File3.php
, the
run-phpt command will detect that only test4.phpt
uses
this file, and will run that test. If a modification is made to
File2.php
, test2.phpt
,
test3.phpt
and test4.phpt
will all be
executed, even though test4.phpt
does not directly
use the File2
class, because File3
extends File2
and so the file is loaded. If a modification
is made to File1.php
, test1.phpt
,
test2.phpt
and test4.phpt
will all
be executed. Finally, if any of the phpt test files are executed, or any
external files that they include are modified, they will be run again. If a
new test, test5.phpt
is added, the run-phpt command will
also detect the test and run it.
This allows extremely efficient development, as surgically running only tests that are affected by source code changes allows assurance that even the most remote file dependencies are validated, and irrelevant tests are not executed unnecessarily. By relying upon the coverage report, it is also easy to catch subtle logic bugs preventing code blocks from being executed, resulting in far more robust code much faster. Pyrus itself was developed using this technique.
--recursive
The --recursive
or -r
option causes Pyrus
to recursively traverse directories specified
--coverage
the --coverage
or -x
option causes
Pyrus to record coverage using the xdebug
extension's
code coverage capabilities.