phpunit.php

Go to the documentation of this file.
00001 <?php
00002 //
00003 // PHP framework for testing, based on the design of "JUnit".
00004 //
00005 // Written by Fred Yankowski <fred@ontosys.com>
00006 //            OntoSys, Inc  <http://www.OntoSys.com>
00007 //
00008 // $Id: phpunit.php,v 1.1 2002/03/30 19:32:17 bmatzelle Exp $
00009 
00010 // Copyright (c) 2000 Fred Yankowski
00011 
00012 // Permission is hereby granted, free of charge, to any person
00013 // obtaining a copy of this software and associated documentation
00014 // files (the "Software"), to deal in the Software without
00015 // restriction, including without limitation the rights to use, copy,
00016 // modify, merge, publish, distribute, sublicense, and/or sell copies
00017 // of the Software, and to permit persons to whom the Software is
00018 // furnished to do so, subject to the following conditions:
00019 //
00020 // The above copyright notice and this permission notice shall be
00021 // included in all copies or substantial portions of the Software.
00022 //
00023 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
00024 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
00025 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
00026 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
00027 // BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
00028 // ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
00029 // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
00030 // SOFTWARE.
00031 //
00032 error_reporting(E_ERROR | E_WARNING | E_PARSE | E_NOTICE |
00033                 E_CORE_ERROR | E_CORE_WARNING);
00034 
00035 /*
00036 interface Test {
00037   function run(&$aTestResult);
00038   function countTestCases();
00039 }
00040 */
00041 
00042 function trace($msg) {
00043   return;
00044   print($msg);
00045   flush();
00046 }
00047 
00048 
00049 class Exception {
00050     /* Emulate a Java exception, sort of... */
00051   var $message;
00052   function Exception($message) {
00053     $this->message = $message;
00054   }
00055   function getMessage() {
00056     return $this->message;
00057   }
00058 }
00059 
00060 class Assert {
00061   function assert($boolean, $message=0) {
00062     if (! $boolean)
00063       $this->fail($message);
00064   }
00065 
00066   function assertEquals($expected, $actual, $message=0) {
00067     if ($expected != $actual) {
00068       $this->failNotEquals($expected, $actual, "expected", $message);
00069     }
00070   }
00071 
00072   function assertRegexp($regexp, $actual, $message=false) {
00073     if (! preg_match($regexp, $actual)) {
00074       $this->failNotEquals($regexp, $actual, "pattern", $message);
00075     }
00076   }
00077 
00078   function failNotEquals($expected, $actual, $expected_label, $message=0) {
00079     // Private function for reporting failure to match.
00080     $str = $message ? ($message . ' ') : '';
00081     $str .= "($expected_label/actual)<br>";
00082     $htmlExpected = htmlspecialchars($expected);
00083     $htmlActual = htmlspecialchars($actual);
00084     $str .= sprintf("<pre>%s\n--------\n%s</pre>",
00085                     $htmlExpected, $htmlActual);
00086     $this->fail($str);
00087   }
00088 }
00089 
00090 class TestCase extends Assert /* implements Test */ {
00091   /* Defines context for running tests.  Specific context -- such as
00092      instance variables, global variables, global state -- is defined
00093      by creating a subclass that specializes the setUp() and
00094      tearDown() methods.  A specific test is defined by a subclass
00095      that specializes the runTest() method. */
00096   var $fName;
00097   var $fResult;
00098   var $fExceptions = array();
00099 
00100   function TestCase($name) {
00101     $this->fName = $name;
00102   }
00103 
00104   function run($testResult=0) {
00105     /* Run this single test, by calling the run() method of the
00106        TestResult object which will in turn call the runBare() method
00107        of this object.  That complication allows the TestResult object
00108        to do various kinds of progress reporting as it invokes each
00109        test.  Create/obtain a TestResult object if none was passed in.
00110        Note that if a TestResult object was passed in, it must be by
00111        reference. */
00112     if (! $testResult)
00113       $testResult = $this->_createResult();
00114     $this->fResult = $testResult;
00115     $testResult->run(&$this);
00116     $this->fResult = 0;
00117     return $testResult;
00118   }
00119 
00120   function countTestCases() {
00121     return 1;
00122   }
00123 
00124   function runTest() {
00125     $name = $this->name();
00126     // Since isset($this->$name) is false, no way to run defensive checks
00127     $this->$name();
00128   }
00129 
00130   function setUp() /* expect override */ {
00131     //print("TestCase::setUp()<br>\n");
00132   }
00133 
00134   function tearDown() /* possible override */ {
00135     //print("TestCase::tearDown()<br>\n");
00136   }
00137 
00139 
00140 
00141   function _createResult() /* protected */ {
00142     /* override this to use specialized subclass of TestResult */
00143     return new TestResult;
00144   }
00145 
00146   function fail($message=0) {
00147     //printf("TestCase::fail(%s)<br>\n", ($message) ? $message : '');
00148     /* JUnit throws AssertionFailedError here.  We just record the
00149        failure and carry on */
00150     $this->fExceptions[] = new Exception(&$message);
00151   }
00152 
00153   function error($message) {
00154     /* report error that requires correction in the test script
00155        itself, or (heaven forbid) in this testing infrastructure */
00156     printf('<b>ERROR: ' . $message . '</b><br>');
00157     $this->fResult->stop();
00158   }
00159 
00160   function failed() {
00161     return count($this->fExceptions);
00162   }
00163 
00164   function getExceptions() {
00165     return $this->fExceptions;
00166   }
00167 
00168   function name() {
00169     return $this->fName;
00170   }
00171 
00172   function runBare() {
00173     $this->setup();
00174     $this->runTest();
00175     $this->tearDown();
00176   }
00177 }
00178 
00179 
00180 class TestSuite /* implements Test */ {
00181   /* Compose a set of Tests (instances of TestCase or TestSuite), and
00182      run them all. */
00183   var $fTests = array();
00184 
00185   function TestSuite($classname=false) {
00186     if ($classname) {
00187       // Find all methods of the given class whose name starts with
00188       // "test" and add them to the test suite.  We are just _barely_
00189       // able to do this with PHP's limited introspection...  Note
00190       // that PHP seems to store method names in lower case, and we
00191       // have to avoid the constructor function for the TestCase class
00192       // superclass.  This will fail when $classname starts with
00193       // "Test" since that will have a constructor method that will
00194       // get matched below and then treated (incorrectly) as a test
00195       // method.  So don't name any TestCase subclasses as "Test..."!
00196       if (floor(phpversion()) >= 4) {
00197         // PHP4 introspection, submitted by Dylan Kuhn
00198         $names = get_class_methods($classname);
00199         while (list($key, $method) = each($names)) {
00200           if (preg_match('/^test/', $method) && $method != "testcase") {  
00201             $this->addTest(new $classname($method));
00202           }
00203         }
00204       }
00205       else {
00206         $dummy = new $classname("dummy");
00207         $names = (array) $dummy;
00208         while (list($key, $value) = each($names)) {
00209           $type = gettype($value);
00210           if ($type == "user function" && preg_match('/^test/', $key)
00211           && $key != "testcase") {  
00212             $this->addTest(new $classname($key));
00213           }
00214         }
00215       }
00216     }
00217   }
00218 
00219   function addTest($test) {
00220     /* Add TestCase or TestSuite to this TestSuite */
00221     $this->fTests[] = $test;
00222   }
00223 
00224   function run(&$testResult) {
00225     /* Run all TestCases and TestSuites comprising this TestSuite,
00226        accumulating results in the given TestResult object. */
00227     reset($this->fTests);
00228     while (list($na, $test) = each($this->fTests)) {
00229       if ($testResult->shouldStop())
00230         break;
00231       $test->run(&$testResult);
00232     }
00233   }
00234 
00235   function countTestCases() {
00236     /* Number of TestCases comprising this TestSuite (including those
00237        in any constituent TestSuites) */
00238     $count = 0;
00239     reset($fTests);
00240     while (list($na, $test_case) = each($this->fTests)) {
00241       $count += $test_case->countTestCases();
00242     }
00243     return $count;
00244   }
00245 }
00246 
00247 
00248 class TestFailure {
00249   /* Record failure of a single TestCase, associating it with the
00250      exception(s) that occurred */
00251   var $fFailedTestName;
00252   var $fExceptions;
00253 
00254   function TestFailure(&$test, &$exceptions) {
00255     $this->fFailedTestName = $test->name();
00256     $this->fExceptions = $exceptions;
00257   }
00258 
00259   function getExceptions() {
00260       return $this->fExceptions;
00261   }
00262   function getTestName() {
00263     return $this->fFailedTestName;
00264   }
00265 }
00266 
00267 
00268 class TestResult {
00269   /* Collect the results of running a set of TestCases. */
00270   var $fFailures = array();
00271   var $fRunTests = 0;
00272   var $fStop = false;
00273 
00274   function TestResult() { }
00275 
00276   function _endTest($test) /* protected */ {
00277       /* specialize this for end-of-test action, such as progress
00278          reports  */
00279   }
00280 
00281   function getFailures() {
00282     return $this->fFailures;
00283   }
00284 
00285   function run($test) {
00286     /* Run a single TestCase in the context of this TestResult */
00287     $this->_startTest($test);
00288     $this->fRunTests++;
00289 
00290     $test->runBare();
00291 
00292     /* this is where JUnit would catch AssertionFailedError */
00293     $exceptions = $test->getExceptions();
00294     if ($exceptions)
00295       $this->fFailures[] = new TestFailure(&$test, &$exceptions);
00296     $this->_endTest($test);
00297   }
00298 
00299   function countTests() {
00300     return $this->fRunTests;
00301   }
00302 
00303   function shouldStop() {
00304     return $this->fStop;
00305   }
00306 
00307   function _startTest($test) /* protected */ {
00308       /* specialize this for start-of-test actions */
00309   }
00310 
00311   function stop() {
00312     /* set indication that the test sequence should halt */
00313     $fStop = true;
00314   }
00315 
00316   function countFailures() {
00317     return count($this->fFailures);
00318   }
00319 }
00320 
00321 
00322 class TextTestResult extends TestResult {
00323   /* Specialize TestResult to produce text/html report */
00324   function TextTestResult() {
00325     $this->TestResult();  // call superclass constructor
00326   }
00327   
00328   function report() {
00329     /* report result of test run */
00330     $nRun = $this->countTests();
00331     $nFailures = $this->countFailures();
00332     printf("<p>%s test%s run<br>", $nRun, ($nRun == 1) ? '' : 's');
00333     printf("%s failure%s.<br>\n", $nFailures, ($nFailures == 1) ? '' : 's');
00334     if ($nFailures == 0)
00335       return;
00336 
00337     print("<ol>\n");
00338     $failures = $this->getFailures();
00339     while (list($i, $failure) = each($failures)) {
00340       $failedTestName = $failure->getTestName();
00341       printf("<li>%s\n", $failedTestName);
00342 
00343       $exceptions = $failure->getExceptions();
00344       print("<ul>");
00345       while (list($na, $exception) = each($exceptions))
00346         printf("<li>%s\n", $exception->getMessage());
00347       print("</ul>");
00348     }
00349     print("</ol>\n");
00350   }
00351 
00352   function _startTest($test) {
00353     printf("%s ", $test->name());
00354     flush();
00355   }
00356 
00357   function _endTest($test) {
00358     $outcome = $test->failed()
00359        ? "<font color=\"red\">FAIL</font>"
00360        : "<font color=\"green\">ok</font>";
00361     printf("$outcome<br>\n");
00362     flush();
00363   }
00364 }
00365 
00366 
00367 class TestRunner {
00368   /* Run a suite of tests and report results. */
00369   function run($suite) {
00370     $result = new TextTestResult;
00371     $suite->run($result);
00372     $result->report();
00373   }
00374 }
00375 
00376 ?>

Generated on Mon Dec 8 01:06:46 2008 for Ship-Simulator by  doxygen 1.5.6