Merge "OOUI: Bring forward UBN fix for DropdownInputWidget with MenuSectionOptionWidget"
authorjenkins-bot <jenkins-bot@gerrit.wikimedia.org>
Wed, 20 Mar 2019 23:30:55 +0000 (23:30 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Wed, 20 Mar 2019 23:30:55 +0000 (23:30 +0000)
docs/export-0.10.xsd
tests/common/TestsAutoLoader.php
tests/phpunit/PHPUnit4And6Compat.php
tests/phpunit/maintenance/DumpAsserter.php [new file with mode: 0644]
tests/phpunit/maintenance/DumpTestCase.php
tests/phpunit/maintenance/backupTextPassTest.php
tests/phpunit/maintenance/backup_LogTest.php
tests/phpunit/maintenance/backup_PageTest.php
tests/phpunit/maintenance/xml.xsd [new file with mode: 0644]

index 9d5d49e..6291bfc 100644 (file)
                                <!-- This isn't a good idea; we should be using "ID" instead of "NMTOKEN" -->
                                <!-- However, "NMTOKEN" is strictest definition that is both compatible with existing -->
                                <!-- usage ([0-9]+) and with the "ID" type. -->
-                               <attribute name="id" type="NMTOKEN" />
+                               <attribute name="id" use="optional" type="NMTOKEN" />
                                <attribute name="bytes" use="optional" type="nonNegativeInteger" />
                        </extension>
                </simpleContent>
index 680b8c3..861111a 100644 (file)
@@ -180,6 +180,7 @@ $wgAutoloadClasses += [
        'GenericArrayObjectTest' => "$testDir/phpunit/includes/libs/GenericArrayObjectTest.php",
 
        # tests/phpunit/maintenance
+       'MediaWiki\Tests\Maintenance\DumpAsserter' => "$testDir/phpunit/maintenance/DumpAsserter.php",
        'MediaWiki\Tests\Maintenance\DumpTestCase' => "$testDir/phpunit/maintenance/DumpTestCase.php",
        'MediaWiki\Tests\Maintenance\MaintenanceBaseTestCase' => "$testDir/phpunit/maintenance/MaintenanceBaseTestCase.php",
 
index 79ce634..1ef0c91 100644 (file)
@@ -130,4 +130,18 @@ trait PHPUnit4And6Compat {
                        // ->disallowMockingUnknownTypes()
                        ->getMock();
        }
+
+       /**
+        * Marks the current test as risky. This
+        * is a forward port of the markAsRisky function that
+        * was introduced in PHPUnit 5.7.6.
+        */
+       public function markAsRisky() {
+               if ( is_callable( 'parent::markAsRisky' ) ) {
+                       return parent::markAsRisky();
+               }
+
+               // "risky" tests are not supported in phpunit 4, so just ignore
+       }
+
 }
diff --git a/tests/phpunit/maintenance/DumpAsserter.php b/tests/phpunit/maintenance/DumpAsserter.php
new file mode 100644 (file)
index 0000000..5b4c6ef
--- /dev/null
@@ -0,0 +1,347 @@
+<?php
+
+namespace MediaWiki\Tests\Maintenance;
+
+use PHPUnit\Framework\Assert;
+use XMLReader;
+
+/**
+ * Helper for asserting the structure of an XML dump stream.
+ */
+class DumpAsserter {
+
+       /**
+        * Holds the XMLReader used for analyzing an XML dump
+        *
+        * @var XMLReader|null
+        */
+       protected $xml = null;
+
+       /**
+        * XML dump schema version
+        *
+        * @var string
+        */
+       protected $schemaVersion;
+
+       /**
+        * DumpAsserts constructor.
+        *
+        * @param string $schemaVersion see XML_DUMP_SCHEMA_VERSION_XX
+        */
+       public function __construct( $schemaVersion ) {
+               $this->schemaVersion = $schemaVersion;
+       }
+
+       /**
+        * Step the current XML reader until node end of given name is found.
+        *
+        * @param string $name Name of the closing element to look for
+        *   (e.g.: "mediawiki" when looking for </mediawiki>)
+        *
+        * @return bool True if the end node could be found. false otherwise.
+        */
+       public function skipToNodeEnd( $name ) {
+               while ( $this->xml->read() ) {
+                       if ( $this->xml->nodeType == XMLReader::END_ELEMENT &&
+                               $this->xml->name == $name
+                       ) {
+                               return true;
+                       }
+               }
+
+               return false;
+       }
+
+       /**
+        * Step the current XML reader to the first element start after the node
+        * end of a given name.
+        *
+        * @param string $name Name of the closing element to look for
+        *   (e.g.: "mediawiki" when looking for </mediawiki>)
+        *
+        * @return bool True if new element after the closing of $name could be
+        *   found. false otherwise.
+        */
+       public function skipPastNodeEnd( $name ) {
+               Assert::assertTrue( $this->skipToNodeEnd( $name ),
+                       "Skipping to end of $name" );
+               while ( $this->xml->read() ) {
+                       if ( $this->xml->nodeType == XMLReader::ELEMENT ) {
+                               return true;
+                       }
+               }
+
+               return false;
+       }
+
+       /**
+        * Opens an XML file to analyze and optionally skips past siteinfo.
+        *
+        * @param string $fname Name of file to analyze
+        * @param bool $skip_siteinfo (optional) If true, step the xml reader
+        *   to the first element after </siteinfo>
+        */
+       public function assertDumpStart( $fname, $skip_siteinfo = true ) {
+               $this->xml = new XMLReader();
+
+               Assert::assertTrue( $this->xml->open( $fname ),
+                       "Opening temporary file $fname via XMLReader failed" );
+               if ( $skip_siteinfo ) {
+                       Assert::assertTrue( $this->skipPastNodeEnd( "siteinfo" ),
+                               "Skipping past end of siteinfo" );
+               }
+       }
+
+       /**
+        * Asserts that the xml reader is at the final closing tag of an xml file and
+        * closes the reader.
+        *
+        * @param string $name (optional) the name of the final tag
+        *   (e.g.: "mediawiki" for </mediawiki>)
+        */
+       public function assertDumpEnd( $name = "mediawiki" ) {
+               $this->assertNodeEnd( $name, false );
+               if ( $this->xml->read() ) {
+                       $this->skipWhitespace();
+               }
+               Assert::assertEquals( $this->xml->nodeType, XMLReader::NONE,
+                       "No proper entity left to parse" );
+               $this->xml->close();
+       }
+
+       /**
+        * Steps the xml reader over white space
+        */
+       public function skipWhitespace() {
+               $cont = true;
+               while ( $cont && ( ( $this->xml->nodeType == XMLReader::WHITESPACE )
+                       || ( $this->xml->nodeType == XMLReader::SIGNIFICANT_WHITESPACE ) ) ) {
+                       $cont = $this->xml->read();
+               }
+       }
+
+       /**
+        * Asserts that the xml reader is at an element of given name, and optionally
+        * skips past it.
+        *
+        * @param string $name The name of the element to check for
+        *   (e.g.: "mediawiki" for <mediawiki>)
+        * @param bool $skip (optional) if true, skip past the found element
+        */
+       public function assertNodeStart( $name, $skip = true ) {
+               Assert::assertEquals( $name, $this->xml->name, "Node name" );
+               Assert::assertEquals( XMLReader::ELEMENT, $this->xml->nodeType, "Node type" );
+               if ( $skip ) {
+                       Assert::assertTrue( $this->xml->read(), "Skipping past start tag" );
+               }
+       }
+
+       /**
+        * Asserts that the xml reader is at an closing element of given name, and optionally
+        * skips past it.
+        *
+        * @param string $name The name of the closing element to check for
+        *   (e.g.: "mediawiki" for </mediawiki>)
+        * @param bool $skip (optional) if true, skip past the found element
+        */
+       public function assertNodeEnd( $name, $skip = true ) {
+               Assert::assertEquals( $name, $this->xml->name, "Node name" );
+               Assert::assertEquals( XMLReader::END_ELEMENT, $this->xml->nodeType, "Node type" );
+               if ( $skip ) {
+                       Assert::assertTrue( $this->xml->read(), "Skipping past end tag" );
+               }
+       }
+
+       /**
+        * Asserts that the xml reader is at an element of given tag that contains a given text,
+        * and skips over the element.
+        *
+        * @param string $name The name of the element to check for
+        *   (e.g.: "mediawiki" for <mediawiki>...</mediawiki>)
+        * @param string|bool $text If string, check if it equals the elements text.
+        *   If false, ignore the element's text
+        * @param bool $skip_ws (optional) if true, skip past white spaces that trail the
+        *   closing element.
+        */
+       public function assertTextNode( $name, $text, $skip_ws = true ) {
+               $this->assertNodeStart( $name );
+
+               if ( $text !== false ) {
+                       Assert::assertEquals( $text, $this->xml->value, "Text of node " . $name );
+               }
+               Assert::assertTrue( $this->xml->read(), "Skipping past processed text of " . $name );
+               $this->assertNodeEnd( $name );
+
+               if ( $skip_ws ) {
+                       $this->skipWhitespace();
+               }
+       }
+
+       /**
+        * Asserts that the xml reader is at the start of a page element and skips over the first
+        * tags, after checking them.
+        *
+        * Besides the opening page element, this function also checks for and skips over the
+        * title, ns, and id tags. Hence after this function, the xml reader is at the first
+        * revision of the current page.
+        *
+        * @param int $id Id of the page to assert
+        * @param int $ns Number of namespage to assert
+        * @param string $name Title of the current page
+        */
+       public function assertPageStart( $id, $ns, $name ) {
+               $this->assertNodeStart( "page" );
+               $this->skipWhitespace();
+
+               $this->assertTextNode( "title", $name );
+               $this->assertTextNode( "ns", $ns );
+               $this->assertTextNode( "id", $id );
+       }
+
+       /**
+        * Asserts that the xml reader is at the page's closing element and skips to the next
+        * element.
+        */
+       public function assertPageEnd() {
+               $this->assertNodeEnd( "page" );
+               $this->skipWhitespace();
+       }
+
+       /**
+        * Asserts that the xml reader is at a revision and checks its representation before
+        * skipping over it.
+        *
+        * @param int $id Id of the revision
+        * @param string $summary Summary of the revision
+        * @param int $text_id Id of the revision's text
+        * @param int $text_bytes Number of bytes in the revision's text
+        * @param string $text_sha1 The base36 SHA-1 of the revision's text
+        * @param string|bool $text (optional) The revision's string, or false to check for a
+        *            revision stub
+        * @param int|bool $parentid (optional) id of the parent revision
+        * @param string $model The expected content model id (default: CONTENT_MODEL_WIKITEXT)
+        * @param string $format The expected format model id (default: CONTENT_FORMAT_WIKITEXT)
+        */
+       public function assertRevision( $id, $summary, $text_id, $text_bytes,
+               $text_sha1, $text = false, $parentid = false,
+               $model = CONTENT_MODEL_WIKITEXT, $format = CONTENT_FORMAT_WIKITEXT
+       ) {
+               $this->assertNodeStart( "revision" );
+               $this->skipWhitespace();
+
+               $this->assertTextNode( "id", $id );
+               if ( $parentid !== false ) {
+                       $this->assertTextNode( "parentid", $parentid );
+               }
+               $this->assertTextNode( "timestamp", false );
+
+               $this->assertNodeStart( "contributor" );
+               $this->skipWhitespace();
+               $this->assertTextNode( "ip", false );
+               $this->assertNodeEnd( "contributor" );
+               $this->skipWhitespace();
+
+               $this->assertTextNode( "comment", $summary );
+               $this->skipWhitespace();
+
+               $this->assertTextNode( "model", $model );
+               $this->skipWhitespace();
+
+               $this->assertTextNode( "format", $format );
+               $this->skipWhitespace();
+
+               if ( $this->xml->name == "text" ) {
+                       // note: <text> tag may occur here or at the very end.
+                       $text_found = true;
+                       $this->assertText( $id, $text_id, $text_bytes, $text );
+               } else {
+                       $text_found = false;
+               }
+
+               $this->assertTextNode( "sha1", $text_sha1 );
+
+               if ( !$text_found ) {
+                       $this->assertText( $id, $text_id, $text_bytes, $text );
+               }
+
+               $this->assertNodeEnd( "revision" );
+               $this->skipWhitespace();
+       }
+
+       public function assertText( $id, $text_id, $text_bytes, $text ) {
+               $this->assertNodeStart( "text", false );
+               if ( $text_bytes !== false ) {
+                       Assert::assertEquals( $this->xml->getAttribute( "bytes" ), $text_bytes,
+                               "Attribute 'bytes' of revision " . $id );
+               }
+
+               if ( $text === false ) {
+                       // Testing for a stub
+                       Assert::assertEquals( $this->xml->getAttribute( "id" ), $text_id,
+                               "Text id of revision " . $id );
+                       Assert::assertFalse( $this->xml->hasValue, "Revision has text" );
+                       Assert::assertTrue( $this->xml->read(), "Skipping text start tag" );
+                       if ( ( $this->xml->nodeType == XMLReader::END_ELEMENT )
+                               && ( $this->xml->name == "text" )
+                       ) {
+                               $this->xml->read();
+                       }
+                       $this->skipWhitespace();
+               } else {
+                       // Testing for a real dump
+                       Assert::assertTrue( $this->xml->read(), "Skipping text start tag" );
+                       Assert::assertEquals( $text, $this->xml->value, "Text of revision " . $id );
+                       Assert::assertTrue( $this->xml->read(), "Skipping past text" );
+                       $this->assertNodeEnd( "text" );
+                       $this->skipWhitespace();
+               }
+       }
+
+       /**
+        * asserts that the xml reader is at the beginning of a log entry and skips over
+        * it while analyzing it.
+        *
+        * @param int $id Id of the log entry
+        * @param string $user_name User name of the log entry's performer
+        * @param int $user_id User id of the log entry 's performer
+        * @param string|null $comment Comment of the log entry. If null, the comment text is ignored.
+        * @param string $type Type of the log entry
+        * @param string $subtype Subtype of the log entry
+        * @param string $title Title of the log entry's target
+        * @param array $parameters (optional) unserialized data accompanying the log entry
+        */
+       public function assertLogItem( $id, $user_name, $user_id, $comment, $type,
+               $subtype, $title, $parameters = []
+       ) {
+               $this->assertNodeStart( "logitem" );
+               $this->skipWhitespace();
+
+               $this->assertTextNode( "id", $id );
+               $this->assertTextNode( "timestamp", false );
+
+               $this->assertNodeStart( "contributor" );
+               $this->skipWhitespace();
+               $this->assertTextNode( "username", $user_name );
+               $this->assertTextNode( "id", $user_id );
+               $this->assertNodeEnd( "contributor" );
+               $this->skipWhitespace();
+
+               if ( $comment !== null ) {
+                       $this->assertTextNode( "comment", $comment );
+               }
+               $this->assertTextNode( "type", $type );
+               $this->assertTextNode( "action", $subtype );
+               $this->assertTextNode( "logtitle", $title );
+
+               $this->assertNodeStart( "params" );
+               $parameters_xml = unserialize( $this->xml->value );
+               Assert::assertEquals( $parameters, $parameters_xml );
+               Assert::assertTrue( $this->xml->read(), "Skipping past processed text of params" );
+               $this->assertNodeEnd( "params" );
+               $this->skipWhitespace();
+
+               $this->assertNodeEnd( "logitem" );
+               $this->skipWhitespace();
+       }
+}
index 4b7a7eb..eebc201 100644 (file)
@@ -3,11 +3,12 @@
 namespace MediaWiki\Tests\Maintenance;
 
 use ContentHandler;
+use DOMDocument;
 use ExecutableFinder;
 use MediaWikiLangTestCase;
-use Page;
 use User;
-use XMLReader;
+use WikiExporter;
+use WikiPage;
 use MWException;
 
 /**
@@ -28,13 +29,6 @@ abstract class DumpTestCase extends MediaWikiLangTestCase {
         */
        protected $exceptionFromAddDBData = null;
 
-       /**
-        * Holds the XMLReader used for analyzing an XML dump
-        *
-        * @var XMLReader|null
-        */
-       protected $xml = null;
-
        /** @var bool|null Whether the 'gzip' utility is available */
        protected static $hasGzip = null;
 
@@ -58,7 +52,7 @@ abstract class DumpTestCase extends MediaWikiLangTestCase {
        /**
         * Adds a revision to a page, while returning the resuting revision's id
         *
-        * @param Page $page Page to add the revision to
+        * @param WikiPage $page Page to add the revision to
         * @param string $text Revisions text
         * @param string $summary Revisions summary
         * @param string $model The model ID (defaults to wikitext)
@@ -66,7 +60,12 @@ abstract class DumpTestCase extends MediaWikiLangTestCase {
         * @throws MWException
         * @return array
         */
-       protected function addRevision( Page $page, $text, $summary, $model = CONTENT_MODEL_WIKITEXT ) {
+       protected function addRevision(
+               WikiPage $page,
+               $text,
+               $summary,
+               $model = CONTENT_MODEL_WIKITEXT
+       ) {
                $status = $page->doEditContent(
                        ContentHandler::makeContent( $text, $page->getTitle(), $model ),
                        $summary
@@ -108,6 +107,36 @@ abstract class DumpTestCase extends MediaWikiLangTestCase {
                );
        }
 
+       public static function setUpBeforeClass() {
+               parent::setUpBeforeClass();
+
+               if ( !function_exists( 'libxml_set_external_entity_loader' ) ) {
+                       return;
+               }
+
+               // The W3C is intentionally slow about returning schema files,
+               // see <https://www.w3.org/Help/Webmaster#slowdtd>.
+               // To work around that, we keep our own copies of the relevant schema files.
+               libxml_set_external_entity_loader(
+                       function ( $public, $system, $context ) {
+                               switch ( $system ) {
+                                       // if more schema files are needed, add them here.
+                                       case 'http://www.w3.org/2001/xml.xsd':
+                                               $file = __DIR__ . '/xml.xsd';
+                                               break;
+                                       default:
+                                               if ( is_file( $system ) ) {
+                                                       $file = $system;
+                                               } else {
+                                                       return null;
+                                               }
+                               }
+
+                               return $file;
+                       }
+               );
+       }
+
        /**
         * Default set up function.
         *
@@ -125,6 +154,21 @@ abstract class DumpTestCase extends MediaWikiLangTestCase {
                $this->setMwGlobals( 'wgUser', new User() );
        }
 
+       /**
+        * Returns the path to the XML schema file for the given schema version.
+        *
+        * @param string|null $schemaVersion
+        *
+        * @return string
+        */
+       protected function getXmlSchemaPath( $schemaVersion = null ) {
+               global $IP;
+
+               $schemaVersion = $schemaVersion ?: '0.10';
+
+               return "$IP/docs/export-$schemaVersion.xsd";
+       }
+
        /**
         * Checks for test output consisting only of lines containing ETA announcements
         */
@@ -152,266 +196,62 @@ abstract class DumpTestCase extends MediaWikiLangTestCase {
        }
 
        /**
-        * Step the current XML reader until node end of given name is found.
-        *
-        * @param string $name Name of the closing element to look for
-        *   (e.g.: "mediawiki" when looking for </mediawiki>)
+        * @param null|string $schemaVersion
         *
-        * @return bool True if the end node could be found. false otherwise.
+        * @return DumpAsserter
         */
-       protected function skipToNodeEnd( $name ) {
-               while ( $this->xml->read() ) {
-                       if ( $this->xml->nodeType == XMLReader::END_ELEMENT &&
-                               $this->xml->name == $name
-                       ) {
-                               return true;
-                       }
-               }
-
-               return false;
+       protected function getDumpAsserter( $schemaVersion = null ) {
+               $schemaVersion = $schemaVersion ?: WikiExporter::schemaVersion();
+               return new DumpAsserter( $schemaVersion );
        }
 
        /**
-        * Step the current XML reader to the first element start after the node
-        * end of a given name.
-        *
-        * @param string $name Name of the closing element to look for
-        *   (e.g.: "mediawiki" when looking for </mediawiki>)
-        *
-        * @return bool True if new element after the closing of $name could be
-        *   found. false otherwise.
+        * Checks an XML file against an XSD schema.
         */
-       protected function skipPastNodeEnd( $name ) {
-               $this->assertTrue( $this->skipToNodeEnd( $name ),
-                       "Skipping to end of $name" );
-               while ( $this->xml->read() ) {
-                       if ( $this->xml->nodeType == XMLReader::ELEMENT ) {
-                               return true;
-                       }
+       protected function assertDumpSchema( $fname, $schemaFile ) {
+               if ( !function_exists( 'libxml_use_internal_errors' ) ) {
+                       // Would be nice to leave a warning somehow.
+                       // We don't want to skip all of the test case that calls this, though.
+                       $this->markAsRisky();
+                       return;
                }
-
-               return false;
-       }
-
-       /**
-        * Opens an XML file to analyze and optionally skips past siteinfo.
-        *
-        * @param string $fname Name of file to analyze
-        * @param bool $skip_siteinfo (optional) If true, step the xml reader
-        *   to the first element after </siteinfo>
-        */
-       protected function assertDumpStart( $fname, $skip_siteinfo = true ) {
-               $this->xml = new XMLReader();
-               $this->assertTrue( $this->xml->open( $fname ),
-                       "Opening temporary file $fname via XMLReader failed" );
-               if ( $skip_siteinfo ) {
-                       $this->assertTrue( $this->skipPastNodeEnd( "siteinfo" ),
-                               "Skipping past end of siteinfo" );
+               if ( defined( 'HHVM_VERSION' ) ) {
+                       // In HHVM, loading a schema from a file is disabled per default.
+                       // This is controlled by hhvm.libxml.ext_entity_whitelist which
+                       // cannot be read with ini_get(), see
+                       // <https://docs.hhvm.com/hhvm/configuration/INI-settings#xml>.
+                       // Would be nice to leave a warning somehow.
+                       // We don't want to skip all of the test case that calls this, though.
+                       $this->markAsRisky();
+                       return;
                }
-       }
 
-       /**
-        * Asserts that the xml reader is at the final closing tag of an xml file and
-        * closes the reader.
-        *
-        * @param string $name (optional) the name of the final tag
-        *   (e.g.: "mediawiki" for </mediawiki>)
-        */
-       protected function assertDumpEnd( $name = "mediawiki" ) {
-               $this->assertNodeEnd( $name, false );
-               if ( $this->xml->read() ) {
-                       $this->skipWhitespace();
-               }
-               $this->assertEquals( $this->xml->nodeType, XMLReader::NONE,
-                       "No proper entity left to parse" );
-               $this->xml->close();
-       }
+               $xml = new DOMDocument();
+               $this->assertTrue( $xml->load( $fname ),
+                       "Opening temporary file $fname via DOMDocument failed" );
 
-       /**
-        * Steps the xml reader over white space
-        */
-       protected function skipWhitespace() {
-               $cont = true;
-               while ( $cont && ( ( $this->xml->nodeType == XMLReader::WHITESPACE )
-                       || ( $this->xml->nodeType == XMLReader::SIGNIFICANT_WHITESPACE ) ) ) {
-                       $cont = $this->xml->read();
-               }
-       }
+               // Don't throw
+               $oldLibXmlInternalErrors = libxml_use_internal_errors( true );
 
-       /**
-        * Asserts that the xml reader is at an element of given name, and optionally
-        * skips past it.
-        *
-        * @param string $name The name of the element to check for
-        *   (e.g.: "mediawiki" for <mediawiki>)
-        * @param bool $skip (optional) if true, skip past the found element
-        */
-       protected function assertNodeStart( $name, $skip = true ) {
-               $this->assertEquals( $name, $this->xml->name, "Node name" );
-               $this->assertEquals( XMLReader::ELEMENT, $this->xml->nodeType, "Node type" );
-               if ( $skip ) {
-                       $this->assertTrue( $this->xml->read(), "Skipping past start tag" );
-               }
-       }
-
-       /**
-        * Asserts that the xml reader is at an closing element of given name, and optionally
-        * skips past it.
-        *
-        * @param string $name The name of the closing element to check for
-        *   (e.g.: "mediawiki" for </mediawiki>)
-        * @param bool $skip (optional) if true, skip past the found element
-        */
-       protected function assertNodeEnd( $name, $skip = true ) {
-               $this->assertEquals( $name, $this->xml->name, "Node name" );
-               $this->assertEquals( XMLReader::END_ELEMENT, $this->xml->nodeType, "Node type" );
-               if ( $skip ) {
-                       $this->assertTrue( $this->xml->read(), "Skipping past end tag" );
-               }
-       }
-
-       /**
-        * Asserts that the xml reader is at an element of given tag that contains a given text,
-        * and skips over the element.
-        *
-        * @param string $name The name of the element to check for
-        *   (e.g.: "mediawiki" for <mediawiki>...</mediawiki>)
-        * @param string|bool $text If string, check if it equals the elements text.
-        *   If false, ignore the element's text
-        * @param bool $skip_ws (optional) if true, skip past white spaces that trail the
-        *   closing element.
-        */
-       protected function assertTextNode( $name, $text, $skip_ws = true ) {
-               $this->assertNodeStart( $name );
-
-               if ( $text !== false ) {
-                       $this->assertEquals( $text, $this->xml->value, "Text of node " . $name );
-               }
-               $this->assertTrue( $this->xml->read(), "Skipping past processed text of " . $name );
-               $this->assertNodeEnd( $name );
-
-               if ( $skip_ws ) {
-                       $this->skipWhitespace();
-               }
-       }
-
-       /**
-        * Asserts that the xml reader is at the start of a page element and skips over the first
-        * tags, after checking them.
-        *
-        * Besides the opening page element, this function also checks for and skips over the
-        * title, ns, and id tags. Hence after this function, the xml reader is at the first
-        * revision of the current page.
-        *
-        * @param int $id Id of the page to assert
-        * @param int $ns Number of namespage to assert
-        * @param string $name Title of the current page
-        */
-       protected function assertPageStart( $id, $ns, $name ) {
-               $this->assertNodeStart( "page" );
-               $this->skipWhitespace();
-
-               $this->assertTextNode( "title", $name );
-               $this->assertTextNode( "ns", $ns );
-               $this->assertTextNode( "id", $id );
-       }
+               // NOTE: if this reports "Invalid Schema", the schema may be referencing an external
+               // entity (typically, another schema) that needs to be mapped in the
+               // libxml_set_external_entity_loader callback defined in setUpBeforeClass() above!
+               // Or $schemaFile doesn't point to a schema file, or the schema is indeed just broken.
+               if ( !$xml->schemaValidate( $schemaFile ) ) {
+                       $errorText = '';
 
-       /**
-        * Asserts that the xml reader is at the page's closing element and skips to the next
-        * element.
-        */
-       protected function assertPageEnd() {
-               $this->assertNodeEnd( "page" );
-               $this->skipWhitespace();
-       }
-
-       /**
-        * Asserts that the xml reader is at a revision and checks its representation before
-        * skipping over it.
-        *
-        * @param int $id Id of the revision
-        * @param string $summary Summary of the revision
-        * @param int $text_id Id of the revision's text
-        * @param int $text_bytes Number of bytes in the revision's text
-        * @param string $text_sha1 The base36 SHA-1 of the revision's text
-        * @param string|bool $text (optional) The revision's string, or false to check for a
-        *            revision stub
-        * @param int|bool $parentid (optional) id of the parent revision
-        * @param string $model The expected content model id (default: CONTENT_MODEL_WIKITEXT)
-        * @param string $format The expected format model id (default: CONTENT_FORMAT_WIKITEXT)
-        */
-       protected function assertRevision( $id, $summary, $text_id, $text_bytes,
-               $text_sha1, $text = false, $parentid = false,
-               $model = CONTENT_MODEL_WIKITEXT, $format = CONTENT_FORMAT_WIKITEXT
-       ) {
-               $this->assertNodeStart( "revision" );
-               $this->skipWhitespace();
-
-               $this->assertTextNode( "id", $id );
-               if ( $parentid !== false ) {
-                       $this->assertTextNode( "parentid", $parentid );
-               }
-               $this->assertTextNode( "timestamp", false );
-
-               $this->assertNodeStart( "contributor" );
-               $this->skipWhitespace();
-               $this->assertTextNode( "ip", false );
-               $this->assertNodeEnd( "contributor" );
-               $this->skipWhitespace();
-
-               $this->assertTextNode( "comment", $summary );
-               $this->skipWhitespace();
-
-               $this->assertTextNode( "model", $model );
-               $this->skipWhitespace();
-
-               $this->assertTextNode( "format", $format );
-               $this->skipWhitespace();
-
-               if ( $this->xml->name == "text" ) {
-                       // note: <text> tag may occur here or at the very end.
-                       $text_found = true;
-                       $this->assertText( $id, $text_id, $text_bytes, $text );
-               } else {
-                       $text_found = false;
-               }
+                       foreach ( libxml_get_errors() as $error ) {
+                               $errorText .= "\nline {$error->line}: {$error->message}";
+                       }
 
-               $this->assertTextNode( "sha1", $text_sha1 );
+                       libxml_clear_errors();
 
-               if ( !$text_found ) {
-                       $this->assertText( $id, $text_id, $text_bytes, $text );
+                       $this->fail(
+                               "Failed asserting that $fname conforms to the schema in $schemaFile:\n$errorText"
+                       );
                }
 
-               $this->assertNodeEnd( "revision" );
-               $this->skipWhitespace();
+               libxml_use_internal_errors( $oldLibXmlInternalErrors );
        }
 
-       protected function assertText( $id, $text_id, $text_bytes, $text ) {
-               $this->assertNodeStart( "text", false );
-               if ( $text_bytes !== false ) {
-                       $this->assertEquals( $this->xml->getAttribute( "bytes" ), $text_bytes,
-                               "Attribute 'bytes' of revision " . $id );
-               }
-
-               if ( $text === false ) {
-                       // Testing for a stub
-                       $this->assertEquals( $this->xml->getAttribute( "id" ), $text_id,
-                               "Text id of revision " . $id );
-                       $this->assertFalse( $this->xml->hasValue, "Revision has text" );
-                       $this->assertTrue( $this->xml->read(), "Skipping text start tag" );
-                       if ( ( $this->xml->nodeType == XMLReader::END_ELEMENT )
-                               && ( $this->xml->name == "text" )
-                       ) {
-                               $this->xml->read();
-                       }
-                       $this->skipWhitespace();
-               } else {
-                       // Testing for a real dump
-                       $this->assertTrue( $this->xml->read(), "Skipping text start tag" );
-                       $this->assertEquals( $text, $this->xml->value, "Text of revision " . $id );
-                       $this->assertTrue( $this->xml->read(), "Skipping past text" );
-                       $this->assertNodeEnd( "text" );
-                       $this->skipWhitespace();
-               }
-       }
 }
index 38a513e..0d4bc56 100644 (file)
@@ -130,45 +130,46 @@ class TextPassDumperDatabaseTest extends DumpTestCase {
                $dumper->dump( WikiExporter::FULL, WikiExporter::TEXT );
 
                // Checking for correctness of the dumped data
-               $this->assertDumpStart( $nameFull );
+               $asserter = $this->getDumpAsserter();
+               $asserter->assertDumpStart( $nameFull );
 
                // Page 1
-               $this->assertPageStart( $this->pageId1, NS_MAIN, "BackupDumperTestP1" );
-               $this->assertRevision( $this->revId1_1, "BackupDumperTestP1Summary1",
+               $asserter->assertPageStart( $this->pageId1, NS_MAIN, "BackupDumperTestP1" );
+               $asserter->assertRevision( $this->revId1_1, "BackupDumperTestP1Summary1",
                        $this->textId1_1, false, "0bolhl6ol7i6x0e7yq91gxgaan39j87",
                        "BackupDumperTestP1Text1" );
-               $this->assertPageEnd();
+               $asserter->assertPageEnd();
 
                // Page 2
-               $this->assertPageStart( $this->pageId2, NS_MAIN, "BackupDumperTestP2" );
-               $this->assertRevision( $this->revId2_1, "BackupDumperTestP2Summary1",
+               $asserter->assertPageStart( $this->pageId2, NS_MAIN, "BackupDumperTestP2" );
+               $asserter->assertRevision( $this->revId2_1, "BackupDumperTestP2Summary1",
                        $this->textId2_1, false, "jprywrymfhysqllua29tj3sc7z39dl2",
                        "BackupDumperTestP2Text1" );
-               $this->assertRevision( $this->revId2_2, "BackupDumperTestP2Summary2",
+               $asserter->assertRevision( $this->revId2_2, "BackupDumperTestP2Summary2",
                        $this->textId2_2, false, "b7vj5ks32po5m1z1t1br4o7scdwwy95",
                        "BackupDumperTestP2Text2", $this->revId2_1 );
-               $this->assertRevision( $this->revId2_3, "BackupDumperTestP2Summary3",
+               $asserter->assertRevision( $this->revId2_3, "BackupDumperTestP2Summary3",
                        $this->textId2_3, false, "jfunqmh1ssfb8rs43r19w98k28gg56r",
                        "BackupDumperTestP2Text3", $this->revId2_2 );
-               $this->assertRevision( $this->revId2_4, "BackupDumperTestP2Summary4 extra",
+               $asserter->assertRevision( $this->revId2_4, "BackupDumperTestP2Summary4 extra",
                        $this->textId2_4, false, "6o1ciaxa6pybnqprmungwofc4lv00wv",
                        "BackupDumperTestP2Text4 some additional Text", $this->revId2_3 );
-               $this->assertPageEnd();
+               $asserter->assertPageEnd();
 
                // Page 3
                // -> Page is marked deleted. Hence not visible
 
                // Page 4
-               $this->assertPageStart( $this->pageId4, NS_TALK, "Talk:BackupDumperTestP1" );
-               $this->assertRevision( $this->revId4_1, "Talk BackupDumperTestP1 Summary1",
+               $asserter->assertPageStart( $this->pageId4, NS_TALK, "Talk:BackupDumperTestP1" );
+               $asserter->assertRevision( $this->revId4_1, "Talk BackupDumperTestP1 Summary1",
                        $this->textId4_1, false, "nktofwzd0tl192k3zfepmlzxoax1lpe",
                        "TALK ABOUT BACKUPDUMPERTESTP1 TEXT1",
                        false,
                        "BackupTextPassTestModel",
                        "text/plain" );
-               $this->assertPageEnd();
+               $asserter->assertPageEnd();
 
-               $this->assertDumpEnd();
+               $asserter->assertDumpEnd();
        }
 
        function testPrefetchPlain() {
@@ -202,49 +203,50 @@ class TextPassDumperDatabaseTest extends DumpTestCase {
                $dumper->dump( WikiExporter::FULL, WikiExporter::TEXT );
 
                // Checking for correctness of the dumped data
-               $this->assertDumpStart( $nameFull );
+               $asserter = $this->getDumpAsserter();
+               $asserter->assertDumpStart( $nameFull );
 
                // Page 1
-               $this->assertPageStart( $this->pageId1, NS_MAIN, "BackupDumperTestP1" );
+               $asserter->assertPageStart( $this->pageId1, NS_MAIN, "BackupDumperTestP1" );
                // Prefetch kicks in. This is still the SHA-1 of the original text,
                // But the actual text (with different SHA-1) comes from prefetch.
-               $this->assertRevision( $this->revId1_1, "BackupDumperTestP1Summary1",
+               $asserter->assertRevision( $this->revId1_1, "BackupDumperTestP1Summary1",
                        $this->textId1_1, false, "0bolhl6ol7i6x0e7yq91gxgaan39j87",
                        "Prefetch_________1Text1" );
-               $this->assertPageEnd();
+               $asserter->assertPageEnd();
 
                // Page 2
-               $this->assertPageStart( $this->pageId2, NS_MAIN, "BackupDumperTestP2" );
-               $this->assertRevision( $this->revId2_1, "BackupDumperTestP2Summary1",
+               $asserter->assertPageStart( $this->pageId2, NS_MAIN, "BackupDumperTestP2" );
+               $asserter->assertRevision( $this->revId2_1, "BackupDumperTestP2Summary1",
                        $this->textId2_1, false, "jprywrymfhysqllua29tj3sc7z39dl2",
                        "BackupDumperTestP2Text1" );
-               $this->assertRevision( $this->revId2_2, "BackupDumperTestP2Summary2",
+               $asserter->assertRevision( $this->revId2_2, "BackupDumperTestP2Summary2",
                        $this->textId2_2, false, "b7vj5ks32po5m1z1t1br4o7scdwwy95",
                        "BackupDumperTestP2Text2", $this->revId2_1 );
                // Prefetch kicks in. This is still the SHA-1 of the original text,
                // But the actual text (with different SHA-1) comes from prefetch.
-               $this->assertRevision( $this->revId2_3, "BackupDumperTestP2Summary3",
+               $asserter->assertRevision( $this->revId2_3, "BackupDumperTestP2Summary3",
                        $this->textId2_3, false, "jfunqmh1ssfb8rs43r19w98k28gg56r",
                        "Prefetch_________2Text3", $this->revId2_2 );
-               $this->assertRevision( $this->revId2_4, "BackupDumperTestP2Summary4 extra",
+               $asserter->assertRevision( $this->revId2_4, "BackupDumperTestP2Summary4 extra",
                        $this->textId2_4, false, "6o1ciaxa6pybnqprmungwofc4lv00wv",
                        "BackupDumperTestP2Text4 some additional Text", $this->revId2_3 );
-               $this->assertPageEnd();
+               $asserter->assertPageEnd();
 
                // Page 3
                // -> Page is marked deleted. Hence not visible
 
                // Page 4
-               $this->assertPageStart( $this->pageId4, NS_TALK, "Talk:BackupDumperTestP1" );
-               $this->assertRevision( $this->revId4_1, "Talk BackupDumperTestP1 Summary1",
+               $asserter->assertPageStart( $this->pageId4, NS_TALK, "Talk:BackupDumperTestP1" );
+               $asserter->assertRevision( $this->revId4_1, "Talk BackupDumperTestP1 Summary1",
                        $this->textId4_1, false, "nktofwzd0tl192k3zfepmlzxoax1lpe",
                        "TALK ABOUT BACKUPDUMPERTESTP1 TEXT1",
                        false,
                        "BackupTextPassTestModel",
                        "text/plain" );
-               $this->assertPageEnd();
+               $asserter->assertPageEnd();
 
-               $this->assertDumpEnd();
+               $asserter->assertDumpEnd();
        }
 
        /**
@@ -329,6 +331,8 @@ class TextPassDumperDatabaseTest extends DumpTestCase {
                $lookingForPage = 1;
                $checkpointFiles = 0;
 
+               $asserter = $this->getDumpAsserter();
+
                // Each run of the following loop body tries to handle exactly 1 /page/ (not
                // iteration of stub content). $i is only increased after having treated page 4.
                for ( $i = 0; $i < $iterations; ) {
@@ -346,7 +350,7 @@ class TextPassDumperDatabaseTest extends DumpTestCase {
                                if ( $checkpointFormat == "gzip" ) {
                                        $this->gunzip( $nameOutputDir . "/" . $fname );
                                }
-                               $this->assertDumpStart( $nameOutputDir . "/" . $fname );
+                               $asserter->assertDumpStart( $nameOutputDir . "/" . $fname );
                                $fileOpened = true;
                                $checkpointFiles++;
                        }
@@ -355,51 +359,90 @@ class TextPassDumperDatabaseTest extends DumpTestCase {
                        switch ( $lookingForPage ) {
                                case 1:
                                        // Page 1
-                                       $this->assertPageStart( $this->pageId1 + $i * self::$numOfPages, NS_MAIN,
-                                               "BackupDumperTestP1" );
-                                       $this->assertRevision( $this->revId1_1 + $i * self::$numOfRevs, "BackupDumperTestP1Summary1",
-                                               $this->textId1_1, false, "0bolhl6ol7i6x0e7yq91gxgaan39j87",
-                                               "BackupDumperTestP1Text1" );
-                                       $this->assertPageEnd();
+                                       $asserter->assertPageStart(
+                                               $this->pageId1 + $i * self::$numOfPages,
+                                               NS_MAIN,
+                                               "BackupDumperTestP1"
+                                       );
+                                       $asserter->assertRevision(
+                                               $this->revId1_1 + $i * self::$numOfRevs,
+                                               "BackupDumperTestP1Summary1",
+                                               $this->textId1_1,
+                                               false,
+                                               "0bolhl6ol7i6x0e7yq91gxgaan39j87",
+                                               "BackupDumperTestP1Text1"
+                                       );
+                                       $asserter->assertPageEnd();
 
                                        $lookingForPage = 2;
                                        break;
 
                                case 2:
                                        // Page 2
-                                       $this->assertPageStart( $this->pageId2 + $i * self::$numOfPages, NS_MAIN,
-                                               "BackupDumperTestP2" );
-                                       $this->assertRevision( $this->revId2_1 + $i * self::$numOfRevs, "BackupDumperTestP2Summary1",
-                                               $this->textId2_1, false, "jprywrymfhysqllua29tj3sc7z39dl2",
-                                               "BackupDumperTestP2Text1" );
-                                       $this->assertRevision( $this->revId2_2 + $i * self::$numOfRevs, "BackupDumperTestP2Summary2",
-                                               $this->textId2_2, false, "b7vj5ks32po5m1z1t1br4o7scdwwy95",
-                                               "BackupDumperTestP2Text2", $this->revId2_1 + $i * self::$numOfRevs );
-                                       $this->assertRevision( $this->revId2_3 + $i * self::$numOfRevs, "BackupDumperTestP2Summary3",
-                                               $this->textId2_3, false, "jfunqmh1ssfb8rs43r19w98k28gg56r",
-                                               "BackupDumperTestP2Text3", $this->revId2_2 + $i * self::$numOfRevs );
-                                       $this->assertRevision( $this->revId2_4 + $i * self::$numOfRevs,
+                                       $asserter->assertPageStart(
+                                               $this->pageId2 + $i * self::$numOfPages,
+                                               NS_MAIN,
+                                               "BackupDumperTestP2"
+                                       );
+                                       $asserter->assertRevision(
+                                               $this->revId2_1 + $i * self::$numOfRevs,
+                                               "BackupDumperTestP2Summary1",
+                                               $this->textId2_1,
+                                               false,
+                                               "jprywrymfhysqllua29tj3sc7z39dl2",
+                                               "BackupDumperTestP2Text1"
+                                       );
+                                       $asserter->assertRevision(
+                                               $this->revId2_2 + $i * self::$numOfRevs,
+                                               "BackupDumperTestP2Summary2",
+                                               $this->textId2_2,
+                                               false,
+                                               "b7vj5ks32po5m1z1t1br4o7scdwwy95",
+                                               "BackupDumperTestP2Text2",
+                                               $this->revId2_1 + $i * self::$numOfRevs
+                                       );
+                                       $asserter->assertRevision(
+                                               $this->revId2_3 + $i * self::$numOfRevs,
+                                               "BackupDumperTestP2Summary3",
+                                               $this->textId2_3,
+                                               false,
+                                               "jfunqmh1ssfb8rs43r19w98k28gg56r",
+                                               "BackupDumperTestP2Text3",
+                                               $this->revId2_2 + $i * self::$numOfRevs
+                                       );
+                                       $asserter->assertRevision(
+                                               $this->revId2_4 + $i * self::$numOfRevs,
                                                "BackupDumperTestP2Summary4 extra",
-                                               $this->textId2_4, false, "6o1ciaxa6pybnqprmungwofc4lv00wv",
+                                               $this->textId2_4,
+                                               false,
+                                               "6o1ciaxa6pybnqprmungwofc4lv00wv",
                                                "BackupDumperTestP2Text4 some additional Text",
-                                               $this->revId2_3 + $i * self::$numOfRevs );
-                                       $this->assertPageEnd();
+                                               $this->revId2_3 + $i * self::$numOfRevs
+                                       );
+                                       $asserter->assertPageEnd();
 
                                        $lookingForPage = 4;
                                        break;
 
                                case 4:
                                        // Page 4
-                                       $this->assertPageStart( $this->pageId4 + $i * self::$numOfPages, NS_TALK,
-                                               "Talk:BackupDumperTestP1" );
-                                       $this->assertRevision( $this->revId4_1 + $i * self::$numOfRevs,
+                                       $asserter->assertPageStart(
+                                               $this->pageId4 + $i * self::$numOfPages,
+                                               NS_TALK,
+                                               "Talk:BackupDumperTestP1"
+                                       );
+                                       $asserter->assertRevision(
+                                               $this->revId4_1 + $i * self::$numOfRevs,
                                                "Talk BackupDumperTestP1 Summary1",
-                                               $this->textId4_1, false, "nktofwzd0tl192k3zfepmlzxoax1lpe",
+                                               $this->textId4_1,
+                                               false,
+                                               "nktofwzd0tl192k3zfepmlzxoax1lpe",
                                                "TALK ABOUT BACKUPDUMPERTESTP1 TEXT1",
                                                false,
                                                "BackupTextPassTestModel",
-                                               "text/plain" );
-                                       $this->assertPageEnd();
+                                               "text/plain"
+                                       );
+                                       $asserter->assertPageEnd();
 
                                        $lookingForPage = 1;
 
@@ -415,7 +458,7 @@ class TextPassDumperDatabaseTest extends DumpTestCase {
                        if ( $this->xml->nodeType == XMLReader::END_ELEMENT
                                && $this->xml->name == "mediawiki"
                        ) {
-                               $this->assertDumpEnd();
+                               $asserter->assertDumpEnd();
                                $fileOpened = false;
                        }
                }
index 9357451..811f1ee 100644 (file)
@@ -2,6 +2,7 @@
 
 namespace MediaWiki\Tests\Maintenance;
 
+use Exception;
 use MediaWiki\MediaWikiServices;
 use DumpBackup;
 use ManualLogEntry;
@@ -98,53 +99,6 @@ class BackupDumperLoggerTest extends DumpTestCase {
                }
        }
 
-       /**
-        * asserts that the xml reader is at the beginning of a log entry and skips over
-        * it while analyzing it.
-        *
-        * @param int $id Id of the log entry
-        * @param string $user_name User name of the log entry's performer
-        * @param int $user_id User id of the log entry 's performer
-        * @param string|null $comment Comment of the log entry. If null, the comment text is ignored.
-        * @param string $type Type of the log entry
-        * @param string $subtype Subtype of the log entry
-        * @param string $title Title of the log entry's target
-        * @param array $parameters (optional) unserialized data accompanying the log entry
-        */
-       private function assertLogItem( $id, $user_name, $user_id, $comment, $type,
-               $subtype, $title, $parameters = []
-       ) {
-               $this->assertNodeStart( "logitem" );
-               $this->skipWhitespace();
-
-               $this->assertTextNode( "id", $id );
-               $this->assertTextNode( "timestamp", false );
-
-               $this->assertNodeStart( "contributor" );
-               $this->skipWhitespace();
-               $this->assertTextNode( "username", $user_name );
-               $this->assertTextNode( "id", $user_id );
-               $this->assertNodeEnd( "contributor" );
-               $this->skipWhitespace();
-
-               if ( $comment !== null ) {
-                       $this->assertTextNode( "comment", $comment );
-               }
-               $this->assertTextNode( "type", $type );
-               $this->assertTextNode( "action", $subtype );
-               $this->assertTextNode( "logtitle", $title );
-
-               $this->assertNodeStart( "params" );
-               $parameters_xml = unserialize( $this->xml->value );
-               $this->assertEquals( $parameters, $parameters_xml );
-               $this->assertTrue( $this->xml->read(), "Skipping past processed text of params" );
-               $this->assertNodeEnd( "params" );
-               $this->skipWhitespace();
-
-               $this->assertNodeEnd( "logitem" );
-               $this->skipWhitespace();
-       }
-
        function testPlain() {
                // Preparing the dump
                $fname = $this->getNewTempFile();
@@ -159,9 +113,12 @@ class BackupDumperLoggerTest extends DumpTestCase {
                $dumper->dump( WikiExporter::LOGS, WikiExporter::TEXT );
 
                // Analyzing the dumped data
-               $this->assertDumpStart( $fname );
+               $this->assertDumpSchema( $fname, $this->getXmlSchemaPath() );
+
+               $asserter = $this->getDumpAsserter();
+               $asserter->assertDumpStart( $fname );
 
-               $this->assertLogItem( $this->logId1, "BackupDumperLogUserA",
+               $asserter->assertLogItem( $this->logId1, "BackupDumperLogUserA",
                        $this->userId1, null, "type", "subtype", "PageA" );
 
                $contLang = MediaWikiServices::getInstance()->getContentLanguage();
@@ -169,15 +126,15 @@ class BackupDumperLoggerTest extends DumpTestCase {
                $namespace = $contLang->getNsText( NS_TALK );
                $this->assertInternalType( 'string', $namespace );
                $this->assertGreaterThan( 0, strlen( $namespace ) );
-               $this->assertLogItem( $this->logId2, "BackupDumperLogUserB",
+               $asserter->assertLogItem( $this->logId2, "BackupDumperLogUserB",
                        $this->userId2, "SomeComment", "supress", "delete",
                        $namespace . ":PageB" );
 
-               $this->assertLogItem( $this->logId3, "BackupDumperLogUserB",
+               $asserter->assertLogItem( $this->logId3, "BackupDumperLogUserB",
                        $this->userId2, "SomeOtherComment", "move", "delete",
                        "PageA", [ 'key1' => 1, 3 => 'value3' ] );
 
-               $this->assertDumpEnd();
+               $asserter->assertDumpEnd();
        }
 
        function testXmlDumpsBackupUseCaseLogging() {
@@ -211,9 +168,12 @@ class BackupDumperLoggerTest extends DumpTestCase {
                // Analyzing the dumped data
                $this->gunzip( $fname );
 
-               $this->assertDumpStart( $fname );
+               $this->assertDumpSchema( $fname, $this->getXmlSchemaPath() );
+
+               $asserter = $this->getDumpAsserter();
+               $asserter->assertDumpStart( $fname );
 
-               $this->assertLogItem( $this->logId1, "BackupDumperLogUserA",
+               $asserter->assertLogItem( $this->logId1, "BackupDumperLogUserA",
                        $this->userId1, null, "type", "subtype", "PageA" );
 
                $contLang = MediaWikiServices::getInstance()->getContentLanguage();
@@ -221,15 +181,15 @@ class BackupDumperLoggerTest extends DumpTestCase {
                $namespace = $contLang->getNsText( NS_TALK );
                $this->assertInternalType( 'string', $namespace );
                $this->assertGreaterThan( 0, strlen( $namespace ) );
-               $this->assertLogItem( $this->logId2, "BackupDumperLogUserB",
+               $asserter->assertLogItem( $this->logId2, "BackupDumperLogUserB",
                        $this->userId2, "SomeComment", "supress", "delete",
                        $namespace . ":PageB" );
 
-               $this->assertLogItem( $this->logId3, "BackupDumperLogUserB",
+               $asserter->assertLogItem( $this->logId3, "BackupDumperLogUserB",
                        $this->userId2, "SomeOtherComment", "move", "delete",
                        "PageA", [ 'key1' => 1, 3 => 'value3' ] );
 
-               $this->assertDumpEnd();
+               $asserter->assertDumpEnd();
 
                // Currently, no reporting is implemented. Alert via failure, once
                // this changes.
index c37be4e..afe8c4b 100644 (file)
@@ -3,6 +3,7 @@
 namespace MediaWiki\Tests\Maintenance;
 
 use DumpBackup;
+use Exception;
 use MediaWiki\MediaWikiServices;
 use MediaWikiTestCase;
 use MWException;
@@ -169,12 +170,19 @@ class BackupDumperPageTest extends DumpTestCase {
                return $dumper;
        }
 
-       function testFullTextPlain() {
+       public function schemaVersionProvider() {
+               yield [ '0.10' ];
+       }
+
+       /**
+        * @dataProvider schemaVersionProvider
+        */
+       function testFullTextPlain( $schemaVersion ) {
                // Preparing the dump
                $fname = $this->getNewTempFile();
 
                $dumper = $this->newDumpBackup(
-                       [ '--full', '--quiet', '--output', 'file:' . $fname ],
+                       [ '--full', '--quiet', '--output', 'file:' . $fname, '--schema-version', $schemaVersion ],
                        $this->pageId1,
                        $this->pageId4 + 1
                );
@@ -183,54 +191,114 @@ class BackupDumperPageTest extends DumpTestCase {
                $dumper->execute();
 
                // Checking the dumped data
-               $this->assertDumpStart( $fname );
+               $this->assertDumpSchema( $fname, $this->getXmlSchemaPath( $schemaVersion ) );
+               $asserter = $this->getDumpAsserter( $schemaVersion );
+
+               $asserter->assertDumpStart( $fname );
 
                // Page 1
-               $this->assertPageStart( $this->pageId1, $this->namespace, $this->pageTitle1->getPrefixedText() );
-               $this->assertRevision( $this->revId1_1, "BackupDumperTestP1Summary1",
-                       $this->textId1_1, 23, "0bolhl6ol7i6x0e7yq91gxgaan39j87",
-                       "BackupDumperTestP1Text1" );
-               $this->assertPageEnd();
+               $asserter->assertPageStart(
+                       $this->pageId1,
+                       $this->namespace,
+                       $this->pageTitle1->getPrefixedText()
+               );
+               $asserter->assertRevision(
+                       $this->revId1_1,
+                       "BackupDumperTestP1Summary1",
+                       $this->textId1_1,
+                       23,
+                       "0bolhl6ol7i6x0e7yq91gxgaan39j87",
+                       "BackupDumperTestP1Text1"
+               );
+               $asserter->assertPageEnd();
 
                // Page 2
-               $this->assertPageStart( $this->pageId2, $this->namespace, $this->pageTitle2->getPrefixedText() );
-               $this->assertRevision( $this->revId2_1, "BackupDumperTestP2Summary1",
-                       $this->textId2_1, 23, "jprywrymfhysqllua29tj3sc7z39dl2",
-                       "BackupDumperTestP2Text1" );
-               $this->assertRevision( $this->revId2_2, "BackupDumperTestP2Summary2",
-                       $this->textId2_2, 23, "b7vj5ks32po5m1z1t1br4o7scdwwy95",
-                       "BackupDumperTestP2Text2", $this->revId2_1 );
-               $this->assertRevision( $this->revId2_3, "BackupDumperTestP2Summary3",
-                       $this->textId2_3, 23, "jfunqmh1ssfb8rs43r19w98k28gg56r",
-                       "BackupDumperTestP2Text3", $this->revId2_2 );
-               $this->assertRevision( $this->revId2_4, "BackupDumperTestP2Summary4 extra",
-                       $this->textId2_4, 44, "6o1ciaxa6pybnqprmungwofc4lv00wv",
-                       "BackupDumperTestP2Text4 some additional Text", $this->revId2_3 );
-               $this->assertPageEnd();
+               $asserter->assertPageStart(
+                       $this->pageId2,
+                       $this->namespace,
+                       $this->pageTitle2->getPrefixedText()
+               );
+               $asserter->assertRevision(
+                       $this->revId2_1,
+                       "BackupDumperTestP2Summary1",
+                       $this->textId2_1,
+                       23,
+                       "jprywrymfhysqllua29tj3sc7z39dl2",
+                       "BackupDumperTestP2Text1"
+               );
+               $asserter->assertRevision(
+                       $this->revId2_2,
+                       "BackupDumperTestP2Summary2",
+                       $this->textId2_2,
+                       23,
+                       "b7vj5ks32po5m1z1t1br4o7scdwwy95",
+                       "BackupDumperTestP2Text2",
+                       $this->revId2_1
+               );
+               $asserter->assertRevision(
+                       $this->revId2_3,
+                       "BackupDumperTestP2Summary3",
+                       $this->textId2_3,
+                       23,
+                       "jfunqmh1ssfb8rs43r19w98k28gg56r",
+                       "BackupDumperTestP2Text3",
+                       $this->revId2_2
+               );
+               $asserter->assertRevision(
+                       $this->revId2_4,
+                       "BackupDumperTestP2Summary4 extra",
+                       $this->textId2_4,
+                       44,
+                       "6o1ciaxa6pybnqprmungwofc4lv00wv",
+                       "BackupDumperTestP2Text4 some additional Text",
+                       $this->revId2_3
+               );
+               $asserter->assertPageEnd();
 
                // Page 3
                // -> Page is marked deleted. Hence not visible
 
                // Page 4
-               $this->assertPageStart(
+               $asserter->assertPageStart(
                        $this->pageId4,
                        $this->talk_namespace,
                        $this->pageTitle4->getPrefixedText()
                );
-               $this->assertRevision( $this->revId4_1, "Talk BackupDumperTestP1 Summary1",
-                       $this->textId4_1, 35, "nktofwzd0tl192k3zfepmlzxoax1lpe",
-                       "Talk about BackupDumperTestP1 Text1" );
-               $this->assertPageEnd();
+               $asserter->assertRevision(
+                       $this->revId4_1,
+                       "Talk BackupDumperTestP1 Summary1",
+                       $this->textId4_1,
+                       35,
+                       "nktofwzd0tl192k3zfepmlzxoax1lpe",
+                       "Talk about BackupDumperTestP1 Text1",
+                       false,
+                       CONTENT_MODEL_WIKITEXT,
+                       CONTENT_FORMAT_WIKITEXT,
+                       $schemaVersion
+               );
+               $asserter->assertPageEnd();
 
-               $this->assertDumpEnd();
+               $asserter->assertDumpEnd();
+
+               // FIXME: add multi-slot test case!
        }
 
-       function testFullStubPlain() {
+       /**
+        * @dataProvider schemaVersionProvider
+        */
+       function testFullStubPlain( $schemaVersion ) {
                // Preparing the dump
                $fname = $this->getNewTempFile();
 
                $dumper = $this->newDumpBackup(
-                       [ '--full', '--quiet', '--output', 'file:' . $fname, '--stub' ],
+                       [
+                               '--full',
+                               '--quiet',
+                               '--output',
+                               'file:' . $fname,
+                               '--stub',
+                               '--schema-version', $schemaVersion,
+                       ],
                        $this->pageId1,
                        $this->pageId4 + 1
                );
@@ -239,48 +307,98 @@ class BackupDumperPageTest extends DumpTestCase {
                $dumper->execute();
 
                // Checking the dumped data
-               $this->assertDumpStart( $fname );
+               $this->assertDumpSchema( $fname, $this->getXmlSchemaPath( $schemaVersion ) );
+               $asserter = $this->getDumpAsserter( $schemaVersion );
+
+               $asserter->assertDumpStart( $fname );
 
                // Page 1
-               $this->assertPageStart( $this->pageId1, $this->namespace, $this->pageTitle1->getPrefixedText() );
-               $this->assertRevision( $this->revId1_1, "BackupDumperTestP1Summary1",
-                       $this->textId1_1, 23, "0bolhl6ol7i6x0e7yq91gxgaan39j87" );
-               $this->assertPageEnd();
+               $asserter->assertPageStart(
+                       $this->pageId1,
+                       $this->namespace,
+                       $this->pageTitle1->getPrefixedText()
+               );
+               $asserter->assertRevision(
+                       $this->revId1_1,
+                       "BackupDumperTestP1Summary1",
+                       $this->textId1_1,
+                       23,
+                       "0bolhl6ol7i6x0e7yq91gxgaan39j87"
+               );
+               $asserter->assertPageEnd();
 
                // Page 2
-               $this->assertPageStart( $this->pageId2, $this->namespace, $this->pageTitle2->getPrefixedText() );
-               $this->assertRevision( $this->revId2_1, "BackupDumperTestP2Summary1",
-                       $this->textId2_1, 23, "jprywrymfhysqllua29tj3sc7z39dl2" );
-               $this->assertRevision( $this->revId2_2, "BackupDumperTestP2Summary2",
-                       $this->textId2_2, 23, "b7vj5ks32po5m1z1t1br4o7scdwwy95", false, $this->revId2_1 );
-               $this->assertRevision( $this->revId2_3, "BackupDumperTestP2Summary3",
-                       $this->textId2_3, 23, "jfunqmh1ssfb8rs43r19w98k28gg56r", false, $this->revId2_2 );
-               $this->assertRevision( $this->revId2_4, "BackupDumperTestP2Summary4 extra",
-                       $this->textId2_4, 44, "6o1ciaxa6pybnqprmungwofc4lv00wv", false, $this->revId2_3 );
-               $this->assertPageEnd();
+               $asserter->assertPageStart(
+                       $this->pageId2,
+                       $this->namespace,
+                       $this->pageTitle2->getPrefixedText()
+               );
+               $asserter->assertRevision(
+                       $this->revId2_1,
+                       "BackupDumperTestP2Summary1",
+                       $this->textId2_1,
+                       23,
+                       "jprywrymfhysqllua29tj3sc7z39dl2"
+               );
+               $asserter->assertRevision(
+                       $this->revId2_2,
+                       "BackupDumperTestP2Summary2",
+                       $this->textId2_2,
+                       23,
+                       "b7vj5ks32po5m1z1t1br4o7scdwwy95",
+                       false,
+                       $this->revId2_1
+               );
+               $asserter->assertRevision(
+                       $this->revId2_3,
+                       "BackupDumperTestP2Summary3",
+                       $this->textId2_3,
+                       23,
+                       "jfunqmh1ssfb8rs43r19w98k28gg56r",
+                       false,
+                       $this->revId2_2
+               );
+               $asserter->assertRevision(
+                       $this->revId2_4,
+                       "BackupDumperTestP2Summary4 extra",
+                       $this->textId2_4,
+                       44,
+                       "6o1ciaxa6pybnqprmungwofc4lv00wv",
+                       false,
+                       $this->revId2_3
+               );
+               $asserter->assertPageEnd();
 
                // Page 3
                // -> Page is marked deleted. Hence not visible
 
                // Page 4
-               $this->assertPageStart(
+               $asserter->assertPageStart(
                        $this->pageId4,
                        $this->talk_namespace,
                        $this->pageTitle4->getPrefixedText()
                );
-               $this->assertRevision( $this->revId4_1, "Talk BackupDumperTestP1 Summary1",
-                       $this->textId4_1, 35, "nktofwzd0tl192k3zfepmlzxoax1lpe" );
-               $this->assertPageEnd();
+               $asserter->assertRevision(
+                       $this->revId4_1,
+                       "Talk BackupDumperTestP1 Summary1",
+                       $this->textId4_1,
+                       35,
+                       "nktofwzd0tl192k3zfepmlzxoax1lpe"
+               );
+               $asserter->assertPageEnd();
 
-               $this->assertDumpEnd();
+               $asserter->assertDumpEnd();
        }
 
-       function testCurrentStubPlain() {
+       /**
+        * @dataProvider schemaVersionProvider
+        */
+       function testCurrentStubPlain( $schemaVersion ) {
                // Preparing the dump
                $fname = $this->getNewTempFile();
 
                $dumper = $this->newDumpBackup(
-                       [ '--output', 'file:' . $fname ],
+                       [ '--output', 'file:' . $fname, '--schema-version', $schemaVersion ],
                        $this->pageId1,
                        $this->pageId4 + 1
                );
@@ -289,34 +407,62 @@ class BackupDumperPageTest extends DumpTestCase {
                $dumper->dump( WikiExporter::CURRENT, WikiExporter::STUB );
 
                // Checking the dumped data
-               $this->assertDumpStart( $fname );
+               $this->assertDumpSchema( $fname, $this->getXmlSchemaPath( $schemaVersion ) );
+
+               $asserter = $this->getDumpAsserter( $schemaVersion );
+               $asserter->assertDumpStart( $fname );
 
                // Page 1
-               $this->assertPageStart( $this->pageId1, $this->namespace, $this->pageTitle1->getPrefixedText() );
-               $this->assertRevision( $this->revId1_1, "BackupDumperTestP1Summary1",
-                       $this->textId1_1, 23, "0bolhl6ol7i6x0e7yq91gxgaan39j87" );
-               $this->assertPageEnd();
+               $asserter->assertPageStart(
+                       $this->pageId1,
+                       $this->namespace,
+                       $this->pageTitle1->getPrefixedText()
+               );
+               $asserter->assertRevision(
+                       $this->revId1_1,
+                       "BackupDumperTestP1Summary1",
+                       $this->textId1_1,
+                       23,
+                       "0bolhl6ol7i6x0e7yq91gxgaan39j87"
+               );
+               $asserter->assertPageEnd();
 
                // Page 2
-               $this->assertPageStart( $this->pageId2, $this->namespace, $this->pageTitle2->getPrefixedText() );
-               $this->assertRevision( $this->revId2_4, "BackupDumperTestP2Summary4 extra",
-                       $this->textId2_4, 44, "6o1ciaxa6pybnqprmungwofc4lv00wv", false, $this->revId2_3 );
-               $this->assertPageEnd();
+               $asserter->assertPageStart(
+                       $this->pageId2,
+                       $this->namespace,
+                       $this->pageTitle2->getPrefixedText()
+               );
+               $asserter->assertRevision(
+                       $this->revId2_4,
+                       "BackupDumperTestP2Summary4 extra",
+                       $this->textId2_4,
+                       44,
+                       "6o1ciaxa6pybnqprmungwofc4lv00wv",
+                       false,
+                       $this->revId2_3
+               );
+               $asserter->assertPageEnd();
 
                // Page 3
                // -> Page is marked deleted. Hence not visible
 
                // Page 4
-               $this->assertPageStart(
+               $asserter->assertPageStart(
                        $this->pageId4,
                        $this->talk_namespace,
                        $this->pageTitle4->getPrefixedText()
                );
-               $this->assertRevision( $this->revId4_1, "Talk BackupDumperTestP1 Summary1",
-                       $this->textId4_1, 35, "nktofwzd0tl192k3zfepmlzxoax1lpe" );
-               $this->assertPageEnd();
+               $asserter->assertRevision(
+                       $this->revId4_1,
+                       "Talk BackupDumperTestP1 Summary1",
+                       $this->textId4_1,
+                       35,
+                       "nktofwzd0tl192k3zfepmlzxoax1lpe"
+               );
+               $asserter->assertPageEnd();
 
-               $this->assertDumpEnd();
+               $asserter->assertDumpEnd();
        }
 
        function testCurrentStubGzip() {
@@ -336,34 +482,56 @@ class BackupDumperPageTest extends DumpTestCase {
 
                // Checking the dumped data
                $this->gunzip( $fname );
-               $this->assertDumpStart( $fname );
+
+               $asserter = $this->getDumpAsserter();
+               $asserter->assertDumpStart( $fname );
 
                // Page 1
-               $this->assertPageStart( $this->pageId1, $this->namespace, $this->pageTitle1->getPrefixedText() );
-               $this->assertRevision( $this->revId1_1, "BackupDumperTestP1Summary1",
-                       $this->textId1_1, 23, "0bolhl6ol7i6x0e7yq91gxgaan39j87" );
-               $this->assertPageEnd();
+               $asserter->assertPageStart(
+                       $this->pageId1,
+                       $this->namespace,
+                       $this->pageTitle1->getPrefixedText()
+               );
+               $asserter->assertRevision(
+                       $this->revId1_1,
+                       "BackupDumperTestP1Summary1",
+                       $this->textId1_1,
+                       23,
+                       "0bolhl6ol7i6x0e7yq91gxgaan39j87"
+               );
+               $asserter->assertPageEnd();
 
                // Page 2
-               $this->assertPageStart( $this->pageId2, $this->namespace, $this->pageTitle2->getPrefixedText() );
-               $this->assertRevision( $this->revId2_4, "BackupDumperTestP2Summary4 extra",
-                       $this->textId2_4, 44, "6o1ciaxa6pybnqprmungwofc4lv00wv", false, $this->revId2_3 );
-               $this->assertPageEnd();
+               $asserter->assertPageStart(
+                       $this->pageId2,
+                       $this->namespace,
+                       $this->pageTitle2->getPrefixedText()
+               );
+               $asserter->assertRevision(
+                       $this->revId2_4,
+                       "BackupDumperTestP2Summary4 extra",
+                       $this->textId2_4,
+                       44,
+                       "6o1ciaxa6pybnqprmungwofc4lv00wv",
+                       false,
+                       $this->revId2_3
+               );
+               $asserter->assertPageEnd();
 
                // Page 3
                // -> Page is marked deleted. Hence not visible
 
                // Page 4
-               $this->assertPageStart(
+               $asserter->assertPageStart(
                        $this->pageId4,
                        $this->talk_namespace,
                        $this->pageTitle4->getPrefixedText()
                );
-               $this->assertRevision( $this->revId4_1, "Talk BackupDumperTestP1 Summary1",
+               $asserter->assertRevision( $this->revId4_1, "Talk BackupDumperTestP1 Summary1",
                        $this->textId4_1, 35, "nktofwzd0tl192k3zfepmlzxoax1lpe" );
-               $this->assertPageEnd();
+               $asserter->assertPageEnd();
 
-               $this->assertDumpEnd();
+               $asserter->assertDumpEnd();
        }
 
        /**
@@ -376,8 +544,10 @@ class BackupDumperPageTest extends DumpTestCase {
         *
         * We reproduce such a setup with our mini fixture, although we omit
         * chunks, and all the other gimmicks of xmldumps-backup.
+        *
+        * @dataProvider schemaVersionProvider
         */
-       function testXmlDumpsBackupUseCase() {
+       function testXmlDumpsBackupUseCase( $schemaVersion ) {
                $this->checkHasGzip();
 
                $fnameMetaHistory = $this->getNewTempFile();
@@ -389,7 +559,7 @@ class BackupDumperPageTest extends DumpTestCase {
                                "--output=gzip:" . $fnameMetaCurrent, "--filter=latest",
                                "--output=gzip:" . $fnameArticles, "--filter=latest",
                                "--filter=notalk", "--filter=namespace:!NS_USER",
-                               "--reporting=1000"
+                               "--reporting=1000", '--schema-version', $schemaVersion
                        ],
                        $this->pageId1,
                        $this->pageId4 + 1
@@ -413,89 +583,187 @@ class BackupDumperPageTest extends DumpTestCase {
                // Checking meta-history -------------------------------------------------
 
                $this->gunzip( $fnameMetaHistory );
-               $this->assertDumpStart( $fnameMetaHistory );
+               $this->assertDumpSchema( $fnameMetaHistory, $this->getXmlSchemaPath( $schemaVersion ) );
+
+               $asserter = $this->getDumpAsserter( $schemaVersion );
+               $asserter->assertDumpStart( $fnameMetaHistory );
 
                // Page 1
-               $this->assertPageStart( $this->pageId1, $this->namespace, $this->pageTitle1->getPrefixedText() );
-               $this->assertRevision( $this->revId1_1, "BackupDumperTestP1Summary1",
-                       $this->textId1_1, 23, "0bolhl6ol7i6x0e7yq91gxgaan39j87" );
-               $this->assertPageEnd();
+               $asserter->assertPageStart(
+                       $this->pageId1,
+                       $this->namespace,
+                       $this->pageTitle1->getPrefixedText()
+               );
+               $asserter->assertRevision(
+                       $this->revId1_1,
+                       "BackupDumperTestP1Summary1",
+                       $this->textId1_1,
+                       23,
+                       "0bolhl6ol7i6x0e7yq91gxgaan39j87"
+               );
+               $asserter->assertPageEnd();
 
                // Page 2
-               $this->assertPageStart( $this->pageId2, $this->namespace, $this->pageTitle2->getPrefixedText() );
-               $this->assertRevision( $this->revId2_1, "BackupDumperTestP2Summary1",
-                       $this->textId2_1, 23, "jprywrymfhysqllua29tj3sc7z39dl2" );
-               $this->assertRevision( $this->revId2_2, "BackupDumperTestP2Summary2",
-                       $this->textId2_2, 23, "b7vj5ks32po5m1z1t1br4o7scdwwy95", false, $this->revId2_1 );
-               $this->assertRevision( $this->revId2_3, "BackupDumperTestP2Summary3",
-                       $this->textId2_3, 23, "jfunqmh1ssfb8rs43r19w98k28gg56r", false, $this->revId2_2 );
-               $this->assertRevision( $this->revId2_4, "BackupDumperTestP2Summary4 extra",
-                       $this->textId2_4, 44, "6o1ciaxa6pybnqprmungwofc4lv00wv", false, $this->revId2_3 );
-               $this->assertPageEnd();
+               $asserter->assertPageStart(
+                       $this->pageId2,
+                       $this->namespace,
+                       $this->pageTitle2->getPrefixedText()
+               );
+               $asserter->assertRevision(
+                       $this->revId2_1,
+                       "BackupDumperTestP2Summary1",
+                       $this->textId2_1,
+                       23,
+                       "jprywrymfhysqllua29tj3sc7z39dl2"
+               );
+               $asserter->assertRevision(
+                       $this->revId2_2,
+                       "BackupDumperTestP2Summary2",
+                       $this->textId2_2,
+                       23,
+                       "b7vj5ks32po5m1z1t1br4o7scdwwy95",
+                       false,
+                       $this->revId2_1
+               );
+               $asserter->assertRevision(
+                       $this->revId2_3,
+                       "BackupDumperTestP2Summary3",
+                       $this->textId2_3,
+                       23,
+                       "jfunqmh1ssfb8rs43r19w98k28gg56r",
+                       false,
+                       $this->revId2_2
+               );
+               $asserter->assertRevision(
+                       $this->revId2_4,
+                       "BackupDumperTestP2Summary4 extra",
+                       $this->textId2_4,
+                       44,
+                       "6o1ciaxa6pybnqprmungwofc4lv00wv",
+                       false,
+                       $this->revId2_3
+               );
+               $asserter->assertPageEnd();
 
                // Page 3
                // -> Page is marked deleted. Hence not visible
 
                // Page 4
-               $this->assertPageStart(
+               $asserter->assertPageStart(
                        $this->pageId4,
                        $this->talk_namespace,
-                       $this->pageTitle4->getPrefixedText()
+                       $this->pageTitle4->getPrefixedText( $schemaVersion )
                );
-               $this->assertRevision( $this->revId4_1, "Talk BackupDumperTestP1 Summary1",
-                       $this->textId4_1, 35, "nktofwzd0tl192k3zfepmlzxoax1lpe" );
-               $this->assertPageEnd();
+               $asserter->assertRevision(
+                       $this->revId4_1,
+                       "Talk BackupDumperTestP1 Summary1",
+                       $this->textId4_1,
+                       35,
+                       "nktofwzd0tl192k3zfepmlzxoax1lpe"
+               );
+               $asserter->assertPageEnd();
 
-               $this->assertDumpEnd();
+               $asserter->assertDumpEnd();
 
                // Checking meta-current -------------------------------------------------
 
                $this->gunzip( $fnameMetaCurrent );
-               $this->assertDumpStart( $fnameMetaCurrent );
+               $this->assertDumpSchema( $fnameMetaCurrent, $this->getXmlSchemaPath( $schemaVersion ) );
+
+               $asserter = $this->getDumpAsserter( $schemaVersion );
+               $asserter->assertDumpStart( $fnameMetaCurrent );
 
                // Page 1
-               $this->assertPageStart( $this->pageId1, $this->namespace, $this->pageTitle1->getPrefixedText() );
-               $this->assertRevision( $this->revId1_1, "BackupDumperTestP1Summary1",
-                       $this->textId1_1, 23, "0bolhl6ol7i6x0e7yq91gxgaan39j87" );
-               $this->assertPageEnd();
+               $asserter->assertPageStart(
+                       $this->pageId1,
+                       $this->namespace,
+                       $this->pageTitle1->getPrefixedText()
+               );
+               $asserter->assertRevision(
+                       $this->revId1_1,
+                       "BackupDumperTestP1Summary1",
+                       $this->textId1_1,
+                       23,
+                       "0bolhl6ol7i6x0e7yq91gxgaan39j87"
+               );
+               $asserter->assertPageEnd();
 
                // Page 2
-               $this->assertPageStart( $this->pageId2, $this->namespace, $this->pageTitle2->getPrefixedText() );
-               $this->assertRevision( $this->revId2_4, "BackupDumperTestP2Summary4 extra",
-                       $this->textId2_4, 44, "6o1ciaxa6pybnqprmungwofc4lv00wv", false, $this->revId2_3 );
-               $this->assertPageEnd();
+               $asserter->assertPageStart(
+                       $this->pageId2,
+                       $this->namespace,
+                       $this->pageTitle2->getPrefixedText()
+               );
+               $asserter->assertRevision(
+                       $this->revId2_4,
+                       "BackupDumperTestP2Summary4 extra",
+                       $this->textId2_4,
+                       44,
+                       "6o1ciaxa6pybnqprmungwofc4lv00wv",
+                       false,
+                       $this->revId2_3
+               );
+               $asserter->assertPageEnd();
 
                // Page 3
                // -> Page is marked deleted. Hence not visible
 
                // Page 4
-               $this->assertPageStart(
+               $asserter->assertPageStart(
                        $this->pageId4,
                        $this->talk_namespace,
                        $this->pageTitle4->getPrefixedText()
                );
-               $this->assertRevision( $this->revId4_1, "Talk BackupDumperTestP1 Summary1",
-                       $this->textId4_1, 35, "nktofwzd0tl192k3zfepmlzxoax1lpe" );
-               $this->assertPageEnd();
+               $asserter->assertRevision(
+                       $this->revId4_1,
+                       "Talk BackupDumperTestP1 Summary1",
+                       $this->textId4_1,
+                       35,
+                       "nktofwzd0tl192k3zfepmlzxoax1lpe"
+               );
+               $asserter->assertPageEnd();
 
-               $this->assertDumpEnd();
+               $asserter->assertDumpEnd();
 
                // Checking articles -------------------------------------------------
 
                $this->gunzip( $fnameArticles );
-               $this->assertDumpStart( $fnameArticles );
+               $this->assertDumpSchema( $fnameArticles, $this->getXmlSchemaPath( $schemaVersion ) );
+
+               $asserter = $this->getDumpAsserter( $schemaVersion );
+               $asserter->assertDumpStart( $fnameArticles );
 
                // Page 1
-               $this->assertPageStart( $this->pageId1, $this->namespace, $this->pageTitle1->getPrefixedText() );
-               $this->assertRevision( $this->revId1_1, "BackupDumperTestP1Summary1",
-                       $this->textId1_1, 23, "0bolhl6ol7i6x0e7yq91gxgaan39j87" );
-               $this->assertPageEnd();
+               $asserter->assertPageStart(
+                       $this->pageId1,
+                       $this->namespace,
+                       $this->pageTitle1->getPrefixedText()
+               );
+               $asserter->assertRevision(
+                       $this->revId1_1,
+                       "BackupDumperTestP1Summary1",
+                       $this->textId1_1,
+                       23,
+                       "0bolhl6ol7i6x0e7yq91gxgaan39j87"
+               );
+               $asserter->assertPageEnd();
 
                // Page 2
-               $this->assertPageStart( $this->pageId2, $this->namespace, $this->pageTitle2->getPrefixedText() );
-               $this->assertRevision( $this->revId2_4, "BackupDumperTestP2Summary4 extra",
-                       $this->textId2_4, 44, "6o1ciaxa6pybnqprmungwofc4lv00wv", false, $this->revId2_3 );
-               $this->assertPageEnd();
+               $asserter->assertPageStart(
+                       $this->pageId2,
+                       $this->namespace,
+                       $this->pageTitle2->getPrefixedText()
+               );
+               $asserter->assertRevision(
+                       $this->revId2_4,
+                       "BackupDumperTestP2Summary4 extra",
+                       $this->textId2_4,
+                       44,
+                       "6o1ciaxa6pybnqprmungwofc4lv00wv",
+                       false,
+                       $this->revId2_3
+               );
+               $asserter->assertPageEnd();
 
                // Page 3
                // -> Page is marked deleted. Hence not visible
@@ -503,7 +771,7 @@ class BackupDumperPageTest extends DumpTestCase {
                // Page 4
                // -> Page is not in $this->namespace. Hence not visible
 
-               $this->assertDumpEnd();
+               $asserter->assertDumpEnd();
 
                $this->expectETAOutput();
        }
diff --git a/tests/phpunit/maintenance/xml.xsd b/tests/phpunit/maintenance/xml.xsd
new file mode 100644 (file)
index 0000000..aea7d0d
--- /dev/null
@@ -0,0 +1,287 @@
+<?xml version='1.0'?>
+<?xml-stylesheet href="../2008/09/xsd.xsl" type="text/xsl"?>
+<xs:schema targetNamespace="http://www.w3.org/XML/1998/namespace" 
+  xmlns:xs="http://www.w3.org/2001/XMLSchema" 
+  xmlns   ="http://www.w3.org/1999/xhtml"
+  xml:lang="en">
+
+ <xs:annotation>
+  <xs:documentation>
+   <div>
+    <h1>About the XML namespace</h1>
+
+    <div class="bodytext">
+     <p>
+      This schema document describes the XML namespace, in a form
+      suitable for import by other schema documents.
+     </p>
+     <p>
+      See <a href="http://www.w3.org/XML/1998/namespace.html">
+      http://www.w3.org/XML/1998/namespace.html</a> and
+      <a href="http://www.w3.org/TR/REC-xml">
+      http://www.w3.org/TR/REC-xml</a> for information 
+      about this namespace.
+     </p>
+     <p>
+      Note that local names in this namespace are intended to be
+      defined only by the World Wide Web Consortium or its subgroups.
+      The names currently defined in this namespace are listed below.
+      They should not be used with conflicting semantics by any Working
+      Group, specification, or document instance.
+     </p>
+     <p>   
+      See further below in this document for more information about <a
+      href="#usage">how to refer to this schema document from your own
+      XSD schema documents</a> and about <a href="#nsversioning">the
+      namespace-versioning policy governing this schema document</a>.
+     </p>
+    </div>
+   </div>
+  </xs:documentation>
+ </xs:annotation>
+
+ <xs:attribute name="lang">
+  <xs:annotation>
+   <xs:documentation>
+    <div>
+     
+      <h3>lang (as an attribute name)</h3>
+      <p>
+       denotes an attribute whose value
+       is a language code for the natural language of the content of
+       any element; its value is inherited.  This name is reserved
+       by virtue of its definition in the XML specification.</p>
+     
+    </div>
+    <div>
+     <h4>Notes</h4>
+     <p>
+      Attempting to install the relevant ISO 2- and 3-letter
+      codes as the enumerated possible values is probably never
+      going to be a realistic possibility.  
+     </p>
+     <p>
+      See BCP 47 at <a href="http://www.rfc-editor.org/rfc/bcp/bcp47.txt">
+       http://www.rfc-editor.org/rfc/bcp/bcp47.txt</a>
+      and the IANA language subtag registry at
+      <a href="http://www.iana.org/assignments/language-subtag-registry">
+       http://www.iana.org/assignments/language-subtag-registry</a>
+      for further information.
+     </p>
+     <p>
+      The union allows for the 'un-declaration' of xml:lang with
+      the empty string.
+     </p>
+    </div>
+   </xs:documentation>
+  </xs:annotation>
+  <xs:simpleType>
+   <xs:union memberTypes="xs:language">
+    <xs:simpleType>    
+     <xs:restriction base="xs:string">
+      <xs:enumeration value=""/>
+     </xs:restriction>
+    </xs:simpleType>
+   </xs:union>
+  </xs:simpleType>
+ </xs:attribute>
+
+ <xs:attribute name="space">
+  <xs:annotation>
+   <xs:documentation>
+    <div>
+     
+      <h3>space (as an attribute name)</h3>
+      <p>
+       denotes an attribute whose
+       value is a keyword indicating what whitespace processing
+       discipline is intended for the content of the element; its
+       value is inherited.  This name is reserved by virtue of its
+       definition in the XML specification.</p>
+     
+    </div>
+   </xs:documentation>
+  </xs:annotation>
+  <xs:simpleType>
+   <xs:restriction base="xs:NCName">
+    <xs:enumeration value="default"/>
+    <xs:enumeration value="preserve"/>
+   </xs:restriction>
+  </xs:simpleType>
+ </xs:attribute>
+ <xs:attribute name="base" type="xs:anyURI"> <xs:annotation>
+   <xs:documentation>
+    <div>
+     
+      <h3>base (as an attribute name)</h3>
+      <p>
+       denotes an attribute whose value
+       provides a URI to be used as the base for interpreting any
+       relative URIs in the scope of the element on which it
+       appears; its value is inherited.  This name is reserved
+       by virtue of its definition in the XML Base specification.</p>
+     
+     <p>
+      See <a
+      href="http://www.w3.org/TR/xmlbase/">http://www.w3.org/TR/xmlbase/</a>
+      for information about this attribute.
+     </p>
+    </div>
+   </xs:documentation>
+  </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="id" type="xs:ID">
+  <xs:annotation>
+   <xs:documentation>
+    <div>
+     
+      <h3>id (as an attribute name)</h3> 
+      <p>
+       denotes an attribute whose value
+       should be interpreted as if declared to be of type ID.
+       This name is reserved by virtue of its definition in the
+       xml:id specification.</p>
+     
+     <p>
+      See <a
+      href="http://www.w3.org/TR/xml-id/">http://www.w3.org/TR/xml-id/</a>
+      for information about this attribute.
+     </p>
+    </div>
+   </xs:documentation>
+  </xs:annotation>
+ </xs:attribute>
+
+ <xs:attributeGroup name="specialAttrs">
+  <xs:attribute ref="xml:base"/>
+  <xs:attribute ref="xml:lang"/>
+  <xs:attribute ref="xml:space"/>
+  <xs:attribute ref="xml:id"/>
+ </xs:attributeGroup>
+
+ <xs:annotation>
+  <xs:documentation>
+   <div>
+   
+    <h3>Father (in any context at all)</h3> 
+
+    <div class="bodytext">
+     <p>
+      denotes Jon Bosak, the chair of 
+      the original XML Working Group.  This name is reserved by 
+      the following decision of the W3C XML Plenary and 
+      XML Coordination groups:
+     </p>
+     <blockquote>
+       <p>
+       In appreciation for his vision, leadership and
+       dedication the W3C XML Plenary on this 10th day of
+       February, 2000, reserves for Jon Bosak in perpetuity
+       the XML name "xml:Father".
+       </p>
+     </blockquote>
+    </div>
+   </div>
+  </xs:documentation>
+ </xs:annotation>
+
+ <xs:annotation>
+  <xs:documentation>
+   <div xml:id="usage" id="usage">
+    <h2><a name="usage">About this schema document</a></h2>
+
+    <div class="bodytext">
+     <p>
+      This schema defines attributes and an attribute group suitable
+      for use by schemas wishing to allow <code>xml:base</code>,
+      <code>xml:lang</code>, <code>xml:space</code> or
+      <code>xml:id</code> attributes on elements they define.
+     </p>
+     <p>
+      To enable this, such a schema must import this schema for
+      the XML namespace, e.g. as follows:
+     </p>
+     <pre>
+          &lt;schema . . .>
+           . . .
+           &lt;import namespace="http://www.w3.org/XML/1998/namespace"
+                      schemaLocation="http://www.w3.org/2001/xml.xsd"/>
+     </pre>
+     <p>
+      or
+     </p>
+     <pre>
+           &lt;import namespace="http://www.w3.org/XML/1998/namespace"
+                      schemaLocation="http://www.w3.org/2009/01/xml.xsd"/>
+     </pre>
+     <p>
+      Subsequently, qualified reference to any of the attributes or the
+      group defined below will have the desired effect, e.g.
+     </p>
+     <pre>
+          &lt;type . . .>
+           . . .
+           &lt;attributeGroup ref="xml:specialAttrs"/>
+     </pre>
+     <p>
+      will define a type which will schema-validate an instance element
+      with any of those attributes.
+     </p>
+    </div>
+   </div>
+  </xs:documentation>
+ </xs:annotation>
+
+ <xs:annotation>
+  <xs:documentation>
+   <div id="nsversioning" xml:id="nsversioning">
+    <h2><a name="nsversioning">Versioning policy for this schema document</a></h2>
+    <div class="bodytext">
+     <p>
+      In keeping with the XML Schema WG's standard versioning
+      policy, this schema document will persist at
+      <a href="http://www.w3.org/2009/01/xml.xsd">
+       http://www.w3.org/2009/01/xml.xsd</a>.
+     </p>
+     <p>
+      At the date of issue it can also be found at
+      <a href="http://www.w3.org/2001/xml.xsd">
+       http://www.w3.org/2001/xml.xsd</a>.
+     </p>
+     <p>
+      The schema document at that URI may however change in the future,
+      in order to remain compatible with the latest version of XML
+      Schema itself, or with the XML namespace itself.  In other words,
+      if the XML Schema or XML namespaces change, the version of this
+      document at <a href="http://www.w3.org/2001/xml.xsd">
+       http://www.w3.org/2001/xml.xsd 
+      </a> 
+      will change accordingly; the version at 
+      <a href="http://www.w3.org/2009/01/xml.xsd">
+       http://www.w3.org/2009/01/xml.xsd 
+      </a> 
+      will not change.
+     </p>
+     <p>
+      Previous dated (and unchanging) versions of this schema 
+      document are at:
+     </p>
+     <ul>
+      <li><a href="http://www.w3.org/2009/01/xml.xsd">
+       http://www.w3.org/2009/01/xml.xsd</a></li>
+      <li><a href="http://www.w3.org/2007/08/xml.xsd">
+       http://www.w3.org/2007/08/xml.xsd</a></li>
+      <li><a href="http://www.w3.org/2004/10/xml.xsd">
+       http://www.w3.org/2004/10/xml.xsd</a></li>
+      <li><a href="http://www.w3.org/2001/03/xml.xsd">
+       http://www.w3.org/2001/03/xml.xsd</a></li>
+     </ul>
+    </div>
+   </div>
+  </xs:documentation>
+ </xs:annotation>
+
+</xs:schema>
+