Inject ForwardCompatibiliy in TestCase

This commit is contained in:
Jérémy Derussé 2019-08-02 12:51:37 +02:00
parent f1801a4f8d
commit 016bd8dd91
No known key found for this signature in database
GPG Key ID: 2083FA5758C473D2
8 changed files with 222 additions and 265 deletions

View File

@ -1,50 +0,0 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bridge\PhpUnit\Legacy;
use PHPUnit\Framework\MockObject\MockObject;
/**
* @internal
*/
trait ForwardCompatTestTraitForV7
{
use ForwardCompatTestTraitForV5;
/**
* @param string|string[] $originalClassName
*/
protected function createMock($originalClassName): MockObject
{
return $this->getMockBuilder($originalClassName)
->disableOriginalConstructor()
->disableOriginalClone()
->disableArgumentCloning()
->disallowMockingUnknownTypes()
->getMock();
}
/**
* @param string|string[] $originalClassName
* @param string[] $methods
*/
protected function createPartialMock($originalClassName, array $methods): MockObject
{
return $this->getMockBuilder($originalClassName)
->disableOriginalConstructor()
->disableOriginalClone()
->disableArgumentCloning()
->disallowMockingUnknownTypes()
->setMethods(empty($methods) ? null : $methods)
->getMock();
}
}

View File

@ -15,106 +15,12 @@ use PHPUnit\Framework\Constraint\IsEqual;
use PHPUnit\Framework\Constraint\LogicalNot; use PHPUnit\Framework\Constraint\LogicalNot;
use PHPUnit\Framework\Constraint\StringContains; use PHPUnit\Framework\Constraint\StringContains;
use PHPUnit\Framework\Constraint\TraversableContains; use PHPUnit\Framework\Constraint\TraversableContains;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
/** /**
* @internal * @internal
*/ */
trait ForwardCompatTestTraitForV5 trait PolyfillAssertTrait
{ {
/**
* @return void
*/
public static function setUpBeforeClass()
{
self::doSetUpBeforeClass();
}
/**
* @return void
*/
public static function tearDownAfterClass()
{
self::doTearDownAfterClass();
}
/**
* @return void
*/
protected function setUp()
{
self::doSetUp();
}
/**
* @return void
*/
protected function tearDown()
{
self::doTearDown();
}
private static function doSetUpBeforeClass()
{
parent::setUpBeforeClass();
}
private static function doTearDownAfterClass()
{
parent::tearDownAfterClass();
}
private function doSetUp()
{
parent::setUp();
}
private function doTearDown()
{
parent::tearDown();
}
/**
* @param string|string[] $originalClassName
*
* @return MockObject
*/
protected function createMock($originalClassName)
{
$mock = $this->getMockBuilder($originalClassName)
->disableOriginalConstructor()
->disableOriginalClone()
->disableArgumentCloning();
if (method_exists($mock, 'disallowMockingUnknownTypes')) {
$mock = $mock->disallowMockingUnknownTypes();
}
return $mock->getMock();
}
/**
* @param string|string[] $originalClassName
* @param string[] $methods
*
* @return MockObject
*/
protected function createPartialMock($originalClassName, array $methods)
{
$mock = $this->getMockBuilder($originalClassName)
->disableOriginalConstructor()
->disableOriginalClone()
->disableArgumentCloning()
->setMethods(empty($methods) ? null : $methods);
if (method_exists($mock, 'disallowMockingUnknownTypes')) {
$mock = $mock->disallowMockingUnknownTypes();
}
return $mock->getMock();
}
/** /**
* @param float $delta * @param float $delta
* @param string $message * @param string $message
@ -320,12 +226,6 @@ trait ForwardCompatTestTraitForV5
*/ */
public static function assertFinite($actual, $message = '') public static function assertFinite($actual, $message = '')
{ {
if (method_exists(TestCase::class, 'assertFinite')) {
parent::assertFinite($actual, $message);
return;
}
static::assertInternalType('float', $actual, $message); static::assertInternalType('float', $actual, $message);
static::assertTrue(is_finite($actual), $message ? $message : "Failed asserting that $actual is finite."); static::assertTrue(is_finite($actual), $message ? $message : "Failed asserting that $actual is finite.");
} }
@ -337,12 +237,6 @@ trait ForwardCompatTestTraitForV5
*/ */
public static function assertInfinite($actual, $message = '') public static function assertInfinite($actual, $message = '')
{ {
if (method_exists(TestCase::class, 'assertInfinite')) {
parent::assertInfinite($actual, $message);
return;
}
static::assertInternalType('float', $actual, $message); static::assertInternalType('float', $actual, $message);
static::assertTrue(is_infinite($actual), $message ? $message : "Failed asserting that $actual is infinite."); static::assertTrue(is_infinite($actual), $message ? $message : "Failed asserting that $actual is infinite.");
} }
@ -354,85 +248,7 @@ trait ForwardCompatTestTraitForV5
*/ */
public static function assertNan($actual, $message = '') public static function assertNan($actual, $message = '')
{ {
if (method_exists(TestCase::class, 'assertNan')) {
parent::assertNan($actual, $message);
return;
}
static::assertInternalType('float', $actual, $message); static::assertInternalType('float', $actual, $message);
static::assertTrue(is_nan($actual), $message ? $message : "Failed asserting that $actual is nan."); static::assertTrue(is_nan($actual), $message ? $message : "Failed asserting that $actual is nan.");
} }
/**
* @param string $exception
*
* @return void
*/
public function expectException($exception)
{
if (method_exists(TestCase::class, 'expectException')) {
parent::expectException($exception);
return;
}
$property = new \ReflectionProperty(class_exists('PHPUnit_Framework_TestCase') ? 'PHPUnit_Framework_TestCase' : TestCase::class, 'expectedException');
$property->setAccessible(true);
$property->setValue($this, $exception);
}
/**
* @param int|string $code
*
* @return void
*/
public function expectExceptionCode($code)
{
if (method_exists(TestCase::class, 'expectExceptionCode')) {
parent::expectExceptionCode($code);
return;
}
$property = new \ReflectionProperty(class_exists('PHPUnit_Framework_TestCase') ? 'PHPUnit_Framework_TestCase' : TestCase::class, 'expectedExceptionCode');
$property->setAccessible(true);
$property->setValue($this, $code);
}
/**
* @param string $message
*
* @return void
*/
public function expectExceptionMessage($message)
{
if (method_exists(TestCase::class, 'expectExceptionMessage')) {
parent::expectExceptionMessage($message);
return;
}
$property = new \ReflectionProperty(class_exists('PHPUnit_Framework_TestCase') ? 'PHPUnit_Framework_TestCase' : TestCase::class, 'expectedExceptionMessage');
$property->setAccessible(true);
$property->setValue($this, $message);
}
/**
* @param string $messageRegExp
*
* @return void
*/
public function expectExceptionMessageRegExp($messageRegExp)
{
if (method_exists(TestCase::class, 'expectExceptionMessageRegExp')) {
parent::expectExceptionMessageRegExp($messageRegExp);
return;
}
$property = new \ReflectionProperty(class_exists('PHPUnit_Framework_TestCase') ? 'PHPUnit_Framework_TestCase' : TestCase::class, 'expectedExceptionMessageRegExp');
$property->setAccessible(true);
$property->setValue($this, $messageRegExp);
}
} }

View File

@ -0,0 +1,109 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bridge\PhpUnit\Legacy;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
/**
* @internal
*/
trait PolyfillTestCaseTrait
{
/**
* @param string|string[] $originalClassName
*
* @return MockObject
*/
protected function createMock($originalClassName)
{
$mock = $this->getMockBuilder($originalClassName)
->disableOriginalConstructor()
->disableOriginalClone()
->disableArgumentCloning();
if (method_exists($mock, 'disallowMockingUnknownTypes')) {
$mock = $mock->disallowMockingUnknownTypes();
}
return $mock->getMock();
}
/**
* @param string|string[] $originalClassName
* @param string[] $methods
*
* @return MockObject
*/
protected function createPartialMock($originalClassName, array $methods)
{
$mock = $this->getMockBuilder($originalClassName)
->disableOriginalConstructor()
->disableOriginalClone()
->disableArgumentCloning()
->setMethods(empty($methods) ? null : $methods);
if (method_exists($mock, 'disallowMockingUnknownTypes')) {
$mock = $mock->disallowMockingUnknownTypes();
}
return $mock->getMock();
}
/**
* @param string $exception
*
* @return void
*/
public function expectException($exception)
{
$property = new \ReflectionProperty(class_exists('PHPUnit_Framework_TestCase') ? 'PHPUnit_Framework_TestCase' : TestCase::class, 'expectedException');
$property->setAccessible(true);
$property->setValue($this, $exception);
}
/**
* @param int|string $code
*
* @return void
*/
public function expectExceptionCode($code)
{
$property = new \ReflectionProperty(class_exists('PHPUnit_Framework_TestCase') ? 'PHPUnit_Framework_TestCase' : TestCase::class, 'expectedExceptionCode');
$property->setAccessible(true);
$property->setValue($this, $code);
}
/**
* @param string $message
*
* @return void
*/
public function expectExceptionMessage($message)
{
$property = new \ReflectionProperty(class_exists('PHPUnit_Framework_TestCase') ? 'PHPUnit_Framework_TestCase' : TestCase::class, 'expectedExceptionMessage');
$property->setAccessible(true);
$property->setValue($this, $message);
}
/**
* @param string $messageRegExp
*
* @return void
*/
public function expectExceptionMessageRegExp($messageRegExp)
{
$property = new \ReflectionProperty(class_exists('PHPUnit_Framework_TestCase') ? 'PHPUnit_Framework_TestCase' : TestCase::class, 'expectedExceptionMessageRegExp');
$property->setAccessible(true);
$property->setValue($this, $messageRegExp);
}
}

View File

@ -0,0 +1,70 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bridge\PhpUnit\Legacy;
/**
* @internal
*/
trait SetUpTearDownTraitForV5
{
/**
* @return void
*/
public static function setUpBeforeClass()
{
self::doSetUpBeforeClass();
}
/**
* @return void
*/
public static function tearDownAfterClass()
{
self::doTearDownAfterClass();
}
/**
* @return void
*/
protected function setUp()
{
self::doSetUp();
}
/**
* @return void
*/
protected function tearDown()
{
self::doTearDown();
}
private static function doSetUpBeforeClass()
{
parent::setUpBeforeClass();
}
private static function doTearDownAfterClass()
{
parent::tearDownAfterClass();
}
private function doSetUp()
{
parent::setUp();
}
private function doTearDown()
{
parent::tearDown();
}
}

View File

@ -14,7 +14,7 @@ namespace Symfony\Bridge\PhpUnit\Legacy;
/** /**
* @internal * @internal
*/ */
trait ForwardCompatTestTraitForV8 trait SetUpTearDownTraitForV8
{ {
public static function setUpBeforeClass(): void public static function setUpBeforeClass(): void
{ {

View File

@ -14,22 +14,15 @@ namespace Symfony\Bridge\PhpUnit;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
// A trait to provide forward compatibility with newest PHPUnit versions // A trait to provide forward compatibility with newest PHPUnit versions
$r = new \ReflectionClass(TestCase::class); $r = new \ReflectionClass(TestCase::class);
if (\PHP_VERSION_ID < 70000 || !$r->getMethod('setUp')->hasReturnType()) {
if (\PHP_VERSION_ID < 70000 || !$r->hasMethod('createMock') || !$r->getMethod('createMock')->hasReturnType()) { trait SetUpTearDownTrait
trait ForwardCompatTestTrait
{ {
use Legacy\ForwardCompatTestTraitForV5; use Legacy\SetUpTearDownTraitForV5;
}
} elseif ($r->getMethod('tearDown')->hasReturnType()) {
trait ForwardCompatTestTrait
{
use Legacy\ForwardCompatTestTraitForV8;
} }
} else { } else {
trait ForwardCompatTestTrait trait SetUpTearDownTrait
{ {
use Legacy\ForwardCompatTestTraitForV7; use Legacy\SetUpTearDownTraitForV8;
} }
} }

View File

@ -13,7 +13,6 @@ namespace Symfony\Bridge\PhpUnit\Tests;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
use Symfony\Bridge\PhpUnit\ClockMock; use Symfony\Bridge\PhpUnit\ClockMock;
use Symfony\Bridge\PhpUnit\ForwardCompatTestTrait;
/** /**
* @author Dominic Tubach <dominic.tubach@to.com> * @author Dominic Tubach <dominic.tubach@to.com>
@ -22,14 +21,12 @@ use Symfony\Bridge\PhpUnit\ForwardCompatTestTrait;
*/ */
class ClockMockTest extends TestCase class ClockMockTest extends TestCase
{ {
use ForwardCompatTestTrait; public static function setUpBeforeClass()
private static function doSetUpBeforeClass()
{ {
ClockMock::register(__CLASS__); ClockMock::register(__CLASS__);
} }
private function doSetUp() protected function setUp()
{ {
ClockMock::withClockMock(1234567890.125); ClockMock::withClockMock(1234567890.125);
} }

View File

@ -63,6 +63,8 @@ if (PHP_VERSION_ID >= 70200) {
$PHPUNIT_VERSION = '4.8'; $PHPUNIT_VERSION = '4.8';
} }
$PHPUNIT_REMOVE_RETURN_TYPEHINT = filter_var($getEnvVar('SYMFONY_PHPUNIT_REMOVE_RETURN_TYPEHINT', '0'), FILTER_VALIDATE_BOOLEAN);
$COMPOSER_JSON = getenv('COMPOSER') ?: 'composer.json'; $COMPOSER_JSON = getenv('COMPOSER') ?: 'composer.json';
$root = __DIR__; $root = __DIR__;
@ -100,19 +102,20 @@ $COMPOSER = file_exists($COMPOSER = $oldPwd.'/composer.phar') || ($COMPOSER = rt
$SYMFONY_PHPUNIT_REMOVE = $getEnvVar('SYMFONY_PHPUNIT_REMOVE', 'phpspec/prophecy'.($PHPUNIT_VERSION < 6.0 ? ' symfony/yaml': '')); $SYMFONY_PHPUNIT_REMOVE = $getEnvVar('SYMFONY_PHPUNIT_REMOVE', 'phpspec/prophecy'.($PHPUNIT_VERSION < 6.0 ? ' symfony/yaml': ''));
$configurationHash = md5(implode(PHP_EOL, array(md5_file(__FILE__), $SYMFONY_PHPUNIT_REMOVE, (int) $PHPUNIT_REMOVE_RETURN_TYPEHINT)));
if (!file_exists("$PHPUNIT_DIR/phpunit-$PHPUNIT_VERSION/phpunit") || md5_file(__FILE__)."\n".$SYMFONY_PHPUNIT_REMOVE !== @file_get_contents("$PHPUNIT_DIR/.$PHPUNIT_VERSION.md5")) { $PHPUNIT_VERSION_DIR=sprintf('phpunit-%s-%d', $PHPUNIT_VERSION, $PHPUNIT_REMOVE_RETURN_TYPEHINT);
if (!file_exists("$PHPUNIT_DIR/$PHPUNIT_VERSION_DIR/phpunit") || $configurationHash !== @file_get_contents("$PHPUNIT_DIR/.$PHPUNIT_VERSION_DIR.md5")) {
// Build a standalone phpunit without symfony/yaml nor prophecy by default // Build a standalone phpunit without symfony/yaml nor prophecy by default
@mkdir($PHPUNIT_DIR, 0777, true); @mkdir($PHPUNIT_DIR, 0777, true);
chdir($PHPUNIT_DIR); chdir($PHPUNIT_DIR);
if (file_exists("phpunit-$PHPUNIT_VERSION")) { if (file_exists("$PHPUNIT_VERSION_DIR")) {
passthru(sprintf('\\' === DIRECTORY_SEPARATOR ? 'rmdir /S /Q %s > NUL': 'rm -rf %s', "phpunit-$PHPUNIT_VERSION.old")); passthru(sprintf('\\' === DIRECTORY_SEPARATOR ? 'rmdir /S /Q %s > NUL': 'rm -rf %s', "$PHPUNIT_VERSION_DIR.old"));
rename("phpunit-$PHPUNIT_VERSION", "phpunit-$PHPUNIT_VERSION.old"); rename("$PHPUNIT_VERSION_DIR", "$PHPUNIT_VERSION_DIR.old");
passthru(sprintf('\\' === DIRECTORY_SEPARATOR ? 'rmdir /S /Q %s': 'rm -rf %s', "phpunit-$PHPUNIT_VERSION.old")); passthru(sprintf('\\' === DIRECTORY_SEPARATOR ? 'rmdir /S /Q %s': 'rm -rf %s', "$PHPUNIT_VERSION_DIR.old"));
} }
passthru("$COMPOSER create-project --no-install --prefer-dist --no-scripts --no-plugins --no-progress --ansi phpunit/phpunit phpunit-$PHPUNIT_VERSION \"$PHPUNIT_VERSION.*\""); passthru("$COMPOSER create-project --no-install --prefer-dist --no-scripts --no-plugins --no-progress --ansi phpunit/phpunit $PHPUNIT_VERSION_DIR \"$PHPUNIT_VERSION.*\"");
chdir("phpunit-$PHPUNIT_VERSION"); chdir("$PHPUNIT_VERSION_DIR");
if ($SYMFONY_PHPUNIT_REMOVE) { if ($SYMFONY_PHPUNIT_REMOVE) {
passthru("$COMPOSER remove --no-update ".$SYMFONY_PHPUNIT_REMOVE); passthru("$COMPOSER remove --no-update ".$SYMFONY_PHPUNIT_REMOVE);
} }
@ -139,6 +142,26 @@ if (!file_exists("$PHPUNIT_DIR/phpunit-$PHPUNIT_VERSION/phpunit") || md5_file(__
if ($exit) { if ($exit) {
exit($exit); exit($exit);
} }
// Mutate TestCase code
$alteredCode = file_get_contents($alteredFile = './src/Framework/TestCase.php');
if ($PHPUNIT_REMOVE_RETURN_TYPEHINT) {
$alteredCode = preg_replace('/^ ((?:protected|public)(?: static)? function \w+\(\)): void/m', ' $1', $alteredCode);
}
$alteredCode = preg_replace('/abstract class (?:TestCase|PHPUnit_Framework_TestCase)[^\{]+\{/', '$0 '.PHP_EOL." use \Symfony\Bridge\PhpUnit\Legacy\PolyfillTestCaseTrait;", $alteredCode, 1);
file_put_contents($alteredFile, $alteredCode);
// Mutate Assert code
$alteredCode = file_get_contents($alteredFile = './src/Framework/Assert.php');
$alteredCode = preg_replace('/abstract class (?:Assert|PHPUnit_Framework_Assert)[^\{]+\{/', '$0 '.PHP_EOL." use \Symfony\Bridge\PhpUnit\Legacy\PolyfillAssertTrait;", $alteredCode, 1);
file_put_contents($alteredFile, $alteredCode);
// remove internal annotation from polyfill
foreach (array('PolyfillTestCaseTrait', 'PolyfillAssertTrait') as $polyfill) {
$traitFile = "./vendor/symfony/phpunit-bridge/Legacy/$polyfill.php";
file_put_contents($traitFile, str_replace(' * @internal', '', file_get_contents($traitFile)));
}
file_put_contents('phpunit', <<<'EOPHP' file_put_contents('phpunit', <<<'EOPHP'
<?php <?php
@ -161,9 +184,8 @@ Symfony\Bridge\PhpUnit\TextUI\Command::main();
EOPHP EOPHP
); );
chdir('..'); chdir('..');
file_put_contents(".$PHPUNIT_VERSION.md5", md5_file(__FILE__)."\n".$SYMFONY_PHPUNIT_REMOVE); file_put_contents(".$PHPUNIT_VERSION_DIR.md5", $configurationHash);
chdir($oldPwd); chdir($oldPwd);
} }
global $argv, $argc; global $argv, $argc;
@ -193,7 +215,7 @@ if (isset($argv[1]) && is_dir($argv[1]) && !file_exists($argv[1].'/phpunit.xml.d
} }
} }
$cmd[0] = sprintf('%s %s --colors=always', $PHP, escapeshellarg("$PHPUNIT_DIR/phpunit-$PHPUNIT_VERSION/phpunit")); $cmd[0] = sprintf('%s %s --colors=always', $PHP, escapeshellarg("$PHPUNIT_DIR/$PHPUNIT_VERSION_DIR/phpunit"));
$cmd = str_replace('%', '%%', implode(' ', $cmd)).' %1$s'; $cmd = str_replace('%', '%%', implode(' ', $cmd)).' %1$s';
if ('\\' === DIRECTORY_SEPARATOR) { if ('\\' === DIRECTORY_SEPARATOR) {
@ -261,7 +283,7 @@ if ($components) {
array_splice($argv, 1, 0, array('--colors=always')); array_splice($argv, 1, 0, array('--colors=always'));
$_SERVER['argv'] = $argv; $_SERVER['argv'] = $argv;
$_SERVER['argc'] = ++$argc; $_SERVER['argc'] = ++$argc;
include "$PHPUNIT_DIR/phpunit-$PHPUNIT_VERSION/phpunit"; include "$PHPUNIT_DIR/$PHPUNIT_VERSION_DIR/phpunit";
} }
exit($exit); exit($exit);