├── .gitignore ├── .travis.install.sh ├── .travis.yml ├── README.md ├── composer.json ├── phpunit-integration.xml.dist ├── phpunit-unit.xml.dist ├── src └── PHPUnit │ └── Runner │ └── CleverAndSmart │ ├── Exception │ ├── PropertyReflectionException.php │ ├── RuntimeException.php │ └── StorageException.php │ ├── PriorityQueue.php │ ├── PrioritySorter.php │ ├── Run.php │ ├── SegmentedQueue.php │ ├── Storage │ ├── MockedStorage.php │ ├── Sqlite3Storage.php │ └── StorageInterface.php │ ├── TestListener.php │ └── Util.php └── tests └── PHPUnit └── Tests └── Runner └── CleverAndSmart ├── Benchmark ├── RunSuiteEvent.php └── bootstrap.php ├── Integration ├── fixture │ ├── DependentTest.php │ ├── FatalErrorTest.php │ ├── Nested │ │ ├── DataTest.php │ │ └── NestedTest.php │ ├── SimpleTest.php │ └── SkipTest.php ├── phpunit-error-failure.xml ├── phpunit-error.xml ├── phpunit-failure.xml ├── phpunit-fatal.xml ├── phpunit-skip.xml ├── phpunit-success.xml └── tests │ └── IntegrationTest.php └── Unit ├── PrioritySorterTest.php ├── SegmentedQueueTest.php ├── Storage └── Sqlite3StorageTest.php └── UtilTest.php /.gitignore: -------------------------------------------------------------------------------- 1 | /vendor/ 2 | /tests/PHPUnit/Tests/Runner/CleverAndSmart/Integration/result-*.xml 3 | /tests/PHPUnit/Tests/Runner/CleverAndSmart/Integration/.phpunit-cas.db 4 | /.phpunit-cas.db 5 | /composer.lock 6 | -------------------------------------------------------------------------------- /.travis.install.sh: -------------------------------------------------------------------------------- 1 | set -x 2 | if [ "$TRAVIS_PHP_VERSION" = 'hhvm' ]; then 3 | export DEBIAN_FRONTEND=noninteractive 4 | sudo apt-get purge -q -y hhvm 5 | sudo add-apt-repository -y ppa:mapnik/boost 6 | sudo apt-get update -q -y 7 | sudo apt-get install -q -y hhvm-nightly 8 | hhvm --version 9 | 10 | curl -sS https://getcomposer.org/installer > composer-installer.php 11 | hhvm composer-installer.php 12 | hhvm -v ResourceLimit.SocketDefaultTimeout=30 -v Http.SlowQueryThreshold=30000 composer.phar require phpunit/phpunit=$PHPUNIT_VERSION 13 | hhvm -v ResourceLimit.SocketDefaultTimeout=30 -v Http.SlowQueryThreshold=30000 composer.phar install 14 | else 15 | composer require phpunit/phpunit=$PHPUNIT_VERSION 16 | composer install 17 | fi 18 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | 3 | sudo: false 4 | 5 | php: 6 | - 5.3 7 | - 5.4 8 | - 5.5 9 | - 5.6 10 | - hhvm 11 | 12 | env: 13 | - PHPUNIT_VERSION="3.7.*" 14 | - PHPUNIT_VERSION="3.8.*" 15 | - PHPUNIT_VERSION="4.0.*" 16 | - PHPUNIT_VERSION="4.1.*" 17 | - PHPUNIT_VERSION="4.2.*" 18 | - PHPUNIT_VERSION="4.3.*" 19 | - PHPUNIT_VERSION="4.4.*" 20 | - PHPUNIT_VERSION="4.5.*" 21 | - PHPUNIT_VERSION="dev-master" 22 | 23 | matrix: 24 | allow_failures: 25 | - php: hhvm 26 | 27 | before_script: 28 | - ./.travis.install.sh 29 | 30 | script: 31 | - vendor/bin/phpunit --configuration phpunit-unit.xml.dist 32 | - vendor/bin/phpunit --configuration phpunit-integration.xml.dist 33 | - vendor/bin/athletic --path tests/PHPUnit/Tests/Runner/CleverAndSmart/Benchmark --bootstrap tests/PHPUnit/Tests/Runner/CleverAndSmart/Benchmark/bootstrap.php --formatter GroupedFormatter 34 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Clever Test Runner for PHPUnit 2 | [![Build Status](https://secure.travis-ci.org/lstrojny/phpunit-clever-and-smart.svg)](http://travis-ci.org/lstrojny/phpunit-clever-and-smart) [![Dependency Status](https://www.versioneye.com/user/projects/542d5df4fc3f5cd7000001fb/badge.svg?style=flat)](https://www.versioneye.com/user/projects/542d5df4fc3f5cd7000001fb) [![Average time to resolve an issue](http://isitmaintained.com/badge/resolution/lstrojny/phpunit-clever-and-smart.svg)](http://isitmaintained.com/project/lstrojny/phpunit-clever-and-smart "Average time to resolve an issue") [![Percentage of issues still open](http://isitmaintained.com/badge/open/lstrojny/phpunit-clever-and-smart.svg)](http://isitmaintained.com/project/lstrojny/phpunit-clever-and-smart "Percentage of issues still open") 3 | 4 | ## Mission 5 | Enable fast feedback cycles by storing test case results in a database and reorder tests on consecutive runs in the 6 | following order: 7 | 1. Failures and errors 8 | 2. So far unrecorded tests 9 | 3. Remaining tests by execution time in ascendant order (fastest first) 10 | 11 | It’s probably not yet very stable but try it out. 12 | 13 | ### What it does 14 | 15 | Run a test suite once with errors 16 | 17 | ``` 18 | PHPUnit 3.7.28 by Sebastian Bergmann. 19 | 20 | .............................................FSFS.............. 63 / 280 ( 22%) 21 | ............................................................... 126 / 280 ( 45%) 22 | ............................................................... 189 / 280 ( 67%) 23 | ............................................................... 252 / 280 ( 90%) 24 | ......................... 25 | ``` 26 | 27 | Rerun that test suite and see how the previous failing tests have been sorted to the beginning of the test run: 28 | 29 | 30 | ``` 31 | PHPUnit 3.7.28 by Sebastian Bergmann. 32 | 33 | FSFS........................................................... 63 / 280 ( 22%) 34 | ............................................................... 126 / 280 ( 45%) 35 | ............................................................... 189 / 280 ( 67%) 36 | ............................................................... 252 / 280 ( 90%) 37 | ......................... 38 | ``` 39 | 40 | ## Installation 41 | 42 | add the following line to your projects' composer.json `require-dev` section. 43 | 44 | ```json 45 | "lstrojny/phpunit-clever-and-smart": "0.*" 46 | ``` 47 | 48 | ## Configuration 49 | To play around with it, add this to your `phpunit.xml(.dist)` 50 | 51 | ```xml 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | ``` 60 | 61 | you might alter the location of the sqlite storage file, by passing a path to the Sqlite3Storage class: 62 | 63 | ```xml 64 | 65 | 66 | 67 | 68 | 69 | /my/path/to/.phpunit-cas.db 70 | 71 | 72 | 73 | 74 | 75 | ``` 76 | 77 | ## Roadmap 78 | 79 | - Test it with as many test suites as possible 80 | - Stabilize 81 | - Merge into PHPUnit core 82 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "lstrojny/phpunit-clever-and-smart", 3 | "description": "A clever test runner for PHPUnit", 4 | "license": "MIT", 5 | "minimum-stability": "dev", 6 | "require": { 7 | "phpunit/phpunit": "*", 8 | "ext-sqlite3": "*" 9 | }, 10 | "require-dev": { 11 | "symfony/process": "2.*", 12 | "athletic/athletic": "~0.1" 13 | }, 14 | "authors": [ 15 | { 16 | "name": "Lars Strojny", 17 | "email": "lars@strojny.net" 18 | } 19 | ], 20 | "autoload": { 21 | "psr-0": { 22 | "PHPUnit\\Runner\\CleverAndSmart": "src/" 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /phpunit-integration.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | ./tests/PHPUnit/Tests/Runner/CleverAndSmart/Integration/tests/ 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /phpunit-unit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | ./tests/PHPUnit/Tests/Runner/CleverAndSmart/Unit 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /src/PHPUnit/Runner/CleverAndSmart/Exception/PropertyReflectionException.php: -------------------------------------------------------------------------------- 1 | errors = $errors; 22 | $this->timings = $timings; 23 | } 24 | 25 | public function sort(TestSuite $suite) 26 | { 27 | $this->sortTestSuite($suite); 28 | } 29 | 30 | private function sortTestSuite(TestSuite $suite) 31 | { 32 | $tests = $suite->tests(); 33 | 34 | $testsOrderResult = array(static::SORT_NONE, null); 35 | 36 | foreach ($tests as $test) { 37 | if ($test instanceof TestCase && Util::getInvisibleProperty($test, 'dependencies', 'hasDependencies')) { 38 | return $testsOrderResult; 39 | } 40 | } 41 | 42 | $orderedTests = new SegmentedQueue($tests); 43 | foreach ($tests as $position => $test) { 44 | list($testOrderResult, $time) = $this->sortTest($test, $position, $orderedTests); 45 | if ($testsOrderResult[0] < $testOrderResult) { 46 | $testsOrderResult = array($testOrderResult, $time); 47 | } 48 | } 49 | 50 | $groups = Util::getInvisibleProperty($suite, 'groups', 'getGroupDetails'); 51 | $groupsOrderResult = array(static::SORT_NONE, null); 52 | foreach ($groups as $groupName => $group) { 53 | 54 | $groupOrderResult = array(static::SORT_NONE, null); 55 | $orderedGroup = new SegmentedQueue($group); 56 | foreach ($group as $position => $test) { 57 | list($testOrderResult, $time) = $this->sortTest($test, $position, $orderedGroup); 58 | if ($groupOrderResult[0] < $testOrderResult) { 59 | $groupOrderResult = array($testOrderResult, $time); 60 | } 61 | } 62 | 63 | if ($groupOrderResult[0] > static::SORT_NONE) { 64 | $groups[$groupName] = iterator_to_array($orderedGroup); 65 | 66 | if ($groupsOrderResult[0] < $groupOrderResult[0]) { 67 | $groupsOrderResult = $groupOrderResult; 68 | } 69 | } 70 | } 71 | 72 | if ($testsOrderResult[0] > static::SORT_NONE) { 73 | Util::setInvisibleProperty($suite, 'tests', iterator_to_array($orderedTests), 'setTests'); 74 | } 75 | 76 | if ($groupsOrderResult) { 77 | Util::setInvisibleProperty($suite, 'groups', $groups, 'setGroupDetails'); 78 | } 79 | 80 | return $testsOrderResult[0] > $groupsOrderResult[0] ? $testsOrderResult : $groupsOrderResult; 81 | } 82 | 83 | private function sortTest($test, $position, SegmentedQueue $orderedTests) 84 | { 85 | if ($test instanceof TestSuite) { 86 | 87 | list($result, $time) = $this->sortTestSuite($test); 88 | 89 | if ($result === static::SORT_ERROR) { 90 | 91 | $orderedTests->unknown[$position] = null; 92 | $orderedTests->errors->push($test); 93 | 94 | } elseif ($result === static::SORT_TIMING) { 95 | 96 | $orderedTests->unknown[$position] = null; 97 | $orderedTests->timed->insert($test, $time); 98 | 99 | } 100 | 101 | return array($result, $time); 102 | } 103 | 104 | if ($test instanceof TestCase) { 105 | 106 | if ($this->isError($test)) { 107 | 108 | $orderedTests->unknown[$position] = null; 109 | $orderedTests->errors->push($test); 110 | 111 | return array(static::SORT_ERROR, null); 112 | } 113 | 114 | if ($time = $this->getTime($test)) { 115 | 116 | $orderedTests->unknown[$position] = null; 117 | $orderedTests->timed->insert($test, $time); 118 | 119 | return array(static::SORT_TIMING, $time); 120 | } 121 | } 122 | 123 | return array(static::SORT_NONE, null); 124 | } 125 | 126 | private function getTime(TestCase $test) 127 | { 128 | $name = $test->getName(); 129 | $class = get_class($test); 130 | 131 | foreach ($this->timings as $timing) { 132 | if ($timing['class'] === $class && $timing['test'] === $name) { 133 | return $timing['time']; 134 | } 135 | } 136 | } 137 | 138 | private function isError(TestCase $test) 139 | { 140 | return in_array(array('class' => get_class($test), 'test' => $test->getName()), $this->errors); 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /src/PHPUnit/Runner/CleverAndSmart/Run.php: -------------------------------------------------------------------------------- 1 | runId = $runId ?: Util::createRunId(); 17 | $this->ranAt = $ranAt ?: microtime(true); 18 | } 19 | 20 | /** 21 | * @return string 22 | */ 23 | public function getRunIdentifier() 24 | { 25 | return $this->runId; 26 | } 27 | 28 | /** 29 | * @return float 30 | */ 31 | public function getRanAt() 32 | { 33 | return $this->ranAt; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/PHPUnit/Runner/CleverAndSmart/SegmentedQueue.php: -------------------------------------------------------------------------------- 1 | unknown = new SplQueue(); 23 | array_map(array($this->unknown, 'push'), $values); 24 | $this->errors = new SplQueue(); 25 | $this->timed = new PriorityQueue(); 26 | } 27 | 28 | public function getIterator() 29 | { 30 | return new ArrayIterator( 31 | array_values( 32 | array_filter( 33 | array_merge( 34 | iterator_to_array($this->errors), 35 | iterator_to_array($this->unknown), 36 | iterator_to_array($this->timed) 37 | ) 38 | ) 39 | ) 40 | ); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/PHPUnit/Runner/CleverAndSmart/Storage/MockedStorage.php: -------------------------------------------------------------------------------- 1 | db = new SQLite3($fileName); 25 | 26 | // method introduced in php 5.3.3 27 | if (method_exists($this->db, 'busyTimeout')) { 28 | $this->db->busyTimeout(1000 * 30); 29 | } 30 | 31 | // PRAGMA need to be set outside a transaction 32 | $this->query('PRAGMA foreign_keys = ON'); 33 | $this->query('PRAGMA page_size = 4096'); 34 | $this->query('PRAGMA cache_size = 10000'); 35 | $this->query('PRAGMA synchronous = OFF'); 36 | $this->query('PRAGMA journal_mode = MEMORY'); 37 | 38 | $this->transactional(array($this, 'initDatabaseSchema')); 39 | } 40 | 41 | public function initDatabaseSchema() 42 | { 43 | $this->query( 44 | 'CREATE TABLE IF NOT EXISTS {{prefix}}run ( 45 | run_id INTEGER PRIMARY KEY AUTOINCREMENT, 46 | run_identifier CHAR(128), 47 | run_ran_at DOUBLE 48 | )' 49 | ); 50 | $this->query( 51 | 'CREATE UNIQUE INDEX IF NOT EXISTS 52 | u_{{prefix}}run_identifier_idx ON {{prefix}}run(run_identifier)' 53 | ); 54 | $this->query( 55 | 'CREATE TABLE IF NOT EXISTS {{prefix}}result ( 56 | result_id INTEGER PRIMARY KEY AUTOINCREMENT, 57 | run_id INTEGER, 58 | result_class VARCHAR(1024), 59 | result_test VARCHAR(1024), 60 | result_identifier CHAR(128), 61 | result_state TINYINT, 62 | result_time DOUBLE, 63 | FOREIGN KEY (run_id) REFERENCES {{prefix}}run(run_id) ON DELETE CASCADE 64 | )' 65 | ); 66 | $this->query( 67 | 'CREATE INDEX IF NOT EXISTS 68 | {{prefix}}result_idx ON {{prefix}}result(result_state, result_identifier)' 69 | ); 70 | } 71 | 72 | public function record(Run $run, TestCase $test, $time, $status) 73 | { 74 | $this->transactional(array($this, 'doRecord'), $run, $test, $time, $status); 75 | } 76 | 77 | public function doRecord(Run $run, TestCase $test, $time, $status) 78 | { 79 | $this->insertResult($this->storeRun($run), $test, $time, $status); 80 | } 81 | 82 | public function getRecordings(array $types, $includeTime = true) 83 | { 84 | $query = 'FROM {{prefix}}result 85 | WHERE result_state IN (%s) 86 | GROUP BY result_identifier 87 | ORDER BY COUNT(*) DESC'; 88 | 89 | if ($includeTime) { 90 | $query = 'SELECT result_class AS class, result_test AS test, AVG(result_time) AS time ' . $query; 91 | } else { 92 | $query = 'SELECT result_class AS class, result_test AS test ' . $query; 93 | } 94 | 95 | return $this->select($query, array($types)); 96 | } 97 | 98 | private function transactional($callable /*, ... $args */) 99 | { 100 | $this->query('BEGIN'); 101 | 102 | $args = func_get_args(); 103 | array_shift($args); 104 | 105 | try { 106 | $result = call_user_func_array($callable, $args); 107 | } catch (Exception $e) { 108 | $this->query('ROLLBACK'); 109 | throw $e; 110 | } 111 | 112 | $this->query('COMMIT'); 113 | 114 | return $result; 115 | } 116 | 117 | private function insertResult($runId, TestCase $test, $time, $status) 118 | { 119 | $className = get_class($test); 120 | $testName = $test->getName(); 121 | $identifier = hash('sha512', $className . $testName); 122 | 123 | $this->query( 124 | "INSERT INTO {{prefix}}result 125 | (run_id, result_class, result_test, result_identifier, result_state, result_time) 126 | VALUES 127 | (%d, '%s', '%s', '%s', %d, %F)", 128 | array($runId, $className, $testName, $identifier, $status, $time) 129 | ); 130 | 131 | if ($status < static::STATUS_FAILURE) { 132 | $this->query( 133 | "DELETE FROM {{prefix}}result 134 | WHERE result_identifier = '%s' 135 | AND (SELECT COUNT(*) FROM {{prefix}}result WHERE result_identifier = '%s' AND result_state < %d) >= 4", 136 | array($identifier, $identifier, StorageInterface::STATUS_FAILURE) 137 | ); 138 | } 139 | } 140 | 141 | private function storeRun(Run $run) 142 | { 143 | $this->query( 144 | "INSERT OR IGNORE INTO {{prefix}}run (run_identifier, run_ran_at) VALUES ('%s', '%s')", 145 | array($run->getRunIdentifier(), $run->getRanAt()) 146 | ); 147 | 148 | return $this->selectOne( 149 | "SELECT run_id FROM {{prefix}}run WHERE run_identifier = '%s'", 150 | array($run->getRunIdentifier()) 151 | ); 152 | } 153 | 154 | public function query($query, array $params = array()) 155 | { 156 | $query = $this->prepareQuery($query, $params); 157 | 158 | return $this->doQuery($query); 159 | } 160 | 161 | private function select($query, array $params = array()) 162 | { 163 | $result = $this->query($query, $params); 164 | 165 | $rows = array(); 166 | while ($row = $result->fetchArray(SQLITE3_ASSOC)) { 167 | $rows[] = $row; 168 | } 169 | 170 | return $rows; 171 | } 172 | 173 | private function selectOne($query, array $params) 174 | { 175 | $rows = $this->select($query, $params); 176 | 177 | if ($rows) { 178 | return current(current($rows)); 179 | } 180 | } 181 | 182 | private function doQuery($query) 183 | { 184 | $result = $this->db->query($query); 185 | 186 | if ($this->db->lastErrorCode() > 0) { 187 | throw StorageException::databaseError($this->db->lastErrorMsg(), $this->db->lastErrorCode()); 188 | } 189 | 190 | return $result; 191 | } 192 | 193 | private function prepareQuery($query, array $params) 194 | { 195 | $query = str_replace('{{prefix}}', $this->getPrefix(), $query); 196 | 197 | $escapedParams = array(); 198 | foreach ($params as $param) { 199 | if (is_string($param) || is_object($param)) { 200 | $escapedParams[] = $this->db->escapeString($param); 201 | } elseif (is_array($param)) { 202 | $escapedParams[] = "'" . join("', '", array_map(array($this->db, 'escapeString'), $param)) . "'"; 203 | } else { 204 | $escapedParams[] = $param; 205 | } 206 | } 207 | 208 | return vsprintf($query, $escapedParams); 209 | } 210 | } 211 | -------------------------------------------------------------------------------- /src/PHPUnit/Runner/CleverAndSmart/Storage/StorageInterface.php: -------------------------------------------------------------------------------- 1 | storage = $storage; 32 | $this->run = new Run(); 33 | } 34 | 35 | public function addError(Test $test, Exception $e, $time) 36 | { 37 | $this->storage->record($this->run, $test, $time, StorageInterface::STATUS_ERROR); 38 | } 39 | 40 | public function addFailure(Test $test, AssertionFailedError $e, $time) 41 | { 42 | $this->storage->record($this->run, $test, $time, StorageInterface::STATUS_ERROR); 43 | } 44 | 45 | public function addRiskyTest(Test $test, Exception $e, $time) 46 | { 47 | } 48 | 49 | public function startTestSuite(TestSuite $suite) 50 | { 51 | if ($this->reordered) { 52 | return; 53 | } 54 | $this->reordered = true; 55 | 56 | $this->sort($suite); 57 | 58 | register_shutdown_function(array($this, 'onFatalError')); 59 | if (function_exists('pcntl_signal')) { 60 | pcntl_signal(SIGINT, array($this, 'onCancel')); 61 | } 62 | } 63 | 64 | private function sort(TestSuite $suite) 65 | { 66 | $sorter = new PrioritySorter( 67 | $this->storage->getRecordings( 68 | array( 69 | StorageInterface::STATUS_ERROR, 70 | StorageInterface::STATUS_FAILURE, 71 | StorageInterface::STATUS_CANCEL, 72 | StorageInterface::STATUS_FATAL_ERROR, 73 | StorageInterface::STATUS_SKIPPED, 74 | StorageInterface::STATUS_INCOMPLETE, 75 | ), 76 | false 77 | ), 78 | $this->storage->getRecordings( 79 | array( 80 | StorageInterface::STATUS_PASSED, 81 | ) 82 | ) 83 | ); 84 | $sorter->sort($suite); 85 | } 86 | 87 | public function startTest(Test $test) 88 | { 89 | $this->currentTest = $test; 90 | } 91 | 92 | public function endTest(Test $test, $time) 93 | { 94 | $this->currentTest = null; 95 | if ($test instanceof TestCase && $test->getStatus() === TestRunner::STATUS_PASSED) { 96 | $this->storage->record($this->run, $test, $time, StorageInterface::STATUS_PASSED); 97 | } 98 | } 99 | 100 | public function addIncompleteTest(Test $test, Exception $e, $time) 101 | { 102 | $this->storage->record($this->run, $test, $time, StorageInterface::STATUS_INCOMPLETE); 103 | } 104 | 105 | public function addSkippedTest(Test $test, Exception $e, $time) 106 | { 107 | $this->storage->record($this->run, $test, $time, StorageInterface::STATUS_SKIPPED); 108 | } 109 | 110 | public function endTestSuite(TestSuite $suite) 111 | { 112 | } 113 | 114 | public function onFatalError() 115 | { 116 | $error = error_get_last(); 117 | if (!$error || $error['type'] !== E_ERROR || !$this->currentTest) { 118 | return; 119 | } 120 | 121 | $this->storage->record($this->run, $this->currentTest, 0, StorageInterface::STATUS_FATAL_ERROR); 122 | } 123 | 124 | public function onCancel() 125 | { 126 | if (!$this->currentTest) { 127 | return; 128 | } 129 | 130 | $this->storage->record($this->run, $this->currentTest, 0, StorageInterface::STATUS_CANCEL); 131 | 132 | exit(1); 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /src/PHPUnit/Runner/CleverAndSmart/Util.php: -------------------------------------------------------------------------------- 1 | {$methodName}(); 39 | } 40 | 41 | return static::getPropertyReflection($object, $propertyName)->getValue($object); 42 | } 43 | 44 | /** 45 | * Set an invisible property from an object (private or protected) 46 | * 47 | * @param object $object 48 | * @param string $propertyName 49 | * @param mixed $value 50 | * @param string $methodName 51 | */ 52 | public static function setInvisibleProperty($object, $propertyName, $value, $methodName = null) 53 | { 54 | if (method_exists($object, $methodName)) { 55 | $object->{$methodName}($value); 56 | return; 57 | } 58 | 59 | static::getPropertyReflection($object, $propertyName)->setValue($object, $value); 60 | } 61 | 62 | /** 63 | * Get proper PropertyReflection object 64 | * 65 | * @param object $object 66 | * @param string $propertyName 67 | * @throws ReflectionException 68 | * @return ReflectionProperty 69 | */ 70 | private static function getPropertyReflection($object, $propertyName) 71 | { 72 | $reflected = new ReflectionObject($object); 73 | 74 | $classHierarchy = array(); 75 | 76 | do { 77 | try { 78 | 79 | $property = $reflected->getProperty($propertyName); 80 | $property->setAccessible(true); 81 | 82 | return $property; 83 | 84 | } catch (ReflectionException $e) { 85 | 86 | $classHierarchy[] = $reflected->getName(); 87 | $e = PropertyReflectionException::propertyNotExistsInHierarchy($propertyName, $e, $classHierarchy); 88 | 89 | } 90 | } while ($reflected = $reflected->getParentClass()); 91 | 92 | throw $e; 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /tests/PHPUnit/Tests/Runner/CleverAndSmart/Benchmark/RunSuiteEvent.php: -------------------------------------------------------------------------------- 1 | sutRoot . 'phpunit.xml.dist'; 18 | // plain config to run the suite as-is 19 | copy($org, $this->plainConfig); 20 | 21 | $this->patchPhpunitConfig($this->sqliteConfig, 'PHPUnit\Runner\CleverAndSmart\Storage\Sqlite3Storage'); 22 | $this->patchPhpunitConfig($this->mockedConfig, 'PHPUnit\Runner\CleverAndSmart\Storage\MockedStorage'); 23 | } 24 | 25 | private function patchPhpunitConfig($patchedConfigFilename, $storageClass) { 26 | $org = $this->sutRoot . 'phpunit.xml.dist'; 27 | 28 | $testlistener = ' 29 | 30 | 31 | 32 | 33 | 34 | 35 | '; 36 | 37 | $content = file_get_contents($org); 38 | $content = str_replace('', $testlistener. '', $content); 39 | file_put_contents($patchedConfigFilename, $content); 40 | } 41 | 42 | public function classTearDown() 43 | { 44 | unlink($this->plainConfig); 45 | unlink($this->mockedConfig); 46 | unlink($this->sqliteConfig); 47 | } 48 | 49 | /** 50 | * @baseline 51 | * @iterations 10 52 | */ 53 | public function plainSuite() 54 | { 55 | $this->runSuite($this->plainConfig); 56 | } 57 | 58 | /** 59 | * @iterations 10 60 | */ 61 | public function instrumentedMockedSuite() 62 | { 63 | $this->runSuite($this->mockedConfig); 64 | } 65 | 66 | /** 67 | * @iterations 10 68 | */ 69 | public function instrumentedSqliteSuite() 70 | { 71 | $this->runSuite($this->sqliteConfig); 72 | } 73 | 74 | private function runSuite($config) { 75 | $cmd = 'phpunit --configuration '. $config .' '. $this->sutRoot; 76 | 77 | $process = new Process($cmd); 78 | $process->run(); 79 | 80 | if (!$process->isSuccessful()) { 81 | throw new \RuntimeException($process->getErrorOutput()); 82 | } 83 | 84 | // no need to output the generated phpunit report on success. 85 | // we are only interessted how long it took. 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /tests/PHPUnit/Tests/Runner/CleverAndSmart/Benchmark/bootstrap.php: -------------------------------------------------------------------------------- 1 | assertTrue(PHPUNIT_RUNNER_CLEVERANDSMART_SUCCESS); 13 | } 14 | 15 | /** @depends testSuccess */ 16 | public function testFailure() 17 | { 18 | usleep(2000); 19 | $this->assertFalse(PHPUNIT_RUNNER_CLEVERANDSMART_FAILURE); 20 | } 21 | 22 | /** @depends testFailure */ 23 | public function testError() 24 | { 25 | usleep(3000); 26 | if (PHPUNIT_RUNNER_CLEVERANDSMART_ERROR) { 27 | throw new \Exception(); 28 | } 29 | $this->assertTrue(true); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /tests/PHPUnit/Tests/Runner/CleverAndSmart/Integration/fixture/FatalErrorTest.php: -------------------------------------------------------------------------------- 1 | assertTrue(true); 13 | } 14 | 15 | public function testFatal() 16 | { 17 | if (PHPUNIT_RUNNER_CLEVERANDSMART_FATAL) { 18 | $this->invalidMethod(); 19 | } 20 | $this->assertTrue(true); 21 | } 22 | 23 | public function testSuccess2() 24 | { 25 | usleep(2000); 26 | $this->assertTrue(true); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /tests/PHPUnit/Tests/Runner/CleverAndSmart/Integration/fixture/Nested/DataTest.php: -------------------------------------------------------------------------------- 1 | assertFalse(PHPUNIT_RUNNER_CLEVERANDSMART_FAILURE); 25 | break; 26 | 27 | case 'PHPUNIT_RUNNER_CLEVERANDSMART_ERROR': 28 | usleep(2000); 29 | if (PHPUNIT_RUNNER_CLEVERANDSMART_ERROR) { 30 | throw new \Exception('ERROR'); 31 | } 32 | $this->assertTrue(true); 33 | break; 34 | 35 | case PHPUNIT_RUNNER_CLEVERANDSMART_SUCCESS: 36 | usleep(3000); 37 | $this->assertTrue(PHPUNIT_RUNNER_CLEVERANDSMART_SUCCESS); 38 | break; 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /tests/PHPUnit/Tests/Runner/CleverAndSmart/Integration/fixture/Nested/NestedTest.php: -------------------------------------------------------------------------------- 1 | assertTrue(PHPUNIT_RUNNER_CLEVERANDSMART_SUCCESS); 13 | } 14 | 15 | public function testFailure() 16 | { 17 | usleep(2000); 18 | $this->assertFalse(PHPUNIT_RUNNER_CLEVERANDSMART_FAILURE); 19 | } 20 | 21 | public function testError() 22 | { 23 | usleep(3000); 24 | if (PHPUNIT_RUNNER_CLEVERANDSMART_ERROR) { 25 | throw new \Exception(); 26 | } 27 | $this->assertTrue(true); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /tests/PHPUnit/Tests/Runner/CleverAndSmart/Integration/fixture/SimpleTest.php: -------------------------------------------------------------------------------- 1 | assertTrue(PHPUNIT_RUNNER_CLEVERANDSMART_SUCCESS); 13 | } 14 | 15 | public function testFailure() 16 | { 17 | usleep(20000); 18 | $this->assertFalse(PHPUNIT_RUNNER_CLEVERANDSMART_FAILURE); 19 | } 20 | 21 | public function testError() 22 | { 23 | usleep(30000); 24 | if (PHPUNIT_RUNNER_CLEVERANDSMART_ERROR) { 25 | throw new \Exception(); 26 | } 27 | $this->assertTrue(true); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /tests/PHPUnit/Tests/Runner/CleverAndSmart/Integration/fixture/SkipTest.php: -------------------------------------------------------------------------------- 1 | assertTrue(true); 13 | } 14 | 15 | public function testSkipped() 16 | { 17 | if (PHPUNIT_RUNNER_CLEVERANDSMART_SKIP) { 18 | $this->markTestSkipped(); 19 | } 20 | $this->assertTrue(true); 21 | usleep(2000); 22 | } 23 | 24 | public function testIncomplete() 25 | { 26 | if (PHPUNIT_RUNNER_CLEVERANDSMART_SKIP) { 27 | $this->markTestIncomplete(); 28 | } 29 | $this->assertTrue(true); 30 | usleep(1000); 31 | } 32 | 33 | public function testSuccess2() 34 | { 35 | usleep(5000); 36 | $this->assertTrue(true); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /tests/PHPUnit/Tests/Runner/CleverAndSmart/Integration/phpunit-error-failure.xml: -------------------------------------------------------------------------------- 1 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | ./fixture 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /tests/PHPUnit/Tests/Runner/CleverAndSmart/Integration/phpunit-error.xml: -------------------------------------------------------------------------------- 1 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | ./fixture 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /tests/PHPUnit/Tests/Runner/CleverAndSmart/Integration/phpunit-failure.xml: -------------------------------------------------------------------------------- 1 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | ./fixture 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /tests/PHPUnit/Tests/Runner/CleverAndSmart/Integration/phpunit-fatal.xml: -------------------------------------------------------------------------------- 1 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | ./fixture 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /tests/PHPUnit/Tests/Runner/CleverAndSmart/Integration/phpunit-skip.xml: -------------------------------------------------------------------------------- 1 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | ./fixture 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /tests/PHPUnit/Tests/Runner/CleverAndSmart/Integration/phpunit-success.xml: -------------------------------------------------------------------------------- 1 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | ./fixture 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /tests/PHPUnit/Tests/Runner/CleverAndSmart/Integration/tests/IntegrationTest.php: -------------------------------------------------------------------------------- 1 | reset(); 13 | } 14 | 15 | public function tearDown() 16 | { 17 | $this->reset(); 18 | } 19 | 20 | private function reset() 21 | { 22 | $file = __DIR__ . '/../.phpunit-cas.db'; 23 | if (file_exists($file)) { 24 | unlink($file); 25 | } 26 | } 27 | 28 | public function testSimpleCase_FailuresOnly() 29 | { 30 | $this->runTests('SimpleTest', 'failure', 'failure', false); 31 | $this->runTests('SimpleTest', 'success', 'success', true); 32 | $this->runTests('SimpleTest', 'success', 'retry', true); 33 | 34 | $this->assertTestSuitePosition('failure', 'SimpleTest', array(5, 1)); 35 | $this->assertTestSuiteResult('failure', 'SimpleTest', 'tests', 3); 36 | $this->assertTestSuiteResult('failure', 'SimpleTest', 'failures', 1); 37 | $this->assertTestSuiteResult('failure', 'SimpleTest', 'errors', 0); 38 | $this->assertTestPosition('failure', 'SimpleTest::testSuccess', 1); 39 | $this->assertTestPosition('failure', 'SimpleTest::testFailure', 2); 40 | $this->assertTestPosition('failure', 'SimpleTest::testError', 3); 41 | 42 | $this->assertTestSuitePosition('success', 'SimpleTest', 1); 43 | $this->assertTestSuiteResult('success', 'SimpleTest', 'tests', 3); 44 | $this->assertTestSuiteResult('success', 'SimpleTest', 'failures', 0); 45 | $this->assertTestSuiteResult('success', 'SimpleTest', 'errors', 0); 46 | $this->assertTestPosition('success', 'SimpleTest::testFailure', 1); 47 | $this->assertTestPosition('success', 'SimpleTest::testSuccess', 2); 48 | $this->assertTestPosition('success', 'SimpleTest::testError', 3); 49 | 50 | $this->assertTestSuitePosition('retry', 'SimpleTest', 1); 51 | $this->assertTestSuiteResult('retry', 'SimpleTest', 'tests', 3); 52 | $this->assertTestSuiteResult('retry', 'SimpleTest', 'failures', 0); 53 | $this->assertTestSuiteResult('retry', 'SimpleTest', 'errors', 0); 54 | $this->assertTestPosition('retry', 'SimpleTest::testFailure', 1); 55 | $this->assertTestPosition('retry', 'SimpleTest::testSuccess', 2); 56 | $this->assertTestPosition('retry', 'SimpleTest::testError', 3); 57 | } 58 | 59 | public function testSimpleCase_ErrorsOnly() 60 | { 61 | $this->runTests('SimpleTest', 'error', 'failure', false); 62 | $this->runTests('SimpleTest', 'success', 'success', true); 63 | $this->runTests('SimpleTest', 'success', 'retry', true); 64 | 65 | $this->assertTestSuitePosition('failure', 'SimpleTest', array(5, 1)); 66 | $this->assertTestSuiteResult('failure', 'SimpleTest', 'tests', 3); 67 | $this->assertTestSuiteResult('failure', 'SimpleTest', 'failures', 0); 68 | $this->assertTestSuiteResult('failure', 'SimpleTest', 'errors', 1); 69 | $this->assertTestPosition('failure', 'SimpleTest::testSuccess', 1); 70 | $this->assertTestPosition('failure', 'SimpleTest::testFailure', 2); 71 | $this->assertTestPosition('failure', 'SimpleTest::testError', 3); 72 | 73 | $this->assertTestSuitePosition('success', 'SimpleTest', 1); 74 | $this->assertTestSuiteResult('success', 'SimpleTest', 'tests', 3); 75 | $this->assertTestSuiteResult('success', 'SimpleTest', 'failures', 0); 76 | $this->assertTestSuiteResult('success', 'SimpleTest', 'errors', 0); 77 | $this->assertTestPosition('success', 'SimpleTest::testError', 1); 78 | $this->assertTestPosition('success', 'SimpleTest::testSuccess', 2); 79 | $this->assertTestPosition('success', 'SimpleTest::testFailure', 3); 80 | 81 | $this->assertTestSuitePosition('retry', 'SimpleTest', 1); 82 | $this->assertTestSuiteResult('retry', 'SimpleTest', 'tests', 3); 83 | $this->assertTestSuiteResult('retry', 'SimpleTest', 'failures', 0); 84 | $this->assertTestSuiteResult('retry', 'SimpleTest', 'errors', 0); 85 | $this->assertTestPosition('retry', 'SimpleTest::testError', 1); 86 | $this->assertTestPosition('retry', 'SimpleTest::testSuccess', 2); 87 | $this->assertTestPosition('retry', 'SimpleTest::testFailure', 3); 88 | } 89 | 90 | public function testSimpleCase_ErrorsAndFailures() 91 | { 92 | $this->runTests('SimpleTest', 'error-failure', 'failure', false); 93 | $this->runTests('SimpleTest', 'success', 'success', true); 94 | $this->runTests('SimpleTest', 'success', 'retry', true); 95 | 96 | $this->assertTestSuitePosition('failure', 'SimpleTest', array(5, 1)); 97 | $this->assertTestSuiteResult('failure', 'SimpleTest', 'tests', 3); 98 | $this->assertTestSuiteResult('failure', 'SimpleTest', 'failures', 1); 99 | $this->assertTestSuiteResult('failure', 'SimpleTest', 'errors', 1); 100 | $this->assertTestPosition('failure', 'SimpleTest::testSuccess', 1); 101 | $this->assertTestPosition('failure', 'SimpleTest::testFailure', 2); 102 | $this->assertTestPosition('failure', 'SimpleTest::testError', 3); 103 | 104 | $this->assertTestSuitePosition('success', 'SimpleTest', 1); 105 | $this->assertTestSuiteResult('success', 'SimpleTest', 'tests', 3); 106 | $this->assertTestSuiteResult('success', 'SimpleTest', 'failures', 0); 107 | $this->assertTestSuiteResult('success', 'SimpleTest', 'errors', 0); 108 | $this->assertTestPosition('success', 'SimpleTest::testFailure', 1); 109 | $this->assertTestPosition('success', 'SimpleTest::testError', 2); 110 | $this->assertTestPosition('success', 'SimpleTest::testSuccess', 3); 111 | 112 | $this->assertTestSuitePosition('retry', 'SimpleTest', 1); 113 | $this->assertTestSuiteResult('retry', 'SimpleTest', 'tests', 3); 114 | $this->assertTestSuiteResult('retry', 'SimpleTest', 'failures', 0); 115 | $this->assertTestSuiteResult('retry', 'SimpleTest', 'errors', 0); 116 | $this->assertTestPosition('retry', 'SimpleTest::testFailure', 1); 117 | $this->assertTestPosition('retry', 'SimpleTest::testError', 2); 118 | $this->assertTestPosition('retry', 'SimpleTest::testSuccess', 3); 119 | } 120 | 121 | public function testSimpleCaseGrouped_FailuresOnly() 122 | { 123 | $this->runTests('SimpleTest', 'failure', 'failure', false, 'grp'); 124 | $this->runTests('SimpleTest', 'success', 'success', true, 'grp'); 125 | $this->runTests('SimpleTest', 'success', 'retry', true, 'grp'); 126 | 127 | $this->assertTestSuitePosition('failure', 'SimpleTest', array(5, 1)); 128 | $this->assertTestSuiteResult('failure', 'SimpleTest', 'tests', 3); 129 | $this->assertTestSuiteResult('failure', 'SimpleTest', 'failures', 1); 130 | $this->assertTestSuiteResult('failure', 'SimpleTest', 'errors', 0); 131 | $this->assertTestPosition('failure', 'SimpleTest::testSuccess', 1); 132 | $this->assertTestPosition('failure', 'SimpleTest::testFailure', 2); 133 | $this->assertTestPosition('failure', 'SimpleTest::testError', 3); 134 | 135 | $this->assertTestSuitePosition('success', 'SimpleTest', 1); 136 | $this->assertTestSuiteResult('success', 'SimpleTest', 'tests', 3); 137 | $this->assertTestSuiteResult('success', 'SimpleTest', 'failures', 0); 138 | $this->assertTestSuiteResult('success', 'SimpleTest', 'errors', 0); 139 | $this->assertTestPosition('success', 'SimpleTest::testFailure', 1); 140 | $this->assertTestPosition('success', 'SimpleTest::testSuccess', 2); 141 | $this->assertTestPosition('success', 'SimpleTest::testError', 3); 142 | 143 | $this->assertTestSuitePosition('retry', 'SimpleTest', 1); 144 | $this->assertTestSuiteResult('retry', 'SimpleTest', 'tests', 3); 145 | $this->assertTestSuiteResult('retry', 'SimpleTest', 'failures', 0); 146 | $this->assertTestSuiteResult('retry', 'SimpleTest', 'errors', 0); 147 | $this->assertTestPosition('retry', 'SimpleTest::testFailure', 1); 148 | $this->assertTestPosition('retry', 'SimpleTest::testSuccess', 2); 149 | $this->assertTestPosition('retry', 'SimpleTest::testError', 3); 150 | } 151 | 152 | public function testSimpleCaseGrouped_ErrorsOnly() 153 | { 154 | $this->runTests('SimpleTest', 'error', 'failure', false, 'grp'); 155 | $this->runTests('SimpleTest', 'success', 'success', true, 'grp'); 156 | $this->runTests('SimpleTest', 'success', 'retry', true, 'grp'); 157 | 158 | $this->assertTestSuitePosition('failure', 'SimpleTest', array(5, 1)); 159 | $this->assertTestSuiteResult('failure', 'SimpleTest', 'tests', 3); 160 | $this->assertTestSuiteResult('failure', 'SimpleTest', 'failures', 0); 161 | $this->assertTestSuiteResult('failure', 'SimpleTest', 'errors', 1); 162 | $this->assertTestPosition('failure', 'SimpleTest::testSuccess', 1); 163 | $this->assertTestPosition('failure', 'SimpleTest::testFailure', 2); 164 | $this->assertTestPosition('failure', 'SimpleTest::testError', 3); 165 | 166 | $this->assertTestSuitePosition('success', 'SimpleTest', 1); 167 | $this->assertTestSuiteResult('success', 'SimpleTest', 'tests', 3); 168 | $this->assertTestSuiteResult('success', 'SimpleTest', 'failures', 0); 169 | $this->assertTestSuiteResult('success', 'SimpleTest', 'errors', 0); 170 | $this->assertTestPosition('success', 'SimpleTest::testError', 1); 171 | $this->assertTestPosition('success', 'SimpleTest::testSuccess', 2); 172 | $this->assertTestPosition('success', 'SimpleTest::testFailure', 3); 173 | 174 | $this->assertTestSuitePosition('retry', 'SimpleTest', 1); 175 | $this->assertTestSuiteResult('retry', 'SimpleTest', 'tests', 3); 176 | $this->assertTestSuiteResult('retry', 'SimpleTest', 'failures', 0); 177 | $this->assertTestSuiteResult('retry', 'SimpleTest', 'errors', 0); 178 | $this->assertTestPosition('retry', 'SimpleTest::testError', 1); 179 | $this->assertTestPosition('retry', 'SimpleTest::testSuccess', 2); 180 | $this->assertTestPosition('retry', 'SimpleTest::testFailure', 3); 181 | } 182 | 183 | public function testSimpleCaseGrouped_ErrorsAndFailures() 184 | { 185 | $this->runTests('SimpleTest', 'error-failure', 'failure', false, 'grp'); 186 | $this->runTests('SimpleTest', 'success', 'success', true, 'grp'); 187 | $this->runTests('SimpleTest', 'success', 'retry', true, 'grp'); 188 | 189 | $this->assertTestSuitePosition('failure', 'SimpleTest', array(5, 1)); 190 | $this->assertTestSuiteResult('failure', 'SimpleTest', 'tests', 3); 191 | $this->assertTestSuiteResult('failure', 'SimpleTest', 'failures', 1); 192 | $this->assertTestSuiteResult('failure', 'SimpleTest', 'errors', 1); 193 | $this->assertTestPosition('failure', 'SimpleTest::testSuccess', 1); 194 | $this->assertTestPosition('failure', 'SimpleTest::testFailure', 2); 195 | $this->assertTestPosition('failure', 'SimpleTest::testError', 3); 196 | 197 | $this->assertTestSuitePosition('success', 'SimpleTest', 1); 198 | $this->assertTestSuiteResult('success', 'SimpleTest', 'tests', 3); 199 | $this->assertTestSuiteResult('success', 'SimpleTest', 'failures', 0); 200 | $this->assertTestSuiteResult('success', 'SimpleTest', 'errors', 0); 201 | $this->assertTestPosition('success', 'SimpleTest::testFailure', 1); 202 | $this->assertTestPosition('success', 'SimpleTest::testError', 2); 203 | $this->assertTestPosition('success', 'SimpleTest::testSuccess', 3); 204 | 205 | $this->assertTestSuitePosition('retry', 'SimpleTest', 1); 206 | $this->assertTestSuiteResult('retry', 'SimpleTest', 'tests', 3); 207 | $this->assertTestSuiteResult('retry', 'SimpleTest', 'failures', 0); 208 | $this->assertTestSuiteResult('retry', 'SimpleTest', 'errors', 0); 209 | $this->assertTestPosition('retry', 'SimpleTest::testFailure', 1); 210 | $this->assertTestPosition('retry', 'SimpleTest::testError', 2); 211 | $this->assertTestPosition('retry', 'SimpleTest::testSuccess', 3); 212 | } 213 | 214 | public function testDataProviderTestCase() 215 | { 216 | $this->runTests('DataTest', 'error', 'failure', false, 'grp'); 217 | $this->runTests('DataTest', 'success', 'success', true, 'grp'); 218 | $this->runTests('DataTest', 'success', 'retry', true, 'grp'); 219 | 220 | $this->assertTestSuitePosition('failure', 'DataTest', 1); 221 | $this->assertTestSuiteResult('failure', 'DataTest', 'tests', 3); 222 | $this->assertTestSuiteResult('failure', 'DataTest', 'failures', 0); 223 | $this->assertTestSuiteResult('failure', 'DataTest', 'errors', 1); 224 | $this->assertTestPosition('failure', 'DataTest::testData with data set #0', 1); 225 | $this->assertTestPosition('failure', 'DataTest::testData with data set #1', 2); 226 | $this->assertTestPosition('failure', 'DataTest::testData with data set #2', 3); 227 | 228 | $this->assertTestSuitePosition('success', 'DataTest', 1); 229 | $this->assertTestSuiteResult('success', 'DataTest', 'tests', 3); 230 | $this->assertTestSuiteResult('success', 'DataTest', 'failures', 0); 231 | $this->assertTestSuiteResult('success', 'DataTest', 'errors', 0); 232 | $this->assertTestPosition('success', 'DataTest::testData with data set #1', 1); 233 | $this->assertTestPosition('success', 'DataTest::testData with data set #0', 2); 234 | $this->assertTestPosition('success', 'DataTest::testData with data set #2', 3); 235 | 236 | $this->assertTestSuitePosition('retry', 'DataTest', 1); 237 | $this->assertTestSuiteResult('retry', 'DataTest', 'tests', 3); 238 | $this->assertTestSuiteResult('retry', 'DataTest', 'failures', 0); 239 | $this->assertTestSuiteResult('retry', 'DataTest', 'errors', 0); 240 | $this->assertTestPosition('retry', 'DataTest::testData with data set #1', 1); 241 | $this->assertTestPosition('retry', 'DataTest::testData with data set #0', 2); 242 | $this->assertTestPosition('retry', 'DataTest::testData with data set #2', 3); 243 | } 244 | 245 | public function testDependentTest() 246 | { 247 | $this->runTests('DependentTest', 'failure', 'failure', false, 'grp'); 248 | $this->runTests('DependentTest', 'success', 'success', true, 'grp'); 249 | $this->runTests('DependentTest', 'success', 'retry', true, 'grp'); 250 | 251 | $this->assertTestSuitePosition('failure', 'DependentTest', 1); 252 | $this->assertTestSuiteResult('failure', 'DependentTest', 'tests', 2); 253 | $this->assertTestSuiteResult('failure', 'DependentTest', 'failures', 1); 254 | $this->assertTestSuiteResult('failure', 'DependentTest', 'errors', 0); 255 | $this->assertTestPosition('failure', 'DependentTest::testSuccess', 1); 256 | $this->assertTestPosition('failure', 'DependentTest::testFailure', 2); 257 | 258 | $this->assertTestSuitePosition('success', 'DependentTest', 1); 259 | $this->assertTestSuiteResult('success', 'DependentTest', 'tests', 3); 260 | $this->assertTestSuiteResult('success', 'DependentTest', 'failures', 0); 261 | $this->assertTestSuiteResult('success', 'DependentTest', 'errors', 0); 262 | $this->assertTestPosition('success', 'DependentTest::testSuccess', 1); 263 | $this->assertTestPosition('success', 'DependentTest::testFailure', 2); 264 | $this->assertTestPosition('success', 'DependentTest::testError', 3); 265 | 266 | $this->assertTestSuitePosition('retry', 'DependentTest', 1); 267 | $this->assertTestSuiteResult('retry', 'DependentTest', 'tests', 3); 268 | $this->assertTestSuiteResult('retry', 'DependentTest', 'failures', 0); 269 | $this->assertTestSuiteResult('retry', 'DependentTest', 'errors', 0); 270 | $this->assertTestPosition('retry', 'DependentTest::testSuccess', 1); 271 | $this->assertTestPosition('retry', 'DependentTest::testFailure', 2); 272 | $this->assertTestPosition('retry', 'DependentTest::testError', 3); 273 | } 274 | 275 | public function testFatalError() 276 | { 277 | $this->runTests('FatalErrorTest', 'fatal', 'fatal', false, 'grp'); 278 | $this->runTests('FatalErrorTest', 'success', 'success', true, 'grp'); 279 | $this->runTests('FatalErrorTest', 'success', 'retry', true, 'grp'); 280 | 281 | $this->assertTestSuitePosition('success', 'FatalErrorTest', 1); 282 | $this->assertTestSuiteResult('success', 'FatalErrorTest', 'tests', 3); 283 | $this->assertTestSuiteResult('success', 'FatalErrorTest', 'failures', 0); 284 | $this->assertTestSuiteResult('success', 'FatalErrorTest', 'errors', 0); 285 | $this->assertTestPosition('success', 'FatalErrorTest::testFatal', 1); 286 | $this->assertTestPosition('success', 'FatalErrorTest::testSuccess2', 2); 287 | $this->assertTestPosition('success', 'FatalErrorTest::testSuccess1', 3); 288 | 289 | $this->assertTestSuitePosition('retry', 'FatalErrorTest', 1); 290 | $this->assertTestSuiteResult('retry', 'FatalErrorTest', 'tests', 3); 291 | $this->assertTestSuiteResult('retry', 'FatalErrorTest', 'failures', 0); 292 | $this->assertTestSuiteResult('retry', 'FatalErrorTest', 'errors', 0); 293 | $this->assertTestPosition('retry', 'FatalErrorTest::testFatal', 1); 294 | $this->assertTestPosition('retry', 'FatalErrorTest::testSuccess1', 2); 295 | $this->assertTestPosition('retry', 'FatalErrorTest::testSuccess2', 3); 296 | } 297 | 298 | public function testSkippedAndIncompleteTests() 299 | { 300 | $this->runTests('SkipTest', 'skip', 'skip', true, 'grp'); 301 | $this->runTests('SkipTest', 'success', 'success', true, 'grp'); 302 | $this->runTests('SkipTest', 'success', 'retry', true, 'grp'); 303 | 304 | $this->assertTestSuitePosition('skip', 'SkipTest', array(1, 6)); 305 | $this->assertTestSuiteResult('skip', 'SkipTest', 'tests', 2); 306 | $this->assertTestSuiteResult('skip', 'SkipTest', 'failures', 0); 307 | $this->assertTestSuiteResult('skip', 'SkipTest', 'errors', 0); 308 | $this->assertTestPosition('skip', 'SkipTest::testSuccess1', 1); 309 | $this->assertTestPosition('skip', 'SkipTest::testSuccess2', 2); 310 | 311 | $this->assertTestSuitePosition('success', 'SkipTest', 1); 312 | $this->assertTestSuiteResult('success', 'SkipTest', 'tests', 4); 313 | $this->assertTestSuiteResult('success', 'SkipTest', 'failures', 0); 314 | $this->assertTestSuiteResult('success', 'SkipTest', 'errors', 0); 315 | $this->assertTestPosition('success', 'SkipTest::testSkipped', 1); 316 | $this->assertTestPosition('success', 'SkipTest::testIncomplete', 2); 317 | $this->assertTestPosition('success', 'SkipTest::testSuccess1', 3); 318 | $this->assertTestPosition('success', 'SkipTest::testSuccess2', 4); 319 | 320 | $this->assertTestSuitePosition('retry', 'SkipTest', 1); 321 | $this->assertTestSuiteResult('retry', 'SkipTest', 'tests', 4); 322 | $this->assertTestSuiteResult('retry', 'SkipTest', 'failures', 0); 323 | $this->assertTestSuiteResult('retry', 'SkipTest', 'errors', 0); 324 | $this->assertTestPosition('retry', 'SkipTest::testSkipped', 1); 325 | $this->assertTestPosition('retry', 'SkipTest::testIncomplete', 2); 326 | $this->assertTestPosition('retry', 'SkipTest::testSuccess1', 3); 327 | $this->assertTestPosition('retry', 'SkipTest::testSuccess2', 4); 328 | } 329 | 330 | private function runTests($testFile, $state, $runName, $expectedResult, $group = null) 331 | { 332 | $phpunit = realpath(__DIR__ . '/../../../../../../../vendor/bin/phpunit'); 333 | 334 | $commandString = defined('PHP_BINARY') ? PHP_BINARY : 'php'; 335 | if (strpos($commandString, 'hhvm') === false) { 336 | $commandString .= ' -d error_log=/tmp/dev.log'; 337 | } 338 | 339 | $commandString .= ' ' . $phpunit 340 | . ' --configuration ' . __DIR__ . '/../phpunit-%s.xml' 341 | . ' --log-junit ' . __DIR__ . '/../result-' . $runName . '.xml' 342 | . ' --filter ' . $testFile; 343 | 344 | if ($group) { 345 | $commandString .= ' --group ' . $group; 346 | } 347 | 348 | $process = new Process(sprintf($commandString, $state)); 349 | $process->setWorkingDirectory(__DIR__ . '/../'); 350 | $process->run(); 351 | 352 | $this->assertTrue( 353 | $expectedResult ? $process->isSuccessful() : !$process->isSuccessful(), 354 | $process->getOutput() . "\n\n\n" . $process->getErrorOutput() 355 | ); 356 | } 357 | 358 | private static function assertTestPosition($runName, $testName, $expectedPosition) 359 | { 360 | $resultFilePath = static::getResultFilePath($runName); 361 | list($class, $method) = explode('::', $testName); 362 | $expression = sprintf( 363 | '//testsuite[contains(@name, "%s")]/testcase[%d][@name="%s"]', 364 | $class, 365 | $expectedPosition, 366 | $method 367 | ); 368 | 369 | $xml = file_get_contents($resultFilePath); 370 | static::assertXpathNotEmpty( 371 | $xml, 372 | $expression, 373 | sprintf('Could not find XPath expression "%s" in "%s" (%s)', $expression, $resultFilePath, $xml) 374 | ); 375 | } 376 | 377 | private static function assertTestSuitePosition($runName, $suite, $expectedPositions) 378 | { 379 | $resultFilePath = static::getResultFilePath($runName); 380 | 381 | $expressions = array(); 382 | foreach ((array) $expectedPositions as $expectedPosition) { 383 | $expressions[] = sprintf( 384 | '//testsuite/testsuite[%d][contains(@name, "%s")]', 385 | $expectedPosition, 386 | $suite 387 | ); 388 | } 389 | 390 | $expression = implode(' | ', $expressions); 391 | 392 | $xml = file_get_contents($resultFilePath); 393 | static::assertXpathNotEmpty( 394 | $xml, 395 | $expression, 396 | sprintf('Could not find XPath expression "%s" in "%s" (%s)', $expression, $resultFilePath, $xml) 397 | ); 398 | } 399 | 400 | private static function assertTestSuiteResult($runName, $class, $attribute, $expectedValue) 401 | { 402 | $resultFilePath = static::getResultFilePath($runName); 403 | $expression = sprintf('//testsuite[contains(@name, "%s")]', $class); 404 | 405 | $xml = file_get_contents($resultFilePath); 406 | static::assertXpathEquals($xml, $expression, $expectedValue, $attribute); 407 | } 408 | 409 | private static function assertXpathNotEmpty($xml, $xpath, $comment = null) 410 | { 411 | $xml = new SimpleXMLElement($xml); 412 | 413 | static::assertNotEmpty( 414 | (array) $xml->xpath($xpath), 415 | $comment ?: sprintf('Could not find "%s" in "%s"', $xpath, $xml) 416 | ); 417 | } 418 | 419 | private static function assertXpathEquals($xml, $xpath, $expectedValue, $attribute = null, $comment = null) 420 | { 421 | static::assertXpathNotEmpty($xml, $xpath); 422 | $xml = new SimpleXMLElement($xml); 423 | $element = current($xml->xpath($xpath)); 424 | if ($attribute) { 425 | $element = $element[$attribute]; 426 | } 427 | static::assertEquals($expectedValue, (string) $element); 428 | } 429 | 430 | private static function getResultFilePath($runName) 431 | { 432 | return __DIR__ . '/../result-' . $runName . '.xml'; 433 | } 434 | } 435 | -------------------------------------------------------------------------------- /tests/PHPUnit/Tests/Runner/CleverAndSmart/Unit/PrioritySorterTest.php: -------------------------------------------------------------------------------- 1 | addTest(new Test('test1')); 19 | $suite->addTest(new Test('test2')); 20 | $suite->addTest(new Test('test3')); 21 | $suite->addTest(new Test('test4')); 22 | 23 | $tests = $suite->tests(); 24 | $this->assertSame('test1', $tests[0]->getName()); 25 | $this->assertSame('test2', $tests[1]->getName()); 26 | $this->assertSame('test3', $tests[2]->getName()); 27 | $this->assertSame('test4', $tests[3]->getName()); 28 | 29 | $sorter = new PrioritySorter( 30 | array(array('class' => 'PHPUnit\Runner\CleverAndSmart\Unit\Test', 'test' => 'test2')) 31 | ); 32 | $sorter->sort($suite); 33 | $tests = $suite->tests(); 34 | 35 | $this->assertSame('test2', $tests[0]->getName()); 36 | $this->assertSame('test1', $tests[1]->getName()); 37 | $this->assertSame('test3', $tests[2]->getName()); 38 | $this->assertSame('test4', $tests[3]->getName()); 39 | } 40 | 41 | public function testSimpleSortingBothMarkedAsErroneous() 42 | { 43 | $suite = new TestSuite('suite1', 'suite1'); 44 | $suite->addTest(new Test('test1')); 45 | $suite->addTest(new Test('test2')); 46 | $suite->addTest(new Test('test3')); 47 | $suite->addTest(new Test('test4')); 48 | 49 | $tests = $suite->tests(); 50 | $this->assertSame('test1', $tests[0]->getName()); 51 | $this->assertSame('test2', $tests[1]->getName()); 52 | $this->assertSame('test3', $tests[2]->getName()); 53 | $this->assertSame('test4', $tests[3]->getName()); 54 | 55 | $sorter = new PrioritySorter( 56 | array( 57 | array('class' => 'PHPUnit\Runner\CleverAndSmart\Unit\Test', 'test' => 'test2'), 58 | array('class' => 'PHPUnit\Runner\CleverAndSmart\Unit\Test', 'test' => 'test1'), 59 | ) 60 | ); 61 | $sorter->sort($suite); 62 | $tests = $suite->tests(); 63 | 64 | $this->assertSame('test1', $tests[0]->getName()); 65 | $this->assertSame('test2', $tests[1]->getName()); 66 | $this->assertSame('test3', $tests[2]->getName()); 67 | $this->assertSame('test4', $tests[3]->getName()); 68 | } 69 | 70 | public function testSimpleSortingNoErrors() 71 | { 72 | $suite = new TestSuite('suite1', 'suite1'); 73 | $suite->addTest(new Test('test1')); 74 | $suite->addTest(new Test('test2')); 75 | $suite->addTest(new Test('test3')); 76 | $suite->addTest(new Test('test4')); 77 | 78 | $tests = $suite->tests(); 79 | $this->assertSame('test1', $tests[0]->getName()); 80 | $this->assertSame('test2', $tests[1]->getName()); 81 | $this->assertSame('test3', $tests[2]->getName()); 82 | $this->assertSame('test4', $tests[3]->getName()); 83 | 84 | $sorter = new PrioritySorter(array()); 85 | $sorter->sort($suite); 86 | $tests = $suite->tests(); 87 | 88 | $this->assertSame('test1', $tests[0]->getName()); 89 | $this->assertSame('test2', $tests[1]->getName()); 90 | $this->assertSame('test3', $tests[2]->getName()); 91 | $this->assertSame('test4', $tests[3]->getName()); 92 | } 93 | 94 | public function testNestedSorting() 95 | { 96 | $suite1 = new TestSuite('suite1', 'suite1'); 97 | $suite2 = new TestSuite('suite2', 'suite2'); 98 | $suite2->addTest(new Test('test3')); 99 | $suite1->addTestSuite($suite2); 100 | $suite1->addTest(new Test('test1')); 101 | $suite1->addTest(new Test('test2')); 102 | $suite1->addTest(new Test('test3')); 103 | $suite1->addTest(new Test('test4')); 104 | 105 | $tests = $suite1->tests(); 106 | $this->assertSame('suite2', $tests[0]->getName()); 107 | $this->assertSame('test1', $tests[1]->getName()); 108 | $this->assertSame('test2', $tests[2]->getName()); 109 | $this->assertSame('test3', $tests[3]->getName()); 110 | $this->assertSame('test4', $tests[4]->getName()); 111 | 112 | $sorter = new PrioritySorter( 113 | array(array('class' => 'PHPUnit\Runner\CleverAndSmart\Unit\Test', 'test' => 'test2')) 114 | ); 115 | $sorter->sort($suite1); 116 | $tests = $suite1->tests(); 117 | 118 | $this->assertSame('test2', $tests[0]->getName()); 119 | $this->assertSame('suite2', $tests[1]->getName()); 120 | $this->assertSame('test1', $tests[2]->getName()); 121 | $this->assertSame('test3', $tests[3]->getName()); 122 | $this->assertSame('test4', $tests[4]->getName()); 123 | } 124 | 125 | public function testNestedSortingWithTimings() 126 | { 127 | $suite1 = new TestSuite('suite1', 'suite1'); 128 | $suite2 = new TestSuite('suite2', 'suite2'); 129 | $suite2->addTest(new Test('test2.1')); 130 | $suite2->addTest(new Test('test2.2')); 131 | $suite1->addTestSuite($suite2); 132 | $suite1->addTest(new Test('test1')); 133 | $suite1->addTest(new Test('test2')); 134 | $suite1->addTest(new Test('test3')); 135 | $suite1->addTest(new Test('test4')); 136 | 137 | $tests = $suite1->tests(); 138 | $this->assertSame('suite2', $tests[0]->getName()); 139 | $this->assertSame('test1', $tests[1]->getName()); 140 | $this->assertSame('test2', $tests[2]->getName()); 141 | $this->assertSame('test3', $tests[3]->getName()); 142 | $this->assertSame('test4', $tests[4]->getName()); 143 | 144 | $sorter = new PrioritySorter( 145 | array(array('class' => 'PHPUnit\Runner\CleverAndSmart\Unit\Test', 'test' => 'test2')), 146 | array( 147 | array('class' => 'PHPUnit\Runner\CleverAndSmart\Unit\Test', 'test' => 'test1', 'time' => 100), 148 | array('class' => 'PHPUnit\Runner\CleverAndSmart\Unit\Test', 'test' => 'test4', 'time' => 200), 149 | array('class' => 'PHPUnit\Runner\CleverAndSmart\Unit\Test', 'test' => 'test2.1', 'time' => 100), 150 | ) 151 | ); 152 | $sorter->sort($suite1); 153 | $tests = $suite1->tests(); 154 | $tests2 = $tests[2]->tests(); 155 | 156 | $this->assertSame('test2', $tests[0]->getName()); 157 | $this->assertSame('test3', $tests[1]->getName()); 158 | $this->assertSame('suite2', $tests[2]->getName()); 159 | $this->assertSame('test2.2', $tests2[0]->getName()); 160 | $this->assertSame('test2.1', $tests2[1]->getName()); 161 | $this->assertSame('test1', $tests[3]->getName()); 162 | $this->assertSame('test4', $tests[4]->getName()); 163 | } 164 | 165 | public function testNestedSortingNoErrors() 166 | { 167 | $suite1 = new TestSuite('suite1', 'suite1'); 168 | $suite2 = new TestSuite('suite2', 'suite2'); 169 | $suite2->addTest(new Test('test3')); 170 | $suite1->addTestSuite($suite2); 171 | $suite1->addTest(new Test('test1')); 172 | $suite1->addTest(new Test('test2')); 173 | $suite1->addTest(new Test('test3')); 174 | $suite1->addTest(new Test('test4')); 175 | 176 | $tests = $suite1->tests(); 177 | $tests0 = $tests[0]->tests(); 178 | 179 | $this->assertSame('suite2', $tests[0]->getName()); 180 | $this->assertSame('test3', $tests0[0]->getName()); 181 | $this->assertSame('test1', $tests[1]->getName()); 182 | $this->assertSame('test2', $tests[2]->getName()); 183 | $this->assertSame('test3', $tests[3]->getName()); 184 | $this->assertSame('test4', $tests[4]->getName()); 185 | 186 | $sorter = new PrioritySorter(array()); 187 | $sorter->sort($suite1); 188 | $tests = $suite1->tests(); 189 | $tests0 = $tests[0]->tests(); 190 | 191 | $this->assertSame('suite2', $tests[0]->getName()); 192 | $this->assertSame('test3', $tests0[0]->getName()); 193 | $this->assertSame('test1', $tests[1]->getName()); 194 | $this->assertSame('test2', $tests[2]->getName()); 195 | $this->assertSame('test3', $tests[3]->getName()); 196 | $this->assertSame('test4', $tests[4]->getName()); 197 | } 198 | 199 | public function testSimpleSortingGroups() 200 | { 201 | $suite = new TestSuite('suite1', 'suite1'); 202 | $suite->addTest(new Test('test1'), array('g1')); 203 | $suite->addTest(new Test('test2'), array('g1')); 204 | $suite->addTest(new Test('test3'), array('g1')); 205 | $suite->addTest(new Test('test4'), array('g1')); 206 | 207 | $tests = $suite->tests(); 208 | $this->assertSame('test1', $tests[0]->getName()); 209 | $this->assertSame('test2', $tests[1]->getName()); 210 | $this->assertSame('test3', $tests[2]->getName()); 211 | $this->assertSame('test4', $tests[3]->getName()); 212 | 213 | $groupDetails = Util::getInvisibleProperty($suite, 'groups', 'getGroupDetails'); 214 | $tests = $groupDetails['g1']; 215 | $this->assertSame('test1', $tests[0]->getName()); 216 | $this->assertSame('test2', $tests[1]->getName()); 217 | $this->assertSame('test3', $tests[2]->getName()); 218 | $this->assertSame('test4', $tests[3]->getName()); 219 | 220 | $sorter = new PrioritySorter( 221 | array(array('class' => 'PHPUnit\Runner\CleverAndSmart\Unit\Test', 'test' => 'test2')) 222 | ); 223 | $sorter->sort($suite); 224 | $tests = $suite->tests(); 225 | 226 | $this->assertSame('test2', $tests[0]->getName()); 227 | $this->assertSame('test1', $tests[1]->getName()); 228 | $this->assertSame('test3', $tests[2]->getName()); 229 | $this->assertSame('test4', $tests[3]->getName()); 230 | 231 | $groupDetails = Util::getInvisibleProperty($suite, 'groups', 'getGroupDetails'); 232 | $tests = $groupDetails['g1']; 233 | $this->assertSame('test2', $tests[0]->getName()); 234 | $this->assertSame('test1', $tests[1]->getName()); 235 | $this->assertSame('test3', $tests[2]->getName()); 236 | $this->assertSame('test4', $tests[3]->getName()); 237 | } 238 | 239 | public function testNestedSortingGroups() 240 | { 241 | $suite1 = new TestSuite('suite1', 'suite1'); 242 | $suite2 = new TestSuite('suite2', 'suite2'); 243 | $suite2->addTest(new Test('test3'), array('g1')); 244 | $suite1->addTestSuite($suite2); 245 | $suite1->addTest(new Test('test1'), array('g1')); 246 | $suite1->addTest(new Test('test2'), array('g1')); 247 | $suite1->addTest(new Test('test4'), array('g1')); 248 | $suite1->addTest(new Test('test5'), array('g1')); 249 | 250 | $tests = $suite1->tests(); 251 | $tests0 = $tests[0]->tests(); 252 | 253 | $this->assertSame('suite2', $tests[0]->getName()); 254 | $this->assertSame('test3', $tests0[0]->getName()); 255 | $this->assertSame('test1', $tests[1]->getName()); 256 | $this->assertSame('test2', $tests[2]->getName()); 257 | $this->assertSame('test4', $tests[3]->getName()); 258 | $this->assertSame('test5', $tests[4]->getName()); 259 | 260 | $groupDetails = Util::getInvisibleProperty($suite1, 'groups', 'getGroupDetails'); 261 | $tests = $groupDetails['g1']; 262 | $tests0 = $tests[0]->tests(); 263 | 264 | $this->assertSame('suite2', $tests[0]->getName()); 265 | $this->assertSame('test3', $tests0[0]->getName()); 266 | $this->assertSame('test1', $tests[1]->getName()); 267 | $this->assertSame('test2', $tests[2]->getName()); 268 | $this->assertSame('test4', $tests[3]->getName()); 269 | $this->assertSame('test5', $tests[4]->getName()); 270 | 271 | $sorter = new PrioritySorter( 272 | array(array('class' => 'PHPUnit\Runner\CleverAndSmart\Unit\Test', 'test' => 'test2')) 273 | ); 274 | $sorter->sort($suite1); 275 | 276 | $tests = $suite1->tests(); 277 | $tests1 = $tests[1]->tests(); 278 | 279 | $this->assertSame('test2', $tests[0]->getName()); 280 | $this->assertSame('suite2', $tests[1]->getName()); 281 | $this->assertSame('test3', $tests1[0]->getName()); 282 | $this->assertSame('test1', $tests[2]->getName()); 283 | $this->assertSame('test4', $tests[3]->getName()); 284 | $this->assertSame('test5', $tests[4]->getName()); 285 | 286 | $groupDetails = Util::getInvisibleProperty($suite1, 'groups', 'getGroupDetails'); 287 | $tests = $groupDetails['g1']; 288 | $tests1 = $tests[1]->tests(); 289 | 290 | $this->assertSame('test2', $tests[0]->getName()); 291 | $this->assertSame('suite2', $tests[1]->getName()); 292 | $this->assertSame('test3', $tests1[0]->getName()); 293 | $this->assertSame('test1', $tests[2]->getName()); 294 | $this->assertSame('test4', $tests[3]->getName()); 295 | $this->assertSame('test5', $tests[4]->getName()); 296 | } 297 | } 298 | -------------------------------------------------------------------------------- /tests/PHPUnit/Tests/Runner/CleverAndSmart/Unit/SegmentedQueueTest.php: -------------------------------------------------------------------------------- 1 | queue = new SegmentedQueue(); 15 | } 16 | 17 | public function testPushToUnknownQueue() 18 | { 19 | $this->queue->unknown->push('test1'); 20 | $this->queue->unknown->push('test2'); 21 | 22 | $this->assertSame(array('test1', 'test2'), iterator_to_array($this->queue)); 23 | } 24 | 25 | public function testPushWithPriority() 26 | { 27 | $this->queue->unknown->push('test1'); 28 | $this->queue->timed->insert('test4', 0.1); 29 | $this->queue->timed->insert('test5', 0.2); 30 | $this->queue->timed->insert('test3', 0.05); 31 | $this->queue->unknown->push('test2'); 32 | 33 | $this->assertSame(array('test1', 'test2', 'test3', 'test4', 'test5'), iterator_to_array($this->queue)); 34 | } 35 | 36 | public function testResetWithNull() 37 | { 38 | $this->queue->unknown->push('test1'); 39 | $this->queue->unknown->push('test2'); 40 | $this->queue->unknown[0] = null; 41 | 42 | $this->assertSame(array('test2'), iterator_to_array($this->queue)); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /tests/PHPUnit/Tests/Runner/CleverAndSmart/Unit/Storage/Sqlite3StorageTest.php: -------------------------------------------------------------------------------- 1 | file = __DIR__ . '/.phpunit-cas-test.db'; 37 | $this->reset(); 38 | 39 | $this->storage = new Sqlite3Storage($this->file); 40 | $this->test1 = new Test(); 41 | $this->test1->setName('testMethod1'); 42 | $this->test2 = new Test(); 43 | $this->test2->setName('testMethod2'); 44 | $this->run1 = new Run(); 45 | $this->run2 = new Run(); 46 | } 47 | 48 | public function tearDown() 49 | { 50 | $this->reset(); 51 | } 52 | 53 | private function reset() 54 | { 55 | if (file_exists($this->file)) { 56 | unlink($this->file); 57 | } 58 | } 59 | 60 | public static function getTypes() 61 | { 62 | $class = new ReflectionClass('PHPUnit\Runner\CleverAndSmart\Storage\StorageInterface'); 63 | 64 | $types = array(); 65 | foreach ($class->getConstants() as $name => $value) { 66 | $types[] = array($value, $name); 67 | } 68 | 69 | return $types; 70 | } 71 | 72 | public static function getErrorTypes() 73 | { 74 | return array( 75 | array(StorageInterface::STATUS_FAILURE, 'STATUS_FAILURE'), 76 | array(StorageInterface::STATUS_ERROR, 'STATUS_ERROR'), 77 | array(StorageInterface::STATUS_FATAL_ERROR, 'STATUS_FATAL_ERROR'), 78 | array(StorageInterface::STATUS_CANCEL, 'STATUS_CANCEL'), 79 | ); 80 | } 81 | 82 | /** @dataProvider getTypes */ 83 | public function testNormalRecording($type) 84 | { 85 | $this->assertEmpty($this->storage->getRecordings(array($type))); 86 | $this->storage->record($this->run1, $this->test1, 1000, $type); 87 | $this->storage->record($this->run1, $this->test1, 2000, $type); 88 | $recordings = $this->storage->getRecordings(array($type)); 89 | $this->assertNotEmpty($recordings); 90 | 91 | $this->assertCount(3, $recordings[0]); 92 | $this->assertArrayHasKey('class', $recordings[0]); 93 | $this->assertArrayHasKey('test', $recordings[0]); 94 | $this->assertArrayHasKey('time', $recordings[0]); 95 | $this->assertSame(1500.0, $recordings[0]['time']); 96 | 97 | $recordings = $this->storage->getRecordings(array($type), false); 98 | $this->assertCount(2, $recordings[0]); 99 | $this->assertArrayHasKey('class', $recordings[0]); 100 | $this->assertArrayHasKey('test', $recordings[0]); 101 | } 102 | 103 | /** @dataProvider getTypes */ 104 | public function testRecordingsAreSortedByFrequency($type) 105 | { 106 | $this->assertEmpty($this->storage->getRecordings(array($type))); 107 | $this->storage->record($this->run1, $this->test1, 0, $type); 108 | $this->storage->record($this->run1, $this->test2, 0, $type); 109 | $this->storage->record($this->run1, $this->test1, 0, $type); 110 | $this->storage->record($this->run1, $this->test2, 0, $type); 111 | $this->storage->record($this->run2, $this->test2, 0, $type); 112 | 113 | $this->assertSame( 114 | array( 115 | array('class' => 'PHPUnit\Runner\CleverAndSmart\Unit\Storage\Test', 'test' => 'testMethod2'), 116 | array('class' => 'PHPUnit\Runner\CleverAndSmart\Unit\Storage\Test', 'test' => 'testMethod1'), 117 | ), 118 | $this->storage->getRecordings(array($type), false) 119 | ); 120 | } 121 | 122 | /** @dataProvider getErrorTypes */ 123 | public function testRecordedErrorsAreRemovedAfterFiveTimes($type) 124 | { 125 | $this->assertEmpty($this->storage->getRecordings(array($type))); 126 | $this->storage->record($this->run1, $this->test1, 1, $type); 127 | $this->assertNotEmpty($this->storage->getRecordings(array($type))); 128 | 129 | $this->storage->record($this->run1, $this->test1, 1, StorageInterface::STATUS_PASSED); 130 | $this->assertNotEmpty($this->storage->getRecordings(array($type))); 131 | $this->storage->record($this->run1, $this->test1, 2, StorageInterface::STATUS_PASSED); 132 | $this->assertNotEmpty($this->storage->getRecordings(array($type))); 133 | $this->storage->record($this->run1, $this->test1, 3, StorageInterface::STATUS_PASSED); 134 | $this->assertNotEmpty($this->storage->getRecordings(array($type))); 135 | $this->storage->record($this->run1, $this->test1, 4, StorageInterface::STATUS_PASSED); 136 | $this->assertEmpty($this->storage->getRecordings(array($type))); 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /tests/PHPUnit/Tests/Runner/CleverAndSmart/Unit/UtilTest.php: -------------------------------------------------------------------------------- 1 | privateProperty; 16 | } 17 | 18 | public function setPrivateProperty($value) 19 | { 20 | $this->privateProperty = $value; 21 | } 22 | 23 | public function getPublicProperty() 24 | { 25 | return $this->publicProperty; 26 | } 27 | 28 | public function setPublicProperty($value) 29 | { 30 | $this->publicProperty = $value; 31 | } 32 | } 33 | 34 | class Child extends Mother 35 | { 36 | protected $protectedProperty = 'protected'; 37 | 38 | public function getProtectedProperty() 39 | { 40 | return $this->protectedProperty; 41 | } 42 | 43 | public function setProtectedProperty($value) 44 | { 45 | $this->protectedProperty = $value; 46 | } 47 | } 48 | 49 | class UtilTest extends TestCase 50 | { 51 | public function testCreateRunId() 52 | { 53 | $idOne = Util::createRunId(); 54 | $idTwo = Util::createRunId(); 55 | 56 | $this->assertSame(128, strlen($idOne)); 57 | $this->assertSame(128, strlen($idTwo)); 58 | $this->assertNotSame($idOne, $idTwo); 59 | } 60 | 61 | public function testGetInvisiblePropertyByMethod() 62 | { 63 | $mother = new Mother(); 64 | $child = new Child(); 65 | 66 | $this->assertSame('private', Util::getInvisibleProperty($mother, 'invalidProperty', 'getPrivateProperty')); 67 | $this->assertSame('private', Util::getInvisibleProperty($child, 'invalidProperty', 'getPrivateProperty')); 68 | $this->assertSame('protected', Util::getInvisibleProperty($child, 'invalidProperty', 'getProtectedProperty')); 69 | $this->assertSame('public', Util::getInvisibleProperty($mother, 'invalidProperty', 'getPublicProperty')); 70 | $this->assertSame('public', Util::getInvisibleProperty($child, 'invalidProperty', 'getPublicProperty')); 71 | } 72 | 73 | public function testGetInvisiblePropertyByProperty() 74 | { 75 | $mother = new Mother(); 76 | $child = new Child(); 77 | 78 | $this->assertSame('private', Util::getInvisibleProperty($mother, 'privateProperty')); 79 | $this->assertSame('private', Util::getInvisibleProperty($mother, 'privateProperty', 'invalidMethod')); 80 | $this->assertSame('private', Util::getInvisibleProperty($child, 'privateProperty')); 81 | $this->assertSame('private', Util::getInvisibleProperty($child, 'privateProperty', 'invalidMethod')); 82 | $this->assertSame('protected', Util::getInvisibleProperty($child, 'protectedProperty')); 83 | $this->assertSame('protected', Util::getInvisibleProperty($child, 'protectedProperty', 'invalidMethod')); 84 | $this->assertSame('public', Util::getInvisibleProperty($mother, 'publicProperty')); 85 | $this->assertSame('public', Util::getInvisibleProperty($mother, 'publicProperty', 'invalidMethod')); 86 | $this->assertSame('public', Util::getInvisibleProperty($child, 'publicProperty')); 87 | $this->assertSame('public', Util::getInvisibleProperty($child, 'publicProperty', 'invalidMethod')); 88 | } 89 | 90 | public function testSetInvisiblePropertyByProperty() 91 | { 92 | $mother = new Mother(); 93 | $child = new Child(); 94 | 95 | Util::setInvisibleProperty($mother, 'privateProperty', 'private2'); 96 | $this->assertSame('private2', Util::getInvisibleProperty($mother, 'invalidProperty', 'getPrivateProperty')); 97 | 98 | Util::setInvisibleProperty($mother, 'privateProperty', 'private3', 'invalidMethod'); 99 | $this->assertSame('private3', Util::getInvisibleProperty($mother, 'invalidProperty', 'getPrivateProperty')); 100 | 101 | Util::setInvisibleProperty($child, 'privateProperty', 'private2'); 102 | $this->assertSame('private2', Util::getInvisibleProperty($child, 'invalidProperty', 'getPrivateProperty')); 103 | 104 | Util::setInvisibleProperty($child, 'privateProperty', 'private3', 'invalidMethod'); 105 | $this->assertSame('private3', Util::getInvisibleProperty($child, 'invalidProperty', 'getPrivateProperty')); 106 | 107 | Util::setInvisibleProperty($child, 'protectedProperty', 'protected2'); 108 | $this->assertSame('protected2', Util::getInvisibleProperty($child, 'invalidProperty', 'getProtectedProperty')); 109 | 110 | Util::setInvisibleProperty($child, 'protectedProperty', 'protected3', 'invalidMethod'); 111 | $this->assertSame('protected3', Util::getInvisibleProperty($child, 'invalidProperty', 'getProtectedProperty')); 112 | 113 | Util::setInvisibleProperty($mother, 'publicProperty', 'public2'); 114 | $this->assertSame('public2', Util::getInvisibleProperty($mother, 'invalidProperty', 'getPublicProperty')); 115 | 116 | Util::setInvisibleProperty($mother, 'publicProperty', 'public3', 'methodName'); 117 | $this->assertSame('public3', Util::getInvisibleProperty($mother, 'invalidProperty', 'getPublicProperty')); 118 | 119 | Util::setInvisibleProperty($child, 'publicProperty', 'public2'); 120 | $this->assertSame('public2', Util::getInvisibleProperty($child, 'invalidProperty', 'getPublicProperty')); 121 | 122 | Util::setInvisibleProperty($child, 'publicProperty', 'public3', 'methodName'); 123 | $this->assertSame('public3', Util::getInvisibleProperty($child, 'invalidProperty', 'getPublicProperty')); 124 | } 125 | 126 | public function testSetInvisiblePropertyByMethod() 127 | { 128 | $mother = new Mother(); 129 | $child = new Child(); 130 | 131 | Util::setInvisibleProperty($mother, 'invalidProperty', 'private2', 'setPrivateProperty'); 132 | $this->assertSame('private2', Util::getInvisibleProperty($mother, 'invalidProperty', 'getPrivateProperty')); 133 | 134 | Util::setInvisibleProperty($child, 'invalidProperty', 'private2', 'setPrivateProperty'); 135 | $this->assertSame('private2', Util::getInvisibleProperty($child, 'invalidProperty', 'getPrivateProperty')); 136 | 137 | Util::setInvisibleProperty($child, 'invalidProperty', 'protected2', 'setProtectedProperty'); 138 | $this->assertSame('protected2', Util::getInvisibleProperty($child, 'invalidProperty', 'getProtectedProperty')); 139 | 140 | Util::setInvisibleProperty($mother, 'invalidProperty', 'public2', 'setPublicProperty'); 141 | $this->assertSame('public2', Util::getInvisibleProperty($mother, 'invalidProperty', 'getPublicProperty')); 142 | 143 | Util::setInvisibleProperty($child, 'invalidProperty', 'public2', 'setPublicProperty'); 144 | $this->assertSame('public2', Util::getInvisibleProperty($child, 'invalidProperty', 'getPublicProperty')); 145 | } 146 | 147 | public function testGetInvalidProperty() 148 | { 149 | $this->setExpectedException( 150 | 'PHPUnit\Runner\CleverAndSmart\Exception\PropertyReflectionException', 151 | 'Property "invalidProperty" does not exist in hierarchy PHPUnit\Runner\CleverAndSmart\Unit\Child < PHPUnit\Runner\CleverAndSmart\Unit\Mother' 152 | ); 153 | Util::getInvisibleProperty(new Child(), 'invalidProperty'); 154 | } 155 | 156 | public function testSetInvalidProperty() 157 | { 158 | $this->setExpectedException( 159 | 'PHPUnit\Runner\CleverAndSmart\Exception\PropertyReflectionException', 160 | 'Property "invalidProperty" does not exist in hierarchy PHPUnit\Runner\CleverAndSmart\Unit\Child < PHPUnit\Runner\CleverAndSmart\Unit\Mother' 161 | ); 162 | Util::setInvisibleProperty(new Child(), 'invalidProperty', 'value'); 163 | } 164 | } 165 | --------------------------------------------------------------------------------