wiki.techinc.nl/tests/phpunit/includes/debug/DeprecationHelperTest.php
Umherirrender 0099099bb0 tests: Change expectDeprecation to expectDeprecationAndContinue
TestCase::expectDeprecation() is deprecated in PHPUnit 10,
use mediawiki own MediaWikiTestCaseTrait::expectDeprecationAndContinue()
for this case. This avoids the trigger_error call and isolate the
deprecation check into MWDebug class.

The continue part is also helpful in StubGlobalUserTest, where after the
first deprecation access more code exists, that was not executed as
PhpUnit 9 converts deprecations to exceptions.
In RCFeedTest the exception needs to be catched as the code proceed
after the deprecation notice is emitted.

Bug: T342110
Change-Id: Iecf827bec0d5215fd21bbb20b84caf928ee108a0
2023-07-18 16:35:43 +00:00

252 lines
9.3 KiB
PHP

<?php
use MediaWiki\MainConfigNames;
use Wikimedia\TestingAccessWrapper;
/**
* @covers DeprecationHelper
*/
class DeprecationHelperTest extends MediaWikiIntegrationTestCase {
/** @var TestDeprecatedClass */
private $testClass;
/** @var TestDeprecatedSubclass */
private $testSubclass;
protected function setUp(): void {
parent::setUp();
$this->testClass = new TestDeprecatedClass();
$this->testSubclass = new TestDeprecatedSubclass();
$this->overrideConfigValue( MainConfigNames::DevelopmentWarnings, false );
}
/**
* @dataProvider provideGet
*/
public function testGet( $propName, $expectedLevel, $expectedMessage ) {
if ( $expectedLevel ) {
$this->assertErrorTriggered( function () use ( $propName ) {
$this->assertSame( null, $this->testClass->$propName );
}, $expectedLevel, $expectedMessage );
} else {
$this->assertDeprecationWarningIssued( function () use ( $propName ) {
$expectedValue = $propName === 'fallbackDeprecatedMethodName' ? 'FOO' : 1;
$this->assertSame( $expectedValue, $this->testClass->$propName );
}, $expectedMessage );
}
}
public static function provideGet() {
return [
[ 'protectedDeprecated', null,
'Use of TestDeprecatedClass::$protectedDeprecated was deprecated in MediaWiki 1.23. ' .
'[Called from DeprecationHelperTest::{closure}' ],
[ 'privateDeprecated', null,
'Use of TestDeprecatedClass::$privateDeprecated was deprecated in MediaWiki 1.24. ' .
'[Called from DeprecationHelperTest::{closure}' ],
[ 'fallbackDeprecated', null,
'Use of TestDeprecatedClass::$fallbackDeprecated was deprecated in MediaWiki 1.25. ' .
'[Called from DeprecationHelperTest::{closure}' ],
[ 'fallbackDeprecatedMethodName', null,
'Use of TestDeprecatedClass::$fallbackDeprecatedMethodName was deprecated in MediaWiki 1.26. ' .
'[Called from DeprecationHelperTest::{closure}' ],
[ 'fallbackGetterOnly', null,
'Use of TestDeprecatedClass::$fallbackGetterOnly was deprecated in MediaWiki 1.25. ' .
'[Called from DeprecationHelperTest::{closure}' ],
[ 'protectedNonDeprecated', E_USER_ERROR,
'Cannot access non-public property TestDeprecatedClass::$protectedNonDeprecated' ],
[ 'privateNonDeprecated', E_USER_ERROR,
'Cannot access non-public property TestDeprecatedClass::$privateNonDeprecated' ],
[ 'nonExistent', E_USER_NOTICE, 'Undefined property: TestDeprecatedClass::$nonExistent' ],
];
}
public function testDeprecateDynamicPropertyAccess() {
$testObject = new class extends TestDeprecatedClass {
public function __construct() {
parent::__construct();
$this->deprecateDynamicPropertiesAccess( '1.23' );
}
};
$this->assertDeprecationWarningIssued(
static function () use ( $testObject ) {
$testObject->dynamic_property = 'bla';
},
'Use of TestDeprecatedClass::$dynamic_property was deprecated in MediaWiki 1.23. ' .
'[Called from DeprecationHelperTest::{closure}'
);
}
public function testDynamicPropertyNullCoalesce() {
$testObject = new TestDeprecatedClass();
$this->assertSame( 'bla', $testObject->dynamic_property ?? 'bla' );
}
public function testDynamicPropertyNullCoalesceDeprecated() {
$testObject = new class extends TestDeprecatedClass {
public function __construct() {
parent::__construct();
$this->deprecateDynamicPropertiesAccess( '1.23' );
}
};
$this->assertDeprecationWarningIssued(
function () use ( $testObject ) {
$this->assertSame( 'bla', $testObject->dynamic_property ?? 'bla' );
},
'Use of TestDeprecatedClass::$dynamic_property was deprecated in MediaWiki 1.23. ' .
'[Called from DeprecationHelperTest::{closure}'
);
}
public function testDynamicPropertyOnMockObject() {
$testObject = $this->getMockBuilder( TestDeprecatedClass::class )
->enableProxyingToOriginalMethods()
->getMock();
$testObject->blabla = 'test';
$this->assertSame( 'test', $testObject->blabla );
}
/**
* @dataProvider provideSet
*/
public function testSet( $propName, $expectedLevel, $expectedMessage, $expectedValue = 0 ) {
$this->assertPropertySame( 1, $this->testClass, $propName );
if ( $expectedLevel ) {
$this->assertErrorTriggered( function () use ( $propName ) {
$this->testClass->$propName = 0;
$this->assertPropertySame( 1, $this->testClass, $propName );
}, $expectedLevel, $expectedMessage );
} else {
if ( $propName === 'nonExistent' ) {
$this->testClass->$propName = 0;
} else {
$this->assertDeprecationWarningIssued( function () use ( $propName ) {
$this->testClass->$propName = 0;
}, $expectedMessage );
}
$this->assertPropertySame( 0, $this->testClass, $propName );
}
$this->assertPropertySame( $expectedValue, $this->testClass, $propName );
}
public static function provideSet() {
return [
[ 'protectedDeprecated', null,
'Use of TestDeprecatedClass::$protectedDeprecated was deprecated in MediaWiki 1.23. ' .
'[Called from DeprecationHelperTest::{closure}' ],
[ 'privateDeprecated', null,
'Use of TestDeprecatedClass::$privateDeprecated was deprecated in MediaWiki 1.24. ' .
'[Called from DeprecationHelperTest::{closure}' ],
[ 'fallbackDeprecated', null,
'Use of TestDeprecatedClass::$fallbackDeprecated was deprecated in MediaWiki 1.25. ' .
'[Called from DeprecationHelperTest::{closure}' ],
[ 'fallbackDeprecatedMethodName', null,
'Use of TestDeprecatedClass::$fallbackDeprecatedMethodName was deprecated in MediaWiki 1.26. ' .
'[Called from DeprecationHelperTest::{closure}' ],
[ 'fallbackGetterOnly', E_USER_ERROR,
'Cannot access non-public property TestDeprecatedClass::$fallbackGetterOnly' ],
[ 'protectedNonDeprecated', E_USER_ERROR,
'Cannot access non-public property TestDeprecatedClass::$protectedNonDeprecated', 1 ],
[ 'privateNonDeprecated', E_USER_ERROR,
'Cannot access non-public property TestDeprecatedClass::$privateNonDeprecated', 1 ],
[ 'nonExistent', null,
'Use of TestDeprecatedClass::$nonExistent was deprecated in MediaWiki 1.23. ' .
'[Called from DeprecationHelperTest::{closure}' ],
];
}
public function testInternalGet() {
$this->assertSame( [
'prod' => 1,
'prond' => 1,
'prid' => 1,
'prind' => 1,
], $this->testClass->getThings() );
}
public function testInternalSet() {
$this->testClass->setThings( 2, 2, 2, 2 );
$wrapper = TestingAccessWrapper::newFromObject( $this->testClass );
$this->assertSame( 2, $wrapper->protectedDeprecated );
$this->assertSame( 2, $wrapper->protectedNonDeprecated );
$this->assertSame( 2, $wrapper->privateDeprecated );
$this->assertSame( 2, $wrapper->privateNonDeprecated );
}
public function testSubclassGetSet() {
$fullName = 'TestDeprecatedClass::$privateNonDeprecated';
$this->assertErrorTriggered( function () {
$this->assertSame( null, $this->testSubclass->getNondeprecatedPrivateParentProperty() );
}, E_USER_ERROR, "Cannot access non-public property $fullName" );
$this->assertErrorTriggered( function () {
$this->testSubclass->setNondeprecatedPrivateParentProperty( 0 );
$wrapper = TestingAccessWrapper::newFromObject( $this->testSubclass );
$this->assertSame( 1, $wrapper->privateNonDeprecated );
}, E_USER_ERROR, "Cannot access non-public property $fullName" );
$fullName = 'TestDeprecatedSubclass::$subclassPrivateNondeprecated';
$this->assertErrorTriggered( function () {
$this->assertSame( null, $this->testSubclass->subclassPrivateNondeprecated );
}, E_USER_ERROR, "Cannot access non-public property $fullName" );
$this->assertErrorTriggered( function () {
$this->testSubclass->subclassPrivateNondeprecated = 0;
$wrapper = TestingAccessWrapper::newFromObject( $this->testSubclass );
$this->assertSame( 1, $wrapper->subclassPrivateNondeprecated );
}, E_USER_ERROR, "Cannot access non-public property $fullName" );
}
protected function assertErrorTriggered( callable $callback, $level, $message ) {
$actualLevel = $actualMessage = null;
set_error_handler( static function ( $errorCode, $errorStr ) use ( &$actualLevel, &$actualMessage ) {
$actualLevel = $errorCode;
$actualMessage = $errorStr;
} );
$callback();
restore_error_handler();
$this->assertSame( $level, $actualLevel );
$this->assertSame( $message, $actualMessage );
}
protected function assertPropertySame( $expected, $object, $propName ) {
try {
$this->assertSame( $expected, TestingAccessWrapper::newFromObject( $object )->$propName );
} catch ( ReflectionException $e ) {
if ( !preg_match( "/Property (TestDeprecated(Class|Subclass)::\\$?)?$propName does not exist/",
$e->getMessage() )
) {
throw $e;
}
// property_exists accepts monkey-patching, Reflection / TestingAccessWrapper doesn't
if ( property_exists( $object, $propName ) ) {
$this->assertSame( $expected, $object->$propName );
}
}
}
protected function assertDeprecationWarningIssued( callable $callback, string $expectedMessage ) {
$this->expectDeprecationAndContinue( '/' . preg_quote( $expectedMessage, '/' ) . '/' );
$callback();
}
/**
* Test bad MW version values to throw exceptions as expected
*
* @dataProvider provideBadMWVersion
*/
public function testBadMWVersion( $version, $expected ) {
$this->expectException( $expected );
wfDeprecated( __METHOD__, $version );
}
public static function provideBadMWVersion() {
return [
[ 1, Exception::class ],
[ 1.33, Exception::class ],
[ true, Exception::class ],
[ null, Exception::class ]
];
}
}