3 use MediaWiki\Config\HashConfig
;
4 use MediaWiki\MainConfigNames
;
5 use MediaWiki\Revision\RevisionRecord
;
6 use MediaWiki\Tests\Maintenance\DumpAsserter
;
7 use MediaWiki\Title\Title
;
8 use Wikimedia\Rdbms\IDBAccessObject
;
9 use Wikimedia\Rdbms\SelectQueryBuilder
;
12 * Import/export round trip test.
15 * @covers \WikiExporter
16 * @covers \WikiImporter
18 class ImportExportTest
extends MediaWikiLangTestCase
{
20 protected function setUp(): void
{
23 $slotRoleRegistry = $this->getServiceContainer()->getSlotRoleRegistry();
25 if ( !$slotRoleRegistry->isDefinedRole( 'ImportExportTest' ) ) {
26 $slotRoleRegistry->defineRoleWithModel( 'ImportExportTest', CONTENT_MODEL_WIKITEXT
);
31 * @param string $schemaVersion
33 * @return WikiExporter
35 private function getExporter( string $schemaVersion ) {
36 $exporter = $this->getServiceContainer()
37 ->getWikiExporterFactory()
38 ->getWikiExporter( $this->getDb(), WikiExporter
::FULL
);
39 $exporter->setSchemaVersion( $schemaVersion );
44 * @param ImportSource $source
46 * @return WikiImporter
48 private function getImporter( ImportSource
$source ) {
49 $config = new HashConfig( [
50 MainConfigNames
::MaxArticleSize
=> 2048,
52 $services = $this->getServiceContainer();
53 $importer = new WikiImporter(
55 $this->getTestSysop()->getAuthority(),
57 $services->getHookContainer(),
58 $services->getContentLanguage(),
59 $services->getNamespaceInfo(),
60 $services->getTitleFactory(),
61 $services->getWikiPageFactory(),
62 $services->getWikiRevisionUploadImporter(),
63 $services->getContentHandlerFactory(),
64 $services->getSlotRoleRegistry()
70 * @param string $testName
74 private function getFilesToImport( string $testName ) {
75 return glob( __DIR__
. "/../../data/import/$testName.import-*.xml" );
80 * @param string $schemaVersion
82 * @return string path of the dump file
84 protected function getDumpTemplatePath( $name, $schemaVersion ) {
85 return __DIR__
. "/../../data/import/$name.$schemaVersion.xml";
89 * @param string $prefix
90 * @param string[] $keys
94 private function getUniqueNames( string $prefix, array $keys ) {
97 foreach ( $keys as $k ) {
98 $names[$k] = "$prefix-$k-" . wfRandomString( 6 );
105 * @param string $xmlData
106 * @param string[] $pageTitles
110 private function injectPageTitles( string $xmlData, array $pageTitles ) {
111 $keys = array_map( static function ( $name ) {
112 return "{{{$name}_title}}";
113 }, array_keys( $pageTitles ) );
117 array_values( $pageTitles ),
123 * @param Title $title
125 * @return RevisionRecord[]
127 private function getRevisions( Title
$title ) {
128 $store = $this->getServiceContainer()->getRevisionStore();
129 $queryBuilder = $store->newSelectQueryBuilder( $this->getDb() )
131 ->where( [ 'rev_page' => $title->getArticleID() ] )
132 ->orderBy( 'rev_id', SelectQueryBuilder
::SORT_ASC
);
134 $rows = $queryBuilder->caller( __METHOD__
)->fetchResultSet();
136 $status = $store->newRevisionsFromBatch( $rows );
137 return $status->getValue();
141 * @param string[] $pageTitles
145 private function getPageInfoVars( array $pageTitles ) {
147 foreach ( $pageTitles as $name => $page ) {
148 $title = Title
::newFromText( $page );
150 if ( !$title->exists( IDBAccessObject
::READ_LATEST
) ) {
151 // map only existing pages, since only they can appear in a dump
155 $vars[ $name . '_pageid' ] = $title->getArticleID();
156 $vars[ $name . '_title' ] = $title->getPrefixedDBkey();
157 $vars[ $name . '_namespace' ] = $title->getNamespace();
160 $revisions = $this->getRevisions( $title );
161 foreach ( $revisions as $rev ) {
162 $revkey = "{$name}_rev" . $n++
;
164 $vars[ $revkey . '_id' ] = $rev->getId();
165 $vars[ $revkey . '_userid' ] = $rev->getUser()->getId();
173 * @param string $schemaVersion
176 private function getSiteVars( $schemaVersion ) {
177 global $wgSitename, $wgDBname, $wgCapitalLinks;
180 $vars['mw_version'] = MW_VERSION
;
181 $vars['schema_version'] = $schemaVersion;
183 $vars['site_name'] = $wgSitename;
184 $vars['project_namespace'] =
185 $this->getServiceContainer()->getTitleFormatter()->getNamespaceName(
189 $vars['site_db'] = $wgDBname;
190 $vars['site_case'] = $wgCapitalLinks ?
'first-letter' : 'case-sensitive';
191 $vars['site_base'] = Title
::newMainPage()->getCanonicalURL();
192 $vars['site_language'] = $this->getServiceContainer()->getContentLanguage()->getHtmlCode();
197 public static function provideImportExport() {
198 foreach ( XmlDumpWriter
::$supportedSchemas as $schemaVersion ) {
199 yield
[ 'Basic', $schemaVersion ];
200 yield
[ 'Dupes', $schemaVersion ];
201 yield
[ 'Slots', $schemaVersion ];
202 yield
[ 'Interleaved', $schemaVersion ];
203 yield
[ 'InterleavedMulti', $schemaVersion ];
204 yield
[ 'MissingMainContentModel', $schemaVersion ];
205 yield
[ 'MissingSlotContentModel', $schemaVersion ];
210 * @dataProvider provideImportExport
212 public function testImportExport( $testName, $schemaVersion ) {
213 $asserter = new DumpAsserter( $schemaVersion );
215 $filesToImport = $this->getFilesToImport( $testName );
216 $fileToExpect = $this->getDumpTemplatePath( "$testName.expected", $schemaVersion );
217 $siteInfoExpect = $this->getDumpTemplatePath( 'SiteInfo', $schemaVersion );
219 $pageKeys = [ 'page1', 'page2', 'page3', 'page4', ];
220 $pageTitles = $this->getUniqueNames( $testName, $pageKeys );
223 foreach ( $filesToImport as $fileName ) {
224 $xmlData = file_get_contents( $fileName );
225 $xmlData = $this->injectPageTitles( $xmlData, $pageTitles );
227 $source = new ImportStringSource( $xmlData );
228 $importer = $this->getImporter( $source );
229 $importer->doImport();
233 $exporter = $this->getExporter( $schemaVersion );
235 $tmpFile = $this->getNewTempFile();
236 $buffer = new DumpFileOutput( $tmpFile );
238 $exporter->setOutputSink( $buffer );
239 $exporter->openStream();
240 $exporter->pagesByName( $pageTitles );
241 $exporter->closeStream();
243 // determine expected variable values
245 $this->getSiteVars( $schemaVersion ),
246 $this->getPageInfoVars( $pageTitles )
249 foreach ( $vars as $key => $value ) {
250 $asserter->setVarMapping( $key, $value );
253 $dumpData = file_get_contents( $tmpFile );
254 $this->assertNotEmpty( $dumpData, 'Dump XML' );
256 // check dump content
257 $asserter->assertDumpStart( $tmpFile, $siteInfoExpect );
258 $asserter->assertDOM( $fileToExpect );
259 $asserter->assertDumpEnd();