Skip to content
This repository has been archived by the owner on Mar 24, 2020. It is now read-only.

Commit

Permalink
implemented working proof of concept
Browse files Browse the repository at this point in the history
  • Loading branch information
everzet committed Jul 7, 2012
1 parent 5dd266e commit e1a0bb2
Show file tree
Hide file tree
Showing 15 changed files with 387 additions and 11 deletions.
4 changes: 4 additions & 0 deletions Guardfile
@@ -0,0 +1,4 @@
guard 'ctags-composer', :src_path => ["src"], :vendor_path => ["vendor"] do
watch(/^(src|tests)\/.*\.php$/)
watch('composer.lock')
end
18 changes: 7 additions & 11 deletions composer.json
Expand Up @@ -18,21 +18,17 @@
],

"require": {
"php": ">=5.3.1",
"symfony/console": ">=2.0.0,<2.2.0-dev",
"symfony/event-dispatcher": ">=2.0.0,<2.2.0-dev",
"symfony/finder": ">=2.0.0,<2.2.0-dev"
},

"require-dev": {
"symfony/console": "@stable",
"symfony/event-dispatcher": "@stable",
"symfony/finder": "@stable"
"php": ">=5.3.1",
"symfony/console": ">=2.0.0,<2.2.0",
"symfony/event-dispatcher": ">=2.0.0,<2.2.0",
"symfony/finder": ">=2.0.0,<2.2.0",
"mockery/mockery": "0.7.*"
},

"autoload": {
"psr-0": {
"PHPSpec\\PHPSpec2": "src/"
"PHPSpec\\PHPSpec2": "src/",
"Spec": "specs/"
}
},

Expand Down
31 changes: 31 additions & 0 deletions specs/Spec/PHPSpec/PHPSpec2/Stub.php
@@ -0,0 +1,31 @@
<?php

namespace Spec\PHPSpec\PHPSpec2;

use PHPSpec\PHPSpec2\SpecificationInterface;

class Stub implements SpecificationInterface
{
public function described_with($stub)
{
$stub->isAnInstanceOf('PHPSpec\PHPSpec2\Stub');
}

public function registers_matcher_if_it_has_aliases($stub, $matcher)
{
$matcher->isAMockOf('PHPSpec\PHPSpec2\Matcher\MatcherInterface');
$matcher->getAliases()->should_return(array('should_be_equal'));

$stub->__registerMatcher($matcher);
$stub->__getMatchers()->should_contain(1);
}

public function does_not_registers_matcher_if_it_has_no_aliases($stub, $matcher)
{
$matcher->isAMockOf('PHPSpec\PHPSpec2\Matcher\MatcherInterface');
$matcher->getAliases()->should_return(array());

$stub->__registerMatcher($matcher);
$stub->__getMatchers()->should_contain(0);
}
}
97 changes: 97 additions & 0 deletions src/PHPSpec/PHPSpec2/Console/Command/TestCommand.php
Expand Up @@ -6,6 +6,16 @@
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Input\InputInterface;

use Symfony\Component\Finder\Finder;
use Symfony\Component\Finder\SplFileInfo;

use ReflectionClass;
use ReflectionMethod;

use PHPSpec\PHPSpec2\Stub;
use PHPSpec\PHPSpec2\Matcher;
use PHPSpec\PHPSpec2\SpecificationInterface;

class TestCommand extends Command
{
/**
Expand All @@ -23,5 +33,92 @@ public function __construct()
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
$specsPath = realpath('specs');

$finder = Finder::create();
$files = $finder
->files()
->name('*.php')
->in($specsPath)
;

foreach ($files as $file) {
if (!$spec = $this->getSpecReflectionFromFile($file, $specsPath)) {
continue;
}

foreach ($spec->getMethods(ReflectionMethod::IS_PUBLIC) as $specMethod) {
$specInstance = $spec->newInstance();
$stubs = array();
foreach (array('describedWith', 'described_with') as $describer) {
// describing methods are not specs
if ($describer === $specMethod->getName()) {
continue 2;
}

// call describing method
if ($spec->hasMethod($describer)) {
$method = $spec->getMethod($describer);
$stubs = $this->getStubsForMethod($method, $stubs);
$this->callMethodWithStubs($specInstance, $method, $stubs);
}
}

$stubs = $this->getStubsForMethod($specMethod, $stubs);
$this->callMethodWithStubs($specInstance, $specMethod, $stubs);
}
}
}

private function createNewStub($subject = null)
{
$stub = new Stub($subject);
$stub->registerMatcher(new Matcher\ShouldReturnMatcher);
$stub->registerMatcher(new Matcher\ShouldContainMatcher);

return $stub;
}

private function getStubsForMethod(ReflectionMethod $method, array $stubs)
{
foreach ($method->getParameters() as $parameter) {
if (!isset($stubs[$parameter->getName()])) {
$stubs[$parameter->getName()] = $this->createNewStub();
}
}

return $stubs;
}

private function callMethodWithStubs(SpecificationInterface $spec, ReflectionMethod $method, array $stubs)
{
$arguments = array();
foreach ($method->getParameters() as $parameter) {
$arguments[] = $stubs[$parameter->getName()];
}

$method->invokeArgs($spec, $arguments);
}

private function getSpecReflectionFromFile(SplFileInfo $file, $specsPath)
{
$filename = realpath($file->getPathname());
$classname = str_replace(DIRECTORY_SEPARATOR, '\\',
str_replace(
$specsPath.DIRECTORY_SEPARATOR, '',
str_replace('.'.$file->getExtension(), '', $filename)
)
);

if (!class_exists($classname)) {
return;
}

$reflection = new ReflectionClass($classname);
if (!$reflection->implementsInterface('PHPSpec\\PHPSpec2\\SpecificationInterface')) {
return;
}

return $reflection;
}
}
@@ -0,0 +1,7 @@
<?php

namespace PHPSpec\PHPSpec2\Exception;

class ClassDoesNotExistsException extends StubException
{
}
7 changes: 7 additions & 0 deletions src/PHPSpec/PHPSpec2/Exception/Exception.php
@@ -0,0 +1,7 @@
<?php

namespace PHPSpec\PHPSpec2\Exception;

class Exception extends \Exception
{
}
9 changes: 9 additions & 0 deletions src/PHPSpec/PHPSpec2/Exception/Matcher/MatcherException.php
@@ -0,0 +1,9 @@
<?php

namespace PHPSpec\PHPSpec2\Exception\Matcher;

use PHPSpec\PHPSpec2\Exception\Exception;

class MatcherException extends Exception
{
}
7 changes: 7 additions & 0 deletions src/PHPSpec/PHPSpec2/Exception/MethodNotFoundException.php
@@ -0,0 +1,7 @@
<?php

namespace PHPSpec\PHPSpec2\Exception;

class MethodNotFoundException extends StubException
{
}
7 changes: 7 additions & 0 deletions src/PHPSpec/PHPSpec2/Exception/PropertyNotFoundException.php
@@ -0,0 +1,7 @@
<?php

namespace PHPSpec\PHPSpec2\Exception;

class PropertyNotFoundException extends StubException
{
}
7 changes: 7 additions & 0 deletions src/PHPSpec/PHPSpec2/Exception/StubException.php
@@ -0,0 +1,7 @@
<?php

namespace PHPSpec\PHPSpec2\Exception;

class StubException extends Exception
{
}
11 changes: 11 additions & 0 deletions src/PHPSpec/PHPSpec2/Matcher/MatcherInterface.php
@@ -0,0 +1,11 @@
<?php

namespace PHPSpec\PHPSpec2\Matcher;

use PHPSpec\PHPSpec2\Stub;

interface MatcherInterface
{
public function getAliases();
public function match(Stub $stub, array $arguments);
}
26 changes: 26 additions & 0 deletions src/PHPSpec/PHPSpec2/Matcher/ShouldContainMatcher.php
@@ -0,0 +1,26 @@
<?php

namespace PHPSpec\PHPSpec2\Matcher;

use PHPSpec\PHPSpec2\Stub;
use PHPSpec\PHPSpec2\Exception\Matcher\MatcherException;

class ShouldContainMatcher implements MatcherInterface
{
public function getAliases()
{
return array('should_contain', 'shouldContain', 'contains');
}

public function match(Stub $stub, array $arguments)
{
if ($arguments[0] !== count($stub->getSubject())) {
throw new MatcherException(sprintf(
'Expected to have %d items in %s, got %d',
$arguments[0],
gettype($stub->getSubject()),
count($stub->getSubject())
));
}
}
}
21 changes: 21 additions & 0 deletions src/PHPSpec/PHPSpec2/Matcher/ShouldReturnMatcher.php
@@ -0,0 +1,21 @@
<?php

namespace PHPSpec\PHPSpec2\Matcher;

use PHPSpec\PHPSpec2\Stub;

class ShouldReturnMatcher implements MatcherInterface
{
public function getAliases()
{
return array('should_return', 'shouldReturn', 'returns');
}

public function match(Stub $stub, array $arguments)
{
return new Stub(
$stub->getSubject()->andReturn($arguments[0]),
$stub->getMatchers()
);
}
}
7 changes: 7 additions & 0 deletions src/PHPSpec/PHPSpec2/SpecificationInterface.php
@@ -0,0 +1,7 @@
<?php

namespace PHPSpec\PHPSpec2;

interface SpecificationInterface
{
}

0 comments on commit e1a0bb2

Please sign in to comment.