MCR: Deprecate and gut Revision class
This is a re-submission of I4f24e7fbb68. As a first major step towards Multi-Content-Revisions (MCR), this patch turns the Revision class into a legacy proxy for the new RevisionRecord and RevisionStore classes. Backwards compatibility is maintained for all but some rare edge cases, like constructing a completely empty Revision object. For more information on MCR, see <https://www.mediawiki.org/wiki/Requests_for_comment/Multi-Content_Revisions>. NOTE: once this is merged, verify create/delete/restore cycle on beta, ideally with emulated replication lag. Bug: T174025 Change-Id: Ia4c20a91e98df0b9b14b138eb4825c55e5200384
This commit is contained in:
parent
13df3d2290
commit
6af796f3e0
13 changed files with 717 additions and 1420 deletions
|
|
@ -71,6 +71,10 @@ changes to languages because of Phabricator reports.
|
|||
* (T180052) Mirandese (mwl) now supports gendered NS_USER/NS_USER_TALK namespaces.
|
||||
|
||||
=== Other changes in 1.31 ===
|
||||
* Introducing multi-content-revision capability into the storage layer. For details,
|
||||
see <https://www.mediawiki.org/wiki/Requests_for_comment/Multi-Content_Revisions>.
|
||||
* The Revision class was deprecated in favor of RevisionStore, BlobStore, and
|
||||
RevisionRecord and its subclasses.
|
||||
* MessageBlobStore::insertMessageBlob() (deprecated in 1.27) was removed.
|
||||
* The global function wfBCP47 was renamed to LanguageCode::bcp47.
|
||||
* The global function wfBCP47 is now deprecated.
|
||||
|
|
@ -123,6 +127,9 @@ changes to languages because of Phabricator reports.
|
|||
* The Block class will no longer accept usable-but-missing usernames for
|
||||
'byText' or ->setBlocker(). Callers should either ensure the blocker exists
|
||||
locally or use a new interwiki-format username like "iw>Example".
|
||||
* The RevisionInsertComplete hook is now deprecated, use RevisionRecordInserted instead.
|
||||
RevisionInsertComplete is still called, but the second and third parameter will always be null.
|
||||
Hard deprecation is scheduled for 1.32.
|
||||
* The following methods that get and set ParserOutput state are deprecated.
|
||||
Callers should use the new stateless $options parameter to
|
||||
ParserOutput::getText() instead.
|
||||
|
|
|
|||
|
|
@ -2810,14 +2810,14 @@ called after the addition of 'qunit' and MediaWiki testing resources.
|
|||
added to any module.
|
||||
&$ResourceLoader: object
|
||||
|
||||
'RevisionInsertComplete': Called after a revision is inserted into the database.
|
||||
&$revision: the Revision
|
||||
$data: the data stored in old_text. The meaning depends on $flags: if external
|
||||
is set, it's the URL of the revision text in external storage; otherwise,
|
||||
it's the revision text itself. In either case, if gzip is set, the revision
|
||||
text is gzipped.
|
||||
$flags: a comma-delimited list of strings representing the options used. May
|
||||
include: utf8 (this will always be set for new revisions); gzip; external.
|
||||
'RevisionRecordInserted': Called after a revision is inserted into the database.
|
||||
$revisionRecord: the RevisionRecord that has just been inserted.
|
||||
|
||||
'RevisionInsertComplete': DEPRECATED! Use RevisionRecordInserted hook instead.
|
||||
Called after a revision is inserted into the database.
|
||||
$revision: the Revision
|
||||
$data: DEPRECATED! Always null!
|
||||
$flags: DEPRECATED! Always null!
|
||||
|
||||
'SearchableNamespaces': An option to modify which namespaces are searchable.
|
||||
&$arr: Array of namespaces ($nsId => $name) which will be used.
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -450,6 +450,46 @@ return [
|
|||
return $factory;
|
||||
},
|
||||
|
||||
'RevisionStore' => function ( MediaWikiServices $services ) {
|
||||
/** @var SqlBlobStore $blobStore */
|
||||
$blobStore = $services->getService( '_SqlBlobStore' );
|
||||
|
||||
$store = new RevisionStore(
|
||||
$services->getDBLoadBalancer(),
|
||||
$blobStore,
|
||||
$services->getMainWANObjectCache()
|
||||
);
|
||||
|
||||
$config = $services->getMainConfig();
|
||||
$store->setContentHandlerUseDB( $config->get( 'ContentHandlerUseDB' ) );
|
||||
|
||||
return $store;
|
||||
},
|
||||
|
||||
'BlobStore' => function ( MediaWikiServices $services ) {
|
||||
return $services->getService( '_SqlBlobStore' );
|
||||
},
|
||||
|
||||
'_SqlBlobStore' => function ( MediaWikiServices $services ) {
|
||||
global $wgContLang; // TODO: manage $wgContLang as a service
|
||||
|
||||
$store = new SqlBlobStore(
|
||||
$services->getDBLoadBalancer(),
|
||||
$services->getMainWANObjectCache()
|
||||
);
|
||||
|
||||
$config = $services->getMainConfig();
|
||||
$store->setCompressRevisions( $config->get( 'CompressRevisions' ) );
|
||||
$store->setCacheExpiry( $config->get( 'RevisionCacheExpiry' ) );
|
||||
$store->setUseExternalStore( $config->get( 'DefaultExternalStore' ) !== false );
|
||||
|
||||
if ( $config->get( 'LegacyEncoding' ) ) {
|
||||
$store->setLegacyEncoding( $config->get( 'LegacyEncoding' ), $wgContLang );
|
||||
}
|
||||
|
||||
return $store;
|
||||
},
|
||||
|
||||
'ExternalStoreFactory' => function ( MediaWikiServices $services ) {
|
||||
$config = $services->getMainConfig();
|
||||
|
||||
|
|
|
|||
|
|
@ -335,8 +335,8 @@ class HistoryAction extends FormlessAction {
|
|||
* @return FeedItem
|
||||
*/
|
||||
function feedItem( $row ) {
|
||||
$rev = new Revision( $row );
|
||||
$rev->setTitle( $this->getTitle() );
|
||||
$rev = new Revision( $row, 0, $this->getTitle() );
|
||||
|
||||
$text = FeedUtils::formatDiffRow(
|
||||
$this->getTitle(),
|
||||
$this->getTitle()->getPreviousRevisionID( $rev->getId() ),
|
||||
|
|
@ -639,12 +639,10 @@ class HistoryPager extends ReverseChronologicalPager {
|
|||
*/
|
||||
function historyLine( $row, $next, $notificationtimestamp = false,
|
||||
$latest = false, $firstInList = false ) {
|
||||
$rev = new Revision( $row );
|
||||
$rev->setTitle( $this->getTitle() );
|
||||
$rev = new Revision( $row, 0, $this->getTitle() );
|
||||
|
||||
if ( is_object( $next ) ) {
|
||||
$prevRev = new Revision( $next );
|
||||
$prevRev->setTitle( $this->getTitle() );
|
||||
$prevRev = new Revision( $next, 0, $this->getTitle() );
|
||||
} else {
|
||||
$prevRev = null;
|
||||
}
|
||||
|
|
|
|||
3
includes/cache/MessageCache.php
vendored
3
includes/cache/MessageCache.php
vendored
|
|
@ -1048,8 +1048,7 @@ class MessageCache {
|
|||
if ( $titleObj->getLatestRevID() ) {
|
||||
$revision = Revision::newKnownCurrent(
|
||||
$dbr,
|
||||
$titleObj->getArticleID(),
|
||||
$titleObj->getLatestRevID()
|
||||
$titleObj
|
||||
);
|
||||
} else {
|
||||
$revision = false;
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@
|
|||
use MediaWiki\Edit\PreparedEdit;
|
||||
use \MediaWiki\Logger\LoggerFactory;
|
||||
use \MediaWiki\MediaWikiServices;
|
||||
use Wikimedia\Assert\Assert;
|
||||
use Wikimedia\Rdbms\FakeResultWrapper;
|
||||
use Wikimedia\Rdbms\IDatabase;
|
||||
use Wikimedia\Rdbms\DBError;
|
||||
|
|
@ -671,7 +672,7 @@ class WikiPage implements Page, IDBAccessObject {
|
|||
$revision = Revision::newFromPageId( $this->getId(), $latest, $flags );
|
||||
} else {
|
||||
$dbr = wfGetDB( DB_REPLICA );
|
||||
$revision = Revision::newKnownCurrent( $dbr, $this->getId(), $latest );
|
||||
$revision = Revision::newKnownCurrent( $dbr, $this->getTitle(), $latest );
|
||||
}
|
||||
|
||||
if ( $revision ) { // sanity
|
||||
|
|
@ -1264,8 +1265,11 @@ class WikiPage implements Page, IDBAccessObject {
|
|||
$conditions['page_latest'] = $lastRevision;
|
||||
}
|
||||
|
||||
$revId = $revision->getId();
|
||||
Assert::parameter( $revId > 0, '$revision->getId()', 'must be > 0' );
|
||||
|
||||
$row = [ /* SET */
|
||||
'page_latest' => $revision->getId(),
|
||||
'page_latest' => $revId,
|
||||
'page_touched' => $dbw->timestamp( $revision->getTimestamp() ),
|
||||
'page_is_new' => ( $lastRevision === 0 ) ? 1 : 0,
|
||||
'page_is_redirect' => $rt !== null ? 1 : 0,
|
||||
|
|
|
|||
|
|
@ -3498,13 +3498,7 @@ class Parser {
|
|||
* @return Revision|bool False if missing
|
||||
*/
|
||||
public static function statelessFetchRevision( Title $title, $parser = false ) {
|
||||
$pageId = $title->getArticleID();
|
||||
$revId = $title->getLatestRevID();
|
||||
|
||||
$rev = Revision::newKnownCurrent( wfGetDB( DB_REPLICA ), $pageId, $revId );
|
||||
if ( $rev ) {
|
||||
$rev->setTitle( $title );
|
||||
}
|
||||
$rev = Revision::newKnownCurrent( wfGetDB( DB_REPLICA ), $title );
|
||||
|
||||
return $rev;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -183,12 +183,10 @@ class ResourceLoaderWikiModule extends ResourceLoaderModule {
|
|||
* @return Content|null
|
||||
*/
|
||||
protected function getContentObj( Title $title ) {
|
||||
$revision = Revision::newKnownCurrent( wfGetDB( DB_REPLICA ), $title->getArticleID(),
|
||||
$title->getLatestRevID() );
|
||||
$revision = Revision::newKnownCurrent( wfGetDB( DB_REPLICA ), $title );
|
||||
if ( !$revision ) {
|
||||
return null;
|
||||
}
|
||||
$revision->setTitle( $title );
|
||||
$content = $revision->getContent( Revision::RAW );
|
||||
if ( !$content ) {
|
||||
wfDebugLog( 'resourceloader', __METHOD__ . ': failed to load content of JS/CSS page!' );
|
||||
|
|
|
|||
|
|
@ -290,15 +290,16 @@ class SpecialNewpages extends IncludableSpecialPage {
|
|||
|
||||
/**
|
||||
* @param stdClass $result Result row from recent changes
|
||||
* @return Revision|bool
|
||||
* @param Title $title
|
||||
* @return bool|Revision
|
||||
*/
|
||||
protected function revisionFromRcResult( stdClass $result ) {
|
||||
protected function revisionFromRcResult( stdClass $result, Title $title ) {
|
||||
return new Revision( [
|
||||
'comment' => CommentStore::newKey( 'rc_comment' )->getComment( $result )->text,
|
||||
'deleted' => $result->rc_deleted,
|
||||
'user_text' => $result->rc_user_text,
|
||||
'user' => $result->rc_user,
|
||||
] );
|
||||
], 0, $title );
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -313,8 +314,7 @@ class SpecialNewpages extends IncludableSpecialPage {
|
|||
|
||||
// Revision deletion works on revisions,
|
||||
// so cast our recent change row to a revision row.
|
||||
$rev = $this->revisionFromRcResult( $result );
|
||||
$rev->setTitle( $title );
|
||||
$rev = $this->revisionFromRcResult( $result, $title );
|
||||
|
||||
$classes = [];
|
||||
$attribs = [ 'data-mw-revid' => $result->rev_id ];
|
||||
|
|
|
|||
|
|
@ -7,6 +7,9 @@ use MediaWiki\Services\DestructibleService;
|
|||
use MediaWiki\Services\SalvageableService;
|
||||
use MediaWiki\Services\ServiceDisabledException;
|
||||
use MediaWiki\Shell\CommandFactory;
|
||||
use MediaWiki\Storage\BlobStore;
|
||||
use MediaWiki\Storage\RevisionStore;
|
||||
use MediaWiki\Storage\SqlBlobStore;
|
||||
|
||||
/**
|
||||
* @covers MediaWiki\MediaWikiServices
|
||||
|
|
@ -331,6 +334,9 @@ class MediaWikiServicesTest extends MediaWikiTestCase {
|
|||
'LocalServerObjectCache' => [ 'LocalServerObjectCache', BagOStuff::class ],
|
||||
'VirtualRESTServiceClient' => [ 'VirtualRESTServiceClient', VirtualRESTServiceClient::class ],
|
||||
'ShellCommandFactory' => [ 'ShellCommandFactory', CommandFactory::class ],
|
||||
'BlobStore' => [ 'BlobStore', BlobStore::class ],
|
||||
'_SqlBlobStore' => [ '_SqlBlobStore', SqlBlobStore::class ],
|
||||
'RevisionStore' => [ 'RevisionStore', RevisionStore::class ],
|
||||
];
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,8 @@
|
|||
<?php
|
||||
use MediaWiki\MediaWikiServices;
|
||||
use MediaWiki\Storage\RevisionStore;
|
||||
use MediaWiki\Storage\IncompleteRevisionException;
|
||||
use MediaWiki\Storage\RevisionRecord;
|
||||
|
||||
/**
|
||||
* RevisionDbTestBase contains test cases for the Revision class that have Database interactions.
|
||||
|
|
@ -72,6 +76,7 @@ abstract class RevisionDbTestBase extends MediaWikiTestCase {
|
|||
MWNamespace::clearCaches();
|
||||
// Reset namespace cache
|
||||
$wgContLang->resetNamespaces();
|
||||
|
||||
if ( !$this->testPage ) {
|
||||
/**
|
||||
* We have to create a new page for each subclass as the page creation may result
|
||||
|
|
@ -102,6 +107,14 @@ abstract class RevisionDbTestBase extends MediaWikiTestCase {
|
|||
$props['text'] = 'Lorem Ipsum';
|
||||
}
|
||||
|
||||
if ( !isset( $props['user_text'] ) ) {
|
||||
$props['user_text'] = 'Tester';
|
||||
}
|
||||
|
||||
if ( !isset( $props['user'] ) ) {
|
||||
$props['user'] = 0;
|
||||
}
|
||||
|
||||
if ( !isset( $props['comment'] ) ) {
|
||||
$props['comment'] = 'just a test';
|
||||
}
|
||||
|
|
@ -110,6 +123,10 @@ abstract class RevisionDbTestBase extends MediaWikiTestCase {
|
|||
$props['page'] = $this->testPage->getId();
|
||||
}
|
||||
|
||||
if ( !isset( $props['content_model'] ) ) {
|
||||
$props['content_model'] = CONTENT_MODEL_WIKITEXT;
|
||||
}
|
||||
|
||||
$rev = new Revision( $props );
|
||||
|
||||
$dbw = wfGetDB( DB_MASTER );
|
||||
|
|
@ -202,14 +219,23 @@ abstract class RevisionDbTestBase extends MediaWikiTestCase {
|
|||
$revId = $rev->insertOn( wfGetDB( DB_MASTER ) );
|
||||
|
||||
$this->assertInternalType( 'integer', $revId );
|
||||
$this->assertInternalType( 'integer', $rev->getTextId() );
|
||||
$this->assertSame( $revId, $rev->getId() );
|
||||
|
||||
// getTextId() must be an int!
|
||||
$this->assertInternalType( 'integer', $rev->getTextId() );
|
||||
|
||||
$mainSlot = $rev->getRevisionRecord()->getSlot( 'main', RevisionRecord::RAW );
|
||||
|
||||
// we currently only support storage in the text table
|
||||
$textId = MediaWikiServices::getInstance()
|
||||
->getBlobStore()
|
||||
->getTextIdFromAddress( $mainSlot->getAddress() );
|
||||
|
||||
$this->assertSelect(
|
||||
'text',
|
||||
[ 'old_id', 'old_text' ],
|
||||
"old_id = {$rev->getTextId()}",
|
||||
[ [ strval( $rev->getTextId() ), 'Revision Text' ] ]
|
||||
"old_id = $textId",
|
||||
[ [ strval( $textId ), 'Revision Text' ] ]
|
||||
);
|
||||
$this->assertSelect(
|
||||
'revision',
|
||||
|
|
@ -228,7 +254,7 @@ abstract class RevisionDbTestBase extends MediaWikiTestCase {
|
|||
[ [
|
||||
strval( $rev->getId() ),
|
||||
strval( $this->testPage->getId() ),
|
||||
strval( $rev->getTextId() ),
|
||||
strval( $textId ),
|
||||
'0',
|
||||
'0',
|
||||
'0',
|
||||
|
|
@ -246,11 +272,12 @@ abstract class RevisionDbTestBase extends MediaWikiTestCase {
|
|||
// If an ExternalStore is set don't use it.
|
||||
$this->setMwGlobals( 'wgDefaultExternalStore', false );
|
||||
$this->setExpectedException(
|
||||
MWException::class,
|
||||
"Cannot insert revision: page ID must be nonzero"
|
||||
IncompleteRevisionException::class,
|
||||
"rev_page field must not be 0!"
|
||||
);
|
||||
|
||||
$rev = new Revision( [] );
|
||||
$title = Title::newFromText( 'Nonexistant-' . __METHOD__ );
|
||||
$rev = new Revision( [], 0, $title );
|
||||
|
||||
$rev->insertOn( wfGetDB( DB_MASTER ) );
|
||||
}
|
||||
|
|
@ -321,12 +348,42 @@ abstract class RevisionDbTestBase extends MediaWikiTestCase {
|
|||
return $f + [ 'ar_namespace', 'ar_title' ];
|
||||
},
|
||||
];
|
||||
yield [
|
||||
function ( $f ) {
|
||||
unset( $f['ar_text'] );
|
||||
return $f;
|
||||
},
|
||||
];
|
||||
yield [
|
||||
function ( $f ) {
|
||||
unset( $f['ar_text_id'] );
|
||||
return $f;
|
||||
},
|
||||
];
|
||||
yield [
|
||||
function ( $f ) {
|
||||
unset( $f['ar_page_id'] );
|
||||
return $f;
|
||||
},
|
||||
];
|
||||
yield [
|
||||
function ( $f ) {
|
||||
unset( $f['ar_parent_id'] );
|
||||
return $f;
|
||||
},
|
||||
];
|
||||
yield [
|
||||
function ( $f ) {
|
||||
unset( $f['ar_rev_id'] );
|
||||
return $f;
|
||||
},
|
||||
];
|
||||
yield [
|
||||
function ( $f ) {
|
||||
unset( $f['ar_sha1'] );
|
||||
return $f;
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -334,6 +391,17 @@ abstract class RevisionDbTestBase extends MediaWikiTestCase {
|
|||
* @covers Revision::newFromArchiveRow
|
||||
*/
|
||||
public function testNewFromArchiveRow( $selectModifier ) {
|
||||
$services = MediaWikiServices::getInstance();
|
||||
|
||||
$store = new RevisionStore(
|
||||
$services->getDBLoadBalancer(),
|
||||
$services->getService( '_SqlBlobStore' ),
|
||||
$services->getMainWANObjectCache()
|
||||
);
|
||||
|
||||
$store->setContentHandlerUseDB( $this->getContentHandlerUseDB() );
|
||||
$this->setService( 'RevisionStore', $store );
|
||||
|
||||
$page = $this->createPage(
|
||||
'RevisionStorageTest_testNewFromArchiveRow',
|
||||
'Lorem Ipsum',
|
||||
|
|
@ -354,6 +422,8 @@ abstract class RevisionDbTestBase extends MediaWikiTestCase {
|
|||
$row = $res->fetchObject();
|
||||
$res->free();
|
||||
|
||||
// MCR migration note: $row is now required to contain ar_title and ar_namespace.
|
||||
// Alternatively, a Title object can be passed to RevisionStore::newRevisionFromArchiveRow
|
||||
$rev = Revision::newFromArchiveRow( $row );
|
||||
|
||||
$this->assertRevEquals( $orig, $rev );
|
||||
|
|
@ -382,7 +452,7 @@ abstract class RevisionDbTestBase extends MediaWikiTestCase {
|
|||
$row = $res->fetchObject();
|
||||
$res->free();
|
||||
|
||||
$rev = Revision::newFromArchiveRow( $row, [ 'comment' => 'SOMEOVERRIDE' ] );
|
||||
$rev = Revision::newFromArchiveRow( $row, [ 'comment_text' => 'SOMEOVERRIDE' ] );
|
||||
|
||||
$this->assertNotEquals( $orig->getComment(), $rev->getComment() );
|
||||
$this->assertEquals( 'SOMEOVERRIDE', $rev->getComment() );
|
||||
|
|
@ -426,7 +496,8 @@ abstract class RevisionDbTestBase extends MediaWikiTestCase {
|
|||
* @covers Revision::newFromPageId
|
||||
*/
|
||||
public function testNewFromPageIdWithNotLatestId() {
|
||||
$this->testPage->doEditContent( new WikitextContent( __METHOD__ ), __METHOD__ );
|
||||
$content = new WikitextContent( __METHOD__ );
|
||||
$this->testPage->doEditContent( $content, __METHOD__ );
|
||||
$rev = Revision::newFromPageId(
|
||||
$this->testPage->getId(),
|
||||
$this->testPage->getRevision()->getPrevious()->getId()
|
||||
|
|
@ -447,6 +518,7 @@ abstract class RevisionDbTestBase extends MediaWikiTestCase {
|
|||
$this->testPage->doEditContent( new WikitextContent( __METHOD__ ), __METHOD__ );
|
||||
$id = $this->testPage->getRevision()->getId();
|
||||
|
||||
$this->hideDeprecated( 'Revision::fetchRevision' );
|
||||
$res = Revision::fetchRevision( $this->testPage->getTitle() );
|
||||
|
||||
# note: order is unspecified
|
||||
|
|
@ -455,8 +527,7 @@ abstract class RevisionDbTestBase extends MediaWikiTestCase {
|
|||
$rows[$row->rev_id] = $row;
|
||||
}
|
||||
|
||||
$this->assertEquals( 1, count( $rows ), 'expected exactly one revision' );
|
||||
$this->assertArrayHasKey( $id, $rows, 'missing revision with id ' . $id );
|
||||
$this->assertEmpty( $rows, 'expected empty set' );
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -541,6 +612,10 @@ abstract class RevisionDbTestBase extends MediaWikiTestCase {
|
|||
'new null revision should have a different id from the original revision' );
|
||||
$this->assertEquals( $orig->getTextId(), $rev->getTextId(),
|
||||
'new null revision should have the same text id as the original revision' );
|
||||
$this->assertEquals( $orig->getSha1(), $rev->getSha1(),
|
||||
'new null revision should have the same SHA1 as the original revision' );
|
||||
$this->assertTrue( $orig->getRevisionRecord()->hasSameContent( $rev->getRevisionRecord() ),
|
||||
'new null revision should have the same content as the original revision' );
|
||||
$this->assertEquals( __METHOD__, $rev->getContent()->getNativeData() );
|
||||
}
|
||||
|
||||
|
|
@ -606,7 +681,7 @@ abstract class RevisionDbTestBase extends MediaWikiTestCase {
|
|||
'user' => $userA->getId(),
|
||||
'text' => 'zero',
|
||||
'content_model' => CONTENT_MODEL_WIKITEXT,
|
||||
'summary' => 'edit zero'
|
||||
'comment' => 'edit zero'
|
||||
] );
|
||||
$revisions[0]->insertOn( $dbw );
|
||||
|
||||
|
|
@ -618,7 +693,7 @@ abstract class RevisionDbTestBase extends MediaWikiTestCase {
|
|||
'user' => $userA->getId(),
|
||||
'text' => 'one',
|
||||
'content_model' => CONTENT_MODEL_WIKITEXT,
|
||||
'summary' => 'edit one'
|
||||
'comment' => 'edit one'
|
||||
] );
|
||||
$revisions[1]->insertOn( $dbw );
|
||||
|
||||
|
|
@ -629,7 +704,7 @@ abstract class RevisionDbTestBase extends MediaWikiTestCase {
|
|||
'user' => $userB->getId(),
|
||||
'text' => 'two',
|
||||
'content_model' => CONTENT_MODEL_WIKITEXT,
|
||||
'summary' => 'edit two'
|
||||
'comment' => 'edit two'
|
||||
] );
|
||||
$revisions[2]->insertOn( $dbw );
|
||||
|
||||
|
|
@ -640,7 +715,7 @@ abstract class RevisionDbTestBase extends MediaWikiTestCase {
|
|||
'user' => $userA->getId(),
|
||||
'text' => 'three',
|
||||
'content_model' => CONTENT_MODEL_WIKITEXT,
|
||||
'summary' => 'edit three'
|
||||
'comment' => 'edit three'
|
||||
] );
|
||||
$revisions[3]->insertOn( $dbw );
|
||||
|
||||
|
|
@ -651,13 +726,24 @@ abstract class RevisionDbTestBase extends MediaWikiTestCase {
|
|||
'user' => $userA->getId(),
|
||||
'text' => 'zero',
|
||||
'content_model' => CONTENT_MODEL_WIKITEXT,
|
||||
'summary' => 'edit four'
|
||||
'comment' => 'edit four'
|
||||
] );
|
||||
$revisions[4]->insertOn( $dbw );
|
||||
|
||||
// test it ---------------------------------
|
||||
$since = $revisions[$sinceIdx]->getTimestamp();
|
||||
|
||||
$allRows = iterator_to_array( $dbw->select(
|
||||
'revision',
|
||||
[ 'rev_id', 'rev_timestamp', 'rev_user' ],
|
||||
[
|
||||
'rev_page' => $page->getId(),
|
||||
//'rev_timestamp > ' . $dbw->addQuotes( $dbw->timestamp( $since ) )
|
||||
],
|
||||
__METHOD__,
|
||||
[ 'ORDER BY' => 'rev_timestamp ASC', 'LIMIT' => 50 ]
|
||||
) );
|
||||
|
||||
$wasLast = Revision::userWasLastToEdit( $dbw, $page->getId(), $userA->getId(), $since );
|
||||
|
||||
$this->assertEquals( $expectedLast, $wasLast );
|
||||
|
|
@ -805,12 +891,16 @@ abstract class RevisionDbTestBase extends MediaWikiTestCase {
|
|||
'text_id' => 123456789, // not in the test DB
|
||||
] );
|
||||
|
||||
MediaWiki\suppressWarnings(); // bad text_id will trigger a warning.
|
||||
|
||||
$this->assertNull( $rev->getContent(),
|
||||
"getContent() should return null if the revision's text blob could not be loaded." );
|
||||
|
||||
// NOTE: check this twice, once for lazy initialization, and once with the cached value.
|
||||
$this->assertNull( $rev->getContent(),
|
||||
"getContent() should return null if the revision's text blob could not be loaded." );
|
||||
|
||||
MediaWiki\suppressWarnings( 'end' );
|
||||
}
|
||||
|
||||
public function provideGetSize() {
|
||||
|
|
@ -904,6 +994,7 @@ abstract class RevisionDbTestBase extends MediaWikiTestCase {
|
|||
*/
|
||||
public function testLoadFromId() {
|
||||
$rev = $this->testPage->getRevision();
|
||||
$this->hideDeprecated( 'Revision::loadFromId' );
|
||||
$this->assertRevEquals(
|
||||
$rev,
|
||||
Revision::loadFromId( wfGetDB( DB_MASTER ), $rev->getId() )
|
||||
|
|
@ -1026,7 +1117,7 @@ abstract class RevisionDbTestBase extends MediaWikiTestCase {
|
|||
$rev[1] = $this->testPage->getLatest();
|
||||
|
||||
$this->assertSame(
|
||||
[ $rev[1] => strval( $textLength ) ],
|
||||
[ $rev[1] => $textLength ],
|
||||
Revision::getParentLengths(
|
||||
wfGetDB( DB_MASTER ),
|
||||
[ $rev[1] ]
|
||||
|
|
@ -1049,7 +1140,7 @@ abstract class RevisionDbTestBase extends MediaWikiTestCase {
|
|||
$rev[2] = $this->testPage->getLatest();
|
||||
|
||||
$this->assertSame(
|
||||
[ $rev[1] => strval( $textOneLength ), $rev[2] => strval( $textTwoLength ) ],
|
||||
[ $rev[1] => $textOneLength, $rev[2] => $textTwoLength ],
|
||||
Revision::getParentLengths(
|
||||
wfGetDB( DB_MASTER ),
|
||||
[ $rev[1], $rev[2] ]
|
||||
|
|
@ -1080,14 +1171,6 @@ abstract class RevisionDbTestBase extends MediaWikiTestCase {
|
|||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers Revision::getTitle
|
||||
*/
|
||||
public function testGetTitle_forBadRevision() {
|
||||
$rev = new Revision( [] );
|
||||
$this->assertNull( $rev->getTitle() );
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers Revision::isMinor
|
||||
*/
|
||||
|
|
@ -1263,14 +1346,21 @@ abstract class RevisionDbTestBase extends MediaWikiTestCase {
|
|||
$rev = $this->testPage->getRevision();
|
||||
|
||||
// Clear any previous cache for the revision during creation
|
||||
$key = $cache->makeGlobalKey( 'revision', $db->getDomainID(), $rev->getPage(), $rev->getId() );
|
||||
$key = $cache->makeGlobalKey( 'revision-row-1.29',
|
||||
$db->getDomainID(),
|
||||
$rev->getPage(),
|
||||
$rev->getId()
|
||||
);
|
||||
$cache->delete( $key, WANObjectCache::HOLDOFF_NONE );
|
||||
$this->assertFalse( $cache->get( $key ) );
|
||||
|
||||
// Get the new revision and make sure it is in the cache and correct
|
||||
$newRev = Revision::newKnownCurrent( $db, $rev->getPage(), $rev->getId() );
|
||||
$this->assertRevEquals( $rev, $newRev );
|
||||
$this->assertRevEquals( $rev, $cache->get( $key ) );
|
||||
|
||||
$cachedRow = $cache->get( $key );
|
||||
$this->assertNotFalse( $cachedRow );
|
||||
$this->assertEquals( $rev->getId(), $cachedRow->rev_id );
|
||||
}
|
||||
|
||||
public function provideUserCanBitfield() {
|
||||
|
|
@ -1377,7 +1467,7 @@ abstract class RevisionDbTestBase extends MediaWikiTestCase {
|
|||
]
|
||||
);
|
||||
$user = $this->getTestUser( $userGroups )->getUser();
|
||||
$revision = new Revision( [ 'deleted' => $bitField ] );
|
||||
$revision = new Revision( [ 'deleted' => $bitField ], 0, $this->testPage->getTitle() );
|
||||
|
||||
$this->assertSame(
|
||||
$expected,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
<?php
|
||||
|
||||
use Wikimedia\TestingAccessWrapper;
|
||||
use MediaWiki\Storage\RevisionStore;
|
||||
use MediaWiki\Storage\SqlBlobStore;
|
||||
use Wikimedia\Rdbms\IDatabase;
|
||||
use Wikimedia\Rdbms\LoadBalancer;
|
||||
|
||||
/**
|
||||
* Test cases in RevisionTest should not interact with the Database.
|
||||
|
|
@ -54,10 +57,10 @@ class RevisionTest extends MediaWikiTestCase {
|
|||
/**
|
||||
* @dataProvider provideConstructFromArray
|
||||
* @covers Revision::__construct
|
||||
* @covers Revision::constructFromRowArray
|
||||
* @covers RevisionStore::newMutableRevisionFromArray
|
||||
*/
|
||||
public function testConstructFromArray( array $rowArray ) {
|
||||
$rev = new Revision( $rowArray );
|
||||
public function testConstructFromArray( $rowArray ) {
|
||||
$rev = new Revision( $rowArray, 0, $this->getMockTitle() );
|
||||
$this->assertNotNull( $rev->getContent(), 'no content object available' );
|
||||
$this->assertEquals( CONTENT_MODEL_JAVASCRIPT, $rev->getContent()->getModel() );
|
||||
$this->assertEquals( CONTENT_MODEL_JAVASCRIPT, $rev->getContentModel() );
|
||||
|
|
@ -65,7 +68,7 @@ class RevisionTest extends MediaWikiTestCase {
|
|||
|
||||
/**
|
||||
* @covers Revision::__construct
|
||||
* @covers Revision::constructFromRowArray
|
||||
* @covers RevisionStore::newMutableRevisionFromArray
|
||||
*/
|
||||
public function testConstructFromEmptyArray() {
|
||||
$rev = new Revision( [], 0, $this->getMockTitle() );
|
||||
|
|
@ -90,30 +93,20 @@ class RevisionTest extends MediaWikiTestCase {
|
|||
99,
|
||||
'SomeTextUserName',
|
||||
];
|
||||
// Note: the below XXX test cases are odd and probably result in unexpected behaviour if used
|
||||
// in production code.
|
||||
yield 'XXX: user text only' => [
|
||||
yield 'user text only' => [
|
||||
[
|
||||
'content' => new JavaScriptContent( 'hello world.' ),
|
||||
'user_text' => '111.111.111.111',
|
||||
],
|
||||
null,
|
||||
0,
|
||||
'111.111.111.111',
|
||||
];
|
||||
yield 'XXX: user id only' => [
|
||||
[
|
||||
'content' => new JavaScriptContent( 'hello world.' ),
|
||||
'user' => 9989,
|
||||
],
|
||||
9989,
|
||||
null,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideConstructFromArray_userSetAsExpected
|
||||
* @covers Revision::__construct
|
||||
* @covers Revision::constructFromRowArray
|
||||
* @covers RevisionStore::newMutableRevisionFromArray
|
||||
*
|
||||
* @param array $rowArray
|
||||
* @param mixed $expectedUserId null to expect the current wgUser ID
|
||||
|
|
@ -133,7 +126,7 @@ class RevisionTest extends MediaWikiTestCase {
|
|||
$expectedUserName = $testUser->getName();
|
||||
}
|
||||
|
||||
$rev = new Revision( $rowArray );
|
||||
$rev = new Revision( $rowArray, 0, $this->getMockTitle() );
|
||||
$this->assertEquals( $expectedUserId, $rev->getUser() );
|
||||
$this->assertEquals( $expectedUserName, $rev->getUserText() );
|
||||
}
|
||||
|
|
@ -143,28 +136,37 @@ class RevisionTest extends MediaWikiTestCase {
|
|||
[
|
||||
'content' => new WikitextContent( 'GOAT' ),
|
||||
'text_id' => 'someid',
|
||||
],
|
||||
],
|
||||
new MWException( "Text already stored in external store (id someid), " .
|
||||
"can't serialize content object" )
|
||||
];
|
||||
yield 'unknown user id and no user name' => [
|
||||
[
|
||||
'content' => new JavaScriptContent( 'hello world.' ),
|
||||
'user' => 9989,
|
||||
],
|
||||
new MWException( 'user_text not given, and unknown user ID 9989' )
|
||||
];
|
||||
yield 'with bad content object (class)' => [
|
||||
[ 'content' => new stdClass() ],
|
||||
new MWException( '`content` field must contain a Content object.' )
|
||||
new MWException( 'content field must contain a Content object.' )
|
||||
];
|
||||
yield 'with bad content object (string)' => [
|
||||
[ 'content' => 'ImAGoat' ],
|
||||
new MWException( '`content` field must contain a Content object.' )
|
||||
new MWException( 'content field must contain a Content object.' )
|
||||
];
|
||||
yield 'bad row format' => [
|
||||
'imastring, not a row',
|
||||
new MWException( 'Revision constructor passed invalid row format.' )
|
||||
new InvalidArgumentException(
|
||||
'$row must be a row object, an associative array, or a RevisionRecord'
|
||||
)
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideConstructFromArrayThrowsExceptions
|
||||
* @covers Revision::__construct
|
||||
* @covers Revision::constructFromRowArray
|
||||
* @covers RevisionStore::newMutableRevisionFromArray
|
||||
*/
|
||||
public function testConstructFromArrayThrowsExceptions( $rowArray, Exception $expectedException ) {
|
||||
$this->setExpectedException(
|
||||
|
|
@ -172,23 +174,25 @@ class RevisionTest extends MediaWikiTestCase {
|
|||
$expectedException->getMessage(),
|
||||
$expectedException->getCode()
|
||||
);
|
||||
new Revision( $rowArray );
|
||||
new Revision( $rowArray, 0, $this->getMockTitle() );
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers Revision::__construct
|
||||
* @covers Revision::constructFromRowArray
|
||||
* @covers RevisionStore::newMutableRevisionFromArray
|
||||
*/
|
||||
public function testConstructFromNothing() {
|
||||
$rev = new Revision( [] );
|
||||
$this->assertNull( $rev->getId(), 'getId()' );
|
||||
$this->setExpectedException(
|
||||
InvalidArgumentException::class
|
||||
);
|
||||
new Revision( [] );
|
||||
}
|
||||
|
||||
public function provideConstructFromRow() {
|
||||
yield 'Full construction' => [
|
||||
[
|
||||
'rev_id' => '2',
|
||||
'rev_page' => '1',
|
||||
'rev_id' => '42',
|
||||
'rev_page' => '23',
|
||||
'rev_text_id' => '2',
|
||||
'rev_timestamp' => '20171017114835',
|
||||
'rev_user_text' => '127.0.0.1',
|
||||
|
|
@ -205,8 +209,8 @@ class RevisionTest extends MediaWikiTestCase {
|
|||
'rev_content_model' => 'GOATMODEL',
|
||||
],
|
||||
function ( RevisionTest $testCase, Revision $rev ) {
|
||||
$testCase->assertSame( 2, $rev->getId() );
|
||||
$testCase->assertSame( 1, $rev->getPage() );
|
||||
$testCase->assertSame( 42, $rev->getId() );
|
||||
$testCase->assertSame( 23, $rev->getPage() );
|
||||
$testCase->assertSame( 2, $rev->getTextId() );
|
||||
$testCase->assertSame( '20171017114835', $rev->getTimestamp() );
|
||||
$testCase->assertSame( '127.0.0.1', $rev->getUserText() );
|
||||
|
|
@ -221,10 +225,10 @@ class RevisionTest extends MediaWikiTestCase {
|
|||
$testCase->assertSame( 'GOATMODEL', $rev->getContentModel() );
|
||||
}
|
||||
];
|
||||
yield 'null fields' => [
|
||||
yield 'default field values' => [
|
||||
[
|
||||
'rev_id' => '2',
|
||||
'rev_page' => '1',
|
||||
'rev_id' => '42',
|
||||
'rev_page' => '23',
|
||||
'rev_text_id' => '2',
|
||||
'rev_timestamp' => '20171017114835',
|
||||
'rev_user_text' => '127.0.0.1',
|
||||
|
|
@ -236,11 +240,24 @@ class RevisionTest extends MediaWikiTestCase {
|
|||
'rev_comment_cid' => null,
|
||||
],
|
||||
function ( RevisionTest $testCase, Revision $rev ) {
|
||||
$testCase->assertNull( $rev->getSize() );
|
||||
$testCase->assertNull( $rev->getParentId() );
|
||||
$testCase->assertNull( $rev->getSha1() );
|
||||
$testCase->assertSame( 'text/x-wiki', $rev->getContentFormat() );
|
||||
$testCase->assertSame( 'wikitext', $rev->getContentModel() );
|
||||
// parent ID may be null
|
||||
$testCase->assertSame( null, $rev->getParentId(), 'revision id' );
|
||||
|
||||
// given fields
|
||||
$testCase->assertSame( $rev->getTimestamp(), '20171017114835', 'timestamp' );
|
||||
$testCase->assertSame( $rev->getUserText(), '127.0.0.1', 'user name' );
|
||||
$testCase->assertSame( $rev->getUser(), 0, 'user id' );
|
||||
$testCase->assertSame( $rev->getComment(), 'Goat Comment!' );
|
||||
$testCase->assertSame( false, $rev->isMinor(), 'minor edit' );
|
||||
$testCase->assertSame( 0, $rev->getVisibility(), 'visibility flags' );
|
||||
|
||||
// computed fields
|
||||
$testCase->assertNotNull( $rev->getSize(), 'size' );
|
||||
$testCase->assertNotNull( $rev->getSha1(), 'hash' );
|
||||
|
||||
// NOTE: model and format will be detected based on the namespace of the (mock) title
|
||||
$testCase->assertSame( 'text/x-wiki', $rev->getContentFormat(), 'format' );
|
||||
$testCase->assertSame( 'wikitext', $rev->getContentModel(), 'model' );
|
||||
}
|
||||
];
|
||||
}
|
||||
|
|
@ -248,11 +265,34 @@ class RevisionTest extends MediaWikiTestCase {
|
|||
/**
|
||||
* @dataProvider provideConstructFromRow
|
||||
* @covers Revision::__construct
|
||||
* @covers Revision::constructFromRowArray
|
||||
* @covers RevisionStore::newMutableRevisionFromArray
|
||||
*/
|
||||
public function testConstructFromRow( array $arrayData, $assertions ) {
|
||||
$data = 'Hello goat.'; // needs to match model and format
|
||||
|
||||
$blobStore = $this->getMockBuilder( SqlBlobStore::class )
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
|
||||
$blobStore->method( 'getBlob' )
|
||||
->will( $this->returnValue( $data ) );
|
||||
|
||||
$blobStore->method( 'getTextIdFromAddress' )
|
||||
->will( $this->returnCallback(
|
||||
function ( $address ) {
|
||||
// Turn "tt:1234" into 12345.
|
||||
// Note that this must be functional so we can test getTextId().
|
||||
// Ideally, we'd un-mock getTextIdFromAddress and use its actual implementation.
|
||||
$parts = explode( ':', $address );
|
||||
return (int)array_pop( $parts );
|
||||
}
|
||||
) );
|
||||
|
||||
// Note override internal service, so RevisionStore uses it as well.
|
||||
$this->setService( '_SqlBlobStore', $blobStore );
|
||||
|
||||
$row = (object)$arrayData;
|
||||
$rev = new Revision( $row );
|
||||
$rev = new Revision( $row, 0, $this->getMockTitle() );
|
||||
$assertions( $this, $rev );
|
||||
}
|
||||
|
||||
|
|
@ -282,7 +322,7 @@ class RevisionTest extends MediaWikiTestCase {
|
|||
* @covers Revision::getId
|
||||
*/
|
||||
public function testGetId( $rowArray, $expectedId ) {
|
||||
$rev = new Revision( $rowArray );
|
||||
$rev = new Revision( $rowArray, 0, $this->getMockTitle() );
|
||||
$this->assertEquals( $expectedId, $rev->getId() );
|
||||
}
|
||||
|
||||
|
|
@ -296,7 +336,7 @@ class RevisionTest extends MediaWikiTestCase {
|
|||
* @covers Revision::setId
|
||||
*/
|
||||
public function testSetId( $input, $expected ) {
|
||||
$rev = new Revision( [] );
|
||||
$rev = new Revision( [], 0, $this->getMockTitle() );
|
||||
$rev->setId( $input );
|
||||
$this->assertSame( $expected, $rev->getId() );
|
||||
}
|
||||
|
|
@ -311,7 +351,7 @@ class RevisionTest extends MediaWikiTestCase {
|
|||
* @covers Revision::setUserIdAndName
|
||||
*/
|
||||
public function testSetUserIdAndName( $inputId, $expectedId, $name ) {
|
||||
$rev = new Revision( [] );
|
||||
$rev = new Revision( [], 0, $this->getMockTitle() );
|
||||
$rev->setUserIdAndName( $inputId, $name );
|
||||
$this->assertSame( $expectedId, $rev->getUser( Revision::RAW ) );
|
||||
$this->assertEquals( $name, $rev->getUserText( Revision::RAW ) );
|
||||
|
|
@ -328,7 +368,7 @@ class RevisionTest extends MediaWikiTestCase {
|
|||
* @covers Revision::getTextId()
|
||||
*/
|
||||
public function testGetTextId( $rowArray, $expected ) {
|
||||
$rev = new Revision( $rowArray );
|
||||
$rev = new Revision( $rowArray, 0, $this->getMockTitle() );
|
||||
$this->assertSame( $expected, $rev->getTextId() );
|
||||
}
|
||||
|
||||
|
|
@ -343,7 +383,7 @@ class RevisionTest extends MediaWikiTestCase {
|
|||
* @covers Revision::getParentId()
|
||||
*/
|
||||
public function testGetParentId( $rowArray, $expected ) {
|
||||
$rev = new Revision( $rowArray );
|
||||
$rev = new Revision( $rowArray, 0, $this->getMockTitle() );
|
||||
$this->assertSame( $expected, $rev->getParentId() );
|
||||
}
|
||||
|
||||
|
|
@ -376,9 +416,44 @@ class RevisionTest extends MediaWikiTestCase {
|
|||
$this->testGetRevisionText( $expected, $rowData );
|
||||
}
|
||||
|
||||
private function getWANObjectCache() {
|
||||
return new WANObjectCache( [ 'cache' => new HashBagOStuff() ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return SqlBlobStore
|
||||
*/
|
||||
private function getBlobStore() {
|
||||
/** @var LoadBalancer $lb */
|
||||
$lb = $this->getMockBuilder( LoadBalancer::class )
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
|
||||
$cache = $this->getWANObjectCache();
|
||||
|
||||
$blobStore = new SqlBlobStore( $lb, $cache );
|
||||
return $blobStore;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return RevisionStore
|
||||
*/
|
||||
private function getRevisionStore() {
|
||||
/** @var LoadBalancer $lb */
|
||||
$lb = $this->getMockBuilder( LoadBalancer::class )
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
|
||||
$cache = $this->getWANObjectCache();
|
||||
|
||||
$blobStore = new RevisionStore( $lb, $this->getBlobStore(), $cache );
|
||||
return $blobStore;
|
||||
}
|
||||
|
||||
public function provideGetRevisionTextWithLegacyEncoding() {
|
||||
yield 'Utf8Native' => [
|
||||
"Wiki est l'\xc3\xa9cole superieur !",
|
||||
'fr',
|
||||
'iso-8859-1',
|
||||
[
|
||||
'old_flags' => 'utf-8',
|
||||
|
|
@ -387,6 +462,7 @@ class RevisionTest extends MediaWikiTestCase {
|
|||
];
|
||||
yield 'Utf8Legacy' => [
|
||||
"Wiki est l'\xc3\xa9cole superieur !",
|
||||
'fr',
|
||||
'iso-8859-1',
|
||||
[
|
||||
'old_flags' => '',
|
||||
|
|
@ -399,8 +475,11 @@ class RevisionTest extends MediaWikiTestCase {
|
|||
* @covers Revision::getRevisionText
|
||||
* @dataProvider provideGetRevisionTextWithLegacyEncoding
|
||||
*/
|
||||
public function testGetRevisionWithLegacyEncoding( $expected, $encoding, $rowData ) {
|
||||
$this->setMwGlobals( 'wgLegacyEncoding', $encoding );
|
||||
public function testGetRevisionWithLegacyEncoding( $expected, $lang, $encoding, $rowData ) {
|
||||
$blobStore = $this->getBlobStore();
|
||||
$blobStore->setLegacyEncoding( $encoding, Language::factory( $lang ) );
|
||||
$this->setService( 'BlobStore', $blobStore );
|
||||
|
||||
$this->testGetRevisionText( $expected, $rowData );
|
||||
}
|
||||
|
||||
|
|
@ -412,6 +491,7 @@ class RevisionTest extends MediaWikiTestCase {
|
|||
*/
|
||||
yield 'Utf8NativeGzip' => [
|
||||
"Wiki est l'\xc3\xa9cole superieur !",
|
||||
'fr',
|
||||
'iso-8859-1',
|
||||
[
|
||||
'old_flags' => 'gzip,utf-8',
|
||||
|
|
@ -420,6 +500,7 @@ class RevisionTest extends MediaWikiTestCase {
|
|||
];
|
||||
yield 'Utf8LegacyGzip' => [
|
||||
"Wiki est l'\xc3\xa9cole superieur !",
|
||||
'fr',
|
||||
'iso-8859-1',
|
||||
[
|
||||
'old_flags' => 'gzip',
|
||||
|
|
@ -432,9 +513,13 @@ class RevisionTest extends MediaWikiTestCase {
|
|||
* @covers Revision::getRevisionText
|
||||
* @dataProvider provideGetRevisionTextWithGzipAndLegacyEncoding
|
||||
*/
|
||||
public function testGetRevisionWithGzipAndLegacyEncoding( $expected, $encoding, $rowData ) {
|
||||
public function testGetRevisionWithGzipAndLegacyEncoding( $expected, $lang, $encoding, $rowData ) {
|
||||
$this->checkPHPExtension( 'zlib' );
|
||||
$this->setMwGlobals( 'wgLegacyEncoding', $encoding );
|
||||
|
||||
$blobStore = $this->getBlobStore();
|
||||
$blobStore->setLegacyEncoding( $encoding, Language::factory( $lang ) );
|
||||
$this->setService( 'BlobStore', $blobStore );
|
||||
|
||||
$this->testGetRevisionText( $expected, $rowData );
|
||||
}
|
||||
|
||||
|
|
@ -460,7 +545,10 @@ class RevisionTest extends MediaWikiTestCase {
|
|||
*/
|
||||
public function testCompressRevisionTextUtf8Gzip() {
|
||||
$this->checkPHPExtension( 'zlib' );
|
||||
$this->setMwGlobals( 'wgCompressRevisions', true );
|
||||
|
||||
$blobStore = $this->getBlobStore();
|
||||
$blobStore->setCompressBlobs( true );
|
||||
$this->setService( 'BlobStore', $blobStore );
|
||||
|
||||
$row = new stdClass;
|
||||
$row->old_text = "Wiki est l'\xc3\xa9cole superieur !";
|
||||
|
|
@ -475,20 +563,41 @@ class RevisionTest extends MediaWikiTestCase {
|
|||
Revision::getRevisionText( $row ), "getRevisionText" );
|
||||
}
|
||||
|
||||
public function provideFetchFromConds() {
|
||||
yield [ 0, [] ];
|
||||
yield [ Revision::READ_LOCKING, [ 'FOR UPDATE' ] ];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideFetchFromConds
|
||||
* @covers Revision::fetchFromConds
|
||||
* @covers Revision::loadFromTitle
|
||||
*/
|
||||
public function testFetchFromConds( $flags, array $options ) {
|
||||
$this->setMwGlobals( 'wgCommentTableSchemaMigrationStage', MIGRATION_OLD );
|
||||
$conditions = [ 'conditionsArray' ];
|
||||
public function testLoadFromTitle() {
|
||||
$title = $this->getMockTitle();
|
||||
|
||||
$conditions = [
|
||||
'rev_id=page_latest',
|
||||
'page_namespace' => $title->getNamespace(),
|
||||
'page_title' => $title->getDBkey()
|
||||
];
|
||||
|
||||
$row = (object)[
|
||||
'rev_id' => '42',
|
||||
'rev_page' => $title->getArticleID(),
|
||||
'rev_text_id' => '2',
|
||||
'rev_timestamp' => '20171017114835',
|
||||
'rev_user_text' => '127.0.0.1',
|
||||
'rev_user' => '0',
|
||||
'rev_minor_edit' => '0',
|
||||
'rev_deleted' => '0',
|
||||
'rev_len' => '46',
|
||||
'rev_parent_id' => '1',
|
||||
'rev_sha1' => 'rdqbbzs3pkhihgbs8qf2q9jsvheag5z',
|
||||
'rev_comment_text' => 'Goat Comment!',
|
||||
'rev_comment_data' => null,
|
||||
'rev_comment_cid' => null,
|
||||
'rev_content_format' => 'GOATFORMAT',
|
||||
'rev_content_model' => 'GOATMODEL',
|
||||
];
|
||||
|
||||
$db = $this->getMock( IDatabase::class );
|
||||
$db->expects( $this->any() )
|
||||
->method( 'getDomainId' )
|
||||
->will( $this->returnValue( wfWikiID() ) );
|
||||
$db->expects( $this->once() )
|
||||
->method( 'selectRow' )
|
||||
->with(
|
||||
|
|
@ -497,17 +606,24 @@ class RevisionTest extends MediaWikiTestCase {
|
|||
$this->isType( 'array' ),
|
||||
$this->equalTo( $conditions ),
|
||||
// Method name
|
||||
$this->equalTo( 'Revision::fetchFromConds' ),
|
||||
$this->equalTo( $options ),
|
||||
$this->stringContains( 'fetchRevisionRowFromConds' ),
|
||||
// We don't really care about the options here
|
||||
$this->isType( 'array' ),
|
||||
// We don't really care about the join conds are they come from the joinCond methods
|
||||
$this->isType( 'array' )
|
||||
)
|
||||
->willReturn( 'RETURNVALUE' );
|
||||
->willReturn( $row );
|
||||
|
||||
$wrapper = TestingAccessWrapper::newFromClass( Revision::class );
|
||||
$result = $wrapper->fetchFromConds( $db, $conditions, $flags );
|
||||
$revision = Revision::loadFromTitle( $db, $title );
|
||||
|
||||
$this->assertEquals( 'RETURNVALUE', $result );
|
||||
$this->assertEquals( $title->getArticleID(), $revision->getTitle()->getArticleID() );
|
||||
$this->assertEquals( $row->rev_id, $revision->getId() );
|
||||
$this->assertEquals( $row->rev_len, $revision->getSize() );
|
||||
$this->assertEquals( $row->rev_sha1, $revision->getSha1() );
|
||||
$this->assertEquals( $row->rev_parent_id, $revision->getParentId() );
|
||||
$this->assertEquals( $row->rev_timestamp, $revision->getTimestamp() );
|
||||
$this->assertEquals( $row->rev_comment_text, $revision->getComment() );
|
||||
$this->assertEquals( $row->rev_user_text, $revision->getUserText() );
|
||||
}
|
||||
|
||||
public function provideDecompressRevisionText() {
|
||||
|
|
@ -572,8 +688,12 @@ class RevisionTest extends MediaWikiTestCase {
|
|||
* @param mixed $expected
|
||||
*/
|
||||
public function testDecompressRevisionText( $legacyEncoding, $text, $flags, $expected ) {
|
||||
$this->setMwGlobals( 'wgLegacyEncoding', $legacyEncoding );
|
||||
$this->setMwGlobals( 'wgLanguageCode', 'en' );
|
||||
$blobStore = $this->getBlobStore();
|
||||
if ( $legacyEncoding ) {
|
||||
$blobStore->setLegacyEncoding( $legacyEncoding, Language::factory( 'en' ) );
|
||||
}
|
||||
|
||||
$this->setService( 'BlobStore', $blobStore );
|
||||
$this->assertSame(
|
||||
$expected,
|
||||
Revision::decompressRevisionText( $text, $flags )
|
||||
|
|
@ -669,14 +789,20 @@ class RevisionTest extends MediaWikiTestCase {
|
|||
* @covers Revision::getRevisionText
|
||||
*/
|
||||
public function testGetRevisionText_external_oldId() {
|
||||
$cache = new WANObjectCache( [ 'cache' => new HashBagOStuff() ] );
|
||||
$cache = $this->getWANObjectCache();
|
||||
$this->setService( 'MainWANObjectCache', $cache );
|
||||
|
||||
$this->setService(
|
||||
'ExternalStoreFactory',
|
||||
new ExternalStoreFactory( [ 'ForTesting' ] )
|
||||
);
|
||||
|
||||
$cacheKey = $cache->makeKey( 'revisiontext', 'textid', '7777' );
|
||||
$lb = $this->getMockBuilder( LoadBalancer::class )
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
|
||||
$blobStore = new SqlBlobStore( $lb, $cache );
|
||||
$this->setService( 'BlobStore', $blobStore );
|
||||
|
||||
$this->assertSame(
|
||||
'AAAABBAAA',
|
||||
|
|
@ -688,6 +814,8 @@ class RevisionTest extends MediaWikiTestCase {
|
|||
]
|
||||
)
|
||||
);
|
||||
|
||||
$cacheKey = $cache->makeKey( 'revisiontext', 'textid', 'tt:7777' );
|
||||
$this->assertSame( 'AAAABBAAA', $cache->get( $cacheKey ) );
|
||||
}
|
||||
|
||||
|
|
@ -883,6 +1011,8 @@ class RevisionTest extends MediaWikiTestCase {
|
|||
'fields' => [
|
||||
'ar_id',
|
||||
'ar_page_id',
|
||||
'ar_namespace',
|
||||
'ar_title',
|
||||
'ar_rev_id',
|
||||
'ar_text',
|
||||
'ar_text_id',
|
||||
|
|
@ -911,6 +1041,8 @@ class RevisionTest extends MediaWikiTestCase {
|
|||
'fields' => [
|
||||
'ar_id',
|
||||
'ar_page_id',
|
||||
'ar_namespace',
|
||||
'ar_title',
|
||||
'ar_rev_id',
|
||||
'ar_text',
|
||||
'ar_text_id',
|
||||
|
|
@ -944,6 +1076,8 @@ class RevisionTest extends MediaWikiTestCase {
|
|||
'fields' => [
|
||||
'ar_id',
|
||||
'ar_page_id',
|
||||
'ar_namespace',
|
||||
'ar_title',
|
||||
'ar_rev_id',
|
||||
'ar_text',
|
||||
'ar_text_id',
|
||||
|
|
@ -980,6 +1114,8 @@ class RevisionTest extends MediaWikiTestCase {
|
|||
'fields' => [
|
||||
'ar_id',
|
||||
'ar_page_id',
|
||||
'ar_namespace',
|
||||
'ar_title',
|
||||
'ar_rev_id',
|
||||
'ar_text',
|
||||
'ar_text_id',
|
||||
|
|
@ -1016,6 +1152,8 @@ class RevisionTest extends MediaWikiTestCase {
|
|||
'fields' => [
|
||||
'ar_id',
|
||||
'ar_page_id',
|
||||
'ar_namespace',
|
||||
'ar_title',
|
||||
'ar_rev_id',
|
||||
'ar_text',
|
||||
'ar_text_id',
|
||||
|
|
@ -1047,6 +1185,11 @@ class RevisionTest extends MediaWikiTestCase {
|
|||
*/
|
||||
public function testGetArchiveQueryInfo( $globals, $expected ) {
|
||||
$this->setMwGlobals( $globals );
|
||||
|
||||
$revisionStore = $this->getRevisionStore();
|
||||
$revisionStore->setContentHandlerUseDB( $globals['wgContentHandlerUseDB'] );
|
||||
$this->setService( 'RevisionStore', $revisionStore );
|
||||
|
||||
$this->assertEquals(
|
||||
$expected,
|
||||
Revision::getArchiveQueryInfo()
|
||||
|
|
@ -1398,6 +1541,11 @@ class RevisionTest extends MediaWikiTestCase {
|
|||
*/
|
||||
public function testGetQueryInfo( $globals, $options, $expected ) {
|
||||
$this->setMwGlobals( $globals );
|
||||
|
||||
$revisionStore = $this->getRevisionStore();
|
||||
$revisionStore->setContentHandlerUseDB( $globals['wgContentHandlerUseDB'] );
|
||||
$this->setService( 'RevisionStore', $revisionStore );
|
||||
|
||||
$this->assertEquals(
|
||||
$expected,
|
||||
Revision::getQueryInfo( $options )
|
||||
|
|
|
|||
Loading…
Reference in a new issue