From 3f77ee1b7ad64384965961a60371f887511edf1c Mon Sep 17 00:00:00 2001 From: Alexei Kornienko Date: Mon, 18 Jun 2012 04:49:51 +0900 Subject: [PATCH 1/2] Added filters system This filters can be used to iterate over TestSuite allowing to simplify group/exclude-group options handling --- PHPUnit/Autoload.php | 5 ++++ .../Filters/ExcludeGroupFilterIterator.php | 14 +++++++++ .../Util/Filters/FilterIteratorFactory.php | 30 +++++++++++++++++++ PHPUnit/Util/Filters/GroupFilterIterator.php | 24 +++++++++++++++ .../Filters/IncludeGroupFilterIterator.php | 14 +++++++++ PHPUnit/Util/Filters/TestFilterIterator.php | 30 +++++++++++++++++++ 6 files changed, 117 insertions(+) create mode 100644 PHPUnit/Util/Filters/ExcludeGroupFilterIterator.php create mode 100644 PHPUnit/Util/Filters/FilterIteratorFactory.php create mode 100644 PHPUnit/Util/Filters/GroupFilterIterator.php create mode 100644 PHPUnit/Util/Filters/IncludeGroupFilterIterator.php create mode 100644 PHPUnit/Util/Filters/TestFilterIterator.php diff --git a/PHPUnit/Autoload.php b/PHPUnit/Autoload.php index 495c40adda3..18e5f84d740 100644 --- a/PHPUnit/Autoload.php +++ b/PHPUnit/Autoload.php @@ -157,6 +157,11 @@ function ($class) 'phpunit_util_fileloader' => '/Util/Fileloader.php', 'phpunit_util_filesystem' => '/Util/Filesystem.php', 'phpunit_util_filter' => '/Util/Filter.php', + 'phpunit_util_filters_excludegroupfilteriterator' => '/Util/Filters/ExcludeGroupFilterIterator.php', + 'phpunit_util_filters_filteriteratorfactory' => '/Util/Filters/FilterIteratorFactory.php', + 'phpunit_util_filters_groupfilteriterator' => '/Util/Filters/GroupFilterIterator.php', + 'phpunit_util_filters_includegroupfilteriterator' => '/Util/Filters/IncludeGroupFilterIterator.php', + 'phpunit_util_filters_testfilteriterator' => '/Util/Filters/TestFilterIterator.php', 'phpunit_util_getopt' => '/Util/Getopt.php', 'phpunit_util_globalstate' => '/Util/GlobalState.php', 'phpunit_util_invalidargumenthelper' => '/Util/InvalidArgumentHelper.php', diff --git a/PHPUnit/Util/Filters/ExcludeGroupFilterIterator.php b/PHPUnit/Util/Filters/ExcludeGroupFilterIterator.php new file mode 100644 index 00000000000..67507699b39 --- /dev/null +++ b/PHPUnit/Util/Filters/ExcludeGroupFilterIterator.php @@ -0,0 +1,14 @@ +getInnerIterator()->current(); + if ($test instanceof PHPUnit_Framework_TestSuite) { + return true; + } + return !in_array(spl_object_hash($test), $this->groupTests); + } + +} \ No newline at end of file diff --git a/PHPUnit/Util/Filters/FilterIteratorFactory.php b/PHPUnit/Util/Filters/FilterIteratorFactory.php new file mode 100644 index 00000000000..74a30ef8780 --- /dev/null +++ b/PHPUnit/Util/Filters/FilterIteratorFactory.php @@ -0,0 +1,30 @@ +isSubclassOf('RecursiveFilterIterator')) { + throw new InvalidArgumentException( + 'Class "' . $filter->name . '" does not extend FilterIterator.' + ); + } + $this->filters[] = array($filter, $args); + } + + /** + * @return FilterIterator + */ + public function factory(Iterator $iterator, PHPUnit_Framework_TestSuite $parent) { + foreach ($this->filters as $filter) { + list($class, $args) = $filter; + /* @var $class ReflectionClass */ + $iterator = $class->newInstance($iterator, $args, $parent); + } + + return $iterator; + } + +} \ No newline at end of file diff --git a/PHPUnit/Util/Filters/GroupFilterIterator.php b/PHPUnit/Util/Filters/GroupFilterIterator.php new file mode 100644 index 00000000000..bfd986035b2 --- /dev/null +++ b/PHPUnit/Util/Filters/GroupFilterIterator.php @@ -0,0 +1,24 @@ +getGroupDetails() as $group => $tests) { + if (in_array($group, $groups)) { + $testHashes = array_map(function($test) { + return spl_object_hash($test); + }, $tests); + $this->groupTests += $testHashes; + } + } + } + +} \ No newline at end of file diff --git a/PHPUnit/Util/Filters/IncludeGroupFilterIterator.php b/PHPUnit/Util/Filters/IncludeGroupFilterIterator.php new file mode 100644 index 00000000000..1292d889f5b --- /dev/null +++ b/PHPUnit/Util/Filters/IncludeGroupFilterIterator.php @@ -0,0 +1,14 @@ +getInnerIterator()->current(); + if ($test instanceof PHPUnit_Framework_TestSuite) { + return true; + } + return in_array(spl_object_hash($test), $this->groupTests); + } + +} \ No newline at end of file diff --git a/PHPUnit/Util/Filters/TestFilterIterator.php b/PHPUnit/Util/Filters/TestFilterIterator.php new file mode 100644 index 00000000000..c57363d5dca --- /dev/null +++ b/PHPUnit/Util/Filters/TestFilterIterator.php @@ -0,0 +1,30 @@ +filter = $filter; + } + + public function accept() { + $test = $this->getInnerIterator()->current(); + if ($test instanceof PHPUnit_Framework_TestSuite) { + return true; + } + + $tmp = PHPUnit_Util_Test::describe($test, FALSE); + + if ($tmp[0] != '') { + $name = join('::', $tmp); + } else { + $name = $tmp[1]; + } + + return preg_match($this->filter, $name); + } + +} \ No newline at end of file From 92a8006cab9825bd428aa9401386b73be97975a5 Mon Sep 17 00:00:00 2001 From: Alexei Kornienko Date: Mon, 18 Jun 2012 04:53:51 +0900 Subject: [PATCH 2/2] Refactor how tests are run -added Filters injection in TestRunner -refactor TestSuite::run --- PHPUnit/Extensions/RepeatedTest.php | 16 +-- PHPUnit/Framework/TestSuite.php | 198 ++++++++++++---------------- PHPUnit/TextUI/TestRunner.php | 46 +++++-- 3 files changed, 123 insertions(+), 137 deletions(-) diff --git a/PHPUnit/Extensions/RepeatedTest.php b/PHPUnit/Extensions/RepeatedTest.php index ab661a688e6..f12c07afaeb 100644 --- a/PHPUnit/Extensions/RepeatedTest.php +++ b/PHPUnit/Extensions/RepeatedTest.php @@ -90,7 +90,7 @@ class PHPUnit_Extensions_RepeatedTest extends PHPUnit_Extensions_TestDecorator * @param boolean $processIsolation * @throws PHPUnit_Framework_Exception */ - public function __construct(PHPUnit_Framework_Test $test, $timesRepeat = 1, $filter = FALSE, array $groups = array(), array $excludeGroups = array(), $processIsolation = FALSE) + public function __construct(PHPUnit_Framework_Test $test, $timesRepeat = 1, $processIsolation = FALSE) { parent::__construct($test); @@ -103,9 +103,6 @@ public function __construct(PHPUnit_Framework_Test $test, $timesRepeat = 1, $fil ); } - $this->filter = $filter; - $this->groups = $groups; - $this->excludeGroups = $excludeGroups; $this->processIsolation = $processIsolation; } @@ -138,16 +135,9 @@ public function run(PHPUnit_Framework_TestResult $result = NULL) for ($i = 0; $i < $this->timesRepeat && !$result->shouldStop(); $i++) { //@codingStandardsIgnoreEnd if ($this->test instanceof PHPUnit_Framework_TestSuite) { - $this->test->run( - $result, - $this->filter, - $this->groups, - $this->excludeGroups, - $this->processIsolation - ); - } else { - $this->test->run($result); + $this->test->setRunTestInSeparateProcess($this->processIsolation); } + $this->test->run($result); } return $result; diff --git a/PHPUnit/Framework/TestSuite.php b/PHPUnit/Framework/TestSuite.php index a9a8488a59a..24e0f991d65 100644 --- a/PHPUnit/Framework/TestSuite.php +++ b/PHPUnit/Framework/TestSuite.php @@ -95,6 +95,8 @@ class PHPUnit_Framework_TestSuite implements PHPUnit_Framework_Test, PHPUnit_Fra */ protected $backupStaticAttributes = NULL; + protected $runTestInSeparateProcess = FALSE; + /** * The name of the test suite. * @@ -128,6 +130,11 @@ class PHPUnit_Framework_TestSuite implements PHPUnit_Framework_Test, PHPUnit_Fra */ protected $testCase = FALSE; + /** + * @var PHPUnit_Util_Filters_FilterIteratorFactory + */ + private $iteratorFilter = NULL; + /** * Constructs a new TestSuite: * @@ -424,17 +431,13 @@ public function addTestFiles($filenames) */ public function count() { - if ($this->numTests > -1) { - return $this->numTests; - } + $numTests = 0; - $this->numTests = 0; - - foreach ($this->tests as $test) { - $this->numTests += count($test); + foreach ($this as $test) { + $numTests += count($test); } - return $this->numTests; + return $numTests; } /** @@ -613,6 +616,10 @@ public function getGroups() return array_keys($this->groups); } + public function getGroupDetails() { + return $this->groups; + } + /** * Runs the tests and collects their result in a TestResult. * @@ -624,149 +631,99 @@ public function getGroups() * @return PHPUnit_Framework_TestResult * @throws PHPUnit_Framework_Exception */ - public function run(PHPUnit_Framework_TestResult $result = NULL, $filter = FALSE, array $groups = array(), array $excludeGroups = array(), $processIsolation = FALSE) + public function run(PHPUnit_Framework_TestResult $result = NULL) { if ($result === NULL) { $result = $this->createResult(); } + if (count($this) == 0) { + return $result; + } + $result->startTestSuite($this); - $doSetup = TRUE; + try { + $this->setUp(); - if (!empty($excludeGroups)) { - foreach ($this->groups as $_group => $_tests) { - if (in_array($_group, $excludeGroups) && - count($_tests) == count($this->tests)) { - $doSetup = FALSE; - } + if ($this->testCase && + // Some extensions use test names that are not classes; + // The method_exists() triggers an autoload call that causes issues with die()ing autoloaders. + class_exists($this->name, false) && + method_exists($this->name, 'setUpBeforeClass')) { + call_user_func(array($this->name, 'setUpBeforeClass')); } } - if ($doSetup) { - try { - $this->setUp(); + catch (PHPUnit_Framework_SkippedTestSuiteError $e) { + $numTests = count($this); - if ($this->testCase && - // Some extensions use test names that are not classes; - // The method_exists() triggers an autoload call that causes issues with die()ing autoloaders. - class_exists($this->name, false) && - method_exists($this->name, 'setUpBeforeClass')) { - call_user_func(array($this->name, 'setUpBeforeClass')); - } + for ($i = 0; $i < $numTests; $i++) { + $result->addFailure($this, $e, 0); } - catch (PHPUnit_Framework_SkippedTestSuiteError $e) { - $numTests = count($this); - - for ($i = 0; $i < $numTests; $i++) { - $result->addFailure($this, $e, 0); - } - - return $result; - } - - catch (Exception $e) { - $numTests = count($this); - - for ($i = 0; $i < $numTests; $i++) { - $result->addError($this, $e, 0); - } - - return $result; - } + return $result; } - if (empty($groups)) { - $tests = $this->tests; - } else { - $tests = new SplObjectStorage; + catch (Exception $e) { + $numTests = count($this); - foreach ($groups as $group) { - if (isset($this->groups[$group])) { - foreach ($this->groups[$group] as $test) { - $tests->attach($test); - } - } + for ($i = 0; $i < $numTests; $i++) { + $result->addError($this, $e, 0); } + + return $result; } - foreach ($tests as $test) { + foreach ($this as $test) { if ($result->shouldStop()) { break; } - if ($test instanceof PHPUnit_Framework_TestSuite) { + if ($test instanceof PHPUnit_Framework_TestCase || + $test instanceof PHPUnit_Framework_TestSuite) { $test->setBackupGlobals($this->backupGlobals); $test->setBackupStaticAttributes($this->backupStaticAttributes); + $test->setRunTestInSeparateProcess($this->runTestInSeparateProcess); + } - $test->run( - $result, $filter, $groups, $excludeGroups, $processIsolation - ); - } else { - $runTest = TRUE; - - if ($filter !== FALSE ) { - $tmp = PHPUnit_Util_Test::describe($test, FALSE); - - if ($tmp[0] != '') { - $name = join('::', $tmp); - } else { - $name = $tmp[1]; - } - - if (preg_match($filter, $name) == 0) { - $runTest = FALSE; - } - } - - if ($runTest && !empty($excludeGroups)) { - foreach ($this->groups as $_group => $_tests) { - if (in_array($_group, $excludeGroups)) { - foreach ($_tests as $_test) { - if ($test === $_test) { - $runTest = FALSE; - break 2; - } - } - } - } - } - - if ($runTest) { - if ($test instanceof PHPUnit_Framework_TestCase) { - $test->setBackupGlobals($this->backupGlobals); - $test->setBackupStaticAttributes( - $this->backupStaticAttributes - ); - $test->setRunTestInSeparateProcess($processIsolation); - } - $this->runTest($test, $result); - } - } + $test->run($result); } - if ($doSetup) { - if ($this->testCase && - // Some extensions use test names that are not classes; - // The method_exists() triggers an autoload call that causes issues with die()ing autoloaders. - class_exists($this->name, false) && - method_exists($this->name, 'tearDownAfterClass')) { - call_user_func(array($this->name, 'tearDownAfterClass')); - } - - $this->tearDown(); + if ($this->testCase && + // Some extensions use test names that are not classes; + // The method_exists() triggers an autoload call that causes issues with die()ing autoloaders. + class_exists($this->name, false) && + method_exists($this->name, 'tearDownAfterClass')) { + call_user_func(array($this->name, 'tearDownAfterClass')); } + $this->tearDown(); + $result->endTestSuite($this); return $result; } + /** + * @param boolean $runTestInSeparateProcess + * @throws InvalidArgumentException + * @since Method available since Release 3.7.0 + */ + public function setRunTestInSeparateProcess($runTestInSeparateProcess) + { + if (is_bool($runTestInSeparateProcess)) { + $this->runTestInSeparateProcess = $runTestInSeparateProcess; + } else { + throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'boolean'); + } + } + /** * Runs a test. * + * @deprecated * @param PHPUnit_Framework_Test $test * @param PHPUnit_Framework_TestResult $result */ @@ -923,9 +880,22 @@ public function setBackupStaticAttributes($backupStaticAttributes) */ public function getIterator() { - return new RecursiveIteratorIterator( - new PHPUnit_Util_TestSuiteIterator($this) - ); + $iterator = new PHPUnit_Util_TestSuiteIterator($this); + + if ($this->iteratorFilter !== NULL) { + $iterator = $this->iteratorFilter->factory($iterator, $this); + } + + return $iterator; + } + + public function injectFilter(PHPUnit_Util_Filters_FilterIteratorFactory $filter) { + $this->iteratorFilter = $filter; + foreach ($this as $test) { + if ($test instanceof PHPUnit_Framework_TestSuite) { + $test->injectFilter($filter); + } + } } /** diff --git a/PHPUnit/TextUI/TestRunner.php b/PHPUnit/TextUI/TestRunner.php index 9a7669e565a..44fa1d4cce3 100644 --- a/PHPUnit/TextUI/TestRunner.php +++ b/PHPUnit/TextUI/TestRunner.php @@ -129,6 +129,38 @@ protected function createTestResult() return new PHPUnit_Framework_TestResult; } + private function processSuiteFilters(PHPUnit_Framework_TestSuite $suite, array $arguments) { + if (!$arguments['filter'] && + empty($arguments['groups']) && + empty($arguments['excludeGroups'])) { + return; + } + + $filterFactory = new PHPUnit_Util_Filters_FilterIteratorFactory(); + + if(!empty($arguments['excludeGroups'])) { + $filterFactory->addFilter( + new ReflectionClass('PHPUnit_Util_Filters_ExcludeGroupFilterIterator'), + $arguments['excludeGroups'] + ); + } + + if(!empty($arguments['groups'])) { + $filterFactory->addFilter( + new ReflectionClass('PHPUnit_Util_Filters_IncludeGroupFilterIterator'), + $arguments['groups'] + ); + } + + if($arguments['filter']) { + $filterFactory->addFilter( + new ReflectionClass('PHPUnit_Util_Filters_TestFilterIterator'), + $arguments['filter'] + ); + } + $suite->injectFilter($filterFactory); + } + /** * @param PHPUnit_Framework_Test $suite * @param array $arguments @@ -138,6 +170,8 @@ public function doRun(PHPUnit_Framework_Test $suite, array $arguments = array()) { $this->handleConfiguration($arguments); + $this->processSuiteFilters($suite, $arguments); + if (isset($arguments['bootstrap'])) { $GLOBALS['__PHPUNIT_BOOTSTRAP'] = $arguments['bootstrap']; } @@ -154,9 +188,6 @@ public function doRun(PHPUnit_Framework_Test $suite, array $arguments = array()) $suite = new PHPUnit_Extensions_RepeatedTest( $suite, $arguments['repeat'], - $arguments['filter'], - $arguments['groups'], - $arguments['excludeGroups'], $arguments['processIsolation'] ); } @@ -341,13 +372,8 @@ public function doRun(PHPUnit_Framework_Test $suite, array $arguments = array()) ); } - $suite->run( - $result, - $arguments['filter'], - $arguments['groups'], - $arguments['excludeGroups'], - $arguments['processIsolation'] - ); + $suite->setRunTestInSeparateProcess($arguments['processIsolation']); + $suite->run($result); unset($suite); $result->flushListeners();