/** * @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'); }
/** * Returns a patched copy of this Content object. * * @param EntityContentDiff $patch * * @throws PatcherException * @return EntityContent */ public function getPatchedCopy(EntityContentDiff $patch) { /* @var EntityHandler $handler */ $handler = $this->getContentHandler(); if ($this->isRedirect()) { $entityAfterPatch = $this->makeEmptyEntity(); } else { $entityAfterPatch = unserialize(serialize($this->getEntity())); } // FIXME: this should either be done in the derivatives, or the patcher // should be injected, so the application can add support for additional entity types. $patcher = new EntityPatcher(); $patcher->patchEntity($entityAfterPatch, $patch->getEntityDiff()); $redirAfterPatch = $this->getPatchedRedirect($patch->getRedirectDiff()); if ($redirAfterPatch !== null && !$entityAfterPatch->isEmpty()) { throw new PatcherException('EntityContent must not contain Entity data as well as' . ' a redirect after applying the patch!'); } elseif ($redirAfterPatch) { $patched = $handler->makeEntityRedirectContent($redirAfterPatch); if (!$patched) { throw new PatcherException('Cannot create a redirect using content model ' . $this->getModel() . '!'); } } else { $patched = $handler->makeEntityContent(new EntityInstanceHolder($entityAfterPatch)); } return $patched; }
/** * 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; }
public function testGivenNonSupportedEntity_exceptionIsThrown() { $patcher = new EntityPatcher(); $this->setExpectedException('RuntimeException'); $patcher->patchEntity(new EntityOfUnknownType(), new EntityDiff()); }