jenkins-bot has submitted this change and it was merged. ( 
https://gerrit.wikimedia.org/r/384298 )

Change subject: Add EntityDiffChangedAspects + Factory
......................................................................


Add EntityDiffChangedAspects + Factory

This should later be used when pushing repo changes to clients.
See also T113468#3641207.

The code in EntityDiffChangedAspectsFactory was mostly copied
from AffectedPagesFinder::getChangedAspects.

Bug: T113468
Change-Id: I0ac579e2063dfe92a974841330586423e5e7f393
---
M lib/autoload.php
A lib/includes/Changes/EntityDiffChangedAspects.php
A lib/includes/Changes/EntityDiffChangedAspectsFactory.php
A lib/tests/phpunit/Changes/EntityDiffChangedAspectsFactoryTest.php
A lib/tests/phpunit/Changes/EntityDiffChangedAspectsTest.php
5 files changed, 869 insertions(+), 0 deletions(-)

Approvals:
  Ladsgroup: Looks good to me, approved
  jenkins-bot: Verified



diff --git a/lib/autoload.php b/lib/autoload.php
index fda04cf..6082d0c 100644
--- a/lib/autoload.php
+++ b/lib/autoload.php
@@ -20,6 +20,8 @@
        'Wikibase\\Lib\\AutoCommentFormatter' => __DIR__ . 
'/includes/Formatters/AutoCommentFormatter.php',
        'Wikibase\\Lib\\Changes\\CentralIdLookupFactory' => __DIR__ . 
'/includes/Changes/CentralIdLookupFactory.php',
        'Wikibase\\Lib\\Changes\\EntityChangeFactory' => __DIR__ . 
'/includes/Changes/EntityChangeFactory.php',
+       'Wikibase\\Lib\\Changes\\EntityDiffChangedAspects' => __DIR__ . 
'/includes/Changes/EntityDiffChangedAspects.php',
+       'Wikibase\\Lib\\Changes\\EntityDiffChangedAspectsFactory' => __DIR__ . 
'/includes/Changes/EntityDiffChangedAspectsFactory.php',
        'Wikibase\\Lib\\CommonsLinkFormatter' => __DIR__ . 
'/includes/Formatters/CommonsLinkFormatter.php',
        'Wikibase\\Lib\\ContentLanguages' => __DIR__ . 
'/includes/ContentLanguages.php',
        'Wikibase\\Lib\\DataTypeDefinitions' => __DIR__ . 
'/includes/DataTypeDefinitions.php',
@@ -156,6 +158,8 @@
        'Wikibase\\Lib\\Tests\\EntityRevisionLookupTest' => __DIR__ . 
'/tests/phpunit/EntityRevisionLookupTest.php',
        'Wikibase\\Lib\\Tests\\MockPropertyLabelResolver' => __DIR__ . 
'/tests/phpunit/MockPropertyLabelResolver.php',
        'Wikibase\\Lib\\Tests\\MockRepository' => __DIR__ . 
'/tests/phpunit/MockRepository.php',
+       'Wikibase\\Lib\\Tests\\Changes\\EntityDiffChangedAspectsTest' => 
__DIR__ . '/tests/phpunit/Changes/EntityDiffChangedAspectsTest.php',
+       'Wikibase\\Lib\\Tests\\Changes\\EntityDiffChangedAspectsFactoryTest' => 
__DIR__ . '/tests/phpunit/Changes/EntityDiffChangedAspectsFactoryTest.php',
        'Wikibase\\Lib\\Tests\\Store\\EntityTermLookupTest' => __DIR__ . 
'/tests/phpunit/Store/EntityTermLookupTest.php',
        'Wikibase\\Lib\\Tests\\Store\\HttpUrlPropertyOrderProviderTestMockHttp' 
=> __DIR__ . 
'/tests/phpunit/Store/HttpUrlPropertyOrderProviderTestMockHttp.php',
        'Wikibase\\Lib\\Tests\\Store\\MockChunkAccess' => __DIR__ . 
'/tests/phpunit/Store/MockChunkAccess.php',
diff --git a/lib/includes/Changes/EntityDiffChangedAspects.php 
b/lib/includes/Changes/EntityDiffChangedAspects.php
new file mode 100644
index 0000000..20b8615
--- /dev/null
+++ b/lib/includes/Changes/EntityDiffChangedAspects.php
@@ -0,0 +1,178 @@
+<?php
+
+namespace Wikibase\Lib\Changes;
+
+use MWException;
+use Serializable;
+use Wikimedia\Assert\Assert;
+
+/**
+ * This class holds a very compact and simple representation of an Entity diff 
for
+ * propagating repo changes to clients (T113468).
+ * This can also be used for entity types which don't have all aspects 
mentioned here,
+ * the aspects are represented as unchanged in that case.
+ *
+ * @license GPL-2.0+
+ * @author Marius Hoch
+ */
+class EntityDiffChangedAspects implements Serializable {
+
+       /**
+        * Increases whenever the array format (self::toArray) changes
+        */
+       const ARRAYFORMATVERSION = 1;
+
+       /**
+        * Language codes of the labels that changed (added, removed or updated)
+        *
+        * @var string[]
+        */
+       private $labelChanges;
+
+       /**
+        * Language codes of the descriptions that changed (added, removed or 
updated)
+        *
+        * @var string[]
+        */
+       private $descriptionChanges;
+
+       /**
+        * Property id serialization from the statements that changed (added, 
removed or updated)
+        *
+        * @var string[]
+        */
+       private $statementChanges;
+
+       /**
+        * Map of site ids to bool. The bool indicates whether only the badge 
has changed (false)
+        * or the actual value of the sitelink changed (true).
+        *
+        * @var bool[]
+        */
+       private $siteLinkChanges;
+
+       /**
+        * Other changes that are not covered in above, like aliases
+        *
+        * @var bool
+        */
+       private $otherChanges;
+
+       /**
+        * Note: If an entity doesn't have a certain aspect, just report that 
no changes happened (empty array).
+        *
+        * @param string[] $labelChanges Language codes of the labels that 
changed (added, removed or updated)
+        * @param string[] $descriptionChanges Language codes of the 
descriptions that changed (added, removed or updated)
+        * @param string[] $statementChanges Property id serialization from the 
statements that changed (added, removed or updated)
+        * @param bool[] $siteLinkChanges Map of site ids to bool: only the 
badge has changed (false) or the actual sitelink changed (true)
+        * @param bool $otherChanges Do we have changes that are not covered 
more specifically?
+        */
+       public function __construct(
+               array $labelChanges,
+               array $descriptionChanges,
+               array $statementChanges,
+               array $siteLinkChanges,
+               $otherChanges
+       ) {
+               Assert::parameterElementType( 'string', $labelChanges, 
'$labelChanges' );
+               Assert::parameterElementType( 'string', $descriptionChanges, 
'$descriptionChanges' );
+               Assert::parameterElementType( 'string', $statementChanges, 
'$statementChanges' );
+               Assert::parameterElementType( 'string', array_keys( 
$siteLinkChanges ), 'array_keys( $siteLinkChanges )' );
+               Assert::parameterElementType( 'boolean', $siteLinkChanges, 
'$siteLinkChanges' );
+               Assert::parameterType( 'boolean', $otherChanges, 
'$otherChanges' );
+
+               $this->labelChanges = $labelChanges;
+               $this->descriptionChanges = $descriptionChanges;
+               $this->statementChanges = $statementChanges;
+               $this->siteLinkChanges = $siteLinkChanges;
+               $this->otherChanges = $otherChanges;
+       }
+
+       /**
+        * Language codes of the labels that changed (added, removed or updated)
+        *
+        * @return string[]
+        */
+       public function getLabelChanges() {
+               return $this->labelChanges;
+       }
+
+       /**
+        * Language codes of the descriptions that changed (added, removed or 
updated)
+        *
+        * @return string[]
+        */
+       public function getDescriptionChanges() {
+               return $this->descriptionChanges;
+       }
+
+       /**
+        * Property id serialization from the statements that changed (added, 
removed or updated)
+        *
+        * @return string[]
+        */
+       public function getStatementChanges() {
+               return $this->statementChanges;
+       }
+
+       /**
+        * Map of site ids to bool: only the badge has changed (false) or the 
actual sitelink changed (true)
+        *
+        * @return bool[]
+        */
+       public function getSiteLinkChanges() {
+               return $this->siteLinkChanges;
+       }
+
+       /**
+        * Do we have changes that are not covered more specifically?
+        *
+        * @return bool
+        */
+       public function hasOtherChanges() {
+               return $this->otherChanges;
+       }
+
+       /**
+        * @see Serializable::serialize
+        *
+        * @return string
+        */
+       public function serialize() {
+               return json_encode( $this->toArray() );
+       }
+
+       /**
+        * @see Serializable::unserialize
+        *
+        * @return string
+        */
+       public function unserialize( $serialized ) {
+               $data = json_decode( $serialized );
+
+               if ( $data->arrayFormatVersion !== self::ARRAYFORMATVERSION ) {
+                       throw new MWException( 'Unsupported format version ' . 
$data->arrayFormatVersion );
+               }
+
+               $this->labelChanges = $data->labelChanges;
+               $this->descriptionChanges = $data->descriptionChanges;
+               $this->statementChanges = $data->statementChanges;
+               $this->siteLinkChanges = (array)$data->siteLinkChanges;
+               $this->otherChanges = $data->otherChanges;
+       }
+
+       /**
+        * @return array[]
+        */
+       public function toArray() {
+               return [
+                       'arrayFormatVersion' => self::ARRAYFORMATVERSION,
+                       'labelChanges' => $this->getLabelChanges(),
+                       'descriptionChanges' => $this->getDescriptionChanges(),
+                       'statementChanges' => $this->getStatementChanges(),
+                       'siteLinkChanges' => $this->getSiteLinkChanges(),
+                       'otherChanges' => $this->hasOtherChanges(),
+               ];
+       }
+
+}
diff --git a/lib/includes/Changes/EntityDiffChangedAspectsFactory.php 
b/lib/includes/Changes/EntityDiffChangedAspectsFactory.php
new file mode 100644
index 0000000..0d7b25a
--- /dev/null
+++ b/lib/includes/Changes/EntityDiffChangedAspectsFactory.php
@@ -0,0 +1,150 @@
+<?php
+
+namespace Wikibase\Lib\Changes;
+
+use Diff\DiffOp\DiffOp;
+use Diff\DiffOp\Diff\Diff;
+use Diff\DiffOp\DiffOpAdd;
+use Diff\DiffOp\DiffOpChange;
+use Diff\DiffOp\DiffOpRemove;
+use Wikibase\DataModel\Statement\Statement;
+use Wikibase\DataModel\Services\Diff\EntityDiff;
+use Wikibase\DataModel\Services\Diff\ItemDiff;
+
+/**
+ * Factory for EntityDiffChangedAspects.
+ *
+ * @license GPL-2.0+
+ * @author Marius Hoch
+ */
+class EntityDiffChangedAspectsFactory {
+
+       /**
+        * Get an EntityDiffChangedAspects instance from an EntityDiff.
+        *
+        * @param Diff $entityDiff
+        * @return EntityDiffChangedAspects
+        */
+       public function newFromEntityDiff( Diff $entityDiff ) {
+               $labelChanges = [];
+               $descriptionChanges = [];
+               $statementChanges = [];
+               $siteLinkChanges = [];
+               $otherChanges = false;
+
+               $remainingDiffOps = count( $entityDiff ); // this is a "deep" 
count!
+
+               if ( $entityDiff instanceof ItemDiff && 
!$entityDiff->getSiteLinkDiff()->isEmpty() ) {
+                       $siteLinkDiff = $entityDiff->getSiteLinkDiff();
+
+                       $remainingDiffOps -= count( $siteLinkDiff );
+                       $siteLinkChanges = $this->getChangedSiteLinks( 
$siteLinkDiff );
+               }
+
+               if ( $entityDiff instanceof EntityDiff ) {
+                       $labelsDiff = $entityDiff->getLabelsDiff();
+                       if ( !empty( $labelsDiff ) ) {
+                               $remainingDiffOps -= count( $labelsDiff );
+                               $labelChanges = $this->getChangedLabels( 
$labelsDiff );
+                       }
+
+                       $descriptionsDiff = $entityDiff->getDescriptionsDiff();
+                       if ( !empty( $descriptionsDiff ) ) {
+                               $remainingDiffOps -= count( $descriptionsDiff );
+                               $descriptionChanges = 
$this->getChangedDescriptions( $descriptionsDiff );
+                       }
+
+                       $claimsDiff = $entityDiff->getClaimsDiff();
+                       if ( !empty( $claimsDiff ) ) {
+                               $remainingDiffOps -= count( $claimsDiff );
+                               $statementChanges = 
$this->getChangedStatements( $claimsDiff );
+                       }
+               }
+
+               if ( $remainingDiffOps > 0 ) {
+                       $otherChanges = true;
+               }
+
+               return new EntityDiffChangedAspects(
+                       $labelChanges,
+                       $descriptionChanges,
+                       $statementChanges,
+                       $siteLinkChanges,
+                       $otherChanges
+               );
+       }
+
+       /**
+        * @param Diff $siteLinkDiff
+        *
+        * @return string[]
+        */
+       private function getChangedSiteLinks( Diff $siteLinkDiff ) {
+               $siteLinkChanges = [];
+
+               foreach ( $siteLinkDiff as $siteId => $diffPerSite ) {
+                       $siteLinkChanges[$siteId] = !$this->isBadgesOnlyChange( 
$diffPerSite );
+               }
+
+               return $siteLinkChanges;
+       }
+
+       /**
+        * @param DiffOp $siteLinkDiffOp
+        *
+        * @return bool
+        */
+       private function isBadgesOnlyChange( DiffOp $siteLinkDiffOp ) {
+               return $siteLinkDiffOp instanceof Diff && !array_key_exists( 
'name', $siteLinkDiffOp );
+       }
+
+       /**
+        * @param Diff $labelsDiff
+        *
+        * @return string[]
+        */
+       private function getChangedLabels( Diff $labelsDiff ) {
+               return array_keys( iterator_to_array( $labelsDiff ) );
+       }
+
+       /**
+        * @param Diff $descriptionsDiff
+        *
+        * @return string[]
+        */
+       private function getChangedDescriptions( Diff $descriptionsDiff ) {
+               return array_keys( iterator_to_array( $descriptionsDiff ) );
+       }
+
+       /**
+        * @param Diff $claimsDiff
+        *
+        * @return string[]
+        */
+       private function getChangedStatements( Diff $claimsDiff ) {
+               $changedStatements = [];
+
+               foreach ( $claimsDiff as $pid => $diffOp ) {
+                       /* @var $statement Statement */
+
+                       if ( $diffOp instanceof DiffOpAdd ) {
+                               $statement = $diffOp->getNewValue();
+                       } elseif ( $diffOp instanceof DiffOpRemove ) {
+                               $statement = $diffOp->getOldValue();
+                       } elseif ( $diffOp instanceof DiffOpChange ) {
+                               $statement = $diffOp->getOldValue();
+                               /* @var $newStatement Statement */
+                               $newStatement = $diffOp->getNewValue();
+
+                               $changedStatements[] = 
$newStatement->getPropertyId()->getSerialization();
+                       } else {
+                               wfLogWarning( 'Unknown DiffOp type ' . 
get_class( $diffOp ) );
+                       }
+
+                       $changedStatements[] = 
$statement->getPropertyId()->getSerialization();
+               }
+
+               return array_unique( $changedStatements );
+       }
+
+}
diff --git a/lib/tests/phpunit/Changes/EntityDiffChangedAspectsFactoryTest.php 
b/lib/tests/phpunit/Changes/EntityDiffChangedAspectsFactoryTest.php
new file mode 100644
index 0000000..ca19dd3
--- /dev/null
+++ b/lib/tests/phpunit/Changes/EntityDiffChangedAspectsFactoryTest.php
@@ -0,0 +1,347 @@
+<?php
+
+namespace Wikibase\Lib\Tests\Changes;
+
+use Diff\DiffOp\DiffOpAdd;
+use PHPUnit_Framework_TestCase;
+use Wikibase\DataModel\Entity\EntityDocument;
+use Wikibase\DataModel\Entity\Item;
+use Wikibase\DataModel\Entity\ItemId;
+use Wikibase\DataModel\Entity\Property;
+use Wikibase\DataModel\Entity\PropertyId;
+use Wikibase\DataModel\Services\Diff\EntityDiff;
+use Wikibase\DataModel\Services\Diff\EntityDiffer;
+use Wikibase\DataModel\SiteLink;
+use Wikibase\DataModel\Snak\PropertyNoValueSnak;
+use Wikibase\DataModel\Snak\PropertySomeValueSnak;
+use Wikibase\DataModel\Statement\Statement;
+use Wikibase\DataModel\Statement\StatementList;
+use Wikibase\Lib\Changes\EntityDiffChangedAspects;
+use Wikibase\Lib\Changes\EntityDiffChangedAspectsFactory;
+
+/**
+ * @covers Wikibase\Lib\Changes\EntityDiffChangedAspectsFactory
+ *
+ * @group Wikibase
+ * @group WikibaseChange
+ *
+ * @license GPL-2.0+
+ * @author Marius Hoch
+ */
+class EntityDiffChangedAspectsFactoryTest extends PHPUnit_Framework_TestCase {
+
+       public function provideNewFromEntityDiff() {
+               $emptyDiff = [
+                       'arrayFormatVersion' => 
EntityDiffChangedAspects::ARRAYFORMATVERSION,
+                       'labelChanges' => [],
+                       'descriptionChanges' => [],
+                       'statementChanges' => [],
+                       'siteLinkChanges' => [],
+                       'otherChanges' => false,
+               ];
+
+               $labelDiff = $emptyDiff;
+               $labelDiff['labelChanges'] = [ 'de' ];
+
+               $descriptionDiff = $emptyDiff;
+               $descriptionDiff['descriptionChanges'] = [ 'ru' ];
+
+               $statementP1Diff = $emptyDiff;
+               $statementP1Diff['statementChanges'] = [ 'P1' ];
+
+               $statementP2Diff = $emptyDiff;
+               $statementP2Diff['statementChanges'] = [ 'P2' ];
+
+               $statementP1P2Diff = $emptyDiff;
+               $statementP1P2Diff['statementChanges'] = [ 'P1', 'P2' ];
+
+               $siteLinkDiff = $emptyDiff;
+               $siteLinkDiff['siteLinkChanges'] = [ 'enwiki' => true ];
+
+               $siteLinkBadgeOnlyDiff = $emptyDiff;
+               $siteLinkBadgeOnlyDiff['siteLinkChanges'] = [ 'enwiki' => false 
];
+
+               $berlinEmptyDiff = [
+                       'arrayFormatVersion' => 
EntityDiffChangedAspects::ARRAYFORMATVERSION,
+                       'labelChanges' => [ 'de', 'ru' ],
+                       'descriptionChanges' => [ 'de', 'es' ],
+                       'statementChanges' => [ 'P2' ],
+                       'siteLinkChanges' => [ 'dewiki' => true, 'enwiki' => 
true ],
+                       'otherChanges' => false,
+               ];
+
+               $parisEmptyDiff = [
+                       'arrayFormatVersion' => 
EntityDiffChangedAspects::ARRAYFORMATVERSION,
+                       'labelChanges' => [ 'fr', 'ru' ],
+                       'descriptionChanges' => [ 'de', 'es', 'pl' ],
+                       'statementChanges' => [ 'P2' ],
+                       'siteLinkChanges' => [ 'dewiki' => true, 'enwiki' => 
true, 'ruwiki' => true ],
+                       'otherChanges' => false,
+               ];
+
+               $berlinParisDiff = [
+                       'arrayFormatVersion' => 
EntityDiffChangedAspects::ARRAYFORMATVERSION,
+                       'labelChanges' => [ 'de', 'fr', 'ru' ],
+                       'descriptionChanges' => [ 'pl' ],
+                       'statementChanges' => [ 'P2' ],
+                       'siteLinkChanges' => [ 'dewiki' => true, 'enwiki' => 
true, 'ruwiki' => true ],
+                       'otherChanges' => false,
+               ];
+
+               $q2 = new ItemId( 'Q2' );
+               $p1 = new PropertyId( 'P1' );
+               $p2 = new PropertyId( 'P2' );
+
+               $noValueP1Statement = new Statement( new PropertyNoValueSnak( 
$p1 ) );
+               $noValueP1Statements = new StatementList( [ $noValueP1Statement 
] );
+
+               $noValueP2Statement = new Statement( new PropertyNoValueSnak( 
$p2 ) );
+               $noValueP2Statements = new StatementList( [ $noValueP2Statement 
] );
+
+               $someValueStatement = new Statement( new PropertySomeValueSnak( 
$p2 ) );
+               $someValueStatements = new StatementList( [ $someValueStatement 
] );
+
+               $emptyItem = new Item( $q2, null, null, null );
+               $emptyProperty = new Property( $p2, null, 'hey', null );
+
+               $labelItem = $emptyItem->copy();
+               $labelItem->setLabel( 'de', 'de label' );
+
+               $descriptionItem = $emptyItem->copy();
+               $descriptionItem->setDescription( 'ru', 'ru desc' );
+
+               $noValueP1StatementItem = $emptyItem->copy();
+               $noValueP1StatementItem->setStatements( $noValueP1Statements );
+
+               $noValueP2StatementItem = $emptyItem->copy();
+               $noValueP2StatementItem->setStatements( $noValueP2Statements );
+
+               $someValueStatementItem = $emptyItem->copy();
+               $someValueStatementItem->setStatements( $someValueStatements );
+
+               $siteLinkItem = $emptyItem->copy();
+               $siteLinkItem->addSiteLink( new SiteLink( 'enwiki', 'PHP' ) );
+
+               $siteLinkBadgeItem = $emptyItem->copy();
+               $siteLinkBadgeItem->addSiteLink( new SiteLink( 'enwiki', 'PHP', 
[ $q2 ] ) );
+
+               $berlinItem = $emptyItem->copy();
+               $berlinItem->addSiteLink( new SiteLink( 'enwiki', 'Berlin' ) );
+               $berlinItem->addSiteLink( new SiteLink( 'dewiki', 'Berlin', [ 
$q2 ] ) );
+               $berlinItem->setLabel( 'de', 'Berlin' );
+               $berlinItem->setLabel( 'ru', 'Берлин' );
+               $berlinItem->setDescription( 'de', 'abc' );
+               $berlinItem->setDescription( 'es', 'def' );
+               $berlinItem->setStatements( $someValueStatements );
+
+               $parisItem = $emptyItem->copy();
+               $parisItem->addSiteLink( new SiteLink( 'enwiki', 'Paris' ) );
+               $parisItem->addSiteLink( new SiteLink( 'dewiki', 'Paris', [ $q2 
] ) );
+               $parisItem->addSiteLink( new SiteLink( 'ruwiki', 'Paris' ) );
+               $parisItem->setLabel( 'fr', 'Paris' );
+               $parisItem->setLabel( 'ru', 'ru label' );
+               $parisItem->setDescription( 'de', 'abc' );
+               $parisItem->setDescription( 'es', 'def' );
+               $parisItem->setDescription( 'pl', 'xyz' );
+               $parisItem->setStatements( $noValueP2Statements );
+
+               $noValueP1Property = $emptyProperty->copy();
+               $noValueP1Property->setStatements( $noValueP1Statements );
+
+               $cases = [
+                       '$emptyItem === $emptyItem' => [
+                               $emptyDiff,
+                               $emptyItem,
+                               $emptyItem
+                       ],
+                       '$emptyProperty === $emptyProperty' => [
+                               $emptyDiff,
+                               $emptyProperty,
+                               $emptyProperty
+                       ],
+                       '$labelItem === $labelItem' => [
+                               $emptyDiff,
+                               $labelItem,
+                               $labelItem
+                       ],
+                       '$descriptionItem === $descriptionItem' => [
+                               $emptyDiff,
+                               $descriptionItem,
+                               $descriptionItem
+                       ],
+                       '$noValueP1StatementItem === $noValueP1StatementItem' 
=> [
+                               $emptyDiff,
+                               $noValueP1StatementItem,
+                               $noValueP1StatementItem
+                       ],
+                       '$noValueP2StatementItem === $noValueP2StatementItem' 
=> [
+                               $emptyDiff,
+                               $noValueP2StatementItem,
+                               $noValueP2StatementItem
+                       ],
+                       '$someValueStatementItem === $someValueStatementItem' 
=> [
+                               $emptyDiff,
+                               $someValueStatementItem,
+                               $someValueStatementItem
+                       ],
+                       '$siteLinkItem === $siteLinkItem' => [
+                               $emptyDiff,
+                               $siteLinkItem,
+                               $siteLinkItem
+                       ],
+                       '$siteLinkBadgeItem === $siteLinkBadgeItem' => [
+                               $emptyDiff,
+                               $siteLinkBadgeItem,
+                               $siteLinkBadgeItem
+                       ],
+                       '$berlinItem === $berlinItem' => [
+                               $emptyDiff,
+                               $berlinItem,
+                               $berlinItem
+                       ],
+                       '$parisItem === $parisItem' => [
+                               $emptyDiff,
+                               $parisItem,
+                               $parisItem
+                       ],
+                       '$noValueP1Property === $noValueP1Property' => [
+                               $emptyDiff,
+                               $noValueP1Property,
+                               $noValueP1Property
+                       ],
+                       'label change' => [
+                               $labelDiff,
+                               $emptyItem,
+                               $labelItem
+                       ],
+                       'description changes' => [
+                               $descriptionDiff,
+                               $emptyItem,
+                               $descriptionItem
+                       ],
+                       'item statement change (no value)' => [
+                               $statementP1Diff,
+                               $emptyItem,
+                               $noValueP1StatementItem
+                       ],
+                       'property statement change (no value)' => [
+                               $statementP1Diff,
+                               $emptyProperty,
+                               $noValueP1Property
+                       ],
+                       'statement change (some value)' => [
+                               $statementP2Diff,
+                               $emptyItem,
+                               $someValueStatementItem
+                       ],
+                       'statement change (other property id + some value <> no 
value)' => [
+                               $statementP1P2Diff,
+                               $someValueStatementItem,
+                               $noValueP1StatementItem
+                       ],
+                       'statement change (some property id + some value <> no 
value)' => [
+                               $statementP2Diff,
+                               $someValueStatementItem,
+                               $noValueP2StatementItem
+                       ],
+                       'sitelink changes' => [
+                               $siteLinkDiff,
+                               $emptyItem,
+                               $siteLinkItem
+                       ],
+                       'sitelink change with badge' => [
+                               $siteLinkDiff,
+                               $emptyItem,
+                               $siteLinkBadgeItem
+                       ],
+                       'sitelink badge only changes' => [
+                               $siteLinkBadgeOnlyDiff,
+                               $siteLinkItem,
+                               $siteLinkBadgeItem
+                       ],
+                       'berlin item <> empty item' => [
+                               $berlinEmptyDiff,
+                               $emptyItem,
+                               $berlinItem
+                       ],
+                       'paris item <> empty item' => [
+                               $parisEmptyDiff,
+                               $emptyItem,
+                               $parisItem
+                       ],
+                       'paris item <> berlin item' => [
+                               $berlinParisDiff,
+                               $berlinItem,
+                               $parisItem
+                       ],
+               ];
+
+               // All cases should result in the same aspect diff if the old 
and new entity are exchanged.
+               $reverseTests = [];
+               foreach ( $cases as $testDescription => $case ) {
+                       $reverseTests[$testDescription . ' (reversed)'] = [
+                               $case[0],
+                               $case[2],
+                               $case[1]
+                       ];
+               }
+
+               return array_merge( $cases, $reverseTests );
+       }
+
+       /**
+        * @dataProvider provideNewFromEntityDiff
+        */
+       public function testNewFromEntityDiff(
+               array $expectedDiffArray,
+               EntityDocument $oldEntity,
+               EntityDocument $newEntity
+       ) {
+               $entityDiffer = new EntityDiffer();
+               $entityDiff = $entityDiffer->diffEntities( $oldEntity, 
$newEntity );
+
+               $factory = new EntityDiffChangedAspectsFactory();
+               $entityDiffChangedAspects = $factory->newFromEntityDiff( 
$entityDiff );
+               $actual = $entityDiffChangedAspects->toArray();
+
+               $this->sortSubArrays( $actual );
+               $this->sortSubArrays( $expectedDiffArray );
+
+               $this->assertEquals( $expectedDiffArray, $actual );
+       }
+
+       public function testNewFromEntityDiff_otherChanges() {
+               $entityDiff = new EntityDiff();
+               // Add some unknown change
+               $entityDiff->addOperations( [ new DiffOpAdd( 1 ) ] );
+
+               $factory = new EntityDiffChangedAspectsFactory();
+               $entityDiffChangedAspects = $factory->newFromEntityDiff( 
$entityDiff );
+               $actual = $entityDiffChangedAspects->toArray();
+
+               $expectedDiff = [
+                       'arrayFormatVersion' => 
EntityDiffChangedAspects::ARRAYFORMATVERSION,
+                       'labelChanges' => [],
+                       'descriptionChanges' => [],
+                       'statementChanges' => [],
+                       'siteLinkChanges' => [],
+                       'otherChanges' => true,
+               ];
+
+               $this->assertEquals( $expectedDiff, $actual );
+       }
+
+       /**
+        * Sort all sub-arrays (but leave the array itself alone).
+        *
+        * @param array &$arr
+        */
+       private function sortSubArrays( array &$arr ) {
+               foreach ( $arr as &$subArr ) {
+                       if ( is_array( $subArr ) ) {
+                               sort( $subArr );
+                       }
+               }
+       }
+
+}
diff --git a/lib/tests/phpunit/Changes/EntityDiffChangedAspectsTest.php 
b/lib/tests/phpunit/Changes/EntityDiffChangedAspectsTest.php
new file mode 100644
index 0000000..bd1bd23
--- /dev/null
+++ b/lib/tests/phpunit/Changes/EntityDiffChangedAspectsTest.php
@@ -0,0 +1,190 @@
+<?php
+
+namespace Wikibase\Lib\Tests\Changes;
+
+use InvalidArgumentException;
+use MWException;
+use PHPUnit_Framework_TestCase;
+use Wikibase\Lib\Changes\EntityDiffChangedAspects;
+
+/**
+ * @covers Wikibase\Lib\Changes\EntityDiffChangedAspects
+ *
+ * @group Wikibase
+ * @group WikibaseChange
+ *
+ * @license GPL-2.0+
+ * @author Marius Hoch
+ */
+class EntityDiffChangedAspectsTest extends PHPUnit_Framework_TestCase {
+
+       public function invalidConstructionProvider() {
+               $validParams = [
+                       'labelChanges' => [ 'a', '1' ],
+                       'descriptionChanges' => [ 'b', '2' ],
+                       'statementChanges' => [ 'c', '3' ],
+                       'siteLinkChanges' => [ 'd' => true ],
+                       'otherChanges' => true,
+               ];
+
+               $invalidLabelChanges = $validParams;
+               $invalidLabelChanges['labelChanges'] = [ 'a', 1 ];
+
+               $invalidDescriptionChanges = $validParams;
+               $invalidDescriptionChanges['descriptionChanges'] = [ 'b', 2 ];
+
+               $invalidStatementChanges = $validParams;
+               $invalidStatementChanges['statementChanges'] = [ 'c', 3 ];
+
+               $invalidSiteLinkChangesKeys = $validParams;
+               $invalidSiteLinkChangesKeys['siteLinkChanges'] = [ 1 => true ];
+
+               $invalidSiteLinkChangesValues = $validParams;
+               $invalidSiteLinkChangesValues['siteLinkChanges'] = [ 'd' => 12 
];
+
+               $invalidOtherChanges = $validParams;
+               $invalidOtherChanges['otherChanges'] = null;
+
+               return [
+                       'Invalid labelChanges' => $invalidLabelChanges,
+                       'Invalid descriptionChanges' => 
$invalidDescriptionChanges,
+                       'Invalid statementChanges' => $invalidStatementChanges,
+                       'Invalid siteLinkChanges keys' => 
$invalidSiteLinkChangesKeys,
+                       'Invalid siteLinkChanges values' => 
$invalidSiteLinkChangesValues,
+                       'Invalid otherChanges' => $invalidOtherChanges,
+               ];
+       }
+
+       /**
+        * @dataProvider invalidConstructionProvider
+        */
+       public function testInvalidConstruction(
+               array $labelChanges,
+               array $descriptionChanges,
+               array $statementChanges,
+               array $siteLinkChanges,
+               $otherChanges
+       ) {
+               $this->setExpectedException( InvalidArgumentException::class );
+
+               new EntityDiffChangedAspects( $labelChanges, 
$descriptionChanges, $statementChanges, $siteLinkChanges, $otherChanges );
+       }
+
+       private function getEntityDiffChangedAspects() {
+               return new EntityDiffChangedAspects(
+                       [ 'a', '1' ],
+                       [ 'b', '2' ],
+                       [ 'c', '3' ],
+                       [ 'd' => true ],
+                       true
+               );
+       }
+
+       public function testGetLabelChanges() {
+               $this->assertSame(
+                       [ 'a', '1' ],
+                       $this->getEntityDiffChangedAspects()->getLabelChanges()
+               );
+       }
+
+       public function testGetDescriptionChanges() {
+               $this->assertSame(
+                       [ 'b', '2' ],
+                       
$this->getEntityDiffChangedAspects()->getDescriptionChanges()
+               );
+       }
+
+       public function testGetStatementChanges() {
+               $this->assertSame(
+                       [ 'c', '3' ],
+                       
$this->getEntityDiffChangedAspects()->getStatementChanges()
+               );
+       }
+
+       public function testGetSiteLinkChanges() {
+               $this->assertSame(
+                       [ 'd' => true ],
+                       
$this->getEntityDiffChangedAspects()->getSiteLinkChanges()
+               );
+       }
+
+       public function testHasOtherChanges() {
+               $this->assertSame(
+                       true,
+                       $this->getEntityDiffChangedAspects()->hasOtherChanges()
+               );
+       }
+
+       public function testToArray() {
+               $expected = [
+                       'arrayFormatVersion' => 
EntityDiffChangedAspects::ARRAYFORMATVERSION,
+                       'labelChanges' => [ 'a', '1' ],
+                       'descriptionChanges' => [ 'b', '2' ],
+                       'statementChanges' => [ 'c', '3' ],
+                       'siteLinkChanges' => [ 'd' => true ],
+                       'otherChanges' => true,
+               ];
+
+               $this->assertSame( $expected, 
$this->getEntityDiffChangedAspects()->toArray() );
+       }
+
+       public function testSerialize() {
+               $entityDiffChangedAspects = 
$this->getEntityDiffChangedAspects();
+
+               $entityDiffChangedAspectsClone = unserialize( serialize( 
$entityDiffChangedAspects ) );
+
+               $this->assertSame( $entityDiffChangedAspects->toArray(), 
$entityDiffChangedAspectsClone->toArray() );
+       }
+
+       /**
+        * @return string
+        */
+       private function getKnownGoodSerialization() {
+               return 
'C:45:"Wikibase\Lib\Changes\EntityDiffChangedAspects":129:' .
+                       
'{{"arrayFormatVersion":1,"labelChanges":[],"descriptionChanges":[],' .
+                       
'"statementChanges":[],"siteLinkChanges":[],"otherChanges":true}}';
+       }
+
+       public function testUnserialize() {
+               $entityDiffChangedAspects = unserialize( 
$this->getKnownGoodSerialization() );
+
+               $this->assertSame(
+                       ( new EntityDiffChangedAspects( [], [], [], [], true ) 
)->toArray(),
+                       $entityDiffChangedAspects->toArray()
+               );
+       }
+
+       public function wrongArrayFormatVersionProvider() {
+               // NOTE: If you remove versions here, make sure all good ones 
can be unserialized!
+               return [
+                       [ -1 ],
+                       [ 0 ],
+                       [ 2 ],
+                       [ '"Milch"' ],
+               ];
+       }
+
+       /**
+        * @dataProvider wrongArrayFormatVersionProvider
+        */
+       public function testUnserialize_wrongFormatVersion( $arrayFormatVersion 
) {
+               $entityDiffChangedAspectsSerialization = 
$this->getKnownGoodSerialization();
+
+               // Change the array version in the serialization
+               $entityDiffChangedAspectsSerialization = str_replace(
+                       '"arrayFormatVersion":1',
+                       '"arrayFormatVersion":' . $arrayFormatVersion,
+                       $entityDiffChangedAspectsSerialization
+               );
+               // Change the length of the serialization "body" (the content 
from Serializable::serialize)
+               $entityDiffChangedAspectsSerialization = str_replace(
+                       '":129:',
+                       '":' . ( strlen( $arrayFormatVersion ) + 128 ) . ':',
+                       $entityDiffChangedAspectsSerialization
+               );
+
+               $this->setExpectedException( MWException::class );
+               unserialize( $entityDiffChangedAspectsSerialization );
+       }
+
+}

-- 
To view, visit https://gerrit.wikimedia.org/r/384298
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: merged
Gerrit-Change-Id: I0ac579e2063dfe92a974841330586423e5e7f393
Gerrit-PatchSet: 9
Gerrit-Project: mediawiki/extensions/Wikibase
Gerrit-Branch: master
Gerrit-Owner: Hoo man <h...@online.de>
Gerrit-Reviewer: Addshore <addshorew...@gmail.com>
Gerrit-Reviewer: Daniel Kinzler <daniel.kinz...@wikimedia.de>
Gerrit-Reviewer: Hoo man <h...@online.de>
Gerrit-Reviewer: Ladsgroup <ladsgr...@gmail.com>
Gerrit-Reviewer: Thiemo Mättig (WMDE) <thiemo.kr...@wikimedia.de>
Gerrit-Reviewer: jenkins-bot <>

_______________________________________________
MediaWiki-commits mailing list
MediaWiki-commits@lists.wikimedia.org
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to