| Current File : /home/jvzmxxx/wiki1/extensions/Wikibase/repo/tests/phpunit/includes/Content/EntityHandlerTest.php |
<?php
namespace Wikibase\Test;
use ContentHandler;
use DataValues\Serializers\DataValueSerializer;
use InvalidArgumentException;
use Language;
use MWException;
use Revision;
use RuntimeException;
use Title;
use Wikibase\Content\EntityInstanceHolder;
use Wikibase\DataModel\Entity\EntityDocument;
use Wikibase\DataModel\Entity\EntityId;
use Wikibase\DataModel\Entity\EntityRedirect;
use Wikibase\DataModel\Term\FingerprintProvider;
use Wikibase\EntityContent;
use Wikibase\InternalSerialization\SerializerFactory;
use Wikibase\Lib\DataTypeDefinitions;
use Wikibase\Lib\EntityTypeDefinitions;
use Wikibase\Repo\Content\EntityHandler;
use Wikibase\Repo\Validators\EntityValidator;
use Wikibase\Repo\Validators\ValidatorErrorLocalizer;
use Wikibase\Repo\WikibaseRepo;
use Wikibase\SettingsArray;
use WikitextContent;
/**
* @covers Wikibase\Repo\Content\EntityHandler
*
* @group Wikibase
* @group WikibaseEntity
* @group WikibaseEntityHandler
* @group WikibaseRepo
*
* @license GPL-2.0+
* @author Jeroen De Dauw < jeroendedauw@gmail.com >
* @author Daniel Kinzler
*/
abstract class EntityHandlerTest extends \MediaWikiTestCase {
abstract public function getClassName();
abstract public function getModelId();
/**
* Returns instances of the EntityHandler deriving class.
* @return array
*/
public function instanceProvider() {
return array(
array( $this->getHandler() ),
);
}
/**
* @param SettingsArray|null $settings
*
* @return WikibaseRepo
*/
protected function getWikibaseRepo( SettingsArray $settings = null ) {
$repoSettings = WikibaseRepo::getDefaultInstance()->getSettings()->getArrayCopy();
if ( $settings ) {
$repoSettings = array_merge( $repoSettings, $settings->getArrayCopy() );
}
return new WikibaseRepo(
new SettingsArray( $repoSettings ),
new DataTypeDefinitions( array() ),
new EntityTypeDefinitions( require __DIR__ . '/../../../../../lib/WikibaseLib.entitytypes.php' ),
Language::factory( 'qqq' )
);
}
/**
* @param SettingsArray|null $settings
*
* @return EntityHandler
*/
abstract protected function getHandler( SettingsArray $settings = null );
/**
* @param EntityDocument|null $entity
*
* @return EntityContent
*/
protected function newEntityContent( EntityDocument $entity = null ) {
if ( !$entity ) {
$entity = $this->newEntity();
}
$handler = $this->getHandler();
return $handler->makeEntityContent( new EntityInstanceHolder( $entity ) );
}
/**
* @param EntityId $id
* @param EntityId $target
*
* @return EntityContent
*/
protected function newRedirectContent( EntityId $id, EntityId $target ) {
$handler = $this->getHandler();
return $handler->makeEntityRedirectContent( new EntityRedirect( $id, $target ) );
}
/**
* @param EntityId|null $id
*
* @return EntityDocument
*/
abstract protected function newEntity( EntityId $id = null );
/**
* Returns EntityContents that can be handled by the EntityHandler deriving class.
*
* @return array[]
*/
public function contentProvider() {
return array(
array( $this->newEntityContent() ),
);
}
/**
* @dataProvider instanceProvider
* @param EntityHandler $entityHandler
*/
public function testGetModelName( EntityHandler $entityHandler ) {
$this->assertEquals( $this->getModelId(), $entityHandler->getModelID() );
$this->assertInstanceOf( ContentHandler::class, $entityHandler );
$this->assertInstanceOf( $this->getClassName(), $entityHandler );
}
/**
* @dataProvider instanceProvider
* @param EntityHandler $entityHandler
*/
public function testGetSpecialPageForCreation( EntityHandler $entityHandler ) {
$specialPageName = $entityHandler->getSpecialPageForCreation();
$this->assertTrue( $specialPageName === null || is_string( $specialPageName ) );
}
public function testGivenNonEntityContent_serializeContentThrowsException() {
$handler = $this->getHandler();
$content = new WikitextContent( '' );
$this->setExpectedException( InvalidArgumentException::class );
$handler->serializeContent( $content );
}
/**
* @dataProvider contentProvider
* @param EntityContent $content
*/
public function testSerialization( EntityContent $content ) {
$handler = $this->getHandler();
foreach ( array( CONTENT_FORMAT_JSON, CONTENT_FORMAT_SERIALIZED ) as $format ) {
$this->assertTrue( $content->equals(
$handler->unserializeContent( $handler->serializeContent( $content, $format ), $format )
) );
}
}
public function testCanBeUsedOn() {
$handler = $this->getHandler();
$this->assertTrue( $handler->canBeUsedOn( Title::makeTitle( $handler->getEntityNamespace(), "1234" ) ),
'It should be possible to create this kind of entity in the respective entity namespace!'
);
$this->assertFalse( $handler->canBeUsedOn( Title::makeTitle( NS_MEDIAWIKI, "Foo" ) ),
'It should be impossible to create an entity outside the respective entity namespace!'
);
}
public function testIsParserCacheSupported() {
$this->assertTrue( $this->getHandler()->isParserCacheSupported() );
}
public function testGetPageLanguage() {
global $wgContLang;
$handler = $this->getHandler();
$title = Title::makeTitle( $handler->getEntityNamespace(), "1234567" );
//NOTE: currently, this tests whether getPageLanguage will always return the content language, even
// if the user language is different. It's unclear whether this is actually the desired behavior,
// since Wikibase Entities are inherently multilingual, so they have no actual "page language".
// test whatever is there
$this->assertEquals( $wgContLang->getCode(), $handler->getPageLanguage( $title )->getCode() );
// test fr
$this->setMwGlobals( 'wgLang', Language::factory( "fr" ) );
$handler = $this->getHandler();
$this->assertEquals( $wgContLang->getCode(), $handler->getPageLanguage( $title )->getCode() );
// test nl
$this->setMwGlobals( 'wgLang', Language::factory( "nl" ) );
$this->setMwGlobals( 'wgContLang', Language::factory( "fr" ) );
$handler = $this->getHandler();
$this->assertEquals( $wgContLang->getCode(), $handler->getPageLanguage( $title )->getCode() );
}
public function testGetPageViewLanguage() {
global $wgLang;
$handler = $this->getHandler();
$title = Title::makeTitle( $handler->getEntityNamespace(), "1234567" );
//NOTE: we expect getPageViewLanguage to return the user language, because Wikibase Entities
// are always shown in the user language.
// test whatever is there
$this->assertEquals( $wgLang->getCode(), $handler->getPageViewLanguage( $title )->getCode() );
// test fr
$this->setMwGlobals( 'wgLang', Language::factory( "fr" ) );
$handler = $this->getHandler();
$this->assertEquals( $wgLang->getCode(), $handler->getPageViewLanguage( $title )->getCode() );
// test nl
$this->setMwGlobals( 'wgLang', Language::factory( "nl" ) );
$handler = $this->getHandler();
$this->assertEquals( $wgLang->getCode(), $handler->getPageViewLanguage( $title )->getCode() );
}
public function testLocalizedModelName() {
$name = ContentHandler::getLocalizedName( $this->getModelId() );
$this->assertNotEquals( $this->getModelId(), $name, "localization of model name" );
}
protected function fakeRevision( EntityContent $content, $id = 0 ) {
$revision = new Revision( array(
'id' => $id,
'page' => $id,
'content' => $content,
) );
return $revision;
}
public function provideGetUndoContent() {
/** @var FingerprintProvider $e1 */
/** @var FingerprintProvider $e2 */
/** @var FingerprintProvider $e3 */
/** @var FingerprintProvider $e4 */
/** @var FingerprintProvider $e5 */
/** @var FingerprintProvider $e5u4 */
/** @var FingerprintProvider $e5u4u3 */
$e1 = $this->newEntity();
$r1 = $this->fakeRevision( $this->newEntityContent( $e1 ), 1 );
$e2 = $this->newEntity();
$e2->getFingerprint()->setLabel( 'en', 'Foo' );
$r2 = $this->fakeRevision( $this->newEntityContent( $e2 ), 2 );
$e3 = $this->newEntity();
$e3->getFingerprint()->setLabel( 'en', 'Foo' );
$e3->getFingerprint()->setLabel( 'de', 'Fuh' );
$r3 = $this->fakeRevision( $this->newEntityContent( $e3 ), 3 );
$e4 = $this->newEntity();
$e4->getFingerprint()->setLabel( 'en', 'Foo' );
$e4->getFingerprint()->setLabel( 'de', 'Fuh' );
$e4->getFingerprint()->setLabel( 'fr', 'Fu' );
$r4 = $this->fakeRevision( $this->newEntityContent( $e4 ), 4 );
$e5 = $this->newEntity();
$e5->getFingerprint()->setLabel( 'en', 'F00' );
$e5->getFingerprint()->setLabel( 'de', 'Fuh' );
$e5->getFingerprint()->setLabel( 'fr', 'Fu' );
$r5 = $this->fakeRevision( $this->newEntityContent( $e5 ), 5 );
$e5u4 = $this->newEntity();
$e5u4->getFingerprint()->setLabel( 'en', 'F00' );
$e5u4->getFingerprint()->setLabel( 'de', 'Fuh' );
$e5u4u3 = $this->newEntity();
$e5u4u3->getFingerprint()->setLabel( 'en', 'F00' );
return array(
array( $r5, $r5, $r4, $this->newEntityContent( $e4 ), "undo last edit" ),
array( $r5, $r4, $r3, $this->newEntityContent( $e5u4 ), "undo previous edit" ),
array( $r5, $r5, $r3, $this->newEntityContent( $e3 ), "undo last two edits" ),
array( $r5, $r4, $r2, $this->newEntityContent( $e5u4u3 ), "undo past two edits" ),
array( $r5, $r2, $r1, null, "undo conflicting edit" ),
array( $r5, $r3, $r1, null, "undo two edits with conflict" ),
);
}
/**
* @dataProvider provideGetUndoContent
*
* @param Revision $latestRevision
* @param Revision $newerRevision
* @param Revision $olderRevision
* @param EntityContent|null $expected
* @param string $message
*/
public function testGetUndoContent(
Revision $latestRevision,
Revision $newerRevision,
Revision $olderRevision,
EntityContent $expected = null,
$message
) {
$handler = $this->getHandler();
$undo = $handler->getUndoContent( $latestRevision, $newerRevision, $olderRevision );
if ( $expected ) {
$this->assertInstanceOf( EntityContent::class, $undo, $message );
$this->assertTrue( $expected->equals( $undo ), $message );
} else {
$this->assertFalse( $undo, $message );
}
}
public function testGetEntityType() {
$handler = $this->getHandler();
$content = $this->newEntityContent();
$entity = $content->getEntity();
$this->assertEquals( $entity->getType(), $handler->getEntityType() );
}
public function testMakeEntityContent() {
$entity = $this->newEntity();
$handler = $this->getHandler();
$content = $handler->makeEntityContent( new EntityInstanceHolder( $entity ) );
$this->assertEquals( $this->getModelId(), $content->getModel() );
$this->assertSame( $entity, $content->getEntity() );
}
public function testMakeEmptyContent() {
// We don't support empty content.
$this->setExpectedException( MWException::class );
$handler = $this->getHandler();
$handler->makeEmptyContent();
}
public function testMakeRedirectContent() {
// We don't support title based redirects.
$this->setExpectedException( MWException::class );
$handler = $this->getHandler();
$handler->makeRedirectContent( Title::newFromText( 'X11', $handler->getEntityNamespace() ) );
}
public function testMakeEmptyEntity() {
$handler = $this->getHandler();
$entity = $handler->makeEmptyEntity();
$this->assertEquals( $handler->getEntityType(), $entity->getType(), 'entity type' );
}
abstract public function entityIdProvider();
/**
* @dataProvider entityIdProvider
*/
public function testMakeEntityId( $idString ) {
$handler = $this->getHandler();
$id = $handler->makeEntityId( $idString );
$this->assertEquals( $handler->getEntityType(), $id->getEntityType() );
$this->assertEquals( $idString, $id->getSerialization() );
}
public function exportTransformProvider() {
$entity = $this->newEntity();
$internalSerializer = WikibaseRepo::getDefaultInstance()->getEntitySerializer();
$oldBlob = json_encode( $internalSerializer->serialize( $entity ) );
// fake several old formats
$type = $entity->getType();
$id = $entity->getId()->getSerialization();
// replace "type":"item","id":"q7" with "entity":["item",7]
$veryOldBlob = preg_replace(
'/"type":"\w+"(,"datatype":"\w+")?,"id":"\w\d+"/',
'"entity":["' . strtolower( $type ) . '",' . substr( $id, 1 ) . ']$1',
$oldBlob
);
// replace "entity":["item",7] with "entity":"q7"
$veryVeryOldBlob = preg_replace(
'/"entity":\["\w+",\d+\]/',
'"entity":"' . strtolower( $id ) . '"',
$veryOldBlob
);
// sanity (cannot compare $veryOldBlob and $oldBlob until we have the new serialization in place)
if ( $veryVeryOldBlob === $veryOldBlob /* || $veryOldBlob === $oldBlob */ ) {
throw new RuntimeException( 'Failed to fake very old serialization format based on oldish serialization format.' );
}
// make new style blob
$newSerializerFactory = new SerializerFactory( new DataValueSerializer() );
$newSerializer = $newSerializerFactory->newEntitySerializer();
$newBlob = json_encode( $newSerializer->serialize( $entity ) );
return array(
'old serialization / ancient id format' => array( $veryVeryOldBlob, $newBlob ),
'old serialization / new silly id format' => array( $veryOldBlob, $newBlob ),
'old serialization / old serializer format' => array( $oldBlob, $newBlob ),
'new serialization format, keep as is' => array( $newBlob, $newBlob ),
);
}
/**
* @dataProvider exportTransformProvider
*/
public function testExportTransform( $blob, $expected ) {
$settings = new SettingsArray();
$settings->setSetting( 'transformLegacyFormatOnExport', true );
$handler = $this->getHandler( $settings );
$actual = $handler->exportTransform( $blob );
$this->assertEquals( $expected, $actual );
}
public function testExportTransform_neverRecodeNonLegacyFormat() {
$settings = new SettingsArray();
$settings->setSetting( 'transformLegacyFormatOnExport', true );
$entity = $this->newEntity();
$entitySerializer = $this->getWikibaseRepo( $settings )->getEntitySerializer();
$expected = json_encode( $entitySerializer->serialize( $entity ) );
$handler = $this->getHandler( $settings );
$actual = $handler->exportTransform( $expected );
$this->assertEquals( $expected, $actual );
}
public function testGetLegacyExportFormatDetector() {
$detector = $this->getHandler()->getLegacyExportFormatDetector();
$this->assertInternalType( 'callable', $detector );
}
public function forCreationParamProvider() {
return array(
array( true ),
array( false ),
);
}
/**
* @dataProvider forCreationParamProvider
*/
public function testGetOnSaveValidators( $forCreation ) {
$handler = $this->getHandler();
$validators = $handler->getOnSaveValidators( $forCreation );
$this->assertInternalType( 'array', $validators );
foreach ( $validators as $validator ) {
$this->assertInstanceOf( EntityValidator::class, $validator );
}
}
public function testGetValidationErrorLocalizer() {
$localizer = $this->getHandler()->getValidationErrorLocalizer();
$this->assertInstanceOf( ValidatorErrorLocalizer::class, $localizer );
}
public function testMakeParserOptions() {
$handler = $this->getHandler();
$options = $handler->makeParserOptions( 'canonical' );
$hash = $options->optionsHash( array( 'userlang' ) );
$this->assertRegExp( '/wb\d+/', $hash, 'contains Wikibase version' );
}
}