Have Parsoid\Config\PageConfigFactory take a rev instead of wikitext

* This let us pass mocked revisions in the parser test runner while
  running in Parsoid mode.

* This leads to improvement in wt2html tests results where a revision
  id is queried. I've verified this in the Cite extension repo as
  also the main parserTests.text file but I cannot enable Parsoid
  integrated testing on the main parser tests file without doing a
  sweep over all parser tests and adding appropriate test sections

* Currently, PageConfigFactory doesn't have unit tests. Will look
  into adding them separately in a followup.

* Moved the setupParsoidTransform function to a more suitable place
  in the ParserTestRunner.php file.

Bug: T270310
Change-Id: I94d68c8528bb2f7b367c68d80d14ebc1ab904a7f
This commit is contained in:
Subramanya Sastry 2022-04-21 16:24:28 +05:30
parent 29578f449e
commit 5f5b4cbbb4
4 changed files with 91 additions and 80 deletions

View file

@ -34,7 +34,6 @@ use MediaWiki\Revision\RevisionRecord;
use ParserCache;
use ParserOptions;
use ParserOutput;
use TitleValue;
use User;
use Wikimedia\Message\MessageValue;
use Wikimedia\ParamValidator\ParamValidator;
@ -230,11 +229,7 @@ class ParsoidHTMLHelper {
// TODO: make ParsoidPageConfigFactory take PageReference as well
return MediaWikiServices::getInstance()
->get( 'ParsoidPageConfigFactory' )
->create(
TitleValue::newFromPage( $this->page ),
null,
$this->revision ? $this->revision->getId() : null
);
->create( $this->page, null, $this->revision );
}
/**

View file

@ -31,7 +31,9 @@ use MediaWiki\Rest\HttpException;
use MediaWiki\Rest\LocalizedHttpException;
use MediaWiki\Rest\Response;
use MediaWiki\Rest\ResponseException;
use MediaWiki\Revision\MutableRevisionRecord;
use MediaWiki\Revision\RevisionAccessException;
use MediaWiki\Revision\SlotRecord;
use MobileContext;
use ParserOutput;
use RequestContext;
@ -53,6 +55,7 @@ use Wikimedia\Parsoid\Utils\ContentUtils;
use Wikimedia\Parsoid\Utils\DOMCompat;
use Wikimedia\Parsoid\Utils\DOMUtils;
use Wikimedia\Parsoid\Utils\Timing;
use WikitextContent;
/**
* Base class for Parsoid handlers.
@ -364,6 +367,24 @@ abstract class ParsoidHandler extends Handler {
throw new LogicException( 'Title not found!' );
}
$user = RequestContext::getMain()->getUser();
if ( $wikitextOverride === null ) {
$revisionRecord = null;
} else {
// Create a mutable revision record point to the same revision
// and set to the desired wikitext.
$revisionRecord = new MutableRevisionRecord( $title );
if ( $revision !== null ) {
$revisionRecord->setId( $revision );
}
$revisionRecord->setSlot(
SlotRecord::newUnsaved(
SlotRecord::MAIN,
new WikitextContent( $wikitextOverride )
)
);
}
// Note: Parsoid by design isn't supposed to use the user
// context right now, and all user state is expected to be
// introduced as a post-parse transform. So although we pass a
@ -371,7 +392,7 @@ abstract class ParsoidHandler extends Handler {
// corner cases; see PageConfigFactory::create() for more.
// @phan-suppress-next-line PhanUndeclaredMethod method defined in subtype
return $this->pageConfigFactory->create(
$title, $user, $revision, $wikitextOverride, $pagelanguageOverride,
$title, $user, $revisionRecord ?? $revision, null, $pagelanguageOverride,
$this->parsoidSettings
);
}

View file

@ -19,9 +19,8 @@
namespace MediaWiki\Parser\Parsoid\Config;
use MediaWiki\Linker\LinkTarget;
use MediaWiki\Logger\LoggerFactory;
use MediaWiki\Revision\MutableRevisionRecord;
use MediaWiki\Page\PageIdentity;
use MediaWiki\Revision\RevisionAccessException;
use MediaWiki\Revision\RevisionRecord;
use MediaWiki\Revision\RevisionStore;
@ -63,41 +62,52 @@ class PageConfigFactory extends \Wikimedia\Parsoid\Config\PageConfigFactory {
*
* Note that Parsoid isn't supposed to use the user context by design; all
* user-specific processing is expected to be introduced as a post-parse
* transform. The $user parameter is therefore usually null, especially
* transform. The $user parameter is therefore usually null, especially
* in background job parsing, although there are corner cases during
* extension processing where a non-null $user could affect the output.
*
* @param LinkTarget $title The page represented by the PageConfig.
* @param PageIdentity $pageId The page represented by the PageConfig.
* @param ?UserIdentity $user User who is doing rendering (for parsing options).
* @param ?int $revisionId The revision of the page.
* @param ?string $wikitextOverride Wikitext to use instead of the
* contents of the specific $revision; used when $revision is null
* (a new page) or when we are parsing a stashed text.
* @param int|RevisionRecord|null $revision Revision id or a revision record
* @param ?string $unused
* @param ?string $pagelanguageOverride
* @param ?array $parsoidSettings Used to enable the debug API if requested
* @return \Wikimedia\Parsoid\Config\PageConfig
*/
public function create(
LinkTarget $title,
PageIdentity $pageId,
?UserIdentity $user = null,
?int $revisionId = null,
?string $wikitextOverride = null,
$revision = null,
?string $unused = null, /* Added to mollify CI with cross-repo uses */
?string $pagelanguageOverride = null,
?array $parsoidSettings = null
): \Wikimedia\Parsoid\Config\PageConfig {
$title = Title::newFromLinkTarget( $title );
$title = Title::castFromPageIdentity( $pageId );
'@phan-var Title $title';
if ( !empty( $parsoidSettings['debugApi'] ) ) {
return ApiPageConfig::fromSettings( $parsoidSettings, [
"title" => $title->getPrefixedText(),
"pageContent" => $wikitextOverride,
"pageLanguage" => $pagelanguageOverride,
"revid" => $revisionId,
"loadData" => true,
] );
if ( $revision === null ) {
throw new \InvalidArgumentException(
"Revision not provided. Cannot lookup revision via debug API." );
}
$content = $revision->getContent( SlotRecord::MAIN );
if ( $content instanceof WikitextContent ) {
$wtContent = $content->getText();
return ApiPageConfig::fromSettings( $parsoidSettings, [
"title" => $title->getPrefixedText(),
"pageContent" => $wtContent,
"pageLanguage" => $pagelanguageOverride,
"revid" => $revision->getId(),
"loadData" => true,
] );
} else {
throw new \UnexpectedValueException(
"Non-wikitext content models not supported by debug API" );
}
}
if ( $revisionId === null ) {
if ( $revision === null ) {
// Fetch the 'latest' revision for the given title.
// Note: This initial fetch of the page context revision is
// *not* using Parser::fetchCurrentRevisionRecordOfTitle()
@ -110,31 +120,31 @@ class PageConfigFactory extends \Wikimedia\Parsoid\Config\PageConfigFactory {
) ?: null;
// Note that $revisionRecord could still be null here if no
// page with that $title yet exists.
} elseif ( !is_int( $revision ) ) {
$revisionRecord = $revision;
} else {
// Fetch the correct revision record by the supplied id.
// This accesses the replica DB and may (or may not) fail over to
// the primary DB if the revision isn't found.
$revisionRecord = $this->revisionStore->getRevisionById(
$revisionId
);
$revisionRecord = $this->revisionStore->getRevisionById( $revision );
if ( $revisionRecord === null ) {
// This revision really ought to exist. Check the primary DB.
// This *could* cause two requests to the primary DB if there
// were pending writes, but this codepath should be very rare.
// [T259855]
$revisionRecord = $this->revisionStore->getRevisionById(
$revisionId, RevisionStore::READ_LATEST
$revision, RevisionStore::READ_LATEST
);
$success = ( $revisionRecord !== null ) ? 'success' : 'failure';
LoggerFactory::getInstance( 'Parsoid' )->error(
"Retried revision fetch after failure: {$success}", [
'id' => $revisionId,
'id' => $revision,
'title' => $title->getPrefixedText(),
]
);
}
if ( $revisionRecord === null ) {
throw new RevisionAccessException( "Can't find revision {$revisionId}" );
throw new RevisionAccessException( "Can't find revision {$revision}" );
}
}
@ -149,24 +159,6 @@ class PageConfigFactory extends \Wikimedia\Parsoid\Config\PageConfigFactory {
throw new RevisionAccessException( 'Not an available content version.' );
}
if ( $wikitextOverride !== null ) {
if ( $revisionRecord ) {
// PORT-FIXME this is not really the right thing to do; need
// a clone-like constructor for MutableRevisionRecord
$revisionRecord = MutableRevisionRecord::newFromParentRevision(
$revisionRecord
);
} else {
$revisionRecord = new MutableRevisionRecord( $title );
}
$revisionRecord->setSlot(
SlotRecord::newUnsaved(
SlotRecord::MAIN,
new WikitextContent( $wikitextOverride )
)
);
}
$parserOptions =
$user
? ParserOptions::newFromUser( $user )

View file

@ -1572,35 +1572,6 @@ class ParserTestRunner {
);
}
private function setupParsoidTransform( ParserTest $test ): array {
$services = MediaWikiServices::getInstance();
$pageConfigFactory = $services->get( 'ParsoidPageConfigFactory' );
$pageConfig = null;
[ $title, $options, $revId ] = $this->setupParserOptions(
$test,
static function ( $context, $title, $revId, $wikitext ) use ( $pageConfigFactory, &$pageConfig ) {
$pageConfig = $pageConfigFactory->create(
$title,
$context->getUser(),
// @todo T270310: Parsoid doesn't have a mechanism
// to override revid with a fake revision, like the
// legacy parser does, so {{REVISIONID}} will be
// 'wrong' in parser tests. Probably need to
// override
// ParserOptions::getCurrentRevisionRecordCallback()
// (like we do for the 'lastsavedrevision' option
// below) in order to fix this.
null/*$revId*/,
// @todo T270310: Parsoid should really accept a
// RevisionRecord here, instead of raw wikitext.
$wikitext,
$context->getLanguage()->getCode()
);
return $pageConfig->getParserOptions();
} );
return [ $pageConfig, $title, $options, $revId ];
}
/**
* @param Parsoid $parsoid
* @param PageConfig $pageConfig
@ -1771,6 +1742,38 @@ class ParserTestRunner {
}
}
private function setupParsoidTransform( ParserTest $test ): array {
$services = MediaWikiServices::getInstance();
$pageConfigFactory = $services->get( 'ParsoidPageConfigFactory' );
$pageConfig = null;
$runner = $this;
[ $title, $options, $revId ] = $this->setupParserOptions(
$test,
static function ( $context, $title, $revId, $wikitext ) use ( $runner, $pageConfigFactory, &$pageConfig ) {
$user = $context->getUser();
$content = new WikitextContent( $wikitext );
$title = Title::newFromRow( (object)[
'page_id' => 187,
'page_len' => $content->getSize(),
'page_latest' => 1337,
'page_namespace' => $title->getNamespace(),
'page_title' => $title->getDBkey(),
'page_is_redirect' => 0
] );
$revRecord = new MutableRevisionRecord( $title );
$revRecord->setContent( SlotRecord::MAIN, $content )
->setUser( $user )
->setTimestamp( strval( $runner->getFakeTimestamp() ) )
->setPageId( $title->getArticleID() )
->setId( $title->getLatestRevID() );
$pageConfig = $pageConfigFactory->create(
$title, $user, $revRecord, $context->getLanguage()->getCode()
);
return $pageConfig->getParserOptions();
} );
return [ $pageConfig, $title, $options, $revId ];
}
/**
* Helper function to register a Parsoid extension module in such a
* way that it can be cleaned up after the test is complete.