Skip to content

Commit

Permalink
feature PHP-CS-Fixer#1384 Add intergration tests (SpacePossum)
Browse files Browse the repository at this point in the history
This PR was squashed before being merged into the 1.11 branch (closes PHP-CS-Fixer#1384).

Discussion
----------

Add intergration tests

A 1.11 version of;
PHP-CS-Fixer#1375
Biggest diff is the caused by the diff. ways the errormanager is connected to the fixer in the branches.

Commits
-------

b0db254 Add intergration tests
  • Loading branch information
keradus committed Sep 10, 2015
2 parents 850336e + b0db254 commit 9e0a1e0
Show file tree
Hide file tree
Showing 5 changed files with 443 additions and 0 deletions.
3 changes: 3 additions & 0 deletions Symfony/CS/Fixer.php
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,9 @@ public function addFixer(FixerInterface $fixer)
$this->fixers[] = $fixer;
}

/**
* @return FixerInterface[]
*/
public function getFixers()
{
$this->sortFixers();
Expand Down
273 changes: 273 additions & 0 deletions Symfony/CS/Tests/AbstractIntegrationTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,273 @@
<?php

/*
* This file is part of the PHP CS utility.
*
* (c) Fabien Potencier <[email protected]>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/

namespace Symfony\CS\Tests;

use Symfony\Component\Filesystem\Exception\IOException;
use Symfony\Component\Filesystem\Filesystem;
use Symfony\CS\ErrorsManager;
use Symfony\CS\FileCacheManager;
use Symfony\CS\Fixer;
use Symfony\CS\FixerInterface;

/**
* Integration test base class.
*
* This test searches for '.test' fixture files in the given directory.
* Each fixture file will be parsed and tested against the expected result.
*
* Fixture files have the following format:
*
* --TEST--
* Example test description
* --CONFIG--
* level=symfony|none|psr0|psr1|psr2|symfony
* fixers=fixer1,fixer2,...*
* --fixers=fixer3,fixer4,...**
* --INPUT--
* Code to fix
* --EXPECT--
* Expected code after fixing***
*
* * Additional fixers may be omitted.
* ** Black listed filters may be omitted.
* *** When the expected block is omitted the input is expected not to
* be changed by the fixers.
*
* @author SpacePossum <[email protected]>
*
* @internal
*/
abstract class AbstractIntegrationTest extends \PHPUnit_Framework_TestCase
{
private static $builtInFixers;

public static function setUpBeforeClass()
{
$tmpFile = static::getTempFile();
if (!is_file($tmpFile)) {
$dir = dirname($tmpFile);
if (!is_dir($dir)) {
$fs = new Filesystem();
$fs->mkdir($dir, 0766);
}
}
}

public static function tearDownAfterClass()
{
unlink(static::getTempFile());
}

/**
* @dataProvider getTests
*
* @see doTestIntegration()
*/
public function testIntegration($testFileName, $testTitle, $fixers, $input, $expected = null)
{
$this->doTestIntegration($testFileName, $testTitle, $fixers, $input, $expected);
}

/**
* Creates test data by parsing '.test' files.
*
* @return array
*/
public function getTests()
{
$fixturesDir = realpath($this->getFixturesDir());
if (!is_dir($fixturesDir)) {
throw new \UnexpectedValueException(sprintf('Given fixture dir "%s" is not a directory.', $fixturesDir));
}

$tests = array();

foreach (new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($fixturesDir), \RecursiveIteratorIterator::LEAVES_ONLY) as $file) {
if (!preg_match('/\.test$/', $file)) {
continue;
}

$test = file_get_contents($file->getRealpath());
$fileName = $file->getFileName();
if (!preg_match('/--TEST--[\n](.*?)\s--CONFIG--[\n](.*?)\s--INPUT--[\n](.*?[\n]*)(?:[\n]--EXPECT--\s(.*)|$)/s', $test, $match)) {
throw new \InvalidArgumentException(sprintf('Test format invalid for "%s".', $fileName));
}

$tests[] = array($fileName, $match[1], $this->getFixersFromConfig($fileName, $match[2]), $match[3], isset($match[4]) ? $match[4] : null);
}

return $tests;
}

/**
* Returns the full path to directory which contains the tests.
*
* @return string
*/
protected static function getFixturesDir()
{
throw new \BadMethodCallException('Method "getFixturesDir" must be overridden by the extending class.');
}

/**
* Returns the full path to the temporary file where the test will write to.
*
* @return string
*/
protected static function getTempFile()
{
throw new \BadMethodCallException('Method "getTempFile" must be overridden by the extending class.');
}

/**
* Applies the given fixers on the input and checks the result.
*
* It will write the input to a temp file. The file will be fixed by a Fixer instance
* configured with the given fixers. The result is compared with the expected output.
* It checks if no errors were reported during the fixing.
*
* @param string $testFileName Filename
* @param string $testTitle Test title
* @param FixerInterface[] $fixers Fixers to use
* @param string $input Code to fix
* @param string|null $expected Expected result or null if the input is expected not to change
*/
protected function doTestIntegration($testFileName, $testTitle, $fixers, $input, $expected = null)
{
$errorsManager = new ErrorsManager();
$fixer = new Fixer();
$fixer->setErrorsManager($errorsManager);

$tmpFile = static::getTempFile();
if (false === @file_put_contents($tmpFile, $input)) {
throw new IOException(sprintf('Failed to write to tmp. file "%s".', $tmpFile));
}

$changed = $fixer->fixFile(new \SplFileInfo($tmpFile), $fixers, false, true, new FileCacheManager(false, null, $fixers));
$this->assertTrue($errorsManager->isEmpty(), 'Errors reported during fixing.');

if (null === $expected) {
$this->assertEmpty($changed, sprintf("Expected no changes made to test \"%s\" in \"%s\".\nFixers applied:\n\"%s\".\nDiff.:\n\"%s\".", $testTitle, $testFileName, $changed === null ? '[None]' : implode(',', $changed['appliedFixers']), $changed === null ? '[None]' : $changed['diff']));

return;
}

$this->assertNotEmpty($changed, sprintf('Expected changes made to test "%s" in "%s".', $testTitle, $testFileName));
$this->assertSame($expected, file_get_contents($tmpFile), sprintf('Expected changes do not match result, for "%s" in "%s".', $testTitle, $testFileName));

// run the test again with the `expected` part, this should always stay the same
$this->testIntegration($testFileName, $testTitle.' "--EXPECT-- part run"', $fixers, $expected);
}

/**
* Parses the '--CONFIG--' block of a '.test' file and determines what fixers should be used.
*
* @param string $fileName
* @param string $config
*
* @return FixerInterface[]
*/
protected function getFixersFromConfig($fileName, $config)
{
static $levelMap = array(
'none' => FixerInterface::NONE_LEVEL,
'psr1' => FixerInterface::PSR1_LEVEL,
'psr2' => FixerInterface::PSR2_LEVEL,
'symfony' => FixerInterface::SYMFONY_LEVEL,
);

$lines = explode("\n", $config);
if (count($lines) < 1) {
throw new \InvalidArgumentException(sprintf('No configuration options found in "%s".', $fileName));
}

$config = array('level' => null, 'fixers' => array(), '--fixers' => array());

foreach ($lines as $line) {
$labelValuePair = explode('=', $line);
if (2 !== count($labelValuePair)) {
throw new \InvalidArgumentException(sprintf('Invalid configuration line "%s" in "%s".', $line, $fileName));
}

$label = strtolower(trim($labelValuePair[0]));
$value = trim($labelValuePair[1]);

switch ($label) {
case 'level' :
if (!array_key_exists($value, $levelMap)) {
throw new \InvalidArgumentException(sprintf('Unknown level "%s" set in configuration in "%s", expected any of "%s".', $value, $fileName, implode(', ', array_keys($levelMap))));
}

if (null !== $config['level']) {
throw new \InvalidArgumentException(sprintf('Cannot use multiple levels in configuration in "%s".', $fileName));
}

$config['level'] = $value;
break;
case 'fixers' :
case '--fixers' :
foreach (explode(',', $value) as $fixer) {
$config[$label][] = strtolower(trim($fixer));
}

break;
default :
throw new \InvalidArgumentException(sprintf('Unknown configuration item "%s" in "%s".', $label, $fileName));
}
}

if (null === $config['level']) {
throw new \InvalidArgumentException(sprintf('Level not set in configuration "%s".', $fileName));
}

if (null === self::$builtInFixers) {
$fixer = new Fixer();
$fixer->registerBuiltInFixers();
self::$builtInFixers = $fixer->getFixers();
}

$fixers = array();
for ($i = count(self::$builtInFixers) - 1; $i >= 0; --$i) {
$fixer = self::$builtInFixers[$i];
$fixerName = $fixer->getName();
if ('psr0' === $fixer->getName()) {
// File based fixer won't work
continue;
}

if ($fixer->getLevel() !== ($fixer->getLevel() & $levelMap[$config['level']])) {
if (false !== $key = array_search($fixerName, $config['fixers'], true)) {
$fixers[] = $fixer;
unset($config['fixers'][$key]);
}
continue;
}

if (false !== $key = array_search($fixerName, $config['--fixers'], true)) {
unset($config['--fixers'][$key]);
continue;
}

if (in_array($fixerName, $config['fixers'], true)) {
throw new \InvalidArgumentException(sprintf('Additional fixer "%s" configured, but is already part of the level.', $fixerName));
}

$fixers[] = $fixer;
}

if (!empty($config['fixers']) || !empty($config['--fixers'])) {
throw new \InvalidArgumentException(sprintf('Unknown fixers in configuration "%s".', implode(',', empty($config['fixers']) ? $config['--fixers'] : $config['fixers'])));
}

return $fixers;
}
}
52 changes: 52 additions & 0 deletions Symfony/CS/Tests/Fixtures/Integration/psr2.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
--TEST--
PSR2 integration
--CONFIG--
level=psr2
--INPUT--
<?php
namespace Vendor\Package;
use FooInterface;
use BarClass as Bar;
use OtherVendor\OtherPackage\BazClass;
class Foo extends Bar implements FooInterface{
public function sampleFunction($a, $b = null)
{
if ($a === $b) {
bar();
} elseif ($a > $b) {
$foo->bar($arg1);
} else {
BazClass::bar($arg2, $arg3);
}
}

static public final function bar() {
// method body
}
}
--EXPECT--
<?php
namespace Vendor\Package;

use FooInterface;
use BarClass as Bar;
use OtherVendor\OtherPackage\BazClass;

class Foo extends Bar implements FooInterface
{
public function sampleFunction($a, $b = null)
{
if ($a === $b) {
bar();
} elseif ($a > $b) {
$foo->bar($arg1);
} else {
BazClass::bar($arg2, $arg3);
}
}

final public static function bar()
{
// method body
}
}
Loading

0 comments on commit 9e0a1e0

Please sign in to comment.