| Current File : /home/jvzmxxx/wiki1/extensions/Wikibase/repo/tests/phpunit/includes/Content/EntityContentTest.php |
<?php
namespace Wikibase\Test;
use Diff\DiffOp\Diff\Diff;
use Diff\DiffOp\DiffOpChange;
use Diff\Patcher\PatcherException;
use ParserOutput;
use PHPUnit_Framework_Assert;
use Title;
use Wikibase\DataModel\Entity\EntityDocument;
use Wikibase\DataModel\Entity\EntityId;
use Wikibase\DataModel\Entity\EntityRedirect;
use Wikibase\DataModel\Services\Diff\EntityDiff;
use Wikibase\DataModel\Term\FingerprintProvider;
use Wikibase\EntityContent;
use Wikibase\Lib\Store\EntityStore;
use Wikibase\Repo\Content\EntityContentDiff;
use Wikibase\Repo\WikibaseRepo;
use WikiPage;
/**
* @covers Wikibase\EntityContent
*
* @group WikibaseRepo
* @group Wikibase
*
* @license GPL-2.0+
* @author Jeroen De Dauw < jeroendedauw@gmail.com >
* @author John Erling Blad < jeblad@gmail.com >
* @author Daniel Kinzler
*/
abstract class EntityContentTest extends \MediaWikiTestCase {
private $originalGroupPermissions;
private $originalUser;
/**
* @var EntityStore
*/
private $entityStore;
protected function setUp() {
global $wgGroupPermissions, $wgUser;
parent::setUp();
$this->originalGroupPermissions = $wgGroupPermissions;
$this->originalUser = $wgUser;
$this->entityStore = WikibaseRepo::getDefaultInstance()->getEntityStore();
}
protected function tearDown() {
global $wgGroupPermissions;
global $wgUser;
$wgGroupPermissions = $this->originalGroupPermissions;
if ( $this->originalUser ) { // should not be null, but sometimes, it is
$wgUser = $this->originalUser;
}
if ( $wgUser ) { // should not be null, but sometimes, it is
// reset rights cache
$wgUser->addGroup( "dummy" );
$wgUser->removeGroup( "dummy" );
}
parent::tearDown();
}
/**
* @return EntityId
*/
abstract protected function getDummyId();
/**
* @param EntityId|null $entityId
*
* @return EntityContent
*/
abstract protected function newEmpty( EntityId $entityId = null );
/**
* @dataProvider getTextForSearchIndexProvider
*
* @param EntityContent $entityContent
* @param string $pattern
*/
public function testGetTextForSearchIndex( EntityContent $entityContent, $pattern ) {
$text = $entityContent->getTextForSearchIndex();
$this->assertRegExp( $pattern . 'm', $text );
}
private function setLabel( EntityDocument $entity, $lang, $text ) {
if ( $entity instanceof FingerprintProvider ) {
$entity->getFingerprint()->setLabel( $lang, $text );
} else {
throw new \InvalidArgumentException( 'FingerprintProvider expected!' );
}
}
public function getTextForSearchIndexProvider() {
$entityContent = $this->newEmpty();
$this->setLabel( $entityContent->getEntity(), 'en', "cake" );
return array(
array( $entityContent, '/^cake$/' )
);
}
public function testWikibaseTextForSearchIndex() {
$entityContent = $this->newEmpty();
$this->setLabel( $entityContent->getEntity(), 'en', "cake" );
$this->mergeMwGlobalArrayValue( 'wgHooks', array(
'WikibaseTextForSearchIndex' => array(
function ( $actualEntityContent, &$text ) use ( $entityContent ) {
PHPUnit_Framework_Assert::assertSame( $entityContent, $actualEntityContent );
PHPUnit_Framework_Assert::assertRegExp( '/cake/m', $text );
$text .= "\nHOOK";
return true;
},
),
) );
$text = $entityContent->getTextForSearchIndex();
$this->assertRegExp( '/cake.*HOOK/s', $text, 'Text for search index should be updated by the hook' );
}
public function testWikibaseTextForSearchIndex_abort() {
$entityContent = $this->newEmpty();
$this->setLabel( $entityContent->getEntity(), 'en', "cake" );
$this->mergeMwGlobalArrayValue( 'wgHooks', array(
'WikibaseTextForSearchIndex' => array(
function () {
return false;
},
),
) );
$text = $entityContent->getTextForSearchIndex();
$this->assertEquals( '', $text, 'Text for search index should be empty if the hook returned false' );
}
public function testGetParserOutput() {
$content = $this->newEmpty();
//@todo: Use a fake ID, no need to hit the database once we
// got rid of the rest of the storage logic.
$this->entityStore->assignFreshId( $content->getEntity() );
$title = Title::newFromText( 'Foo' );
$parserOutput = $content->getParserOutput( $title );
$expectedUsedOptions = array( 'userlang', 'editsection' );
$actualOptions = $parserOutput->getUsedOptions();
$this->assertEquals(
$expectedUsedOptions,
$actualOptions,
'Cache-split flags are not what they should be',
0.0,
1,
true
);
$this->assertInstanceOf( ParserOutput::class, $parserOutput );
$this->assertEquals( EntityContent::STATUS_EMPTY, $parserOutput->getProperty( 'wb-status' ) );
}
public function providePageProperties() {
$cases = array();
$emptyContent = $this->newEmpty( $this->getDummyId() );
$cases['empty'] = array(
$emptyContent,
array( 'wb-status' => EntityContent::STATUS_EMPTY, 'wb-claims' => 0 )
);
$contentWithLabel = $this->newEmpty( $this->getDummyId() );
$this->setLabel( $contentWithLabel->getEntity(), 'en', 'Foo' );
$cases['labels'] = array(
$contentWithLabel,
array( 'wb-status' => EntityContent::STATUS_STUB, 'wb-claims' => 0 )
);
return $cases;
}
/**
* @dataProvider providePageProperties
*/
public function testPageProperties( EntityContent $content, array $expectedProps ) {
$title = Title::newFromText( 'Foo' );
$parserOutput = $content->getParserOutput( $title, null, null, false );
foreach ( $expectedProps as $name => $expected ) {
$actual = $parserOutput->getProperty( $name );
$this->assertEquals( $expected, $actual, "page property $name" );
}
}
public function provideGetEntityStatus() {
$contentWithLabel = $this->newEmpty();
$this->setLabel( $contentWithLabel->getEntity(), 'de', 'xyz' );
return array(
'empty' => array(
$this->newEmpty(),
EntityContent::STATUS_EMPTY
),
'labels' => array(
$contentWithLabel,
EntityContent::STATUS_STUB
),
);
}
/**
* @dataProvider provideGetEntityStatus
*/
public function testGetEntityStatus( EntityContent $content, $status ) {
$actual = $content->getEntityStatus();
$this->assertEquals( $status, $actual );
}
abstract public function provideGetEntityId();
/**
* @dataProvider provideGetEntityId
*/
public function testGetEntityId( EntityContent $content, $expected ) {
$actual = $content->getEntityId();
$this->assertEquals( $expected, $actual );
}
public function provideGetEntityPageProperties() {
$empty = $this->newEmpty();
$labeledEntityContent = $this->newEmpty();
$this->setLabel( $labeledEntityContent->getEntity(), 'de', 'xyz' );
return array(
'empty' => array(
$empty,
array(
'wb-status' => EntityContent::STATUS_EMPTY,
'wb-claims' => 0,
)
),
'labels' => array(
$labeledEntityContent,
array(
'wb-status' => EntityContent::STATUS_STUB,
'wb-claims' => 0,
)
),
);
}
/**
* @dataProvider provideGetEntityPageProperties
*/
public function testGetEntityPageProperties( EntityContent $content, array $pageProps ) {
$actual = $content->getEntityPageProperties();
foreach ( $pageProps as $key => $value ) {
$this->assertArrayHasKey( $key, $actual );
$this->assertEquals( $value, $actual[$key], $key );
}
$this->assertArrayEquals( array_keys( $pageProps ), array_keys( $actual ) );
}
public function diffProvider() {
$empty = $this->newEmpty( $this->getDummyId() );
$spam = $this->newEmpty( $this->getDummyId() );
$this->setLabel( $spam->getEntity(), 'en', 'Spam' );
$ham = $this->newEmpty( $this->getDummyId() );
$this->setLabel( $ham->getEntity(), 'en', 'Ham' );
$spamToHam = new DiffOpChange( 'Spam', 'Ham' );
$spamToHamDiff = new EntityDiff( array(
'label' => new Diff( array( 'en' => $spamToHam ) ),
) );
return array(
'empty' => array( $empty, $empty, new EntityContentDiff( new EntityDiff(), new Diff() ) ),
'same' => array( $ham, $ham, new EntityContentDiff( new EntityDiff(), new Diff() ) ),
'spam to ham' => array( $spam, $ham, new EntityContentDiff( $spamToHamDiff, new Diff() ) ),
);
}
/**
* @dataProvider diffProvider
*
* @param EntityContent $a
* @param EntityContent $b
* @param EntityContentDiff $expected
*/
public function testGetDiff( EntityContent $a, EntityContent $b, EntityContentDiff $expected ) {
$actual = $a->getDiff( $b );
$this->assertInstanceOf( EntityContentDiff::class, $actual );
$expectedEntityOps = $expected->getEntityDiff()->getOperations();
$actualEntityOps = $actual->getEntityDiff()->getOperations();
// HACK: ItemDiff always sets this, even if it's empty. Ignore.
if ( isset( $actualEntityOps['claim'] ) && $actualEntityOps['claim']->isEmpty() ) {
unset( $actualEntityOps['claim'] );
}
$this->assertArrayEquals( $expectedEntityOps, $actualEntityOps, false, true );
$expectedRedirectOps = $expected->getRedirectDiff()->getOperations();
$actualRedirectOps = $actual->getRedirectDiff()->getOperations();
$this->assertArrayEquals( $expectedRedirectOps, $actualRedirectOps, false, true );
}
public function patchedCopyProvider() {
$spam = $this->newEmpty( $this->getDummyId() );
$this->setLabel( $spam->getEntity(), 'en', 'Spam' );
$ham = $this->newEmpty( $this->getDummyId() );
$this->setLabel( $ham->getEntity(), 'en', 'Ham' );
$spamToHam = new DiffOpChange( 'Spam', 'Ham' );
$spamToHamDiff = new EntityDiff( array(
'label' => new Diff( array( 'en' => $spamToHam ) ),
) );
return array(
'empty' => array( $spam, new EntityContentDiff( new EntityDiff(), new Diff() ), $spam ),
'spam to ham' => array( $spam, new EntityContentDiff( $spamToHamDiff, new Diff() ), $ham ),
);
}
/**
* @dataProvider patchedCopyProvider
*
* @param EntityContent $base
* @param EntityContentDiff $patch
* @param EntityContent|null $expected
*/
public function testGetPatchedCopy( EntityContent $base, EntityContentDiff $patch, EntityContent $expected = null ) {
if ( $expected === null ) {
$this->setExpectedException( PatcherException::class );
}
$actual = $base->getPatchedCopy( $patch );
if ( $expected !== null ) {
$this->assertTrue( $expected->equals( $actual ), 'equals()' );
}
}
public function copyProvider() {
$empty = $this->newEmpty();
$labels = $this->newEmpty();
$this->setLabel( $labels->getEntity(), 'en', 'Foo' );
return array(
'empty' => array( $empty ),
'labels' => array( $labels ),
);
}
/**
* @dataProvider copyProvider
* @param EntityContent $content
*/
public function testCopy( EntityContent $content ) {
$copy = $content->copy();
$this->assertNotSame( $content, $copy, 'Copy must not be the same instance.' );
$this->assertTrue( $content->equals( $copy ), 'Copy must be equal to the original.' );
$this->assertSame( get_class( $content ), get_class( $copy ), 'Copy must have the same type.' );
$this->assertEquals( $content->getNativeData(), $copy->getNativeData(), 'Copy must have the same data.' );
}
public function equalsProvider() {
$empty = $this->newEmpty();
$labels1 = $this->newEmpty();
$this->setLabel( $labels1->getEntity(), 'en', 'Foo' );
$labels2 = $this->newEmpty();
$this->setLabel( $labels2->getEntity(), 'de', 'Foo' );
return array(
'empty' => array( $empty, $empty, true ),
'same labels' => array( $labels1, $labels1, true ),
'different labels' => array( $labels1, $labels2, false ),
);
}
/**
* @dataProvider equalsProvider
*/
public function testEquals( EntityContent $a, EntityContent $b, $equals ) {
$actual = $a->equals( $b );
$this->assertEquals( $equals, $actual );
$actual = $b->equals( $a );
$this->assertEquals( $equals, $actual );
}
private function createTitleForEntity( EntityDocument $entity ) {
// NOTE: needs database access
$this->entityStore->assignFreshId( $entity );
$titleLookup = WikibaseRepo::getDefaultInstance()->getEntityTitleLookup();
$title = $titleLookup->getTitleForId( $entity->getId() );
if ( !$title->exists( Title::GAID_FOR_UPDATE ) ) {
$store = WikibaseRepo::getDefaultInstance()->getEntityStore();
$store->saveEntity( $entity, 'test', $GLOBALS['wgUser'] );
// $title lies, make a new one
$title = Title::makeTitleSafe( $title->getNamespace(), $title->getText() );
}
// sanity check - page must exist now
$this->assertTrue( $title->exists( Title::GAID_FOR_UPDATE ), 'sanity check: exists()' );
return $title;
}
public function testGetSecondaryDataUpdates() {
$entityContent = $this->newEmpty();
$title = $this->createTitleForEntity( $entityContent->getEntity() );
// NOTE: $title->exists() must be true.
$updates = $entityContent->getSecondaryDataUpdates( $title );
$this->assertDataUpdates( $updates );
}
public function testGetDeletionUpdates() {
$entityContent = $this->newEmpty();
$title = $this->createTitleForEntity( $entityContent->getEntity() );
$updates = $entityContent->getDeletionUpdates( new WikiPage( $title ) );
$this->assertDataUpdates( $updates );
}
private function assertDataUpdates( $updates ) {
$this->assertInternalType( 'array', $updates );
$this->assertContainsOnlyInstancesOf( 'DataUpdate', $updates );
}
public function entityRedirectProvider() {
return array(
'empty' => array( $this->newEmpty(), null ),
);
}
/**
* @dataProvider entityRedirectProvider
*/
public function testGetEntityRedirect( EntityContent $content, EntityRedirect $redirect = null ) {
$this->assertEquals( $content->getEntityRedirect(), $redirect );
if ( $redirect === null ) {
$this->assertNull( $content->getRedirectTarget() );
} else {
$this->assertNotNull( $content->getRedirectTarget() );
}
}
}