added initial test coverage and filtering stuff
This commit is contained in:
		
							parent
							
								
									586215680e
								
							
						
					
					
						commit
						54f54d9794
					
				| @ -17,4 +17,6 @@ | ||||
| 	Autoloader::loadClassMapFromFile(dirname(__FILE__) . '/manifest.php'); | ||||
| 	spl_autoload_register('Autoloader::autoload'); | ||||
| 	 | ||||
| 	CoverageFilter::addDefaultFilters(); | ||||
| 
 | ||||
| ?>
 | ||||
| @ -189,34 +189,55 @@ | ||||
| 			return $this; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Adds tests in bulk | ||||
| 		 * | ||||
| 		 * @author  Tommy Montgomery | ||||
| 		 * @version 1.0 | ||||
| 		 * @since   1.0 | ||||
| 		 * @uses    addTest() | ||||
| 		 *  | ||||
| 		 * @param  array $tests | ||||
| 		 * @return TestRunner | ||||
| 		 */ | ||||
| 		public final function addTests(array $tests) { | ||||
| 			foreach ($tests as $test) { | ||||
| 				$this->addTest($test); | ||||
| 			} | ||||
| 			 | ||||
| 			return $this; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Gets the value of an option | ||||
| 		 * | ||||
| 		 * @author  Tommy Montgomery | ||||
| 		 * @version 1.0 | ||||
| 		 * @since   1.0 | ||||
| 		 *  | ||||
| 		 * @param  string $option | ||||
| 		 * @throws InvalidArgumentException | ||||
| 		 * @return mixed | ||||
| 		 */ | ||||
| 		public final function getOption($option) { | ||||
| 			if (!array_key_exists($option, $this->options)) { | ||||
| 				throw new InvalidArgumentException('Option "' . $option . '" does not exist'); | ||||
| 			} | ||||
| 			 | ||||
| 			return $this->options[$option]; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Parses options | ||||
| 		 * | ||||
| 		 * @author  Tommy Montgomery | ||||
| 		 * @version 1.0 | ||||
| 		 * @since   1.0 | ||||
| 		 * @uses    getAllowableOptions() | ||||
| 		 *  | ||||
| 		 * @param  array $unparsed The unparsed options | ||||
| 		 * @throws {@link InvalidOptionException} | ||||
| 		 * @return array The parsed options | ||||
| 		 */ | ||||
| 		protected final function parseOptions(array $unparsed) { | ||||
| 			$allowedOptions = $this->getAllowableOptions(); | ||||
| 			$options = array_fill_keys(array_keys($allowedOptions), null); | ||||
| 			foreach ($unparsed as $option => $value) { | ||||
| 				if (!isset($allowedOptions[$option])) { | ||||
| 					throw new InvalidOptionException($option); | ||||
| 				} | ||||
| 				if (gettype($value) !== $allowedOptions[$option]) { | ||||
| 					throw new InvalidOptionException($option, 'Expected a value of type ' . $allowedOptions[$option] . ', got ' . gettype($value)); | ||||
| 				} | ||||
| 				 | ||||
| 				$options[$option] = $value; | ||||
| 			} | ||||
| 			 | ||||
| 			return $options; | ||||
| 		} | ||||
| 		protected abstract function parseOptions(array $unparsed); | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Runs the tests and returns the results | ||||
| @ -263,6 +284,17 @@ | ||||
| 			} | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Called before runTests() | ||||
| 		 * | ||||
| 		 * @author  Tommy Montgomery | ||||
| 		 * @version 1.0 | ||||
| 		 * @since   1.0 | ||||
| 		 */ | ||||
| 		protected function preRun() { | ||||
| 			 | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Runs all tests and publishes the results | ||||
| 		 * | ||||
| @ -274,17 +306,34 @@ | ||||
| 		 * @uses    TestListener::afterTestRunner() | ||||
| 		 */ | ||||
| 		public final function run() { | ||||
| 			$this->preRun(); | ||||
| 			 | ||||
| 			foreach ($this->listeners as $listener) { | ||||
| 				$listener->beforeTestRunner($this); | ||||
| 			} | ||||
| 			 | ||||
| 			$this->startTime = microtime(true); | ||||
| 			$this->publishResults($this->runTests()); | ||||
| 			$results         = $this->runTests(); | ||||
| 			$this->endTime   = microtime(true); | ||||
| 			 | ||||
| 			$this->publishResults($results); | ||||
| 			 | ||||
| 			foreach ($this->listeners as $listener) { | ||||
| 				$listener->afterTestRunner($this); | ||||
| 			} | ||||
| 			 | ||||
| 			$this->postRun(); | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Called after runTests() | ||||
| 		 * | ||||
| 		 * @author  Tommy Montgomery | ||||
| 		 * @version 1.0 | ||||
| 		 * @since   1.0 | ||||
| 		 */ | ||||
| 		protected function postRun() { | ||||
| 		 | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| @ -315,17 +364,6 @@ | ||||
| 			return Util::countTests($this->tests); | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Gets all allowable options for this test runner | ||||
| 		 * | ||||
| 		 * @author  Tommy Montgomery | ||||
| 		 * @version 1.0 | ||||
| 		 * @since   1.0 | ||||
| 		 * | ||||
| 		 * @return array | ||||
| 		 */ | ||||
| 		protected abstract function getAllowableOptions(); | ||||
| 		 | ||||
| 	} | ||||
| 
 | ||||
| ?>
 | ||||
| @ -20,22 +20,124 @@ | ||||
| 	class ConsoleTestRunner extends TestRunner { | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Gets allowable options | ||||
| 		 * | ||||
| 		 * Currently supported options: | ||||
| 		 * - recursive (boolean) | ||||
| 		 * - bootstrap (string) | ||||
| 		 * {@inheritdoc} | ||||
| 		 * | ||||
| 		 * @author  Tommy Montgomery | ||||
| 		 * @version 1.0 | ||||
| 		 * @since   1.0 | ||||
| 		 * @version 1.0 | ||||
| 		 * @uses    getOption() | ||||
| 		 */ | ||||
| 		protected function preRun() { | ||||
| 			parent::preRun(); | ||||
| 			 | ||||
| 			if ($this->getOption('coverage-html') !== null || $this->getOption('coverage-console') !== null/* || $this->getOption('coverage-xml')*/) { | ||||
| 				xdebug_start_code_coverage(XDEBUG_CC_UNUSED | XDEBUG_CC_DEAD_CODE); | ||||
| 			} | ||||
| 			 | ||||
| 			$bootstrap = $this->getOption('bootstrap'); | ||||
| 			if ($bootstrap !== null) { | ||||
| 				require_once $bootstrap; | ||||
| 			} | ||||
| 		} | ||||
| 		 | ||||
| 		protected function postRun() { | ||||
| 			$html    = $this->getOption('coverage-html'); | ||||
| 			$console = $this->getOption('coverage-console'); | ||||
| 			if ($html !== null || $console !== null) { | ||||
| 				$coverage = xdebug_get_code_coverage(); | ||||
| 				xdebug_stop_code_coverage(); | ||||
| 				if ($console !== null) { | ||||
| 					CoverageReporter::createConsoleReport($coverage); | ||||
| 				} | ||||
| 				if ($html !== null) { | ||||
| 					CoverageReporter::createHtmlReport($html, $coverage); | ||||
| 				} | ||||
| 				 | ||||
| 				unset($coverage); | ||||
| 			} | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Parses test runner options | ||||
| 		 * | ||||
| 		 * @author  Tommy Montgomery | ||||
| 		 * @since   1.0 | ||||
| 		 * @version 1.0 | ||||
| 		 * @uses    validateOptions() | ||||
| 		 * @uses    getAllowableOptions() | ||||
| 		 * | ||||
| 		 * @param  array $unparsed Unparsed options | ||||
| 		 * @return array The parsed options | ||||
| 		 */ | ||||
| 		protected function parseOptions(array $unparsed) { | ||||
| 			$options = $this->getAllowableOptions(); | ||||
| 			foreach ($options as $name => $default) { | ||||
| 				if (array_key_exists($name, $unparsed)) { | ||||
| 					$options[$name] = $unparsed[$name]; | ||||
| 				} | ||||
| 			} | ||||
| 			 | ||||
| 			$this->validateOptions($options); | ||||
| 			 | ||||
| 			return $options; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Validates the options | ||||
| 		 * | ||||
| 		 * @author  Tommy Montgomery | ||||
| 		 * @since   1.0 | ||||
| 		 * @version 1.0 | ||||
| 		 * | ||||
| 		 * @param  array $options | ||||
| 		 * @throws {@link InvalidOptionException} | ||||
| 		 */ | ||||
| 		protected function validateOptions(array $options) { | ||||
| 			if ($options['bootstrap'] !== null && !is_file($options['bootstrap'])) { | ||||
| 				throw new InvalidOptionException('bootstrap', 'Bootstrap must be a file'); | ||||
| 			} | ||||
| 			if (!is_bool($options['recursive'])) { | ||||
| 				throw new InvalidOptionException('recursive', 'recursive must be a boolean'); | ||||
| 			} | ||||
| 			if (/*$options['coverage-xml'] !== null || */$options['coverage-html'] !== null || $options['coverage-console'] !== null) { | ||||
| 				if (!extension_loaded('xdebug')) { | ||||
| 					throw new InvalidOptionException('coverage-(xml|html|console)', 'xdebug extension is not loaded'); | ||||
| 				} | ||||
| 				/*if ($options['coverage-xml'] !== null) { | ||||
| 					$dir = dirname($options['coverage-xml']); | ||||
| 					if (!is_dir($dir)) { | ||||
| 						if (!mkdir($dir, 0777, true)) { | ||||
| 							throw new TUnitException('Could not create directory: ' . $dir); | ||||
| 						} | ||||
| 					} | ||||
| 				}*/ | ||||
| 				if ($options['coverage-html'] !== null) { | ||||
| 					//create coverage directory if needed
 | ||||
| 					if (!is_dir($options['coverage-html'])) { | ||||
| 						if (!mkdir($options['coverage-html'], 0777, true)) { | ||||
| 							throw new TUnitException('Could not create directory: ' . $options['coverage-html']); | ||||
| 						} | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Gets allowable options and default values | ||||
| 		 * | ||||
| 		 * @author  Tommy Montgomery | ||||
| 		 * @since   1.0 | ||||
| 		 * @version 1.0 | ||||
| 		 * | ||||
| 		 * @return array | ||||
| 		 */ | ||||
| 		protected function getAllowableOptions() { | ||||
| 		public function getAllowableOptions() { | ||||
| 			return array( | ||||
| 				'recursive' => 'boolean', | ||||
| 				'bootstrap' => 'string' | ||||
| 				'bootstrap'        => null, | ||||
| 				'recursive'        => false, | ||||
| 				//'coverage-xml'     => null,
 | ||||
| 				'coverage-html'    => null, | ||||
| 				'coverage-console' => false | ||||
| 			); | ||||
| 		} | ||||
| 		 | ||||
|  | ||||
							
								
								
									
										54
									
								
								src/TUnit/framework/reporting/CoverageFilter.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								src/TUnit/framework/reporting/CoverageFilter.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,54 @@ | ||||
| <?php | ||||
| 
 | ||||
| 	final class CoverageFilter { | ||||
| 		 | ||||
| 		private static $files        = array(); | ||||
| 		private static $directories  = array(); | ||||
| 		 | ||||
| 		private function __construct() {} | ||||
| 		 | ||||
| 		public static function addFile($file) { | ||||
| 			self::$files[] = $file; | ||||
| 		} | ||||
| 		 | ||||
| 		public static function addDirectory($dir) { | ||||
| 			self::$directories[] = $dir; | ||||
| 		} | ||||
| 		 | ||||
| 		public static function clear() { | ||||
| 			self::$files        = array(); | ||||
| 			self::$directories  = array(); | ||||
| 		} | ||||
| 		 | ||||
| 		public static function getDirectories() { | ||||
| 			return self::$dirs; | ||||
| 		} | ||||
| 		 | ||||
| 		public static function getFiles() { | ||||
| 			return self::$files; | ||||
| 		} | ||||
| 		 | ||||
| 		public static function addDefaultFilters() { | ||||
| 			self::$directories[] = dirname(dirname(dirname(__FILE__))); | ||||
| 		} | ||||
| 		 | ||||
| 		public static function filter(array $data) { | ||||
| 			foreach ($data as $file => $arr) { | ||||
| 				if (in_array($file, self::$files)) { | ||||
| 					unset($data[$file]); | ||||
| 				} else { | ||||
| 					foreach (self::$directories as $dir) { | ||||
| 						if (strpos($file, $dir) === 0) { | ||||
| 							unset($data[$file]); | ||||
| 							break; | ||||
| 						} | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 			 | ||||
| 			return $data; | ||||
| 		} | ||||
| 		 | ||||
| 	} | ||||
| 
 | ||||
| ?>
 | ||||
							
								
								
									
										49
									
								
								src/TUnit/framework/reporting/CoverageReporter.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								src/TUnit/framework/reporting/CoverageReporter.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,49 @@ | ||||
| <?php | ||||
| 
 | ||||
| 	class CoverageReporter { | ||||
| 		 | ||||
| 		const UNUSED = -1; | ||||
| 		const DEAD   = -2; | ||||
| 		 | ||||
| 		private function __construct() {} | ||||
| 		 | ||||
| 		public static function createConsoleReport(array $coverageData) { | ||||
| 			$coverageData = CoverageFilter::filter($coverageData); | ||||
| 			echo "\nCode coverage information:\n\n"; | ||||
| 			//print_r($coverageData);
 | ||||
| 			foreach ($coverageData as $file => $data) { | ||||
| 				fwrite(STDOUT, $file . "\n"); | ||||
| 				$loc  = 0; | ||||
| 				$dloc = 0; | ||||
| 				$cloc = 0; | ||||
| 				 | ||||
| 				foreach ($data as $line => $unitsCovered) { | ||||
| 					$loc++; | ||||
| 					if ($unitsCovered > 0) { | ||||
| 						$cloc++; | ||||
| 					} else if ($unitsCovered === self::DEAD) { | ||||
| 						$dloc++; | ||||
| 					} | ||||
| 				} | ||||
| 				 | ||||
| 				fwrite(STDOUT, "  Covered:    $cloc\n"); | ||||
| 				fwrite(STDOUT, "  Dead:       $dloc\n"); | ||||
| 				fwrite(STDOUT, "  Executable: $loc (" . round($cloc / $loc * 100, 2) . "%)\n"); | ||||
| 			} | ||||
| 		} | ||||
| 		 | ||||
| 		public static function createXmlReport($file, array $coverageData) { | ||||
| 		 | ||||
| 		} | ||||
| 		 | ||||
| 		public static function createHtmlReport($dir, array $coverageData) { | ||||
| 			if (!is_dir($dir)) { | ||||
| 				throw new TUnitException($dir . ' is not a directory'); | ||||
| 			} | ||||
| 			 | ||||
| 			 | ||||
| 		} | ||||
| 		 | ||||
| 	} | ||||
| 
 | ||||
| ?>
 | ||||
| @ -3,7 +3,7 @@ | ||||
| 	/** | ||||
| 	 * Autoload manifest | ||||
| 	 * | ||||
| 	 * Autogenerated by manifester.php on 2009-06-19 20:50:38 | ||||
| 	 * Autogenerated by manifester.php on 2009-06-27 16:39:48 | ||||
| 	 * | ||||
| 	 * @package TUnit | ||||
| 	 * @version 0.4.0 | ||||
| @ -20,6 +20,8 @@ | ||||
| 		'ConsoleListener'          => 'TUnit/framework/listeners/ConsoleListener.php', | ||||
| 		'ConsoleTestRunner'        => 'TUnit/framework/TestRunners.php', | ||||
| 		'Constraint'               => 'TUnit/framework/constraints/Constraint.php', | ||||
| 		'CoverageFilter'           => 'TUnit/framework/reporting/CoverageFilter.php', | ||||
| 		'CoverageReporter'         => 'TUnit/framework/reporting/CoverageReporter.php', | ||||
| 		'DefaultConstraint'        => 'TUnit/framework/constraints/DefaultConstraint.php', | ||||
| 		'EmptyConstraint'          => 'TUnit/framework/constraints/SimpleConstraints.php', | ||||
| 		'EqualConstraint'          => 'TUnit/framework/constraints/EqualConstraint.php', | ||||
| @ -47,6 +49,7 @@ | ||||
| 		'Product'                  => 'TUnit/util/Product.php', | ||||
| 		'RecursivePhpFileIterator' => 'TUnit/util/PhpFileIterator.php', | ||||
| 		'RecursiveTestIterator'    => 'TUnit/util/RecursiveTestIterator.php', | ||||
| 		'RecursivelyCountable'     => 'TUnit/framework/test/Testable.php', | ||||
| 		'SimpleConstraint'         => 'TUnit/framework/constraints/SimpleConstraints.php', | ||||
| 		'SingleTestResult'         => 'TUnit/framework/result/SingleTestResult.php', | ||||
| 		'TUnitException'           => 'TUnit/framework/exceptions/Exceptions.php', | ||||
|  | ||||
| @ -108,6 +108,8 @@ | ||||
| 						$ref = new ReflectionClass($className); | ||||
| 						if ($ref->isSubClassOf('TestCase')) { | ||||
| 							$tests[] = $ref->newInstance($className); | ||||
| 							//add to filter
 | ||||
| 							CoverageFilter::addFile($file); | ||||
| 						} | ||||
| 						unset($ref); | ||||
| 						 | ||||
|  | ||||
| @ -47,7 +47,10 @@ | ||||
| 	         ->addSwitch(new CliSwitch('help',             'h',  false, null,     'Display this help message (also --usage)')) | ||||
| 	         ->addSwitch(new CliSwitch('usage',            null, false, null,     'Display this help message')) | ||||
| 	         ->addSwitch(new CliSwitch('recursive',        null, false, null,     'Recurse into subdirectories')) | ||||
| 	         ->addSwitch(new CliSwitch('bootstrap', 'b',  false, 'file',   'File to include before tests are run')); | ||||
| 	         ->addSwitch(new CliSwitch('bootstrap',        'b',  false, 'file',   'File to include before tests are run')) | ||||
| 	         ->addSwitch(new CliSwitch('coverage-xml',     null, false, 'file',   'Generate code coverage report in XML clover format (requires xdebug)')) | ||||
| 	         ->addSwitch(new CliSwitch('coverage-html',    null, false, 'dir',    'Generate code coverage report in HTML (requires xdebug, optionally uses ezComponents)')) | ||||
| 	         ->addSwitch(new CliSwitch('coverage-console', null, false, null,     'Generate code coverage report suitable for console viewing')); | ||||
| 
 | ||||
| 	array_shift($argv); | ||||
| 	$args = Cli::parseArgs($argv, $switches); | ||||
| @ -65,30 +68,21 @@ | ||||
| 		exit(1); | ||||
| 	} | ||||
| 	 | ||||
| 	$runner = new ConsoleTestRunner(); | ||||
| 	$runner->setOptions($options); | ||||
| 	 | ||||
| 	//accumulate tests
 | ||||
| 	$tests = TestAccumulator::getTests($args, isset($options['recursive'])); | ||||
| 	$tests = TestAccumulator::getTests($args, $runner->getOption('recursive')); | ||||
| 	 | ||||
| 	if (empty($tests)) { | ||||
| 		fwrite(STDERR, 'Found no TestCase subclasses in the given files'); | ||||
| 		exit(1); | ||||
| 	} | ||||
| 	 | ||||
| 	if (isset($options['bootstrap'])) { | ||||
| 		if (!is_file($options['bootstrap'])) { | ||||
| 			fwrite(STDERR, 'Bootstrap file given (' . $options['bootstrap'] . ') is not a file'); | ||||
| 			exit(1); | ||||
| 		} | ||||
| 	$runner->addTests($tests) | ||||
| 	       ->addListener(new ConsoleListener()) | ||||
| 	       ->run(); | ||||
| 	 | ||||
| 		require_once $options['bootstrap']; | ||||
| 	} | ||||
| 	 | ||||
| 	$runner = new ConsoleTestRunner( | ||||
| 		array(new TestSuite('Main Test Suite', $tests)), | ||||
| 		array(new ConsoleListener()), | ||||
| 		$options | ||||
| 	); | ||||
| 	 | ||||
| 	$runner->run(); | ||||
| 	exit(0); | ||||
| 
 | ||||
| ?>
 | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user