update v1.0.6

This commit is contained in:
sujitprasad
2016-02-16 22:42:08 +05:30
parent e6b579d67b
commit 073a49a8af
587 changed files with 21487 additions and 22766 deletions

View File

@@ -1,3 +1,14 @@
2.4.1 / 2016/01/01
==================
* Correctly handle nested class definitions
* Correctly handle anonymous functions in code generation
* Fixed rerunning on Windows platform
* Fixed code generation on Windows platform
* Fixed issue with fatal errors being suppressed
* Handle underscores correctly when using PSR-4
* Fixed HTML formatter
2.4.0 / 2015/11/28
==================

View File

@@ -24,6 +24,7 @@ init:
- SET COMPOSER_NO_INTERACTION=1
- SET PHP=1
- SET ANSICON=121x90 (121x90)
- git config --global core.autocrlf input
install:
- IF EXIST c:\php (SET PHP=0) ELSE (mkdir c:\php)

View File

@@ -2,13 +2,13 @@ default:
suites:
application:
contexts: [ ApplicationContext, FilesystemContext ]
filters: { tags: ~@isolated }
filters: { tags: "~@isolated" }
isolated:
contexts: [ IsolatedProcessContext, FilesystemContext ]
filters: { tags: @isolated }
filters: { tags: "@isolated" }
smoke:
contexts: [ IsolatedProcessContext, FilesystemContext ]
filters: { tags: @smoke && ~@isolated }
filters: { tags: "@smoke && ~@isolated" }
no-smoke:
suites:

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env php
<?php
define('PHPSPEC_VERSION', '2.4.0');
define('PHPSPEC_VERSION', '2.4.1');
if (is_file($autoload = getcwd() . '/vendor/autoload.php')) {
require $autoload;

View File

@@ -262,6 +262,67 @@ Feature: Developer generates a method
"""
Scenario: Generating a method in a class with existing methods containing anonymous functions
Given the spec file "spec/MyNamespace/ExistingMethodAnonymousFunctionSpec.php" contains:
"""
<?php
namespace spec\MyNamespace;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
class ExistingMethodAnonymousFunctionSpec extends ObjectBehavior
{
function it_should_do_something()
{
$this->foo()->shouldReturn('bar');
}
}
"""
And the class file "src/MyNamespace/ExistingMethodAnonymousFunction.php" contains:
"""
<?php
namespace MyNamespace;
class ExistingMethodAnonymousFunction
{
public function existing()
{
return function () {
return 'something';
};
}
}
"""
When I run phpspec and answer "y" when asked if I want to generate the code
Then the class in "src/MyNamespace/ExistingMethodAnonymousFunction.php" should contain:
"""
<?php
namespace MyNamespace;
class ExistingMethodAnonymousFunction
{
public function existing()
{
return function () {
return 'something';
};
}
public function foo()
{
// TODO: write logic here
}
}
"""
Scenario: Generating a constructor in a file with no methods
Given the spec file "spec/MyNamespace/CommentMethodSpec.php" contains:
"""

View File

@@ -1 +0,0 @@
formatter.name: pretty

View File

@@ -70,6 +70,39 @@ class TokenizedTypeHintRewriterSpec extends ObjectBehavior
');
}
function it_does_not_remove_typehints_in_methods()
{
$this->rewrite('
<?php
class Foo
{
public function bar(\Foo\Bar $bar)
{
new class($argument) implements InterfaceName
{
public function foo(Foo $foo) {}
};
}
}
')->shouldReturn('
<?php
class Foo
{
public function bar($bar)
{
new class($argument) implements InterfaceName
{
public function foo(Foo $foo) {}
};
}
}
');
}
function it_removes_typehints_for_multiple_arguments_in_methods()
{
$this->rewrite('

View File

@@ -503,21 +503,21 @@ class PSR0LocatorSpec extends ObjectBehavior
function it_supports_psr0_namespace_queries(Filesystem $filesystem)
{
$this->beConstructedWith('', 'spec', $this->srcPath, $this->specPath, $filesystem);
$filesystem->pathExists($this->specPath.'/spec/PhpSpec/Console/ApplicationSpec.php')->willReturn(true);
$filesystem->pathExists($this->specPath.$this->convert_to_path('/spec/PhpSpec/Console/ApplicationSpec.php'))->willReturn(true);
$this->supportsQuery('PhpSpec\\Console\\Application')->shouldReturn(true);
}
function it_supports_psr0_namespace_queries_with_a_namespace_prefix(Filesystem $filesystem)
{
$this->beConstructedWith('PhpSpec', 'spec', $this->srcPath, $this->specPath, $filesystem);
$filesystem->pathExists($this->specPath.'/spec/PhpSpec/Console/ApplicationSpec.php')->willReturn(true);
$filesystem->pathExists($this->specPath.$this->convert_to_path('/spec/PhpSpec/Console/ApplicationSpec.php'))->willReturn(true);
$this->supportsQuery('Console\\Application')->shouldReturn(true);
}
function it_supports_psr4_namespace_queries(Filesystem $filesystem)
{
$this->beConstructedWith('Test\\Namespace\\PhpSpec', 'spec', $this->srcPath, $this->specPath, $filesystem, 'Test\\Namespace');
$filesystem->pathExists($this->specPath.'/spec/PhpSpec/Console/ApplicationSpec.php')->willReturn(true);
$filesystem->pathExists($this->specPath.$this->convert_to_path('/spec/PhpSpec/Console/ApplicationSpec.php'))->willReturn(true);
$this->supportsQuery('Test\\Namespace\\PhpSpec\\Console\\Application')->shouldReturn(true);
}

View File

@@ -11,6 +11,8 @@ class PSR0ResourceSpec extends ObjectBehavior
function let(Locator $locator)
{
$this->beConstructedWith(array('usr', 'lib', 'config'), $locator);
$locator->isPSR4()->willReturn(false);
}
function it_uses_last_segment_as_name()
@@ -92,4 +94,16 @@ class PSR0ResourceSpec extends ObjectBehavior
$this->getSpecClassname()->shouldReturn('spec\Local\usr\lib\configSpec');
}
function it_does_not_split_underscores_when_locator_has_psr4_prefix($locator)
{
$this->beConstructedWith(array('usr', 'lib', 'config_test'), $locator);
$locator->getFullSrcPath()->willReturn('/local/');
$locator->getFullSpecPath()->willReturn('/local/spec/');
$locator->isPSR4()->willReturn(true);
$this->getSrcFilename()->shouldReturn('/local/usr/lib/config_test.php');
$this->getSpecFilename()->shouldReturn('/local/spec/usr/lib/config_testSpec.php');
}
}

View File

@@ -31,6 +31,12 @@ class ClassFileAnalyserSpec extends ObjectBehavior
$this->getEndLineOfNamedMethod($class, 'methodOne')->shouldReturn(13);
}
function it_should_return_the_line_number_of_the_end_of_the_last_method()
{
$class = $this->getSingleMethodClassContainingAnonymousFunction();
$this->getEndLineOfLastMethod($class)->shouldReturn(12);
}
private function getSingleMethodClass()
{
return <<<SINGLE_METHOD_CLASS
@@ -63,4 +69,28 @@ final class MyClass
}
NO_METHOD_CLASS;
}
private function getSingleMethodClassContainingAnonymousFunction()
{
return <<<SINGLE_METHOD_CLASS_CONTAINING_ANONYMOUS_FUNCTION
<?php
namespace MyNamespace;
class MyClass
{
public function testAnAnonymousFunction()
{
return function () {
return 'something';
};
}
// com
}
/*
comment }
*/
SINGLE_METHOD_CLASS_CONTAINING_ANONYMOUS_FUNCTION;
}
}

View File

@@ -21,11 +21,13 @@ final class TokenizedTypeHintRewriter implements TypeHintRewriter
const STATE_READING_CLASS = 1;
const STATE_READING_FUNCTION = 2;
const STATE_READING_ARGUMENTS = 3;
const STATE_READING_FUNCTION_BODY = 4;
private $state = self::STATE_DEFAULT;
private $currentClass;
private $currentFunction;
private $currentBodyLevel;
private $typehintTokens = array(
T_WHITESPACE, T_STRING, T_NS_SEPARATOR
@@ -79,12 +81,10 @@ final class TokenizedTypeHintRewriter implements TypeHintRewriter
private function stripTypeHints($tokens)
{
foreach ($tokens as $index => $token) {
switch ($this->state) {
case self::STATE_READING_ARGUMENTS:
if (')' == $token) {
$this->state = self::STATE_READING_CLASS;
$this->currentFunction = '';
}
elseif ($this->tokenHasType($token, T_VARIABLE)) {
$this->extractTypehints($tokens, $index, $token);
@@ -99,12 +99,30 @@ final class TokenizedTypeHintRewriter implements TypeHintRewriter
}
break;
case self::STATE_READING_CLASS:
if ($this->tokenHasType($token, T_STRING) && !$this->currentClass) {
if ('{' == $token && $this->currentFunction) {
$this->state = self::STATE_READING_FUNCTION_BODY;
$this->currentBodyLevel = 1;
}
elseif ($this->tokenHasType($token, T_STRING) && !$this->currentClass) {
$this->currentClass = $token[1];
}
elseif($this->tokenHasType($token, T_FUNCTION)) {
$this->state = self::STATE_READING_FUNCTION;
}
break;
case self::STATE_READING_FUNCTION_BODY:
if ('{' == $token) {
$this->currentBodyLevel++;
}
elseif ('}' == $token) {
$this->currentBodyLevel--;
if ($this->currentBodyLevel === 0) {
$this->currentFunction = '';
$this->state = self::STATE_READING_CLASS;
}
}
break;
default:
if ($this->tokenHasType($token, T_CLASS)) {

View File

@@ -83,7 +83,7 @@ final class TokenizedCodeWriter implements CodeWriter
*/
private function insertStringAfterLine($target, $toInsert, $line, $leadingNewline = true)
{
$lines = explode(PHP_EOL, $target);
$lines = explode("\n", $target);
$lastLines = array_slice($lines, $line);
$toInsert = trim($toInsert, "\n\r");
if ($leadingNewline) {
@@ -103,7 +103,7 @@ final class TokenizedCodeWriter implements CodeWriter
private function insertStringBeforeLine($target, $toInsert, $line)
{
$line--;
$lines = explode(PHP_EOL, $target);
$lines = explode("\n", $target);
$lastLines = array_slice($lines, $line);
array_unshift($lastLines, trim($toInsert, "\n\r") . PHP_EOL);
array_splice($lines, $line, count($lines), $lastLines);

View File

@@ -21,6 +21,7 @@ use PhpSpec\Formatter\Presenter\Exception\CallArgumentsPresenter;
use PhpSpec\Formatter\Presenter\Exception\GenericPhpSpecExceptionPresenter;
use PhpSpec\Formatter\Presenter\Exception\HtmlPhpSpecExceptionPresenter;
use PhpSpec\Formatter\Presenter\Exception\SimpleExceptionPresenter;
use PhpSpec\Formatter\Presenter\Exception\SimpleExceptionElementPresenter;
use PhpSpec\Formatter\Presenter\Exception\TaggingExceptionElementPresenter;
use PhpSpec\Formatter\Presenter\SimplePresenter;
use PhpSpec\Formatter\Presenter\TaggedPresenter;
@@ -173,7 +174,7 @@ class PresenterAssembler
private function assembleHtmlPresenter(ServiceContainer $container)
{
$container->setShared('formatter.presenter.html', function (ServiceContainer $c) {
new SimplePresenter(
return new SimplePresenter(
$c->get('formatter.presenter.value_presenter'),
new SimpleExceptionPresenter(
$c->get('formatter.presenter.differ'),

View File

@@ -669,6 +669,12 @@ class ContainerAssembler
$c->get('process.executioncontext')
);
});
$container->setShared('process.rerunner.platformspecific.windowspassthru', function (ServiceContainer $c) {
return ReRunner\WindowsPassthruReRunner::withExecutionContext(
$c->get('process.phpexecutablefinder'),
$c->get('process.executioncontext')
);
});
$container->setShared('process.phpexecutablefinder', function () {
return new PhpExecutableFinder();
});

View File

@@ -96,7 +96,7 @@ class ConsoleFormatter extends BasicFormatter implements FatalPresenter
public function displayFatal(CurrentExampleTracker $currentExample, $error)
{
if (
(null !== $error && $currentExample->getCurrentExample()) ||
(null !== $error && ($currentExample->getCurrentExample() || $error['type'] == E_ERROR)) ||
(is_null($currentExample->getCurrentExample()) && defined('HHVM_VERSION'))
) {
ini_set('display_errors', "stderr");

View File

@@ -166,6 +166,14 @@ class PSR0Locator implements ResourceLocatorInterface
;
}
/**
* @return boolean
*/
public function isPSR4()
{
return $this->psr4Prefix !== null;
}
/**
* @param string $query
*
@@ -272,6 +280,11 @@ class PSR0Locator implements ResourceLocatorInterface
return $resources;
}
/**
* @param $path
*
* @return null|string
*/
private function findSpecClassname($path)
{
// Find namespace and class name
@@ -337,6 +350,11 @@ class PSR0Locator implements ResourceLocatorInterface
return new PSR0Resource(explode('\\', $classname), $this);
}
/**
* @param string $classname
*
* @throws InvalidArgumentException
*/
private function validatePsr0Classname($classname)
{
$pattern = '/^([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*[\/\\\\]?)*[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$/';
@@ -351,7 +369,8 @@ class PSR0Locator implements ResourceLocatorInterface
}
/**
* @param $query
* @param string $query
*
* @return string
*/
private function getQueryPath($query)
@@ -359,12 +378,13 @@ class PSR0Locator implements ResourceLocatorInterface
$sepr = DIRECTORY_SEPARATOR;
$replacedQuery = str_replace(array('\\', '/'), $sepr, $query);
if (false !== strpos($query, '\\')) {
if ($this->queryContainsQualifiedClassName($query)) {
$namespacedQuery = null === $this->psr4Prefix ?
$replacedQuery :
substr($replacedQuery, strlen($this->srcNamespace));
$path = $this->fullSpecPath . $namespacedQuery . 'Spec.php';
if ($this->filesystem->pathExists($path)) {
return $path;
}
@@ -372,4 +392,34 @@ class PSR0Locator implements ResourceLocatorInterface
return rtrim(realpath($replacedQuery), $sepr);
}
/**
* @param string $query
*
* @return bool
*/
private function queryContainsQualifiedClassName($query)
{
return $this->queryContainsBlackslashes($query) && !$this->isWindowsPath($query);
}
/**
* @param string $query
*
* @return bool
*/
private function queryContainsBlackslashes($query)
{
return false !== strpos($query, '\\');
}
/**
* @param string $query
*
* @return bool
*/
private function isWindowsPath($query)
{
return preg_match('/^\w:/', $query);
}
}

View File

@@ -57,6 +57,10 @@ class PSR0Resource implements ResourceInterface
*/
public function getSrcFilename()
{
if ($this->locator->isPSR4()) {
return $this->locator->getFullSrcPath().implode(DIRECTORY_SEPARATOR, $this->parts).'.php';
}
$nsParts = $this->parts;
$classname = array_pop($nsParts);
$parts = array_merge($nsParts, explode('_', $classname));
@@ -88,6 +92,11 @@ class PSR0Resource implements ResourceInterface
*/
public function getSpecFilename()
{
if ($this->locator->isPSR4()) {
return $this->locator->getFullSpecPath().
implode(DIRECTORY_SEPARATOR, $this->parts).'Spec.php';
}
$nsParts = $this->parts;
$classname = array_pop($nsParts);
$parts = array_merge($nsParts, explode('_', $classname));

View File

@@ -43,7 +43,8 @@ class PassthruReRunner extends PhpExecutableReRunner
{
return (php_sapi_name() == 'cli')
&& $this->getExecutablePath()
&& function_exists('passthru');
&& function_exists('passthru')
&& (stripos(PHP_OS, "win") !== 0);
}
public function reRunSuite()

View File

@@ -14,6 +14,7 @@
namespace PhpSpec\Util;
use PhpSpec\Exception\Generator\NamedMethodNotFoundException;
use PhpSpec\Exception\Generator\NoMethodFoundInClass;
final class ClassFileAnalyser
{
@@ -37,7 +38,7 @@ final class ClassFileAnalyser
public function getEndLineOfLastMethod($class)
{
$tokens = $this->getTokensForClass($class);
$index = $this->findIndexOfMethodEnd($tokens, $this->findIndexOfLastMethod($tokens));
$index = $this->findEndOfLastMethod($tokens, $this->findIndexOfClassEnd($tokens));
return $tokens[$index][2];
}
@@ -86,19 +87,6 @@ final class ClassFileAnalyser
}
}
/**
* @param array $tokens
* @return int
*/
private function findIndexOfLastMethod(array $tokens)
{
for ($i = count($tokens) - 1; $i >= 0; $i--) {
if ($this->tokenIsFunction($tokens[$i])) {
return $i;
}
}
}
/**
* @param array $tokens
* @param int $index
@@ -158,7 +146,7 @@ final class ClassFileAnalyser
private function findIndexOfNamedMethodEnd(array $tokens, $methodName)
{
$index = $this->findIndexOfNamedMethod($tokens, $methodName);
return $this->findIndexOfMethodEnd($tokens, $index);
return $this->findIndexOfMethodOrClassEnd($tokens, $index);
}
/**
@@ -203,7 +191,7 @@ final class ClassFileAnalyser
* @param int $index
* @return int
*/
private function findIndexOfMethodEnd(array $tokens, $index)
private function findIndexOfMethodOrClassEnd(array $tokens, $index)
{
$braceCount = 0;
@@ -232,4 +220,32 @@ final class ClassFileAnalyser
{
return is_array($token) && $token[0] === T_FUNCTION;
}
/**
* @param array $tokens
* @return int
*/
private function findIndexOfClassEnd(array $tokens)
{
$classTokens = array_filter($tokens, function ($token) {
return is_array($token) && $token[0] === T_CLASS;
});
$classTokenIndex = key($classTokens);
return $this->findIndexOfMethodOrClassEnd($tokens, $classTokenIndex) - 1;
}
/**
* @param array $tokens
* @param int $index
* @return int
*/
public function findEndOfLastMethod(array $tokens, $index)
{
for ($i = $index - 1; $i > 0; $i--) {
if ($tokens[$i] == "}") {
return $i + 1;
}
}
throw new NoMethodFoundInClass();
}
}