02c58e2d48af49cb34b8188a36e70cb2a78baaec
[mailer.git] / inc / phpmailer / test / phpunit.php
1 <?php\r
2 //\r
3 // PHP framework for testing, based on the design of "JUnit".\r
4 //\r
5 // Written by Fred Yankowski <fred@ontosys.com>\r
6 //            OntoSys, Inc  <http://www.OntoSys.com>\r
7 //\r
8 // $Id: phpunit.php 880 2009-03-07 02:55:57Z quix0r $\r
9 \r
10 // Copyright (c) 2000 Fred Yankowski\r
11 \r
12 // Permission is hereby granted, free of charge, to any person\r
13 // obtaining a copy of this software and associated documentation\r
14 // files (the "Software"), to deal in the Software without\r
15 // restriction, including without limitation the rights to use, copy,\r
16 // modify, merge, publish, distribute, sublicense, and/or sell copies\r
17 // of the Software, and to permit persons to whom the Software is\r
18 // furnished to do so, subject to the following conditions:\r
19 //\r
20 // The above copyright notice and this permission notice shall be\r
21 // included in all copies or substantial portions of the Software.\r
22 //\r
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
24 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
25 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
26 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
27 // BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
28 // ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
29 // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
30 // SOFTWARE.\r
31 //\r
32 error_reporting(E_ERROR | E_WARNING | E_PARSE | E_NOTICE |\r
33                 E_CORE_ERROR | E_CORE_WARNING);\r
34 \r
35 /*\r
36 interface Test {\r
37   function run(&$aTestResult);\r
38   function countTestCases();\r
39 }\r
40 */\r
41 \r
42 function trace($msg) {\r
43   return;\r
44   print($msg);\r
45   flush();\r
46 }\r
47 \r
48 \r
49 class Exception {\r
50     /* Emulate a Java exception, sort of... */\r
51   var $message;\r
52   function Exception($message) {\r
53     $this->message = $message;\r
54   }\r
55   function getMessage() {\r
56     return $this->message;\r
57   }\r
58 }\r
59 \r
60 class Assert {\r
61   function assert($boolean, $message=0) {\r
62     if (! $boolean)\r
63       $this->fail($message);\r
64   }\r
65 \r
66   function assertEquals($expected, $actual, $message=0) {\r
67     if ($expected != $actual) {\r
68       $this->failNotEquals($expected, $actual, "expected", $message);\r
69     }\r
70   }\r
71 \r
72   function assertRegexp($regexp, $actual, $message=false) {\r
73     if (! preg_match($regexp, $actual)) {\r
74       $this->failNotEquals($regexp, $actual, "pattern", $message);\r
75     }\r
76   }\r
77 \r
78   function failNotEquals($expected, $actual, $expected_label, $message=0) {\r
79     // Private function for reporting failure to match.\r
80     $str = $message ? ($message . ' ') : '';\r
81     $str .= "($expected_label/actual)<br>";\r
82     $htmlExpected = htmlspecialchars($expected);\r
83     $htmlActual = htmlspecialchars($actual);\r
84     $str .= sprintf("<pre>%s\n--------\n%s</pre>",\r
85                     $htmlExpected, $htmlActual);\r
86     $this->fail($str);\r
87   }\r
88 }\r
89 \r
90 class TestCase extends Assert /* implements Test */ {\r
91   /* Defines context for running tests.  Specific context -- such as\r
92      instance variables, global variables, global state -- is defined\r
93      by creating a subclass that specializes the setUp() and\r
94      tearDown() methods.  A specific test is defined by a subclass\r
95      that specializes the runTest() method. */\r
96   var $fName;\r
97   var $fResult;\r
98   var $fExceptions = array();\r
99 \r
100   function TestCase($name) {\r
101     $this->fName = $name;\r
102   }\r
103 \r
104   function run($testResult=0) {\r
105     /* Run this single test, by calling the run() method of the\r
106        TestResult object which will in turn call the runBare() method\r
107        of this object.  That complication allows the TestResult object\r
108        to do various kinds of progress reporting as it invokes each\r
109        test.  Create/obtain a TestResult object if none was passed in.\r
110        Note that if a TestResult object was passed in, it must be by\r
111        reference. */\r
112     if (! $testResult)\r
113       $testResult = $this->_createResult();\r
114     $this->fResult = $testResult;\r
115     $testResult->run(&$this);\r
116     $this->fResult = '0';\r
117     return $testResult;\r
118   }\r
119 \r
120   function countTestCases() {\r
121     return 1;\r
122   }\r
123 \r
124   function runTest() {\r
125     $name = $this->name();\r
126     // Since isset($this->$name) is false, no way to run defensive checks\r
127     $this->$name();\r
128   }\r
129 \r
130   function setUp() /* expect override */ {\r
131     //print("TestCase::setUp()<br>\n");\r
132   }\r
133 \r
134   function tearDown() /* possible override */ {\r
135     //print("TestCase::tearDown()<br>\n");\r
136   }\r
137 \r
138   ////////////////////////////////////////////////////////////////\r
139 \r
140 \r
141   function _createResult() /* protected */ {\r
142     /* override this to use specialized subclass of TestResult */\r
143     return new TestResult;\r
144   }\r
145 \r
146   function fail($message=0) {\r
147     //printf("TestCase::fail(%s)<br>\n", ($message) ? $message : '');\r
148     /* JUnit throws AssertionFailedError here.  We just record the\r
149        failure and carry on */\r
150     $this->fExceptions[] = new Exception(&$message);\r
151   }\r
152 \r
153   function error($message) {\r
154     /* report error that requires correction in the test script\r
155        itself, or (heaven forbid) in this testing infrastructure */\r
156     printf('<b>ERROR: ' . $message . '</b><br>');\r
157     $this->fResult->stop();\r
158   }\r
159 \r
160   function failed() {\r
161     return count($this->fExceptions);\r
162   }\r
163 \r
164   function getExceptions() {\r
165     return $this->fExceptions;\r
166   }\r
167 \r
168   function name() {\r
169     return $this->fName;\r
170   }\r
171 \r
172   function runBare() {\r
173     $this->setup();\r
174     $this->runTest();\r
175     $this->tearDown();\r
176   }\r
177 }\r
178 \r
179 \r
180 class TestSuite /* implements Test */ {\r
181   /* Compose a set of Tests (instances of TestCase or TestSuite), and\r
182      run them all. */\r
183   var $fTests = array();\r
184 \r
185   function TestSuite($classname=false) {\r
186     if ($classname) {\r
187       // Find all methods of the given class whose name starts with\r
188       // "test" and add them to the test suite.  We are just _barely_\r
189       // able to do this with PHP's limited introspection...  Note\r
190       // that PHP seems to store method names in lower case, and we\r
191       // have to avoid the constructor function for the TestCase class\r
192       // superclass.  This will fail when $classname starts with\r
193       // "Test" since that will have a constructor method that will\r
194       // get matched below and then treated (incorrectly) as a test\r
195       // method.  So don't name any TestCase subclasses as "Test..."!\r
196       if (floor(phpversion()) >= 4) {\r
197         // PHP4 introspection, submitted by Dylan Kuhn\r
198         $names = get_class_methods($classname);\r
199         while (list($key, $method) = each($names)) {\r
200           if (preg_match('/^test/', $method) && $method != "testcase") {  \r
201             $this->addTest(new $classname($method));\r
202           }\r
203         }\r
204       }\r
205       else {\r
206         $dummy = new $classname("dummy");\r
207         $names = (array) $dummy;\r
208         while (list($key, $value) = each($names)) {\r
209           $type = gettype($value);\r
210           if ($type == "user function" && preg_match('/^test/', $key)\r
211           && $key != "testcase") {  \r
212             $this->addTest(new $classname($key));\r
213           }\r
214         }\r
215       }\r
216     }\r
217   }\r
218 \r
219   function addTest($test) {\r
220     /* Add TestCase or TestSuite to this TestSuite */\r
221     $this->fTests[] = $test;\r
222   }\r
223 \r
224   function run(&$testResult) {\r
225     /* Run all TestCases and TestSuites comprising this TestSuite,\r
226        accumulating results in the given TestResult object. */\r
227     reset($this->fTests);\r
228     while (list($na, $test) = each($this->fTests)) {\r
229       if ($testResult->shouldStop())\r
230         break;\r
231       $test->run(&$testResult);\r
232     }\r
233   }\r
234 \r
235   function countTestCases() {\r
236     /* Number of TestCases comprising this TestSuite (including those\r
237        in any constituent TestSuites) */\r
238     $count = '0';\r
239     reset($fTests);\r
240     while (list($na, $test_case) = each($this->fTests)) {\r
241       $count += $test_case->countTestCases();\r
242     }\r
243     return $count;\r
244   }\r
245 }\r
246 \r
247 \r
248 class TestFailure {\r
249   /* Record failure of a single TestCase, associating it with the\r
250      exception(s) that occurred */\r
251   var $fFailedTestName;\r
252   var $fExceptions;\r
253 \r
254   function TestFailure(&$test, &$exceptions) {\r
255     $this->fFailedTestName = $test->name();\r
256     $this->fExceptions = $exceptions;\r
257   }\r
258 \r
259   function getExceptions() {\r
260       return $this->fExceptions;\r
261   }\r
262   function getTestName() {\r
263     return $this->fFailedTestName;\r
264   }\r
265 }\r
266 \r
267 \r
268 class TestResult {\r
269   /* Collect the results of running a set of TestCases. */\r
270   var $fFailures = array();\r
271   var $fRunTests = '0';\r
272   var $fStop = false;\r
273 \r
274   function TestResult() { }\r
275 \r
276   function _endTest($test) /* protected */ {\r
277       /* specialize this for end-of-test action, such as progress\r
278          reports  */\r
279   }\r
280 \r
281   function getFailures() {\r
282     return $this->fFailures;\r
283   }\r
284 \r
285   function run($test) {\r
286     /* Run a single TestCase in the context of this TestResult */\r
287     $this->_startTest($test);\r
288     $this->fRunTests++;\r
289 \r
290     $test->runBare();\r
291 \r
292     /* this is where JUnit would catch AssertionFailedError */\r
293     $exceptions = $test->getExceptions();\r
294     if ($exceptions)\r
295       $this->fFailures[] = new TestFailure(&$test, &$exceptions);\r
296     $this->_endTest($test);\r
297   }\r
298 \r
299   function countTests() {\r
300     return $this->fRunTests;\r
301   }\r
302 \r
303   function shouldStop() {\r
304     return $this->fStop;\r
305   }\r
306 \r
307   function _startTest($test) /* protected */ {\r
308       /* specialize this for start-of-test actions */\r
309   }\r
310 \r
311   function stop() {\r
312     /* set indication that the test sequence should halt */\r
313     $fStop = true;\r
314   }\r
315 \r
316   function countFailures() {\r
317     return count($this->fFailures);\r
318   }\r
319 }\r
320 \r
321 \r
322 class TextTestResult extends TestResult {\r
323   /* Specialize TestResult to produce text/html report */\r
324   function TextTestResult() {\r
325     $this->TestResult();  // call superclass constructor\r
326   }\r
327   \r
328   function report() {\r
329     /* report result of test run */\r
330     $nRun = $this->countTests();\r
331     $nFailures = $this->countFailures();\r
332     printf("<p>%s test%s run<br>", $nRun, ($nRun == 1) ? '' : 's');\r
333     printf("%s failure%s.<br>\n", $nFailures, ($nFailures == 1) ? '' : 's');\r
334     if ($nFailures == '0')\r
335       return;\r
336 \r
337     print("<ol>\n");\r
338     $failures = $this->getFailures();\r
339     while (list($i, $failure) = each($failures)) {\r
340       $failedTestName = $failure->getTestName();\r
341       printf("<li>%s\n", $failedTestName);\r
342 \r
343       $exceptions = $failure->getExceptions();\r
344       print("<ul>");\r
345       while (list($na, $exception) = each($exceptions))\r
346         printf("<li>%s\n", $exception->getMessage());\r
347       print("</ul>");\r
348     }\r
349     print("</ol>\n");\r
350   }\r
351 \r
352   function _startTest($test) {\r
353     printf("%s ", $test->name());\r
354     flush();\r
355   }\r
356 \r
357   function _endTest($test) {\r
358     $outcome = $test->failed()\r
359        ? "<font color=\"red\">FAIL</font>"\r
360        : "<font color=\"green\">ok</font>";\r
361     printf("$outcome<br>\n");\r
362     flush();\r
363   }\r
364 }\r
365 \r
366 \r
367 class TestRunner {\r
368   /* Run a suite of tests and report results. */\r
369   function run($suite) {\r
370     $result = new TextTestResult;\r
371     $suite->run($result);\r
372     $result->report();\r
373   }\r
374 }\r
375 \r
376 ?>\r