jenkins-bot has submitted this change and it was merged.
Change subject: Filter page purged by usage aspect.
......................................................................
Filter page purged by usage aspect.
Bug: 71352
Change-Id: I9ce6a2e90347f6ce8a9ce9ef4d0a083592211d23
---
M client/includes/Changes/AffectedPagesFinder.php
M client/includes/Changes/ChangeHandler.php
M client/includes/DataAccess/PropertyParserFunction/Runner.php
M client/includes/Usage/EntityUsage.php
M client/includes/Usage/HashUsageAccumulator.php
A client/includes/Usage/PageEntityUsages.php
M client/includes/Usage/ParserOutputUsageAccumulator.php
M client/includes/Usage/Sql/SqlUsageTracker.php
M client/includes/Usage/UsageAccumulator.php
A client/includes/Usage/UsageAspectTransformer.php
M client/includes/Usage/UsageLookup.php
M client/includes/WikibaseClient.php
M client/includes/scribunto/WikibaseLuaBindings.php
M client/tests/phpunit/includes/Changes/AffectedPagesFinderTest.php
M client/tests/phpunit/includes/Changes/ChangeHandlerTest.php
M client/tests/phpunit/includes/DataAccess/PropertyParserFunction/RunnerTest.php
A client/tests/phpunit/includes/Usage/PageEntityUsagesTest.php
M client/tests/phpunit/includes/Usage/UsageAccumulatorContractTester.php
A client/tests/phpunit/includes/Usage/UsageAspectTransformerTest.php
M client/tests/phpunit/includes/Usage/UsageLookupContractTester.php
20 files changed, 1,203 insertions(+), 218 deletions(-)
Approvals:
Thiemo Mättig (WMDE): Looks good to me, approved
jenkins-bot: Verified
diff --git a/client/includes/Changes/AffectedPagesFinder.php
b/client/includes/Changes/AffectedPagesFinder.php
index 39df103..8355d35 100644
--- a/client/includes/Changes/AffectedPagesFinder.php
+++ b/client/includes/Changes/AffectedPagesFinder.php
@@ -2,17 +2,27 @@
namespace Wikibase\Client\Changes;
+use ArrayIterator;
use Diff\DiffOp\Diff\Diff;
+use Diff\DiffOp\DiffOp;
use Diff\DiffOp\DiffOpAdd;
use Diff\DiffOp\DiffOpChange;
use Diff\DiffOp\DiffOpRemove;
+use InvalidArgumentException;
use Iterator;
use Title;
use UnexpectedValueException;
use Wikibase\Change;
use Wikibase\Client\Store\TitleFactory;
+use Wikibase\Client\Usage\EntityUsage;
+use Wikibase\Client\Usage\PageEntityUsages;
+use Wikibase\Client\Usage\UsageAspectTransformer;
use Wikibase\Client\Usage\UsageLookup;
+use Wikibase\EntityChange;
use Wikibase\ItemChange;
+use Wikibase\DataModel\Entity\Diff\EntityDiff;
+use Wikibase\DataModel\Entity\Diff\ItemDiff;
+use Wikibase\DataModel\Entity\EntityId;
use Wikibase\Lib\Store\StorageException;
use Wikibase\NamespaceChecker;
@@ -40,6 +50,11 @@
private $siteId;
/**
+ * @var string
+ */
+ private $contentLanguageCode;
+
+ /**
* @var boolean
*/
private $checkPageExistence;
@@ -54,18 +69,35 @@
* @param NamespaceChecker $namespaceChecker
* @param TitleFactory $titleFactory
* @param string $siteId
+ * @param string $contentLanguageCode
* @param boolean $checkPageExistence
+ *
+ * @throws InvalidArgumentException
*/
public function __construct(
UsageLookup $usageLookup,
NamespaceChecker $namespaceChecker,
TitleFactory $titleFactory,
$siteId,
+ $contentLanguageCode,
$checkPageExistence = true
) {
+ if ( !is_string( $siteId ) ) {
+ throw new InvalidArgumentException( '$siteId must be a
string' );
+ }
+
+ if ( !is_string( $contentLanguageCode ) ) {
+ throw new InvalidArgumentException(
'$contentLanguageCode must be a string' );
+ }
+
+ if ( !is_bool( $checkPageExistence ) ) {
+ throw new InvalidArgumentException(
'$checkPageExistence must be a boolean' );
+ }
+
$this->usageLookup = $usageLookup;
$this->namespaceChecker = $namespaceChecker;
$this->siteId = $siteId;
+ $this->contentLanguageCode = $contentLanguageCode;
$this->checkPageExistence = $checkPageExistence;
$this->titleFactory = $titleFactory;
}
@@ -75,50 +107,112 @@
*
* @param Change $change
*
- * @return Title[]
+ * @return Iterator<PageEntityUsages>
*/
- public function getPages( Change $change ) {
- if ( ! ( $change instanceof ItemChange ) ) {
+ public function getAffectedUsagesByPage( Change $change ) {
+ if ( ! ( $change instanceof EntityChange ) ) {
return array();
}
- $titles = $this->getReferencedPages( $change );
+ $pageUpdates = $this->getAffectedPages( $change );
+ $pageUpdates = $this->filterUpdates( $pageUpdates );
- return $this->filterTitlesToUpdate( $titles );
+ return $pageUpdates;
}
/**
- * Returns the pages that need some kind of updating given the change.
+ * @param EntityChange $change
*
- * @param ItemChange $change
- *
- * @return Title[] the titles of the pages to update. May contain
duplicates.
+ * @return string[]
*/
- private function getReferencedPages( ItemChange $change ) {
- $itemId = $change->getEntityId();
+ public function getChangedAspects( EntityChange $change ) {
+ $aspects = array();
- $pageIds = $this->usageLookup->getPagesUsing( array( $itemId )
);
- $titles = $this->getTitlesFromIDs( $pageIds );
+ /** @var EntityDiff $diff */
+ $diff = $change->getDiff();
+ $remainingDiffOps = count( $diff ); // this is a "deep" count!
- $siteLinkDiff = $change->getSiteLinkDiff();
+ if ( $diff instanceof ItemDiff &&
!$diff->getSiteLinkDiff()->isEmpty() ) {
+ $sitelinkDiff = $diff->getSiteLinkDiff();
- if ( $this->isRelevantSiteLinkChange( $siteLinkDiff ) ) {
- $namesFromDiff = $this->getPagesReferencedInDiff(
$siteLinkDiff );
- $titlesFromDiff = $this->getTitlesFromTexts(
$namesFromDiff );
+ $aspects[] = EntityUsage::SITELINK_USAGE;
+ $remainingDiffOps-= count( $sitelinkDiff );
- $titles = array_merge( $titles, $titlesFromDiff );
+ if ( isset( $sitelinkDiff[$this->siteId] ) &&
!$this->isBadgesOnlyChange( $sitelinkDiff[$this->siteId] ) ) {
+ $aspects[] = EntityUsage::TITLE_USAGE;
+ }
}
- return $titles;
+ if ( !$diff->getLabelsDiff()->isEmpty() ) {
+ $labelDiff = $diff->getLabelsDiff();
+
+ if ( isset( $labelDiff[$this->contentLanguageCode] ) ) {
+ $aspects[] = EntityUsage::LABEL_USAGE;
+ $remainingDiffOps--;
+ }
+ }
+
+ if ( $remainingDiffOps > 0 ) {
+ $aspects[] = EntityUsage::OTHER_USAGE;
+ }
+
+ sort( $aspects );
+ return $aspects;
}
/**
- * @param Diff $siteLinkDiff
+ * Returns the page updates implied by the given the change.
*
- * @return boolean
+ * @param EntityChange $change
+ *
+ * @return Iterator<PageEntityUsages>
*/
- private function isRelevantSiteLinkChange( Diff $siteLinkDiff ) {
- return isset( $siteLinkDiff[$this->siteId] ) &&
!$this->isBadgesOnlyChange( $siteLinkDiff );
+ private function getAffectedPages( EntityChange $change ) {
+ $itemId = $change->getEntityId();
+ $changedAspects = $this->getChangedAspects( $change );
+
+ // @todo: more than one item at once!
+ $relevantAspects = array_merge( array( 'X' ), $changedAspects
); // X implies all!
+ $usages = $this->usageLookup->getPagesUsing( array( $itemId ),
$relevantAspects );
+
+ // @todo: use iterators throughout!
+ $usages = iterator_to_array( $usages, true );
+
+ $usages = $this->transformAllPageEntityUsages( $usages,
$itemId, $changedAspects );
+
+ if ( $change instanceof ItemChange && in_array(
EntityUsage::TITLE_USAGE, $changedAspects ) ) {
+ $siteLinkDiff = $change->getSiteLinkDiff();
+ $namesFromDiff = $this->getPagesReferencedInDiff(
$siteLinkDiff );
+ $titlesFromDiff = $this->getTitlesFromTexts(
$namesFromDiff );
+ $usagesFromDiff = $this->makeVirtualUsages(
$titlesFromDiff, $itemId, array( EntityUsage::SITELINK_USAGE ) );
+
+ //FIXME: we can't really merge if $usages is an
iterator, not an array.
+ //TODO: Inject $usagesFromDiff "on the fly" while
streaming other usages.
+ //NOTE: $usages must pass through mergeUsagesInto for
re-indexing
+ $mergedUsages = array();
+ $this->mergeUsagesInto( $usages, $mergedUsages );
+ $this->mergeUsagesInto( $usagesFromDiff, $mergedUsages
);
+ $usages = $mergedUsages;
+ }
+
+ return new ArrayIterator( $usages );
+ }
+
+ /**
+ * @param PageEntityUsages[] $from PageEntityUsages
+ * @param PageEntityUsages[] &$into Array to merge into
+ */
+ private function mergeUsagesInto( array $from, array &$into ) {
+ /** @var PageEntityUsages $pageEntityUsages */
+ foreach ( $from as $pageEntityUsages ) {
+ $key = $pageEntityUsages->getPageId();
+
+ if ( isset( $into[$key] ) ) {
+ $into[$key]->addUsages(
$pageEntityUsages->getUsages() );
+ } else {
+ $into[$key] = $pageEntityUsages;
+ }
+ }
}
/**
@@ -155,28 +249,29 @@
}
/**
- * @param Diff $siteLinkDiff
+ * @param DiffOp $siteLinkDiffOp
*
* @return boolean
*/
- private function isBadgesOnlyChange( Diff $siteLinkDiff ) {
- $siteLinkDiffOp = $siteLinkDiff[$this->siteId];
+ private function isBadgesOnlyChange( DiffOp $siteLinkDiffOp ) {
return ( $siteLinkDiffOp instanceof Diff && !array_key_exists(
'name', $siteLinkDiffOp ) );
}
/**
- * Filters titles to update. This removes duplicates, non-existing
pages, and pages from
+ * Filters updates based on namespace. This removes duplicates,
non-existing pages, and pages from
* namespaces that are not considered "enabled" by the namespace
checker.
*
- * @param Title[] $titles
+ * @param PageEntityUsages[]|Iterator<PageEntityUsages> $updates
*
- * @return Title[]
+ * @return Iterator<PageEntityUsages>
*/
- private function filterTitlesToUpdate( array $titles ) {
+ private function filterUpdates( $updates ) {
$titlesToUpdate = array();
- foreach ( $titles as $title ) {
+ foreach ( $updates as $pageUpdates ) {
+ $title = $this->titleFactory->newFromID(
$pageUpdates->getPageId() );
+
if ( $this->checkPageExistence && !$title->exists() ) {
continue;
}
@@ -187,31 +282,11 @@
continue;
}
- // Use the string representation as a key to get rid of
any duplicates.
- $key = $title->getPrefixedDBkey();
- $titlesToUpdate[$key] = $title;
+ $key = $title->getArticleID();
+ $titlesToUpdate[$key] = $pageUpdates;
}
- return $titlesToUpdate;
- }
-
- /**
- * @param int[]|Iterator $pageIds
- *
- * @return Title[]
- */
- private function getTitlesFromIDs( $pageIds ) {
- $titles = array();
-
- foreach ( $pageIds as $id ) {
- try {
- $titles[] = $this->titleFactory->newFromID( $id
);
- } catch ( StorageException $ex ) {
- // Page probably got deleted just now. Skip it.
- }
- }
-
- return $titles;
+ return new ArrayIterator( $titlesToUpdate );
}
/**
@@ -233,4 +308,42 @@
return $titles;
}
+ /**
+ * @param Title[] $titles
+ * @param EntityId $entityId
+ * @param string[] $aspects
+ *
+ * @return PageEntityUsages[]
+ */
+ private function makeVirtualUsages( array $titles, EntityId $entityId,
array $aspects ) {
+ $usagesForItem = array();
+ foreach ( $aspects as $aspect ) {
+ $usagesForItem[] = new EntityUsage( $entityId, $aspect
);
+ }
+
+ $usagesPerPage = array();
+ foreach ( $titles as $title ) {
+ $pid = $title->getArticleID();
+ $usagesPerPage[$pid] = new PageEntityUsages( $pid,
$usagesForItem );
+ }
+
+ return $usagesPerPage;
+ }
+
+ private function transformAllPageEntityUsages( array $usages, EntityId
$entityId, array $changedAspects ) {
+ $aspectTransformer = new UsageAspectTransformer();
+ $aspectTransformer->setRelevantAspects( $entityId,
$changedAspects );
+
+ $transformed = array();
+
+ foreach( $usages as $key => $usagesOnPage ) {
+ $transformedUsagesOnPage =
$aspectTransformer->transformPageEntityUsages( $usagesOnPage );
+ if ( !$transformedUsagesOnPage->isEmpty() ) {
+ $transformed[$key] = $transformedUsagesOnPage;
+ }
+ }
+
+ return $transformed;
+ }
+
}
diff --git a/client/includes/Changes/ChangeHandler.php
b/client/includes/Changes/ChangeHandler.php
index 037dfcd..57e3082 100644
--- a/client/includes/Changes/ChangeHandler.php
+++ b/client/includes/Changes/ChangeHandler.php
@@ -6,11 +6,13 @@
use MWException;
use Title;
use Wikibase\Change;
+use Wikibase\Client\Store\TitleFactory;
use Wikibase\DataModel\Entity\Diff\EntityDiff;
use Wikibase\DataModel\Entity\Diff\ItemDiff;
use Wikibase\EntityChange;
use Wikibase\ItemChange;
use Wikibase\SiteLinkCommentCreator;
+use Wikibase\Lib\Store\StorageException;
/**
* Interface for change handling. Whenever a change is detected,
@@ -51,6 +53,16 @@
const HISTORY_ENTRY_ACTION = 16;
/**
+ * @var AffectedPagesFinder
+ */
+ private $affectedPagesFinder;
+
+ /**
+ * @var TitleFactory
+ */
+ private $titleFactory;
+
+ /**
* @var PageUpdater $updater
*/
private $updater;
@@ -61,17 +73,13 @@
private $changeListTransformer;
/**
- * @var AffectedPagesFinder
- */
- private $affectedPagesFinder;
-
- /**
* @var string
*/
private $localSiteId;
public function __construct(
AffectedPagesFinder $affectedPagesFinder,
+ TitleFactory $titleFactory,
PageUpdater $updater,
ChangeListTransformer $changeListTransformer,
$localSiteId,
@@ -80,6 +88,7 @@
) {
$this->changeListTransformer = $changeListTransformer;
$this->affectedPagesFinder = $affectedPagesFinder;
+ $this->titleFactory = $titleFactory;
$this->updater = $updater;
if ( !is_string( $localSiteId ) ) {
@@ -185,7 +194,8 @@
public function getPagesToUpdate( Change $change ) {
wfProfileIn( __METHOD__ );
- $pagesToUpdate = $this->affectedPagesFinder->getPages( $change
);
+ $usages = $this->affectedPagesFinder->getAffectedUsagesByPage(
$change );
+ $pagesToUpdate = $this->getTitlesFromPageEntityUsages( $usages
);
wfProfileOut( __METHOD__ );
@@ -193,6 +203,26 @@
}
/**
+ * @param PageEntityUsages[]|Iterator<PageEntityUsages> $pageIds
+ *
+ * @return Title[]
+ */
+ private function getTitlesFromPageEntityUsages( $usages ) {
+ $titles = array();
+
+ foreach ( $usages as $pageEntityUsages ) {
+ try {
+ $pid = $pageEntityUsages->getPageId();
+ $titles[] = $this->titleFactory->newFromID(
$pid );
+ } catch ( StorageException $ex ) {
+ // Page probably got deleted just now. Skip it.
+ }
+ }
+
+ return $titles;
+ }
+
+ /**
* Main entry point for handling changes
*
* @since 0.4
diff --git a/client/includes/DataAccess/PropertyParserFunction/Runner.php
b/client/includes/DataAccess/PropertyParserFunction/Runner.php
index 978904a..1779af9 100644
--- a/client/includes/DataAccess/PropertyParserFunction/Runner.php
+++ b/client/includes/DataAccess/PropertyParserFunction/Runner.php
@@ -75,9 +75,9 @@
$rendered = $renderer->render( $entityId, $propertyLabelOrId );
$result = $this->buildResult( $rendered );
- // Track usage of "all" (that is, arbitrary) data from the item.
+ // Track usage of "other" (that is, not label/title/sitelinks)
data from the item.
$usageAcc = new ParserOutputUsageAccumulator(
$parser->getOutput() );
- $usageAcc->addAllUsage( $entityId );
+ $usageAcc->addOtherUsage( $entityId );
wfProfileOut( __METHOD__ );
return $result;
diff --git a/client/includes/Usage/EntityUsage.php
b/client/includes/Usage/EntityUsage.php
index 40725df..ab2fbe7 100644
--- a/client/includes/Usage/EntityUsage.php
+++ b/client/includes/Usage/EntityUsage.php
@@ -43,6 +43,14 @@
const ALL_USAGE = 'X';
/**
+ * Usage flag indicating that some aspect of the entity was changed
+ * which is not covered by any other usage flag (except "all"). That is,
+ * the specific usage flags together with the "other" flag are
equivalent
+ * to the "all" flag ( S + T + L + O = X or rather O = X - S - T - L ).
+ */
+ const OTHER_USAGE = 'O';
+
+ /**
* A list of all valid aspects
*
* @var array
@@ -51,6 +59,7 @@
self::SITELINK_USAGE,
self::LABEL_USAGE,
self::TITLE_USAGE,
+ self::OTHER_USAGE,
self::ALL_USAGE
);
diff --git a/client/includes/Usage/HashUsageAccumulator.php
b/client/includes/Usage/HashUsageAccumulator.php
index 7302851..7bc0637 100644
--- a/client/includes/Usage/HashUsageAccumulator.php
+++ b/client/includes/Usage/HashUsageAccumulator.php
@@ -41,7 +41,7 @@
}
/**
- * Registers the usage an entity's label (in the local content
language).
+ * @see UsageAccumulator::addLabelUsage
*
* @param EntityId $id
*/
@@ -50,17 +50,16 @@
}
/**
- * Registers the usage of an entity's local page title, e.g. to refer to
- * the corresponding page on the local wiki.
+ * @see UsageAccumulator::addTitleUsage
*
* @param EntityId $id
*/
- public function addPageUsage( EntityId $id ) {
+ public function addTitleUsage( EntityId $id ) {
$this->addUsage( $id, EntityUsage::TITLE_USAGE );
}
/**
- * Registers the usage of an entity's sitelinks, e.g. to generate
language links.
+ * @see UsageAccumulator::addSitelinksUsage
*
* @param EntityId $id
*/
@@ -69,8 +68,16 @@
}
/**
- * Registers the usage of other or all data of an entity (e.g. when
accessed
- * programmatically using Lua).
+ * @see UsageAccumulator::addOtherUsage
+ *
+ * @param EntityId $id
+ */
+ public function addOtherUsage( EntityId $id ) {
+ $this->addUsage( $id, EntityUsage::OTHER_USAGE );
+ }
+
+ /**
+ * @see UsageAccumulator::addAllUsage
*
* @param EntityId $id
*/
diff --git a/client/includes/Usage/PageEntityUsages.php
b/client/includes/Usage/PageEntityUsages.php
new file mode 100644
index 0000000..4eca198
--- /dev/null
+++ b/client/includes/Usage/PageEntityUsages.php
@@ -0,0 +1,162 @@
+<?php
+
+namespace Wikibase\Client\Usage;
+
+use InvalidArgumentException;
+use Wikibase\DataModel\Entity\EntityId;
+
+/**
+ * Value object representing the entity usages on a single page.
+ *
+ * @license GPL 2+
+ * @author Daniel Kinzler
+ */
+class PageEntityUsages {
+
+ /**
+ * @var int
+ */
+ private $pageId;
+
+ /**
+ * @var EntityUsage[]
+ */
+ private $usages = array();
+
+ /**
+ * @param int $pageId
+ * @param EntityUsage[] $usages
+ *
+ * @throws InvalidArgumentException
+ */
+ public function __construct( $pageId, array $usages = array() ) {
+ if ( !is_int( $pageId ) || $pageId < 1 ) {
+ throw new InvalidArgumentException( '$pageId must be an
integer > 0' );
+ }
+
+ $this->pageId = $pageId;
+ $this->addUsages( $usages );
+ }
+
+ /**
+ * Returns the page this PageEntityUsages object applies to.
+ *
+ * @return int
+ */
+ public function getPageId() {
+ return $this->pageId;
+ }
+
+ /**
+ * @return EntityUsage[] $usages EntityUsage objects keyed and sorted
by identity string.
+ */
+ public function getUsages() {
+ return $this->usages;
+ }
+
+ /**
+ * @return bool
+ */
+ public function isEmpty() {
+ return empty( $this->usages );
+ }
+
+ /**
+ * @param array $usages
+ *
+ * @throws InvalidArgumentException
+ */
+ public function addUsages( array $usages ) {
+ foreach ( $usages as $usage ) {
+ if ( !$usage instanceof EntityUsage ) {
+ throw new InvalidArgumentException( '$usages
must contain only EntityUsage objects' );
+ }
+
+ $key = $usage->getIdentityString();
+ $this->usages[$key] = $usage;
+ }
+
+ ksort( $this->usages );
+ }
+
+ /**
+ * Collects all usage aspects present on the page.
+ *
+ * string[] aspect names (sorted)
+ */
+ public function getAspects() {
+ $aspects = array();
+
+ foreach ( $this->usages as $usage ) {
+ $aspect = $usage->getAspect();
+ $aspects[$aspect] = true;
+ }
+
+ ksort( $aspects );
+ return array_keys( $aspects );
+ }
+
+ /**
+ * @param PageEntityUsages $other
+ *
+ * @return bool
+ */
+ public function equals( PageEntityUsages $other ) {
+ if ( !$other->getPageId() === $this->getPageId() ) {
+ return false;
+ } elseif( array_keys( $other->getUsages() ) != array_keys(
$this->getUsages() ) ) {
+ return false;
+ }
+
+ return true;
+ }
+
+
+ /**
+ * Returns all entities used on the page represented by this
PageEntityUsages object.
+ *
+ * @return EntityId[] List of EntityIde objects, keyed and sorted by
their identity string.
+ */
+ public function getEntityIds() {
+ $entityIds = array();
+
+ foreach ( $this->usages as $usage ) {
+ $id = $usage->getEntityId();
+ $key = $id->getSerialization();
+ $entityIds[$key] = $id;
+ }
+
+ ksort( $entityIds );
+ return $entityIds;
+ }
+
+ /**
+ * Returns the aspects used by the given entity on the page
+ * represented by this PageEntityUsages object.
+ *
+ * @param EntityId $id
+ *
+ * @return string[] List of aspect names, sorted.
+ */
+ public function getUsageAspects( EntityId $id ) {
+ $aspects = array();
+
+ foreach ( $this->usages as $usage ) {
+ if ( $id->equals( $usage->getEntityId() ) ) {
+ $aspects[] = $usage->getAspect();
+ }
+ }
+
+ sort( $aspects );
+ return $aspects;
+ }
+
+ public function __toString() {
+ $s = 'Page ' . $this->getPageId() . ' uses (';
+ $s .= implode( '|', array_keys( $this->getUsages() ) );
+ $s .= ')';
+
+ return $s;
+ }
+
+}
diff --git a/client/includes/Usage/ParserOutputUsageAccumulator.php
b/client/includes/Usage/ParserOutputUsageAccumulator.php
index 4eb5c69..59b1528 100644
--- a/client/includes/Usage/ParserOutputUsageAccumulator.php
+++ b/client/includes/Usage/ParserOutputUsageAccumulator.php
@@ -51,7 +51,7 @@
}
/**
- * Registers the usage an entity's label (in the local content
language).
+ * @see UsageAccumulator::addLabelUsage
*
* @param EntityId $id
*/
@@ -60,17 +60,16 @@
}
/**
- * Registers the usage of an entity's local page title, e.g. to refer to
- * the corresponding page on the local wiki.
+ * @see UsageAccumulator::addTitleUsage
*
* @param EntityId $id
*/
- public function addPageUsage( EntityId $id ) {
+ public function addTitleUsage( EntityId $id ) {
$this->addUsage( $id, EntityUsage::TITLE_USAGE );
}
/**
- * Registers the usage of an entity's sitelinks, e.g. to generate
language links.
+ * @see UsageAccumulator::addSitelinksUsage
*
* @param EntityId $id
*/
@@ -79,8 +78,16 @@
}
/**
- * Registers the usage of other or all data of an entity (e.g. when
accessed
- * programmatically using Lua).
+ * @see UsageAccumulator::addOtherUsage
+ *
+ * @param EntityId $id
+ */
+ public function addOtherUsage( EntityId $id ) {
+ $this->addUsage( $id, EntityUsage::OTHER_USAGE );
+ }
+
+ /**
+ * @see UsageAccumulator::addAllUsage
*
* @param EntityId $id
*/
diff --git a/client/includes/Usage/Sql/SqlUsageTracker.php
b/client/includes/Usage/Sql/SqlUsageTracker.php
index b29c94b..98720a1 100644
--- a/client/includes/Usage/Sql/SqlUsageTracker.php
+++ b/client/includes/Usage/Sql/SqlUsageTracker.php
@@ -10,6 +10,7 @@
use Iterator;
use Wikibase\Client\Store\Sql\ConnectionManager;
use Wikibase\Client\Usage\EntityUsage;
+use Wikibase\Client\Usage\PageEntityUsages;
use Wikibase\Client\Usage\UsageLookup;
use Wikibase\Client\Usage\UsageTracker;
use Wikibase\Client\Usage\UsageTrackerException;
@@ -221,7 +222,7 @@
* @param EntityId[] $entityIds
* @param string[] $aspects
*
- * @return Iterator An iterator over page IDs.
+ * @return Iterator<PageEntityUsages> An iterator over entity usages
grouped by page
* @throws UsageTrackerException
*/
public function getPagesUsing( array $entityIds, array $aspects =
array() ) {
@@ -240,12 +241,12 @@
$res = $db->select(
'wbc_entity_usage',
- array( 'DISTINCT eu_page_id' ),
+ array( 'eu_page_id', 'eu_entity_id', 'eu_aspect' ),
$where,
__METHOD__
);
- $pages = $this->extractProperty( $res, 'eu_page_id' );
+ $pages = $this->foldRowsIntoPageEntityUsages( $res );
$this->connectionManager->releaseConnection( $db );
@@ -254,6 +255,34 @@
}
/**
+ * @param array|Iterator $rows
+ *
+ * @return PageEntityUsages[]
+ */
+ private function foldRowsIntoPageEntityUsages( $rows ) {
+ $usagesPerPage = array();
+
+ foreach ( $rows as $row ) {
+ $pageId = (int)$row->eu_page_id;
+
+ if ( isset( $usagesPerPage[$pageId] ) ) {
+ $pageEntityUsages = $usagesPerPage[$pageId];
+ } else {
+ $pageEntityUsages = new PageEntityUsages(
$pageId );
+ }
+
+ $entityId = $this->idParser->parse( $row->eu_entity_id
);
+ $usage = new EntityUsage( $entityId, $row->eu_aspect );
+ $pageEntityUsages->addUsages( array( $usage ) );
+
+ $usagesPerPage[$pageId] = $pageEntityUsages;
+ }
+
+ return $usagesPerPage;
+ }
+
+
+ /**
* @see UsageTracker::getUnusedEntities
*
* @param EntityId[] $entityIds
diff --git a/client/includes/Usage/UsageAccumulator.php
b/client/includes/Usage/UsageAccumulator.php
index 364668c..d6ff542 100644
--- a/client/includes/Usage/UsageAccumulator.php
+++ b/client/includes/Usage/UsageAccumulator.php
@@ -25,7 +25,7 @@
*
* @param EntityId $id
*/
- public function addPageUsage( EntityId $id );
+ public function addTitleUsage( EntityId $id );
/**
* Registers the usage of an entity's sitelinks, e.g. to generate
language links.
@@ -35,7 +35,16 @@
public function addSiteLinksUsage( EntityId $id );
/**
- * Registers the usage of other or all data of an entity (e.g. when
accessed
+ * Registers the usage of other (i.e. not label, sitelink, or title) of
an
+ * entity (e.g. access to statements or labels in labels a language
other
+ * than the content language).
+ *
+ * @param EntityId $id
+ */
+ public function addOtherUsage( EntityId $id );
+
+ /**
+ * Registers the usage of any/all data of an entity (e.g. when accessed
* programmatically using Lua).
*
* @param EntityId $id
diff --git a/client/includes/Usage/UsageAspectTransformer.php
b/client/includes/Usage/UsageAspectTransformer.php
new file mode 100644
index 0000000..83df0ad
--- /dev/null
+++ b/client/includes/Usage/UsageAspectTransformer.php
@@ -0,0 +1,147 @@
+<?php
+
+namespace Wikibase\Client\Usage;
+
+use Wikibase\DataModel\Entity\EntityId;
+
+/**
+ * Transforms usage aspect based on a filter of aspects relevant in some
context.
+ * Relevant aspects for each entity are collected using the
setRelevantAspects()
+ * method.
+ *
+ * @example: If a page uses the "label" (L) and "title" (T) aspects of item
Q1, a
+ * UsageAspectTransformer that was set up to consider the label aspect of Q1
+ * to be relevant will transform the usage Q1#L + Q1#T to the "relevant" usage
Q1#L.
+ *
+ * @example: The "all" (X) aspect is treated specially: If a page uses the X
aspect,
+ * a UsageAspectTransformer that was constructed to consider e.g. the label
and title
+ * aspects of Q1 to be relevant will transform the usage Q1#X to the "relevant"
+ * usage Q1#L + Q1#T. Conversely, if a page uses the "sitelink" (S) aspect, a
+ * UsageAspectTransformer that was constructed to consider all (X) usages
relevant
+ * will keep the usage Q1#S usage as "relevant".
+ *
+ * @license GPL 2+
+ * @author Daniel Kinzler
+ */
+class UsageAspectTransformer {
+
+ /**
+ * @var array[] An associative array, mapping entity IDs to lists of
aspect names.
+ */
+ private $relevantAspectsPerEntity;
+
+ /**
+ * @param EntityId $entityId
+ * @param array[] $aspects
+ */
+ public function setRelevantAspects( EntityId $entityId, array $aspects
) {
+ $key = $entityId->getSerialization();
+ $this->relevantAspectsPerEntity[$key] = $aspects;
+ }
+
+ /**
+ * @param EntityId $entityId
+ *
+ * @return string[]
+ */
+ public function getRelevantAspects( EntityId $entityId ) {
+ $key = $entityId->getSerialization();
+ return isset( $this->relevantAspectsPerEntity[$key] ) ?
$this->relevantAspectsPerEntity[$key] : array();
+ }
+
+ /**
+ * Gets EntityUsage objects for each aspect in $aspects that is
relevant according to
+ * getRelevantAspects( $entityId ).
+ *
+ * @example: If was called with setRelevantAspects( $q3, array( 'T',
'L' ) ),
+ * getFilteredUsages( $q3, array( 'S', 'L' ) ) will return EntityUsage(
$q3, 'L'),
+ * while getFilteredUsages( $q3, array( 'X' ) ) will return
EntityUsage( $q3, 'T')
+ * and EntityUsage( $q3, 'L').
+ *
+ * @param EntityId $entityId
+ * @param string[] $aspects
+ *
+ * @return EntityUsage[] $usages;
+ */
+ public function getFilteredUsages( EntityId $entityId, array $aspects )
{
+ $relevant = $this->getRelevantAspects( $entityId );
+ $effectiveAspects = $this->getFilteredAspects( $aspects,
$relevant );
+
+ return $this->buildEntityUsages( $entityId, $effectiveAspects );
+ }
+
+ /**
+ * Transforms the entity usages from $pageEntityUsages according to the
relevant
+ * aspects defined by calling setRelevantAspects(). A new
PageEntityUsages
+ * containing the filtered usage list is returned.
+ *
+ * @see getFilteredUsages()
+ *
+ * @param PageEntityUsages $pageEntityUsages
+ *
+ * @return PageEntityUsages
+ */
+ public function transformPageEntityUsages( PageEntityUsages
$pageEntityUsages ) {
+ $entityIds = $pageEntityUsages->getEntityIds();
+ $transformedPageEntityUsages = new PageEntityUsages(
$pageEntityUsages->getPageId(), array() );
+
+ foreach ( $entityIds as $id ) {
+ $aspects = $pageEntityUsages->getUsageAspects( $id );
+ $usages = $this->getFilteredUsages( $id, $aspects );
+ $transformedPageEntityUsages->addUsages( $usages );
+ }
+
+ return $transformedPageEntityUsages;
+ }
+
+ /**
+ * @param EntityId $entityId
+ * @param array[] $aspects
+ *
+ * @return EntityUsage[]
+ */
+ private function buildEntityUsages( EntityId $entityId, array $aspects
) {
+ $usages = array();
+
+ foreach ( $aspects as $aspect ) {
+ $entityUsage = new EntityUsage( $entityId, $aspect );
+ $key = $entityUsage->getIdentityString();
+
+ $usages[$key] = $entityUsage;
+ }
+
+ ksort( $usages );
+ return $usages;
+ }
+
+ /**
+ * Filter $aspects based on the aspects provided by $relevant,
according to the rules
+ * defined for combining aspects (see class level documentation).
+ *
+ * @note This returns the intersection of $aspects and $relevant,
+ * except if on of the list contains the ALL_USAGE code (X).
+ * If X is present in $aspects, this method will return $relevant (if
"all" is in the
+ * base set, the filtered set will be the filter itself).
+ * If X is present in $relevant, this method returns $aspects (if all
aspects are relevant,
+ * nothing is filtered out).
+ *
+ * @param string[] $aspects
+ * @param string[] $relevant
+ *
+ * @return string[]
+ */
+ private function getFilteredAspects( array $aspects, array $relevant ) {
+ if ( empty( $aspects ) || empty( $relevant ) ) {
+ return array();
+ }
+
+ if ( in_array( 'X', $aspects ) ) {
+ return $relevant;
+ } elseif ( in_array( 'X', $relevant ) ) {
+ return $aspects;
+ }
+
+ return array_intersect( $aspects, $relevant );
+ }
+
+}
diff --git a/client/includes/Usage/UsageLookup.php
b/client/includes/Usage/UsageLookup.php
index 6c93d3d..5f5630d 100644
--- a/client/includes/Usage/UsageLookup.php
+++ b/client/includes/Usage/UsageLookup.php
@@ -32,7 +32,7 @@
* @param string[] $aspects Which aspects to consider (if omitted, all
aspects are considered).
* Use the EntityUsage::XXX_USAGE constants to represent aspects.
*
- * @return Iterator An iterator over the IDs of pages using any of the
given entities.
+ * @return Iterator<PageEntityUsages> An iterator over PageEntityUsages
of pages using any of the given entities.
* If $aspects is given, only usages of these aspects are
included in the result.
* @throws UsageTrackerException
*/
diff --git a/client/includes/WikibaseClient.php
b/client/includes/WikibaseClient.php
index 7ed0f92..ae0dadc 100644
--- a/client/includes/WikibaseClient.php
+++ b/client/includes/WikibaseClient.php
@@ -729,7 +729,7 @@
$this->getNamespaceChecker(),
new TitleFactory(),
$this->settings->getSetting( 'siteGlobalID' ),
- true
+ $this->getContentLanguage()->getCode()
);
}
@@ -741,6 +741,7 @@
return new ChangeHandler(
$this->getAffectedPagesFinder(),
+ new TitleFactory(),
new WikiPageUpdater(),
new ChangeRunCoalescer(
$this->getStore()->getEntityRevisionLookup(),
diff --git a/client/includes/scribunto/WikibaseLuaBindings.php
b/client/includes/scribunto/WikibaseLuaBindings.php
index d1b4d8a..028c91c 100644
--- a/client/includes/scribunto/WikibaseLuaBindings.php
+++ b/client/includes/scribunto/WikibaseLuaBindings.php
@@ -9,6 +9,7 @@
use Wikibase\DataModel\Entity\EntityDocument;
use Wikibase\DataModel\Entity\EntityIdParser;
use Wikibase\DataModel\Entity\EntityIdParsingException;
+use Wikibase\DataModel\Entity\Item;
use Wikibase\DataModel\Entity\ItemId;
use Wikibase\DataModel\Entity\PropertyDataTypeLookup;
use Wikibase\LanguageFallbackChainFactory;
@@ -234,7 +235,7 @@
return null;
}
- $this->usageAccumulator->addPageUsage( $id );
+ $this->usageAccumulator->addTitleUsage( $id );
return $id->getSerialization();
}
@@ -303,12 +304,13 @@
return null;
}
+ /** @var Item $item */
$item = $this->entityLookup->getEntity( $itemId );
if ( !$item || !$item->getSiteLinkList()->hasLinkWithSiteId(
$this->siteId ) ) {
return null;
}
- $this->usageAccumulator->addPageUsage( $itemId );
+ $this->usageAccumulator->addTitleUsage( $itemId );
return $item->getSiteLinkList()->getBySiteId( $this->siteId
)->getPageName();
}
diff --git a/client/tests/phpunit/includes/Changes/AffectedPagesFinderTest.php
b/client/tests/phpunit/includes/Changes/AffectedPagesFinderTest.php
index 20b829c..5d6d768 100644
--- a/client/tests/phpunit/includes/Changes/AffectedPagesFinderTest.php
+++ b/client/tests/phpunit/includes/Changes/AffectedPagesFinderTest.php
@@ -6,6 +6,8 @@
use Title;
use Wikibase\Client\Changes\AffectedPagesFinder;
use Wikibase\Client\Store\TitleFactory;
+use Wikibase\Client\Usage\EntityUsage;
+use Wikibase\Client\Usage\PageEntityUsages;
use Wikibase\DataModel\Entity\Item;
use Wikibase\DataModel\Entity\ItemId;
use Wikibase\DataModel\SiteLink;
@@ -23,10 +25,14 @@
*
* @licence GNU GPL v2+
* @author Katie Filbert < [email protected] >
+ * @author Daniel Kinzler
*/
class AffectedPagesFinderTest extends \MediaWikiTestCase {
/**
+ * Returns a TitleFactory that generates Title objects based on the
assumption
+ * that a page's title is the same as the page's article ID (in decimal
notation).
+ *
* @return TitleFactory
*/
private function getTitleFactory() {
@@ -35,14 +41,9 @@
$titleFactory->expects( $this->any() )
->method( 'newFromID' )
->will( $this->returnCallback( function( $id ) {
- switch ( $id ) {
- case 1:
- return Title::makeTitle(
NS_MAIN, 'Berlin' );
- case 2:
- return Title::makeTitle(
NS_MAIN, 'Rome' );
- default:
- throw new StorageException(
'Unknown ID: ' . $id );
- }
+ $title = Title::makeTitle( NS_MAIN, "$id" );
+ $title->resetArticleID( $id );
+ return $title;
} ) );
$titleFactory->expects( $this->any() )
@@ -54,16 +55,14 @@
throw new StorageException( 'Bad title
text: ' . $text );
}
+ $title->resetArticleID( intval( $text ) );
return $title;
} ) );
return $titleFactory;
}
- /**
- * @dataProvider getPagesProvider
- */
- public function testGetPages( array $expected, array $usage, ItemChange
$change, $message ) {
+ private function getAffectedPagesFinder( array $usage ) {
$usageLookup = $this->getMock(
'Wikibase\Client\Usage\UsageLookup' );
$usageLookup->expects( $this->any() )
@@ -71,7 +70,7 @@
->will( $this->returnValue( new ArrayIterator( $usage )
) );
$namespaceChecker = $this->getMockBuilder(
'Wikibase\NamespaceChecker' )
-
->disableOriginalConstructor()->getMock();
+ ->disableOriginalConstructor()->getMock();
$namespaceChecker->expects( $this->any() )
->method( 'isWikibaseEnabled' )
@@ -79,157 +78,363 @@
$titleFactory = $this->getTitleFactory();
- $referencedPagesFinder = new AffectedPagesFinder(
+ $affectedPagesFinder = new AffectedPagesFinder(
$usageLookup,
$namespaceChecker,
$titleFactory,
'enwiki',
+ 'en',
false
);
- $referencedPages = $referencedPagesFinder->getPages( $change );
- $referencedPageNames = $this->getPrefixedTitles(
$referencedPages );
- $expectedPageNames = $this->getPrefixedTitles( $expected );
-
- $this->assertEquals( $expectedPageNames, $referencedPageNames,
$message );
+ return $affectedPagesFinder;
}
- public function getPagesProvider() {
- $berlin = Title::makeTitle( NS_MAIN, 'Berlin' );
- $rome = Title::makeTitle( NS_MAIN, 'Rome' );
-
+ public function getChangedAspectsProvider() {
$changeFactory = TestChanges::getEntityChangeFactory();
-
$cases = array();
- $cases[] = array(
- array( $berlin ),
- array(),
+ $q1 = new ItemId( 'Q1' );
+ $q2 = new ItemId( 'Q2' );
+
+ $cases['create linked item Q1'] = array(
+ array( EntityUsage::SITELINK_USAGE,
EntityUsage::TITLE_USAGE ),
$changeFactory->newFromUpdate(
ItemChange::ADD,
null,
- $this->getItemWithSiteLinks( array( 'enwiki' =>
'Berlin' ) )
- ),
- 'created item with site link to client'
+ $this->getItemWithSiteLinks( $q1, array(
'enwiki' => '1' ) )
+ )
);
- $cases[] = array(
- array( $berlin ),
- array(),
+ $cases['unlink item Q1'] = array(
+ array( EntityUsage::SITELINK_USAGE,
EntityUsage::TITLE_USAGE ),
$changeFactory->newFromUpdate(
ItemChange::UPDATE,
- $this->getItemWithSiteLinks( array( 'enwiki' =>
'Berlin' ) ),
- $this->getEmptyItem()
- ),
- 'removed site link to client'
+ $this->getItemWithSiteLinks( $q1, array(
'enwiki' => '1' ) ),
+ $this->getEmptyItem( $q1 )
+ )
);
- $cases[] = array(
- array( $rome ),
- array(),
+ $cases['link item Q2'] = array(
+ array( EntityUsage::SITELINK_USAGE,
EntityUsage::TITLE_USAGE ),
$changeFactory->newFromUpdate(
ItemChange::UPDATE,
- $this->getEmptyItem(),
- $this->getItemWithSiteLinks( array( 'enwiki' =>
'Rome' ) )
- ),
- 'added site link to client'
+ $this->getEmptyItem( $q2 ),
+ $this->getItemWithSiteLinks( $q2, array(
'enwiki' => '2' ) )
+ )
);
- $cases[] = array(
- array( $berlin, $rome ),
- array(),
+ $cases['change link of Q1'] = array(
+ array( EntityUsage::SITELINK_USAGE,
EntityUsage::TITLE_USAGE ),
$changeFactory->newFromUpdate(
ItemChange::UPDATE,
- $this->getItemWithSiteLinks( array( 'enwiki' =>
'Rome' ) ),
- $this->getItemWithSiteLinks( array( 'enwiki' =>
'Berlin' ) )
- ),
- 'changed client site link'
+ $this->getItemWithSiteLinks( $q1, array(
'enwiki' => '1' ) ),
+ $this->getItemWithSiteLinks( $q1, array(
'enwiki' => '2' ) )
+ )
);
- $cases[] = array(
- array( $rome ),
- array(),
+ $cases['delete linked item Q2'] = array(
+ array( EntityUsage::SITELINK_USAGE,
EntityUsage::TITLE_USAGE ),
$changeFactory->newFromUpdate(
ItemChange::REMOVE,
- $this->getItemWithSiteLinks( array( 'enwiki' =>
'Rome' ) ),
+ $this->getItemWithSiteLinks( $q2, array(
'enwiki' => '2' ) ),
null
),
'item connected to client was deleted'
);
- $cases[] = array(
- array( $rome ),
- array( 2 ),
+ $cases['add another sitelink to Q2'] = array(
+ array( EntityUsage::SITELINK_USAGE ),
$changeFactory->newFromUpdate(
ItemChange::UPDATE,
- $this->getItemWithSiteLinks( array( 'enwiki' =>
'Rome' ) ),
- $this->getItemWithSiteLinks( array(
- 'enwiki' => 'Rome',
- 'itwiki' => 'Roma',
+ $this->getItemWithSiteLinks( $q2, array(
'enwiki' => '2' ) ),
+ $this->getItemWithSiteLinks( $q2, array(
+ 'enwiki' => '2',
+ 'itwiki' => 'DUE',
) )
- ),
- 'added site link on connected item'
+ )
);
- $cases[] = array(
- array(),
- array(),
+ $cases['other language label change on Q1'] = array(
+ array( EntityUsage::OTHER_USAGE ),
$changeFactory->newFromUpdate(
ItemChange::UPDATE,
- $this->getEmptyItem(),
- $this->getItemWithLabel( 'de', 'Berlin' )
- ),
- 'unrelated label change'
+ $this->getEmptyItem( $q1 ),
+ $this->getItemWithLabel( $q1, 'de', 'EINS' )
+ )
);
- $connectedItem = $this->getItemWithSiteLinks( array( 'enwiki'
=> 'Berlin' ) );
- $connectedItemWithLabel = $this->getItemWithSiteLinks( array(
'enwiki' => 'Berlin' ) );
- $connectedItemWithLabel->setLabel( 'enwiki', 'Berlin' );
-
- $cases[] = array(
- array( $berlin ),
- array( 1 ),
- $changeFactory->newFromUpdate( ItemChange::UPDATE,
$connectedItem, $connectedItemWithLabel ),
- 'connected item label change'
+ $cases['local label change on Q1 (used by Q2)'] = array(
+ array( EntityUsage::LABEL_USAGE ),
+ $changeFactory->newFromUpdate(
+ ItemChange::UPDATE,
+ $this->getEmptyItem( $q1 ),
+ $this->getItemWithLabel( $q1, 'en', 'ONE' )
+ )
);
- $itemWithBadge = $this->getEmptyItem();
$badges = array( new ItemId( 'Q34' ) );
- $itemWithBadge->addSiteLink( new SiteLink( 'enwiki', 'Rome',
$badges ) );
-
- $cases[] = array(
- array(),
- array(),
+ $cases['badge only change on Q1'] = array(
+ array( EntityUsage::SITELINK_USAGE ),
$changeFactory->newFromUpdate( ItemChange::UPDATE,
- $this->getItemWithSiteLinks( array( 'enwiki' =>
'Rome' ) ),
- $itemWithBadge ),
- 'badge change'
+ $this->getItemWithSiteLinks( $q1, array(
'enwiki' => '1' ) ),
+ $this->getItemWithSiteLinks( $q1, array(
'enwiki' => '1' ), $badges ) )
);
return $cases;
}
/**
+ * @dataProvider getChangedAspectsProvider
+ */
+ public function testGetChangedAspects( array $expected, ItemChange
$change ) {
+ $referencedPagesFinder = $this->getAffectedPagesFinder( array()
);
+
+ $actual = $referencedPagesFinder->getChangedAspects( $change );
+
+ sort( $expected );
+ sort( $actual );
+ $this->assertEquals( $expected, $actual );
+ }
+
+ public function getAffectedUsagesByPageProvider() {
+ $changeFactory = TestChanges::getEntityChangeFactory();
+
+ $q1 = new ItemId( 'Q1' );
+ $q2 = new ItemId( 'Q2' );
+
+ $q1SitelinkUsage = new EntityUsage( $q1,
EntityUsage::SITELINK_USAGE );
+ $q2SitelinkUsage = new EntityUsage( $q2,
EntityUsage::SITELINK_USAGE );
+ $q2AllUsage = new EntityUsage( $q2, EntityUsage::ALL_USAGE );
+ $q2OtherUsage = new EntityUsage( $q2, EntityUsage::OTHER_USAGE
);
+
+ $q1LabelUsage = new EntityUsage( $q1, EntityUsage::LABEL_USAGE
);
+ $q2LabelUsage = new EntityUsage( $q2, EntityUsage::LABEL_USAGE
);
+
+ $q1TitleUsage = new EntityUsage( $q1, EntityUsage::TITLE_USAGE
);
+ $q2TitleUsage = new EntityUsage( $q2, EntityUsage::TITLE_USAGE
);
+
+ $page1Q1Usages = new PageEntityUsages( 1, array(
+ $q1SitelinkUsage,
+ ) );
+
+ $page2Q1Usages = new PageEntityUsages( 2, array(
+ $q1LabelUsage,
+ $q1TitleUsage,
+ ) );
+
+ $page1Q2Usages = new PageEntityUsages( 1, array(
+ $q2LabelUsage,
+ $q2TitleUsage,
+ ) );
+
+ $page2Q2Usages = new PageEntityUsages( 2, array(
+ $q2AllUsage,
+ ) );
+
+ // Cases
+ // item with link created
+ // item with link deleted
+ // link added
+ // removed added
+ // link changed
+ // direct aspect match
+ // no aspect match
+ // all matches any
+ // any matches all
+
+ $cases = array();
+
+ $cases['create linked item Q1'] = array(
+ array(
+ new PageEntityUsages( 1, array(
$q1SitelinkUsage ) ),
+ ),
+ array(), // No usages recorded yet
+ $changeFactory->newFromUpdate(
+ ItemChange::ADD,
+ null,
+ $this->getItemWithSiteLinks( $q1, array(
'enwiki' => '1' ) )
+ )
+ );
+
+ $cases['unlink item Q1'] = array(
+ array(
+ new PageEntityUsages( 1, array(
$q1SitelinkUsage ) ),
+ new PageEntityUsages( 2, array( $q1TitleUsage )
),
+ ),
+ array( $page1Q1Usages, $page2Q1Usages ), // "1" was
recorded to be linked to Q1 and the local title used on page "2"
+ $changeFactory->newFromUpdate(
+ ItemChange::UPDATE,
+ $this->getItemWithSiteLinks( $q1, array(
'enwiki' => '1' ) ),
+ $this->getEmptyItem( $q1 )
+ )
+ );
+
+ $cases['link item Q2'] = array(
+ array(
+ new PageEntityUsages( 1, array( $q2TitleUsage )
),
+ new PageEntityUsages( 2, array( $q2TitleUsage,
$q2SitelinkUsage ) ),
+ ),
+ array( $page1Q2Usages, $page2Q2Usages ),
+ $changeFactory->newFromUpdate(
+ ItemChange::UPDATE,
+ $this->getEmptyItem( $q2 ),
+ $this->getItemWithSiteLinks( $q2, array(
'enwiki' => '2' ) )
+ )
+ );
+
+ $cases['change link of Q1, with NO prior record'] = array(
+ array(
+ new PageEntityUsages( 1, array(
$q1SitelinkUsage ) ),
+ new PageEntityUsages( 2, array(
$q1SitelinkUsage ) ),
+ ),
+ array(),
+ $changeFactory->newFromUpdate(
+ ItemChange::UPDATE,
+ $this->getItemWithSiteLinks( $q1, array(
'enwiki' => '1' ) ),
+ $this->getItemWithSiteLinks( $q1, array(
'enwiki' => '2' ) )
+ )
+ );
+
+ $cases['change link of Q1, with prior record'] = array(
+ array(
+ new PageEntityUsages( 1, array(
$q1SitelinkUsage ) ),
+ new PageEntityUsages( 2, array(
$q1SitelinkUsage, $q1TitleUsage ) ),
+ ),
+ array( $page1Q1Usages, $page2Q1Usages ),
+ $changeFactory->newFromUpdate(
+ ItemChange::UPDATE,
+ $this->getItemWithSiteLinks( $q1, array(
'enwiki' => '1' ) ),
+ $this->getItemWithSiteLinks( $q1, array(
'enwiki' => '2' ) )
+ )
+ );
+
+ $badges = array( new ItemId( 'Q34' ) );
+ $cases['badge only change on Q1'] = array(
+ array(
+ new PageEntityUsages( 1, array(
$q1SitelinkUsage ) ),
+ ),
+ array( $page1Q1Usages, $page2Q1Usages ),
+ $changeFactory->newFromUpdate( ItemChange::UPDATE,
+ $this->getItemWithSiteLinks( $q1, array(
'enwiki' => '1' ) ),
+ $this->getItemWithSiteLinks( $q1, array(
'enwiki' => '1' ), $badges ) )
+ );
+
+ $cases['delete linked item Q2'] = array(
+ array(
+ new PageEntityUsages( 1, array( $q2TitleUsage )
),
+ new PageEntityUsages( 2, array( $q2TitleUsage,
$q2SitelinkUsage ) ),
+ ),
+ array( $page1Q2Usages, $page2Q2Usages ),
+ $changeFactory->newFromUpdate(
+ ItemChange::REMOVE,
+ $this->getItemWithSiteLinks( $q2, array(
'enwiki' => '2' ) ),
+ null
+ ),
+ 'item connected to client was deleted'
+ );
+
+ $cases['add another sitelink to Q2'] = array(
+ array(
+ new PageEntityUsages( 2, array(
$q2SitelinkUsage ) ),
+ ),
+ array( $page2Q2Usages ),
+ $changeFactory->newFromUpdate(
+ ItemChange::UPDATE,
+ $this->getItemWithSiteLinks( $q2, array(
'enwiki' => '2' ) ),
+ $this->getItemWithSiteLinks( $q2, array(
+ 'enwiki' => '2',
+ 'itwiki' => 'DUE',
+ ) )
+ )
+ );
+
+ $cases['other language label change on Q1 (not used on any
page)'] = array(
+ array(),
+ array( $page1Q1Usages, $page2Q1Usages ),
+ $changeFactory->newFromUpdate(
+ ItemChange::UPDATE,
+ $this->getEmptyItem( $q1 ),
+ $this->getItemWithLabel( $q1, 'de', 'EINS' )
+ )
+ );
+
+ $cases['other language label change on Q2 (used on page 2)'] =
array(
+ array(
+ new PageEntityUsages( 2, array( $q2OtherUsage )
),
+ ),
+ array( $page1Q2Usages, $page2Q2Usages ),
+ $changeFactory->newFromUpdate(
+ ItemChange::UPDATE,
+ $this->getEmptyItem( $q2 ),
+ $this->getItemWithLabel( $q2, 'de', 'EINS' )
+ )
+ );
+
+ $cases['local label change on Q1 (used by page 2)'] = array(
+ array(
+ new PageEntityUsages( 2, array( $q1LabelUsage )
),
+ ),
+ array( $page1Q1Usages, $page2Q1Usages ),
+ $changeFactory->newFromUpdate(
+ ItemChange::UPDATE,
+ $this->getEmptyItem( $q1 ),
+ $this->getItemWithLabel( $q1, 'en', 'ONE' )
+ )
+ );
+
+ $cases['label change on Q2 (used by page 1 and page 2)'] =
array(
+ array(
+ new PageEntityUsages( 1, array( $q2LabelUsage )
),
+ new PageEntityUsages( 2, array( $q2LabelUsage )
),
+ ),
+ array( $page1Q2Usages, $page2Q2Usages ),
+ $changeFactory->newFromUpdate(
+ ItemChange::UPDATE,
+ $this->getEmptyItem( $q2 ),
+ $this->getItemWithLabel( $q2, 'en', 'TWO' )
+ )
+ );
+
+ return $cases;
+ }
+
+ /**
+ * @dataProvider getAffectedUsagesByPageProvider
+ */
+ public function testGetAffectedUsagesByPage( array $expected, array
$usage, ItemChange $change ) {
+ $referencedPagesFinder = $this->getAffectedPagesFinder( $usage
);
+
+ $actual = $referencedPagesFinder->getAffectedUsagesByPage(
$change );
+
+ $this->assertPageEntityUsages( $expected, $actual );
+ }
+
+ /**
+ * @param ItemId $id
+ *
* @return Item
*/
- private function getEmptyItem() {
+ private function getEmptyItem( ItemId $id ) {
$item = Item::newEmpty();
- $item->setId( 2 );
+ $item->setId( $id );
return $item->copy();
}
/**
+ * @param ItemId $id
* @param string[] $links
+ * @param ItemId[] $badges
*
* @return Item
*/
- private function getItemWithSiteLinks( array $links ) {
- $item = $this->getEmptyItem();
+ private function getItemWithSiteLinks( ItemId $id, array $links, array
$badges = array() ) {
+ $item = $this->getEmptyItem( $id );
foreach( $links as $siteId => $page ) {
$item->addSiteLink(
- new SiteLink( $siteId, $page )
+ new SiteLink( $siteId, $page, $badges )
);
}
@@ -237,28 +442,40 @@
}
/**
+ * @param ItemId $id
* @param string $languageCode
* @param string $label
*
* @return Item
*/
- private function getItemWithLabel( $languageCode, $label ) {
- $item = $this->getEmptyItem();
+ private function getItemWithLabel( ItemId $id, $languageCode, $label ) {
+ $item = $this->getEmptyItem( $id );
$item->setLabel( $languageCode, $label );
return $item;
}
/**
- * @param Title[] $titles
+ * @param PageEntityUsages[]|Iterator<PageEntityUsages> $usagesPerPage
*
- * @return string[]
+ * @return PageEntityUsages[]
*/
- private function getPrefixedTitles( array $titles ) {
- return array_values(
- array_map( function( Title $title ) {
- return $title->getPrefixedText();
- }, $titles )
+ private function getPageEntityUsageStrings( $usagesPerPage ) {
+ $strings = array();
+
+ foreach ( $usagesPerPage as $pageUsages ) {
+ $strings[] = "$pageUsages";
+ }
+
+ sort( $strings );
+ return $strings;
+ }
+
+ private function assertPageEntityUsages( $expected, $actual, $message =
'' ) {
+ $this->assertEquals(
+ $this->getPageEntityUsageStrings( $expected ),
+ $this->getPageEntityUsageStrings( $actual ),
+ $message
);
}
diff --git a/client/tests/phpunit/includes/Changes/ChangeHandlerTest.php
b/client/tests/phpunit/includes/Changes/ChangeHandlerTest.php
index 57b5d06..e300e7f 100644
--- a/client/tests/phpunit/includes/Changes/ChangeHandlerTest.php
+++ b/client/tests/phpunit/includes/Changes/ChangeHandlerTest.php
@@ -10,6 +10,8 @@
use Wikibase\Client\Changes\ChangeHandler;
use Wikibase\Client\Changes\PageUpdater;
use Wikibase\Client\Store\TitleFactory;
+use Wikibase\Client\Usage\EntityUsage;
+use Wikibase\Client\Usage\PageEntityUsages;
use Wikibase\Client\Usage\UsageLookup;
use Wikibase\Client\WikibaseClient;
use Wikibase\DataModel\Entity\Entity;
@@ -71,11 +73,13 @@
$namespaceChecker,
$titleFactory,
'enwiki',
+ 'en',
false
);
$handler = new ChangeHandler(
$affectedPagesFinder,
+ $titleFactory,
$updater ? : new MockPageUpdater(),
$transformer,
'enwiki',
@@ -411,6 +415,7 @@
*/
private function getTitleFactory( array $entities ) {
$titlesById = $this->getFakePageIdMap( $entities );
+ $pageIdsByTitle = array_flip( $titlesById );
$titleFactory = $this->getMock(
'Wikibase\Client\Store\TitleFactory' );
@@ -426,11 +431,17 @@
$titleFactory->expects( $this->any() )
->method( 'newFromText' )
- ->will( $this->returnCallback( function( $text,
$defaultNs = NS_MAIN ) {
+ ->will( $this->returnCallback( function( $text,
$defaultNs = NS_MAIN ) use ( $pageIdsByTitle ) {
$title = Title::newFromText( $text, $defaultNs
);
if ( !$title ) {
throw new StorageException( 'Bad title
text: ' . $text );
+ }
+
+ if ( isset( $pageIdsByTitle[$text] ) ) {
+ $title->resetArticleID(
$pageIdsByTitle[$text] );
+ } else {
+ throw new StorageException( 'Unknown
title text: ' . $text );
}
return $title;
@@ -458,11 +469,19 @@
$pages = array();
foreach ( $ids as $id ) {
+ if ( !( $id instanceof ItemId )
) {
+ continue;
+ }
+
$links =
$siteLinklookup->getSiteLinksForItem( $id );
foreach ( $links as $link ) {
if ( $link->getSiteId()
== $site->getGlobalId() ) {
// we use the
numeric item id as the fake page id of the local page!
- $pages[] =
$id->getNumericId();
+ $usages = array(
+ new
EntityUsage( $id, EntityUsage::SITELINK_USAGE ),
+ new
EntityUsage( $id, EntityUsage::LABEL_USAGE )
+ );
+ $pages[] = new
PageEntityUsages( $id->getNumericId(), $usages );
}
}
}
@@ -526,7 +545,7 @@
array( // #6
$changes['set-de-label'],
array( 'q100' => array( 'enwiki' => 'Emmy2' ) ),
- array( 'Emmy2' )
+ array(), // For the dummy page, only label and
sitelink usage is defined.
),
array( // #7
$changes['set-de-label'],
@@ -538,22 +557,26 @@
array( 'q100' => array( 'enwiki' => 'Emmy2' ) ),
array( 'Emmy2' )
),
+ array( // #8
+ $changes['set-en-label'],
+ array( 'q100' => array( 'enwiki' =>
'User:Emmy2' ) ), // bad namespace
+ array( )
+ ),
array( // #9
$changes['set-en-aliases'],
array( 'q100' => array( 'enwiki' => 'Emmy2' ) ),
- array( 'Emmy2' ), // or nothing, may change
- array(), // because no actions are to be taken,
the effective list is empty.
+ array(), // For the dummy page, only label and
sitelink usage is defined.
),
array( // #10
$changes['add-claim'],
array( 'q100' => array( 'enwiki' => 'Emmy2' ) ),
- array( 'Emmy2' )
+ array( ) // statements are ignored
),
array( // #11
$changes['remove-claim'],
array( 'q100' => array( 'enwiki' => 'Emmy2' ) ),
- array( 'Emmy2' )
+ array( ) // statements are ignored
),
array( // #12
@@ -574,8 +597,9 @@
),
array( // #15
$changes['change-enwiki-sitelink'],
- array( 'q100' => array( 'enwiki' => 'Emmy' ) ),
- array( 'Emmy', 'Emmy2' )
+ array( 'q100' => array( 'enwiki' => 'Emmy' ),
'q200' => array( 'enwiki' => 'Emmy2' ) ),
+ array( 'Emmy', 'Emmy2' ),
+ true
),
array( // #16
$changes['change-enwiki-sitelink-badges'],
@@ -631,7 +655,7 @@
/**
* @dataProvider provideGetPagesToUpdate
*/
- public function testGetPagesToUpdate( Change $change, $entities, array
$expected ) {
+ public function testGetPagesToUpdate( Change $change, $entities, array
$expected, $dummy = false ) {
$handler = $this->newChangeHandler( null, $entities );
$toUpdate = $handler->getPagesToUpdate( $change );
@@ -649,9 +673,7 @@
$cases = array();
foreach ( $pto as $case ) {
- // $case[2] is the list of pages to update,
- // $case[3] may be a list filtered according to the
actions that apply.
- $updated = isset( $case[3] ) ? $case[3] : $case[2];
+ $updated = $case[2];
$cases[] = array(
$case[0], // $change
diff --git
a/client/tests/phpunit/includes/DataAccess/PropertyParserFunction/RunnerTest.php
b/client/tests/phpunit/includes/DataAccess/PropertyParserFunction/RunnerTest.php
index 822f607..a088cef 100644
---
a/client/tests/phpunit/includes/DataAccess/PropertyParserFunction/RunnerTest.php
+++
b/client/tests/phpunit/includes/DataAccess/PropertyParserFunction/RunnerTest.php
@@ -43,15 +43,24 @@
);
$this->assertEquals( $expected, $result );
- $this->assertUsageTracking( $itemId, EntityUsage::ALL_USAGE,
$parser->getOutput() );
+ $this->assertUsageTracking( $itemId, EntityUsage::OTHER_USAGE,
$parser->getOutput() );
}
private function assertUsageTracking( ItemId $id, $aspect, ParserOutput
$parserOutput ) {
$usageAcc = new ParserOutputUsageAccumulator( $parserOutput );
- $usage = $usageAcc->getUsages();
+ $usages = $usageAcc->getUsages();
$expected = new EntityUsage( $id, $aspect );
- $this->assertContains( $expected, $usage, '', false, false );
+ $usageIdentities = array_map(
+ function ( EntityUsage $usage ) {
+ return $usage->getIdentityString();
+ },
+ $usages
+ );
+
+ $expectedIdentities = array( $expected->getIdentityString() );
+
+ $this->assertEquals( $expectedIdentities, array_values(
$usageIdentities ) );
}
private function getSiteLinkLookup( ItemId $itemId ) {
diff --git a/client/tests/phpunit/includes/Usage/PageEntityUsagesTest.php
b/client/tests/phpunit/includes/Usage/PageEntityUsagesTest.php
new file mode 100644
index 0000000..d923d26
--- /dev/null
+++ b/client/tests/phpunit/includes/Usage/PageEntityUsagesTest.php
@@ -0,0 +1,48 @@
+<?php
+namespace Wikibase\Client\Tests\Usage;
+
+use PHPUnit_Framework_TestCase;
+use Wikibase\Client\Usage\EntityUsage;
+use Wikibase\Client\Usage\PageEntityUsages;
+use Wikibase\DataModel\Entity\ItemId;
+
+/**
+ * @covers Wikibase\Client\Usage\PageEntityUsages
+ *
+ * @group Wikibase
+ * @group WikibaseClient
+ * @group WikibaseUsageTracking
+ *
+ * @license GPL 2+
+ * @author Daniel Kinzler
+ */
+class PageEntityUsagesTest extends PHPUnit_Framework_TestCase {
+
+ public function testGetters() {
+ $q7 = new ItemId( 'Q7' );
+ $q11 = new ItemId( 'Q11' );
+
+ $usages = array(
+ new EntityUsage( $q7, EntityUsage::ALL_USAGE ),
+ new EntityUsage( $q11, EntityUsage::LABEL_USAGE ),
+ new EntityUsage( $q11, EntityUsage::TITLE_USAGE ),
+ );
+
+ $pageUsages = new PageEntityUsages( 6, $usages );
+
+ $this->assertEquals( 6, $pageUsages->getPageId() );
+
+ $expectedAspects = array(
+ EntityUsage::LABEL_USAGE,
+ EntityUsage::TITLE_USAGE,
+ EntityUsage::ALL_USAGE,
+ );
+
+ $this->assertEquals( $expectedAspects,
$pageUsages->getAspects() );
+ $this->assertEquals( array( 'Q11' => $q11, 'Q7' => $q7 ),
$pageUsages->getEntityIds() );
+ $this->assertEquals( array( 'Q11#L', 'Q11#T', 'Q7#X' ),
array_keys( $pageUsages->getUsages() ) );
+
+ $this->assertEquals( array( EntityUsage::ALL_USAGE ),
$pageUsages->getUsageAspects( $q7 ) );
+ }
+
+}
diff --git
a/client/tests/phpunit/includes/Usage/UsageAccumulatorContractTester.php
b/client/tests/phpunit/includes/Usage/UsageAccumulatorContractTester.php
index 0f38d66..ea033eb 100644
--- a/client/tests/phpunit/includes/Usage/UsageAccumulatorContractTester.php
+++ b/client/tests/phpunit/includes/Usage/UsageAccumulatorContractTester.php
@@ -33,7 +33,7 @@
$this->usageAccumulator->addSiteLinksUsage( $q2 );
$this->usageAccumulator->addLabelUsage( $q2 );
- $this->usageAccumulator->addPageUsage( $q2 );
+ $this->usageAccumulator->addTitleUsage( $q2 );
$this->usageAccumulator->addAllUsage( $q3 );
$usage = $this->usageAccumulator->getUsages();
diff --git a/client/tests/phpunit/includes/Usage/UsageAspectTransformerTest.php
b/client/tests/phpunit/includes/Usage/UsageAspectTransformerTest.php
new file mode 100644
index 0000000..7d7c041
--- /dev/null
+++ b/client/tests/phpunit/includes/Usage/UsageAspectTransformerTest.php
@@ -0,0 +1,148 @@
+<?php
+namespace Wikibase\Client\Usage\Tests;
+
+use Wikibase\Client\Usage\EntityUsage;
+use Wikibase\Client\Usage\PageEntityUsages;
+use Wikibase\Client\Usage\UsageAspectTransformer;
+use Wikibase\DataModel\Entity\BasicEntityIdParser;
+use Wikibase\DataModel\Entity\ItemId;
+
+/**
+ * @covers Wikibase\Client\Usage\UsageAspectTransformer
+ *
+ * @group Wikibase
+ * @group WikibaseClient
+ * @group WikibaseUsageTracking
+ *
+ * @license GPL 2+
+ * @author Daniel Kinzler
+ */
+class UsageAspectTransformerTest extends \PHPUnit_Framework_TestCase {
+
+ public function testGetRelevantAspects() {
+ $q1 = new ItemId( 'Q1' );
+ $q99 = new ItemId( 'Q99' );
+
+ $aspects = array(
+ EntityUsage::TITLE_USAGE,
+ EntityUsage::LABEL_USAGE,
+ );
+
+ $transformer = new UsageAspectTransformer();
+ $transformer->setRelevantAspects( $q1, $aspects );
+
+ $this->assertEquals( $aspects,
$transformer->getRelevantAspects( $q1 ) );
+ $this->assertEquals( array(), $transformer->getRelevantAspects(
$q99 ) );
+ }
+
+ public function provideGetFilteredUsages() {
+ $q1 = new ItemId( 'Q1' );
+
+ return array(
+ 'empty' => array(
+ $q1,
+ array(),
+ array(),
+ array()
+ ),
+ 'non relevant' => array(
+ $q1,
+ array(),
+ array( 'X' ),
+ array()
+ ),
+ 'non used' => array(
+ $q1,
+ array( 'X' ),
+ array(),
+ array()
+ ),
+ 'simple filter' => array(
+ $q1,
+ array( 'T', 'L' ),
+ array( 'L', 'S' ),
+ array( 'Q1#L' )
+ ),
+ 'all filter' => array(
+ $q1,
+ array( 'X' ),
+ array( 'S', 'L' ),
+ array( 'Q1#L', 'Q1#S' )
+ ),
+ 'filter all' => array(
+ $q1,
+ array( 'S', 'L' ),
+ array( 'X' ),
+ array( 'Q1#L', 'Q1#S' )
+ ),
+ );
+ }
+
+ /**
+ * @dataProvider provideGetFilteredUsages
+ */
+ public function testGetFilteredUsages( $entityId, $relevant, $used,
$expected ) {
+ $transformer = new UsageAspectTransformer();
+ $transformer->setRelevantAspects( $entityId, $relevant );
+
+ $usages = $transformer->getFilteredUsages( $entityId, $used );
+ $this->assertEquals( $expected, array_keys( $usages ) );
+ }
+
+ public function provideTransformPageEntityUsages() {
+ $q1 = new ItemId( 'Q1' );
+ $q2 = new ItemId( 'Q2' );
+
+ $usages = new PageEntityUsages( 23, array(
+ new EntityUsage( $q1, EntityUsage::LABEL_USAGE ),
+ new EntityUsage( $q1, EntityUsage::TITLE_USAGE ),
+ new EntityUsage( $q2, EntityUsage::ALL_USAGE ),
+ ) );
+
+ return array(
+ 'empty' => array(
+ array(),
+ $usages,
+ array()
+ ),
+ 'non relevant' => array(
+ array(),
+ $usages,
+ array()
+ ),
+ 'simple filter' => array(
+ array(
+ 'Q1' => array( 'T' ),
+ ),
+ $usages,
+ array( 'Q1#T' )
+ ),
+ 'all filter' => array(
+ array(
+ 'Q2' => array( 'T', 'L' ),
+ 'Q1' => array( 'X' ),
+ ),
+ $usages,
+ array( 'Q1#L', 'Q1#T', 'Q2#L', 'Q2#T' )
+ ),
+ );
+ }
+
+ /**
+ * @dataProvider provideTransformPageEntityUsages
+ */
+ public function testTransformPageEntityUsages( $relevant,
PageEntityUsages $usages, $expected ) {
+ $transformer = new UsageAspectTransformer();
+ $idParser = new BasicEntityIdParser();
+
+ foreach ( $relevant as $id => $aspects ) {
+ $transformer->setRelevantAspects( $idParser->parse( $id
), $aspects );
+ }
+
+ $transformed = $transformer->transformPageEntityUsages( $usages
);
+
+ $this->assertEquals( $usages->getPageId(),
$transformed->getPageId() );
+ $this->assertEquals( $expected, array_keys(
$transformed->getUsages() ) );
+ }
+
+}
diff --git a/client/tests/phpunit/includes/Usage/UsageLookupContractTester.php
b/client/tests/phpunit/includes/Usage/UsageLookupContractTester.php
index 7791451..9b86f57 100644
--- a/client/tests/phpunit/includes/Usage/UsageLookupContractTester.php
+++ b/client/tests/phpunit/includes/Usage/UsageLookupContractTester.php
@@ -3,6 +3,7 @@
use PHPUnit_Framework_Assert as Assert;
use Wikibase\Client\Usage\EntityUsage;
+use Wikibase\Client\Usage\PageEntityUsages;
use Wikibase\Client\Usage\UsageLookup;
use Wikibase\Client\Usage\UsageTracker;
use Wikibase\DataModel\Entity\ItemId;
@@ -64,28 +65,31 @@
$q4 = new ItemId( 'Q4' );
$q6 = new ItemId( 'Q6' );
- $u3i = new EntityUsage( $q3, EntityUsage::SITELINK_USAGE );
+ $u3s = new EntityUsage( $q3, EntityUsage::SITELINK_USAGE );
$u3l = new EntityUsage( $q3, EntityUsage::LABEL_USAGE );
$u4l = new EntityUsage( $q4, EntityUsage::LABEL_USAGE );
+ $u4t = new EntityUsage( $q4, EntityUsage::TITLE_USAGE );
- $usages = array( $u3i, $u3l, $u4l );
-
- $this->tracker->trackUsedEntities( 23, $usages );
+ $this->tracker->trackUsedEntities( 23, array( $u3s, $u3l, $u4l
) );
+ $this->tracker->trackUsedEntities( 42, array( $u4l, $u4t ) );
Assert::assertEmpty(
iterator_to_array( $this->lookup->getPagesUsing( array(
$q6 ) ) )
);
- Assert::assertEquals(
- array( 23 ),
+ $this->assertSamePageEntityUsages(
+ array( 23 => new PageEntityUsages( 23, array( $u3s,
$u3l ) ) ),
iterator_to_array( $this->lookup->getPagesUsing( array(
$q3 ) ) ),
'Pages using Q3'
);
- Assert::assertEquals(
- array( 23 ),
- iterator_to_array( $this->lookup->getPagesUsing( array(
$q4 ) ) ),
- 'Pages using Q4'
+ $this->assertSamePageEntityUsages(
+ array(
+ 23 => new PageEntityUsages( 23, array( $u3l,
$u4l ) ),
+ 42 => new PageEntityUsages( 42, array( $u4l ) ),
+ ),
+ iterator_to_array( $this->lookup->getPagesUsing( array(
$q4, $q3 ), array( EntityUsage::LABEL_USAGE ) ) ),
+ 'Pages using "label" on Q4 or Q3'
);
Assert::assertEmpty(
@@ -98,14 +102,35 @@
'Pages using "sitelinks" on Q4'
);
- Assert::assertCount( 1,
- iterator_to_array( $this->lookup->getPagesUsing( array(
$q3, $q4 ), array( EntityUsage::LABEL_USAGE, EntityUsage::SITELINK_USAGE ) ) ),
- 'Pages using "label" or "sitelinks" on Q3 or Q4'
+ Assert::assertCount( 2,
+ iterator_to_array( $this->lookup->getPagesUsing( array(
$q3, $q4 ), array( EntityUsage::TITLE_USAGE, EntityUsage::SITELINK_USAGE ) ) ),
+ 'Pages using "title" or "sitelinks" on Q3 or Q4'
);
$this->tracker->trackUsedEntities( 23, array() );
}
+ /**
+ *
+ * @param PageEntityUsages[] $expected
+ * @param PageEntityUsages[] $actual
+ * @param string $message
+ */
+ private function assertSamePageEntityUsages( array $expected, array
$actual, $message = '' ) {
+ if ( $message !== '' ) {
+ $message .= "\n";
+ }
+
+ foreach ( $expected as $key => $expectedUsages ) {
+ $actualUsages = $actual[$key];
+
+ Assert::assertEquals( $expectedUsages->getPageId(),
$actualUsages->getPageId(), $message . "[Page $key] " . 'Page ID mismatches!' );
+ Assert::assertEquals( $expectedUsages->getUsages(),
$actualUsages->getUsages(), $message . "[Page $key] " . 'Usages:' );
+ }
+
+ Assert::assertEmpty( array_slice( $actual, count( $expected )
), $message . 'Extra entries found!' );
+ }
+
public function testGetUnusedEntities() {
$q3 = new ItemId( 'Q3' );
$q4 = new ItemId( 'Q4' );
--
To view, visit https://gerrit.wikimedia.org/r/167856
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: merged
Gerrit-Change-Id: I9ce6a2e90347f6ce8a9ce9ef4d0a083592211d23
Gerrit-PatchSet: 15
Gerrit-Project: mediawiki/extensions/Wikibase
Gerrit-Branch: master
Gerrit-Owner: Daniel Kinzler <[email protected]>
Gerrit-Reviewer: Adrian Lang <[email protected]>
Gerrit-Reviewer: Aude <[email protected]>
Gerrit-Reviewer: Daniel Kinzler <[email protected]>
Gerrit-Reviewer: JanZerebecki <[email protected]>
Gerrit-Reviewer: Multichill <[email protected]>
Gerrit-Reviewer: Thiemo Mättig (WMDE) <[email protected]>
Gerrit-Reviewer: jenkins-bot <>
_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits