diff --git a/src/Framework/Environment.php b/src/Framework/Environment.php index ab3e3748..2baba5e0 100644 --- a/src/Framework/Environment.php +++ b/src/Framework/Environment.php @@ -172,6 +172,27 @@ public static function getTestAnnotations() } + /** + * Removes keyword final from source codes. + * @return void + */ + public static function bypassFinals() + { + FileMutator::addMutator(function ($code) { + if (strpos($code, 'final') !== false) { + $tokens = token_get_all($code); + $code = ''; + foreach ($tokens as $token) { + $code .= is_array($token) + ? ($token[0] === T_FINAL ? '' : $token[1]) + : $token; + } + } + return $code; + }); + } + + /** * Loads data according to the file annotation or specified by Tester\Runner\TestHandler::initiateDataProvider() * @return array diff --git a/src/Framework/FileMutator.php b/src/Framework/FileMutator.php new file mode 100644 index 00000000..b4843b8a --- /dev/null +++ b/src/Framework/FileMutator.php @@ -0,0 +1,212 @@ +handle); + } + + + public function dir_opendir($path, $options) + { + $this->handle = $this->native('opendir', $path, $this->context); + return (bool) $this->handle; + } + + + public function dir_readdir() + { + return readdir($this->handle); + } + + + public function dir_rewinddir() + { + return rewinddir($this->handle); + } + + + public function mkdir($path, $mode, $options) + { + return $this->native('mkdir', $mode, false, $this->context); + } + + + public function rename($pathFrom, $pathTo) + { + return $this->native('rename', $pathFrom, $pathTo, $this->context); + } + + + public function rmdir($path, $options) + { + return $this->native('rmdir', $this->context); + } + + + public function stream_cast($castAs) + { + return $this->handle; + } + + + public function stream_close() + { + fclose($this->handle); + } + + + public function stream_eof() + { + return feof($this->handle); + } + + + public function stream_flush() + { + return fflush($this->handle); + } + + + public function stream_lock($operation) + { + return flock($this->handle, $operation); + } + + + public function stream_metadata($path, $option, $value) + { + switch ($option) { + case STREAM_META_TOUCH: + return $this->native('touch', $path, $value[0], $value[1]); + case STREAM_META_OWNER_NAME: + case STREAM_META_OWNER: + return $this->native('chown', $path, $value); + case STREAM_META_GROUP_NAME: + case STREAM_META_GROUP: + return $this->native('chgrp', $path, $value); + case STREAM_META_ACCESS: + return $this->native('chmod', $path, $value); + } + } + + + public function stream_open($path, $mode, $options, &$openedPath) + { + $usePath = (bool) ($options & STREAM_USE_PATH); + if (pathinfo($path, PATHINFO_EXTENSION) === 'php') { + $content = $this->native('file_get_contents', $path, $usePath, $this->context); + if ($content === false) { + return false; + } else { + foreach (self::$mutators as $mutator) { + $content = call_user_func($mutator, $content); + } + $this->handle = tmpfile(); + $this->native('fwrite', $this->handle, $content); + $this->native('fseek', $this->handle, 0); + return true; + } + } else { + $this->handle = $this->context + ? $this->native('fopen', $path, $mode, $usePath, $this->context) + : $this->native('fopen', $path, $mode, $usePath); + return (bool) $this->handle; + } + } + + + public function stream_read($count) + { + return fread($this->handle, $count); + } + + + public function stream_seek($offset, $whence = SEEK_SET) + { + return fseek($this->handle, $offset, $whence); + } + + + public function stream_set_option($option, $arg1, $arg2) + { + } + + + public function stream_stat() + { + return fstat($this->handle); + } + + + public function stream_tell() + { + return ftell($this->handle); + } + + + public function stream_truncate($newSize) + { + return ftruncate($this->handle, $newSize); + } + + + public function stream_write($data) + { + return fwrite($this->handle, $data); + } + + + public function unlink($path) + { + return $this->native('unlink', $path); + } + + + public function url_stat($path, $flags) + { + return $this->native('fstat', $path, $flags); + } + + + private function native($func) + { + stream_wrapper_restore(self::PROTOCOL); + $res = call_user_func_array($func, array_slice(func_get_args(), 1)); + stream_wrapper_unregister(self::PROTOCOL); + stream_wrapper_register(self::PROTOCOL, __CLASS__); + return $res; + } +} diff --git a/src/bootstrap.php b/src/bootstrap.php index 51366b9c..f2f18b2c 100644 --- a/src/bootstrap.php +++ b/src/bootstrap.php @@ -13,6 +13,7 @@ require __DIR__ . '/Framework/FileMock.php'; require __DIR__ . '/Framework/TestCase.php'; require __DIR__ . '/Framework/DomQuery.php'; +require __DIR__ . '/Framework/FileMutator.php'; require __DIR__ . '/CodeCoverage/Collector.php'; require __DIR__ . '/Runner/Job.php'; diff --git a/tests/Framework/FileMutator.phpt b/tests/Framework/FileMutator.phpt new file mode 100644 index 00000000..8e1ca74d --- /dev/null +++ b/tests/Framework/FileMutator.phpt @@ -0,0 +1,14 @@ +isFinal()); +Assert::false($rc->getMethod('finalMethod')->isFinal()); diff --git a/tests/Framework/fixtures/final.class.php b/tests/Framework/fixtures/final.class.php new file mode 100644 index 00000000..410c0ab3 --- /dev/null +++ b/tests/Framework/fixtures/final.class.php @@ -0,0 +1,8 @@ +