update v1.0.4

This commit is contained in:
sujitprasad
2016-01-04 18:05:45 +05:30
parent 372485336b
commit 4864e5a3f1
529 changed files with 20956 additions and 8178 deletions

View File

@@ -5,7 +5,7 @@ imports:
tools:
php_code_sniffer:
filter:
excluded-paths: [ spec/*, integration/*, features/* ]
excluded-paths: [ spec/*, integration/*, features/*, src/PhpSpec/Loader/StreamWrapper.php ]
config:
standard: PSR2

View File

@@ -19,7 +19,6 @@ matrix:
- php: hhvm
- php: 7.0
allow_failures:
- php: 7.0
- env: DEPENDENCIES='dev'
fast_finish: true
@@ -33,7 +32,7 @@ install:
- if [ "$DEPENDENCIES" == "low" ]; then composer update --prefer-lowest; fi;
before_script:
- echo "<?php if (PHP_VERSION_ID >= 50400) echo ',@php5.4';" > php_version_tags.php
- echo "<?php if (defined('HHVM_VERSION')) { echo ',@hhvm'; } else { if (PHP_VERSION_ID >= 50400) echo ',@php5.4'; if (PHP_VERSION_ID >= 70000) echo ',@php7'; }" > php_version_tags.php
script:
- bin/phpspec run --format=pretty

View File

@@ -1,3 +1,40 @@
2.4.0 / 2015/11/28
==================
* Improved docblock for beConstructedThrough()
2.4.0-rc1 / 2015/11/20
======================
* No changes from RC1
2.4.0-beta / 2015-11-13
=======================
* Handle and present fatal errors
2.4.0-alpha2 / 2015-11-03
=========================
* Fixed edge case with partial use statements
2.4.0-alpha1 / 2015-11-01
=========================
* Initial support for typehinted doubles in PHP7
* Specs can now be run by specifying a fully qualified class name
* New shouldContain matcher for strings
* Warning added when trying to typehint scalars or callable in spec
* No longer truncates strings when diffing arrays in verbose mode
* New %resource_name% placeholder for generated specs
* Fixed case error in class name that triggered strictness warnings on some platforms
* Fixed undefined index error in some versions of Windows
* Clarified in composer that ext-tokenizer is required
* Supported installation with Symfony 3.0
* Fixed error when spec and src paths are the same
* New event is fired when phpspec creates a file
* Internal refactoring of Presenter objects
2.3.0 / 2015-09-07
==================

50
vendor/phpspec/phpspec/appveyor.yml vendored Normal file
View File

@@ -0,0 +1,50 @@
build: false
shallow_clone: true
platform: x86
clone_folder: c:\projects\phpspec
environment:
matrix:
- PHP_DOWNLOAD_FILE: php-5.6.14-nts-Win32-VC11-x86.zip
matrix:
allow_failures:
- PHP_DOWNLOAD_FILE: php-5.6.14-nts-Win32-VC11-x86.zip
skip_commits:
message: /\[ci skip\]/
cache:
- c:\php -> appveyor.yml
- '%LOCALAPPDATA%\Composer'
- vendor
init:
- SET PATH=c:\php;%PATH%
- SET COMPOSER_NO_INTERACTION=1
- SET PHP=1
- SET ANSICON=121x90 (121x90)
install:
- IF EXIST c:\php (SET PHP=0) ELSE (mkdir c:\php)
- cd c:\php
- IF %PHP%==1 appveyor DownloadFile http://windows.php.net/downloads/releases/archives/%PHP_DOWNLOAD_FILE%
- IF %PHP%==1 7z x %PHP_DOWNLOAD_FILE% -y > 7z.log
- IF %PHP%==1 echo @php %%~dp0composer.phar %%* > composer.bat
- appveyor DownloadFile https://getcomposer.org/composer.phar
- copy php.ini-production php.ini /Y
- echo date.timezone="UTC" >> php.ini
- echo extension_dir=ext >> php.ini
- echo extension=php_openssl.dll >> php.ini
- echo extension=php_curl.dll >> php.ini
- echo extension=php_mbstring.dll >> php.ini
- echo extension=php_fileinfo.dll >> php.ini
- cd c:\projects\phpspec
- SET COMPOSER_ROOT_VERSION=dev-master
- composer update --no-progress --ansi
test_script:
- cd c:\projects\phpspec
- php bin\phpspec run --format=pretty
- php vendor\phpunit\phpunit\phpunit --testdox
- php vendor\behat\behat\bin\behat --format=pretty --tags="~@php-version,@php5.4"

View File

@@ -9,8 +9,6 @@ default:
smoke:
contexts: [ IsolatedProcessContext, FilesystemContext ]
filters: { tags: @smoke && ~@isolated }
formatters:
progress: ~
no-smoke:
suites:

View File

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

View File

@@ -22,18 +22,19 @@
"phpspec/prophecy": "~1.4",
"phpspec/php-diff": "~1.0.0",
"sebastian/exporter": "~1.0",
"symfony/console": "~2.3",
"symfony/event-dispatcher": "~2.1",
"symfony/process": "^2.6",
"symfony/finder": "~2.1",
"symfony/yaml": "~2.1",
"doctrine/instantiator": "^1.0.1"
"symfony/console": "~2.3|~3.0",
"symfony/event-dispatcher": "~2.1|~3.0",
"symfony/process": "^2.6|~3.0",
"symfony/finder": "~2.1|~3.0",
"symfony/yaml": "~2.1|~3.0",
"doctrine/instantiator": "^1.0.1",
"ext-tokenizer": "*"
},
"require-dev": {
"behat/behat": "^3.0.11",
"bossa/phpspec2-expect": "~1.0",
"symfony/filesystem": "~2.1",
"symfony/filesystem": "~2.1|~3.0",
"phpunit/phpunit": "~4.4"
},

View File

@@ -7,11 +7,10 @@ use Behat\Gherkin\Node\TableNode;
use Fake\Prompter;
use Fake\ReRunner;
use Matcher\ApplicationOutputMatcher;
use Matcher\ExitStatusMatcher;
use Matcher\ValidJUnitXmlMatcher;
use PhpSpec\Console\Application;
use PhpSpec\Loader\StreamWrapper;
use PhpSpec\Matcher\MatchersProviderInterface;
use Symfony\Component\Console\Helper\HelperSet;
use Symfony\Component\Console\Tester\ApplicationTester;
/**
@@ -49,6 +48,8 @@ class ApplicationContext implements Context, MatchersProviderInterface
*/
public function setupApplication()
{
StreamWrapper::register();
$this->application = new Application('2.1-dev');
$this->application->setAutoExit(false);
@@ -89,14 +90,16 @@ class ApplicationContext implements Context, MatchersProviderInterface
* @When I run phpspec (non interactively)
* @When I run phpspec using the :formatter format
* @When I run phpspec with the :option option
* @When I run phpspec with :spec specs to run
* @When /I run phpspec with option (?P<option>.*)/
* @When /I run phpspec (?P<interactive>interactively)$/
* @When /I run phpspec (?P<interactive>interactively) with the (?P<option>.*) option/
*/
public function iRunPhpspec($formatter = null, $option = null, $interactive=null)
public function iRunPhpspec($formatter = null, $option = null, $interactive = null, $spec = null)
{
$arguments = array (
'command' => 'run'
'command' => 'run',
'spec' => $spec
);
if ($formatter) {
@@ -294,4 +297,31 @@ class ApplicationContext implements Context, MatchersProviderInterface
new ValidJUnitXmlMatcher()
);
}
/**
* @When I run phpspec with the spec :spec
*/
public function iRunPhpspecWithTheSpec($spec)
{
$arguments = array (
'command' => 'run',
1 => $spec
);
$this->lastExitCode = $this->tester->run($arguments, array('interactive' => false));
}
/**
* @When I run phpspec with the spec :spec and the config :config
*/
public function iRunPhpspecWithTheSpecAndTheConfig($spec, $config)
{
$arguments = array (
'command' => 'run',
1 => $spec,
'--config' => $config
);
$this->lastExitCode = $this->tester->run($arguments, array('interactive' => false));
}
}

View File

@@ -64,15 +64,22 @@ class FilesystemContext implements Context, MatchersProviderInterface
/**
* @Given the class file :file contains:
* @Given the spec file :file contains:
* @Given the trait file :file contains:
*/
public function theClassOrTraitOrSpecFileContains($file, PyStringNode $contents)
public function theClassOrTraitFileContains($file, PyStringNode $contents)
{
$this->theFileContains($file, $contents);
require_once($file);
}
/**
* @Given the spec file :file contains:
*/
public function theSpecFileContains($file, PyStringNode $contents)
{
$this->theFileContains($file, $contents);
}
/**
* @Given the config file contains:
*/

View File

@@ -3,6 +3,8 @@
use Behat\Behat\Context\Context;
use Behat\Behat\Context\SnippetAcceptingContext;
use Symfony\Component\Process\Process;
use Symfony\Component\Filesystem\Filesystem;
use Behat\Gherkin\Node\PyStringNode;
/**
* Defines application features from the specific context.
@@ -14,6 +16,8 @@ class IsolatedProcessContext implements Context, SnippetAcceptingContext
*/
private $process;
private $lastOutput;
/**
* @Given I have started describing the :class class
*/
@@ -36,7 +40,8 @@ class IsolatedProcessContext implements Context, SnippetAcceptingContext
$command = sprintf('%s %s', $this->buildPhpSpecCmd(), 'run');
$env = array(
'SHELL_INTERACTIVE' => true,
'HOME' => $_SERVER['HOME']
'HOME' => $_SERVER['HOME'],
'PATH' => $_SERVER['PATH']
);
$this->process = $process = new Process($command);
@@ -69,4 +74,51 @@ class IsolatedProcessContext implements Context, SnippetAcceptingContext
{
expect($this->process->getErrorOutput())->toMatch('/autoload/');
}
/**
* @When I run phpspec
*/
public function iRunPhpspec()
{
$process = new Process(
$this->buildPhpSpecCmd() . ' run'
);
$process->run();
$this->lastOutput = $process->getOutput();
}
/**
* @When I run phpspec with the :formatter formatter
*/
public function iRunPhpspecWithThe($formatter)
{
$process = new Process(
$this->buildPhpSpecCmd() . " --format=$formatter run"
);
$process->run();
$this->lastOutput = $process->getErrorOutput();
}
/**
* @When I run phpspec on HHVM with the :formatter formatter
*/
public function iRunPhpspecOnHhvmWithThe($formatter)
{
$process = new Process(
$this->buildPhpSpecCmd() . " --format=$formatter run"
);
$process->run();
$this->lastOutput = $process->getOutput();
}
/**
* @Then I should see :message
*/
public function iShouldSee($message)
{
expect(strpos($this->lastOutput, $message))->toNotBe(false);
}
}

View File

@@ -272,7 +272,7 @@ Feature: Developer generates a method
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
class CommentmethodSpec extends ObjectBehavior
class CommentMethodSpec extends ObjectBehavior
{
function it_should_do_something()
{

View File

@@ -334,7 +334,7 @@ Feature: Developer specifies object construction
Then the suite should pass
Scenario: Developer cannot redefine constructor parameters if object is already instantiated
Given the spec file "spec/Runner/ConstructorExample9/ClassWithConstructorSpec.php" contains:
Given the spec file "spec/Runner/ConstructorExample9/ClassConstructorSpec.php" contains:
"""
<?php

View File

@@ -0,0 +1,126 @@
Feature: Developer is notified of which scenario caused a fatal error
As a Developer
I want to know in which scenario or example my script was running
So that I can better trace where my changes caused a fatal error
@isolated
Scenario: Spec attempts to call an undeclared function and outputs to stdout
Given the spec file "spec/Message/Fatal/FatalSpec.php" contains:
"""
<?php
namespace spec\Message\Fatal;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
class FatalSpec extends ObjectBehavior
{
function it_fatals_when_calling_an_undeclared_function()
{
anything();
}
}
"""
And the class file "src/Message/Fatal/Fatal.php" contains:
"""
<?php
namespace Message\Fatal;
class Fatal
{
public function __construct($param)
{
if ($param == 'throw') {
throw new \Exception();
}
}
}
"""
When I run phpspec
Then I should see "Fatal error happened while executing the following"
And I should see "it fatals when calling an undeclared function"
@isolated @php-version @php5.4 @php7
Scenario: Fatal error writer message not shown, when formatter does not support it, outputs to stderr.
Given the spec file "spec/Message/Fatal/Fatal2Spec.php" contains:
"""
<?php
namespace spec\Message\Fatal;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
class Fatal2Spec extends ObjectBehavior
{
function it_fatals_when_calling_an_undeclared_function()
{
anything();
}
}
"""
And the class file "src/Message/Fatal/Fatal2.php" contains:
"""
<?php
namespace Message\Fatal;
class Fatal2
{
public function __construct($param)
{
if ($param == 'throw') {
throw new \Exception();
}
}
}
"""
When I run phpspec with the "junit" formatter
Then I should see "Call to undefined function"
@isolated @hhvm
Scenario: Fatal error writer message not shown, when formatter does not support it, outputs to stdout.
Given the spec file "spec/Message/Fatal/FatalHhvmSpec.php" contains:
"""
<?php
namespace spec\Message\Fatal;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
class FatalHhvmSpec extends ObjectBehavior
{
function it_fatals_when_calling_an_undeclared_function()
{
anything();
}
}
"""
And the class file "src/Message/Fatal/FatalHhvm.php" contains:
"""
<?php
namespace Message\Fatal;
class FatalHhvm
{
public function __construct($param)
{
if ($param == 'throw') {
throw new \Exception();
}
}
}
"""
When I run phpspec on HHVM with the "junit" formatter
Then I should see "Call to undefined function"

View File

@@ -0,0 +1,82 @@
Feature: Developer is shown a parse error
As a Developer
I want to know if a parse error was thrown
So that I can know that I can handle pass errors
@isolated @php-version @php5.4 @php7
Scenario: Spec attempts to call an undeclared function and outputs to stderr
Given the spec file "spec/Message/Fatal/ParseSpec.php" contains:
"""
<?php
namespace spec\Message\Fatal;
use Parse;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
class ParseSpec extends ObjectBehavior
{
function it_thro ws_a_syntax_error()
{
$this->cool();
}
}
"""
And the spec file "src/Message/Fatal/Parse.php" contains:
"""
<?php
namespace Message\Parse;
class Parse
{
public function cool()
{
return true;
}
}
"""
When I run phpspec with the "junit" formatter
Then I should see "syntax error"
@isolated @hhvm
Scenario: Spec attempts to call an undeclared function and outputs to stdout
Given the spec file "spec/Message/Fatal/ParseHhvmSpec.php" contains:
"""
<?php
namespace spec\Message\Fatal;
use Parse;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
class ParseHhvmSpec extends ObjectBehavior
{
function it_thro ws_a_syntax_error()
{
$this->cool();
}
}
"""
And the spec file "src/Message/Fatal/ParseHhvm.php" contains:
"""
<?php
namespace Message\Parse;
class ParseHhvm
{
public function cool()
{
return true;
}
}
"""
When I run phpspec on HHVM with the "junit" formatter
Then I should see "syntax error"

View File

@@ -91,9 +91,9 @@ Feature: Developer is shown diffs
@@ -1,4 +1,4 @@
[
- int => 1,
- string => ""foo"...",
- string => "foo",
+ int => 3,
+ string => ""bar"...",
+ string => "bar",
]
"""
@@ -266,13 +266,13 @@ Feature: Developer is shown diffs
"""
@@ -1,4 +1,4 @@
[
key1 => ""val1"...",
- key2 => ""val2"...",
+ key5 => ""val5"...",
key1 => "val1",
- key2 => "val2",
+ key5 => "val5",
]
"""
Scenario: Unexpected method arguments call with multiple arguments icluding null diffing
Scenario: Unexpected method arguments call with multiple arguments including null diffing
Given the spec file "spec/Diffs/DiffExample6/ClassUnderSpecificationSpec.php" contains:
"""
<?php
@@ -328,8 +328,8 @@ Feature: Developer is shown diffs
"""
@@ -1,3 +1,3 @@
[
- key => ""value"...",
+ key => ""another value"...",
- key => "value",
+ key => "another value",
]
"""
And I should see:
@@ -464,3 +464,103 @@ Feature: Developer is shown diffs
- methodTwo(exact("value"))
- methodOne(exact("another value"))
"""
Scenario: Array diffing with long strings
Given the spec file "spec/Diffs/DiffExample9/ClassWithArraysSpec.php" contains:
"""
<?php
namespace spec\Diffs\DiffExample9;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
class ClassWithArraysSpec extends ObjectBehavior
{
function it_is_equal()
{
$this->getArray()->shouldReturn(array(
'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam nunc nulla, posuere et arcu ut.'
));
}
}
"""
And the class file "src/Diffs/DiffExample9/ClassWithArrays.php" contains:
"""
<?php
namespace Diffs\DiffExample9;
class ClassWithArrays
{
public function getArray()
{
return array(
'Vestibulum vehicula nisl at ex maximus, nec lobortis orci luctus. Integer euismod in nunc nec lobortis'
);
}
}
"""
When I run phpspec with the "verbose" option
Then I should see:
"""
@@ -1,3 +1,3 @@
[
- 0 => "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam nunc nulla, posuere et arcu ut.",
+ 0 => "Vestibulum vehicula nisl at ex maximus, nec lobortis orci luctus. Integer euismod in nunc nec lobortis",
]
"""
Scenario: Array diffing with multi line strings
Given the spec file "spec/Diffs/DiffExample10/ClassWithArraysSpec.php" contains:
"""
<?php
namespace spec\Diffs\DiffExample10;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
class ClassWithArraysSpec extends ObjectBehavior
{
function it_is_equal()
{
$this->getArray()->shouldReturn(array(
'Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Etiam nunc nulla, posuere et arcu ut.'
));
}
}
"""
And the class file "src/Diffs/DiffExample10/ClassWithArrays.php" contains:
"""
<?php
namespace Diffs\DiffExample10;
class ClassWithArrays
{
public function getArray()
{
return array(
'Vestibulum vehicula nisl at ex maximus, nec lobortis orci luctus.
Integer euismod in nunc nec lobortis'
);
}
}
"""
When I run phpspec with the "verbose" option
Then I should see:
"""
@@ -1,4 +1,4 @@
[
- 0 => "Lorem ipsum dolor sit amet, consectetur adipiscing elit.
- Etiam nunc nulla, posuere et arcu ut.",
+ 0 => "Vestibulum vehicula nisl at ex maximus, nec lobortis orci luctus.
+ Integer euismod in nunc nec lobortis",
]
"""

View File

@@ -0,0 +1,129 @@
Feature: Developer uses unsupported collaborator type hinting
As a developer
I should be shown special exception when I declare collaborators with unsupported type hinting
Scenario: Array collaborator type hinting
Given the spec file "spec/InvalidUsage/InvalidUsageExample1/StorageSpec.php" contains:
"""
<?php
namespace spec\InvalidUsage\InvalidUsageExample1;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
class StorageSpec extends ObjectBehavior
{
function it_can_store_data(array $data)
{
$this->store($data)->shouldReturn(true);
}
}
"""
And the class file "src/InvalidUsage/InvalidUsageExample1/Storage.php" contains:
"""
<?php
namespace InvalidUsage\InvalidUsageExample1;
class Storage
{
public function store(array $data)
{
return true;
}
}
"""
When I run phpspec
Then I should see:
"""
collaborator must be an object: argument 0 defined in
spec\InvalidUsage\InvalidUsageExample1\StorageSpec::it_can_store_data.
"""
@php-version @php5.4
Scenario: Callable collaborator type hinting
Given the spec file "spec/InvalidUsage/InvalidUsageExample2/InvokerSpec.php" contains:
"""
<?php
namespace spec\InvalidUsage\InvalidUsageExample2;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
class InvokerSpec extends ObjectBehavior
{
function it_invokes_callable(callable $callback)
{
$this->invoke($callback)->shouldReturn(true);
}
}
"""
And the class file "src/InvalidUsage/InvalidUsageExample2/Invoker.php" contains:
"""
<?php
namespace InvalidUsage\InvalidUsageExample2;
class Invoker
{
public function invoke(callable $data, array $parameters = array())
{
return true;
}
}
"""
When I run phpspec
Then I should see:
"""
collaborator must be an object: argument 0 defined in
spec\InvalidUsage\InvalidUsageExample2\InvokerSpec::it_invokes_callable.
"""
@php-version @php7
Scenario: Integer collaborator type hinting
Given the spec file "spec/InvalidUsage/InvalidUsageExample3/StorageSpec.php" contains:
"""
<?php
namespace spec\InvalidUsage\InvalidUsageExample3;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
class StorageSpec extends ObjectBehavior
{
function it_can_store_data(int $data)
{
$this->store($data)->shouldReturn(true);
}
}
"""
And the class file "src/InvalidUsage/InvalidUsageExample3/Storage.php" contains:
"""
<?php
namespace InvalidUsage\InvalidUsageExample3;
class Storage
{
public function store(int $data)
{
return true;
}
}
"""
When I run phpspec
Then I should see:
"""
collaborator must be an object: argument 0 defined in
spec\InvalidUsage\InvalidUsageExample3\StorageSpec::it_can_store_data.
"""

View File

@@ -0,0 +1,41 @@
Feature: Developer uses string-contain matcher
As a Developer
I want a string-contain matcher
In order to confirm a string contains an expected substring
Scenario: "Contain" alias matches using the string-contain matcher
Given the spec file "spec/Matchers/StringContainExample1/MovieSpec.php" contains:
"""
<?php
namespace spec\Matchers\StringContainExample1;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
class MovieSpec extends ObjectBehavior
{
function it_should_have_a_title_that_contains_days()
{
$this->getTitle()->shouldContain('days');
}
}
"""
And the class file "src/Matchers/StringContainExample1/Movie.php" contains:
"""
<?php
namespace Matchers\StringContainExample1;
class Movie
{
public function getTitle()
{
return 'The future days of past';
}
}
"""
When I run phpspec
Then the suite should pass

View File

@@ -130,3 +130,75 @@ Feature: Developer runs the specs
"""
When I run phpspec
Then I should see "Letgo is called"
Scenario: Fully qualified class name can run specs
Given the spec file "spec/Runner/Namespace/Example1Spec.php" contains:
"""
<?php
namespace spec\Runner\TestNamespace;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
class Example1Spec extends ObjectBehavior
{
function it_is_initializable()
{
$this->shouldHaveType('Runner\TestNamespace\Example1');
}
}
"""
And the class file "src/Runner/TestNamespace/Example1.php" contains:
"""
<?php
namespace Runner\TestNamespace;
class Example1
{
}
"""
When I run phpspec with the spec "Runner\TestNamespace\Example1"
Then the suite should pass
Scenario: Fully qualified PSR4 class name can run specs
Given the spec file "spec/Runner/Namespace/Example2Spec.php" contains:
"""
<?php
namespace spec\Psr4\Runner\TestNamespace;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
class Example2Spec extends ObjectBehavior
{
function it_is_initializable()
{
$this->shouldHaveType('Psr4\Runner\TestNamespace\Example2');
}
}
"""
And the class file "src/Psr4/Runner/TestNamespace/Example2.php" contains:
"""
<?php
namespace Psr4\Runner\TestNamespace;
class Example2
{
}
"""
And the config file located in "Psr4" contains:
"""
suites:
behat_suite:
namespace: Psr4
psr4_prefix: Psr4
"""
When I run phpspec with the spec "Psr4\Runner\TestNamespace\Example2" and the config "Psr4/phpspec.yml"
Then the suite should pass

View File

@@ -0,0 +1,52 @@
Feature: Developer runs specs with the given specs path configured to be the same as source path
As a Developer
I want to run the specs from given directory
In order to get feedback on a state of requested part of my application
Scenario: Reporting success when running a spec with correctly implemented class, passing spec path as an argument
Given the config file contains:
"""
suites:
code_generator_suite:
namespace: Runner\SpecPathExample
psr4_prefix: Runner\SpecPathExample
src_path: src/Runner/SpecPathExample
spec_path: src/Runner/SpecPathExample
"""
And the spec file "src/Runner/SpecPathExample/spec/MarkdownSpec.php" contains:
"""
<?php
namespace spec\Runner\SpecPathExample;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
class MarkdownSpec extends ObjectBehavior
{
function it_converts_plain_text_to_html_paragraphs()
{
$this->toHtml('Hi, there')->shouldReturn('<p>Hi, there</p>');
}
}
"""
And the class file "src/Runner/SpecPathExample/Markdown.php" contains:
"""
<?php
namespace Runner\SpecPathExample;
class Markdown
{
public function toHtml($text)
{
return sprintf('<p>%s</p>', $text);
}
}
"""
When I run phpspec with "src/Runner/SpecPathExample/spec" specs to run
Then 1 example should have been run
And the suite should pass

View File

@@ -0,0 +1,35 @@
<?php
namespace integration\PhpSpec\Loader;
use PhpSpec\CodeAnalysis\TokenizedNamespaceResolver;
use PhpSpec\CodeAnalysis\TokenizedTypeHintRewriter;
use PhpSpec\Loader\StreamWrapper;
use PhpSpec\Loader\Transformer\InMemoryTypeHintIndex;
use PhpSpec\Loader\Transformer\TypeHintRewriter;
class StreamWrapperTest extends \PHPUnit_Framework_Testcase
{
function setUp()
{
$wrapper = new StreamWrapper();
$wrapper->addTransformer(new TypeHintRewriter(new TokenizedTypeHintRewriter(new InMemoryTypeHintIndex(), new TokenizedNamespaceResolver())));
StreamWrapper::register();
}
/**
* @test
* @requires PHP 7.0
*/
function it_loads_a_spec_with_no_typehints()
{
require StreamWrapper::wrapPath(__DIR__.'/examples/ExampleSpec.php');
$reflection = new \ReflectionClass('integration\PhpSpec\Loader\examples\ExampleSpec');
$method = $reflection->getMethod('it_requires_a_stdclass');
$parameters = $method->getParameters();
$this->assertNull($parameters[0]->getClass());
}
}

View File

@@ -0,0 +1,13 @@
<?php
namespace integration\PhpSpec\Loader\examples;
use PhpSpec\ObjectBehavior;
class ExampleSpec extends ObjectBehavior
{
function it_requires_a_stdclass(\stdClass $class)
{
}
}

View File

@@ -5,7 +5,6 @@ namespace spec\PhpSpec\CodeAnalysis;
use Phpspec\CodeAnalysis\AccessInspectorInterface;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
use spec\PhpSpec\Listener\DoubleOfStdClass;
class MagicAwareAccessInspectorSpec extends ObjectBehavior
{

View File

@@ -0,0 +1,42 @@
<?php
namespace spec\PhpSpec\CodeAnalysis;
use PhpSpec\CodeAnalysis\NamespaceResolver;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
class StaticRejectingNamespaceResolverSpec extends ObjectBehavior
{
function let(NamespaceResolver $namespaceResolver)
{
$this->beConstructedWith($namespaceResolver);
}
function it_is_initializable()
{
$this->shouldHaveType('PhpSpec\CodeAnalysis\NamespaceResolver');
}
function it_delegates_analysis_to_wrapped_resolver(NamespaceResolver $namespaceResolver)
{
$this->analyse('foo');
$namespaceResolver->analyse('foo')->shouldhaveBeenCalled();
}
function it_delegates_resolution_to_wrapped_resolver(NamespaceResolver $namespaceResolver)
{
$namespaceResolver->resolve('Bar')->willReturn('Foo\Bar');
$this->resolve('Bar')->shouldReturn('Foo\Bar');
}
function it_does_not_allow_resolution_of_scalar_types()
{
$this->shouldThrow('PhpSpec\CodeAnalysis\DisallowedScalarTypehintException')->duringResolve('int');
$this->shouldThrow('PhpSpec\CodeAnalysis\DisallowedScalarTypehintException')->duringResolve('float');
$this->shouldThrow('PhpSpec\CodeAnalysis\DisallowedScalarTypehintException')->duringResolve('string');
$this->shouldThrow('PhpSpec\CodeAnalysis\DisallowedScalarTypehintException')->duringResolve('bool');
}
}

View File

@@ -0,0 +1,105 @@
<?php
namespace spec\PhpSpec\CodeAnalysis;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
class TokenizedNamespaceResolverSpec extends ObjectBehavior
{
function it_is_initializable()
{
$this->shouldHaveType('PhpSpec\CodeAnalysis\NamespaceResolver');
}
function it_resolves_types_outside_of_namespaces()
{
$this->analyse('
<?php
class Foo
{
}
');
$this->resolve('Bar')->shouldReturn('Bar');
$this->resolve('Bar')->shouldReturn('Bar');
}
function it_resolves_types_from_current_namespace()
{
$this->analyse('
<?php
namespace Baz;
class Foo
{
}
');
$this->resolve('Foo')->shouldReturn('Baz\Foo');
$this->resolve('Bar')->shouldReturn('Baz\Bar');
}
function it_resolves_types_with_use_statements()
{
$this->analyse('
<?php
namespace Baz;
use Boz\Bar;
class Foo
{
}
');
$this->resolve('Foo')->shouldReturn('Baz\Foo');
$this->resolve('Bar')->shouldReturn('Boz\Bar');
}
function it_resolves_types_with_use_aliases()
{
$this->analyse('
<?php
namespace Baz;
use Boz\Bar as Biz;
class Foo
{
}
');
$this->resolve('Foo')->shouldReturn('Baz\Foo');
$this->resolve('Biz')->shouldReturn('Boz\Bar');
}
function it_resolves_types_with_partial_use_statements()
{
$this->analyse('
<?php
namespace Baz;
use Boz\Bar;
class Foo
{
function it_something(Bar\Baz $boz)
{
}
}
');
$this->resolve('Foo')->shouldReturn('Baz\Foo');
$this->resolve('Bar\Baz')->shouldReturn('Boz\Bar\Baz');
}
}

View File

@@ -0,0 +1,147 @@
<?php
namespace spec\PhpSpec\CodeAnalysis;
use PhpSpec\CodeAnalysis\DisallowedScalarTypehintException;
use PhpSpec\CodeAnalysis\NamespaceResolver;
use PhpSpec\Loader\Transformer\TypeHintIndex;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
class TokenizedTypeHintRewriterSpec extends ObjectBehavior
{
function let(TypeHintIndex $typeHintIndex, NamespaceResolver $namespaceResolver)
{
$this->beConstructedWith($typeHintIndex, $namespaceResolver);
}
function it_is_a_typehint_rewriter()
{
$this->shouldHaveType('PhpSpec\CodeAnalysis\TypeHintRewriter');
}
function it_leaves_alone_specs_with_no_typehints()
{
$this->rewrite('
<?php
class Foo
{
public function bar()
{
}
}
')->shouldReturn('
<?php
class Foo
{
public function bar()
{
}
}
');
}
function it_removes_typehints_from_single_argument_methods()
{
$this->rewrite('
<?php
class Foo
{
public function bar(\Foo\Bar $bar)
{
}
}
')->shouldReturn('
<?php
class Foo
{
public function bar($bar)
{
}
}
');
}
function it_removes_typehints_for_multiple_arguments_in_methods()
{
$this->rewrite('
<?php
class Foo
{
public function bar(Bar $bar, Baz $baz)
{
}
}
')->shouldReturn('
<?php
class Foo
{
public function bar($bar,$baz)
{
}
}
');
}
function it_indexes_typehints_that_are_removed(TypeHintIndex $typeHintIndex, NamespaceResolver $namespaceResolver)
{
$namespaceResolver->analyse(Argument::any())->shouldBeCalled();
$namespaceResolver->resolve('Foo')->willReturn('Foo');
$namespaceResolver->resolve('Foo\Bar')->willReturn('Foo\Bar');
$namespaceResolver->resolve('Baz')->willReturn('Baz');
$this->rewrite('
<?php
class Foo
{
public function bar(Foo\Bar $bar, Baz $baz)
{
}
}
');
$typeHintIndex->add('Foo', 'bar', '$bar', 'Foo\Bar')->shouldHaveBeenCalled();
$typeHintIndex->add('Foo', 'bar', '$baz', 'Baz')->shouldHaveBeenCalled();
}
function it_indexes_invalid_typehints(
TypeHintIndex $typeHintIndex,
NamespaceResolver $namespaceResolver
) {
$e = new DisallowedScalarTypehintException();
$namespaceResolver->analyse(Argument::any())->shouldBeCalled();
$namespaceResolver->resolve('Foo')->willReturn('Foo');
$namespaceResolver->resolve('int')->willThrow($e);
$this->rewrite('
<?php
class Foo
{
public function bar(int $bar)
{
}
}
');
$typeHintIndex->addInvalid('Foo', 'bar', '$bar', $e)->shouldHaveBeenCalled();
$typeHintIndex->add('Foo', 'bar', '$bar', Argument::any())->shouldNotHaveBeenCalled();
}
}

View File

@@ -0,0 +1,109 @@
<?php
namespace spec\PhpSpec\CodeGenerator\Generator;
use PhpSpec\CodeGenerator\Generator\GeneratorInterface;
use PhpSpec\Event\FileCreationEvent;
use PhpSpec\Locator\ResourceInterface;
use PhpSpec\ObjectBehavior;
use PhpSpec\Util\Filesystem;
use Prophecy\Argument;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
class NewFileNotifyingGeneratorSpec extends ObjectBehavior
{
const EVENT_CLASS = 'PhpSpec\Event\FileCreationEvent';
public function let(GeneratorInterface $generator, EventDispatcherInterface $dispatcher, Filesystem $filesystem)
{
$this->beConstructedWith($generator, $dispatcher, $filesystem);
}
function it_is_a_code_generator()
{
$this->shouldImplement('PhpSpec\CodeGenerator\Generator\GeneratorInterface');
}
function it_should_proxy_the_support_call_to_the_decorated_object($generator, ResourceInterface $resource)
{
$generator->supports($resource, 'foo', array('bar'))->willReturn(true);
$this->supports($resource, 'foo', array('bar'))->shouldReturn(true);
}
function it_should_proxy_the_priority_call_to_the_decorated_object($generator)
{
$generator->getPriority()->willReturn(5);
$this->getPriority()->shouldReturn(5);
}
function it_should_proxy_the_generate_call_to_the_decorated_object($generator, ResourceInterface $resource)
{
$this->generate($resource, array());
$generator->generate($resource, array())->shouldHaveBeenCalled();
}
function it_should_dispatch_an_event_when_a_file_is_created($dispatcher, $filesystem, ResourceInterface $resource)
{
$path = '/foo';
$resource->getSrcFilename()->willReturn($path);
$event = new FileCreationEvent($path);
$filesystem->pathExists($path)->willReturn(false, true);
$this->generate($resource, array());
$dispatcher->dispatch('afterFileCreation', $event)->shouldHaveBeenCalled();
}
function it_should_dispatch_an_event_with_the_spec_path_when_a_spec_is_created($generator, $dispatcher, $filesystem, ResourceInterface $resource)
{
$path = '/foo';
$generator->supports($resource, 'specification', array())->willReturn(true);
$generator->generate(Argument::cetera())->shouldBeCalled();
$resource->getSpecFilename()->willReturn($path);
$filesystem->pathExists($path)->willReturn(false, true);
$event = new FileCreationEvent($path);
$this->generate($resource, array());
$dispatcher->dispatch('afterFileCreation', $event)->shouldHaveBeenCalled();
}
function it_should_check_that_the_file_was_created($generator, $filesystem, ResourceInterface $resource)
{
$path = '/foo';
$resource->getSrcFilename()->willReturn($path);
$filesystem->pathExists($path)->willReturn(false);
$generator->supports(Argument::cetera())->willReturn(false);
$generator->generate($resource, array())->will(function () use ($filesystem, $path) {
$filesystem->pathExists($path)->willReturn(true);
});
$this->generate($resource, array());
}
function it_should_not_dispatch_an_event_if_the_file_was_not_created($dispatcher, $filesystem, ResourceInterface $resource)
{
$path = '/foo';
$resource->getSrcFilename()->willReturn($path);
$filesystem->pathExists($path)->willReturn(false);
$this->generate($resource, array());
$dispatcher->dispatch('afterFileCreation', Argument::any())->shouldNotHaveBeenCalled();
}
function it_should_not_dispatch_an_event_if_the_file_already_existed($dispatcher, $filesystem, ResourceInterface $resource)
{
$path = '/foo';
$resource->getSrcFilename()->willReturn($path);
$filesystem->pathExists($path)->willReturn(true);
$this->generate($resource, array());
$dispatcher->dispatch('afterFileCreation', Argument::any())->shouldNotHaveBeenCalled();
}
}

View File

@@ -40,24 +40,26 @@ class SpecificationGeneratorSpec extends ObjectBehavior
function it_generates_spec_class_from_resource_and_puts_it_into_appropriate_folder(
$io, $tpl, $fs, ResourceInterface $resource
) {
$resource->getSpecName()->willReturn('App');
$resource->getSpecFilename()->willReturn('/project/spec/Acme/App.php');
$resource->getSpecName()->willReturn('AppSpec');
$resource->getSpecFilename()->willReturn('/project/spec/Acme/AppSpec.php');
$resource->getSpecNamespace()->willReturn('spec\Acme');
$resource->getSrcClassname()->willReturn('Acme\App');
$resource->getName()->willReturn('App');
$values = array(
'%filepath%' => '/project/spec/Acme/App.php',
'%name%' => 'App',
'%filepath%' => '/project/spec/Acme/AppSpec.php',
'%name%' => 'AppSpec',
'%namespace%' => 'spec\Acme',
'%subject%' => 'Acme\App'
'%subject%' => 'Acme\App',
'%subject_class%' => 'App'
);
$tpl->render('specification', $values)->willReturn(null);
$tpl->renderString(Argument::type('string'), $values)->willReturn('generated code');
$fs->pathExists('/project/spec/Acme/App.php')->willReturn(false);
$fs->pathExists('/project/spec/Acme/AppSpec.php')->willReturn(false);
$fs->isDirectory('/project/spec/Acme')->willReturn(true);
$fs->putFileContents('/project/spec/Acme/App.php', 'generated code')->shouldBeCalled();
$fs->putFileContents('/project/spec/Acme/AppSpec.php', 'generated code')->shouldBeCalled();
$this->generate($resource);
}
@@ -65,39 +67,42 @@ class SpecificationGeneratorSpec extends ObjectBehavior
function it_uses_template_provided_by_templating_system_if_there_is_one(
$io, $tpl, $fs, ResourceInterface $resource
) {
$resource->getSpecName()->willReturn('App');
$resource->getSpecFilename()->willReturn('/project/spec/Acme/App.php');
$resource->getSpecName()->willReturn('AppSpec');
$resource->getSpecFilename()->willReturn('/project/spec/Acme/AppSpec.php');
$resource->getSpecNamespace()->willReturn('spec\Acme');
$resource->getSrcClassname()->willReturn('Acme\App');
$resource->getName()->willReturn('App');
$values = array(
'%filepath%' => '/project/spec/Acme/App.php',
'%name%' => 'App',
'%filepath%' => '/project/spec/Acme/AppSpec.php',
'%name%' => 'AppSpec',
'%namespace%' => 'spec\Acme',
'%subject%' => 'Acme\App'
'%subject%' => 'Acme\App',
'%subject_class%' => 'App'
);
$tpl->render('specification', $values)->willReturn('template code');
$tpl->renderString(Argument::type('string'), $values)->willReturn('generated code');
$fs->pathExists('/project/spec/Acme/App.php')->willReturn(false);
$fs->pathExists('/project/spec/Acme/AppSpec.php')->willReturn(false);
$fs->isDirectory('/project/spec/Acme')->willReturn(true);
$fs->putFileContents('/project/spec/Acme/App.php', 'template code')->shouldBeCalled();
$fs->putFileContents('/project/spec/Acme/AppSpec.php', 'template code')->shouldBeCalled();
$this->generate($resource);
}
function it_creates_folder_for_spec_if_needed($io, $tpl, $fs, ResourceInterface $resource)
{
$resource->getSpecName()->willReturn('App');
$resource->getSpecFilename()->willReturn('/project/spec/Acme/App.php');
$resource->getSpecName()->willReturn('AppAppSpec');
$resource->getSpecFilename()->willReturn('/project/spec/Acme/AppSpec.php');
$resource->getSpecNamespace()->willReturn('spec\Acme');
$resource->getSrcClassname()->willReturn('Acme\App');
$resource->getName()->willReturn('App');
$fs->pathExists('/project/spec/Acme/App.php')->willReturn(false);
$fs->pathExists('/project/spec/Acme/AppSpec.php')->willReturn(false);
$fs->isDirectory('/project/spec/Acme')->willReturn(false);
$fs->makeDirectory('/project/spec/Acme')->shouldBeCalled();
$fs->putFileContents('/project/spec/Acme/App.php', Argument::any())->willReturn(null);
$fs->putFileContents('/project/spec/Acme/AppSpec.php', Argument::any())->willReturn(null);
$this->generate($resource);
}
@@ -105,12 +110,12 @@ class SpecificationGeneratorSpec extends ObjectBehavior
function it_asks_confirmation_if_spec_already_exists(
$io, $tpl, $fs, ResourceInterface $resource
) {
$resource->getSpecName()->willReturn('App');
$resource->getSpecFilename()->willReturn('/project/spec/Acme/App.php');
$resource->getSpecName()->willReturn('AppSpec');
$resource->getSpecFilename()->willReturn('/project/spec/Acme/AppSpec.php');
$resource->getSpecNamespace()->willReturn('spec\Acme');
$resource->getSrcClassname()->willReturn('Acme\App');
$fs->pathExists('/project/spec/Acme/App.php')->willReturn(true);
$fs->pathExists('/project/spec/Acme/AppSpec.php')->willReturn(true);
$io->askConfirmation(Argument::type('string'), false)->willReturn(false);
$fs->putFileContents(Argument::cetera())->shouldNotBeCalled();

View File

@@ -0,0 +1,31 @@
<?php
namespace spec\PhpSpec\Event;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
class FileCreationEventSpec extends ObjectBehavior
{
private $filepath = 'foo/bar.php';
function let()
{
$this->beConstructedWith($this->filepath);
}
function it_should_be_a_symfony_event()
{
$this->shouldHaveType('Symfony\Component\EventDispatcher\Event');
}
function it_should_be_a_phpspec_event()
{
$this->shouldImplement('PhpSpec\Event\EventInterface');
}
function it_should_return_the_created_file_path()
{
$this->getFilePath()->shouldReturn($this->filepath);
}
}

View File

@@ -0,0 +1,39 @@
<?php
namespace spec\PhpSpec\Exception\Wrapper;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
class InvalidCollaboratorTypeExceptionSpec extends ObjectBehavior
{
function let(\ReflectionParameter $parameter, \ReflectionFunctionAbstract $function)
{
$this->beConstructedWith($parameter, $function);
}
function it_is_initializable()
{
$this->shouldHaveType('PhpSpec\Exception\Wrapper\InvalidCollaboratorTypeException');
$this->shouldHaveType('PhpSpec\Exception\Wrapper\CollaboratorException');
}
function it_generates_correct_message_based_on_function_and_parameter(
\ReflectionParameter $parameter,
\ReflectionMethod $function,
\ReflectionClass $class
) {
$parameter->getPosition()->willReturn(2);
$function->getDeclaringClass()->willReturn($class);
$class->getName()->willReturn('Acme\Foo');
$function->getName()->willReturn('bar');
$this->getMessage()->shouldStartWith('Collaborator must be an object: argument 2 defined in Acme\Foo::bar.');
}
function it_sets_cause(\ReflectionFunction $function)
{
$this->getCause()->shouldReturn($function);
}
}

View File

@@ -0,0 +1,65 @@
<?php
namespace spec\PhpSpec\Formatter\Presenter\Exception;
use PhpSpec\Formatter\Presenter\Differ\Differ;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
use Prophecy\Argument\ArgumentsWildcard;
use Prophecy\Call\Call;
use Prophecy\Exception\Call\UnexpectedCallException;
use Prophecy\Prophecy\MethodProphecy;
use Prophecy\Prophecy\ObjectProphecy;
class CallArgumentsPresenterSpec extends ObjectBehavior
{
function let(Differ $differ)
{
$this->beConstructedWith($differ);
}
function it_should_return_empty_string_if_there_are_no_method_prophecies(
UnexpectedCallException $exception, ObjectProphecy $objectProphecy
) {
$exception->getObjectProphecy()->willReturn($objectProphecy);
$exception->getArguments()->shouldBeCalled();
$exception->getMethodName()->willReturn('method');
$objectProphecy->getMethodProphecies('method')->willReturn(array());
$this->presentDifference($exception)->shouldReturn('');
}
function it_should_return_empty_string_if_method_prophecies_all_contain_calls(
UnexpectedCallException $exception, ObjectProphecy $objectProphecy, MethodProphecy $prophecy,
Call $call, ArgumentsWildcard $wildcard
) {
$exception->getObjectProphecy()->willReturn($objectProphecy);
$exception->getArguments()->shouldBeCalled();
$exception->getMethodName()->willReturn('method');
$objectProphecy->getMethodProphecies(Argument::any())->willReturn(array($prophecy));
$objectProphecy->findProphecyMethodCalls('method', $wildcard)->willReturn(array($call));
$prophecy->getArgumentsWildcard()->willReturn($wildcard);
$this->presentDifference($exception)->shouldReturn('');
}
function it_should_return_empty_string_if_argument_counts_do_not_match(
UnexpectedCallException $exception, ObjectProphecy $objectProphecy, MethodProphecy $prophecy,
ArgumentsWildcard $wildcard
) {
$exception->getObjectProphecy()->willReturn($objectProphecy);
$exception->getArguments()->willReturn(array('a', 'b'));
$exception->getMethodName()->shouldBeCalled();
$objectProphecy->getMethodProphecies(Argument::any())->willReturn(array($prophecy));
$objectProphecy->findProphecyMethodCalls(Argument::any(), Argument::any())->willReturn(array());
$prophecy->getArgumentsWildcard()->willReturn($wildcard);
$wildcard->getTokens()->willReturn(array('a'));
$this->presentDifference($exception)->shouldReturn('');
}
}

View File

@@ -0,0 +1,20 @@
<?php
namespace spec\PhpSpec\Formatter\Presenter\Exception;
use PhpSpec\Formatter\Presenter\Exception\ExceptionElementPresenter;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
class GenericPhpSpecExceptionPresenterSpec extends ObjectBehavior
{
function let(ExceptionElementPresenter $elementPresenter)
{
$this->beConstructedWith($elementPresenter);
}
function it_is_a_phpspec_exception_presenter()
{
$this->shouldImplement('PhpSpec\Formatter\Presenter\Exception\PhpSpecExceptionPresenter');
}
}

View File

@@ -0,0 +1,14 @@
<?php
namespace spec\PhpSpec\Formatter\Presenter\Exception;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
class HtmlPhpSpecExceptionPresenterSpec extends ObjectBehavior
{
function it_is_a_phpspec_exception_presenter()
{
$this->shouldImplement('PhpSpec\Formatter\Presenter\Exception\PhpSpecExceptionPresenter');
}
}

View File

@@ -0,0 +1,79 @@
<?php
namespace spec\PhpSpec\Formatter\Presenter\Exception;
use PhpSpec\Formatter\Presenter\Value\ExceptionTypePresenter;
use PhpSpec\Formatter\Presenter\Value\ValuePresenter;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
class SimpleExceptionElementPresenterSpec extends ObjectBehavior
{
function let(ExceptionTypePresenter $typePresenter, ValuePresenter $valuePresenter)
{
$this->beConstructedWith($typePresenter, $valuePresenter);
}
function it_is_an_exception_element_presenter()
{
$this->shouldImplement('PhpSpec\Formatter\Presenter\Exception\ExceptionElementPresenter');
}
function it_should_return_a_simple_exception_thrown_message(
ExceptionTypePresenter $typePresenter, \Exception $exception
) {
$typePresenter->present($exception)->willReturn('exc');
$this->presentExceptionThrownMessage($exception)->shouldReturn('Exception exc has been thrown.');
}
function it_should_present_a_code_line()
{
$this->presentCodeLine('3', '4')->shouldReturn('3 4');
}
function it_should_present_a_highlighted_line_unchanged()
{
$this->presentHighlight('foo')->shouldReturn('foo');
}
function it_should_present_the_header_of_an_exception_trace_unchanged()
{
$this->presentExceptionTraceHeader('foo')->shouldReturn('foo');
}
function it_should_present_every_argument_in_an_exception_trace_method_as_a_value(ValuePresenter $valuePresenter)
{
$args = array('foo', 42);
$valuePresenter->presentValue('foo')->shouldBeCalled();
$valuePresenter->presentValue(42)->shouldBeCalled();
$this->presentExceptionTraceMethod('', '', '', $args);
}
function it_should_present_an_exception_trace_method(ValuePresenter $valuePresenter)
{
$valuePresenter->presentValue('a')->willReturn('zaz');
$valuePresenter->presentValue('b')->willReturn('zbz');
$this->presentExceptionTraceMethod('class', 'type', 'method', array('a', 'b'))
->shouldReturn(' classtypemethod(zaz, zbz)');
}
function it_should_present_every_argument_in_an_exception_trace_function_as_a_value(ValuePresenter $valuePresenter)
{
$args = array('foo', 42);
$valuePresenter->presentValue('foo')->shouldBeCalled();
$valuePresenter->presentValue(42)->shouldBeCalled();
$this->presentExceptionTraceFunction('', $args);
}
function it_should_present_an_exception_trace_function(ValuePresenter $valuePresenter)
{
$valuePresenter->presentValue('a')->willReturn('zaz');
$valuePresenter->presentValue('b')->willReturn('zbz');
$this->presentExceptionTraceFunction('function', array('a', 'b'))
->shouldReturn(' function(zaz, zbz)');
}
}

View File

@@ -0,0 +1,30 @@
<?php
namespace spec\PhpSpec\Formatter\Presenter\Exception;
use PhpSpec\Formatter\Presenter\Differ\Differ;
use PhpSpec\Formatter\Presenter\Exception\CallArgumentsPresenter;
use PhpSpec\Formatter\Presenter\Exception\ExceptionElementPresenter;
use PhpSpec\Formatter\Presenter\Exception\PhpSpecExceptionPresenter;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
class SimpleExceptionPresenterSpec extends ObjectBehavior
{
function let(
Differ $differ, ExceptionElementPresenter $exceptionElementPresenter,
CallArgumentsPresenter $callArgumentsPresenter, PhpSpecExceptionPresenter $phpspecExceptionPresenter
) {
$this->beConstructedWith(
$differ,
$exceptionElementPresenter,
$callArgumentsPresenter,
$phpspecExceptionPresenter
);
}
function it_is_an_exception_presenter()
{
$this->shouldImplement('PhpSpec\Formatter\Presenter\Exception\ExceptionPresenter');
}
}

View File

@@ -0,0 +1,67 @@
<?php
namespace spec\PhpSpec\Formatter\Presenter\Exception;
use PhpSpec\Formatter\Presenter\Exception\ExceptionElementPresenter;
use PhpSpec\Formatter\Presenter\Value\ExceptionTypePresenter;
use PhpSpec\Formatter\Presenter\Value\ValuePresenter;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
class TaggingExceptionElementPresenterSpec extends ObjectBehavior
{
function let(ExceptionTypePresenter $exceptionTypePresenter, ValuePresenter $valuePresenter)
{
$this->beConstructedWith($exceptionTypePresenter, $valuePresenter);
}
function it_is_an_exception_element_presenter()
{
$this->shouldImplement('PhpSpec\Formatter\Presenter\Exception\ExceptionElementPresenter');
}
function it_should_tag_an_exception_thrown_message(
ExceptionTypePresenter $exceptionTypePresenter,
\Exception $exception
) {
$exceptionTypePresenter->present($exception)->willReturn('exc');
$this->presentExceptionThrownMessage($exception)->shouldReturn('Exception <label>exc</label> has been thrown.');
}
function it_should_present_a_tagged_code_line()
{
$this->presentCodeLine('3', 'foo')->shouldReturn('<lineno>3</lineno> <code>foo</code>');
}
function it_should_present_a_tagged_highlighted_line()
{
$this->presentHighlight('foo')->shouldReturn('<hl>foo</hl>');
}
function it_should_present_a_tagged_header_of_an_exception_trace()
{
$this->presentExceptionTraceHeader('foo')->shouldReturn('<trace>foo</trace>');
}
function it_should_present_a_tagged_exception_trace_method(ValuePresenter $valuePresenter)
{
$valuePresenter->presentValue('a')->willReturn('zaz');
$valuePresenter->presentValue('b')->willReturn('zbz');
$result = ' <trace><trace-class>class</trace-class><trace-type>type</trace-type>'.
'<trace-func>method</trace-func>(<trace-args><value>zaz</value>, <value>zbz</value></trace-args>)</trace>';
$this->presentExceptionTraceMethod('class', 'type', 'method', array('a', 'b'))->shouldReturn($result);
}
function it_should_present_a_tagged_exception_trace_function(ValuePresenter $valuePresenter)
{
$valuePresenter->presentValue('a')->willReturn('zaz');
$valuePresenter->presentValue('b')->willReturn('zbz');
$result = ' <trace><trace-func>function</trace-func>'.
'(<trace-args><value>zaz</value>, <value>zbz</value></trace-args>)</trace>';
$this->presentExceptionTraceFunction('function', array('a', 'b'))->shouldReturn($result);
}
}

View File

@@ -0,0 +1,53 @@
<?php
/*
* This file is part of PhpSpec, A php toolset to drive emergent
* design by specification.
*
* (c) Marcello Duarte <marcello.duarte@gmail.com>
* (c) Konstantin Kudryashov <ever.zet@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace spec\PhpSpec\Formatter\Presenter;
use PhpSpec\Formatter\Presenter\Exception\ExceptionPresenter;
use PhpSpec\Formatter\Presenter\Value\ValuePresenter;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
class SimplePresenterSpec extends ObjectBehavior
{
function let(ValuePresenter $valuePresenter, ExceptionPresenter $exceptionPresenter)
{
$this->beConstructedWith($valuePresenter, $exceptionPresenter);
}
function it_is_a_presenter()
{
$this->shouldImplement('PhpSpec\Formatter\Presenter\Presenter');
}
function it_returns_a_string_unchanged()
{
$message = 'this is a string';
$this->presentString($message)->shouldReturn($message);
}
function it_should_be_a_proxy_for_an_exception_presenter(
ExceptionPresenter $exceptionPresenter, \Exception $exception
) {
$result = 'this is the result';
$exceptionPresenter->presentException($exception, true)->willReturn($result);
$this->presentException($exception, true)->shouldReturn($result);
}
function it_should_be_a_proxy_for_a_value_presenter(
ValuePresenter $valuePresenter
) {
$valuePresenter->presentValue('foo')->willReturn('zfooz');
$this->presentValue('foo')->shouldReturn('zfooz');
}
}

View File

@@ -4,6 +4,7 @@ namespace spec\PhpSpec\Formatter\Presenter;
use PhpSpec\ObjectBehavior;
use PhpSpec\Formatter\Presenter\Differ\Differ;
use PhpSpec\Wrapper\Subject;
class StringPresenterSpec extends ObjectBehavior
{
@@ -106,13 +107,6 @@ class StringPresenterSpec extends ObjectBehavior
{
$this->presentString('some string')->shouldReturn('some string');
}
function its_presentValue_displays_invokable_objects_as_objects()
{
$invokable = new ObjectBehavior();
$invokable->setSpecificationSubject($this);
$this->presentValue($invokable)->shouldReturn('[obj:PhpSpec\Formatter\Presenter\StringPresenter]');
}
}
class WithMethod

View File

@@ -0,0 +1,38 @@
<?php
namespace spec\PhpSpec\Formatter\Presenter;
use PhpSpec\Formatter\Presenter\Presenter;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
class TaggingPresenterSpec extends ObjectBehavior
{
function let(Presenter $presenter)
{
$this->beConstructedWith($presenter);
}
function it_is_a_presenter()
{
$this->shouldImplement('PhpSpec\Formatter\Presenter\Presenter');
}
function it_should_tag_strings()
{
$this->presentString('foo')->shouldReturn('<value>foo</value>');
}
function it_should_tag_values_from_the_decorated_presenter(Presenter $presenter)
{
$presenter->presentValue('foo')->willReturn('zfooz');
$this->presentValue('foo')->shouldReturn('<value>zfooz</value>');
}
function it_should_return_presented_exceptions_from_the_decorated_presenter_unchanged(
Presenter $presenter, \Exception $exception
) {
$presenter->presentException($exception, true)->willReturn('exc');
$this->presentException($exception, true)->shouldReturn('exc');
}
}

View File

@@ -0,0 +1,29 @@
<?php
namespace spec\PhpSpec\Formatter\Presenter\Value;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
class ArrayTypePresenterSpec extends ObjectBehavior
{
function it_is_a_type_presenter()
{
$this->shouldImplement('PhpSpec\Formatter\Presenter\Value\TypePresenter');
}
function it_should_support_array_values()
{
$this->supports(array())->shouldReturn(true);
}
function it_should_present_an_empty_array_as_a_string()
{
$this->present(array())->shouldReturn('[array:0]');
}
function it_should_present_a_populated_array_as_a_string()
{
$this->present(array('a', 'b'))->shouldReturn('[array:2]');
}
}

View File

@@ -0,0 +1,25 @@
<?php
namespace spec\PhpSpec\Formatter\Presenter\Value;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
class BaseExceptionTypePresenterSpec extends ObjectBehavior
{
function it_is_a_type_presenter()
{
$this->shouldImplement('PhpSpec\Formatter\Presenter\Value\ExceptionTypePresenter');
}
function it_should_support_exceptions()
{
$this->supports(new \Exception())->shouldReturn(true);
}
function it_should_present_an_exception_as_a_string()
{
$this->present(new \Exception('foo'))
->shouldReturn('[exc:Exception("foo")]');
}
}

View File

@@ -0,0 +1,30 @@
<?php
namespace spec\PhpSpec\Formatter\Presenter\Value;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
class BooleanTypePresenterSpec extends ObjectBehavior
{
function it_is_a_type_presenter()
{
$this->shouldImplement('PhpSpec\Formatter\Presenter\Value\TypePresenter');
}
function it_should_support_boolean_values()
{
$this->supports(true)->shouldReturn(true);
$this->supports(false)->shouldReturn(true);
}
function it_should_present_a_true_boolean_as_a_string()
{
$this->present(true)->shouldReturn('true');
}
function it_should_present_a_false_boolean_as_a_string()
{
$this->present(false)->shouldReturn('false');
}
}

View File

@@ -0,0 +1,113 @@
<?php
namespace spec\PhpSpec\Formatter\Presenter\Value;
use PhpSpec\Formatter\Presenter\Presenter;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
class CallableTypePresenterSpec extends ObjectBehavior
{
function let(Presenter $presenter)
{
$this->beConstructedWith($presenter);
}
function it_is_a_type_presenter()
{
$this->shouldImplement('PhpSpec\Formatter\Presenter\Value\TypePresenter');
}
function it_should_support_callable_values()
{
$this->supports(function () {})->shouldReturn(true);
}
function it_should_present_a_closure()
{
$this->present(function () {})->shouldReturn('[closure]');
}
function it_should_present_function_callable_as_string()
{
$this->present('date')->shouldReturn('[date()]');
}
function it_should_present_a_method_as_string(
WithMethod $object, Presenter $presenter
) {
$className = get_class($object->getWrappedObject());
$presenter->presentValue($object->getWrappedObject())->willReturn(sprintf('[obj:%s]', $className));
$this->present(array($object, 'specMethod'))
->shouldReturn(sprintf('[obj:%s]::specMethod()', $className));
}
function it_should_present_a_magic_method_as_string(
WithMagicCall $object, Presenter $presenter
) {
$className = get_class($object->getWrappedObject());
$presenter->presentValue($object->getWrappedObject())->willReturn(sprintf('[obj:%s]', $className));
$this->present(array($object, 'undefinedMethod'))
->shouldReturn(sprintf('[obj:%s]::undefinedMethod()', $className));
}
function it_should_present_a_static_method_as_string(WithMethod $object)
{
$className = get_class($object->getWrappedObject());
$this->present(array($className, 'specMethod'))
->shouldReturn(sprintf('%s::specMethod()', $className));
}
function it_should_present_a_static_magic_method_as_string()
{
$className = __NAMESPACE__ . '\\WithStaticMagicCall';
$this->present(array($className, 'undefinedMethod'))
->shouldReturn(sprintf('%s::undefinedMethod()', $className));
}
function it_should_present_an_invokable_object_as_string(WithMagicInvoke $object)
{
$className = get_class($object->getWrappedObject());
$this->present($object)->shouldReturn(sprintf('[obj:%s]', $className));
}
}
class WithMethod
{
function specMethod()
{
}
}
class WithStaticMethod
{
function specMethod()
{
}
}
class WithMagicInvoke
{
function __invoke()
{
}
}
class WithStaticMagicCall
{
static function __callStatic($method, $name)
{
}
}
class WithMagicCall
{
function __call($method, $name)
{
}
}

View File

@@ -0,0 +1,93 @@
<?php
namespace spec\PhpSpec\Formatter\Presenter\Value;
use PhpSpec\Formatter\Presenter\Value\TypePresenter;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
class ComposedValuePresenterSpec extends ObjectBehavior
{
function it_is_a_value_presenter()
{
$this->shouldHaveType('PhpSpec\Formatter\Presenter\Value\ComposedValuePresenter');
}
function it_should_accept_a_type_presenter(TypePresenter $typePresenter)
{
$this->addTypePresenter($typePresenter)->shouldReturn(null);
}
function it_should_call_supports_on_value_presenters_until_one_returns_true(
TypePresenter $presenter1, TypePresenter $presenter2, TypePresenter $presenter3
) {
$value = 'blah';
$presenter1->getPriority()->willReturn(3);
$presenter2->getPriority()->willReturn(2);
$presenter3->getPriority()->willReturn(1);
$this->addTypePresenter($presenter1);
$this->addTypePresenter($presenter2);
$this->addTypePresenter($presenter3);
$presenter1->supports($value)->willReturn(false)->shouldBeCalled();
$presenter2->supports($value)->willReturn(true)->shouldBeCalled();
$presenter2->present(Argument::any())->shouldBeCalled();
$presenter3->supports($value)->shouldNotBeCalled();
$this->presentValue($value);
}
function it_should_order_presenters_by_their_priority_in_descending_order(
TypePresenter $presenter1, TypePresenter $presenter2
) {
$value = 'foo';
$presenter1->getPriority()->willReturn(10);
$presenter2->getPriority()->willReturn(20);
$this->addTypePresenter($presenter1);
$this->addTypePresenter($presenter2);
$presenter1->supports($value)->shouldBeCalled()->willReturn(true);
$presenter2->supports($value)->shouldBeCalled();
$presenter1->present(Argument::any())->shouldBeCalled();
$this->presentValue($value);
}
function it_should_call_present_on_a_supporting_type_presenter(TypePresenter $typePresenter)
{
$value = 'blah';
$typePresenter->supports($value)->willReturn(true);
$typePresenter->present($value)->shouldBeCalled();
$this->addTypePresenter($typePresenter);
$this->presentValue($value);
}
function it_should_return_the_type_presenter_presented_value(TypePresenter $typePresenter)
{
$value = 'blah';
$presented = $value.'presented';
$typePresenter->supports($value)->willReturn(true);
$typePresenter->present($value)->willReturn($presented);
$this->addTypePresenter($typePresenter);
$this->presentValue($value)->shouldReturn($presented);
}
function it_returns_a_default_when_no_type_presenters_support_the_value()
{
$this->presentValue('blah')->shouldReturn('[string:blah]');
}
function it_should_present_a_simple_type_as_typed_value()
{
$this->presentValue(42)->shouldReturn('[integer:42]');
$this->presentValue(42.0)->shouldReturn('[double:42]');
}
}

View File

@@ -0,0 +1,24 @@
<?php
namespace spec\PhpSpec\Formatter\Presenter\Value;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
class NullTypePresenterSpec extends ObjectBehavior
{
function it_is_a_type_presenter()
{
$this->shouldImplement('PhpSpec\Formatter\Presenter\Value\TypePresenter');
}
function it_should_support_null_values()
{
$this->supports(null)->shouldReturn(true);
}
function it_should_present_null_as_a_string()
{
$this->present(null)->shouldReturn('null');
}
}

View File

@@ -0,0 +1,24 @@
<?php
namespace spec\PhpSpec\Formatter\Presenter\Value;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
class ObjectTypePresenterSpec extends ObjectBehavior
{
function it_is_a_type_presenter()
{
$this->shouldImplement('PhpSpec\Formatter\Presenter\Value\TypePresenter');
}
function it_should_support_object_values()
{
$this->supports(new \stdClass())->shouldReturn(true);
}
function it_should_present_an_object_as_a_string()
{
$this->present(new \stdClass())->shouldReturn('[obj:stdClass]');
}
}

View File

@@ -0,0 +1,26 @@
<?php
namespace spec\PhpSpec\Formatter\Presenter\Value;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
class QuotingStringTypePresenterSpec extends ObjectBehavior
{
function it_is_a_string_type_presenter()
{
$this->shouldImplement('PhpSpec\Formatter\Presenter\Value\StringTypePresenter');
}
function it_should_support_string_values()
{
$this->supports('')->shouldReturn(true);
$this->supports('foo')->shouldReturn(true);
}
function it_should_present_a_string_as_a_quoted_string()
{
$this->present('')->shouldReturn('""');
$this->present('foo')->shouldReturn('"foo"');
}
}

View File

@@ -0,0 +1,47 @@
<?php
namespace spec\PhpSpec\Formatter\Presenter\Value;
use PhpSpec\Formatter\Presenter\Value\StringTypePresenter;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
class TruncatingStringTypePresenterSpec extends ObjectBehavior
{
function let(StringTypePresenter $stringTypePresenter)
{
$this->beConstructedWith($stringTypePresenter);
}
function it_is_a_string_type_presenter()
{
$this->shouldImplement('PhpSpec\Formatter\Presenter\Value\StringTypePresenter');
}
function it_should_support_string_values(StringTypePresenter $stringTypePresenter)
{
$stringTypePresenter->supports('foo')->willReturn(true);
$this->supports('foo')->shouldReturn(true);
}
function it_should_pass_short_values_directly_to_the_decorated_string_type_presenter(
StringTypePresenter $stringTypePresenter
) {
$stringTypePresenter->present('foo')->willReturn('zfooz');
$this->present('foo')->shouldReturn('zfooz');
}
function it_should_return_long_values_truncated(
StringTypePresenter $stringTypePresenter
) {
$stringTypePresenter->present('some_string_longer_than_t...')
->willReturn('some_string_longer_than_t...');
$this->present('some_string_longer_than_twenty_five_chars')->shouldReturn('some_string_longer_than_t...');
}
function it_presents_only_first_line_of_multiline_string(StringTypePresenter $stringTypePresenter)
{
$stringTypePresenter->present('some...')->willReturn('some...');
$this->present("some\nmultiline\nvalue")->shouldReturn('some...');
}
}

View File

@@ -0,0 +1,57 @@
<?php
namespace spec\PhpSpec\Listener;
use PhpSpec\Event\ExampleEvent;
use PhpSpec\Event\SuiteEvent;
use PhpSpec\Message\CurrentExampleTracker;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
class CurrentExampleListenerSpec extends ObjectBehavior
{
function let()
{
$currentExample = new CurrentExampleTracker();
$this->beConstructedWith($currentExample);
}
function it_is_initializable()
{
$this->shouldHaveType('PhpSpec\Listener\CurrentExampleListener');
}
function it_should_implement_event_subscriber_interface()
{
$this->shouldHaveType('Symfony\Component\EventDispatcher\EventSubscriberInterface');
}
function it_should_call_beforeCurrentExample(ExampleEvent $example)
{
$currentExample = new CurrentExampleTracker();
$fatalError = 'Fatal error happened before example';
$example->getTitle()->willReturn($fatalError);
$currentExample->setCurrentExample($fatalError);
$this->beforeCurrentExample($example);
$example->getTitle()->shouldHaveBeenCalled();
}
function it_should_call_afterCurrentExample(ExampleEvent $example)
{
$currentExample = new CurrentExampleTracker();
$currentExample->setCurrentExample(null);
$example->getTitle()->willReturn(null);
$this->afterCurrentExample($example);
$example->getTitle()->shouldNotHaveBeenCalled();
}
function it_should_call_afterSuiteEvent(SuiteEvent $example)
{
$fatalError = '3';
$currentExample = new CurrentExampleTracker();
$currentExample->setCurrentExample("Exited with code: " . $fatalError);
$example->getResult()->willReturn($fatalError);
$this->afterSuiteEvent($example);
$example->getResult()->shouldHaveBeenCalled();
}
}

View File

@@ -0,0 +1,43 @@
<?php
namespace spec\PhpSpec\Loader\Transformer;
use PhpSpec\CodeAnalysis\DisallowedScalarTypehintException;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
class InMemoryTypeHintIndexSpec extends ObjectBehavior
{
function it_is_a_typehint_index()
{
$this->shouldHaveType('PhpSpec\Loader\Transformer\TypeHintIndex');
}
function it_is_case_insensitive()
{
$this->add('Foo', 'boz', '$bar', 'Baz');
$this->lookup('FoO', 'bOz', '$bAr')->shouldReturn('Baz');
}
function it_remembers_the_typehints_that_are_added()
{
$this->add('Foo', 'boz', '$bar', 'Baz');
$this->lookup('Foo', 'boz', '$bar')->shouldReturn('Baz');
}
function it_returns_false_for_typehints_that_have_not_been_added()
{
$this->lookup('Foo', 'boz', '$bar')->shouldBe(false);
}
function it_throws_invalid_argument_exceptions()
{
$e = new DisallowedScalarTypehintException();
$this->addInvalid('Foo', 'boz', '$bar', $e);
$this->shouldThrow($e)->duringLookup('Foo', 'boz', '$bar');
}
}

View File

@@ -0,0 +1,28 @@
<?php
namespace spec\PhpSpec\Loader\Transformer;
use PhpSpec\CodeAnalysis\TypeHintRewriter;
use PhpSpec\Loader\Transformer\TypeHintIndex;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
class TypeHintRewriterSpec extends ObjectBehavior
{
function let(TypeHintRewriter $rewriter)
{
$this->beConstructedWith($rewriter);
}
function it_is_a_transformer()
{
$this->shouldHaveType('PhpSpec\Loader\SpecTransformer');
}
function it_delegates_transforming_to_rewriter(TypeHintRewriter $rewriter)
{
$rewriter->rewrite('<?php echo "hello world";')->willReturn('hello world');
$this->transform('<?php echo "hello world";')->shouldReturn('hello world');
}
}

View File

@@ -500,6 +500,27 @@ class PSR0LocatorSpec extends ObjectBehavior
$this->shouldThrow($exception)->during('__construct', array('p\pf\N\S', 'spec', $this->srcPath, $this->specPath, null, 'wrong\prefix'));
}
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);
$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);
$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);
$this->supportsQuery('Test\\Namespace\\PhpSpec\\Console\\Application')->shouldReturn(true);
}
private function convert_to_path($path)
{
if ('/' === DIRECTORY_SEPARATOR) {

View File

@@ -0,0 +1,73 @@
<?php
/*
* This file is part of PhpSpec, A php toolset to drive emergent
* design by specification.
*
* (c) Marcello Duarte <marcello.duarte@gmail.com>
* (c) Konstantin Kudryashov <ever.zet@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace spec\PhpSpec\Matcher;
use PhpSpec\Formatter\Presenter\PresenterInterface;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
class StringContainMatcherSpec extends ObjectBehavior
{
function let(PresenterInterface $presenter)
{
$presenter->presentString(Argument::type('string'))->willReturnArgument();
$this->beConstructedWith($presenter);
}
function it_is_a_matcher()
{
$this->shouldBeAnInstanceOf('PhpSpec\Matcher\MatcherInterface');
}
function it_supports_contain_keyword_string_subject_and_argument()
{
$this->supports('contain', 'hello world', array('llo'))->shouldReturn(true);
}
function it_does_not_support_non_string_keyword()
{
$this->supports('contain', array(), array())->shouldReturn(false);
}
function it_does_not_support_missing_argument()
{
$this->supports('contain', 'hello world', array())->shouldReturn(false);
}
function it_does_not_support_non_string_argument()
{
$this->supports('contain', 'hello world', array(array()))->shouldReturn(false);
}
function it_matches_strings_that_contain_specified_substring()
{
$this->shouldNotThrow()->duringPositiveMatch('contains', 'hello world', array('ello'));
}
function it_does_not_match_strings_that_do_not_contain_specified_substring()
{
$this->shouldThrow()->duringPositiveMatch('contains', 'hello world', array('row'));
}
function it_matches_strings_that_do_not_contain_specified_substring()
{
$this->shouldNotThrow()->duringNegativeMatch('contains', 'hello world', array('row'));
}
function it_does_not_match_strings_that_do_contain_specified_substring()
{
$this->shouldThrow()->duringNegativeMatch('contains', 'hello world', array('ello'));
}
}

View File

@@ -0,0 +1,28 @@
<?php
namespace spec\PhpSpec\Message;
use PhpSpec\Message\CurrentExampleTracker;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
class CurrentExampleTrackerSpec extends ObjectBehavior
{
function it_is_initializable()
{
$this->shouldHaveType('PhpSpec\Message\CurrentExampleTracker');
}
function it_should_set_a_message()
{
$currentExample = new CurrentExampleTracker();
$currentExample->setCurrentExample('test');
expect($currentExample->getCurrentExample())->toBe('test');
}
function it_should_be_null_on_construction()
{
$currentExample = new CurrentExampleTracker();
expect($currentExample->getCurrentExample())->toBe(null);
}
}

View File

@@ -0,0 +1,22 @@
<?php
namespace spec\PhpSpec\Process\Shutdown;
use PhpSpec\ObjectBehavior;
use PhpSpec\Process\Shutdown\ShutdownActionInterface;
use Prophecy\Argument;
class ShutdownSpec extends ObjectBehavior
{
function it_has_type_shutdown()
{
$this->beAnInstanceOf('PhpSpec/Process/Shutdown/Shutdown');
}
function it_runs_through_all_registered_actions(ShutdownActionInterface $action)
{
$action->runAction(null)->shouldBeCalled();
$this->registerAction($action);
$this->runShutdown();
}
}

View File

@@ -0,0 +1,26 @@
<?php
namespace spec\PhpSpec\Process\Shutdown;
use PhpSpec\Formatter\FatalPresenter;
use PhpSpec\Message\CurrentExampleTracker;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
class UpdateConsoleActionSpec extends ObjectBehavior
{
function let(FatalPresenter $currentExampleWriter)
{
$currentExample = new CurrentExampleTracker();
$this->beConstructedWith($currentExample, $currentExampleWriter);
}
function it_should_update_the_console(FatalPresenter $currentExampleWriter)
{
$currentExample = new CurrentExampleTracker();
$error = array('type' => 1, 'message' => 'Hello');
$currentExample->getCurrentExample('Hello');
$currentExampleWriter->displayFatal($currentExample, $error)->shouldBeCalled();
$this->runAction($error);
}
}

View File

@@ -11,7 +11,7 @@
* file that was distributed with this source code.
*/
namespace Phpspec\CodeAnalysis;
namespace PhpSpec\CodeAnalysis;
interface AccessInspectorInterface
{

View File

@@ -0,0 +1,18 @@
<?php
/*
* This file is part of PhpSpec, A php toolset to drive emergent
* design by specification.
*
* (c) Marcello Duarte <marcello.duarte@gmail.com>
* (c) Konstantin Kudryashov <ever.zet@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpSpec\CodeAnalysis;
class DisallowedScalarTypehintException extends \Exception
{
}

View File

@@ -0,0 +1,21 @@
<?php
/*
* This file is part of PhpSpec, A php toolset to drive emergent
* design by specification.
*
* (c) Marcello Duarte <marcello.duarte@gmail.com>
* (c) Konstantin Kudryashov <ever.zet@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpSpec\CodeAnalysis;
interface NamespaceResolver
{
public function analyse($code);
public function resolve($typeAlias);
}

View File

@@ -0,0 +1,50 @@
<?php
/*
* This file is part of PhpSpec, A php toolset to drive emergent
* design by specification.
*
* (c) Marcello Duarte <marcello.duarte@gmail.com>
* (c) Konstantin Kudryashov <ever.zet@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpSpec\CodeAnalysis;
final class StaticRejectingNamespaceResolver implements NamespaceResolver
{
/**
* @var NamespaceResolver
*/
private $namespaceResolver;
public function __construct(NamespaceResolver $namespaceResolver)
{
$this->namespaceResolver = $namespaceResolver;
}
public function analyse($code)
{
$this->namespaceResolver->analyse($code);
}
public function resolve($typeAlias)
{
$this->guardScalarTypeHints($typeAlias);
return $this->namespaceResolver->resolve($typeAlias);
}
/**
* @param $typeAlias
* @throws \Exception
*/
private function guardScalarTypeHints($typeAlias)
{
if (in_array($typeAlias, array('int', 'float', 'string', 'bool'))) {
throw new DisallowedScalarTypehintException("Scalar type $typeAlias cannot be resolved within a namespace");
}
}
}

View File

@@ -0,0 +1,115 @@
<?php
/*
* This file is part of PhpSpec, A php toolset to drive emergent
* design by specification.
*
* (c) Marcello Duarte <marcello.duarte@gmail.com>
* (c) Konstantin Kudryashov <ever.zet@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpSpec\CodeAnalysis;
final class TokenizedNamespaceResolver implements NamespaceResolver
{
const STATE_DEFAULT = 0;
const STATE_READING_NAMESPACE = 1;
const STATE_READING_USE = 2;
private $state = self::STATE_DEFAULT;
private $currentNamespace;
private $currentUse;
private $uses = array();
/**
* @param string $code
*/
public function analyse($code)
{
$this->state = self::STATE_DEFAULT;
$this->currentUse = null;
$this->uses = array();
$tokens = token_get_all($code);
foreach ($tokens as $index => $token) {
switch ($this->state) {
case self::STATE_READING_NAMESPACE:
if (';' == $token) {
$this->currentNamespace = trim($this->currentNamespace);
$this->state = self::STATE_DEFAULT;
}
elseif (is_array($token)) {
$this->currentNamespace .= $token[1];
}
break;
case self::STATE_READING_USE:
if (';' == $token) {
$this->storeCurrentUse();
$this->state = self::STATE_DEFAULT;
}
if (',' == $token) {
$this->storeCurrentUse();
}
elseif (is_array($token)) {
$this->currentUse .= $token[1];
}
break;
default:
if (is_array($token) && T_NAMESPACE == $token[0]) {
$this->state = self::STATE_READING_NAMESPACE;
$this->currentNamespace = '';
$this->uses = array();
}
elseif (is_array($token) && T_USE == $token[0]) {
$this->state = self::STATE_READING_USE;
$this->currentUse = '';
}
}
}
}
/**
* @param string $typeAlias
*
* @return string
*/
public function resolve($typeAlias)
{
if (strpos($typeAlias, '\\') === 0) {
return substr($typeAlias, 1);
}
if (($divider = strpos($typeAlias, '\\')) && array_key_exists(strtolower(substr($typeAlias, 0, $divider)), $this->uses)) {
return $this->uses[strtolower(substr($typeAlias, 0, $divider))] . substr($typeAlias, $divider);
}
if (array_key_exists(strtolower($typeAlias), $this->uses)) {
return $this->uses[strtolower($typeAlias)];
}
if ($this->currentNamespace) {
return $this->currentNamespace . '\\' . $typeAlias;
}
return $typeAlias;
}
private function storeCurrentUse()
{
if (preg_match('/\s*(.*)\s+as\s+(.*)\s*/', $this->currentUse, $matches)) {
$this->uses[strtolower(trim($matches[2]))] = trim($matches[1]);
}
elseif(preg_match('/\\\\([^\\\\]+)\s*$/', $this->currentUse, $matches)){
$this->uses[strtolower($matches[1])] = trim($this->currentUse);
}
else {
$this->uses[strtolower(trim($this->currentUse))] = trim($this->currentUse);
}
$this->currentUse = '';
}
}

View File

@@ -0,0 +1,174 @@
<?php
/*
* This file is part of PhpSpec, A php toolset to drive emergent
* design by specification.
*
* (c) Marcello Duarte <marcello.duarte@gmail.com>
* (c) Konstantin Kudryashov <ever.zet@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpSpec\CodeAnalysis;
use PhpSpec\Loader\Transformer\TypeHintIndex;
final class TokenizedTypeHintRewriter implements TypeHintRewriter
{
const STATE_DEFAULT = 0;
const STATE_READING_CLASS = 1;
const STATE_READING_FUNCTION = 2;
const STATE_READING_ARGUMENTS = 3;
private $state = self::STATE_DEFAULT;
private $currentClass;
private $currentFunction;
private $typehintTokens = array(
T_WHITESPACE, T_STRING, T_NS_SEPARATOR
);
/**
* @var TypeHintIndex
*/
private $typeHintIndex;
/**
* @var NamespaceResolver
*/
private $namespaceResolver;
/**
* @param TypeHintIndex $typeHintIndex
* @param NamespaceResolver $namespaceResolver
*/
public function __construct(TypeHintIndex $typeHintIndex, NamespaceResolver $namespaceResolver)
{
$this->typeHintIndex = $typeHintIndex;
$this->namespaceResolver = $namespaceResolver;
}
/**
* @param string $classDefinition
*
* @return string
*/
public function rewrite($classDefinition)
{
$this->namespaceResolver->analyse($classDefinition);
$this->reset();
$tokens = $this->stripTypeHints(token_get_all($classDefinition));
$tokensToString = $this->tokensToString($tokens);
return $tokensToString;
}
private function reset()
{
$this->state = self::STATE_DEFAULT;
$this->currentClass = '';
$this->currentFunction = '';
}
/**
* @param array $tokens
* @return array $tokens
*/
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);
}
break;
case self::STATE_READING_FUNCTION:
if ('(' == $token) {
$this->state = self::STATE_READING_ARGUMENTS;
}
elseif ($this->tokenHasType($token, T_STRING) && !$this->currentFunction) {
$this->currentFunction = $token[1];
}
break;
case self::STATE_READING_CLASS:
if ($this->tokenHasType($token, T_STRING) && !$this->currentClass) {
$this->currentClass = $token[1];
}
elseif($this->tokenHasType($token, T_FUNCTION)) {
$this->state = self::STATE_READING_FUNCTION;
}
break;
default:
if ($this->tokenHasType($token, T_CLASS)) {
$this->state = self::STATE_READING_CLASS;
}
}
}
return $tokens;
}
/**
* @param array $tokens
* @return string
*/
private function tokensToString($tokens)
{
return join('', array_map(function ($token) {
return is_array($token) ? $token[1] : $token;
}, $tokens));
}
/**
* @param array $tokens
* @param integer $index
* @param array $token
*/
private function extractTypehints(&$tokens, $index, $token)
{
$typehint = '';
for ($i = $index - 1; in_array($tokens[$i][0], $this->typehintTokens); $i--) {
$typehint = $tokens[$i][1] . $typehint;
unset($tokens[$i]);
}
if ($typehint = trim($typehint)) {
$class = $this->namespaceResolver->resolve($this->currentClass);
try {
$typehintFcqn = $this->namespaceResolver->resolve($typehint);
$this->typeHintIndex->add(
$class,
trim($this->currentFunction),
$token[1],
$typehintFcqn
);
} catch (DisallowedScalarTypehintException $e) {
$this->typeHintIndex->addInvalid(
$class,
trim($this->currentFunction),
$token[1],
$e
);
}
}
}
/**
* @param array|string $token
* @param string $type
*
* @return bool
*/
private function tokenHasType($token, $type)
{
return is_array($token) && $type == $token[0];
}
}

View File

@@ -0,0 +1,24 @@
<?php
/*
* This file is part of PhpSpec, A php toolset to drive emergent
* design by specification.
*
* (c) Marcello Duarte <marcello.duarte@gmail.com>
* (c) Konstantin Kudryashov <ever.zet@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpSpec\CodeAnalysis;
interface TypeHintRewriter
{
/**
* @param string $classDefinition
*
* @return string
*/
public function rewrite($classDefinition);
}

View File

@@ -0,0 +1,107 @@
<?php
namespace PhpSpec\CodeGenerator\Generator;
use PhpSpec\Event\FileCreationEvent;
use PhpSpec\Locator\ResourceInterface;
use PhpSpec\Util\Filesystem;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
class NewFileNotifyingGenerator implements GeneratorInterface
{
/**
* @var GeneratorInterface
*/
private $generator;
/**
* @var EventDispatcherInterface
*/
private $dispatcher;
/**
* @var Filesystem
*/
private $filesystem;
/**
* @param GeneratorInterface $generator
* @param EventDispatcherInterface $dispatcher
* @param Filesystem $filesystem
*/
public function __construct(GeneratorInterface $generator, EventDispatcherInterface $dispatcher, Filesystem $filesystem = null)
{
$this->generator = $generator;
$this->dispatcher = $dispatcher;
$this->filesystem = $filesystem ?: new Filesystem();
}
/**
* @param ResourceInterface $resource
* @param string $generation
* @param array $data
*
* @return bool
*/
public function supports(ResourceInterface $resource, $generation, array $data)
{
return $this->generator->supports($resource, $generation, $data);
}
/**
* @param ResourceInterface $resource
* @param array $data
*/
public function generate(ResourceInterface $resource, array $data)
{
$filePath = $this->getFilePath($resource);
$fileExisted = $this->fileExists($filePath);
$this->generator->generate($resource, $data);
$this->dispatchEventIfFileWasCreated($fileExisted, $filePath);
}
/**
* @return int
*/
public function getPriority()
{
return $this->generator->getPriority();
}
/**
* @param ResourceInterface $resource
* @return string
*/
private function getFilePath(ResourceInterface $resource)
{
if ($this->generator->supports($resource, 'specification', array())) {
return $resource->getSpecFilename();
}
return $resource->getSrcFilename();
}
/**
* @param string $filePath
* @return bool
*/
private function fileExists($filePath)
{
return $this->filesystem->pathExists($filePath);
}
/**
* @param bool $fileExisted
* @param string $filePath
*/
private function dispatchEventIfFileWasCreated($fileExisted, $filePath)
{
if (!$fileExisted && $this->fileExists($filePath)) {
$event = new FileCreationEvent($filePath);
$this->dispatcher->dispatch('afterFileCreation', $event);
}
}
}

View File

@@ -39,15 +39,17 @@ abstract class PromptingGenerator implements GeneratorInterface
* @var Filesystem
*/
private $filesystem;
/**
* @var ExecutionContextInterface
*/
private $executionContext;
/**
* @param IO $io
* @param IO $io
* @param TemplateRenderer $templates
* @param Filesystem $filesystem
* @param Filesystem $filesystem
* @param ExecutionContextInterface $executionContext
*/
public function __construct(IO $io, TemplateRenderer $templates, Filesystem $filesystem = null, ExecutionContextInterface $executionContext = null)
{
@@ -65,7 +67,7 @@ abstract class PromptingGenerator implements GeneratorInterface
{
$filepath = $this->getFilePath($resource);
if ($this->ifFileAlreadyExists($filepath)) {
if ($this->fileAlreadyExists($filepath)) {
if ($this->userAborts($filepath)) {
return;
}
@@ -114,7 +116,7 @@ abstract class PromptingGenerator implements GeneratorInterface
*
* @return bool
*/
private function ifFileAlreadyExists($filepath)
private function fileAlreadyExists($filepath)
{
return $this->filesystem->pathExists($filepath);
}

View File

@@ -50,10 +50,11 @@ class SpecificationGenerator extends PromptingGenerator
protected function renderTemplate(ResourceInterface $resource, $filepath)
{
$values = array(
'%filepath%' => $filepath,
'%name%' => $resource->getSpecName(),
'%namespace%' => $resource->getSpecNamespace(),
'%subject%' => $resource->getSrcClassname()
'%filepath%' => $filepath,
'%name%' => $resource->getSpecName(),
'%namespace%' => $resource->getSpecNamespace(),
'%subject%' => $resource->getSrcClassname(),
'%subject_class%' => $resource->getName()
);
if (!$content = $this->getTemplateRenderer()->render('specification', $values)) {

View File

@@ -14,6 +14,7 @@
namespace PhpSpec\Console;
use PhpSpec\Console\Prompter\Factory;
use PhpSpec\Loader\StreamWrapper;
use PhpSpec\Process\Context\JsonExecutionContext;
use Symfony\Component\Console\Application as BaseApplication;
use Symfony\Component\Console\Input\InputInterface;
@@ -87,6 +88,12 @@ class Application extends BaseApplication
$this->container->get('console.io')->setConsoleWidth($this->getTerminalWidth());
StreamWrapper::reset();
foreach ($this->container->getByPrefix('loader.resource_loader.spec_transformer') as $transformer) {
StreamWrapper::addTransformer($transformer);
}
StreamWrapper::register();
return parent::doRun($input, $output);
}

View File

@@ -0,0 +1,198 @@
<?php
/*
* This file is part of PhpSpec, A php toolset to drive emergent
* design by specification.
*
* (c) Marcello Duarte <marcello.duarte@gmail.com>
* (c) Konstantin Kudryashov <ever.zet@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpSpec\Console\Assembler;
use PhpSpec\Formatter\Presenter\Differ\ArrayEngine;
use PhpSpec\Formatter\Presenter\Differ\Differ;
use PhpSpec\Formatter\Presenter\Differ\ObjectEngine;
use PhpSpec\Formatter\Presenter\Differ\StringEngine;
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\TaggingExceptionElementPresenter;
use PhpSpec\Formatter\Presenter\SimplePresenter;
use PhpSpec\Formatter\Presenter\TaggedPresenter;
use PhpSpec\Formatter\Presenter\TaggingPresenter;
use PhpSpec\Formatter\Presenter\Value\ArrayTypePresenter;
use PhpSpec\Formatter\Presenter\Value\BaseExceptionTypePresenter;
use PhpSpec\Formatter\Presenter\Value\BooleanTypePresenter;
use PhpSpec\Formatter\Presenter\Value\CallableTypePresenter;
use PhpSpec\Formatter\Presenter\Value\ComposedValuePresenter;
use PhpSpec\Formatter\Presenter\Value\NullTypePresenter;
use PhpSpec\Formatter\Presenter\Value\ObjectTypePresenter;
use PhpSpec\Formatter\Presenter\Value\QuotingStringTypePresenter;
use PhpSpec\Formatter\Presenter\Value\TruncatingStringTypePresenter;
use PhpSpec\ServiceContainer;
use SebastianBergmann\Exporter\Exporter;
class PresenterAssembler
{
/**
* @param ServiceContainer $container
*/
public function assemble(ServiceContainer $container)
{
$this->assembleDiffer($container);
$this->assembleDifferEngines($container);
$this->assembleTypePresenters($container);
$this->assemblePresenter($container);
$this->assembleHtmlPresenter($container);
}
/**
* @param ServiceContainer $container
*/
private function assembleDiffer(ServiceContainer $container)
{
$container->setShared('formatter.presenter.differ', function (ServiceContainer $c) {
$differ = new Differ();
array_map(
array($differ, 'addEngine'),
$c->getByPrefix('formatter.presenter.differ.engines')
);
return $differ;
});
}
/**
* @param ServiceContainer $container
*/
private function assembleDifferEngines(ServiceContainer $container)
{
$container->set('formatter.presenter.differ.engines.string', function () {
return new StringEngine();
});
$container->set('formatter.presenter.differ.engines.array', function () {
return new ArrayEngine();
});
$container->set('formatter.presenter.differ.engines.object', function (ServiceContainer $c) {
return new ObjectEngine(
new Exporter(),
$c->get('formatter.presenter.differ.engines.string')
);
});
}
/**
* @param ServiceContainer $container
*/
private function assembleTypePresenters(ServiceContainer $container)
{
$container->setShared('formatter.presenter.value.array_type_presenter', function () {
return new ArrayTypePresenter();
});
$container->setShared('formatter.presenter.value.boolean_type_presenter', function () {
return new BooleanTypePresenter();
});
$container->setShared('formatter.presenter.value.callable_type_presenter', function (ServiceContainer $c) {
return new CallableTypePresenter($c->get('formatter.presenter'));
});
$container->setShared('formatter.presenter.value.exception_type_presenter', function () {
return new BaseExceptionTypePresenter();
});
$container->setShared('formatter.presenter.value.null_type_presenter', function () {
return new NullTypePresenter();
});
$container->setShared('formatter.presenter.value.object_type_presenter', function () {
return new ObjectTypePresenter();
});
$container->setShared('formatter.presenter.value.string_type_presenter', function () {
return new TruncatingStringTypePresenter(new QuotingStringTypePresenter());
});
$container->addConfigurator(function (ServiceContainer $c) {
array_map(
array($c->get('formatter.presenter.value_presenter'), 'addTypePresenter'),
$c->getByPrefix('formatter.presenter.value')
);
});
}
/**
* @param ServiceContainer $container
*/
private function assemblePresenter(ServiceContainer $container)
{
$container->setShared('formatter.presenter', function (ServiceContainer $c) {
return new TaggingPresenter(
new SimplePresenter(
$c->get('formatter.presenter.value_presenter'),
new SimpleExceptionPresenter(
$c->get('formatter.presenter.differ'),
$c->get('formatter.presenter.exception_element_presenter'),
new CallArgumentsPresenter($c->get('formatter.presenter.differ')),
$c->get('formatter.presenter.exception.phpspec')
)
)
);
});
$container->setShared('formatter.presenter.value_presenter', function () {
return new ComposedValuePresenter();
});
$container->setShared('formatter.presenter.exception_element_presenter', function (ServiceContainer $c) {
return new TaggingExceptionElementPresenter(
$c->get('formatter.presenter.value.exception_type_presenter'),
$c->get('formatter.presenter.value_presenter')
);
});
$container->setShared('formatter.presenter.exception.phpspec', function (ServiceContainer $c) {
return new GenericPhpSpecExceptionPresenter(
$c->get('formatter.presenter.exception_element_presenter')
);
});
}
/**
* @param ServiceContainer $container
*/
private function assembleHtmlPresenter(ServiceContainer $container)
{
$container->setShared('formatter.presenter.html', function (ServiceContainer $c) {
new SimplePresenter(
$c->get('formatter.presenter.value_presenter'),
new SimpleExceptionPresenter(
$c->get('formatter.presenter.differ'),
$c->get('formatter.presenter.html.exception_element_presenter'),
new CallArgumentsPresenter($c->get('formatter.presenter.differ')),
$c->get('formatter.presenter.html.exception.phpspec')
)
);
});
$container->setShared('formatter.presenter.html.exception_element_presenter', function (ServiceContainer $c) {
return new SimpleExceptionElementPresenter(
$c->get('formatter.presenter.value.exception_type_presenter'),
$c->get('formatter.presenter.value_presenter')
);
});
$container->setShared('formatter.presenter.html.exception.phpspec', function () {
return new HtmlPhpSpecExceptionPresenter();
});
}
}

View File

@@ -13,12 +13,16 @@
namespace PhpSpec\Console\Command;
use PhpSpec\Formatter\FatalPresenter;
use PhpSpec\Process\Shutdown\UpdateConsoleAction;
use PhpSpec\ServiceContainer;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
/**
* Main command, responsible for running the specs
*/
@@ -134,6 +138,25 @@ EOF
'formatter.name',
$input->getOption('format') ?: $container->getParam('formatter.name')
);
$formatterName = $container->getParam('formatter.name', 'progress');
$currentFormatter = $container->get('formatter.formatters.'.$formatterName);
if ($currentFormatter instanceof FatalPresenter) {
$container->setShared('process.shutdown.update_console_action', function(ServiceContainer $c) use ($currentFormatter) {
return new UpdateConsoleAction(
$c->get('current_example'),
$currentFormatter
);
});
$container->get('process.shutdown')->registerAction(
$container->get('process.shutdown.update_console_action')
);
$container->get('process.shutdown')->registerShutdown();
}
$container->configure();
$locator = $input->getArgument('spec');

View File

@@ -14,7 +14,11 @@
namespace PhpSpec\Console;
use PhpSpec\CodeAnalysis\MagicAwareAccessInspector;
use PhpSpec\CodeAnalysis\StaticRejectingNamespaceResolver;
use PhpSpec\CodeAnalysis\TokenizedNamespaceResolver;
use PhpSpec\CodeAnalysis\TokenizedTypeHintRewriter;
use PhpSpec\CodeAnalysis\VisibilityAccessInspector;
use PhpSpec\Console\Assembler\PresenterAssembler;
use PhpSpec\Process\Prerequisites\SuitePrerequisites;
use SebastianBergmann\Exporter\Exporter;
use PhpSpec\Process\ReRunner;
@@ -30,8 +34,9 @@ use PhpSpec\Matcher;
use PhpSpec\Runner;
use PhpSpec\Wrapper;
use PhpSpec\Config\OptionsConfig;
use RuntimeException;
use Symfony\Component\Process\PhpExecutableFinder;
use PhpSpec\Message\CurrentExampleTracker;
use PhpSpec\Process\Shutdown\Shutdown;
class ContainerAssembler
{
@@ -54,6 +59,8 @@ class ContainerAssembler
$this->setupRerunner($container);
$this->setupMatchers($container);
$this->setupSubscribers($container);
$this->setupCurrentExample($container);
$this->setupShutdown($container);
}
private function setupIO(ServiceContainer $container)
@@ -193,6 +200,11 @@ class ContainerAssembler
$c->get('console.io')
);
});
$container->setShared('event_dispatcher.listeners.current_example_listener', function (ServiceContainer $c) {
return new Listener\CurrentExampleListener(
$c->get('current_example')
);
});
}
/**
@@ -212,26 +224,41 @@ class ContainerAssembler
});
$container->set('code_generator.generators.specification', function (ServiceContainer $c) {
return new CodeGenerator\Generator\SpecificationGenerator(
$specificationGenerator = new CodeGenerator\Generator\SpecificationGenerator(
$c->get('console.io'),
$c->get('code_generator.templates')
);
return new CodeGenerator\Generator\NewFileNotifyingGenerator(
$specificationGenerator,
$c->get('event_dispatcher')
);
});
$container->set('code_generator.generators.class', function (ServiceContainer $c) {
return new CodeGenerator\Generator\ClassGenerator(
$classGenerator = new CodeGenerator\Generator\ClassGenerator(
$c->get('console.io'),
$c->get('code_generator.templates'),
null,
$c->get('process.executioncontext')
);
return new CodeGenerator\Generator\NewFileNotifyingGenerator(
$classGenerator,
$c->get('event_dispatcher')
);
});
$container->set('code_generator.generators.interface', function (ServiceContainer $c) {
return new CodeGenerator\Generator\InterfaceGenerator(
$interfaceGenerator = new CodeGenerator\Generator\InterfaceGenerator(
$c->get('console.io'),
$c->get('code_generator.templates'),
null,
$c->get('process.executioncontext')
);
return new CodeGenerator\Generator\NewFileNotifyingGenerator(
$interfaceGenerator,
$c->get('event_dispatcher')
);
});
$container->set('code_generator.generators.method', function (ServiceContainer $c) {
return new CodeGenerator\Generator\MethodGenerator(
@@ -276,7 +303,7 @@ class ContainerAssembler
if (!empty($_SERVER['HOMEDRIVE']) && !empty($_SERVER['HOMEPATH'])) {
$home = $_SERVER['HOMEDRIVE'].$_SERVER['HOMEPATH'];
} else {
$home = $_SERVER['HOME'];
$home = getenv('HOME');
}
$container->setParam('code_generator.templates.paths', array(
@@ -290,33 +317,8 @@ class ContainerAssembler
*/
private function setupPresenter(ServiceContainer $container)
{
$container->setShared('formatter.presenter', function (ServiceContainer $c) {
return new SpecFormatter\Presenter\TaggedPresenter($c->get('formatter.presenter.differ'));
});
$container->setShared('formatter.presenter.differ', function (ServiceContainer $c) {
$differ = new SpecFormatter\Presenter\Differ\Differ();
array_map(
array($differ, 'addEngine'),
$c->getByPrefix('formatter.presenter.differ.engines')
);
return $differ;
});
$container->set('formatter.presenter.differ.engines.string', function () {
return new SpecFormatter\Presenter\Differ\StringEngine();
});
$container->set('formatter.presenter.differ.engines.array', function () {
return new SpecFormatter\Presenter\Differ\ArrayEngine();
});
$container->set('formatter.presenter.differ.engines.object', function (ServiceContainer $c) {
return new SpecFormatter\Presenter\Differ\ObjectEngine(
new Exporter(),
$c->get('formatter.presenter.differ.engines.string')
);
});
$presenterAssembler = new PresenterAssembler();
$presenterAssembler->assemble($container);
}
/**
@@ -382,6 +384,29 @@ class ContainerAssembler
$container->setShared('loader.resource_loader', function (ServiceContainer $c) {
return new Loader\ResourceLoader($c->get('locator.resource_manager'));
});
if (PHP_VERSION >= 7) {
$container->setShared('loader.resource_loader.spec_transformer.typehint_rewriter', function (ServiceContainer $c) {
return new Loader\Transformer\TypeHintRewriter($c->get('analysis.typehintrewriter'));
});
}
$container->setShared('analysis.typehintrewriter', function($c) {
return new TokenizedTypeHintRewriter(
$c->get('loader.transformer.typehintindex'),
$c->get('analysis.namespaceresolver')
);
});
$container->setShared('loader.transformer.typehintindex', function() {
return new Loader\Transformer\InMemoryTypeHintIndex();
});
$container->setShared('analysis.namespaceresolver.tokenized', function() {
return new TokenizedNamespaceResolver();
});
$container->setShared('analysis.namespaceresolver', function ($c) {
if (PHP_VERSION >= 7) {
return new StaticRejectingNamespaceResolver($c->get('analysis.namespaceresolver.tokenized'));
}
return $c->get('analysis.namespaceresolver.tokenized');
});
}
/**
@@ -447,7 +472,7 @@ class ContainerAssembler
$io = new SpecFormatter\Html\IO();
$template = new SpecFormatter\Html\Template($io);
$factory = new SpecFormatter\Html\ReportItemFactory($template);
$presenter = new SpecFormatter\Html\HtmlPresenter($c->get('formatter.presenter.differ'));
$presenter = $c->get('formatter.presenter.html');
return new SpecFormatter\HtmlFormatter(
$factory,
@@ -519,7 +544,10 @@ class ContainerAssembler
);
});
$container->set('runner.maintainers.collaborators', function (ServiceContainer $c) {
return new Runner\Maintainer\CollaboratorsMaintainer($c->get('unwrapper'));
return new Runner\Maintainer\CollaboratorsMaintainer(
$c->get('unwrapper'),
$c->get('loader.transformer.typehintindex')
);
});
$container->set('runner.maintainers.let_letgo', function () {
return new Runner\Maintainer\LetAndLetgoMaintainer();
@@ -603,6 +631,9 @@ class ContainerAssembler
$container->set('matchers.string_regex', function (ServiceContainer $c) {
return new Matcher\StringRegexMatcher($c->get('formatter.presenter'));
});
$container->set('matchers.string_contain', function (ServiceContainer $c) {
return new Matcher\StringContainMatcher($c->get('formatter.presenter'));
});
}
/**
@@ -655,4 +686,24 @@ class ContainerAssembler
);
});
}
/**
* @param ServiceContainer $container
*/
private function setupCurrentExample(ServiceContainer $container)
{
$container->setShared('current_example', function () {
return new CurrentExampleTracker();
});
}
/**
* @param ServiceContainer $container
*/
private function setupShutdown(ServiceContainer $container)
{
$container->setShared('process.shutdown', function() {
return new Shutdown();
});
}
}

View File

@@ -0,0 +1,38 @@
<?php
/*
* This file is part of PhpSpec, A php toolset to drive emergent
* design by specification.
*
* (c) Marcello Duarte <marcello.duarte@gmail.com>
* (c) Konstantin Kudryashov <ever.zet@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpSpec\Event;
use Symfony\Component\EventDispatcher\Event;
final class FileCreationEvent extends Event implements EventInterface
{
/**
* @var string
*/
private $filepath;
public function __construct($filepath)
{
$this->filepath = $filepath;
}
/**
* @return string
*/
public function getFilePath()
{
return $this->filepath;
}
}

View File

@@ -29,13 +29,22 @@ class CollaboratorNotFoundException extends FractureException
* @param string $message
* @param integer $code
* @param Exception $previous
* @param ReflectionParameter $reflectionParameter
* @param ReflectionParameter|null $reflectionParameter
* @param string|null $className
*/
public function __construct($message, $code = 0, Exception $previous = null, ReflectionParameter $reflectionParameter = null)
{
public function __construct(
$message,
$code = 0,
Exception $previous = null,
ReflectionParameter $reflectionParameter = null,
$className = null
) {
if ($reflectionParameter) {
$this->collaboratorName = $this->extractCollaboratorName($reflectionParameter);
}
if ($className) {
$this->collaboratorName = $className;
}
parent::__construct($message . ': ' . $this->collaboratorName, $code, $previous);
}

View File

@@ -0,0 +1,44 @@
<?php
/*
* This file is part of PhpSpec, A php toolset to drive emergent
* design by specification.
*
* (c) Marcello Duarte <marcello.duarte@gmail.com>
* (c) Konstantin Kudryashov <ever.zet@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpSpec\Exception\Wrapper;
class InvalidCollaboratorTypeException extends CollaboratorException
{
public function __construct(\ReflectionParameter $parameter, \ReflectionFunctionAbstract $function)
{
$message = sprintf(
'Collaborator must be an object: argument %s defined in %s. ' .
'You can create non-object values manually.',
$parameter->getPosition(),
$this->fetchFunctionIdentifier($function)
);
$this->setCause($function);
parent::__construct($message);
}
private function fetchFunctionIdentifier(\ReflectionFunctionAbstract $function)
{
$functionIdentifier = $function->getName();
if ($function instanceof \ReflectionMethod) {
$functionIdentifier = sprintf('%s::%s', $function->getDeclaringClass()->getName(), $function->getName());
}
return $functionIdentifier;
}
}

View File

@@ -19,8 +19,9 @@ use PhpSpec\Exception\Example\PendingException;
use PhpSpec\Exception\Example\SkippingException;
use PhpSpec\Formatter\Presenter\PresenterInterface;
use PhpSpec\Listener\StatisticsCollector;
use PhpSpec\Message\CurrentExampleTracker;
class ConsoleFormatter extends BasicFormatter
class ConsoleFormatter extends BasicFormatter implements FatalPresenter
{
/**
* @var IO
@@ -91,4 +92,21 @@ class ConsoleFormatter extends BasicFormatter
$this->io->writeln(sprintf('<%s>%s</%s>', $type, lcfirst($message), $type), 6);
$this->io->writeln();
}
public function displayFatal(CurrentExampleTracker $currentExample, $error)
{
if (
(null !== $error && $currentExample->getCurrentExample()) ||
(is_null($currentExample->getCurrentExample()) && defined('HHVM_VERSION'))
) {
ini_set('display_errors', "stderr");
$failedOpen = ($this->io->isDecorated()) ? '<failed>' : '';
$failedClosed = ($this->io->isDecorated()) ? '</failed>' : '';
$failedCross = ($this->io->isDecorated()) ? '✘' : '';
$this->io->writeln("$failedOpen$failedCross Fatal error happened while executing the following $failedClosed");
$this->io->writeln("$failedOpen {$currentExample->getCurrentExample()} $failedClosed");
$this->io->writeln("$failedOpen {$error['message']} in {$error['file']} on line {$error['line']} $failedClosed");
}
}
}

View File

@@ -0,0 +1,21 @@
<?php
/*
* This file is part of PhpSpec, A php toolset to drive emergent
* design by specification.
*
* (c) Marcello Duarte <marcello.duarte@gmail.com>
* (c) Konstantin Kudryashov <ever.zet@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpSpec\Formatter;
use PhpSpec\Message\CurrentExampleTracker;
interface FatalPresenter
{
public function displayFatal(CurrentExampleTracker $currentExample, $error);
}

View File

@@ -17,6 +17,9 @@ use PhpSpec\Formatter\Presenter\StringPresenter;
use Exception;
use PhpSpec\Exception\Exception as PhpSpecException;
/**
* @deprecated Use /PhpSpec/Formatter/Presenter/SimplePresenter with an HtmlPhpSpecExceptionPresenter instead
*/
class HtmlPresenter extends StringPresenter
{
/**

View File

@@ -55,13 +55,7 @@ class ArrayEngine extends StringEngine
);
break;
case 'string':
if (25 > strlen($val) && false === strpos($val, PHP_EOL)) {
$val = sprintf('"%s"', $val);
}
$lines = explode(PHP_EOL, $val);
$val = sprintf('"%s..."', substr($lines[0], 0, 25));
$val = sprintf('"%s"', $val);
$line = sprintf('%s => %s,', $key, $val);
break;
case 'integer':

View File

@@ -0,0 +1,60 @@
<?php
/*
* This file is part of PhpSpec, A php toolset to drive emergent
* design by specification.
*
* (c) Marcello Duarte <marcello.duarte@gmail.com>
* (c) Konstantin Kudryashov <ever.zet@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpSpec\Formatter\Presenter\Exception;
use PhpSpec\Exception\Exception;
abstract class AbstractPhpSpecExceptionPresenter
{
/**
* @param Exception $exception
* @return string
*/
public function presentException(Exception $exception)
{
list($file, $line) = $this->getExceptionExamplePosition($exception);
return $this->presentFileCode($file, $line);
}
/**
* @param Exception $exception
* @return array
*/
private function getExceptionExamplePosition(Exception $exception)
{
$cause = $exception->getCause();
foreach ($exception->getTrace() as $call) {
if (!isset($call['file'])) {
continue;
}
if (!empty($cause) && $cause->getFilename() === $call['file']) {
return array($call['file'], $call['line']);
}
}
return array($exception->getFile(), $exception->getLine());
}
/**
* @param string $file
* @param integer $lineno
* @param integer $context
*
* @return string
*/
abstract protected function presentFileCode($file, $lineno, $context = 6);
}

View File

@@ -0,0 +1,149 @@
<?php
/*
* This file is part of PhpSpec, A php toolset to drive emergent
* design by specification.
*
* (c) Marcello Duarte <marcello.duarte@gmail.com>
* (c) Konstantin Kudryashov <ever.zet@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpSpec\Formatter\Presenter\Exception;
use PhpSpec\Formatter\Presenter\Differ\Differ;
use Prophecy\Argument\Token\ExactValueToken;
use Prophecy\Exception\Call\UnexpectedCallException;
use Prophecy\Prophecy\MethodProphecy;
class CallArgumentsPresenter
{
/**
* @var Differ
*/
private $differ;
/**
* @param Differ $differ
*/
public function __construct(Differ $differ)
{
$this->differ = $differ;
}
/**
* @param UnexpectedCallException $exception
* @return string
*/
public function presentDifference(UnexpectedCallException $exception)
{
$actualArguments = $exception->getArguments();
$methodProphecies = $exception->getObjectProphecy()->getMethodProphecies($exception->getMethodName());
if ($this->noMethodPropheciesForUnexpectedCall($methodProphecies)) {
return '';
}
$presentedMethodProphecy = $this->findFirstUnexpectedArgumentsCallProphecy($methodProphecies, $exception);
if (is_null($presentedMethodProphecy)) {
return '';
}
$expectedTokens = $presentedMethodProphecy->getArgumentsWildcard()->getTokens();
if ($this->parametersCountMismatch($expectedTokens, $actualArguments)) {
return '';
}
$expectedArguments = $this->convertArgumentTokensToDiffableValues($expectedTokens);
$text = $this->generateArgumentsDifferenceText($actualArguments, $expectedArguments);
return $text;
}
/**
* @param MethodProphecy[] $methodProphecies
* @return bool
*/
private function noMethodPropheciesForUnexpectedCall(array $methodProphecies)
{
return count($methodProphecies) === 0;
}
/**
* @param MethodProphecy[] $methodProphecies
* @param UnexpectedCallException $exception
*
* @return MethodProphecy
*/
private function findFirstUnexpectedArgumentsCallProphecy(
array $methodProphecies,
UnexpectedCallException $exception
) {
$objectProphecy = $exception->getObjectProphecy();
foreach ($methodProphecies as $methodProphecy) {
$calls = $objectProphecy->findProphecyMethodCalls(
$exception->getMethodName(),
$methodProphecy->getArgumentsWildcard()
);
if (count($calls)) {
continue;
}
return $methodProphecy;
}
}
/**
* @param array $expectedTokens
* @param array $actualArguments
*
* @return bool
*/
private function parametersCountMismatch(array $expectedTokens, array $actualArguments)
{
return count($expectedTokens) !== count($actualArguments);
}
/**
* @param array $tokens
*
* @return array
*/
private function convertArgumentTokensToDiffableValues(array $tokens)
{
$values = array();
foreach ($tokens as $token) {
if ($token instanceof ExactValueToken) {
$values[] = $token->getValue();
} else {
$values[] = (string)$token;
}
}
return $values;
}
/**
* @param array $actualArguments
* @param array $expectedArguments
*
* @return string
*/
private function generateArgumentsDifferenceText(array $actualArguments, array $expectedArguments)
{
$text = '';
foreach($actualArguments as $i => $actualArgument) {
$expectedArgument = $expectedArguments[$i];
$actualArgument = is_null($actualArgument) ? 'null' : $actualArgument;
$expectedArgument = is_null($expectedArgument) ? 'null' : $expectedArgument;
$text .= $this->differ->compare($expectedArgument, $actualArgument);
}
return $text;
}
}

View File

@@ -0,0 +1,59 @@
<?php
/*
* This file is part of PhpSpec, A php toolset to drive emergent
* design by specification.
*
* (c) Marcello Duarte <marcello.duarte@gmail.com>
* (c) Konstantin Kudryashov <ever.zet@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpSpec\Formatter\Presenter\Exception;
interface ExceptionElementPresenter
{
/**
* @param \Exception $exception
* @return string
*/
public function presentExceptionThrownMessage(\Exception $exception);
/**
* @param string $number
* @param string $line
* @return string
*/
public function presentCodeLine($number, $line);
/**
* @param string $line
* @return string
*/
public function presentHighlight($line);
/**
* @param string $header
* @return string
*/
public function presentExceptionTraceHeader($header);
/**
* @param string $class
* @param string $type
* @param string $method
* @param array $args
* @return string
*/
public function presentExceptionTraceMethod($class, $type, $method, array $args);
/**
* @param string $function
* @param array $args
* @return string
*/
public function presentExceptionTraceFunction($function, array $args);
}

View File

@@ -0,0 +1,24 @@
<?php
/*
* This file is part of PhpSpec, A php toolset to drive emergent
* design by specification.
*
* (c) Marcello Duarte <marcello.duarte@gmail.com>
* (c) Konstantin Kudryashov <ever.zet@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpSpec\Formatter\Presenter\Exception;
interface ExceptionPresenter
{
/**
* @param \Exception $exception
* @param bool $verbose
* @return string
*/
public function presentException(\Exception $exception, $verbose = false);
}

View File

@@ -0,0 +1,59 @@
<?php
/*
* This file is part of PhpSpec, A php toolset to drive emergent
* design by specification.
*
* (c) Marcello Duarte <marcello.duarte@gmail.com>
* (c) Konstantin Kudryashov <ever.zet@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpSpec\Formatter\Presenter\Exception;
final class GenericPhpSpecExceptionPresenter extends AbstractPhpSpecExceptionPresenter implements PhpSpecExceptionPresenter
{
/**
* @var ExceptionElementPresenter
*/
private $exceptionElementPresenter;
/**
* @param ExceptionElementPresenter $exceptionElementPresenter
*/
public function __construct(ExceptionElementPresenter $exceptionElementPresenter)
{
$this->exceptionElementPresenter = $exceptionElementPresenter;
}
/**
* @param string $file
* @param integer $lineno
* @param integer $context
*
* @return string
*/
protected function presentFileCode($file, $lineno, $context = 6)
{
$lines = explode(PHP_EOL, file_get_contents($file));
$offset = max(0, $lineno - ceil($context / 2));
$lines = array_slice($lines, $offset, $context);
$text = PHP_EOL;
foreach ($lines as $line) {
$offset++;
if ($offset == $lineno) {
$text .= $this->exceptionElementPresenter->presentHighlight(sprintf('%4d', $offset).' '.$line);
} else {
$text .= $this->exceptionElementPresenter->presentCodeLine(sprintf('%4d', $offset), $line);
}
$text .= PHP_EOL;
}
return $text;
}
}

View File

@@ -0,0 +1,46 @@
<?php
/*
* This file is part of PhpSpec, A php toolset to drive emergent
* design by specification.
*
* (c) Marcello Duarte <marcello.duarte@gmail.com>
* (c) Konstantin Kudryashov <ever.zet@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpSpec\Formatter\Presenter\Exception;
final class HtmlPhpSpecExceptionPresenter extends AbstractPhpSpecExceptionPresenter implements PhpSpecExceptionPresenter
{
/**
* @param string $file
* @param integer $lineno
* @param integer $context
*
* @return string
*/
protected function presentFileCode($file, $lineno, $context = 6)
{
$lines = explode(PHP_EOL, file_get_contents($file));
$offset = max(0, $lineno - ceil($context / 2));
$lines = array_slice($lines, $offset, $context);
$text = PHP_EOL;
foreach ($lines as $line) {
$offset++;
$text .= sprintf(
'<span class="linenum">%d</span><span class="%s">%s</span>' . PHP_EOL,
$offset,
$offset == $lineno ? 'offending' : 'normal',
$line
);
}
return $text;
}
}

View File

@@ -0,0 +1,26 @@
<?php
/*
* This file is part of PhpSpec, A php toolset to drive emergent
* design by specification.
*
* (c) Marcello Duarte <marcello.duarte@gmail.com>
* (c) Konstantin Kudryashov <ever.zet@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpSpec\Formatter\Presenter\Exception;
use PhpSpec\Exception\Exception;
interface PhpSpecExceptionPresenter
{
/**
* @param Exception $exception
* @return string
*/
public function presentException(Exception $exception);
}

View File

@@ -0,0 +1,111 @@
<?php
/*
* This file is part of PhpSpec, A php toolset to drive emergent
* design by specification.
*
* (c) Marcello Duarte <marcello.duarte@gmail.com>
* (c) Konstantin Kudryashov <ever.zet@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpSpec\Formatter\Presenter\Exception;
use PhpSpec\Formatter\Presenter\Value\ExceptionTypePresenter;
use PhpSpec\Formatter\Presenter\Value\ValuePresenter;
final class SimpleExceptionElementPresenter implements ExceptionElementPresenter
{
/**
* @var ExceptionTypePresenter
*/
private $exceptionTypePresenter;
/**
* @var ValuePresenter
*/
private $valuePresenter;
/**
* @param ExceptionTypePresenter $exceptionTypePresenter
* @param ValuePresenter $valuePresenter
*/
public function __construct(ExceptionTypePresenter $exceptionTypePresenter, ValuePresenter $valuePresenter)
{
$this->exceptionTypePresenter = $exceptionTypePresenter;
$this->valuePresenter = $valuePresenter;
}
/**
* @param \Exception $exception
* @return string
*/
public function presentExceptionThrownMessage(\Exception $exception)
{
return sprintf(
'Exception %s has been thrown.',
$this->exceptionTypePresenter->present($exception)
);
}
/**
* @param string $number
* @param string $line
* @return string
*/
public function presentCodeLine($number, $line)
{
return sprintf('%s %s', $number, $line);
}
/**
* @param string $line
* @return string
*/
public function presentHighlight($line)
{
return $line;
}
/**
* @param string $header
* @return string
*/
public function presentExceptionTraceHeader($header)
{
return $header;
}
/**
* @param string $class
* @param string $type
* @param string $method
* @param array $args
* @return string
*/
public function presentExceptionTraceMethod($class, $type, $method, array $args)
{
return sprintf(' %s%s%s(%s)', $class, $type, $method, $this->presentExceptionTraceArguments($args));
}
/**
* @param string $function
* @param array $args
* @return string
*/
public function presentExceptionTraceFunction($function, array $args)
{
return sprintf(' %s(%s)', $function, $this->presentExceptionTraceArguments($args));
}
/**
* @param array $args
* @return array
*/
private function presentExceptionTraceArguments(array $args)
{
return implode(', ', array_map(array($this->valuePresenter, 'presentValue'), $args));
}
}

View File

@@ -0,0 +1,273 @@
<?php
/*
* This file is part of PhpSpec, A php toolset to drive emergent
* design by specification.
*
* (c) Marcello Duarte <marcello.duarte@gmail.com>
* (c) Konstantin Kudryashov <ever.zet@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpSpec\Formatter\Presenter\Exception;
use PhpSpec\Exception\Example\ErrorException;
use PhpSpec\Exception\Example\NotEqualException;
use PhpSpec\Exception\Example\PendingException;
use PhpSpec\Exception\Exception as PhpSpecException;
use PhpSpec\Formatter\Presenter\Differ\Differ;
use Prophecy\Exception\Call\UnexpectedCallException;
use Prophecy\Exception\Exception as ProphecyException;
final class SimpleExceptionPresenter implements ExceptionPresenter
{
/**
* @var Differ
*/
private $differ;
/**
* @var string
*/
private $phpspecPath;
/**
* @var string
*/
private $runnerPath;
/**
* @var ExceptionElementPresenter
*/
private $exceptionElementPresenter;
/**
* @var CallArgumentsPresenter
*/
private $callArgumentsPresenter;
/**
* @var PhpSpecExceptionPresenter
*/
private $phpspecExceptionPresenter;
/**
* @param Differ $differ
* @param ExceptionElementPresenter $exceptionElementPresenter
* @param CallArgumentsPresenter $callArgumentsPresenter
* @param PhpSpecExceptionPresenter $phpspecExceptionPresenter
*/
public function __construct(
Differ $differ,
ExceptionElementPresenter $exceptionElementPresenter,
CallArgumentsPresenter $callArgumentsPresenter,
PhpSpecExceptionPresenter $phpspecExceptionPresenter
) {
$this->differ = $differ;
$this->exceptionElementPresenter = $exceptionElementPresenter;
$this->callArgumentsPresenter = $callArgumentsPresenter;
$this->phpspecExceptionPresenter = $phpspecExceptionPresenter;
$this->phpspecPath = dirname(dirname(__DIR__));
$this->runnerPath = $this->phpspecPath.DIRECTORY_SEPARATOR.'Runner';
}
/**
* @param \Exception $exception
* @param bool $verbose
* @return string
*/
public function presentException(\Exception $exception, $verbose = false)
{
if ($exception instanceof PhpSpecException) {
$presentation = wordwrap($exception->getMessage(), 120);
} elseif ($exception instanceof ProphecyException) {
$presentation = $exception->getMessage();
} else {
$presentation = $this->exceptionElementPresenter->presentExceptionThrownMessage($exception);
}
if (!$verbose || $exception instanceof PendingException) {
return $presentation;
}
return $this->getVerbosePresentation($exception, $presentation);
}
/**
* @param \Exception $exception
* @param string $presentation
* @return string
*/
private function getVerbosePresentation(\Exception $exception, $presentation)
{
if ($exception instanceof NotEqualException) {
if ($diff = $this->presentExceptionDifference($exception)) {
$presentation .= PHP_EOL . $diff;
}
}
if ($exception instanceof PhpSpecException && !$exception instanceof ErrorException) {
$presentation .= PHP_EOL . $this->phpspecExceptionPresenter->presentException($exception);
}
if ($exception instanceof UnexpectedCallException) {
$presentation .= $this->callArgumentsPresenter->presentDifference($exception);
}
return $presentation . $this->presentExceptionStackTrace($exception);
}
/**
* @param NotEqualException $exception
*
* @return string
*/
private function presentExceptionDifference(NotEqualException $exception)
{
return $this->differ->compare($exception->getExpected(), $exception->getActual());
}
/**
* @param \Exception $exception
*
* @return string
*/
private function presentExceptionStackTrace(\Exception $exception)
{
$offset = 0;
$text = '';
$text .= $this->presentExceptionTraceLocation($offset++, $exception->getFile(), $exception->getLine());
$text .= $this->presentExceptionTraceFunction(
'throw new '.get_class($exception),
array($exception->getMessage())
);
foreach ($exception->getTrace() as $call) {
// skip internal framework calls
if ($this->shouldStopTracePresentation($call)) {
break;
}
if ($this->shouldSkipTracePresentation($call)) {
continue;
}
$text .= $this->presentExceptionTraceDetails($call, $offset++);
}
return empty($text) ? $text : PHP_EOL . $text;
}
/**
* @param string $header
*
* @return string
*/
private function presentExceptionTraceHeader($header)
{
return $this->exceptionElementPresenter->presentExceptionTraceHeader($header) . PHP_EOL;
}
/**
* @param string $class
* @param string $type
* @param string $method
* @param array $args
*
* @return string
*/
private function presentExceptionTraceMethod($class, $type, $method, array $args)
{
return $this->exceptionElementPresenter->presentExceptionTraceMethod($class, $type, $method, $args) . PHP_EOL;
}
/**
* @param string $function
* @param array $args
*
* @return string
*/
private function presentExceptionTraceFunction($function, array $args)
{
return $this->exceptionElementPresenter->presentExceptionTraceFunction($function, $args) . PHP_EOL;
}
/**
* @param int $offset
* @param string $file
* @param int $line
*
* @return string
*/
private function presentExceptionTraceLocation($offset, $file, $line)
{
return $this->presentExceptionTraceHeader(sprintf(
"%2d %s:%d",
$offset,
str_replace(getcwd().DIRECTORY_SEPARATOR, '', $file),
$line
));
}
/**
* @param array $call
* @return bool
*/
private function shouldStopTracePresentation(array $call)
{
return isset($call['file']) && false !== strpos($call['file'], $this->runnerPath);
}
/**
* @param array $call
* @return bool
*/
private function shouldSkipTracePresentation(array $call)
{
if (isset($call['file']) && 0 === strpos($call['file'], $this->phpspecPath)) {
return true;
}
return isset($call['class']) && 0 === strpos($call['class'], "PhpSpec\\");
}
/**
* @param array $call
* @param int $offset
* @return string
*/
private function presentExceptionTraceDetails(array $call, $offset)
{
$text = '';
if (isset($call['file'])) {
$text .= $this->presentExceptionTraceLocation($offset, $call['file'], $call['line']);
} else {
$text .= $this->presentExceptionTraceHeader(sprintf("%2d [internal]", $offset));
}
if (!isset($call['args'])) {
$call['args'] = array();
}
if (isset($call['class'])) {
$text .= $this->presentExceptionTraceMethod(
$call['class'],
$call['type'],
$call['function'],
$call['args']
);
} elseif (isset($call['function'])) {
$text .= $this->presentExceptionTraceFunction(
$call['function'],
$call['args']
);
}
return $text;
}
}

View File

@@ -0,0 +1,122 @@
<?php
/*
* This file is part of PhpSpec, A php toolset to drive emergent
* design by specification.
*
* (c) Marcello Duarte <marcello.duarte@gmail.com>
* (c) Konstantin Kudryashov <ever.zet@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpSpec\Formatter\Presenter\Exception;
use PhpSpec\Formatter\Presenter\Value\ExceptionTypePresenter;
use PhpSpec\Formatter\Presenter\Value\ValuePresenter;
final class TaggingExceptionElementPresenter implements ExceptionElementPresenter
{
/**
* @var ExceptionTypePresenter
*/
private $exceptionTypePresenter;
/**
* @var ValuePresenter
*/
private $valuePresenter;
/**
* @param ExceptionTypePresenter $exceptionTypePresenter
* @param ValuePresenter $valuePresenter
*/
public function __construct(ExceptionTypePresenter $exceptionTypePresenter, ValuePresenter $valuePresenter)
{
$this->exceptionTypePresenter = $exceptionTypePresenter;
$this->valuePresenter = $valuePresenter;
}
/**
* @param \Exception $exception
* @return string
*/
public function presentExceptionThrownMessage(\Exception $exception)
{
return sprintf(
'Exception <label>%s</label> has been thrown.',
$this->exceptionTypePresenter->present($exception)
);
}
/**
* @param string $number
* @param string $line
* @return string
*/
public function presentCodeLine($number, $line)
{
return sprintf('<lineno>%s</lineno> <code>%s</code>', $number, $line);
}
/**
* @param string $line
* @return string
*/
public function presentHighlight($line)
{
return sprintf('<hl>%s</hl>', $line);
}
/**
* @param string $header
* @return string
*/
public function presentExceptionTraceHeader($header)
{
return sprintf('<trace>%s</trace>', $header);
}
/**
* @param string $class
* @param string $type
* @param string $method
* @param array $args
* @return string
*/
public function presentExceptionTraceMethod($class, $type, $method, array $args)
{
$template = ' <trace><trace-class>%s</trace-class><trace-type>%s</trace-type>'.
'<trace-func>%s</trace-func>(<trace-args>%s</trace-args>)</trace>';
return sprintf($template, $class, $type, $method, $this->presentExceptionTraceArguments($args));
}
/**
* @param string $function
* @param array $args
* @return string
*/
public function presentExceptionTraceFunction($function, array $args)
{
$template = ' <trace><trace-func>%s</trace-func>(<trace-args>%s</trace-args>)</trace>';
return sprintf($template, $function, $this->presentExceptionTraceArguments($args));
}
/**
* @param array $args
* @return array
*/
private function presentExceptionTraceArguments(array $args)
{
$valuePresenter = $this->valuePresenter;
$taggedArgs = array_map(function ($arg) use ($valuePresenter) {
return sprintf('<value>%s</value>', $valuePresenter->presentValue($arg));
}, $args);
return implode(', ', $taggedArgs);
}
}

View File

@@ -0,0 +1,27 @@
<?php
/*
* This file is part of PhpSpec, A php toolset to drive emergent
* design by specification.
*
* (c) Marcello Duarte <marcello.duarte@gmail.com>
* (c) Konstantin Kudryashov <ever.zet@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpSpec\Formatter\Presenter;
use PhpSpec\Formatter\Presenter\Exception\ExceptionPresenter;
use PhpSpec\Formatter\Presenter\Value\ValuePresenter;
interface Presenter extends ExceptionPresenter, ValuePresenter
{
/**
* @param string $string
*
* @return string
*/
public function presentString($string);
}

View File

@@ -13,27 +13,9 @@
namespace PhpSpec\Formatter\Presenter;
interface PresenterInterface
/**
* @deprecated Use PhpSpec\Formatter\Presenter\Presenter instead
*/
interface PresenterInterface extends Presenter
{
/**
* @param mixed $value
*
* @return string
*/
public function presentValue($value);
/**
* @param \Exception $exception
* @param bool $verbose
*
* @return string
*/
public function presentException(\Exception $exception, $verbose = false);
/**
* @param string $string
*
* @return string
*/
public function presentString($string);
}

View File

@@ -0,0 +1,71 @@
<?php
/*
* This file is part of PhpSpec, A php toolset to drive emergent
* design by specification.
*
* (c) Marcello Duarte <marcello.duarte@gmail.com>
* (c) Konstantin Kudryashov <ever.zet@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpSpec\Formatter\Presenter;
use PhpSpec\Formatter\Presenter\Exception\ExceptionPresenter;
use PhpSpec\Formatter\Presenter\Value\ValuePresenter;
final class SimplePresenter implements PresenterInterface
{
/**
* @var ValuePresenter
*/
private $valuePresenter;
/**
* @var ExceptionPresenter
*/
private $exceptionPresenter;
/**
* @param ValuePresenter $valuePresenter
* @param ExceptionPresenter $exceptionPresenter
*/
public function __construct(ValuePresenter $valuePresenter, ExceptionPresenter $exceptionPresenter)
{
$this->valuePresenter = $valuePresenter;
$this->exceptionPresenter = $exceptionPresenter;
}
/**
* @param mixed $value
*
* @return string
*/
public function presentValue($value)
{
return $this->valuePresenter->presentValue($value);
}
/**
* @param \Exception $exception
* @param bool $verbose
*
* @return string
*/
public function presentException(\Exception $exception, $verbose = false)
{
return $this->exceptionPresenter->presentException($exception, $verbose);
}
/**
* @param string $string
*
* @return string
*/
public function presentString($string)
{
return $string;
}
}

View File

@@ -19,10 +19,14 @@ use PhpSpec\Exception\Example\NotEqualException;
use PhpSpec\Exception\Example\ErrorException;
use PhpSpec\Exception\Example\PendingException;
use Prophecy\Argument\Token\ExactValueToken;
use Prophecy\Argument\Token\TokenInterface;
use Prophecy\Exception\Call\UnexpectedCallException;
use Prophecy\Exception\Exception as ProphecyException;
use Prophecy\Prophecy\MethodProphecy;
/**
* @deprecated Use PhpSpec\Formatter\Presenter\SimplePresenter instead
*/
class StringPresenter implements PresenterInterface
{
/**
@@ -436,7 +440,7 @@ class StringPresenter implements PresenterInterface
}
/**
* @param array $expectedTokens
* @param TokenInterface[] $expectedTokens
* @param array $actualArguments
*
* @return bool
@@ -447,7 +451,7 @@ class StringPresenter implements PresenterInterface
}
/**
* @param array $tokens
* @param TokenInterface[] $tokens
*
* @return array
*/

View File

@@ -13,6 +13,9 @@
namespace PhpSpec\Formatter\Presenter;
/**
* @deprecated Use PhpSpec\Formatter\Presenter\TaggingPresenter instead
*/
class TaggedPresenter extends StringPresenter
{
public function presentString($string)

View File

@@ -0,0 +1,59 @@
<?php
/*
* This file is part of PhpSpec, A php toolset to drive emergent
* design by specification.
*
* (c) Marcello Duarte <marcello.duarte@gmail.com>
* (c) Konstantin Kudryashov <ever.zet@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpSpec\Formatter\Presenter;
final class TaggingPresenter implements PresenterInterface
{
/**
* @var Presenter
*/
private $presenter;
/**
* @param Presenter $presenter
*/
public function __construct(Presenter $presenter)
{
$this->presenter = $presenter;
}
/**
* @param \Exception $exception
* @param bool $verbose
* @return string
*/
public function presentException(\Exception $exception, $verbose = false)
{
return $this->presenter->presentException($exception, $verbose);
}
/**
* @param string $string
*
* @return string
*/
public function presentString($string)
{
return sprintf('<value>%s</value>', $string);
}
/**
* @param mixed $value
* @return string
*/
public function presentValue($value)
{
return $this->presentString($this->presenter->presentValue($value));
}
}

View File

@@ -0,0 +1,43 @@
<?php
/*
* This file is part of PhpSpec, A php toolset to drive emergent
* design by specification.
*
* (c) Marcello Duarte <marcello.duarte@gmail.com>
* (c) Konstantin Kudryashov <ever.zet@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpSpec\Formatter\Presenter\Value;
final class ArrayTypePresenter implements TypePresenter
{
/**
* @param mixed $value
* @return bool
*/
public function supports($value)
{
return 'array' === strtolower(gettype($value));
}
/**
* @param mixed $value
* @return string
*/
public function present($value)
{
return sprintf('[array:%d]', count($value));
}
/**
* @return int
*/
public function getPriority()
{
return 20;
}
}

View File

@@ -0,0 +1,47 @@
<?php
/*
* This file is part of PhpSpec, A php toolset to drive emergent
* design by specification.
*
* (c) Marcello Duarte <marcello.duarte@gmail.com>
* (c) Konstantin Kudryashov <ever.zet@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpSpec\Formatter\Presenter\Value;
final class BaseExceptionTypePresenter implements ExceptionTypePresenter
{
/**
* @param mixed $value
* @return bool
*/
public function supports($value)
{
return $value instanceof \Exception;
}
/**
* @param mixed $value
* @return string
*/
public function present($value)
{
return sprintf(
'[exc:%s("%s")]',
get_class($value),
$value->getMessage()
);
}
/**
* @return int
*/
public function getPriority()
{
return 60;
}
}

View File

@@ -0,0 +1,43 @@
<?php
/*
* This file is part of PhpSpec, A php toolset to drive emergent
* design by specification.
*
* (c) Marcello Duarte <marcello.duarte@gmail.com>
* (c) Konstantin Kudryashov <ever.zet@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpSpec\Formatter\Presenter\Value;
final class BooleanTypePresenter implements TypePresenter
{
/**
* @param mixed $value
* @return bool
*/
public function supports($value)
{
return 'boolean' === strtolower(gettype($value));
}
/**
* @param mixed $value
* @return string
*/
public function present($value)
{
return $value ? 'true' : 'false';
}
/**
* @return int
*/
public function getPriority()
{
return 40;
}
}

View File

@@ -0,0 +1,71 @@
<?php
/*
* This file is part of PhpSpec, A php toolset to drive emergent
* design by specification.
*
* (c) Marcello Duarte <marcello.duarte@gmail.com>
* (c) Konstantin Kudryashov <ever.zet@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpSpec\Formatter\Presenter\Value;
use PhpSpec\Formatter\Presenter\Presenter;
final class CallableTypePresenter implements TypePresenter
{
/**
* @var Presenter
*/
private $presenter;
/**
* @param Presenter $presenter
*/
public function __construct(Presenter $presenter)
{
$this->presenter = $presenter;
}
/**
* @param mixed $value
* @return bool
*/
public function supports($value)
{
return is_callable($value);
}
/**
* @param mixed $value
* @return string
*/
public function present($value)
{
if (is_array($value)) {
$type = is_object($value[0]) ? $this->presenter->presentValue($value[0]) : $value[0];
return sprintf('%s::%s()', $type, $value[1]);
}
if ($value instanceof \Closure) {
return '[closure]';
}
if (is_object($value)) {
return sprintf('[obj:%s]', get_class($value));
}
return sprintf('[%s()]', $value);
}
/**
* @return int
*/
public function getPriority()
{
return 70;
}
}

View File

@@ -0,0 +1,49 @@
<?php
/*
* This file is part of PhpSpec, A php toolset to drive emergent
* design by specification.
*
* (c) Marcello Duarte <marcello.duarte@gmail.com>
* (c) Konstantin Kudryashov <ever.zet@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpSpec\Formatter\Presenter\Value;
final class ComposedValuePresenter implements ValuePresenter
{
/**
* @var TypePresenter[]
*/
private $typePresenters = array();
/**
* @param TypePresenter $typePresenter
*/
public function addTypePresenter(TypePresenter $typePresenter)
{
$this->typePresenters[] = $typePresenter;
@usort($this->typePresenters, function ($presenter1, $presenter2) {
return $presenter2->getPriority() - $presenter1->getPriority();
});
}
/**
* @param mixed $value
* @return string
*/
public function presentValue($value)
{
foreach ($this->typePresenters as $typePresenter) {
if ($typePresenter->supports($value)) {
return $typePresenter->present($value);
}
}
return sprintf('[%s:%s]', strtolower(gettype($value)), $value);
}
}

View File

@@ -0,0 +1,18 @@
<?php
/*
* This file is part of PhpSpec, A php toolset to drive emergent
* design by specification.
*
* (c) Marcello Duarte <marcello.duarte@gmail.com>
* (c) Konstantin Kudryashov <ever.zet@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpSpec\Formatter\Presenter\Value;
interface ExceptionTypePresenter extends TypePresenter
{
}

View File

@@ -0,0 +1,43 @@
<?php
/*
* This file is part of PhpSpec, A php toolset to drive emergent
* design by specification.
*
* (c) Marcello Duarte <marcello.duarte@gmail.com>
* (c) Konstantin Kudryashov <ever.zet@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpSpec\Formatter\Presenter\Value;
final class NullTypePresenter implements TypePresenter
{
/**
* @param mixed $value
* @return bool
*/
public function supports($value)
{
return null === $value;
}
/**
* @param mixed $value
* @return string
*/
public function present($value)
{
return 'null';
}
/**
* @return int
*/
public function getPriority()
{
return 50;
}
}

View File

@@ -0,0 +1,43 @@
<?php
/*
* This file is part of PhpSpec, A php toolset to drive emergent
* design by specification.
*
* (c) Marcello Duarte <marcello.duarte@gmail.com>
* (c) Konstantin Kudryashov <ever.zet@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PhpSpec\Formatter\Presenter\Value;
final class ObjectTypePresenter implements TypePresenter
{
/**
* @param mixed $value
* @return bool
*/
public function supports($value)
{
return 'object' === strtolower(gettype($value));
}
/**
* @param mixed $value
* @return string
*/
public function present($value)
{
return sprintf('[obj:%s]', get_class($value));
}
/**
* @return int
*/
public function getPriority()
{
return 30;
}
}

Some files were not shown because too many files have changed in this diff Show More