public function testGivenTwoEmptyItems_emptyItemDiffIsReturned() { $differ = new EntityDiffer(); $diff = $differ->diffEntities(new Item(), new Item()); $this->assertInstanceOf('Wikibase\\DataModel\\Services\\Diff\\ItemDiff', $diff); $this->assertTrue($diff->isEmpty()); }
/** * Constructs an EntityChange from the given old and new Entity. * * @since 0.5 * * @param string $action The action name * @param EntityDocument|null $oldEntity * @param EntityDocument|null $newEntity * @param array $fields additional fields to set * * @return EntityChange * @throws MWException */ public function newFromUpdate($action, EntityDocument $oldEntity = null, EntityDocument $newEntity = null, array $fields = array()) { if ($oldEntity === null && $newEntity === null) { throw new MWException('Either $oldEntity or $newEntity must be given'); } if ($oldEntity === null) { $oldEntity = $this->entityFactory->newEmpty($newEntity->getType()); $id = $newEntity->getId(); } elseif ($newEntity === null) { $newEntity = $this->entityFactory->newEmpty($oldEntity->getType()); $id = $oldEntity->getId(); } elseif ($oldEntity->getType() !== $newEntity->getType()) { throw new MWException('Entity type mismatch'); } else { $id = $newEntity->getId(); } // HACK: don't include statements diff, since those are unused and not helpful // performance-wise to the dispatcher and change handling. // For a better solution, see T113468. if ($oldEntity instanceof StatementListHolder) { $oldEntity->setStatements(new StatementList()); $newEntity->setStatements(new StatementList()); } $diff = $this->entityDiffer->diffEntities($oldEntity, $newEntity); /** @var EntityChange $instance */ $instance = self::newForEntity($action, $id, $fields); $instance->setDiff($diff); return $instance; }
/** * @dataProvider provideConflictDetection */ public function testConflictDetection(Entity $base, Entity $current, Entity $new, $expectedConflicts) { $differ = new EntityDiffer(); $patcher = new EntityPatcher(); $patch = $differ->diffEntities($base, $new); $patchedCurrent = unserialize(serialize($current)); $patcher->patchEntity($patchedCurrent, $patch); $cleanPatch = $differ->diffEntities($base, $patchedCurrent); $conflicts = $patch->count() - $cleanPatch->count(); $this->assertEquals($expectedConflicts, $conflicts, 'check number of conflicts detected'); }
/** * Attempts to fix an edit conflict by patching the intended change into the latest revision after * checking for conflicts. This modifies $this->newEntity but does not write anything to the * database. Saving of the new content may still fail. * * @return bool True if the conflict could be resolved, false otherwise */ public function fixEditConflict() { $baseRev = $this->getBaseRevision(); $latestRev = $this->getLatestRevision(); if (!$latestRev) { wfLogWarning('Failed to load latest revision of entity ' . $this->newEntity->getId() . '! ' . 'This may indicate entries missing from thw wb_entities_per_page table.'); return false; } $entityDiffer = new EntityDiffer(); $entityPatcher = new EntityPatcher(); // calculate patch against base revision // NOTE: will fail if $baseRev or $base are null, which they may be if // this gets called at an inappropriate time. The data flow in this class // should be improved. $patch = $entityDiffer->diffEntities($baseRev->getEntity(), $this->newEntity); if ($patch->isEmpty()) { // we didn't technically fix anything, but if there is nothing to change, // so just keep the current content as it is. $this->newEntity = $latestRev->getEntity()->copy(); return true; } // apply the patch( base -> new ) to the latest revision. $patchedLatest = $latestRev->getEntity()->copy(); $entityPatcher->patchEntity($patchedLatest, $patch); // detect conflicts against latest revision $cleanPatch = $entityDiffer->diffEntities($latestRev->getEntity(), $patchedLatest); $conflicts = $patch->count() - $cleanPatch->count(); if ($conflicts > 0) { // patch doesn't apply cleanly if ($this->userWasLastToEdit($this->user, $this->newEntity->getId(), $this->getBaseRevisionId())) { // it's a self-conflict if ($cleanPatch->count() === 0) { // patch collapsed, possibly because of diff operation change from base to latest return false; } else { // we still have a working patch, try to apply $this->status->warning('wikibase-self-conflict-patched'); } } else { // there are unresolvable conflicts. return false; } } else { // can apply cleanly $this->status->warning('wikibase-conflict-patched'); } // remember the patched entity as the actual new entity to save $this->newEntity = $patchedLatest; return true; }
/** * Returns a diff between this EntityContent and the given EntityContent. * * @param EntityContent $toContent * * @return EntityContentDiff */ public function getDiff(EntityContent $toContent) { $fromContent = $this; $differ = new MapDiffer(); $redirectDiffOps = $differ->doDiff($fromContent->getRedirectData(), $toContent->getRedirectData()); $redirectDiff = new Diff($redirectDiffOps, true); $fromEntity = $fromContent->isRedirect() ? $this->makeEmptyEntity() : $fromContent->getEntity(); $toEntity = $toContent->isRedirect() ? $this->makeEmptyEntity() : $toContent->getEntity(); $entityDiffer = new EntityDiffer(); $entityDiff = $entityDiffer->diffEntities($fromEntity, $toEntity); return new EntityContentDiff($entityDiff, $redirectDiff); }