1<?php 2/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ 3 4/** 5 * PHP Version 4 6 * 7 * Copyright (c) 2002-2005, Sebastian Bergmann <sb@sebastian-bergmann.de>. 8 * All rights reserved. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 14 * * Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 17 * * Redistributions in binary form must reproduce the above copyright 18 * notice, this list of conditions and the following disclaimer in 19 * the documentation and/or other materials provided with the 20 * distribution. 21 * 22 * * Neither the name of Sebastian Bergmann nor the names of his 23 * contributors may be used to endorse or promote products derived 24 * from this software without specific prior written permission. 25 * 26 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 27 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 28 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 29 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 30 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 31 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 32 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 33 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 34 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRIC 35 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 36 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 37 * POSSIBILITY OF SUCH DAMAGE. 38 * 39 * @category Testing 40 * @package PHPUnit 41 * @author Scott Mattocks <scott@crisscott.com> 42 * @copyright 2002-2005 Sebastian Bergmann <sb@sebastian-bergmann.de> 43 * @license http://www.opensource.org/licenses/bsd-license.php BSD License 44 * @version CVS: $Id: Skeleton.php,v 1.8 2005/11/10 09:47:14 sebastian Exp $ 45 * @link http://pear.php.net/package/PHPUnit 46 * @since File available since Release 1.1.0 47 */ 48 49/** 50 * Class for creating a PHPUnit_TestCase skeleton file. 51 * 52 * This class will take a classname as a parameter on construction and will 53 * create a PHP file that contains the skeleton of a PHPUnit_TestCase 54 * subclass. The test case will contain a test foreach method of the class. 55 * Methods of the parent class will, by default, be excluded from the test 56 * class. Passing and optional construction parameter will include them. 57 * 58 * Example 59 * 60 * <?php 61 * require_once 'PHPUnit/Skeleton.php'; 62 * $ps = new PHPUnit_Skeleton('PHPUnit_Skeleton', 'PHPUnit/Skeleton.php'); 63 * 64 * // Generate the test class. 65 * // Default settings will not include any parent class methods, but 66 * // will include private methods. 67 * $ps->createTestClass(); 68 * 69 * // Write the new test class to file. 70 * // By default, code to run the test will be included. 71 * $ps->writeTestClass(); 72 * ?> 73 * 74 * Now open the skeleton class and fill in the details. 75 * If you run the test as is, all tests will fail and 76 * you will see plenty of undefined constant errors. 77 * 78 * @category Testing 79 * @package PHPUnit 80 * @author Scott Mattocks <scott@crisscott.com> 81 * @copyright 2002-2005 Sebastian Bergmann <sb@sebastian-bergmann.de> 82 * @license http://www.opensource.org/licenses/bsd-license.php BSD License 83 * @version Release: 1.3.2 84 * @link http://pear.php.net/package/PHPUnit 85 * @since Class available since Release 1.1.0 86 */ 87class PHPUnit_Skeleton { 88 /** 89 * Path to the class file to create a skeleton for. 90 * @var string 91 */ 92 var $classPath; 93 94 /** 95 * The name of the class 96 * @var string 97 */ 98 var $className; 99 100 /** 101 * Path to the configuration file needed by class to test. 102 * @var string 103 */ 104 var $configFile; 105 106 /** 107 * Whether or not to include the methods of the parent class when testing. 108 * @var boolean 109 */ 110 var $includeParents; 111 112 /** 113 * Whether or not to test private methods. 114 * @var boolean 115 */ 116 var $includePrivate; 117 118 /** 119 * The test class that will be created. 120 * @var string 121 */ 122 var $testClass; 123 124 /** 125 * Constructor. Sets the class members and check that the class 126 * to test is accessible. 127 * 128 * @access public 129 * @param string $className 130 * @param string $classPath 131 * @param boolean $includeParents Wheter to include the parent's methods in the test. 132 * @return void 133 */ 134 function PHPUnit_Skeleton($className, $classPath, $includeParents = FALSE, $includePrivate = TRUE) { 135 // Set up the members. 136 if (@is_readable($classPath)) { 137 $this->className = $className; 138 $this->classPath = $classPath; 139 } else { 140 $this->_handleErrors($classPath . ' is not readable. Cannot create test class.'); 141 } 142 143 // Do we want to include parent methods? 144 $this->includeParents = $includeParents; 145 146 // Do we want to allow private methods? 147 $this->includePrivate = $includePrivate; 148 } 149 150 /** 151 * The class to test may require a special config file before it can be 152 * instantiated. This method lets you set that file. 153 * 154 * @access public 155 * @param string $configPath 156 * @return void 157 */ 158 function setConfigFile($configFile) { 159 // Check that the file is readable 160 if (@is_readable($configFile)) { 161 $this->configFile = $configFile; 162 } else { 163 $this->_handleErrors($configFile . ' is not readable. Cannot create test class.'); 164 } 165 } 166 167 /** 168 * Create the code that will be the skeleton of the test case. 169 * 170 * The test case must have a clss definition, one var, a constructor 171 * setUp, tearDown, and methods. Optionally and by default the code 172 * to run the test is added when the class is written to file. 173 * 174 * @access public 175 * @param none 176 * @return void 177 */ 178 function createTestClass() { 179 // Instantiate the object. 180 if (isset($this->configFile)) { 181 require_once $this->configFile; 182 } 183 184 require_once $this->classPath; 185 186 // Get the methods. 187 $classMethods = get_class_methods($this->className); 188 189 // Remove the parent methods if needed. 190 if (!$this->includeParents) { 191 $parentMethods = get_class_methods(get_parent_class($this->className)); 192 193 if (count($parentMethods)) { 194 $classMethods = array_diff($classMethods, $parentMethods); 195 } 196 } 197 198 // Create the class definition, constructor, setUp and tearDown. 199 $this->_createDefinition(); 200 $this->_createConstructor(); 201 $this->_createSetUpTearDown(); 202 203 if (count($classMethods)) { 204 // Foreach method create a test case. 205 foreach ($classMethods as $method) { 206 // Unless it is the constructor. 207 if (strcasecmp($this->className, $method) !== 0) { 208 // Check for private methods. 209 if (!$this->includePrivate && strpos($method, '_') === 0) { 210 continue; 211 } else { 212 $this->_createMethod($method); 213 } 214 } 215 } 216 } 217 218 // Finis off the class. 219 $this->_finishClass(); 220 } 221 222 /** 223 * Create the class definition. 224 * 225 * The definition consist of a header comment, require statment 226 * for getting the PHPUnit file, the actual class definition, 227 * and the definition of the class member variable. 228 * 229 * All of the code needed for the new class is stored in the 230 * testClass member. 231 * 232 * @access private 233 * @param none 234 * @return void 235 */ 236 function _createDefinition() { 237 // Create header comment. 238 $this->testClass = 239 "/**\n" . 240 " * PHPUnit test case for " . $this->className . "\n" . 241 " * \n" . 242 " * The method skeletons below need to be filled in with \n" . 243 " * real data so that the tests will run correctly. Replace \n" . 244 " * all EXPECTED_VAL and PARAM strings with real data. \n" . 245 " * \n" . 246 " * Created with PHPUnit_Skeleton on " . date('Y-m-d') . "\n" . 247 " */\n"; 248 249 // Add the require statements. 250 $this->testClass .= "require_once 'PHPUnit.php';\n"; 251 252 // Add the class definition and variable definition. 253 $this->testClass .= 254 "class " . $this->className . "Test extends PHPUnit_TestCase {\n\n" . 255 " var \$" . $this->className . ";\n\n"; 256 } 257 258 /** 259 * Create the class constructor. (PHP4 style) 260 * 261 * The constructor simply calls the PHPUnit_TestCase method. 262 * This code is taken from the PHPUnit documentation. 263 * 264 * All of the code needed for the new class is stored in the 265 * testClass member. 266 * 267 * @access private 268 * @param none 269 * @return void 270 */ 271 function _createConstructor() { 272 // Create the test class constructor. 273 $this->testClass.= 274 " function " . $this->className . "Test(\$name)\n" . 275 " {\n" . 276 " \$this->PHPUnit_TestCase(\$name);\n" . 277 " }\n\n"; 278 } 279 280 /** 281 * Create setUp and tearDown methods. 282 * 283 * The setUp method creates the instance of the object to test. 284 * The tearDown method releases the instance. 285 * This code is taken from the PHPUnit documentation. 286 * 287 * All of the code needed for the new class is stored in the 288 * testClass member. 289 * 290 * @access private 291 * @param none 292 * @return void 293 */ 294 function _createSetUpTearDown() { 295 // Create the setUp method. 296 $this->testClass .= 297 " function setUp()\n" . 298 " {\n"; 299 300 if (isset($this->configFile)) { 301 $this->testClass .= 302 " require_once '" . $this->configFile . "';\n"; 303 } 304 305 $this->testClass .= 306 " require_once '" . $this->classPath . "';\n" . 307 " \$this->" . $this->className . " =& new " . $this->className . "(PARAM);\n" . 308 " }\n\n"; 309 310 // Create the tearDown method. 311 $this->testClass .= 312 " function tearDown()\n" . 313 " {\n" . 314 " unset(\$this->" . $this->className . ");\n" . 315 " }\n\n"; 316 } 317 318 /** 319 * Create a basic skeleton for test methods. 320 * 321 * This code is taken from the PHPUnit documentation. 322 * 323 * All of the code needed for the new class is stored in the 324 * testClass member. 325 * 326 * @access private 327 * @param none 328 * @return void 329 */ 330 function _createMethod($methodName) { 331 // Create a test method. 332 $this->testClass .= 333 " function test" . $methodName . "()\n" . 334 " {\n" . 335 " \$result = \$this->" . $this->className . "->" . $methodName . "(PARAM);\n" . 336 " \$expected = EXPECTED_VAL;\n" . 337 " \$this->assertEquals(\$expected, \$result);\n" . 338 " }\n\n"; 339 } 340 341 /** 342 * Add the closing brace needed for a proper class definition. 343 * 344 * All of the code needed for the new class is stored in the 345 * testClass member. 346 * 347 * @access private 348 * @param none 349 * @return void 350 */ 351 function _finishClass() { 352 // Close off the class. 353 $this->testClass.= "}\n"; 354 } 355 356 /** 357 * Create the code that will actually run the test. 358 * 359 * This code is added by default so that the test can be run 360 * just by running the file. To have it not added pass false 361 * as the second parameter to the writeTestClass method. 362 * This code is taken from the PHPUnit documentation. 363 * 364 * All of the code needed for the new class is stored in the 365 * testClass member. 366 * 367 * @access private 368 * @param none 369 * @return void 370 */ 371 function _createTest() { 372 // Create a call to the test. 373 $test = 374 "// Running the test.\n" . 375 "\$suite = new PHPUnit_TestSuite('" . $this->className . "Test');\n" . 376 "\$result = PHPUnit::run(\$suite);\n" . 377 "echo \$result->toString();\n"; 378 379 return $test; 380 } 381 382 /** 383 * Write the test class to file. 384 * 385 * This will write the test class created using the createTestClass 386 * method to a file called <className>Test.php. By default the file 387 * is written to the current directory and will have code to run 388 * the test appended to the bottom of the file. 389 * 390 * @access public 391 * @param string $destination The directory to write the file to. 392 * @param boolean $addTest Wheter to add the test running code. 393 * @return void 394 */ 395 function writeTestClass($destination = './', $addTest = TRUE) { 396 // Check for something to write to file. 397 if (!isset($this->testClass)) { 398 $this->_handleErrors('Noting to write.', PHPUS_WARNING); 399 return; 400 } 401 402 // Open the destination file. 403 $fp = fopen($destination . $this->className . 'Test.php', 'w'); 404 fwrite($fp, "<?php\n"); 405 406 // Write the test class. 407 fwrite($fp, $this->testClass); 408 409 // Add the call to test the class in the file if we were asked to. 410 if ($addTest) { 411 fwrite($fp, $this->_createTest()); 412 } 413 414 // Close the file. 415 fwrite($fp, "?>\n"); 416 fclose($fp); 417 } 418 419 /** 420 * Error handler. 421 * 422 * This method should be rewritten to use the prefered error 423 * handling method. (PEAR_ErrorStack) 424 * 425 * @access private 426 * @param string $message The error message. 427 * @param integer $type An indication of the severity of the error. 428 * @return void Code may cause PHP to exit. 429 */ 430 function _handleErrors($message, $type = E_USER_ERROR) { 431 // For now just echo the message. 432 echo $message; 433 434 // Check to see if we should quit. 435 if ($type == E_USER_ERROR) { 436 exit; 437 } 438 } 439} 440 441/* 442 * Local variables: 443 * tab-width: 4 444 * c-basic-offset: 4 445 * c-hanging-comment-ender-p: nil 446 * End: 447 */ 448?> 449