Skip to content

Commit

Permalink
Merge pull request #558 from Landerstraeten/phpunit-bridge
Browse files Browse the repository at this point in the history
Add phpunit bridge
  • Loading branch information
Landerstraeten committed Nov 30, 2018
2 parents d2d188b + 2dd294c commit 73712a0
Show file tree
Hide file tree
Showing 7 changed files with 276 additions and 0 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ parameters:
phpspec: ~
phpstan: ~
phpunit: ~
phpunitbridge: ~
phpversion: ~
progpilot: ~
psalm: ~
Expand Down
1 change: 1 addition & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
"sensiolabs/security-checker": "Lets GrumPHP be sure that there are no known security issues.",
"squizlabs/php_codesniffer": "Lets GrumPHP sniff on your code.",
"sstalle/php7cc": "Lets GrumPHP check PHP 5.3 - 5.6 code compatibility with PHP 7.",
"symfony/phpunit-bridge": "Lets GrumPHP run your unit tests with the phpunit-bridge of Symfony.",
"vimeo/psalm": "Lets GrumPHP discover errors in your code without running it."
},
"authors": [
Expand Down
2 changes: 2 additions & 0 deletions doc/tasks.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ parameters:
phpspec: ~
phpstan: ~
phpunit: ~
phpunitbridge: ~
phpversion: ~
progpilot: ~
psalm: ~
Expand Down Expand Up @@ -95,6 +96,7 @@ Every task has it's own default configuration. It is possible to overwrite the p
- [Phpspec](tasks/phpspec.md)
- [PHPStan](tasks/phpstan.md)
- [Phpunit](tasks/phpunit.md)
- [Phpunit bridge](tasks/phpunitbridge.md)
- [PhpVersion](tasks/phpversion.md)
- [Progpilot](tasks/progpilot.md)
- [Psalm](tasks/psalm.md)
Expand Down
56 changes: 56 additions & 0 deletions doc/tasks/phpunitbridge.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Phpunit bridge

The Phpunit Bridge task will run your unit tests thanks to the Symfony Phpunit Bridge.

***Composer***

```
composer require --dev symfony/phpunit-bridge
```

***Config***

The task lives under the `phpunitbridge` namespace and has following configurable parameters:

```yaml
# grumphp.yml
parameters:
tasks:
phpunitbridge:
config_file: ~
testsuite: ~
group: []
always_execute: false
```
**config_file**
*Default: null*
If your phpunit.xml file is located at an exotic location, you can specify your custom config file location with this option.
This option is set to `null` by default.
This means that `phpunit.xml` or `phpunit.xml.dist` are automatically loaded if one of them exist in the current directory.


**testsuite**

*Default: null*

If you wish to only run tests from a certain Suite.
`testsuite: unit`


**group**

*Default: array()*

If you wish to only run tests from a certain Group.
`group: [fast,quick,small]`


**always_execute**

*Default: false*

Always run the whole test suite, even if no PHP files were changed.

9 changes: 9 additions & 0 deletions resources/config/tasks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,15 @@ services:
tags:
- {name: grumphp.task, config: phpunit}

task.phpunitbridge:
class: GrumPHP\Task\PhpunitBridge
arguments:
- '@config'
- '@process_builder'
- '@formatter.raw_process'
tags:
- {name: grumphp.task, config: phpunitbridge}

task.phpversion:
class: GrumPHP\Task\PhpVersion
arguments:
Expand Down
126 changes: 126 additions & 0 deletions spec/Task/PhpunitBridgeSpec.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
<?php

namespace spec\GrumPHP\Task;

use GrumPHP\Collection\FilesCollection;
use GrumPHP\Collection\ProcessArgumentsCollection;
use GrumPHP\Configuration\GrumPHP;
use GrumPHP\Formatter\ProcessFormatterInterface;
use GrumPHP\Process\ProcessBuilder;
use GrumPHP\Runner\TaskResult;
use GrumPHP\Runner\TaskResultInterface;
use GrumPHP\Task\Context\ContextInterface;
use GrumPHP\Task\Context\GitPreCommitContext;
use GrumPHP\Task\Context\RunContext;
use GrumPHP\Task\Phpunit;
use GrumPHP\Task\PhpunitBridge;
use PhpSpec\ObjectBehavior;
use Symfony\Component\Finder\SplFileInfo;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Process\Process;

class PhpunitBridgeSpec extends ObjectBehavior
{
function let(GrumPHP $grumPHP, ProcessBuilder $processBuilder, ProcessFormatterInterface $formatter)
{
$grumPHP->getTaskConfiguration('phpunitbridge')->willReturn([]);
$this->beConstructedWith($grumPHP, $processBuilder, $formatter);
}

function it_is_initializable()
{
$this->shouldHaveType(PhpunitBridge::class);
}

function it_should_have_a_name()
{
$this->getName()->shouldBe('phpunitbridge');
}

function it_should_have_configurable_options()
{
$options = $this->getConfigurableOptions();
$options->shouldBeAnInstanceOf(OptionsResolver::class);
$options->getDefinedOptions()->shouldContain('config_file');
$options->getDefinedOptions()->shouldContain('testsuite');
$options->getDefinedOptions()->shouldContain('group');
$options->getDefinedOptions()->shouldContain('always_execute');
}

function it_should_run_in_git_pre_commit_context(GitPreCommitContext $context)
{
$this->canRunInContext($context)->shouldReturn(true);
}

function it_should_run_in_run_context(RunContext $context)
{
$this->canRunInContext($context)->shouldReturn(true);
}

function it_does_not_do_anything_if_there_are_no_files(ProcessBuilder $processBuilder, ContextInterface $context)
{
$processBuilder->buildProcess('phpunitbridge')->shouldNotBeCalled();
$processBuilder->buildProcess()->shouldNotBeCalled();
$context->getFiles()->willReturn(new FilesCollection());

$result = $this->run($context);
$result->shouldBeAnInstanceOf(TaskResultInterface::class);
$result->getResultCode()->shouldBe(TaskResult::SKIPPED);
}

function it_runs_if_there_are_no_files_but_always_execute_is_passed(GrumPHP $grumPHP, Process $process, ProcessBuilder $processBuilder, ContextInterface $context)
{
$grumPHP->getTaskConfiguration('phpunitbridge')->willReturn([
'always_execute' => true,
]);

$arguments = new ProcessArgumentsCollection();
$processBuilder->createArgumentsForCommand('simple-phpunit')->willReturn($arguments);
$processBuilder->buildProcess($arguments)->willReturn($process);

$process->run()->shouldBeCalled();
$process->isSuccessful()->willReturn(true);

$context->getFiles()->willReturn(new FilesCollection());

$result = $this->run($context);
$result->shouldBeAnInstanceOf(TaskResultInterface::class);
$result->isPassed()->shouldBe(true);
}

function it_runs_the_suite(ProcessBuilder $processBuilder, Process $process, ContextInterface $context)
{
$arguments = new ProcessArgumentsCollection();
$processBuilder->createArgumentsForCommand('simple-phpunit')->willReturn($arguments);
$processBuilder->buildProcess($arguments)->willReturn($process);

$process->run()->shouldBeCalled();
$process->isSuccessful()->willReturn(true);

$context->getFiles()->willReturn(new FilesCollection([
new SplFileInfo('test.php', '.', 'test.php')
]));

$result = $this->run($context);
$result->shouldBeAnInstanceOf(TaskResultInterface::class);
$result->isPassed()->shouldBe(true);
}

function it_throws_exception_if_the_process_fails(ProcessBuilder $processBuilder, Process $process, ContextInterface $context)
{
$arguments = new ProcessArgumentsCollection();
$processBuilder->createArgumentsForCommand('simple-phpunit')->willReturn($arguments);
$processBuilder->buildProcess($arguments)->willReturn($process);

$process->run()->shouldBeCalled();
$process->isSuccessful()->willReturn(false);

$context->getFiles()->willReturn(new FilesCollection([
new SplFileInfo('test.php', '.', 'test.php')
]));

$result = $this->run($context);
$result->shouldBeAnInstanceOf(TaskResultInterface::class);
$result->isPassed()->shouldBe(false);
}
}
81 changes: 81 additions & 0 deletions src/Task/PhpunitBridge.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
<?php

namespace GrumPHP\Task;

use GrumPHP\Runner\TaskResult;
use GrumPHP\Task\Context\ContextInterface;
use GrumPHP\Task\Context\GitPreCommitContext;
use GrumPHP\Task\Context\RunContext;
use Symfony\Component\OptionsResolver\OptionsResolver;

/**
* PhpunitBridge task
*
* @link https://symfony.com/doc/current/components/phpunit_bridge.html
*/
class PhpunitBridge extends AbstractExternalTask
{
/**
* @return string
*/
public function getName()
{
return 'phpunitbridge';
}

/**
* @return OptionsResolver
*/
public function getConfigurableOptions()
{
$resolver = new OptionsResolver();
$resolver->setDefaults([
'config_file' => null,
'testsuite' => null,
'group' => [],
'always_execute' => false,
]);

$resolver->addAllowedTypes('config_file', ['null', 'string']);
$resolver->addAllowedTypes('testsuite', ['null', 'string']);
$resolver->addAllowedTypes('group', ['array']);
$resolver->addAllowedTypes('always_execute', ['bool']);

return $resolver;
}

/**
* {@inheritdoc}
*/
public function canRunInContext(ContextInterface $context)
{
return ($context instanceof GitPreCommitContext || $context instanceof RunContext);
}

/**
* {@inheritdoc}
*/
public function run(ContextInterface $context)
{
$config = $this->getConfiguration();

$files = $context->getFiles()->name('*.php');
if (0 === count($files) && !$config['always_execute']) {
return TaskResult::createSkipped($this, $context);
}

$arguments = $this->processBuilder->createArgumentsForCommand('simple-phpunit');
$arguments->addOptionalArgument('--configuration=%s', $config['config_file']);
$arguments->addOptionalArgument('--testsuite=%s', $config['testsuite']);
$arguments->addOptionalCommaSeparatedArgument('--group=%s', $config['group']);

$process = $this->processBuilder->buildProcess($arguments);
$process->run();

if (!$process->isSuccessful()) {
return TaskResult::createFailed($this, $context, $this->formatter->format($process));
}

return TaskResult::createPassed($this, $context);
}
}

0 comments on commit 73712a0

Please sign in to comment.