<!-- 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>
'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",
// ->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
+ }
+
}
--- /dev/null
+<?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();
+ }
+}
namespace MediaWiki\Tests\Maintenance;
use ContentHandler;
+use DOMDocument;
use ExecutableFinder;
use MediaWikiLangTestCase;
-use Page;
use User;
-use XMLReader;
+use WikiExporter;
+use WikiPage;
use MWException;
/**
*/
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;
/**
* 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)
* @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
);
}
+ 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.
*
$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
*/
}
/**
- * 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();
- }
- }
}
$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() {
$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();
}
/**
$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; ) {
if ( $checkpointFormat == "gzip" ) {
$this->gunzip( $nameOutputDir . "/" . $fname );
}
- $this->assertDumpStart( $nameOutputDir . "/" . $fname );
+ $asserter->assertDumpStart( $nameOutputDir . "/" . $fname );
$fileOpened = true;
$checkpointFiles++;
}
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;
if ( $this->xml->nodeType == XMLReader::END_ELEMENT
&& $this->xml->name == "mediawiki"
) {
- $this->assertDumpEnd();
+ $asserter->assertDumpEnd();
$fileOpened = false;
}
}
namespace MediaWiki\Tests\Maintenance;
+use Exception;
use MediaWiki\MediaWikiServices;
use DumpBackup;
use ManualLogEntry;
}
}
- /**
- * 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();
$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();
$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() {
// 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();
$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.
namespace MediaWiki\Tests\Maintenance;
use DumpBackup;
+use Exception;
use MediaWiki\MediaWikiServices;
use MediaWikiTestCase;
use MWException;
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
);
$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
);
$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
);
$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() {
// 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();
}
/**
*
* 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();
"--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
// 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
// Page 4
// -> Page is not in $this->namespace. Hence not visible
- $this->assertDumpEnd();
+ $asserter->assertDumpEnd();
$this->expectETAOutput();
}
--- /dev/null
+<?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>
+ <schema . . .>
+ . . .
+ <import namespace="http://www.w3.org/XML/1998/namespace"
+ schemaLocation="http://www.w3.org/2001/xml.xsd"/>
+ </pre>
+ <p>
+ or
+ </p>
+ <pre>
+ <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>
+ <type . . .>
+ . . .
+ <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>
+