Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Import PHPUnit 1.x releases.

  • Loading branch information...
commit 5a245230f45b4a9d595fdeec7324e5ddbdc4a460 0 parents
@sebastianbergmann authored
83 PHPUnit.php
@@ -0,0 +1,83 @@
+<?php
+//
+// +------------------------------------------------------------------------+
+// | PEAR :: PHPUnit |
+// +------------------------------------------------------------------------+
+// | Copyright (c) 2002-2005 Sebastian Bergmann <sb@sebastian-bergmann.de>. |
+// +------------------------------------------------------------------------+
+// | This source file is subject to version 3.00 of the PHP License, |
+// | that is available at http://www.php.net/license/3_0.txt. |
+// | If you did not receive a copy of the PHP license and are unable to |
+// | obtain it through the world-wide-web, please send a note to |
+// | license@php.net so we can mail you a copy immediately. |
+// +------------------------------------------------------------------------+
+//
+// $Id$
+//
+
+require_once 'PHPUnit/TestCase.php';
+require_once 'PHPUnit/TestResult.php';
+require_once 'PHPUnit/TestSuite.php';
+
+/**
+ * PHPUnit runs a TestSuite and returns a TestResult object.
+ *
+ * Here is an example:
+ *
+ * <code>
+ * <?php
+ * require_once 'PHPUnit.php';
+ *
+ * class MathTest extends PHPUnit_TestCase {
+ * var $fValue1;
+ * var $fValue2;
+ *
+ * function MathTest($name) {
+ * $this->PHPUnit_TestCase($name);
+ * }
+ *
+ * function setUp() {
+ * $this->fValue1 = 2;
+ * $this->fValue2 = 3;
+ * }
+ *
+ * function testAdd() {
+ * $this->assertTrue($this->fValue1 + $this->fValue2 == 5);
+ * }
+ * }
+ *
+ * $suite = new PHPUnit_TestSuite();
+ * $suite->addTest(new MathTest('testAdd'));
+ *
+ * $result = PHPUnit::run($suite);
+ * print $result->toHTML();
+ * ?>
+ * </code>
+ *
+ * Alternatively, you can pass a class name to the PHPUnit_TestSuite()
+ * constructor and let it automatically add all methods of that class
+ * that start with 'test' to the suite:
+ *
+ * <code>
+ * <?php
+ * $suite = new PHPUnit_TestSuite('MathTest');
+ * $result = PHPUnit::run($suite);
+ * print $result->toHTML();
+ * ?>
+ * </code>
+ *
+ * @author Sebastian Bergmann <sb@sebastian-bergmann.de>
+ * @copyright Copyright &copy; 2002-2005 Sebastian Bergmann <sb@sebastian-bergmann.de>
+ * @license http://www.php.net/license/3_0.txt The PHP License, Version 3.0
+ * @category Testing
+ * @package PHPUnit
+ */
+class PHPUnit {
+ function &run(&$suite) {
+ $result = new PHPUnit_TestResult();
+ $suite->run($result);
+
+ return $result;
+ }
+}
+?>
384 PHPUnit/Assert.php
@@ -0,0 +1,384 @@
+<?php
+//
+// +------------------------------------------------------------------------+
+// | PEAR :: PHPUnit |
+// +------------------------------------------------------------------------+
+// | Copyright (c) 2002-2005 Sebastian Bergmann <sb@sebastian-bergmann.de>. |
+// +------------------------------------------------------------------------+
+// | This source file is subject to version 3.00 of the PHP License, |
+// | that is available at http://www.php.net/license/3_0.txt. |
+// | If you did not receive a copy of the PHP license and are unable to |
+// | obtain it through the world-wide-web, please send a note to |
+// | license@php.net so we can mail you a copy immediately. |
+// +------------------------------------------------------------------------+
+//
+// $Id$
+//
+
+/**
+ * A set of assert methods.
+ *
+ * @author Sebastian Bergmann <sb@sebastian-bergmann.de>
+ * @copyright Copyright &copy; 2002-2005 Sebastian Bergmann <sb@sebastian-bergmann.de>
+ * @license http://www.php.net/license/3_0.txt The PHP License, Version 3.0
+ * @category Testing
+ * @package PHPUnit
+ */
+class PHPUnit_Assert {
+ /**
+ * @var boolean
+ * @access private
+ */
+ var $_looselyTyped = FALSE;
+
+ /**
+ * Asserts that a haystack contains a needle.
+ *
+ * @param mixed
+ * @param mixed
+ * @param string
+ * @access public
+ * @since 1.1.0
+ */
+ function assertContains($needle, $haystack, $message = '') {
+ if (is_string($needle) && is_string($haystack)) {
+ $this->assertTrue(strpos($haystack, $needle) !== FALSE ? TRUE : FALSE);
+ }
+
+ else if (is_array($haystack) && !is_object($needle)) {
+ $this->assertTrue(in_array($needle, $haystack), $message);
+ }
+
+ else {
+ $this->fail('Unsupported parameter passed to assertContains().');
+ }
+ }
+
+ /**
+ * Asserts that a haystack does not contain a needle.
+ *
+ * @param mixed
+ * @param mixed
+ * @param string
+ * @access public
+ * @since 1.1.0
+ */
+ function assertNotContains($needle, $haystack, $message = '') {
+ if (is_string($needle) && is_string($haystack)) {
+ $this->assertFalse(strpos($haystack, $needle) !== FALSE ? TRUE : FALSE);
+ }
+
+ else if (is_array($haystack) && !is_object($needle)) {
+ $this->assertFalse(in_array($needle, $haystack), $message);
+ }
+
+ else {
+ $this->fail('Unsupported parameter passed to assertNotContains().');
+ }
+ }
+
+ /**
+ * Asserts that two variables are equal.
+ *
+ * @param mixed
+ * @param mixed
+ * @param string
+ * @param mixed
+ * @access public
+ */
+ function assertEquals($expected, $actual, $message = '', $delta = 0) {
+ if ((is_array($actual) && is_array($expected)) ||
+ (is_object($actual) && is_object($expected))) {
+ if (is_array($actual) && is_array($expected)) {
+ ksort($actual);
+ ksort($expected);
+ }
+
+ if ($this->_looselyTyped) {
+ $actual = $this->_convertToString($actual);
+ $expected = $this->_convertToString($expected);
+ }
+
+ $actual = serialize($actual);
+ $expected = serialize($expected);
+
+ $message = sprintf(
+ '%sexpected %s, actual %s',
+
+ !empty($message) ? $message . ' ' : '',
+ $expected,
+ $actual
+ );
+
+ if ($actual !== $expected) {
+ return $this->fail($message);
+ }
+ }
+
+ elseif (is_numeric($actual) && is_numeric($expected)) {
+ $message = sprintf(
+ '%sexpected %s%s, actual %s',
+
+ !empty($message) ? $message . ' ' : '',
+ $expected,
+ ($delta != 0) ? ('+/- ' . $delta) : '',
+ $actual
+ );
+
+ if (!($actual >= ($expected - $delta) && $actual <= ($expected + $delta))) {
+ return $this->fail($message);
+ }
+ }
+
+ else {
+ $message = sprintf(
+ '%sexpected %s, actual %s',
+
+ !empty($message) ? $message . ' ' : '',
+ $expected,
+ $actual
+ );
+
+ if ($actual !== $expected) {
+ return $this->fail($message);
+ }
+ }
+ }
+
+ /**
+ * Asserts that two variables reference the same object.
+ * This requires the Zend Engine 2 to work.
+ *
+ * @param object
+ * @param object
+ * @param string
+ * @access public
+ * @deprecated
+ */
+ function assertSame($expected, $actual, $message = '') {
+ if (!version_compare(phpversion(), '5.0.0', '>=')) {
+ $this->fail('assertSame() only works with PHP >= 5.0.0.');
+ }
+
+ if ((is_object($expected) || is_null($expected)) &&
+ (is_object($actual) || is_null($actual))) {
+ $message = sprintf(
+ '%sexpected two variables to reference the same object',
+
+ !empty($message) ? $message . ' ' : ''
+ );
+
+ if ($expected !== $actual) {
+ return $this->fail($message);
+ }
+ } else {
+ $this->fail('Unsupported parameter passed to assertSame().');
+ }
+ }
+
+ /**
+ * Asserts that two variables do not reference the same object.
+ * This requires the Zend Engine 2 to work.
+ *
+ * @param object
+ * @param object
+ * @param string
+ * @access public
+ * @deprecated
+ */
+ function assertNotSame($expected, $actual, $message = '') {
+ if (!version_compare(phpversion(), '5.0.0', '>=')) {
+ $this->fail('assertNotSame() only works with PHP >= 5.0.0.');
+ }
+
+ if ((is_object($expected) || is_null($expected)) &&
+ (is_object($actual) || is_null($actual))) {
+ $message = sprintf(
+ '%sexpected two variables to reference different objects',
+
+ !empty($message) ? $message . ' ' : ''
+ );
+
+ if ($expected === $actual) {
+ return $this->fail($message);
+ }
+ } else {
+ $this->fail('Unsupported parameter passed to assertNotSame().');
+ }
+ }
+
+ /**
+ * Asserts that a variable is not NULL.
+ *
+ * @param mixed
+ * @param string
+ * @access public
+ */
+ function assertNotNull($actual, $message = '') {
+ $message = sprintf(
+ '%sexpected NOT NULL, actual NULL',
+
+ !empty($message) ? $message . ' ' : ''
+ );
+
+ if (is_null($actual)) {
+ return $this->fail($message);
+ }
+ }
+
+ /**
+ * Asserts that a variable is NULL.
+ *
+ * @param mixed
+ * @param string
+ * @access public
+ */
+ function assertNull($object, $message = '') {
+ $message = sprintf(
+ '%sexpected NULL, actual NOT NULL',
+
+ !empty($message) ? $message . ' ' : ''
+ );
+
+ if (!is_null($actual)) {
+ return $this->fail($message);
+ }
+ }
+
+ /**
+ * Asserts that a condition is true.
+ *
+ * @param boolean
+ * @param string
+ * @access public
+ */
+ function assertTrue($condition, $message = '') {
+ $message = sprintf(
+ '%sexpected TRUE, actual FALSE',
+
+ !empty($message) ? $message . ' ' : ''
+ );
+
+ if (!$condition) {
+ return $this->fail($message);
+ }
+ }
+
+ /**
+ * Asserts that a condition is false.
+ *
+ * @param boolean
+ * @param string
+ * @access public
+ */
+ function assertFalse($condition, $message = '') {
+ $message = sprintf(
+ '%sexpected FALSE, actual TRUE',
+
+ !empty($message) ? $message . ' ' : ''
+ );
+
+ if ($condition) {
+ return $this->fail($message);
+ }
+ }
+
+ /**
+ * Asserts that a string matches a given regular expression.
+ *
+ * @param string
+ * @param string
+ * @param string
+ * @access public
+ */
+ function assertRegExp($pattern, $string, $message = '') {
+ $message = sprintf(
+ '%s"%s" does not match pattern "%s"',
+
+ !empty($message) ? $message . ' ' : '',
+ $string,
+ $pattern
+ );
+
+ if (!preg_match($pattern, $string)) {
+ return $this->fail($message);
+ }
+ }
+
+ /**
+ * Asserts that a string does not match a given regular expression.
+ *
+ * @param string
+ * @param string
+ * @param string
+ * @access public
+ * @since 1.1.0
+ */
+ function assertNotRegExp($pattern, $string, $message = '') {
+ $message = sprintf(
+ '%s"%s" matches pattern "%s"',
+
+ !empty($message) ? $message . ' ' : '',
+ $string,
+ $pattern
+ );
+
+ if (preg_match($pattern, $string)) {
+ return $this->fail($message);
+ }
+ }
+
+ /**
+ * Asserts that a variable is of a given type.
+ *
+ * @param string $expected
+ * @param mixed $actual
+ * @param optional string $message
+ * @access public
+ */
+ function assertType($expected, $actual, $message = '') {
+ return $this->assertEquals(
+ $expected,
+ gettype($actual),
+ $message
+ );
+ }
+
+ /**
+ * Converts a value to a string.
+ *
+ * @param mixed $value
+ * @access private
+ */
+ function _convertToString($value) {
+ foreach ($value as $k => $v) {
+ if (is_array($v)) {
+ $value[$k] = $this->_convertToString($value[$k]);
+ } else {
+ settype($value[$k], 'string');
+ }
+ }
+
+ return $value;
+ }
+
+ /**
+ * @param boolean $looselyTyped
+ * @access public
+ */
+ function setLooselyTyped($looselyTyped) {
+ if (is_bool($looselyTyped)) {
+ $this->_looselyTyped = $looselyTyped;
+ }
+ }
+
+ /**
+ * Fails a test with the given message.
+ *
+ * @param string
+ * @access protected
+ * @abstract
+ */
+ function fail($message = '') { /* abstract */ }
+}
+?>
698 PHPUnit/GUI/Gtk.php
@@ -0,0 +1,698 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+// +----------------------------------------------------------------------+
+// | PHP Version 4 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 2004 Scott Mattocks |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 3.0 of the PHP license, |
+// | that is bundled with this package in the file LICENSE, and is |
+// | available through the world-wide-web at the following url: |
+// | http://www.php.net/license/3_0.txt. |
+// | If you did not receive a copy of the PHP license and are unable to |
+// | obtain it through the world-wide-web, please send a note to |
+// | license@php.net so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Author: Scott Mattocks <scottmattocks@php.net> |
+// +----------------------------------------------------------------------+
+//
+// $Id$
+/**
+ * GTK GUI interface for PHPUnit.
+ *
+ * This class is a PHP port of junit.awtui.testrunner. Documentation
+ * for junit.awtui.testrunner can be found at
+ * http://junit.sourceforge.net
+ *
+ * Due to the limitations of PHP4 and PHP-Gtk, this class can not
+ * duplicate all of the functionality of the JUnit GUI. Some of the
+ * things this class cannot do include:
+ * - Reloading the class for each run
+ * - Stopping the test in progress
+ *
+ * To use simply intantiate the class and call main()
+ * $gtk =& new PHPUnit_GUI_Gtk;
+ * $gtk->main();
+ *
+ * Once the window has finished loading, you can enter the name of
+ * a class that has been loaded (include/require some where in your
+ * code, or you can pass the name of the file containing the class.
+ *
+ * You can also load classes using the SetupDecorator class.
+ * require_once 'PHPUnit/GUI/SetupDecorator.php';
+ * require_once 'PHPUnit/GUI/Gtk.php';
+ * $gui = new PHPUnit_GUI_SetupDecorator(new PHPUnit_GUI_Gtk());
+ * $gui->getSuitesFromDir('/path/to/test','.*\.php$',array('index.php','sql.php'));
+ * $gui->show();
+ *
+ * @todo Allow file drop. (Gtk_FileDrop)
+ *
+ * @author Scott Mattocks
+ * @copyright Copyright &copy; 2004 Scott Mattocks
+ * @license PHP 3.0
+ * @version @VER@
+ * @category PHP
+ * @package PHPUnit
+ * @subpackage GUI
+ */
+class PHPUnit_GUI_Gtk {
+
+ /**
+ * The main gtk window
+ * @var object
+ */
+ var $gui;
+ /**
+ * The text entry that contains the name of the
+ * file that holds the test(s)/suite(s).
+ * @var object
+ */
+ var $suiteField;
+ /**
+ * The label that shows the number of tests that
+ * were run.
+ * @var object
+ */
+ var $numberOfRuns;
+ /**
+ * The label that shows the number of errors that
+ * were encountered.
+ * @var object
+ */
+ var $numberOfErrors;
+ /**
+ * The label that shows the number of failures
+ * that were encountered.
+ * @var object
+ */
+ var $numberOfFailures;
+ /**
+ * The label for reporting user messages.
+ * @var object
+ */
+ var $statusLine;
+ /**
+ * The text area for reporting messages from successful
+ * test runs. (not necessarily successful tests)
+ * @var object
+ */
+ var $reportArea;
+ /**
+ * The text area for reporting errors encountered when
+ * running tests.
+ * @var object
+ */
+ var $dumpArea;
+ /**
+ * The progress bar indicator. Shows the percentage of
+ * passed tests.
+ * @var object
+ */
+ var $progress;
+ /**
+ * A checkbox for the user to indicate whether or not they
+ * would like to see results from all tests or just failures.
+ * @object
+ */
+ var $showPassed;
+
+ /**
+ * Constructor.
+ *
+ * The constructor checks for the gtk extension and loads it
+ * if needed. Then it creates the GUI. The GUI is not shown
+ * nor is the main gtk loop started until main() is called.
+ *
+ * @access public
+ * @param none
+ * @return void
+ */
+ function PHPUnit_GUI_Gtk()
+ {
+ // Check for php-gtk extension.
+ if (!extension_loaded('gtk')) {
+ dl( 'php_gtk.' . PHP_SHLIB_SUFFIX);
+ }
+
+ // Create the interface but don't start the loop
+ $this->_createUI();
+ }
+ /**
+ * Start the main gtk loop.
+ *
+ * main() first sets the default state of the showPassed
+ * check box. Next all widgets that are part of the GUI
+ * are shown. Finally the main gtk loop is started.
+ *
+ * @access public
+ * @param boolean $showPassed
+ * @return void
+ */
+ function main($showPassed = true)
+ {
+ $this->showPassed->set_active($showPassed);
+ $this->gui->show_all();
+
+ gtk::main();
+ }
+ /**
+ * Create the user interface.
+ *
+ * The user interface is pretty simple. It consists of a
+ * menu, text entry, run button, some labels, a progress
+ * indicator, and a couple of areas for notification of
+ * any messages.
+ *
+ * @access private
+ * @param none
+ * @return void
+ */
+ function _createUI()
+ {
+ // Create a window.
+ $window =& new GtkWindow;
+ $window->set_title('PHPUnit Gtk');
+ $window->set_usize(400, -1);
+
+ // Create the main box.
+ $mainBox =& new GtkVBox;
+ $window->add($mainBox);
+
+ // Start with the menu.
+ $mainBox->pack_start($this->_createMenu());
+
+ // Then add the suite field entry.
+ $mainBox->pack_start($this->_createSuiteEntry());
+
+ // Then add the report labels.
+ $mainBox->pack_start($this->_createReportLabels());
+
+ // Next add the progress bar.
+ $mainBox->pack_start($this->_createProgressBar());
+
+ // Then add the report area and the dump area.
+ $mainBox->pack_start($this->_createReportAreas());
+
+ // Finish off with the status line.
+ $mainBox->pack_start($this->_createStatusLine());
+
+ // Connect the destroy signal.
+ $window->connect_object('destroy', array('gtk', 'main_quit'));
+
+ // Assign the member.
+ $this->gui =& $window;
+ }
+ /**
+ * Create the menu.
+ *
+ * The menu is very simple. It an exit menu item, which exits
+ * the application, and an about menu item, which shows some
+ * basic information about the application itself.
+ *
+ * @access private
+ * @param none
+ * @return &object The GtkMenuBar
+ */
+ function &_createMenu()
+ {
+ // Create the menu bar.
+ $menuBar =& new GtkMenuBar;
+
+ // Create the main (only) menu item.
+ $phpHeader =& new GtkMenuItem('PHPUnit');
+
+ // Add the menu item to the menu bar.
+ $menuBar->append($phpHeader);
+
+ // Create the PHPUnit menu.
+ $phpMenu =& new GtkMenu;
+
+ // Add the menu items
+ $about =& new GtkMenuItem('About...');
+ $about->connect('activate', array(&$this, 'about'));
+ $phpMenu->append($about);
+
+ $exit =& new GtkMenuItem('Exit');
+ $exit->connect_object('activate', array('gtk', 'main_quit'));
+ $phpMenu->append($exit);
+
+ // Complete the menu.
+ $phpHeader->set_submenu($phpMenu);
+
+ return $menuBar;
+ }
+ /**
+ * Create the suite entry and related widgets.
+ *
+ * The suite entry has some supporting components such as a
+ * label, the show passed check box and the run button. All
+ * of these items are packed into two nested boxes.
+ *
+ * @access private
+ * @param none
+ * @return &object A box that contains all of the suite entry pieces.
+ */
+ function &_createSuiteEntry()
+ {
+ // Create the outermost box.
+ $outerBox =& new GtkVBox;
+
+ // Create the suite label, box, and field.
+ $suiteLabel =& new GtkLabel('Test class name:');
+ $suiteBox =& new GtkHBox;
+ $this->suiteField =& new GtkEntry;
+ $this->suiteField->set_text($suiteName != NULL ? $suiteName : '');
+
+ // Create the button the user will use to start the test.
+ $runButton =& new GtkButton('Run');
+ $runButton->connect_object('clicked', array(&$this, 'run'));
+
+ // Create the check box that lets the user show only failures.
+ $this->showPassed =& new GtkCheckButton('Show passed tests');
+
+ // Add the components to their respective boxes.
+ $suiteLabel->set_alignment(0, 0);
+ $outerBox->pack_start($suiteLabel);
+ $outerBox->pack_start($suiteBox);
+ $outerBox->pack_start($this->showPassed);
+
+ $suiteBox->pack_start($this->suiteField);
+ $suiteBox->pack_start($runButton);
+
+ return $outerBox;
+ }
+
+ /**
+ * Create the labels that tell the user what has happened.
+ *
+ * There are three labels, one each for total runs, errors and
+ * failures. There is also one label for each of these that
+ * describes what the label is. It could be done with one label
+ * instead of two but that would make updates much harder.
+ *
+ * @access private
+ * @param none
+ * @return &object A box containing the labels.
+ */
+ function &_createReportLabels()
+ {
+ // Create a box to hold everything.
+ $labelBox =& new GtkHBox;
+
+ // Create the non-updated labels.
+ $numberOfRuns =& new GtkLabel('Runs:');
+ $numberOfErrors =& new GtkLabel('Errors:');
+ $numberOfFailures =& new GtkLabel('Failures:');
+
+ // Create the labels that will be updated.
+ // These are asssigned to members to make it easier to
+ // set their values later.
+ $this->numberOfRuns =& new GtkLabel(0);
+ $this->numberOfErrors =& new GtkLabel(0);
+ $this->numberOfFailures =& new GtkLabel(0);
+
+ // Pack everything in.
+ $labelBox->pack_start($numberOfRuns);
+ $labelBox->pack_start($this->numberOfRuns);
+ $labelBox->pack_start($numberOfErrors);
+ $labelBox->pack_start($this->numberOfErrors);
+ $labelBox->pack_start($numberOfFailures);
+ $labelBox->pack_start($this->numberOfFailures);
+
+ return $labelBox;
+ }
+
+ /**
+ * Create the success/failure indicator.
+ *
+ * A GtkProgressBar is used to visually indicate how many
+ * tests were successful compared to how many were not. The
+ * progress bar shows the percentage of success and will
+ * change from green to red if there are any failures.
+ *
+ * @access private
+ * @param none
+ * @return &object The progress bar
+ */
+ function &_createProgressBar()
+ {
+ // Create the progress bar.
+ $this->progress =& new GtkProgressBar(new GtkAdjustment(0, 0, 1, .1, 1, 0));
+
+ // Set the progress bar to print the percentage.
+ $this->progress->set_show_text(true);
+
+ return $this->progress;
+ }
+
+ /**
+ * Create the report text areas.
+ *
+ * The report area consists of one text area for failures, one
+ * text area for errors and one label for identification purposes.
+ * All three widgets are packed into a box.
+ *
+ * @access private
+ * @param none
+ * @return &object The box containing the report areas.
+ */
+ function &_createReportAreas()
+ {
+ // Create the containing box.
+ $reportBox =& new GtkVBox;
+
+ // Create the identification label
+ $reportLabel =& new GtkLabel('Errors and Failures:');
+ $reportLabel->set_alignment(0, 0);
+
+ // Create the scrolled windows for the text areas.
+ $reportScroll =& new GtkScrolledWindow;
+ $dumpScroll =& new GtkScrolledWindow;
+
+ // Make the scroll areas big enough.
+ $reportScroll->set_usize(-1, 150);
+ $dumpScroll->set_usize(-1, 150);
+
+ // Only show the vertical scroll bar when needed.
+ // Never show the horizontal scroll bar.
+ $reportScroll->set_policy(GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
+ $dumpScroll->set_policy(GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
+
+ // Create the text areas.
+ $this->reportArea =& new GtkText;
+ $this->dumpArea =& new GtkText;
+
+ // Don't let words get broken.
+ $this->reportArea->set_word_wrap(true);
+ $this->dumpArea->set_word_wrap(true);
+
+ // Pack everything in.
+ $reportBox->pack_start($reportLabel);
+ $reportScroll->add($this->reportArea);
+ $reportBox->pack_start($reportScroll);
+ $dumpScroll->add($this->dumpArea);
+ $reportBox->pack_start($dumpScroll);
+
+ return $reportBox;
+ }
+
+ /**
+ * Create a status label.
+ *
+ * A status line at the bottom of the application is used
+ * to notify the user of non-test related messages such as
+ * failures loading a test suite.
+ *
+ * @access private
+ * @param none
+ * @return &object The status label.
+ */
+ function &_createStatusLine()
+ {
+ // Create the status label.
+ $this->statusLine =& new GtkLabel('');
+ $this->statusLine->set_alignment(0, 0);
+
+ return $this->statusLine;
+ }
+
+ /**
+ * Show a popup with information about the application.
+ *
+ * The popup should show information about the version,
+ * the author, the license, where to get the latest
+ * version and a short description.
+ *
+ * @access public
+ * @param none
+ * @return void
+ */
+ function about()
+ {
+ // Create the new window.
+ $about =& new GtkWindow;
+ $about->set_title('About PHPUnit GUI Gtk');
+ $about->set_usize(250, -1);
+
+ // Put two vboxes in the hbox.
+ $vBox =& new GtkVBox;
+ $about->add($vBox);
+
+ // Create the labels.
+ $version =& new GtkLabel(" Version: 1.0");
+ $license =& new GtkLabel(" License: PHP License v3.0");
+ $where =& new GtkLabel(" Download from: http://pear.php.net/PHPUnit/");
+ $unitAuth =& new GtkLabel(" PHPUnit Author: Sebastian Bergman");
+ $gtkAuth =& new GtkLabel(" Gtk GUI Author: Scott Mattocks");
+
+ // Align everything to the left
+ $where->set_alignment(0, .5);
+ $version->set_alignment(0, .5);
+ $license->set_alignment(0, .5);
+ $gtkAuth->set_alignment(0, .5);
+ $unitAuth->set_alignment(0, .5);
+
+ // Pack everything into the vBox;
+ $vBox->pack_start($version);
+ $vBox->pack_start($license);
+ $vBox->pack_start($where);
+ $vBox->pack_start($unitAuth);
+ $vBox->pack_start($gtkAuth);
+
+ // Connect the destroy signal.
+ $about->connect('destroy', array('gtk', 'true'));
+
+ // Show the goods.
+ $about->show_all();
+ }
+
+ /**
+ * Load the test suite.
+ *
+ * This method tries to load test suite based on the user
+ * info. If the user passes the name of a tests suite, it
+ * is instantiated and a new object is returned. If the
+ * user passes a file that contains a test suite, the class
+ * is instantiated and a new object is returned. If the user
+ * passes a file that contains a test case, the test case is
+ * passed to a new test suite and the new suite object is
+ * returned.
+ *
+ * @access public
+ * @param string The file that contains a test case/suite or the classname.
+ * @return &object The new test suite.
+ */
+ function &loadTest(&$file)
+ {
+ // Check to see if a class name was given.
+ if (is_a($file, 'PHPUnit_TestSuite')) {
+ return $file;
+ } elseif (class_exists($file)) {
+ require_once 'PHPUnit/TestSuite.php';
+ return new PHPUnit_TestSuite($file);
+ }
+
+ // Check that the file exists.
+ if (!@is_readable($file)) {
+ $this->_showStatus('Cannot find file: ' . $file);
+ return false;
+ }
+
+ $this->_showStatus('Loading test suite...');
+
+ // Instantiate the class.
+ // If the path is /path/to/test/TestClass.php
+ // the class name should be test_TestClass
+ require_once $file;
+ $className = str_replace(DIRECTORY_SEPARATOR, '_', $file);
+ $className = substr($className, 0, strpos($className, '.'));
+
+ require_once 'PHPUnit/TestSuite.php';
+ return new PHPUnit_TestSuite($className);
+ }
+
+ /**
+ * Run the test suite.
+ *
+ * This method runs the test suite and updates the messages
+ * for the user. When finished it changes the status line
+ * to 'Test Complete'
+ *
+ * @access public
+ * @param none
+ * @return void
+ */
+ function runTest()
+ {
+ // Notify the user that the test is running.
+ $this->_showStatus('Running Test...');
+
+ // Run the test.
+ $result = PHPUnit::run($this->suite);
+
+ // Update the labels.
+ $this->_setLabelValue($this->numberOfRuns, $result->runCount());
+ $this->_setLabelValue($this->numberOfErrors, $result->errorCount());
+ $this->_setLabelValue($this->numberOfFailures, $result->failureCount());
+
+ // Update the progress bar.
+ $this->_updateProgress($result->runCount(),
+ $result->errorCount(),
+ $result->failureCount()
+ );
+
+ // Show the errors.
+ $this->_showFailures($result->errors(), $this->dumpArea);
+
+ // Show the messages from the tests.
+ if ($this->showPassed->get_active()) {
+ // Show failures and success.
+ $this->_showAll($result, $this->reportArea);
+ } else {
+ // Show only failures.
+ $this->_showFailures($result->failures(), $this->reportArea);
+ }
+
+ // Update the status message.
+ $this->_showStatus('Test complete');
+ }
+
+ /**
+ * Set the text of a label.
+ *
+ * Change the text of a given label.
+ *
+ * @access private
+ * @param widget &$label The label whose value is to be changed.
+ * @param string $value The new text of the label.
+ * @return void
+ */
+ function _setLabelValue(&$label, $value)
+ {
+ $label->set_text($value);
+ }
+
+ /**
+ * The main work of the application.
+ *
+ * Load the test suite and then execute the tests.
+ *
+ * @access public
+ * @param none
+ * @return void
+ */
+ function run()
+ {
+ // Load the test suite.
+ $this->suite =& $this->loadTest($this->suiteField->get_text());
+
+ // Check to make sure the suite was loaded properly.
+ if (!is_object($this->suite)) {
+ // Raise an error.
+ $this->_showStatus('Could not load test suite.');
+ return false;
+ }
+
+ // Run the tests.
+ $this->runTest();
+ }
+
+ /**
+ * Update the status message.
+ *
+ * @access private
+ * @param string $status The new message.
+ * @return void
+ */
+ function _showStatus($status)
+ {
+ $this->statusLine->set_text($status);
+ }
+
+ /**
+ * Alias for main()
+ *
+ * @see main
+ */
+ function show($showPassed = true)
+ {
+ $this->main($showPassed);
+ }
+
+ /**
+ * Add a suite to the tests.
+ *
+ * This method is require by SetupDecorator. It adds a
+ * suite to the the current set of suites.
+ *
+ * @access public
+ * @param object $testSuite The suite to add.
+ * @return void
+ */
+ function addSuites($testSuite)
+ {
+ if (!is_array($testSuite)) {
+ settype($testSuite, 'array');
+ }
+
+ foreach ($testSuite as $suite) {
+
+ if (is_a($this->suite, 'PHPUnit_TestSuite')) {
+ $this->suite->addTestSuite($suite->getName());
+ } else {
+ $this->suite =& $this->loadTest($suite);
+ }
+
+ // Set the suite field.
+ $text = $this->suiteField->get_text();
+ if (empty($text)) {
+ $this->suiteField->set_text($this->suite->getName());
+ }
+ }
+ }
+
+ /**
+ * Show all test messages.
+ *
+ * @access private
+ * @param object The TestResult from the test suite.
+ * @return void
+ */
+ function _showAll(&$result)
+ {
+ // Clear the area first.
+ $this->reportArea->delete_text(0, -1);
+ $this->reportArea->insert_text($result->toString(), 0);
+ }
+
+ /**
+ * Show failure/error messages in the given text area.
+ *
+ * @access private
+ * @param object &$results The results of the test.
+ * @param widget &$area The area to show the results in.
+ */
+ function _showFailures(&$results, &$area)
+ {
+ $area->delete_text(0, -1);
+ foreach (array_reverse($results, true) as $result) {
+ $area->insert_text($result->toString(), 0);
+ }
+ }
+
+ /**
+ * Update the progress indicator.
+ *
+ * @access private
+ * @param integer $runs
+ * @param integer $errors
+ * @param integer $failures
+ * @return void
+ */
+ function _updateProgress($runs, $errors, $failures)
+ {
+ $percentage = 1 - (($errors + $failures) / $runs);
+ $this->progress->set_percentage($percentage);
+ }
+}
+?>
210 PHPUnit/GUI/HTML.php
@@ -0,0 +1,210 @@
+<?php
+//
+// +------------------------------------------------------------------------+
+// | PEAR :: PHPUnit |
+// +------------------------------------------------------------------------+
+// | Copyright (c) 2002-2005 Sebastian Bergmann <sb@sebastian-bergmann.de>. |
+// +------------------------------------------------------------------------+
+// | This source file is subject to version 3.00 of the PHP License, |
+// | that is available at http://www.php.net/license/3_0.txt. |
+// | If you did not receive a copy of the PHP license and are unable to |
+// | obtain it through the world-wide-web, please send a note to |
+// | license@php.net so we can mail you a copy immediately. |
+// +------------------------------------------------------------------------+
+//
+// $Id$
+//
+
+/**
+ * This class provides a HTML GUI.
+ *
+ * @author Wolfram Kriesing <wolfram@kriesing.de>
+ * @license http://www.php.net/license/3_0.txt The PHP License, Version 3.0
+ * @category Testing
+ * @package PHPUnit
+ * @subpackage GUI
+ */
+class PHPUnit_GUI_HTML
+{
+ var $_suites = array();
+
+ /**
+ * the current implementation of PHPUnit is designed
+ * this way that adding a suite to another suite only
+ * grabs all the tests and adds them to the suite, so you
+ * have no chance to find out which test goes with which suite
+ * therefore you can simply pass an array of suites to this constructor here
+ *
+ * @param array The suites to be tested. If not given, then you might
+ * be using the SetupDecorator, which detects them automatically
+ * when calling getSuitesFromDir()
+ */
+ function PHPUnit_GUI_HTML($suites = array())
+ {
+ if (!is_array($suites)) {
+ $this->_suites = array($suites);
+ } else {
+ $this->_suites = $suites;
+ }
+ }
+
+ /**
+ * Add suites to the GUI
+ *
+ * @param object this should be an instance of PHPUnit_TestSuite
+ */
+ function addSuites($suites)
+ {
+ $this->_suites = array_merge($this->_suites,$suites);
+ }
+
+ /**
+ * this prints the HTML code straight out
+ *
+ */
+ function show()
+ {
+ $request = $_REQUEST;
+ $showPassed = FALSE;
+ $submitted = @$request['submitted'];
+
+ if ($submitted) {
+ $showPassed = @$request['showOK'] ? TRUE : FALSE;
+ }
+
+ $suiteResults = array();
+
+ foreach ($this->_suites as $aSuite) {
+ $aSuiteResult = array();
+
+ // remove the first directory's name from the test-suite name, since it
+ // mostly is something like 'tests' or alike
+ $removablePrefix = explode('_',$aSuite->getName());
+ $aSuiteResult['name'] = str_replace($removablePrefix[0].'_', '', $aSuite->getName());
+
+ if ($submitted && isset($request[$aSuiteResult['name']])) {
+ $result = PHPUnit::run($aSuite);
+
+ $aSuiteResult['counts']['run'] = $result->runCount();
+ $aSuiteResult['counts']['error'] = $result->errorCount();
+ $aSuiteResult['counts']['failure'] = $result->failureCount();
+
+ $aSuiteResult['results'] = $this->_prepareResult($result,$showPassed);
+
+ $per = 100/$result->runCount();
+ $failed = ($per*$result->errorCount())+($per*$result->failureCount());
+ $aSuiteResult['percent'] = round(100-$failed,2);
+ } else {
+ $aSuiteResult['addInfo'] = 'NOT EXECUTED';
+ }
+
+ $suiteResults[] = $aSuiteResult;
+ }
+
+ $final['name'] = 'OVERALL RESULT';
+ $final['counts'] = array();
+ $final['percent'] = 0;
+ $numExecutedTests = 0;
+
+ foreach ($suiteResults as $aSuiteResult) {
+ if (sizeof(@$aSuiteResult['counts'])) {
+ foreach ($aSuiteResult['counts'] as $key=>$aCount) {
+ if (!isset($final['counts'][$key])) {
+ $final['counts'][$key] = 0;
+ }
+
+ $final['counts'][$key] += $aCount;
+ }
+ }
+ }
+
+ if (isset($final['counts']['run'])) {
+ $per = 100/$final['counts']['run'];
+ $failed = ($per*$final['counts']['error'])+($per*$final['counts']['failure']);
+ $final['percent'] = round(100-$failed,2);
+ } else {
+ $final['percent'] = 0;
+ }
+
+ array_unshift($suiteResults,$final);
+
+ include 'PHPUnit/GUI/HTML.tpl';
+ }
+
+ function _prepareResult($result,$showPassed)
+ {
+ $ret = array();
+ $failures = $result->failures();
+
+ foreach($failures as $aFailure) {
+ $ret['failures'][] = $this->_prepareFailure($aFailure);
+ }
+
+ $errors = $result->errors();
+
+ foreach($errors as $aError) {
+ $ret['errors'][] = $this->_prepareErrors($aError);
+ }
+
+ if ($showPassed) {
+ $passed = $result->passedTests();
+
+ foreach($passed as $aPassed) {
+ $ret['passed'][] = $this->_preparePassedTests($aPassed);
+ }
+ }
+
+ return $ret;
+ }
+
+ function _prepareFailure($failure)
+ {
+ $test = $failure->failedTest();
+ $ret['testName'] = $test->getName();
+ $exception = $failure->thrownException();
+
+ // a serialized string starts with a 'character:decimal:{'
+ // if so we try to unserialize it
+ // this piece of the regular expression is for detecting a serialized
+ // type like 'a:3:' for an array with three element or an object i.e. 'O:12:"class":3'
+ $serialized = '(\w:\d+:(?:"[^"]+":\d+:)?\{.*\})';
+
+ // Spaces might make a diff, so we shall show them properly (since a
+ // user agent ignores them).
+ if (preg_match('/^(.*)expected ' . $serialized . ', actual ' . $serialized . '$/sU', $exception, $matches)) {
+ ob_start();
+ print_r(unserialize($matches[2]));
+ $ret['expected'] = htmlspecialchars($matches[1]) . "<pre>" . htmlspecialchars(rtrim(ob_get_contents())) . "</pre>";
+ // Improved compatibility, ob_clean() would be PHP >= 4.2.0 only.
+ ob_end_clean();
+
+ ob_start();
+ print_r(unserialize($matches[3]));
+ $ret['actual'] = htmlspecialchars($matches[1]) . "<pre>" . htmlspecialchars(rtrim(ob_get_contents())) . "</pre>";
+ ob_end_clean();
+ }
+
+ else if (preg_match('/^(.*)expected (.*), actual (.*)$/sU', $exception, $matches)) {
+ $ret['expected'] = nl2br(str_replace(" ", "&nbsp;", htmlspecialchars($matches[1] . $matches[2])));
+ $ret['actual'] = nl2br(str_replace(" ", "&nbsp;", htmlspecialchars($matches[1] . $matches[3])));
+ } else {
+ $ret['message'] = nl2br(str_replace(" ", "&nbsp;", htmlspecialchars($exception)));
+ }
+
+ return $ret;
+ }
+
+ function _preparePassedTests($passed)
+ {
+ $ret['testName'] = $passed->getName();
+ return $ret;
+ }
+
+ function _prepareError($error)
+ {
+ $ret['testName'] = $error->getName();
+ $ret['message'] = $error->toString();
+ return $ret;
+ }
+}
+?>
156 PHPUnit/GUI/HTML.tpl
@@ -0,0 +1,156 @@
+<html>
+ <head>
+ <STYLE type="text/css">
+
+ body, td {
+ background-color: lightgrey;
+ }
+
+ table.outline, outlineFailure {
+ background-color: black;
+ border-width: 1px;
+ }
+
+ td {
+ padding: 2px;
+ }
+
+ th {
+ text-align: left;
+ color: white;
+ background-color: black;
+ }
+
+ .success {
+ background-color: lightgreen;
+ }
+
+ .failure {
+ background-color: orange;
+ }
+ .info {
+ padding: 2px;
+ color: orange;
+ }
+
+ </STYLE>
+ </head>
+ <body>
+ <form action="<?php echo $_SERVER['PHP_SELF'] ?>" method="post" name="optionsForm">
+ <table align="center" class="outline" width="70%">
+ <tr>
+ <th colspan="10">
+ Options
+ </th>
+ </tr>
+ <tr>
+ <td colspan="10">
+ <input type="checkbox" onClick="unCheckAll()" name="allChecked">
+ (un)check all
+ &nbsp; &nbsp;
+ show OK <input type="checkbox" name="showOK" <?php echo @$request['showOK']?'checked':''?>>
+ &nbsp; &nbsp;
+ <input type="submit" name="submitted" value="run tests">
+ </td>
+ </tr>
+
+ <?php foreach($suiteResults as $aResult): ?>
+ <tr>
+ <th colspan="10">
+ <input type="checkbox" name="<?php echo $aResult['name'] ?>" <?php echo @$request[$aResult['name']]?'checked':'' ?>>
+ <?php echo $aResult['name'] ?>
+ &nbsp;
+ <?php if (isset($aResult['addInfo'])): ?>
+ <font class="info"><?php echo @$aResult['addInfo'] ?></font>
+ <?php endif ?>
+ </th>
+ </tr>
+
+ <?php if(@$aResult['percent']): ?>
+ <tr>
+ <td colspan="10" nowrap="nowrap">
+ <table style="width:100%; padding:2px;" cellspacing="0" cellspan="0" cellpadding="0">
+ <tr>
+ <td width="<?php echo $aResult['percent'] ?>%" class="success" align="center" style="padding:0;">
+ <?php echo $aResult['percent']?$aResult['percent'].'%':'' ?>
+ </td>
+ <td width="<?php echo 100-$aResult['percent'] ?>%" class="failure" align="center" style="padding:0;">
+ <?php echo (100-$aResult['percent'])?(100-$aResult['percent'].'%'):'' ?>
+ </td>
+ </tr>
+ </table>
+ </td>
+ </tr>
+ <?php endif ?>
+
+ <?php if(@$aResult['counts']): ?>
+ <tr>
+ <td colspan="10">
+ <?php foreach($aResult['counts'] as $aCount=>$value): ?>
+ <?php echo $aCount ?>s = <?php echo $value ?> &nbsp; &nbsp; &nbsp; &nbsp;
+ <?php endforeach ?>
+ </td>
+ </tr>
+ <?php endif ?>
+
+ <?php if(isset($aResult['results']['failures']) && sizeof($aResult['results']['failures']))
+ foreach($aResult['results']['failures'] as $aFailure): ?>
+ <tr>
+ <td class="failure"><?php echo $aFailure['testName'] ?></td>
+ <td class="failure">
+ <?php if(isset($aFailure['message']) && $aFailure['message']): ?>
+ <?php echo $aFailure['message'] ?>
+ <?php else: ?>
+ <table class="outlineFailure">
+ <tr>
+ <td>expected</td>
+ <td><?php echo $aFailure['expected'] ?></td>
+ </tr>
+ <tr>
+ <td>actual</td>
+ <td><?php echo $aFailure['actual'] ?></td>
+ </tr>
+ </table>
+ <?php endif ?>
+ </td>
+ </tr>
+ <?php endforeach ?>
+
+ <?php if(isset($aResult['results']['errors']) && sizeof($aResult['results']['errors']))
+ foreach($aResult['results']['errors'] as $aError): ?>
+ <tr>
+ <td class="failure"><?php echo $aError['testName'] ?></td>
+ <td class="failure">
+ <?php echo $aError['message'] ?>
+ </td>
+ </tr>
+ <?php endforeach ?>
+
+ <?php if(isset($aResult['results']['passed']) && sizeof($aResult['results']['passed']))
+ foreach($aResult['results']['passed'] as $aPassed): ?>
+ <tr>
+ <td class="success"><?php echo $aPassed['testName'] ?></td>
+ <td class="success"><b>OK</b></td>
+ </tr>
+ <?php endforeach ?>
+
+ <?php endforeach ?>
+ </table>
+ </form>
+
+ <script>
+ var allSuiteNames = new Array();
+ <?php foreach($suiteResults as $aResult): ?>
+ allSuiteNames[allSuiteNames.length] = "<?php echo $aResult['name'] ?>";
+ <?php endforeach ?>
+ function unCheckAll()
+ {
+ _checked = document.optionsForm.allChecked.checked;
+ for (i=0;i<allSuiteNames.length;i++) {
+ document.optionsForm[allSuiteNames[i]].checked = _checked;
+ }
+ }
+ </script>
+
+ </body>
+</html>
167 PHPUnit/GUI/SetupDecorator.php
@@ -0,0 +1,167 @@
+<?php
+//
+// +------------------------------------------------------------------------+
+// | PEAR :: PHPUnit |
+// +------------------------------------------------------------------------+
+// | Copyright (c) 2002-2005 Sebastian Bergmann <sb@sebastian-bergmann.de>. |
+// +------------------------------------------------------------------------+
+// | This source file is subject to version 3.00 of the PHP License, |
+// | that is available at http://www.php.net/license/3_0.txt. |
+// | If you did not receive a copy of the PHP license and are unable to |
+// | obtain it through the world-wide-web, please send a note to |
+// | license@php.net so we can mail you a copy immediately. |
+// +------------------------------------------------------------------------+
+//
+// $Id$
+//
+
+/**
+ * This decorator actually just adds the functionality to read the
+ * test-suite classes from a given directory and instanciate them
+ * automatically, use it as given in the example below.
+ *
+ * <code>
+ * <?php
+ * $gui = new PHPUnit_GUI_SetupDecorator(new PHPUnit_GUI_HTML());
+ * $gui->getSuitesFromDir('/path/to/dir/tests','.*\.php$',array('index.php','sql.php'));
+ * $gui->show();
+ * ?>
+ * </code>
+ *
+ * The example calls this class and tells it to:
+ *
+ * - find all file under the directory /path/to/dir/tests
+ * - for files, which end with '.php' (this is a piece of a regexp, that's why the . is escaped)
+ * - and to exclude the files 'index.php' and 'sql.php'
+ * - and include all the files that are left in the tests.
+ *
+ * Given that the path (the first parameter) ends with 'tests' it will be assumed
+ * that the classes are named tests_* where * is the directory plus the filename,
+ * according to PEAR standards.
+ *
+ * So that:
+ *
+ * - 'testMe.php' in the dir 'tests' bill be assumed to contain a class tests_testMe
+ * - '/moretests/aTest.php' should contain a class 'tests_moretests_aTest'
+ *
+ * @author Wolfram Kriesing <wolfram@kriesing.de>
+ * @license http://www.php.net/license/3_0.txt The PHP License, Version 3.0
+ * @category Testing
+ * @package PHPUnit
+ * @subpackage GUI
+ */
+class PHPUnit_GUI_SetupDecorator
+{
+ /**
+ *
+ *
+ */
+ function PHPUnit_GUI_SetupDecorator(&$gui)
+ {
+ $this->_gui = $gui;
+ }
+
+ /**
+ * just forwarding the action to the decorated class.
+ *
+ */
+ function show($showPassed=TRUE)
+ {
+ $this->_gui->show($showPassed);
+ }
+
+ /**
+ * Setup test suites that can be found in the given directory
+ * Using the second parameter you can also choose a subsets of the files found
+ * in the given directory. I.e. only all the files that contain '_UnitTest_',
+ * in order to do this simply call it like this:
+ * <code>getSuitesFromDir($dir,'.*_UnitTest_.*')</code>.
+ * There you can already see that the pattern is built for the use within a regular expression.
+ *
+ * @param string the directory where to search for test-suite files
+ * @param string the pattern (a regexp) by which to find the files
+ * @param array an array of file names that shall be excluded
+ */
+ function getSuitesFromDir($dir, $filenamePattern = '', $exclude = array())
+ {
+ if ($dir{strlen($dir)-1} == DIRECTORY_SEPARATOR) {
+ $dir = substr($dir, 0, -1);
+ }
+
+ $files = $this->_getFiles($dir, $filenamePattern, $exclude, realpath($dir . '/..'));
+ asort($files);
+
+ foreach ($files as $className => $aFile) {
+ include_once($aFile);
+
+ if (class_exists($className)) {
+ $suites[] =& new PHPUnit_TestSuite($className);
+ } else {
+ trigger_error("$className could not be found in $dir$aFile!");
+ }
+ }
+
+ $this->_gui->addSuites($suites);
+ }
+
+ /**
+ * This method searches recursively through the directories
+ * to find all the files that shall be added to the be visible.
+ *
+ * @param string the path where find the files
+ * @param srting the string pattern by which to find the files
+ * @param string the file names to be excluded
+ * @param string the root directory, which serves as the prefix to the fully qualified filename
+ * @access private
+ */
+ function _getFiles($dir, $filenamePattern, $exclude, $rootDir)
+ {
+ $files = array();
+
+ if ($dp = opendir($dir)) {
+ while (FALSE !== ($file = readdir($dp))) {
+ $filename = $dir . DIRECTORY_SEPARATOR . $file;
+ $match = TRUE;
+
+ if ($filenamePattern && !preg_match("~$filenamePattern~", $file)) {
+ $match = FALSE;
+ }
+
+ if (sizeof($exclude)) {
+ foreach ($exclude as $aExclude) {
+ if (strpos($file, $aExclude) !== FALSE) {
+ $match = FALSE;
+ break;
+ }
+ }
+ }
+
+ if (is_file($filename) && $match) {
+ $tmp = str_replace($rootDir, '', $filename);
+
+ if (strpos($tmp, DIRECTORY_SEPARATOR) === 0) {
+ $tmp = substr($tmp, 1);
+ }
+
+ if (strpos($tmp, '/') === 0) {
+ $tmp = substr($tmp, 1);
+ }
+
+ $className = str_replace(DIRECTORY_SEPARATOR, '_', $tmp);
+ $className = basename($className, '.php');
+
+ $files[$className] = $filename;
+ }
+
+ if ($file != '.' && $file != '..' && is_dir($filename)) {
+ $files = array_merge($files, $this->_getFiles($filename, $filenamePattern, $exclude, $rootDir));
+ }
+ }
+
+ closedir($dp);
+ }
+
+ return $files;
+ }
+}
+?>
112 PHPUnit/RepeatedTest.php
@@ -0,0 +1,112 @@
+<?php
+//
+// +------------------------------------------------------------------------+
+// | PEAR :: PHPUnit |
+// +------------------------------------------------------------------------+
+// | Copyright (c) 2002-2005 Sebastian Bergmann <sb@sebastian-bergmann.de>. |
+// +------------------------------------------------------------------------+
+// | This source file is subject to version 3.00 of the PHP License, |
+// | that is available at http://www.php.net/license/3_0.txt. |
+// | If you did not receive a copy of the PHP license and are unable to |
+// | obtain it through the world-wide-web, please send a note to |
+// | license@php.net so we can mail you a copy immediately. |
+// +------------------------------------------------------------------------+
+//
+// $Id$
+//
+
+require_once 'PHPUnit/TestDecorator.php';
+
+/**
+ * A Decorator that runs a test repeatedly.
+ *
+ * Here is an example:
+ *
+ * <code>
+ * <?php
+ * require_once 'PHPUnit.php';
+ * require_once 'PHPUnit/RepeatedTest.php';
+ *
+ * class MathTest extends PHPUnit_TestCase {
+ * var $fValue1;
+ * var $fValue2;
+ *
+ * function MathTest($name) {
+ * $this->PHPUnit_TestCase($name);
+ * }
+ *
+ * function setUp() {
+ * $this->fValue1 = 2;
+ * $this->fValue2 = 3;
+ * }
+ *
+ * function testAdd() {
+ * $this->assertTrue($this->fValue1 + $this->fValue2 == 5);
+ * }
+ * }
+ *
+ * $suite = new PHPUnit_TestSuite;
+ *
+ * $suite->addTest(
+ * new PHPUnit_RepeatedTest(
+ * new MathTest('testAdd'),
+ * 10
+ * )
+ * );
+ *
+ * $result = PHPUnit::run($suite);
+ * print $result->toString();
+ * ?>
+ * </code>
+ *
+ * @author Sebastian Bergmann <sb@sebastian-bergmann.de>
+ * @copyright Copyright &copy; 2002-2005 Sebastian Bergmann <sb@sebastian-bergmann.de>
+ * @license http://www.php.net/license/3_0.txt The PHP License, Version 3.0
+ * @category Testing
+ * @package PHPUnit
+ */
+class PHPUnit_RepeatedTest extends PHPUnit_TestDecorator {
+ /**
+ * @var integer
+ * @access private
+ */
+ var $_timesRepeat = 1;
+
+ /**
+ * Constructor.
+ *
+ * @param object
+ * @param integer
+ * @access public
+ */
+ function PHPUnit_RepeatedTest(&$test, $timesRepeat = 1) {
+ $this->PHPUnit_TestDecorator($test);
+ $this->_timesRepeat = $timesRepeat;
+ }
+
+ /**
+ * Counts the number of test cases that
+ * will be run by this test.
+ *
+ * @return integer
+ * @access public
+ */
+ function countTestCases() {
+ return $this->_timesRepeat * $this->_test->countTestCases();
+ }
+
+ /**
+ * Runs the decorated test and collects the
+ * result in a TestResult.
+ *
+ * @param object
+ * @access public
+ * @abstract
+ */
+ function run(&$result) {
+ for ($i = 0; $i < $this->_timesRepeat; $i++) {
+ $this->_test->run($result);
+ }
+ }
+}
+?>
406 PHPUnit/Skeleton.php
@@ -0,0 +1,406 @@
+<?php
+//
+// +------------------------------------------------------------------------+
+// | PEAR :: PHPUnit |
+// +------------------------------------------------------------------------+
+// | Copyright (c) 2002-2004 Sebastian Bergmann <sb@sebastian-bergmann.de>. |
+// +------------------------------------------------------------------------+
+// | This source file is subject to version 3.00 of the PHP License, |
+// | that is available at http://www.php.net/license/3_0.txt. |
+// | If you did not receive a copy of the PHP license and are unable to |
+// | obtain it through the world-wide-web, please send a note to |
+// | license@php.net so we can mail you a copy immediately. |
+// +------------------------------------------------------------------------+
+//
+// $Id$
+//
+
+/**
+ * Class for creating a PHPUnit_TestCase skeleton file.
+ *
+ * This class will take a classname as a parameter on construction and will
+ * create a PHP file that contains the skeleton of a PHPUnit_TestCase
+ * subclass. The test case will contain a test foreach method of the class.
+ * Methods of the parent class will, by default, be excluded from the test
+ * class. Passing and optional construction parameter will include them.
+ *
+ * Example
+ *
+ * <?php
+ * require_once 'PHPUnit/Skeleton.php';
+ * $ps = new PHPUnit_Skeleton('PHPUnit_Skeleton', 'PHPUnit/Skeleton.php');
+ *
+ * // Generate the test class.
+ * // Default settings will not include any parent class methods, but
+ * // will include private methods.
+ * $ps->createTestClass();
+ *
+ * // Write the new test class to file.
+ * // By default, code to run the test will be included.
+ * $ps->writeTestClass();
+ * ?>
+ *
+ * Now open the skeleton class and fill in the details.
+ * If you run the test as is, all tests will fail and
+ * you will see plenty of undefined constant errors.
+ *
+ * @author Scott Mattocks <scott@crisscott.com>
+ * @copyright Copyright &copy; 2002-2005 Sebastian Bergmann <sb@sebastian-bergmann.de>
+ * @license http://www.php.net/license/3_0.txt The PHP License, Version 3.0
+ * @category Testing
+ * @package PHPUnit
+ */
+class PHPUnit_Skeleton {
+ /**
+ * Path to the class file to create a skeleton for.
+ * @var string
+ */
+ var $classPath;
+
+ /**
+ * The name of the class
+ * @var string
+ */
+ var $className;
+
+ /**
+ * Path to the configuration file needed by class to test.
+ * @var string
+ */
+ var $configFile;
+
+ /**
+ * Whether or not to include the methods of the parent class when testing.
+ * @var boolean
+ */
+ var $includeParents;
+
+ /**
+ * Whether or not to test private methods.
+ * @var boolean
+ */
+ var $includePrivate;
+
+ /**
+ * The test class that will be created.
+ * @var string
+ */
+ var $testClass;
+
+ /**
+ * Constructor. Sets the class members and check that the class
+ * to test is accessible.
+ *
+ * @access public
+ * @param string $className
+ * @param string $classPath
+ * @param boolean $includeParents Wheter to include the parent's methods in the test.
+ * @return void
+ */
+ function PHPUnit_Skeleton($className, $classPath, $includeParents = FALSE, $includePrivate = TRUE) {
+ // Set up the members.
+ if (@is_readable($classPath)) {
+ $this->className = $className;
+ $this->classPath = $classPath;
+ } else {
+ $this->_handleErrors($classPath . ' is not readable. Cannot create test class.');
+ }
+
+ // Do we want to include parent methods?
+ $this->includeParents = $includeParents;
+
+ // Do we want to allow private methods?
+ $this->includePrivate = $includePrivate;
+ }
+
+ /**
+ * The class to test may require a special config file before it can be
+ * instantiated. This method lets you set that file.
+ *
+ * @access public
+ * @param string $configPath
+ * @return void
+ */
+ function setConfigFile($configFile) {
+ // Check that the file is readable
+ if (@is_readable($configFile)) {
+ $this->configFile = $configFile;
+ } else {
+ $this->_handleErrors($configFile . ' is not readable. Cannot create test class.');
+ }
+ }
+
+ /**
+ * Create the code that will be the skeleton of the test case.
+ *
+ * The test case must have a clss definition, one var, a constructor
+ * setUp, tearDown, and methods. Optionally and by default the code
+ * to run the test is added when the class is written to file.
+ *
+ * @access public
+ * @param none
+ * @return void
+ */
+ function createTestClass() {
+ // Instantiate the object.
+ if (isset($this->configFile)) {
+ require_once $this->configFile;
+ }
+
+ require_once $this->classPath;
+
+ // Get the methods.
+ $classMethods = get_class_methods($this->className);
+
+ // Remove the parent methods if needed.
+ if (!$this->includeParents) {
+ $parentMethods = get_class_methods(get_parent_class($this->className));
+
+ if (count($parentMethods)) {
+ $classMethods = array_diff($classMethods, $parentMethods);
+ }
+ }
+
+ // Create the class definition, constructor, setUp and tearDown.
+ $this->_createDefinition();
+ $this->_createConstructor();
+ $this->_createSetUpTearDown();
+
+ if (count($classMethods)) {
+ // Foreach method create a test case.
+ foreach ($classMethods as $method) {
+ // Unless it is the constructor.
+ if (strcasecmp($this->className, $method) !== 0) {
+ // Check for private methods.
+ if (!$this->includePrivate && strpos($method, '_') === 0) {
+ continue;
+ } else {
+ $this->_createMethod($method);
+ }
+ }
+ }
+ }
+
+ // Finis off the class.
+ $this->_finishClass();
+ }
+
+ /**
+ * Create the class definition.
+ *
+ * The definition consist of a header comment, require statment
+ * for getting the PHPUnit file, the actual class definition,
+ * and the definition of the class member variable.
+ *
+ * All of the code needed for the new class is stored in the
+ * testClass member.
+ *
+ * @access private
+ * @param none
+ * @return void
+ */
+ function _createDefinition() {
+ // Create header comment.
+ $this->testClass =
+ "/**\n" .
+ " * PHPUnit test case for " . $this->className . "\n" .
+ " * \n" .
+ " * The method skeletons below need to be filled in with \n" .
+ " * real data so that the tests will run correctly. Replace \n" .
+ " * all EXPECTED_VAL and PARAM strings with real data. \n" .
+ " * \n" .
+ " * Created with PHPUnit_Skeleton on " . date('Y-m-d') . "\n" .
+ " */\n";
+
+ // Add the require statements.
+ $this->testClass .= "require_once 'PHPUnit.php';\n";
+
+ // Add the class definition and variable definition.
+ $this->testClass .=
+ "class " . $this->className . "Test extends PHPUnit_TestCase {\n\n" .
+ " var \$" . $this->className . ";\n\n";
+ }
+
+ /**
+ * Create the class constructor. (PHP4 style)
+ *
+ * The constructor simply calls the PHPUnit_TestCase method.
+ * This code is taken from the PHPUnit documentation.
+ *
+ * All of the code needed for the new class is stored in the
+ * testClass member.
+ *
+ * @access private
+ * @param none
+ * @return void
+ */
+ function _createConstructor() {
+ // Create the test class constructor.
+ $this->testClass.=
+ " function " . $this->className . "Test(\$name)\n" .
+ " {\n" .
+ " \$this->PHPUnit_TestCase(\$name);\n" .
+ " }\n\n";
+ }
+
+ /**
+ * Create setUp and tearDown methods.
+ *
+ * The setUp method creates the instance of the object to test.
+ * The tearDown method releases the instance.
+ * This code is taken from the PHPUnit documentation.
+ *
+ * All of the code needed for the new class is stored in the
+ * testClass member.
+ *
+ * @access private
+ * @param none
+ * @return void
+ */
+ function _createSetUpTearDown() {
+ // Create the setUp method.
+ $this->testClass .=
+ " function setUp()\n" .
+ " {\n";
+
+ if (isset($this->configFile)) {
+ $this->testClass .=
+ " require_once '" . $this->configFile . "';\n";
+ }
+
+ $this->testClass .=
+ " require_once '" . $this->classPath . "';\n" .
+ " \$this->" . $this->className . " =& new " . $this->className . "(PARAM);\n" .
+ " }\n\n";
+
+ // Create the tearDown method.
+ $this->testClass .=
+ " function tearDown()\n" .
+ " {\n" .
+ " unset(\$this->" . $this->className . ");\n" .
+ " }\n\n";
+ }
+
+ /**
+ * Create a basic skeleton for test methods.
+ *
+ * This code is taken from the PHPUnit documentation.
+ *
+ * All of the code needed for the new class is stored in the
+ * testClass member.
+ *
+ * @access private
+ * @param none
+ * @return void
+ */
+ function _createMethod($methodName) {
+ // Create a test method.
+ $this->testClass .=
+ " function test" . $methodName . "()\n" .
+ " {\n" .
+ " \$result = \$this->" . $this->className . "->" . $methodName . "(PARAM);\n" .
+ " \$expected = EXPECTED_VAL;\n" .
+ " \$this->assertEquals(\$expected, \$result);\n" .
+ " }\n\n";
+ }
+
+ /**
+ * Add the closing brace needed for a proper class definition.
+ *
+ * All of the code needed for the new class is stored in the
+ * testClass member.
+ *
+ * @access private
+ * @param none
+ * @return void
+ */
+ function _finishClass() {
+ // Close off the class.
+ $this->testClass.= "}\n";
+ }
+
+ /**
+ * Create the code that will actually run the test.
+ *
+ * This code is added by default so that the test can be run
+ * just by running the file. To have it not added pass false
+ * as the second parameter to the writeTestClass method.
+ * This code is taken from the PHPUnit documentation.
+ *
+ * All of the code needed for the new class is stored in the
+ * testClass member.
+ *
+ * @access private
+ * @param none
+ * @return void
+ */
+ function _createTest() {
+ // Create a call to the test.
+ $test =
+ "// Running the test.\n" .
+ "\$suite = new PHPUnit_TestSuite('" . $this->className . "Test');\n" .
+ "\$result = PHPUnit::run(\$suite);\n" .
+ "echo \$result->toString();\n";
+
+ return $test;
+ }
+
+ /**
+ * Write the test class to file.
+ *
+ * This will write the test class created using the createTestClass
+ * method to a file called <className>Test.php. By default the file
+ * is written to the current directory and will have code to run
+ * the test appended to the bottom of the file.
+ *
+ * @access public
+ * @param string $destination The directory to write the file to.
+ * @param boolean $addTest Wheter to add the test running code.
+ * @return void
+ */
+ function writeTestClass($destination = './', $addTest = TRUE) {
+ // Check for something to write to file.
+ if (!isset($this->testClass)) {
+ $this->_handleErrors('Noting to write.', PHPUS_WARNING);
+ return;
+ }
+
+ // Open the destination file.
+ $fp = fopen($destination . $this->className . 'Test.php', 'w');
+ fwrite($fp, "<?php\n");
+
+ // Write the test class.
+ fwrite($fp, $this->testClass);
+
+ // Add the call to test the class in the file if we were asked to.
+ if ($addTest) {
+ fwrite($fp, $this->_createTest());
+ }
+
+ // Close the file.
+ fwrite($fp, "?>\n");
+ fclose($fp);
+ }
+
+ /**
+ * Error handler.
+ *
+ * This method should be rewritten to use the prefered error
+ * handling method. (PEAR_ErrorStack)
+ *
+ * @access private
+ * @param string $message The error message.
+ * @param integer $type An indication of the severity of the error.
+ * @return void Code may cause PHP to exit.
+ */
+ function _handleErrors($message, $type = E_USER_ERROR) {
+ // For now just echo the message.
+ echo $message;
+
+ // Check to see if we should quit.
+ if ($type == E_USER_ERROR) {
+ exit;
+ }
+ }
+}
+?>
237 PHPUnit/TestCase.php
@@ -0,0 +1,237 @@
+<?php
+//
+// +------------------------------------------------------------------------+
+// | PEAR :: PHPUnit |
+// +------------------------------------------------------------------------+
+// | Copyright (c) 2002-2005 Sebastian Bergmann <sb@sebastian-bergmann.de>. |
+// +------------------------------------------------------------------------+
+// | This source file is subject to version 3.00 of the PHP License, |
+// | that is available at http://www.php.net/license/3_0.txt. |
+// | If you did not receive a copy of the PHP license and are unable to |
+// | obtain it through the world-wide-web, please send a note to |
+// | license@php.net so we can mail you a copy immediately. |
+// +------------------------------------------------------------------------+
+//
+// $Id$
+//
+
+require_once 'PHPUnit/Assert.php';
+require_once 'PHPUnit/TestResult.php';
+
+/**
+ * A TestCase defines the fixture to run multiple tests.
+ *
+ * To define a TestCase
+ *
+ * 1) Implement a subclass of PHPUnit_TestCase.
+ * 2) Define instance variables that store the state of the fixture.
+ * 3) Initialize the fixture state by overriding setUp().
+ * 4) Clean-up after a test by overriding tearDown().
+ *
+ * Each test runs in its own fixture so there can be no side effects
+ * among test runs.
+ *
+ * Here is an example:
+ *
+ * <code>
+ * <?php
+ * class MathTest extends PHPUnit_TestCase {
+ * var $fValue1;
+ * var $fValue2;
+ *
+ * function MathTest($name) {
+ * $this->PHPUnit_TestCase($name);
+ * }
+ *
+ * function setUp() {
+ * $this->fValue1 = 2;
+ * $this->fValue2 = 3;
+ * }
+ * }