From 684178dfb60c514547d46cca610b88f72df015ce Mon Sep 17 00:00:00 2001 From: daniel Date: Wed, 23 May 2012 08:53:01 +0200 Subject: [PATCH] Use Title, not IContextSource; remove createArticle, etc. This merges the latest core patch into the Wikidata branch, implementing suggestions collected on gerrit. Most importantly: * Methods in the Content class no longer rely on a IContextSource * createArticle and createEditPage were removed from Contenthandler --- includes/Article.php | 11 +- includes/Content.php | 34 +- includes/ContentHandler.php | 65 --- includes/EditPage.php | 2 +- includes/LinksUpdate.php | 1 + includes/Title.php | 6 +- includes/WikiFilePage.php | 4 +- includes/WikiPage.php | 28 +- includes/actions/EditAction.php | 5 +- maintenance/refreshLinks.php | 2 +- tests/phpunit/includes/ContentHandlerTest.php | 4 +- tests/phpunit/includes/WikiPageTest.php | 56 ++- .../phpunit/includes/WikitextContentTest.php | 4 +- .../includes/WikitextContentTest.php.orig | 438 ++++++++++++++++++ 14 files changed, 538 insertions(+), 122 deletions(-) create mode 100644 tests/phpunit/includes/WikitextContentTest.php.orig diff --git a/includes/Article.php b/includes/Article.php index defc9e19f8..57ccab2397 100644 --- a/includes/Article.php +++ b/includes/Article.php @@ -119,14 +119,13 @@ class Article extends Page { if ( !$page ) { switch( $title->getNamespace() ) { case NS_FILE: - $page = new ImagePage( $title ); #FIXME: teach ImagePage to use ContentHandler + $page = new ImagePage( $title ); break; case NS_CATEGORY: - $page = new CategoryPage( $title ); #FIXME: teach ImagePage to use ContentHandler + $page = new CategoryPage( $title ); break; default: - $handler = ContentHandler::getForTitle( $title ); - $page = $handler->createArticle( $title ); + $page = new Article( $title ); } } $page->setContext( $context ); @@ -642,7 +641,7 @@ class Article extends Page { # Viewing a redirect page (e.g. with parameter redirect=no) $outputPage->addHTML( $this->viewRedirect( $rt ) ); # Parse just to get categories, displaytitle, etc. - $this->mParserOutput = $content->getParserOutput( $this->getContext(), $oldid, $parserOptions, false ); + $this->mParserOutput = $content->getParserOutput( $this->getTitle(), $oldid, $parserOptions, false ); $outputPage->addParserOutputNoText( $this->mParserOutput ); $outputDone = true; } @@ -780,7 +779,7 @@ class Article extends Page { // Give hooks a chance to customise the output if ( !Hooks::isRegistered('ShowRawCssJs') || wfRunHooks( 'ShowRawCssJs', array( $this->fetchContent(), $this->getTitle(), $wgOut ) ) ) { #FIXME: fetchContent() is deprecated - $po = $this->mContentObject->getParserOutput( $this->getContext() ); + $po = $this->mContentObject->getParserOutput( $this->getTitle() ); $wgOut->addHTML( $po->getText() ); } } diff --git a/includes/Content.php b/includes/Content.php index 54024991cf..9463c91c61 100644 --- a/includes/Content.php +++ b/includes/Content.php @@ -291,7 +291,7 @@ abstract class Content { public abstract function isCountable( $hasLinks = null ) ; /** - * @param IContextSource $context + * @param Title $title * @param null $revId * @param null|ParserOptions $options * @param Boolean $generateHtml whether to generate Html (default: true). If false, @@ -302,20 +302,20 @@ abstract class Content { * * @return ParserOutput */ - public abstract function getParserOutput( IContextSource $context, $revId = null, ParserOptions $options = null, $generateHtml = true ); + public abstract function getParserOutput( Title $title, $revId = null, ParserOptions $options = null, $generateHtml = true ); /** * Returns a list of DataUpdate objects for recording information about this Content in some secondary * data store. If the optional second argument, $old, is given, the updates may model only the changes that * need to be made to replace information about the old content with infomration about the new content. * - * This default implementation calls $this->getParserOutput( $context, null, null, false ), and then - * calls getSecondaryDataUpdates( $context->getTitle(), $recursive ) on the resulting ParserOutput object. + * This default implementation calls $this->getParserOutput( $title, null, null, false ), and then + * calls getSecondaryDataUpdates( $title, $recursive ) on the resulting ParserOutput object. * * Subclasses may implement this to determine the necessary updates more efficiently, or make use of information * about the old content. * - * @param IContextSource $context the content for determining the necessary updates + * @param Title $title the context for determining the necessary updates * @param Content|null $old a Content object representing the previous content, i.e. the content being * replaced by this Content object. * @param bool $recursive whether to include recursive updates (default: false). @@ -324,9 +324,9 @@ abstract class Content { * * @since WD.1 */ - public function getSecondaryDataUpdates( IContextSource $context, Content $old = null, $recursive = false ) { - $po = $this->getParserOutput( $context, null, null, false ); - return $po->getSecondaryDataUpdates( $context->getTitle(), $recursive ); + public function getSecondaryDataUpdates( Title $title, Content $old = null, $recursive = false ) { + $po = $this->getParserOutput( $title, null, null, false ); + return $po->getSecondaryDataUpdates( $title, $recursive ); } /** @@ -563,7 +563,7 @@ abstract class TextContent extends Content { * * @return ParserOutput representing the HTML form of the text */ - public function getParserOutput( IContextSource $context, $revId = null, ParserOptions $options = null, $generateHtml = true ) { + public function getParserOutput( Title $title, $revId = null, ParserOptions $options = null, $generateHtml = true ) { # generic implementation, relying on $this->getHtml() if ( $generateHtml ) $html = $this->getHtml( $options ); @@ -603,14 +603,14 @@ class WikitextContent extends TextContent { * * @return ParserOutput representing the HTML form of the text */ - public function getParserOutput( IContextSource $context, $revId = null, ParserOptions $options = null, $generateHtml = true ) { + public function getParserOutput( Title $title, $revId = null, ParserOptions $options = null, $generateHtml = true ) { global $wgParser; if ( !$options ) { - $options = ParserOptions::newFromUserAndLang( $context->getUser(), $context->getLanguage() ); + $options = new ParserOptions(); } - $po = $wgParser->parse( $this->mText, $context->getTitle(), $options, true, true, $revId ); + $po = $wgParser->parse( $this->mText, $title, $options, true, true, $revId ); return $po; } @@ -748,7 +748,7 @@ class WikitextContent extends TextContent { * * @return bool true if the content is countable */ - public function isCountable( $hasLinks = null, IContextSource $context = null ) { + public function isCountable( $hasLinks = null, Title $title = null ) { global $wgArticleCountMethod, $wgRequest; if ( $this->isRedirect( ) ) { @@ -764,12 +764,12 @@ class WikitextContent extends TextContent { return strpos( $text, ',' ) !== false; case 'link': if ( $hasLinks === null ) { # not known, find out - if ( !$context ) { # make dummy context - //XXX: caller of this method often knows the title, but not a context... - $context = new RequestContext( $wgRequest ); + if ( !$title ) { + $context = RequestContext::getMain(); + $title = $context->getTitle(); } - $po = $this->getParserOutput( $context, null, null, false ); + $po = $this->getParserOutput( $title, null, null, false ); $links = $po->getLinks(); $hasLinks = !empty( $links ); } diff --git a/includes/ContentHandler.php b/includes/ContentHandler.php index ba33a0144a..80f729be15 100644 --- a/includes/ContentHandler.php +++ b/includes/ContentHandler.php @@ -475,56 +475,6 @@ abstract class ContentHandler { return array(); } - /** - * Return an Article object suitable for viewing the given object - * - * NOTE: does *not* do special handling for Image and Category pages! - * Use Article::newFromTitle() for that! - * - * @since WD.1 - * - * @param Title $title - * @return Article - * @todo Article is being refactored into an action class, keep track of that - * @todo Article really defines the view of the content... rename this method to createViewPage ? - */ - public function createArticle( Title $title ) { - $this->checkModelID( $title->getContentModel() ); - - $article = new Article($title); - return $article; - } - - /** - * Return an EditPage object suitable for editing the given object. - * This default implementation always fails with an MWException, because there is no - * generic edit page implementation suitable for all content models. - * - * @since WD.1 - * - * @param Article $article - * @return EditPage - */ - public function createEditPage( Article $article ) { - throw new MWException( "ContentHandler class " . get_classs( $this ) . " does not provide an EditPage." ); - } - - /** - * Return an ExternalEdit object suitable for editing the given object - * - * @since WD.1 - * - * @param IContextSource $context - * @return ExternalEdit - * @todo does anyone or anythign actually use the external edit facility? Can we just deprecate and ignore it? - */ - public function createExternalEdit( IContextSource $context ) { - $this->checkModelID( $context->getTitle()->getContentModel() ); - - $externalEdit = new ExternalEdit( $context ); - return $externalEdit; - } - /** * Factory * @since WD.1 @@ -816,21 +766,6 @@ abstract class TextContentHandler extends ContentHandler { return $content->getNativeData(); } - /** - * Return an EditPage object for editing the given object - * - * @since WD.1 - * - * @param Article $article - * @return EditPage - */ - public function createEditPage( Article $article ) { - $this->checkModelID( $article->getPage()->getContentModel() ); - - $editPage = new EditPage( $article ); - return $editPage; - } - /** * attempts to merge differences between three versions. * Returns a new Content object for a clean merge and false for failure or a conflict. diff --git a/includes/EditPage.php b/includes/EditPage.php index 72341b4f90..5273c54d09 100644 --- a/includes/EditPage.php +++ b/includes/EditPage.php @@ -2821,7 +2821,7 @@ HTML $content = $content->preSaveTransform( $this->mTitle, $wgUser, $parserOptions ); // TODO: might be a saner way to get a meaningfull context here? - $parserOutput = $content->getParserOutput( $this->getArticle()->getContext(), null, $parserOptions ); + $parserOutput = $content->getParserOutput( $this->getArticle()->getTitle(), null, $parserOptions ); $previewHTML = $parserOutput->getText(); $this->mParserOutput = $parserOutput; diff --git a/includes/LinksUpdate.php b/includes/LinksUpdate.php index 56595cacc5..4b484a7934 100644 --- a/includes/LinksUpdate.php +++ b/includes/LinksUpdate.php @@ -63,6 +63,7 @@ class LinksUpdate extends SqlDataUpdate { $this->mTitle = $title; $this->mId = $title->getArticleID(); + assert( $this->mId > 0 ); $this->mParserOutput = $parserOutput; diff --git a/includes/Title.php b/includes/Title.php index 880c95f70d..1681c4a371 100644 --- a/includes/Title.php +++ b/includes/Title.php @@ -2857,8 +2857,12 @@ class Title { if ( !$this->getArticleID( $flags ) ) { return $this->mRedirect = false; } + $linkCache = LinkCache::singleton(); - $this->mRedirect = (bool)$linkCache->getGoodLinkFieldObj( $this, 'redirect' ); + $cached = $linkCache->getGoodLinkFieldObj( $this, 'redirect' ); + assert( $cached !== null ); # assert the assumption that the cache actually knows about this title + + $this->mRedirect = (bool)$cached; return $this->mRedirect; } diff --git a/includes/WikiFilePage.php b/includes/WikiFilePage.php index 9fb1522d73..3197b1dfc4 100644 --- a/includes/WikiFilePage.php +++ b/includes/WikiFilePage.php @@ -41,7 +41,9 @@ class WikiFilePage extends WikiPage { } public function getActionOverrides() { - return array( 'revert' => 'RevertFileAction' ); + $overrides = parent::getActionOverrides(); + $overrides[ 'revert' ] = 'RevertFileAction'; + return $overrides; } /** diff --git a/includes/WikiPage.php b/includes/WikiPage.php index 8f73eecab5..dae465e278 100644 --- a/includes/WikiPage.php +++ b/includes/WikiPage.php @@ -1093,11 +1093,10 @@ class WikiPage extends Page { * @param $parserOptions ParserOptions to use for the parse operation * @param $oldid Revision ID to get the text from, passing null or 0 will * get the current revision (default value) - * @param $context IContextSource context for parsing * * @return ParserOutput or false if the revision was not found */ - public function getParserOutput( ParserOptions $parserOptions, $oldid = null, IContextSource $context = null ) { + public function getParserOutput( ParserOptions $parserOptions, $oldid = null ) { wfProfileIn( __METHOD__ ); $useParserCache = $this->isParserCacheUsed( $parserOptions, $oldid ); @@ -1118,7 +1117,7 @@ class WikiPage extends Page { $oldid = $this->getLatest(); } - $pool = new PoolWorkArticleView( $this, $parserOptions, $oldid, $useParserCache, null, $context ); + $pool = new PoolWorkArticleView( $this, $parserOptions, $oldid, $useParserCache, null ); $pool->execute(); wfProfileOut( __METHOD__ ); @@ -1934,11 +1933,7 @@ class WikiPage extends Page { $edit->popts = $this->makeParserOptions( 'canonical' ); - // TODO: is there no better way to obtain a context here? - $context = RequestContext::getMain(); - $context->setTitle( $this->mTitle ); - $edit->output = $edit->pstContent->getParserOutput( $context, $revid, $edit->popts ); - $edit->updates = $edit->pstContent->getSecondaryDataUpdate( $context ); + $edit->output = $edit->pstContent->getParserOutput( $this->mTitle, $revid, $edit->popts ); $edit->newContent = $content; $edit->oldContent = $this->getContent( Revision::RAW ); @@ -1992,7 +1987,7 @@ class WikiPage extends Page { } # Update the links tables and other secondary data - $updates = $editInfo->updates; + $updates = $editInfo->output->getSecondaryDataUpdates( $this->getTitle() ); DataUpdate::runUpdates( $updates ); wfRunHooks( 'ArticleEditUpdates', array( &$this, &$editInfo, $options['changed'] ) ); @@ -3225,9 +3220,9 @@ class PoolWorkArticleView extends PoolCounterWork { private $parserOptions; /** - * @var string|null + * @var Content|null */ - private $text; + private $content = null; /** * @var ParserOutput|bool @@ -3252,23 +3247,16 @@ class PoolWorkArticleView extends PoolCounterWork { * @param $useParserCache Boolean: whether to use the parser cache * @param $parserOptions parserOptions to use for the parse operation * @param $content Content|String: content to parse or null to load it; may also be given as a wikitext string, for BC - * @param $context IContextSource context for parsing */ - function __construct( Page $page, ParserOptions $parserOptions, $revid, $useParserCache, $content = null, IContextSource $context = null ) { + function __construct( Page $page, ParserOptions $parserOptions, $revid, $useParserCache, $content = null ) { if ( is_string($content) ) { #BC: old style call $modelId = $page->getRevision()->getContentModel(); $format = $page->getRevision()->getContentFormat(); $content = ContentHandler::makeContent( $content, $page->getTitle(), $modelId, $format ); } - if ( is_null( $context ) ) { - $context = RequestContext::getMain(); - #XXX: clone and then set title? - } - $this->page = $page; $this->revid = $revid; - $this->context = $context; $this->cacheable = $useParserCache; $this->parserOptions = $parserOptions; $this->content = $content; @@ -3327,7 +3315,7 @@ class PoolWorkArticleView extends PoolCounterWork { $time = - microtime( true ); // TODO: page might not have this method? Hard to tell what page is supposed to be here... - $this->parserOutput = $content->getParserOutput( $this->context, $this->revid, $this->parserOptions ); + $this->parserOutput = $content->getParserOutput( $this->page->getTitle(), $this->revid, $this->parserOptions ); $time += microtime( true ); # Timing hack diff --git a/includes/actions/EditAction.php b/includes/actions/EditAction.php index 2888d247d8..3771586df3 100644 --- a/includes/actions/EditAction.php +++ b/includes/actions/EditAction.php @@ -40,7 +40,10 @@ class EditAction extends FormlessAction { $context = $this->getContext(); if ( wfRunHooks( 'CustomEditor', array( $page, $user ) ) ) { - $handler = ContentHandler::getForTitle( $page->getTitle() ); + if ( ( $page->getContent() instanceof TextContentHandler ) ) { + $modelName = ContentHandler::getContentModelName( $page->getContentModel() ); + throw new MWException( "Can't use default editor for non-text content. ContentHandler for $modelName apparently does not provide an action handler for the edit action." ); + } if ( ExternalEdit::useExternalEngine( $context, 'edit' ) && $this->getName() == 'edit' && !$request->getVal( 'section' ) diff --git a/maintenance/refreshLinks.php b/maintenance/refreshLinks.php index f954b9ef5d..602874b7f7 100644 --- a/maintenance/refreshLinks.php +++ b/maintenance/refreshLinks.php @@ -220,7 +220,7 @@ class RefreshLinks extends Maintenance { $context = RequestContext::getMain(); - $updates = $content->getSecondaryDataUpdates( $context ); + $updates = $parserOutput->getSecondaryDataUpdates( $page->getTitle(), false ); DataUpdate::runUpdates( $updates ); $dbw->commit( __METHOD__ ); diff --git a/tests/phpunit/includes/ContentHandlerTest.php b/tests/phpunit/includes/ContentHandlerTest.php index 8465c20569..05cfb67909 100644 --- a/tests/phpunit/includes/ContentHandlerTest.php +++ b/tests/phpunit/includes/ContentHandlerTest.php @@ -383,7 +383,7 @@ class DummyContentForTesting extends Content { } /** - * @param IContextSource $context + * @param Title $title * @param null $revId * @param null|ParserOptions $options * @param Boolean $generateHtml whether to generate Html (default: true). If false, @@ -392,7 +392,7 @@ class DummyContentForTesting extends Content { * * @return ParserOutput */ - public function getParserOutput( IContextSource $context, $revId = null, ParserOptions $options = NULL, $generateHtml = true ) + public function getParserOutput( Title $title, $revId = null, ParserOptions $options = NULL, $generateHtml = true ) { return new ParserOutput( $this->data ); } diff --git a/tests/phpunit/includes/WikiPageTest.php b/tests/phpunit/includes/WikiPageTest.php index 2540603880..cdf6fcda83 100644 --- a/tests/phpunit/includes/WikiPageTest.php +++ b/tests/phpunit/includes/WikiPageTest.php @@ -32,6 +32,8 @@ class WikiPageTest extends MediaWikiTestCase { public function setUp() { $this->pages_to_delete = array(); + + LinkCache::singleton()->clear(); # avoid cached redirect status, etc } public function tearDown() { @@ -48,6 +50,10 @@ class WikiPageTest extends MediaWikiTestCase { } } + /** + * @param Title $title + * @return WikiPage + */ protected function newPage( $title ) { if ( is_string( $title ) ) $title = Title::newFromText( $title ); @@ -58,9 +64,23 @@ class WikiPageTest extends MediaWikiTestCase { return $p; } + + /** + * @param String|Title|WikiPage $page + * @param String $text + * @param int $model + * + * @return WikiPage + */ protected function createPage( $page, $text, $model = null ) { if ( is_string( $page ) ) $page = Title::newFromText( $page ); - if ( $page instanceof Title ) $page = $this->newPage( $page ); + + if ( $page instanceof Title ) { + $title = $page; + $page = $this->newPage( $page ); + } else { + $title = null; + } $content = ContentHandler::makeContent( $text, $page->getTitle(), $model ); $page->doEditContent( $content, "testing", EDIT_NEW ); @@ -77,13 +97,23 @@ class WikiPageTest extends MediaWikiTestCase { . " nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat.", $title ); - $page->doEditContent( $content, "testing 1" ); + $page->doEditContent( $content, "[[testing]] 1" ); + $this->assertTrue( $title->getArticleID() > 0, "Title object should have new page id" ); + $this->assertTrue( $page->getId() > 0, "WikiPage should have new page id" ); $this->assertTrue( $title->exists(), "Title object should indicate that the page now exists" ); $this->assertTrue( $page->exists(), "WikiPage object should indicate that the page now exists" ); $id = $page->getId(); + # ------------------------ + $dbr = wfGetDB( DB_SLAVE ); + $res = $dbr->select( 'pagelinks', '*', array( 'pl_from' => $id ) ); + $n = $res->numRows(); + $res->free(); + + $this->assertEquals( 1, $n, 'pagelinks should contain one link from the page' ); + # ------------------------ $page = new WikiPage( $title ); @@ -120,13 +150,23 @@ class WikiPageTest extends MediaWikiTestCase { $text = "[[Lorem ipsum]] dolor sit amet, consetetur sadipscing elitr, sed diam " . " nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat."; - $page->doEdit( $text, "testing 1" ); + $page->doEdit( $text, "[[testing]] 1" ); + $this->assertTrue( $title->getArticleID() > 0, "Title object should have new page id" ); + $this->assertTrue( $page->getId() > 0, "WikiPage should have new page id" ); $this->assertTrue( $title->exists(), "Title object should indicate that the page now exists" ); $this->assertTrue( $page->exists(), "WikiPage object should indicate that the page now exists" ); $id = $page->getId(); + # ------------------------ + $dbr = wfGetDB( DB_SLAVE ); + $res = $dbr->select( 'pagelinks', '*', array( 'pl_from' => $id ) ); + $n = $res->numRows(); + $res->free(); + + $this->assertEquals( 1, $n, 'pagelinks should contain one link from the page' ); + # ------------------------ $page = new WikiPage( $title ); @@ -186,6 +226,8 @@ class WikiPageTest extends MediaWikiTestCase { $page->doDeleteArticle( "testing deletion" ); + $this->assertFalse( $page->getTitle()->getArticleID() > 0, "Title object should now have page id 0" ); + $this->assertFalse( $page->getId() > 0, "WikiPage should now have page id 0" ); $this->assertFalse( $page->exists(), "WikiPage::exists should return false after page was deleted" ); $this->assertNull( $page->getContent(), "WikiPage::getContent should return null after page was deleted" ); $this->assertFalse( $page->getText(), "WikiPage::getText should return false after page was deleted" ); @@ -444,15 +486,19 @@ class WikiPageTest extends MediaWikiTestCase { public function testIsCountable( $title, $text, $mode, $expected ) { global $wgArticleCountMethod; - $old = $wgArticleCountMethod; + $oldArticleCountMethod = $wgArticleCountMethod; $wgArticleCountMethod = $mode; $page = $this->createPage( $title, $text ); + $hasLinks = wfGetDB( DB_SLAVE )->selectField( 'pagelinks', 1, + array( 'pl_from' => $page->getId() ), __METHOD__ ); + $editInfo = $page->prepareContentForEdit( $page->getContent() ); $v = $page->isCountable(); $w = $page->isCountable( $editInfo ); - $wgArticleCountMethod = $old; + + $wgArticleCountMethod = $oldArticleCountMethod; $this->assertEquals( $expected, $v, "isCountable( null ) returned unexpected value " . var_export( $v, true ) . " instead of " . var_export( $expected, true ) . " in mode `$mode` for text \"$text\"" ); diff --git a/tests/phpunit/includes/WikitextContentTest.php b/tests/phpunit/includes/WikitextContentTest.php index 57f992adc1..5feec832e7 100644 --- a/tests/phpunit/includes/WikitextContentTest.php +++ b/tests/phpunit/includes/WikitextContentTest.php @@ -27,7 +27,7 @@ class WikitextContentTest extends MediaWikiTestCase { public function testGetParserOutput( $text, $expectedHtml ) { $content = $this->newContent( $text ); - $po = $content->getParserOutput( $this->context ); + $po = $content->getParserOutput( $this->context->getTitle() ); $this->assertEquals( $expectedHtml, $po->getText() ); return $po; @@ -304,7 +304,7 @@ just a test" $content = $this->newContent( $text ); - $v = $content->isCountable( $hasLinks, $this->context ); + $v = $content->isCountable( $hasLinks, $this->context->getTitle() ); $wgArticleCountMethod = $old; $this->assertEquals( $expected, $v, "isCountable() returned unexpected value " . var_export( $v, true ) diff --git a/tests/phpunit/includes/WikitextContentTest.php.orig b/tests/phpunit/includes/WikitextContentTest.php.orig new file mode 100644 index 0000000000..b0e9c8dc9d --- /dev/null +++ b/tests/phpunit/includes/WikitextContentTest.php.orig @@ -0,0 +1,438 @@ +context = new RequestContext( new FauxRequest() ); + $this->context->setTitle( Title::newFromText( "Test" ) ); + } + + public function newContent( $text ) { + return new WikitextContent( $text ); + } + + public function dataGetParserOutput() { + return array( + array("hello ''world''\n", "

hello world\n

"), + // @todo: more...? + ); + } + + /** + * @dataProvider dataGetParserOutput + */ + public function testGetParserOutput( $text, $expectedHtml ) { + $content = $this->newContent( $text ); + + $po = $content->getParserOutput( $this->context ); + + $this->assertEquals( $expectedHtml, $po->getText() ); + } + + public function dataGetSecondaryDataUpdates() { + return array( + // @todo: more...? + ); + } + + /** + * @dataProvider dataGetParserOutput + */ + public function testGetSecondaryDataUpdates( $text, $expectedLinks ) { + $content = $this->newContent( "hello [[world]]\n" ); + + $updates = $content->getSecondaryDataUpdates( $this->context ); + + $this->assertEquals( 1, count( $updates ) ); + $this->assertEquals( "LinksUpdate", get_class( $updates[0] ) ); + } + + static $sections = + +"Intro + +== stuff == +hello world + +== test == +just a test + +== foo == +more stuff +"; + + public function dataGetSection() { + return array( + array( WikitextContentTest::$sections, + "0", + "Intro" + ), + array( WikitextContentTest::$sections, + "2", +"== test == +just a test" + ), + array( WikitextContentTest::$sections, + "8", + false + ), + ); + } + + /** + * @dataProvider dataGetSection + */ + public function testGetSection( $text, $sectionId, $expectedText ) { + $content = $this->newContent( $text ); + + $sectionContent = $content->getSection( $sectionId ); + + $this->assertEquals( $expectedText, is_null( $sectionContent ) ? null : $sectionContent->getNativeData() ); + } + + public function dataReplaceSection() { + return array( + array( WikitextContentTest::$sections, + "0", + "No more", + null, + trim( preg_replace( '/^Intro/sm', 'No more', WikitextContentTest::$sections ) ) + ), + array( WikitextContentTest::$sections, + "", + "No more", + null, + "No more" + ), + array( WikitextContentTest::$sections, + "2", + "== TEST ==\nmore fun", + null, + trim( preg_replace( '/^== test ==.*== foo ==/sm', "== TEST ==\nmore fun\n\n== foo ==", WikitextContentTest::$sections ) ) + ), + array( WikitextContentTest::$sections, + "8", + "No more", + null, + WikitextContentTest::$sections + ), + array( WikitextContentTest::$sections, + "new", + "No more", + "New", + trim( WikitextContentTest::$sections ) . "\n\n\n== New ==\n\nNo more" + ), + ); + } + + /** + * @dataProvider dataReplaceSection + */ + public function testReplaceSection( $text, $section, $with, $sectionTitle, $expected ) { + $content = $this->newContent( $text ); + $c = $content->replaceSection( $section, $this->newContent( $with ), $sectionTitle ); + + $this->assertEquals( $expected, is_null( $c ) ? null : $c->getNativeData() ); + } + + public function testAddSectionHeader( ) { + $content = $this->newContent( 'hello world' ); + $content = $content->addSectionHeader( 'test' ); + + $this->assertEquals( "== test ==\n\nhello world", $content->getNativeData() ); + } + + public function dataPreSaveTransform() { + return array( + array( 'hello this is ~~~', + "hello this is [[Special:Contributions/127.0.0.1|127.0.0.1]]", + ), + array( 'hello \'\'this\'\' is ~~~', + 'hello \'\'this\'\' is ~~~', + ), + ); + } + + /** + * @dataProvider dataPreSaveTransform + */ + public function testPreSaveTransform( $text, $expected ) { + global $wgUser, $wgContLang; + $options = ParserOptions::newFromUserAndLang( $wgUser, $wgContLang ); + + $content = $this->newContent( $text ); + $content = $content->preSaveTransform( $this->context->getTitle(), $this->context->getUser(), $options ); + + $this->assertEquals( $expected, $content->getNativeData() ); + } + + public function dataPreloadTransform() { + return array( + array( 'hello this is ~~~', + "hello this is ~~~", + ), + array( 'hello \'\'this\'\' is foobar', + 'hello \'\'this\'\' is bar', + ), + ); + } + + /** + * @dataProvider dataPreloadTransform + */ + public function testPreloadTransform( $text, $expected ) { + global $wgUser, $wgContLang; + $options = ParserOptions::newFromUserAndLang( $wgUser, $wgContLang ); + + $content = $this->newContent( $text ); + $content = $content->preloadTransform( $this->context->getTitle(), $options ); + + $this->assertEquals( $expected, $content->getNativeData() ); + } + + public function dataGetRedirectTarget() { + return array( + array( '#REDIRECT [[Test]]', + 'Test', + ), + array( '#REDIRECT Test', + null, + ), + array( '* #REDIRECT [[Test]]', + null, + ), + ); + } + + /** + * @dataProvider dataGetRedirectTarget + */ + public function testGetRedirectTarget( $text, $expected ) { + $content = $this->newContent( $text ); + $t = $content->getRedirectTarget( ); + + if ( is_null( $expected ) ) $this->assertNull( $t, "text should not have generated a redirect target: $text" ); + else $this->assertEquals( $expected, $t->getPrefixedText() ); + } + + /** + * @dataProvider dataGetRedirectTarget + */ + public function isRedirect( $text, $expected ) { + $content = $this->newContent( $text ); + + $this->assertEquals( !is_null($expected), $content->isRedirect() ); + } + + + /** + * @todo: test needs database! + */ + /* + public function getRedirectChain() { + $text = $this->getNativeData(); + return Title::newFromRedirectArray( $text ); + } + */ + + /** + * @todo: test needs database! + */ + /* + public function getUltimateRedirectTarget() { + $text = $this->getNativeData(); + return Title::newFromRedirectRecurse( $text ); + } + */ + + + public function dataIsCountable() { + return array( + array( '', + null, + 'any', + true + ), + array( 'Foo', + null, + 'any', + true + ), + array( 'Foo', + null, + 'comma', + false + ), + array( 'Foo, bar', + null, + 'comma', + true + ), + array( 'Foo', + null, + 'link', + false + ), + array( 'Foo [[bar]]', + null, + 'link', + true + ), + array( 'Foo', + true, + 'link', + true + ), + array( 'Foo [[bar]]', + false, + 'link', + false + ), + array( '#REDIRECT [[bar]]', + true, + 'any', + false + ), + array( '#REDIRECT [[bar]]', + true, + 'comma', + false + ), + array( '#REDIRECT [[bar]]', + true, + 'link', + false + ), + ); + } + + + /** + * @dataProvider dataIsCountable + */ + public function testIsCountable( $text, $hasLinks, $mode, $expected ) { + global $wgArticleCountMethod; + + $old = $wgArticleCountMethod; + $wgArticleCountMethod = $mode; + + $content = $this->newContent( $text ); + + $v = $content->isCountable( $hasLinks, $this->context ); + $wgArticleCountMethod = $old; + + $this->assertEquals( $expected, $v, "isCountable() returned unexpected value " . var_export( $v, true ) + . " instead of " . var_export( $expected, true ) . " in mode `$mode` for text \"$text\"" ); + } + + public function dataGetTextForSummary() { + return array( + array( "hello\nworld.", + 16, + 'hello world.', + ), + array( 'hello world.', + 8, + 'hello...', + ), + array( '[[hello world]].', + 8, + 'hel...', + ), + ); + } + + /** + * @dataProvider dataGetTextForSummary + */ + public function testGetTextForSummary( $text, $maxlength, $expected ) { + $content = $this->newContent( $text ); + + $this->assertEquals( $expected, $content->getTextForSummary( $maxlength ) ); + } + + + public function testGetTextForSearchIndex( ) { + $content = $this->newContent( "hello world." ); + + $this->assertEquals( "hello world.", $content->getTextForSearchIndex() ); + } + + public function testCopy() { + $content = $this->newContent( "hello world." ); + $copy = $content->copy(); + + $this->assertTrue( $content->equals( $copy ), "copy must be equal to original" ); + $this->assertEquals( "hello world.", $copy->getNativeData() ); + } + + public function testGetSize( ) { + $content = $this->newContent( "hello world." ); + + $this->assertEquals( 12, $content->getSize() ); + } + + public function testGetNativeData( ) { + $content = $this->newContent( "hello world." ); + + $this->assertEquals( "hello world.", $content->getNativeData() ); + } + + public function testGetWikitextForTransclusion( ) { + $content = $this->newContent( "hello world." ); + + $this->assertEquals( "hello world.", $content->getWikitextForTransclusion() ); + } + + # ================================================================================================================= + + public function testGetModel() { + $content = $this->newContent( "hello world." ); + + $this->assertEquals( CONTENT_MODEL_WIKITEXT, $content->getModel() ); + } + + public function testGetContentHandler() { + $content = $this->newContent( "hello world." ); + + $this->assertEquals( CONTENT_MODEL_WIKITEXT, $content->getContentHandler()->getModelID() ); + } + + public function dataIsEmpty( ) { + return array( + array( '', true ), + array( ' ', false ), + array( '0', false ), + array( 'hallo welt.', false ), + ); + } + + /** + * @dataProvider dataIsEmpty + */ + public function testIsEmpty( $text, $empty ) { + $content = $this->newContent( $text ); + + $this->assertEquals( $empty, $content->isEmpty() ); + } + + public function dataEquals( ) { + return array( + array( new WikitextContent( "hallo" ), null, false ), + array( new WikitextContent( "hallo" ), new WikitextContent( "hallo" ), true ), + array( new WikitextContent( "hallo" ), new JavascriptContent( "hallo" ), false ), + array( new WikitextContent( "hallo" ), new WikitextContent( "HALLO" ), false ), + ); + } + + /** + * @dataProvider dataEquals + */ + public function testEquals( Content $a, Content $b = null, $equal = false ) { + $this->assertEquals( $equal, $a->equals( $b ) ); + } + +} -- 2.20.1