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\StringContains;
use PHPUnit\Framework\Constraint\TraversableContains;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
/**
* @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 string $message
@ -320,12 +226,6 @@ trait ForwardCompatTestTraitForV5
*/
public static function assertFinite($actual, $message = '')
{
if (method_exists(TestCase::class, 'assertFinite')) {
parent::assertFinite($actual, $message);
return;
}
static::assertInternalType('float', $actual, $message);
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 = '')
{
if (method_exists(TestCase::class, 'assertInfinite')) {
parent::assertInfinite($actual, $message);
return;
}
static::assertInternalType('float', $actual, $message);
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 = '')
{
if (method_exists(TestCase::class, 'assertNan')) {
parent::assertNan($actual, $message);
return;
}
static::assertInternalType('float', $actual, $message);
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
*/
trait ForwardCompatTestTraitForV8
trait SetUpTearDownTraitForV8
{
public static function setUpBeforeClass(): void
{

View File

@ -14,22 +14,15 @@ namespace Symfony\Bridge\PhpUnit;
use PHPUnit\Framework\TestCase;
// A trait to provide forward compatibility with newest PHPUnit versions
$r = new \ReflectionClass(TestCase::class);
if (\PHP_VERSION_ID < 70000 || !$r->hasMethod('createMock') || !$r->getMethod('createMock')->hasReturnType()) {
trait ForwardCompatTestTrait
if (\PHP_VERSION_ID < 70000 || !$r->getMethod('setUp')->hasReturnType()) {
trait SetUpTearDownTrait
{
use Legacy\ForwardCompatTestTraitForV5;
}
} elseif ($r->getMethod('tearDown')->hasReturnType()) {
trait ForwardCompatTestTrait
{
use Legacy\ForwardCompatTestTraitForV8;
use Legacy\SetUpTearDownTraitForV5;
}
} 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 Symfony\Bridge\PhpUnit\ClockMock;
use Symfony\Bridge\PhpUnit\ForwardCompatTestTrait;
/**
* @author Dominic Tubach <dominic.tubach@to.com>
@ -22,14 +21,12 @@ use Symfony\Bridge\PhpUnit\ForwardCompatTestTrait;
*/
class ClockMockTest extends TestCase
{
use ForwardCompatTestTrait;
private static function doSetUpBeforeClass()
public static function setUpBeforeClass()
{
ClockMock::register(__CLASS__);
}
private function doSetUp()
protected function setUp()
{
ClockMock::withClockMock(1234567890.125);
}

View File

@ -63,6 +63,8 @@ if (PHP_VERSION_ID >= 70200) {
$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';
$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': ''));
if (!file_exists("$PHPUNIT_DIR/phpunit-$PHPUNIT_VERSION/phpunit") || md5_file(__FILE__)."\n".$SYMFONY_PHPUNIT_REMOVE !== @file_get_contents("$PHPUNIT_DIR/.$PHPUNIT_VERSION.md5")) {
$configurationHash = md5(implode(PHP_EOL, array(md5_file(__FILE__), $SYMFONY_PHPUNIT_REMOVE, (int) $PHPUNIT_REMOVE_RETURN_TYPEHINT)));
$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
@mkdir($PHPUNIT_DIR, 0777, true);
chdir($PHPUNIT_DIR);
if (file_exists("phpunit-$PHPUNIT_VERSION")) {
passthru(sprintf('\\' === DIRECTORY_SEPARATOR ? 'rmdir /S /Q %s > NUL': 'rm -rf %s', "phpunit-$PHPUNIT_VERSION.old"));
rename("phpunit-$PHPUNIT_VERSION", "phpunit-$PHPUNIT_VERSION.old");
passthru(sprintf('\\' === DIRECTORY_SEPARATOR ? 'rmdir /S /Q %s': 'rm -rf %s', "phpunit-$PHPUNIT_VERSION.old"));
if (file_exists("$PHPUNIT_VERSION_DIR")) {
passthru(sprintf('\\' === DIRECTORY_SEPARATOR ? 'rmdir /S /Q %s > NUL': 'rm -rf %s', "$PHPUNIT_VERSION_DIR.old"));
rename("$PHPUNIT_VERSION_DIR", "$PHPUNIT_VERSION_DIR.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.*\"");
chdir("phpunit-$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_VERSION_DIR");
if ($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) {
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'
<?php
@ -161,9 +184,8 @@ Symfony\Bridge\PhpUnit\TextUI\Command::main();
EOPHP
);
chdir('..');
file_put_contents(".$PHPUNIT_VERSION.md5", md5_file(__FILE__)."\n".$SYMFONY_PHPUNIT_REMOVE);
file_put_contents(".$PHPUNIT_VERSION_DIR.md5", $configurationHash);
chdir($oldPwd);
}
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';
if ('\\' === DIRECTORY_SEPARATOR) {
@ -261,7 +283,7 @@ if ($components) {
array_splice($argv, 1, 0, array('--colors=always'));
$_SERVER['argv'] = $argv;
$_SERVER['argc'] = ++$argc;
include "$PHPUNIT_DIR/phpunit-$PHPUNIT_VERSION/phpunit";
include "$PHPUNIT_DIR/$PHPUNIT_VERSION_DIR/phpunit";
}
exit($exit);