From 9fe46fdd32aae14313ddc4b371cc1a31342ba843 Mon Sep 17 00:00:00 2001 From: addshore Date: Thu, 12 Oct 2017 13:23:33 +0100 Subject: [PATCH] Split Revision tests into Unit & Integration classes Change-Id: If10b102a1a0d680b5f067bf34c0fafcb59c09048 --- tests/common/TestsAutoLoader.php | 1 - ...geTest.php => RevisionIntegrationTest.php} | 324 +++++++++--- tests/phpunit/includes/RevisionTest.php | 478 ------------------ .../RevisionTestModifyableContent.php | 4 +- .../RevisionTestModifyableContentHandler.php | 2 +- tests/phpunit/includes/RevisionUnitTest.php | 205 ++++++++ .../mocks/content/DummyContentForTesting.php | 4 +- .../content/DummyContentHandlerForTesting.php | 2 +- 8 files changed, 461 insertions(+), 559 deletions(-) rename tests/phpunit/includes/{RevisionStorageTest.php => RevisionIntegrationTest.php} (69%) delete mode 100644 tests/phpunit/includes/RevisionTest.php create mode 100644 tests/phpunit/includes/RevisionUnitTest.php diff --git a/tests/common/TestsAutoLoader.php b/tests/common/TestsAutoLoader.php index a965d3e493..ca31fbc7ec 100644 --- a/tests/common/TestsAutoLoader.php +++ b/tests/common/TestsAutoLoader.php @@ -66,7 +66,6 @@ $wgAutoloadClasses += [ # tests/phpunit/includes 'RevisionTestModifyableContent' => "$testDir/phpunit/includes/RevisionTestModifyableContent.php", 'RevisionTestModifyableContentHandler' => "$testDir/phpunit/includes/RevisionTestModifyableContentHandler.php", - 'RevisionStorageTest' => "$testDir/phpunit/includes/RevisionStorageTest.php", 'TestLogger' => "$testDir/phpunit/includes/TestLogger.php", # tests/phpunit/includes/api diff --git a/tests/phpunit/includes/RevisionStorageTest.php b/tests/phpunit/includes/RevisionIntegrationTest.php similarity index 69% rename from tests/phpunit/includes/RevisionStorageTest.php rename to tests/phpunit/includes/RevisionIntegrationTest.php index a15b9b4407..e942ee5071 100644 --- a/tests/phpunit/includes/RevisionStorageTest.php +++ b/tests/phpunit/includes/RevisionIntegrationTest.php @@ -1,16 +1,13 @@ tablesUsed = array_merge( $this->tablesUsed, - [ 'page', + [ + 'page', 'revision', 'ip_changes', 'text', + 'archive', 'recentchanges', 'logging', @@ -35,7 +34,9 @@ class RevisionStorageTest extends MediaWikiTestCase { 'externallinks', 'imagelinks', 'templatelinks', - 'iwlinks' ] ); + 'iwlinks' + ] + ); } protected function setUp() { @@ -54,14 +55,15 @@ class RevisionStorageTest extends MediaWikiTestCase { $this->mergeMwGlobalArrayValue( 'wgNamespaceContentModels', [ - 12312 => 'DUMMY', + 12312 => DummyContentForTesting::MODEL_ID, ] ); $this->mergeMwGlobalArrayValue( 'wgContentHandlers', [ - 'DUMMY' => 'DummyContentHandlerForTesting', + DummyContentForTesting::MODEL_ID => 'DummyContentHandlerForTesting', + RevisionTestModifyableContent::MODEL_ID => 'RevisionTestModifyableContentHandler', ] ); @@ -75,8 +77,6 @@ class RevisionStorageTest extends MediaWikiTestCase { CONTENT_MODEL_WIKITEXT ); } - - $this->tablesUsed[] = 'archive'; } protected function tearDown() { @@ -342,71 +342,6 @@ class RevisionStorageTest extends MediaWikiTestCase { $this->assertEquals( $page->getId(), $rev->getPage() ); } - /** - * @covers Revision::getContent - */ - public function testGetContent_failure() { - $rev = new Revision( [ - 'page' => $this->the_page->getId(), - 'content_model' => $this->the_page->getContentModel(), - 'text_id' => 123456789, // not in the test DB - ] ); - - $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." ); - } - - /** - * @covers Revision::getContent - */ - public function testGetContent() { - $orig = $this->makeRevision( [ 'text' => 'hello hello.' ] ); - $rev = Revision::newFromId( $orig->getId() ); - - $this->assertEquals( 'hello hello.', $rev->getContent()->getNativeData() ); - } - - /** - * @covers Revision::getContentModel - */ - public function testGetContentModel() { - global $wgContentHandlerUseDB; - - if ( !$wgContentHandlerUseDB ) { - $this->markTestSkipped( '$wgContentHandlerUseDB is disabled' ); - } - - $orig = $this->makeRevision( [ 'text' => 'hello hello.', - 'content_model' => CONTENT_MODEL_JAVASCRIPT ] ); - $rev = Revision::newFromId( $orig->getId() ); - - $this->assertEquals( CONTENT_MODEL_JAVASCRIPT, $rev->getContentModel() ); - } - - /** - * @covers Revision::getContentFormat - */ - public function testGetContentFormat() { - global $wgContentHandlerUseDB; - - if ( !$wgContentHandlerUseDB ) { - $this->markTestSkipped( '$wgContentHandlerUseDB is disabled' ); - } - - $orig = $this->makeRevision( [ - 'text' => 'hello hello.', - 'content_model' => CONTENT_MODEL_JAVASCRIPT, - 'content_format' => CONTENT_FORMAT_JAVASCRIPT - ] ); - $rev = Revision::newFromId( $orig->getId() ); - - $this->assertEquals( CONTENT_FORMAT_JAVASCRIPT, $rev->getContentFormat() ); - } - /** * @covers Revision::isCurrent */ @@ -621,4 +556,241 @@ class RevisionStorageTest extends MediaWikiTestCase { $this->assertEquals( $expectedLast, $wasLast ); } + + /** + * @param string $text + * @param string $title + * @param string $model + * @param string $format + * + * @return Revision + */ + private function newTestRevision( $text, $title = "Test", + $model = CONTENT_MODEL_WIKITEXT, $format = null + ) { + if ( is_string( $title ) ) { + $title = Title::newFromText( $title ); + } + + $content = ContentHandler::makeContent( $text, $title, $model, $format ); + + $rev = new Revision( + [ + 'id' => 42, + 'page' => 23, + 'title' => $title, + + 'content' => $content, + 'length' => $content->getSize(), + 'comment' => "testing", + 'minor_edit' => false, + + 'content_format' => $format, + ] + ); + + return $rev; + } + + public function provideGetContentModel() { + // NOTE: we expect the help namespace to always contain wikitext + return [ + [ 'hello world', 'Help:Hello', null, null, CONTENT_MODEL_WIKITEXT ], + [ 'hello world', 'User:hello/there.css', null, null, CONTENT_MODEL_CSS ], + [ serialize( 'hello world' ), 'Dummy:Hello', null, null, DummyContentForTesting::MODEL_ID ], + ]; + } + + /** + * @dataProvider provideGetContentModel + * @covers Revision::getContentModel + */ + public function testGetContentModel( $text, $title, $model, $format, $expectedModel ) { + $rev = $this->newTestRevision( $text, $title, $model, $format ); + + $this->assertEquals( $expectedModel, $rev->getContentModel() ); + } + + public function provideGetContentFormat() { + // NOTE: we expect the help namespace to always contain wikitext + return [ + [ 'hello world', 'Help:Hello', null, null, CONTENT_FORMAT_WIKITEXT ], + [ 'hello world', 'Help:Hello', CONTENT_MODEL_CSS, null, CONTENT_FORMAT_CSS ], + [ 'hello world', 'User:hello/there.css', null, null, CONTENT_FORMAT_CSS ], + [ serialize( 'hello world' ), 'Dummy:Hello', null, null, DummyContentForTesting::MODEL_ID ], + ]; + } + + /** + * @dataProvider provideGetContentFormat + * @covers Revision::getContentFormat + */ + public function testGetContentFormat( $text, $title, $model, $format, $expectedFormat ) { + $rev = $this->newTestRevision( $text, $title, $model, $format ); + + $this->assertEquals( $expectedFormat, $rev->getContentFormat() ); + } + + public function provideGetContentHandler() { + // NOTE: we expect the help namespace to always contain wikitext + return [ + [ 'hello world', 'Help:Hello', null, null, 'WikitextContentHandler' ], + [ 'hello world', 'User:hello/there.css', null, null, 'CssContentHandler' ], + [ serialize( 'hello world' ), 'Dummy:Hello', null, null, 'DummyContentHandlerForTesting' ], + ]; + } + + /** + * @dataProvider provideGetContentHandler + * @covers Revision::getContentHandler + */ + public function testGetContentHandler( $text, $title, $model, $format, $expectedClass ) { + $rev = $this->newTestRevision( $text, $title, $model, $format ); + + $this->assertEquals( $expectedClass, get_class( $rev->getContentHandler() ) ); + } + + public function provideGetContent() { + // NOTE: we expect the help namespace to always contain wikitext + return [ + [ 'hello world', 'Help:Hello', null, null, Revision::FOR_PUBLIC, 'hello world' ], + [ + serialize( 'hello world' ), + 'Hello', + DummyContentForTesting::MODEL_ID, + null, + Revision::FOR_PUBLIC, + serialize( 'hello world' ) + ], + [ + serialize( 'hello world' ), + 'Dummy:Hello', + null, + null, + Revision::FOR_PUBLIC, + serialize( 'hello world' ) + ], + ]; + } + + /** + * @dataProvider provideGetContent + * @covers Revision::getContent + */ + public function testGetContent( $text, $title, $model, $format, + $audience, $expectedSerialization + ) { + $rev = $this->newTestRevision( $text, $title, $model, $format ); + $content = $rev->getContent( $audience ); + + $this->assertEquals( + $expectedSerialization, + is_null( $content ) ? null : $content->serialize( $format ) + ); + } + + /** + * @covers Revision::getContent + */ + public function testGetContent_failure() { + $rev = new Revision( [ + 'page' => $this->the_page->getId(), + 'content_model' => $this->the_page->getContentModel(), + 'text_id' => 123456789, // not in the test DB + ] ); + + $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." ); + } + + public function provideGetSize() { + return [ + [ "hello world.", CONTENT_MODEL_WIKITEXT, 12 ], + [ serialize( "hello world." ), DummyContentForTesting::MODEL_ID, 12 ], + ]; + } + + /** + * @covers Revision::getSize + * @dataProvider provideGetSize + */ + public function testGetSize( $text, $model, $expected_size ) { + $rev = $this->newTestRevision( $text, 'RevisionTest_testGetSize', $model ); + $this->assertEquals( $expected_size, $rev->getSize() ); + } + + public function provideGetSha1() { + return [ + [ "hello world.", CONTENT_MODEL_WIKITEXT, Revision::base36Sha1( "hello world." ) ], + [ + serialize( "hello world." ), + DummyContentForTesting::MODEL_ID, + Revision::base36Sha1( serialize( "hello world." ) ) + ], + ]; + } + + /** + * @covers Revision::getSha1 + * @dataProvider provideGetSha1 + */ + public function testGetSha1( $text, $model, $expected_hash ) { + $rev = $this->newTestRevision( $text, 'RevisionTest_testGetSha1', $model ); + $this->assertEquals( $expected_hash, $rev->getSha1() ); + } + + /** + * Tests whether $rev->getContent() returns a clone when needed. + * + * @covers Revision::getContent + */ + public function testGetContentClone() { + $content = new RevisionTestModifyableContent( "foo" ); + + $rev = new Revision( + [ + 'id' => 42, + 'page' => 23, + 'title' => Title::newFromText( "testGetContentClone_dummy" ), + + 'content' => $content, + 'length' => $content->getSize(), + 'comment' => "testing", + 'minor_edit' => false, + ] + ); + + /** @var RevisionTestModifyableContent $content */ + $content = $rev->getContent( Revision::RAW ); + $content->setText( "bar" ); + + /** @var RevisionTestModifyableContent $content2 */ + $content2 = $rev->getContent( Revision::RAW ); + // content is mutable, expect clone + $this->assertNotSame( $content, $content2, "expected a clone" ); + // clone should contain the original text + $this->assertEquals( "foo", $content2->getText() ); + + $content2->setText( "bla bla" ); + // clones should be independent + $this->assertEquals( "bar", $content->getText() ); + } + + /** + * Tests whether $rev->getContent() returns the same object repeatedly if appropriate. + * @covers Revision::getContent + */ + public function testGetContentUncloned() { + $rev = $this->newTestRevision( "hello", "testGetContentUncloned_dummy", CONTENT_MODEL_WIKITEXT ); + $content = $rev->getContent( Revision::RAW ); + $content2 = $rev->getContent( Revision::RAW ); + + // for immutable content like wikitext, this should be the same object + $this->assertSame( $content, $content2 ); + } + } diff --git a/tests/phpunit/includes/RevisionTest.php b/tests/phpunit/includes/RevisionTest.php deleted file mode 100644 index ef4d127d85..0000000000 --- a/tests/phpunit/includes/RevisionTest.php +++ /dev/null @@ -1,478 +0,0 @@ -setMwGlobals( [ - 'wgContLang' => Language::factory( 'en' ), - 'wgLanguageCode' => 'en', - 'wgLegacyEncoding' => false, - 'wgCompressRevisions' => false, - - 'wgContentHandlerTextFallback' => 'ignore', - ] ); - - $this->mergeMwGlobalArrayValue( - 'wgExtraNamespaces', - [ - 12312 => 'Dummy', - 12313 => 'Dummy_talk', - ] - ); - - $this->mergeMwGlobalArrayValue( - 'wgNamespaceContentModels', - [ - 12312 => 'testing', - ] - ); - - $this->mergeMwGlobalArrayValue( - 'wgContentHandlers', - [ - 'testing' => 'DummyContentHandlerForTesting', - 'RevisionTestModifyableContent' => 'RevisionTestModifyableContentHandler', - ] - ); - - MWNamespace::clearCaches(); - // Reset namespace cache - $wgContLang->resetNamespaces(); - } - - protected function tearDown() { - global $wgContLang; - - MWNamespace::clearCaches(); - // Reset namespace cache - $wgContLang->resetNamespaces(); - - parent::tearDown(); - } - - public function provideConstructFromArray() { - yield 'with text' => [ - [ - 'text' => 'hello world.', - 'content_model' => CONTENT_MODEL_JAVASCRIPT - ], - ]; - yield 'with content' => [ - [ - 'content' => new JavaScriptContent( 'hellow world.' ) - ], - ]; - } - - /** - * @dataProvider provideConstructFromArray - */ - public function testConstructFromArray( $rowArray ) { - $rev = new Revision( $rowArray ); - $this->assertNotNull( $rev->getContent(), 'no content object available' ); - $this->assertEquals( CONTENT_MODEL_JAVASCRIPT, $rev->getContent()->getModel() ); - $this->assertEquals( CONTENT_MODEL_JAVASCRIPT, $rev->getContentModel() ); - } - - public function provideConstructFromArrayThrowsExceptions() { - yield 'content and text_id both not empty' => [ - [ - 'content' => new WikitextContent( 'GOAT' ), - 'text_id' => 'someid', - ], - new MWException( "Text already stored in external store (id someid), " . - "can't serialize content object" ) - ]; - yield 'with bad content object (class)' => [ - [ 'content' => new stdClass() ], - 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.' ) - ]; - yield 'bad row format' => [ - 'imastring, not a row', - new MWException( 'Revision constructor passed invalid row format.' ) - ]; - } - - /** - * @dataProvider provideConstructFromArrayThrowsExceptions - */ - public function testConstructFromArrayThrowsExceptions( $rowArray, Exception $expectedException ) { - $this->setExpectedException( - get_class( $expectedException ), - $expectedException->getMessage(), - $expectedException->getCode() - ); - new Revision( $rowArray ); - } - - public function provideGetRevisionText() { - yield 'Generic test' => [ - 'This is a goat of revision text.', - [ - 'old_flags' => '', - 'old_text' => 'This is a goat of revision text.', - ], - ]; - } - - /** - * @covers Revision::getRevisionText - * @dataProvider provideGetRevisionText - */ - public function testGetRevisionText( $expected, $rowData, $prefix = 'old_', $wiki = false ) { - $this->assertEquals( - $expected, - Revision::getRevisionText( (object)$rowData, $prefix, $wiki ) ); - } - - public function provideGetRevisionTextWithZlibExtension() { - yield 'Generic gzip test' => [ - 'This is a small goat of revision text.', - [ - 'old_flags' => 'gzip', - 'old_text' => gzdeflate( 'This is a small goat of revision text.' ), - ], - ]; - } - - /** - * @covers Revision::getRevisionText - * @dataProvider provideGetRevisionTextWithZlibExtension - */ - public function testGetRevisionWithZlibExtension( $expected, $rowData ) { - $this->checkPHPExtension( 'zlib' ); - $this->testGetRevisionText( $expected, $rowData ); - } - - public function provideGetRevisionTextWithLegacyEncoding() { - yield 'Utf8Native' => [ - "Wiki est l'\xc3\xa9cole superieur !", - 'iso-8859-1', - [ - 'old_flags' => 'utf-8', - 'old_text' => "Wiki est l'\xc3\xa9cole superieur !", - ] - ]; - yield 'Utf8Legacy' => [ - "Wiki est l'\xc3\xa9cole superieur !", - 'iso-8859-1', - [ - 'old_flags' => '', - 'old_text' => "Wiki est l'\xe9cole superieur !", - ] - ]; - } - - /** - * @covers Revision::getRevisionText - * @dataProvider provideGetRevisionTextWithLegacyEncoding - */ - public function testGetRevisionWithLegacyEncoding( $expected, $encoding, $rowData ) { - $GLOBALS['wgLegacyEncoding'] = $encoding; - $this->testGetRevisionText( $expected, $rowData ); - } - - public function provideGetRevisionTextWithGzipAndLegacyEncoding() { - yield 'Utf8NativeGzip' => [ - "Wiki est l'\xc3\xa9cole superieur !", - 'iso-8859-1', - [ - 'old_flags' => 'gzip,utf-8', - 'old_text' => gzdeflate( "Wiki est l'\xc3\xa9cole superieur !" ), - ] - ]; - yield 'Utf8LegacyGzip' => [ - "Wiki est l'\xc3\xa9cole superieur !", - 'iso-8859-1', - [ - 'old_flags' => 'gzip', - 'old_text' => gzdeflate( "Wiki est l'\xe9cole superieur !" ), - ] - ]; - } - - /** - * @covers Revision::getRevisionText - * @dataProvider provideGetRevisionTextWithGzipAndLegacyEncoding - */ - public function testGetRevisionWithGzipAndLegacyEncoding( $expected, $encoding, $rowData ) { - $this->checkPHPExtension( 'zlib' ); - $GLOBALS['wgLegacyEncoding'] = $encoding; - $this->testGetRevisionText( $expected, $rowData ); - } - - /** - * @covers Revision::compressRevisionText - */ - public function testCompressRevisionTextUtf8() { - $row = new stdClass; - $row->old_text = "Wiki est l'\xc3\xa9cole superieur !"; - $row->old_flags = Revision::compressRevisionText( $row->old_text ); - $this->assertTrue( false !== strpos( $row->old_flags, 'utf-8' ), - "Flags should contain 'utf-8'" ); - $this->assertFalse( false !== strpos( $row->old_flags, 'gzip' ), - "Flags should not contain 'gzip'" ); - $this->assertEquals( "Wiki est l'\xc3\xa9cole superieur !", - $row->old_text, "Direct check" ); - $this->assertEquals( "Wiki est l'\xc3\xa9cole superieur !", - Revision::getRevisionText( $row ), "getRevisionText" ); - } - - /** - * @covers Revision::compressRevisionText - */ - public function testCompressRevisionTextUtf8Gzip() { - $this->checkPHPExtension( 'zlib' ); - $this->setMwGlobals( 'wgCompressRevisions', true ); - - $row = new stdClass; - $row->old_text = "Wiki est l'\xc3\xa9cole superieur !"; - $row->old_flags = Revision::compressRevisionText( $row->old_text ); - $this->assertTrue( false !== strpos( $row->old_flags, 'utf-8' ), - "Flags should contain 'utf-8'" ); - $this->assertTrue( false !== strpos( $row->old_flags, 'gzip' ), - "Flags should contain 'gzip'" ); - $this->assertEquals( "Wiki est l'\xc3\xa9cole superieur !", - gzinflate( $row->old_text ), "Direct check" ); - $this->assertEquals( "Wiki est l'\xc3\xa9cole superieur !", - Revision::getRevisionText( $row ), "getRevisionText" ); - } - - /** - * @param string $text - * @param string $title - * @param string $model - * @param string $format - * - * @return Revision - */ - private function newTestRevision( $text, $title = "Test", - $model = CONTENT_MODEL_WIKITEXT, $format = null - ) { - if ( is_string( $title ) ) { - $title = Title::newFromText( $title ); - } - - $content = ContentHandler::makeContent( $text, $title, $model, $format ); - - $rev = new Revision( - [ - 'id' => 42, - 'page' => 23, - 'title' => $title, - - 'content' => $content, - 'length' => $content->getSize(), - 'comment' => "testing", - 'minor_edit' => false, - - 'content_format' => $format, - ] - ); - - return $rev; - } - - public function provideGetContentModel() { - // NOTE: we expect the help namespace to always contain wikitext - return [ - [ 'hello world', 'Help:Hello', null, null, CONTENT_MODEL_WIKITEXT ], - [ 'hello world', 'User:hello/there.css', null, null, CONTENT_MODEL_CSS ], - [ serialize( 'hello world' ), 'Dummy:Hello', null, null, "testing" ], - ]; - } - - /** - * @group Database - * @dataProvider provideGetContentModel - * @covers Revision::getContentModel - */ - public function testGetContentModel( $text, $title, $model, $format, $expectedModel ) { - $rev = $this->newTestRevision( $text, $title, $model, $format ); - - $this->assertEquals( $expectedModel, $rev->getContentModel() ); - } - - public function provideGetContentFormat() { - // NOTE: we expect the help namespace to always contain wikitext - return [ - [ 'hello world', 'Help:Hello', null, null, CONTENT_FORMAT_WIKITEXT ], - [ 'hello world', 'Help:Hello', CONTENT_MODEL_CSS, null, CONTENT_FORMAT_CSS ], - [ 'hello world', 'User:hello/there.css', null, null, CONTENT_FORMAT_CSS ], - [ serialize( 'hello world' ), 'Dummy:Hello', null, null, "testing" ], - ]; - } - - /** - * @group Database - * @dataProvider provideGetContentFormat - * @covers Revision::getContentFormat - */ - public function testGetContentFormat( $text, $title, $model, $format, $expectedFormat ) { - $rev = $this->newTestRevision( $text, $title, $model, $format ); - - $this->assertEquals( $expectedFormat, $rev->getContentFormat() ); - } - - public function provideGetContentHandler() { - // NOTE: we expect the help namespace to always contain wikitext - return [ - [ 'hello world', 'Help:Hello', null, null, 'WikitextContentHandler' ], - [ 'hello world', 'User:hello/there.css', null, null, 'CssContentHandler' ], - [ serialize( 'hello world' ), 'Dummy:Hello', null, null, 'DummyContentHandlerForTesting' ], - ]; - } - - /** - * @group Database - * @dataProvider provideGetContentHandler - * @covers Revision::getContentHandler - */ - public function testGetContentHandler( $text, $title, $model, $format, $expectedClass ) { - $rev = $this->newTestRevision( $text, $title, $model, $format ); - - $this->assertEquals( $expectedClass, get_class( $rev->getContentHandler() ) ); - } - - public function provideGetContent() { - // NOTE: we expect the help namespace to always contain wikitext - return [ - [ 'hello world', 'Help:Hello', null, null, Revision::FOR_PUBLIC, 'hello world' ], - [ - serialize( 'hello world' ), - 'Hello', - "testing", - null, - Revision::FOR_PUBLIC, - serialize( 'hello world' ) - ], - [ - serialize( 'hello world' ), - 'Dummy:Hello', - null, - null, - Revision::FOR_PUBLIC, - serialize( 'hello world' ) - ], - ]; - } - - /** - * @group Database - * @dataProvider provideGetContent - * @covers Revision::getContent - */ - public function testGetContent( $text, $title, $model, $format, - $audience, $expectedSerialization - ) { - $rev = $this->newTestRevision( $text, $title, $model, $format ); - $content = $rev->getContent( $audience ); - - $this->assertEquals( - $expectedSerialization, - is_null( $content ) ? null : $content->serialize( $format ) - ); - } - - public function provideGetSize() { - return [ - [ "hello world.", CONTENT_MODEL_WIKITEXT, 12 ], - [ serialize( "hello world." ), "testing", 12 ], - ]; - } - - /** - * @covers Revision::getSize - * @group Database - * @dataProvider provideGetSize - */ - public function testGetSize( $text, $model, $expected_size ) { - $rev = $this->newTestRevision( $text, 'RevisionTest_testGetSize', $model ); - $this->assertEquals( $expected_size, $rev->getSize() ); - } - - public function provideGetSha1() { - return [ - [ "hello world.", CONTENT_MODEL_WIKITEXT, Revision::base36Sha1( "hello world." ) ], - [ - serialize( "hello world." ), - "testing", - Revision::base36Sha1( serialize( "hello world." ) ) - ], - ]; - } - - /** - * @covers Revision::getSha1 - * @group Database - * @dataProvider provideGetSha1 - */ - public function testGetSha1( $text, $model, $expected_hash ) { - $rev = $this->newTestRevision( $text, 'RevisionTest_testGetSha1', $model ); - $this->assertEquals( $expected_hash, $rev->getSha1() ); - } - - /** - * Tests whether $rev->getContent() returns a clone when needed. - * - * @group Database - * @covers Revision::getContent - */ - public function testGetContentClone() { - $content = new RevisionTestModifyableContent( "foo" ); - - $rev = new Revision( - [ - 'id' => 42, - 'page' => 23, - 'title' => Title::newFromText( "testGetContentClone_dummy" ), - - 'content' => $content, - 'length' => $content->getSize(), - 'comment' => "testing", - 'minor_edit' => false, - ] - ); - - /** @var RevisionTestModifyableContent $content */ - $content = $rev->getContent( Revision::RAW ); - $content->setText( "bar" ); - - /** @var RevisionTestModifyableContent $content2 */ - $content2 = $rev->getContent( Revision::RAW ); - // content is mutable, expect clone - $this->assertNotSame( $content, $content2, "expected a clone" ); - // clone should contain the original text - $this->assertEquals( "foo", $content2->getText() ); - - $content2->setText( "bla bla" ); - // clones should be independent - $this->assertEquals( "bar", $content->getText() ); - } - - /** - * Tests whether $rev->getContent() returns the same object repeatedly if appropriate. - * - * @group Database - * @covers Revision::getContent - */ - public function testGetContentUncloned() { - $rev = $this->newTestRevision( "hello", "testGetContentUncloned_dummy", CONTENT_MODEL_WIKITEXT ); - $content = $rev->getContent( Revision::RAW ); - $content2 = $rev->getContent( Revision::RAW ); - - // for immutable content like wikitext, this should be the same object - $this->assertSame( $content, $content2 ); - } -} diff --git a/tests/phpunit/includes/RevisionTestModifyableContent.php b/tests/phpunit/includes/RevisionTestModifyableContent.php index 11e4e183f3..6dcba53c6e 100644 --- a/tests/phpunit/includes/RevisionTestModifyableContent.php +++ b/tests/phpunit/includes/RevisionTestModifyableContent.php @@ -2,8 +2,10 @@ class RevisionTestModifyableContent extends TextContent { + const MODEL_ID = "RevisionTestModifyableContent"; + public function __construct( $text ) { - parent::__construct( $text, "RevisionTestModifyableContent" ); + parent::__construct( $text, self::MODEL_ID ); } public function copy() { diff --git a/tests/phpunit/includes/RevisionTestModifyableContentHandler.php b/tests/phpunit/includes/RevisionTestModifyableContentHandler.php index e262b40d05..bc4e40a417 100644 --- a/tests/phpunit/includes/RevisionTestModifyableContentHandler.php +++ b/tests/phpunit/includes/RevisionTestModifyableContentHandler.php @@ -3,7 +3,7 @@ class RevisionTestModifyableContentHandler extends TextContentHandler { public function __construct() { - parent::__construct( "RevisionTestModifyableContent", [ CONTENT_FORMAT_TEXT ] ); + parent::__construct( RevisionTestModifyableContent::MODEL_ID, [ CONTENT_FORMAT_TEXT ] ); } public function unserializeContent( $text, $format = null ) { diff --git a/tests/phpunit/includes/RevisionUnitTest.php b/tests/phpunit/includes/RevisionUnitTest.php new file mode 100644 index 0000000000..232b0868fc --- /dev/null +++ b/tests/phpunit/includes/RevisionUnitTest.php @@ -0,0 +1,205 @@ + [ + [ + 'text' => 'hello world.', + 'content_model' => CONTENT_MODEL_JAVASCRIPT + ], + ]; + yield 'with content' => [ + [ + 'content' => new JavaScriptContent( 'hellow world.' ) + ], + ]; + } + + /** + * @dataProvider provideConstructFromArray + */ + public function testConstructFromArray( $rowArray ) { + $rev = new Revision( $rowArray ); + $this->assertNotNull( $rev->getContent(), 'no content object available' ); + $this->assertEquals( CONTENT_MODEL_JAVASCRIPT, $rev->getContent()->getModel() ); + $this->assertEquals( CONTENT_MODEL_JAVASCRIPT, $rev->getContentModel() ); + } + + public function provideConstructFromArrayThrowsExceptions() { + yield 'content and text_id both not empty' => [ + [ + 'content' => new WikitextContent( 'GOAT' ), + 'text_id' => 'someid', + ], + new MWException( "Text already stored in external store (id someid), " . + "can't serialize content object" ) + ]; + yield 'with bad content object (class)' => [ + [ 'content' => new stdClass() ], + 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.' ) + ]; + yield 'bad row format' => [ + 'imastring, not a row', + new MWException( 'Revision constructor passed invalid row format.' ) + ]; + } + + /** + * @dataProvider provideConstructFromArrayThrowsExceptions + */ + public function testConstructFromArrayThrowsExceptions( $rowArray, Exception $expectedException ) { + $this->setExpectedException( + get_class( $expectedException ), + $expectedException->getMessage(), + $expectedException->getCode() + ); + new Revision( $rowArray ); + } + + public function provideGetRevisionText() { + yield 'Generic test' => [ + 'This is a goat of revision text.', + [ + 'old_flags' => '', + 'old_text' => 'This is a goat of revision text.', + ], + ]; + } + + /** + * @covers Revision::getRevisionText + * @dataProvider provideGetRevisionText + */ + public function testGetRevisionText( $expected, $rowData, $prefix = 'old_', $wiki = false ) { + $this->assertEquals( + $expected, + Revision::getRevisionText( (object)$rowData, $prefix, $wiki ) ); + } + + public function provideGetRevisionTextWithZlibExtension() { + yield 'Generic gzip test' => [ + 'This is a small goat of revision text.', + [ + 'old_flags' => 'gzip', + 'old_text' => gzdeflate( 'This is a small goat of revision text.' ), + ], + ]; + } + + /** + * @covers Revision::getRevisionText + * @dataProvider provideGetRevisionTextWithZlibExtension + */ + public function testGetRevisionWithZlibExtension( $expected, $rowData ) { + $this->checkPHPExtension( 'zlib' ); + $this->testGetRevisionText( $expected, $rowData ); + } + + public function provideGetRevisionTextWithLegacyEncoding() { + yield 'Utf8Native' => [ + "Wiki est l'\xc3\xa9cole superieur !", + 'iso-8859-1', + [ + 'old_flags' => 'utf-8', + 'old_text' => "Wiki est l'\xc3\xa9cole superieur !", + ] + ]; + yield 'Utf8Legacy' => [ + "Wiki est l'\xc3\xa9cole superieur !", + 'iso-8859-1', + [ + 'old_flags' => '', + 'old_text' => "Wiki est l'\xe9cole superieur !", + ] + ]; + } + + /** + * @covers Revision::getRevisionText + * @dataProvider provideGetRevisionTextWithLegacyEncoding + */ + public function testGetRevisionWithLegacyEncoding( $expected, $encoding, $rowData ) { + $this->setMwGlobals( 'wgLegacyEncoding', $encoding ); + $this->testGetRevisionText( $expected, $rowData ); + } + + public function provideGetRevisionTextWithGzipAndLegacyEncoding() { + /** + * WARNING! + * Do not set the external flag! + * Otherwise, getRevisionText will hit the live database (if ExternalStore is enabled)! + */ + yield 'Utf8NativeGzip' => [ + "Wiki est l'\xc3\xa9cole superieur !", + 'iso-8859-1', + [ + 'old_flags' => 'gzip,utf-8', + 'old_text' => gzdeflate( "Wiki est l'\xc3\xa9cole superieur !" ), + ] + ]; + yield 'Utf8LegacyGzip' => [ + "Wiki est l'\xc3\xa9cole superieur !", + 'iso-8859-1', + [ + 'old_flags' => 'gzip', + 'old_text' => gzdeflate( "Wiki est l'\xe9cole superieur !" ), + ] + ]; + } + + /** + * @covers Revision::getRevisionText + * @dataProvider provideGetRevisionTextWithGzipAndLegacyEncoding + */ + public function testGetRevisionWithGzipAndLegacyEncoding( $expected, $encoding, $rowData ) { + $this->checkPHPExtension( 'zlib' ); + $this->setMwGlobals( 'wgLegacyEncoding', $encoding ); + $this->testGetRevisionText( $expected, $rowData ); + } + + /** + * @covers Revision::compressRevisionText + */ + public function testCompressRevisionTextUtf8() { + $row = new stdClass; + $row->old_text = "Wiki est l'\xc3\xa9cole superieur !"; + $row->old_flags = Revision::compressRevisionText( $row->old_text ); + $this->assertTrue( false !== strpos( $row->old_flags, 'utf-8' ), + "Flags should contain 'utf-8'" ); + $this->assertFalse( false !== strpos( $row->old_flags, 'gzip' ), + "Flags should not contain 'gzip'" ); + $this->assertEquals( "Wiki est l'\xc3\xa9cole superieur !", + $row->old_text, "Direct check" ); + $this->assertEquals( "Wiki est l'\xc3\xa9cole superieur !", + Revision::getRevisionText( $row ), "getRevisionText" ); + } + + /** + * @covers Revision::compressRevisionText + */ + public function testCompressRevisionTextUtf8Gzip() { + $this->checkPHPExtension( 'zlib' ); + $this->setMwGlobals( 'wgCompressRevisions', true ); + + $row = new stdClass; + $row->old_text = "Wiki est l'\xc3\xa9cole superieur !"; + $row->old_flags = Revision::compressRevisionText( $row->old_text ); + $this->assertTrue( false !== strpos( $row->old_flags, 'utf-8' ), + "Flags should contain 'utf-8'" ); + $this->assertTrue( false !== strpos( $row->old_flags, 'gzip' ), + "Flags should contain 'gzip'" ); + $this->assertEquals( "Wiki est l'\xc3\xa9cole superieur !", + gzinflate( $row->old_text ), "Direct check" ); + $this->assertEquals( "Wiki est l'\xc3\xa9cole superieur !", + Revision::getRevisionText( $row ), "getRevisionText" ); + } + +} diff --git a/tests/phpunit/mocks/content/DummyContentForTesting.php b/tests/phpunit/mocks/content/DummyContentForTesting.php index cdb3f78ab8..4392964982 100644 --- a/tests/phpunit/mocks/content/DummyContentForTesting.php +++ b/tests/phpunit/mocks/content/DummyContentForTesting.php @@ -2,8 +2,10 @@ class DummyContentForTesting extends AbstractContent { + const MODEL_ID = "testing"; + public function __construct( $data ) { - parent::__construct( "testing" ); + parent::__construct( self::MODEL_ID ); $this->data = $data; } diff --git a/tests/phpunit/mocks/content/DummyContentHandlerForTesting.php b/tests/phpunit/mocks/content/DummyContentHandlerForTesting.php index 6b9b782397..78d5dc7b28 100644 --- a/tests/phpunit/mocks/content/DummyContentHandlerForTesting.php +++ b/tests/phpunit/mocks/content/DummyContentHandlerForTesting.php @@ -3,7 +3,7 @@ class DummyContentHandlerForTesting extends ContentHandler { public function __construct( $dataModel ) { - parent::__construct( $dataModel, [ "testing" ] ); + parent::__construct( $dataModel, [ DummyContentForTesting::MODEL_ID ] ); } /** -- 2.20.1